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 | |
Denis Vlasenko | b6adbf1 | 2007-05-26 19:00:18 +0000 | [diff] [blame] | 11 | #include "libbb.h" |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 12 | |
Denis Vlasenko | 5df955f | 2007-03-13 13:01:14 +0000 | [diff] [blame] | 13 | #define OPT_DONT_SET_PASS (1 << 4) |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 14 | #define OPT_SYSTEM_ACCOUNT (1 << 5) |
Denis Vlasenko | 5df955f | 2007-03-13 13:01:14 +0000 | [diff] [blame] | 15 | #define OPT_DONT_MAKE_HOME (1 << 6) |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 16 | |
| 17 | |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 18 | /* remix */ |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 19 | /* recoded such that the uid may be passed in *p */ |
| 20 | static void passwd_study(struct passwd *p) |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 21 | { |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 22 | int max; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 23 | |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 24 | if (getpwnam(p->pw_name)) |
| 25 | bb_error_msg_and_die("login '%s' is in use", p->pw_name); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 26 | |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 27 | if (option_mask32 & OPT_SYSTEM_ACCOUNT) { |
| 28 | p->pw_uid = 0; |
| 29 | max = 999; |
| 30 | } else { |
| 31 | p->pw_uid = 1000; |
| 32 | max = 64999; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 33 | } |
| 34 | |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 35 | /* check for a free uid (and maybe gid) */ |
| 36 | while (getpwuid(p->pw_uid) || (!p->pw_gid && getgrgid(p->pw_uid))) |
| 37 | p->pw_uid++; |
Denis Vlasenko | 6cee58e | 2007-11-04 15:43:26 +0000 | [diff] [blame] | 38 | |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 39 | if (!p->pw_gid) { |
| 40 | /* new gid = uid */ |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 41 | p->pw_gid = p->pw_uid; |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 42 | if (getgrnam(p->pw_name)) |
| 43 | bb_error_msg_and_die("group name '%s' is in use", p->pw_name); |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 44 | } |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 45 | |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 46 | if (p->pw_uid > max) |
| 47 | bb_error_msg_and_die("no free uids left"); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 48 | } |
| 49 | |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 50 | static void addgroup_wrapper(struct passwd *p) |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 51 | { |
Eric Andersen | c38678d | 2002-09-16 06:22:25 +0000 | [diff] [blame] | 52 | char *cmd; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 53 | |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 54 | cmd = xasprintf("addgroup -g %u '%s'", (unsigned)p->pw_gid, p->pw_name); |
Robert Griebl | 2c0d0f0 | 2002-07-16 23:50:05 +0000 | [diff] [blame] | 55 | system(cmd); |
| 56 | free(cmd); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 57 | } |
| 58 | |
Bernhard Reutner-Fischer | 86f5c99 | 2006-01-22 22:55:11 +0000 | [diff] [blame] | 59 | static void passwd_wrapper(const char *login) ATTRIBUTE_NORETURN; |
Eric Andersen | c38678d | 2002-09-16 06:22:25 +0000 | [diff] [blame] | 60 | |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 61 | static void passwd_wrapper(const char *login) |
| 62 | { |
Denis Vlasenko | 6ca409e | 2007-08-12 20:58:27 +0000 | [diff] [blame] | 63 | static const char prog[] ALIGN1 = "passwd"; |
| 64 | |
Denis Vlasenko | 1d76f43 | 2007-02-06 01:20:12 +0000 | [diff] [blame] | 65 | BB_EXECLP(prog, prog, login, NULL); |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 66 | bb_error_msg_and_die("cannot execute %s, you must set password manually", prog); |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 67 | } |
| 68 | |
Denis Vlasenko | 753f42a | 2008-02-15 15:17:23 +0000 | [diff] [blame] | 69 | #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS |
| 70 | static const char adduser_longopts[] ALIGN1 = |
| 71 | "home\0" Required_argument "h" |
| 72 | "gecos\0" Required_argument "g" |
| 73 | "shell\0" Required_argument "s" |
| 74 | "ingroup\0" Required_argument "G" |
| 75 | "disabled-password\0" No_argument "D" |
| 76 | "empty-password\0" No_argument "D" |
| 77 | "system\0" No_argument "S" |
| 78 | "no-create-home\0" No_argument "H" |
| 79 | ; |
| 80 | #endif |
| 81 | |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 82 | /* |
| 83 | * adduser will take a login_name as its first parameter. |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 84 | * home, shell, gecos: |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 85 | * can be customized via command-line parameters. |
Denis Vlasenko | 9a44c4f | 2006-12-28 05:44:47 +0000 | [diff] [blame] | 86 | */ |
Denis Vlasenko | 9b49a5e | 2007-10-11 10:05:36 +0000 | [diff] [blame] | 87 | int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
Denis Vlasenko | 68404f1 | 2008-03-17 09:00:54 +0000 | [diff] [blame^] | 88 | int adduser_main(int argc ATTRIBUTE_UNUSED, char **argv) |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 89 | { |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 90 | struct passwd pw; |
Tim Riker | c1ef7bd | 2006-01-25 00:08:53 +0000 | [diff] [blame] | 91 | const char *usegroup = NULL; |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 92 | FILE *file; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 93 | |
Denis Vlasenko | 753f42a | 2008-02-15 15:17:23 +0000 | [diff] [blame] | 94 | #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS |
| 95 | applet_long_options = adduser_longopts; |
| 96 | #endif |
| 97 | |
Denis Vlasenko | 3ef70d4 | 2006-12-26 17:37:38 +0000 | [diff] [blame] | 98 | /* got root? */ |
| 99 | if (geteuid()) { |
| 100 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); |
| 101 | } |
| 102 | |
Denis Vlasenko | 06c0a71 | 2007-01-29 22:51:44 +0000 | [diff] [blame] | 103 | pw.pw_gecos = (char *)"Linux User,,,"; |
Rob Landley | 9a2dd51 | 2006-04-04 19:19:53 +0000 | [diff] [blame] | 104 | pw.pw_shell = (char *)DEFAULT_SHELL; |
| 105 | pw.pw_dir = NULL; |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 106 | |
Denis Vlasenko | f1f1b69 | 2007-07-30 12:32:37 +0000 | [diff] [blame] | 107 | /* exactly one non-option arg */ |
| 108 | opt_complementary = "=1"; |
Denis Vlasenko | fe7cd64 | 2007-08-18 15:32:12 +0000 | [diff] [blame] | 109 | getopt32(argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup); |
Denis Vlasenko | 74324c8 | 2007-06-04 10:16:52 +0000 | [diff] [blame] | 110 | argv += optind; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 111 | |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 112 | /* fill in the passwd struct */ |
Denis Vlasenko | 74324c8 | 2007-06-04 10:16:52 +0000 | [diff] [blame] | 113 | pw.pw_name = argv[0]; |
| 114 | if (!pw.pw_dir) { |
| 115 | /* create string for $HOME if not specified already */ |
| 116 | pw.pw_dir = xasprintf("/home/%s", argv[0]); |
| 117 | } |
Denis Vlasenko | 06c0a71 | 2007-01-29 22:51:44 +0000 | [diff] [blame] | 118 | pw.pw_passwd = (char *)"x"; |
Denis Vlasenko | 9a44c4f | 2006-12-28 05:44:47 +0000 | [diff] [blame] | 119 | pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */ |
Eric Andersen | f0f754a | 2003-06-21 20:03:07 +0000 | [diff] [blame] | 120 | |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 121 | /* make sure everything is kosher and setup uid && maybe gid */ |
| 122 | passwd_study(&pw); |
| 123 | |
| 124 | /* add to passwd */ |
| 125 | file = xfopen(bb_path_passwd_file, "a"); |
| 126 | //fseek(file, 0, SEEK_END); /* paranoia, "a" should ensure that anyway */ |
| 127 | if (putpwent(&pw, file) != 0) { |
| 128 | bb_perror_nomsg_and_die(); |
| 129 | } |
| 130 | /* do fclose even if !ENABLE_FEATURE_CLEAN_UP. |
| 131 | * We will exec passwd, files must be flushed & closed before that! */ |
| 132 | fclose(file); |
| 133 | |
| 134 | #if ENABLE_FEATURE_SHADOWPASSWDS |
| 135 | /* add to shadow if necessary */ |
| 136 | file = fopen_or_warn(bb_path_shadow_file, "a"); |
| 137 | if (file) { |
| 138 | //fseek(file, 0, SEEK_END); |
| 139 | fprintf(file, "%s:!:%u:0:99999:7:::\n", |
| 140 | pw.pw_name, /* username */ |
| 141 | (unsigned)(time(NULL) / 86400) /* sp->sp_lstchg */ |
| 142 | /*0,*/ /* sp->sp_min */ |
| 143 | /*99999,*/ /* sp->sp_max */ |
| 144 | /*7*/ /* sp->sp_warn */ |
| 145 | ); |
| 146 | fclose(file); |
| 147 | } |
| 148 | #endif |
| 149 | |
| 150 | /* add to group */ |
| 151 | /* addgroup should be responsible for dealing w/ gshadow */ |
| 152 | /* if using a pre-existing group, don't create one */ |
| 153 | if (!usegroup) |
| 154 | addgroup_wrapper(&pw); |
| 155 | |
| 156 | /* Clear the umask for this process so it doesn't |
| 157 | * screw up the permissions on the mkdir and chown. */ |
| 158 | umask(0); |
| 159 | if (!(option_mask32 & OPT_DONT_MAKE_HOME)) { |
| 160 | /* Set the owner and group so it is owned by the new user, |
| 161 | then fix up the permissions to 2755. Can't do it before |
| 162 | since chown will clear the setgid bit */ |
| 163 | if (mkdir(pw.pw_dir, 0755) |
| 164 | || chown(pw.pw_dir, pw.pw_uid, pw.pw_gid) |
| 165 | || chmod(pw.pw_dir, 02755) /* set setgid bit on homedir */ |
Denis Vlasenko | 6cee58e | 2007-11-04 15:43:26 +0000 | [diff] [blame] | 166 | ) { |
Denis Vlasenko | 15ca51e | 2007-10-29 19:25:45 +0000 | [diff] [blame] | 167 | bb_simple_perror_msg(pw.pw_dir); |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | if (!(option_mask32 & OPT_DONT_SET_PASS)) { |
| 172 | /* interactively set passwd */ |
| 173 | passwd_wrapper(pw.pw_name); |
| 174 | } |
| 175 | |
| 176 | return 0; |
Robert Griebl | 1fca558 | 2002-06-04 20:45:46 +0000 | [diff] [blame] | 177 | } |