Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 1 | /* vi: set sw=4 ts=4: */ |
| 2 | /* |
| 3 | * adduser - add users to /etc/passwd and /etc/shadow |
| 4 | * |
| 5 | * Copyright (C) 1999 by Lineo, inc. and John Beppu |
| 6 | * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> |
| 7 | * |
Bernhard Reutner-Fischer | 86f5c99 | 2006-01-22 22:55:11 +0000 | [diff] [blame] | 8 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 9 | */ |
| 10 | |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 11 | #include "busybox.h" |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 12 | |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 13 | #define DONT_SET_PASS (1 << 4) |
| 14 | #define DONT_MAKE_HOME (1 << 6) |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 15 | |
| 16 | |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 17 | /* 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 | struct passwd *pw; |
| 22 | FILE *passwd; |
| 23 | |
| 24 | const int min = 500; |
| 25 | const int max = 65000; |
| 26 | |
Rob Landley | d921b2e | 2006-08-03 15:41:12 +0000 | [diff] [blame] | 27 | passwd = xfopen(filename, "r"); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 28 | |
| 29 | /* EDR if uid is out of bounds, set to min */ |
| 30 | if ((p->pw_uid > max) || (p->pw_uid < min)) |
| 31 | p->pw_uid = min; |
| 32 | |
Eric Andersen | c7bda1c | 2004-03-15 08:29:22 +0000 | [diff] [blame] | 33 | /* stuff to do: |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 34 | * make sure login isn't taken; |
| 35 | * find free uid and gid; |
| 36 | */ |
| 37 | while ((pw = fgetpwent(passwd))) { |
| 38 | if (strcmp(pw->pw_name, p->pw_name) == 0) { |
| 39 | /* return 0; */ |
| 40 | return 1; |
| 41 | } |
| 42 | if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max) |
| 43 | && (pw->pw_uid >= min)) { |
| 44 | p->pw_uid = pw->pw_uid + 1; |
| 45 | } |
| 46 | } |
| 47 | |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 48 | if (p->pw_gid == 0) { |
| 49 | /* EDR check for an already existing gid */ |
| 50 | while (getgrgid(p->pw_uid) != NULL) |
| 51 | p->pw_uid++; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 52 | |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 53 | /* EDR also check for an existing group definition */ |
| 54 | if (getgrnam(p->pw_name) != NULL) |
| 55 | return 3; |
| 56 | |
| 57 | /* EDR create new gid always = uid */ |
| 58 | p->pw_gid = p->pw_uid; |
| 59 | } |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 60 | |
| 61 | /* EDR bounds check */ |
| 62 | if ((p->pw_uid > max) || (p->pw_uid < min)) |
| 63 | return 2; |
| 64 | |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 65 | /* return 1; */ |
| 66 | return 0; |
| 67 | } |
| 68 | |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 69 | static void addgroup_wrapper(struct passwd *p) |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 70 | { |
Eric Andersen | c38678d | 2002-09-16 06:22:25 +0000 | [diff] [blame] | 71 | char *cmd; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 72 | |
Rob Landley | d921b2e | 2006-08-03 15:41:12 +0000 | [diff] [blame] | 73 | cmd = xasprintf("addgroup -g %d \"%s\"", p->pw_gid, p->pw_name); |
Robert Griebl | 2c0d0f0 | 2002-07-16 23:50:05 +0000 | [diff] [blame] | 74 | system(cmd); |
| 75 | free(cmd); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 76 | } |
| 77 | |
Bernhard Reutner-Fischer | 86f5c99 | 2006-01-22 22:55:11 +0000 | [diff] [blame] | 78 | static void passwd_wrapper(const char *login) ATTRIBUTE_NORETURN; |
Eric Andersen | c38678d | 2002-09-16 06:22:25 +0000 | [diff] [blame] | 79 | |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 80 | static void passwd_wrapper(const char *login) |
| 81 | { |
| 82 | static const char prog[] = "passwd"; |
Denis Vlasenko | 1d76f43 | 2007-02-06 01:20:12 +0000 | [diff] [blame] | 83 | BB_EXECLP(prog, prog, login, NULL); |
Denis Vlasenko | e1a0d48 | 2006-10-20 13:28:22 +0000 | [diff] [blame] | 84 | bb_error_msg_and_die("failed to execute '%s', you must set the password for '%s' manually", prog, login); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 85 | } |
| 86 | |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 87 | /* putpwent(3) remix */ |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 88 | static int adduser(struct passwd *p, unsigned long flags) |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 89 | { |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 90 | FILE *file; |
Rob Landley | 6210375 | 2006-07-16 18:58:18 +0000 | [diff] [blame] | 91 | int addgroup = !p->pw_gid; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 92 | |
| 93 | /* make sure everything is kosher and setup uid && gid */ |
Rob Landley | d921b2e | 2006-08-03 15:41:12 +0000 | [diff] [blame] | 94 | file = xfopen(bb_path_passwd_file, "a"); |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 95 | fseek(file, 0, SEEK_END); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 96 | |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 97 | switch (passwd_study(bb_path_passwd_file, p)) { |
| 98 | case 1: |
| 99 | bb_error_msg_and_die("%s: login already in use", p->pw_name); |
| 100 | case 2: |
| 101 | bb_error_msg_and_die("illegal uid or no uids left"); |
| 102 | case 3: |
| 103 | bb_error_msg_and_die("%s: group name already in use", p->pw_name); |
| 104 | } |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 105 | |
| 106 | /* add to passwd */ |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 107 | if (putpwent(p, file) == -1) { |
| 108 | bb_perror_nomsg_and_die(); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 109 | } |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 110 | fclose(file); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 111 | |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 112 | #if ENABLE_FEATURE_SHADOWPASSWDS |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 113 | /* add to shadow if necessary */ |
Rob Landley | d921b2e | 2006-08-03 15:41:12 +0000 | [diff] [blame] | 114 | file = xfopen(bb_path_shadow_file, "a"); |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 115 | fseek(file, 0, SEEK_END); |
| 116 | fprintf(file, "%s:!:%ld:%d:%d:%d:::\n", |
Denis Vlasenko | c345d8e | 2006-10-14 11:47:02 +0000 | [diff] [blame] | 117 | p->pw_name, /* username */ |
Denis Vlasenko | f7996f3 | 2007-01-11 17:20:00 +0000 | [diff] [blame] | 118 | time(NULL) / 86400, /* sp->sp_lstchg */ |
Denis Vlasenko | c345d8e | 2006-10-14 11:47:02 +0000 | [diff] [blame] | 119 | 0, /* sp->sp_min */ |
| 120 | 99999, /* sp->sp_max */ |
| 121 | 7); /* sp->sp_warn */ |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 122 | fclose(file); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 123 | #endif |
| 124 | |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 125 | /* add to group */ |
| 126 | /* addgroup should be responsible for dealing w/ gshadow */ |
| 127 | /* if using a pre-existing group, don't create one */ |
Rob Landley | 6210375 | 2006-07-16 18:58:18 +0000 | [diff] [blame] | 128 | if (addgroup) addgroup_wrapper(p); |
| 129 | |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 130 | /* Clear the umask for this process so it doesn't |
| 131 | * * screw up the permissions on the mkdir and chown. */ |
| 132 | umask(0); |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 133 | if (!(flags & DONT_MAKE_HOME)) { |
| 134 | /* Set the owner and group so it is owned by the new user, |
| 135 | then fix up the permissions to 2755. Can't do it before |
| 136 | since chown will clear the setgid bit */ |
| 137 | if (mkdir(p->pw_dir, 0755) |
| 138 | || chown(p->pw_dir, p->pw_uid, p->pw_gid) |
| 139 | || chmod(p->pw_dir, 02755)) { |
Denis Vlasenko | 9213a9e | 2006-09-17 16:28:10 +0000 | [diff] [blame] | 140 | bb_perror_msg("%s", p->pw_dir); |
| 141 | } |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 142 | } |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 143 | |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 144 | if (!(flags & DONT_SET_PASS)) { |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 145 | /* interactively set passwd */ |
| 146 | passwd_wrapper(p->pw_name); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 147 | } |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 148 | |
| 149 | return 0; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 150 | } |
| 151 | |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 152 | /* |
| 153 | * adduser will take a login_name as its first parameter. |
| 154 | * |
| 155 | * home |
| 156 | * shell |
Eric Andersen | c7bda1c | 2004-03-15 08:29:22 +0000 | [diff] [blame] | 157 | * gecos |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 158 | * |
| 159 | * can be customized via command-line parameters. |
Denis Vlasenko | 9a44c4f | 2006-12-28 05:44:47 +0000 | [diff] [blame] | 160 | */ |
Denis Vlasenko | 06af216 | 2007-02-03 17:28:39 +0000 | [diff] [blame] | 161 | int adduser_main(int argc, char **argv); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 162 | int adduser_main(int argc, char **argv) |
| 163 | { |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 164 | struct passwd pw; |
Tim Riker | c1ef7bd | 2006-01-25 00:08:53 +0000 | [diff] [blame] | 165 | const char *usegroup = NULL; |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 166 | unsigned long flags; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 167 | |
Denis Vlasenko | 3ef70d4 | 2006-12-26 17:37:38 +0000 | [diff] [blame] | 168 | /* got root? */ |
| 169 | if (geteuid()) { |
| 170 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); |
| 171 | } |
| 172 | |
Denis Vlasenko | 06c0a71 | 2007-01-29 22:51:44 +0000 | [diff] [blame] | 173 | pw.pw_gecos = (char *)"Linux User,,,"; |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 174 | pw.pw_shell = (char *)DEFAULT_SHELL; |
| 175 | pw.pw_dir = NULL; |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 176 | |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 177 | /* check for min, max and missing args and exit on error */ |
Denis Vlasenko | 67b23e6 | 2006-10-03 21:00:06 +0000 | [diff] [blame] | 178 | opt_complementary = "-1:?1:?"; |
| 179 | flags = getopt32(argc, argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 180 | |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 181 | /* create string for $HOME if not specified already */ |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 182 | if (!pw.pw_dir) { |
| 183 | snprintf(bb_common_bufsiz1, BUFSIZ, "/home/%s", argv[optind]); |
Denis Vlasenko | 3ef70d4 | 2006-12-26 17:37:38 +0000 | [diff] [blame] | 184 | pw.pw_dir = bb_common_bufsiz1; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 185 | } |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 186 | |
| 187 | /* create a passwd struct */ |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 188 | pw.pw_name = argv[optind]; |
Denis Vlasenko | 06c0a71 | 2007-01-29 22:51:44 +0000 | [diff] [blame] | 189 | pw.pw_passwd = (char *)"x"; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 190 | pw.pw_uid = 0; |
Denis Vlasenko | 9a44c4f | 2006-12-28 05:44:47 +0000 | [diff] [blame] | 191 | pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */ |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 192 | |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 193 | /* grand finale */ |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 194 | return adduser(&pw, flags); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 195 | } |