Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/loginutils/adduser.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/loginutils/adduser.c
r1765 r2725 6 6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> 7 7 * 8 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.8 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 9 9 */ 10 11 10 #include "libbb.h" 12 11 12 #if CONFIG_LAST_SYSTEM_ID < CONFIG_FIRST_SYSTEM_ID 13 #error Bad LAST_SYSTEM_ID or FIRST_SYSTEM_ID in .config 14 #endif 15 16 /* #define OPT_HOME (1 << 0) */ /* unused */ 17 /* #define OPT_GECOS (1 << 1) */ /* unused */ 18 #define OPT_SHELL (1 << 2) 19 #define OPT_GID (1 << 3) 13 20 #define OPT_DONT_SET_PASS (1 << 4) 21 #define OPT_SYSTEM_ACCOUNT (1 << 5) 14 22 #define OPT_DONT_MAKE_HOME (1 << 6) 15 16 23 #define OPT_UID (1 << 7) 24 25 /* We assume UID_T_MAX == INT_MAX */ 17 26 /* remix */ 18 /* EDR recoded such that the uid may be passed in *p */ 19 static int passwd_study(const char *filename, struct passwd *p) 20 { 21 enum { min = 500, max = 65000 }; 22 FILE *passwd; 23 /* We are using reentrant fgetpwent_r() in order to avoid 24 * pulling in static buffers from libc (think static build here) */ 25 char buffer[256]; 26 struct passwd pw; 27 struct passwd *result; 28 29 passwd = xfopen(filename, "r"); 30 31 /* EDR if uid is out of bounds, set to min */ 32 if ((p->pw_uid > max) || (p->pw_uid < min)) 33 p->pw_uid = min; 34 35 /* stuff to do: 36 * make sure login isn't taken; 37 * find free uid and gid; 38 */ 39 while (!fgetpwent_r(passwd, &pw, buffer, sizeof(buffer), &result)) { 40 if (strcmp(pw.pw_name, p->pw_name) == 0) { 41 /* return 0; */ 42 return 1; 43 } 44 if ((pw.pw_uid >= p->pw_uid) && (pw.pw_uid < max) 45 && (pw.pw_uid >= min)) { 46 p->pw_uid = pw.pw_uid + 1; 47 } 48 } 49 50 if (p->pw_gid == 0) { 51 /* EDR check for an already existing gid */ 52 while (getgrgid(p->pw_uid) != NULL) 53 p->pw_uid++; 54 55 /* EDR also check for an existing group definition */ 56 if (getgrnam(p->pw_name) != NULL) 57 return 3; 58 59 /* EDR create new gid always = uid */ 60 p->pw_gid = p->pw_uid; 61 } 62 63 /* EDR bounds check */ 64 if ((p->pw_uid > max) || (p->pw_uid < min)) 65 return 2; 66 67 /* return 1; */ 68 return 0; 69 } 70 71 static void addgroup_wrapper(struct passwd *p) 27 /* recoded such that the uid may be passed in *p */ 28 static void passwd_study(struct passwd *p) 29 { 30 int max = UINT_MAX; 31 32 if (getpwnam(p->pw_name)) { 33 bb_error_msg_and_die("%s '%s' in use", "user", p->pw_name); 34 /* this format string is reused in adduser and addgroup */ 35 } 36 37 if (!(option_mask32 & OPT_UID)) { 38 if (option_mask32 & OPT_SYSTEM_ACCOUNT) { 39 p->pw_uid = CONFIG_FIRST_SYSTEM_ID; 40 max = CONFIG_LAST_SYSTEM_ID; 41 } else { 42 p->pw_uid = CONFIG_LAST_SYSTEM_ID + 1; 43 max = 64999; 44 } 45 } 46 /* check for a free uid (and maybe gid) */ 47 while (getpwuid(p->pw_uid) || (p->pw_gid == (gid_t)-1 && getgrgid(p->pw_uid))) { 48 if (option_mask32 & OPT_UID) { 49 /* -u N, cannot pick uid other than N: error */ 50 bb_error_msg_and_die("%s '%s' in use", "uid", itoa(p->pw_uid)); 51 /* this format string is reused in adduser and addgroup */ 52 } 53 if (p->pw_uid == max) { 54 bb_error_msg_and_die("no %cids left", 'u'); 55 } 56 p->pw_uid++; 57 } 58 59 if (p->pw_gid == (gid_t)-1) { 60 p->pw_gid = p->pw_uid; /* new gid = uid */ 61 if (getgrnam(p->pw_name)) { 62 bb_error_msg_and_die("%s '%s' in use", "group", p->pw_name); 63 /* this format string is reused in adduser and addgroup */ 64 } 65 } 66 } 67 68 static void addgroup_wrapper(struct passwd *p, const char *group_name) 72 69 { 73 70 char *cmd; 74 71 75 cmd = xasprintf("addgroup -g %d \"%s\"", p->pw_gid, p->pw_name); 72 if (group_name) /* Add user to existing group */ 73 cmd = xasprintf("addgroup '%s' '%s'", p->pw_name, group_name); 74 else /* Add user to his own group with the first free gid found in passwd_study */ 75 cmd = xasprintf("addgroup -g %u '%s'", (unsigned)p->pw_gid, p->pw_name); 76 /* Warning: to be compatible with external addgroup programs we should use --gid instead */ 76 77 system(cmd); 77 78 free(cmd); 78 79 } 79 80 80 static void passwd_wrapper(const char *login) ATTRIBUTE_NORETURN;81 static void passwd_wrapper(const char *login) NORETURN; 81 82 82 83 static void passwd_wrapper(const char *login) 83 84 { 84 static const char prog[] ALIGN1 = "passwd"; 85 86 BB_EXECLP(prog, prog, login, NULL); 87 bb_error_msg_and_die("failed to execute '%s', you must set the password for '%s' manually", prog, login); 88 } 89 90 /* putpwent(3) remix */ 91 static int adduser(struct passwd *p) 92 { 93 FILE *file; 94 int addgroup = !p->pw_gid; 95 96 /* make sure everything is kosher and setup uid && gid */ 97 file = xfopen(bb_path_passwd_file, "a"); 98 fseek(file, 0, SEEK_END); 99 100 switch (passwd_study(bb_path_passwd_file, p)) { 101 case 1: 102 bb_error_msg_and_die("%s: login already in use", p->pw_name); 103 case 2: 104 bb_error_msg_and_die("illegal uid or no uids left"); 105 case 3: 106 bb_error_msg_and_die("%s: group name already in use", p->pw_name); 107 } 108 109 /* add to passwd */ 110 if (putpwent(p, file) == -1) { 111 bb_perror_nomsg_and_die(); 112 } 113 /* Do fclose even if !ENABLE_FEATURE_CLEAN_UP. 114 * We will exec passwd, files must be flushed & closed before that! */ 115 fclose(file); 116 117 #if ENABLE_FEATURE_SHADOWPASSWDS 118 /* add to shadow if necessary */ 119 file = fopen_or_warn(bb_path_shadow_file, "a"); 120 if (file) { 121 fseek(file, 0, SEEK_END); 122 fprintf(file, "%s:!:%ld:%d:%d:%d:::\n", 123 p->pw_name, /* username */ 124 time(NULL) / 86400, /* sp->sp_lstchg */ 125 0, /* sp->sp_min */ 126 99999, /* sp->sp_max */ 127 7); /* sp->sp_warn */ 128 fclose(file); 129 } 130 #endif 131 132 /* add to group */ 133 /* addgroup should be responsible for dealing w/ gshadow */ 134 /* if using a pre-existing group, don't create one */ 135 if (addgroup) addgroup_wrapper(p); 136 137 /* Clear the umask for this process so it doesn't 138 * screw up the permissions on the mkdir and chown. */ 139 umask(0); 140 if (!(option_mask32 & OPT_DONT_MAKE_HOME)) { 141 /* Set the owner and group so it is owned by the new user, 142 then fix up the permissions to 2755. Can't do it before 143 since chown will clear the setgid bit */ 144 if (mkdir(p->pw_dir, 0755) 145 || chown(p->pw_dir, p->pw_uid, p->pw_gid) 146 || chmod(p->pw_dir, 02755)) { 147 bb_perror_msg("%s", p->pw_dir); 148 } 149 } 150 151 if (!(option_mask32 & OPT_DONT_SET_PASS)) { 152 /* interactively set passwd */ 153 passwd_wrapper(p->pw_name); 154 } 155 156 return 0; 157 } 85 BB_EXECLP("passwd", "passwd", login, NULL); 86 bb_error_msg_and_die("can't execute passwd, you must set password manually"); 87 } 88 89 #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS 90 static const char adduser_longopts[] ALIGN1 = 91 "home\0" Required_argument "h" 92 "gecos\0" Required_argument "g" 93 "shell\0" Required_argument "s" 94 "ingroup\0" Required_argument "G" 95 "disabled-password\0" No_argument "D" 96 "empty-password\0" No_argument "D" 97 "system\0" No_argument "S" 98 "no-create-home\0" No_argument "H" 99 "uid\0" Required_argument "u" 100 ; 101 #endif 158 102 159 103 /* 160 104 * adduser will take a login_name as its first parameter. 161 * 162 * home 163 * shell 164 * gecos 165 * 105 * home, shell, gecos: 166 106 * can be customized via command-line parameters. 167 107 */ 168 int adduser_main(int argc, char **argv) ;169 int adduser_main(int argc , char **argv)108 int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 109 int adduser_main(int argc UNUSED_PARAM, char **argv) 170 110 { 171 111 struct passwd pw; 172 112 const char *usegroup = NULL; 113 char *p; 114 unsigned opts; 115 116 #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS 117 applet_long_options = adduser_longopts; 118 #endif 173 119 174 120 /* got root? */ … … 182 128 183 129 /* exactly one non-option arg */ 184 opt_complementary = "=1"; 185 getopt32(argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup); 130 /* disable interactive passwd for system accounts */ 131 opt_complementary = "=1:SD:u+"; 132 if (sizeof(pw.pw_uid) == sizeof(int)) { 133 opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &pw.pw_uid); 134 } else { 135 unsigned uid; 136 opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &uid); 137 if (opts & OPT_UID) { 138 pw.pw_uid = uid; 139 } 140 } 186 141 argv += optind; 187 142 188 /* create apasswd struct */143 /* fill in the passwd struct */ 189 144 pw.pw_name = argv[0]; 145 die_if_bad_username(pw.pw_name); 190 146 if (!pw.pw_dir) { 191 147 /* create string for $HOME if not specified already */ … … 193 149 } 194 150 pw.pw_passwd = (char *)"x"; 195 pw.pw_uid = 0; 196 pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */ 197 198 /* grand finale */ 199 return adduser(&pw); 200 } 151 if (opts & OPT_SYSTEM_ACCOUNT) { 152 if (!usegroup) { 153 usegroup = "nogroup"; 154 } 155 if (!(opts & OPT_SHELL)) { 156 pw.pw_shell = (char *) "/bin/false"; 157 } 158 } 159 pw.pw_gid = usegroup ? xgroup2gid(usegroup) : -1; /* exits on failure */ 160 161 /* make sure everything is kosher and setup uid && maybe gid */ 162 passwd_study(&pw); 163 164 p = xasprintf("x:%u:%u:%s:%s:%s", 165 (unsigned) pw.pw_uid, (unsigned) pw.pw_gid, 166 pw.pw_gecos, pw.pw_dir, pw.pw_shell); 167 if (update_passwd(bb_path_passwd_file, pw.pw_name, p, NULL) < 0) { 168 return EXIT_FAILURE; 169 } 170 if (ENABLE_FEATURE_CLEAN_UP) 171 free(p); 172 173 #if ENABLE_FEATURE_SHADOWPASSWDS 174 /* /etc/shadow fields: 175 * 1. username 176 * 2. encrypted password 177 * 3. last password change (unix date (unix time/24*60*60)) 178 * 4. minimum days required between password changes 179 * 5. maximum days password is valid 180 * 6. days before password is to expire that user is warned 181 * 7. days after password expires that account is disabled 182 * 8. unix date when login expires (i.e. when it may no longer be used) 183 */ 184 /* fields: 2 3 4 5 6 78 */ 185 p = xasprintf("!:%u:0:99999:7:::", (unsigned)(time(NULL)) / (24*60*60)); 186 /* ignore errors: if file is missing we suppose admin doesn't want it */ 187 update_passwd(bb_path_shadow_file, pw.pw_name, p, NULL); 188 if (ENABLE_FEATURE_CLEAN_UP) 189 free(p); 190 #endif 191 192 /* add to group */ 193 addgroup_wrapper(&pw, usegroup); 194 195 /* clear the umask for this process so it doesn't 196 * screw up the permissions on the mkdir and chown. */ 197 umask(0); 198 if (!(opts & OPT_DONT_MAKE_HOME)) { 199 /* set the owner and group so it is owned by the new user, 200 * then fix up the permissions to 2755. Can't do it before 201 * since chown will clear the setgid bit */ 202 int mkdir_err = mkdir(pw.pw_dir, 0755); 203 if (mkdir_err == 0) { 204 /* New home. Copy /etc/skel to it */ 205 const char *args[] = { 206 "chown", "-R", 207 xasprintf("%u:%u", (int)pw.pw_uid, (int)pw.pw_gid), 208 pw.pw_dir, NULL 209 }; 210 /* Be silent on any errors (like: no /etc/skel) */ 211 logmode = LOGMODE_NONE; 212 copy_file("/etc/skel", pw.pw_dir, FILEUTILS_RECUR); 213 logmode = LOGMODE_STDIO; 214 chown_main(4, (char**)args); 215 } 216 if ((mkdir_err != 0 && errno != EEXIST) 217 || chown(pw.pw_dir, pw.pw_uid, pw.pw_gid) != 0 218 || chmod(pw.pw_dir, 02755) != 0 /* set setgid bit on homedir */ 219 ) { 220 bb_simple_perror_msg(pw.pw_dir); 221 } 222 } 223 224 if (!(opts & OPT_DONT_SET_PASS)) { 225 /* interactively set passwd */ 226 passwd_wrapper(pw.pw_name); 227 } 228 229 return EXIT_SUCCESS; 230 }
Note:
See TracChangeset
for help on using the changeset viewer.