Linux adduserコマンド解析

5573 ワード

Linuxのコマンドの大部分はbusyboxで実装されており、addUserもユーザーを追加するために使用されています.
しゅパラメータ
-c:コメント文字を付けてpasswdのコメント欄に保存します.
-d:ユーザー登録時の開始ディレクトリを指定します.
-D:プリセット値を変更します.
-e:アカウントの有効期限を指定します.デフォルトは永続的に有効です.
-f:パスワードが期限切れになってから何日後にアカウントを閉じるかを指定します.
-g:ユーザが属するグループを指定します.
-G:ユーザーが属する追加グループを指定します.
-m:ユーザーのログインディレクトリを自動的に作成します.
M:ユーザーの登録ディレクトリを自動的に作成しないでください.
-n:ユーザー名のグループの作成をキャンセルします.
-r:システムアカウントを作成します.
-s:ユーザーのログイン後に使用するshellを指定します.
-u:ユーザーID番号を指定します.
 
addUserの実装を見てみましょう
addUserの実装は比較的簡単で,主にユーザのコマンドラインパラメータを解析し,/etc/passwdに関連情報を保存する.
まずadduserを見てみましょうmain
/*
 * adduser will take a login_name as its first parameter.
 *
 * home
 * shell
 * gecos
 *
 * can be customized via command-line parameters.
 * ________________________________________________________________________ */
int adduser_main(int argc, char **argv)
{
	struct passwd pw;
	const char *usegroup = NULL;
	unsigned long flags;

	pw.pw_gecos = "Linux User,,,";
	pw.pw_shell = (char *)DEFAULT_SHELL;
	pw.pw_dir = NULL;

	/* check for min, max and missing args and exit on error */
	bb_opt_complementally = "-1:?1:?";//    
	flags = bb_getopt_ulflags(argc, argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup);

	/* got root? */
	if(geteuid()) {
		bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
 	}

	/* create string for $HOME if not specified already */
	if (!pw.pw_dir) {
		snprintf(bb_common_bufsiz1, BUFSIZ, "/home/%s", argv[optind]);
		pw.pw_dir =  &bb_common_bufsiz1[0];
	}

	/* create a passwd struct */
	pw.pw_name = argv[optind];
	pw.pw_passwd = "x";
	pw.pw_uid = 0;
	pw.pw_gid = (usegroup) ? bb_xgetgrnam(usegroup) : 0; /* exits on failure */

	/* grand finale */
	return adduser(&pw, flags);//      add  
}

このコードは、新しいpasswd構造でユーザー関連情報を保存し、adduserコマンドのオプションを解析してflagsに保存し、最終的にadduserを呼び出します.
/* putpwent(3) remix */
static int adduser(struct passwd *p, unsigned long flags)
{
	FILE *file;
	int addgroup = !p->pw_gid;

	/* make sure everything is kosher and setup uid && gid */
	file = bb_xfopen(bb_path_passwd_file, "a");
	fseek(file, 0, SEEK_END);

	switch (passwd_study(bb_path_passwd_file, p)) {//  uid 
		case 1:
			bb_error_msg_and_die("%s: login already in use", p->pw_name);
		case 2:
			bb_error_msg_and_die("illegal uid or no uids left");
		case 3:
			bb_error_msg_and_die("%s: group name already in use", p->pw_name);
 	}

	/* add to passwd */
	if (putpwent(p, file) == -1) {//  /etc/passwd
		bb_perror_nomsg_and_die();
	}
	fclose(file);

#if ENABLE_FEATURE_SHADOWPASSWDS
	/* add to shadow if necessary */
	file = bb_xfopen(bb_path_shadow_file, "a");
	fseek(file, 0, SEEK_END);
	fprintf(file, "%s:!:%ld:%d:%d:%d:::
", p->pw_name, /* username */ time(NULL) / 86400, /* sp->sp_lstchg */ 0, /* sp->sp_min */ 99999, /* sp->sp_max */ 7); /* sp->sp_warn */ fclose(file); #endif /* add to group */ /* addgroup should be responsible for dealing w/ gshadow */ /* if using a pre-existing group, don't create one */ if (addgroup) addgroup_wrapper(p); /* Clear the umask for this process so it doesn't * * screw up the permissions on the mkdir and chown. */ umask(0); if (!(flags & DONT_MAKE_HOME)) { /* Set the owner and group so it is owned by the new user, then fix up the permissions to 2755. Can't do it before since chown will clear the setgid bit */ if (mkdir(p->pw_dir, 0755)// || chown(p->pw_dir, p->pw_uid, p->pw_gid) || chmod(p->pw_dir, 02755)) { bb_perror_msg("%s", p->pw_dir); } } if (!(flags & DONT_SET_PASS)) { /* interactively set passwd */ passwd_wrapper(p->pw_name); } return 0; }

Adduserは主に3つのことをしました.
1、読み出し/etc/passwd/既存のユーザに基づいて新たに追加されたユーザにuidを付与
2、付与結果を/etc/passwdに戻す
3、ユーザーディレクトリを作成し、権限などを変更する
uidが付与するプロセスpasswdを見てみましょうstudy
/* remix */
/* EDR recoded such that the uid may be passed in *p */
static int passwd_study(const char *filename, struct passwd *p)
{
	struct passwd *pw;
	FILE *passwd;

	const int min = 500;
	const int max = 65000;

	passwd = bb_xfopen(filename, "r");

	/* EDR if uid is out of bounds, set to min */
	if ((p->pw_uid > max) || (p->pw_uid < min))
		p->pw_uid = min;

	/* stuff to do:
	 * make sure login isn't taken;
	 * find free uid and gid;
	 */
	while ((pw = fgetpwent(passwd))) {
		if (strcmp(pw->pw_name, p->pw_name) == 0) {//        
			/* return 0; */
			return 1;
		}
		if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max)
			&& (pw->pw_uid >= min)) {//        uid + 1
			p->pw_uid = pw->pw_uid + 1;
		}
	}

	if (p->pw_gid == 0) {
		/* EDR check for an already existing gid */
		while (getgrgid(p->pw_uid) != NULL)
			p->pw_uid++;

		/* EDR also check for an existing group definition */
		if (getgrnam(p->pw_name) != NULL)
			return 3;

		/* EDR create new gid always = uid */
		p->pw_gid = p->pw_uid;
	}

	/* EDR bounds check */
	if ((p->pw_uid > max) || (p->pw_uid < min))
		return 2;

	/* return 1; */
	return 0;
}

この関数も簡単で、既存のuidの最大値を検索し、追加するユーザーのuidを最大値に1を加算します.もちろん、uidの最大値を超えてはいけません.
 
上から見ると、adduserは実はカーネルと付き合う必要はありません.ただ、関連情報を/etc/passwdに保存しています.では、ls-lで表示されているユーザーはどうやって来たのでしょうか.lsの分析を見てください.