blob: b6d0c62ea864f44018b46fe89aa9f895461ea539 [file] [log] [blame]
Robert Griebl1fca5582002-06-04 20:45:46 +00001/* 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-Fischer86f5c992006-01-22 22:55:11 +00008 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Robert Griebl1fca5582002-06-04 20:45:46 +00009 */
10
Robert Griebl1fca5582002-06-04 20:45:46 +000011#include "busybox.h"
Robert Griebl1fca5582002-06-04 20:45:46 +000012
Rob Landley9a2dd512006-04-04 19:19:53 +000013#define DONT_SET_PASS (1 << 4)
14#define DONT_MAKE_HOME (1 << 6)
Robert Griebl1fca5582002-06-04 20:45:46 +000015
16
Robert Griebl1fca5582002-06-04 20:45:46 +000017/* remix */
18/* EDR recoded such that the uid may be passed in *p */
19static 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 Landleyd921b2e2006-08-03 15:41:12 +000027 passwd = xfopen(filename, "r");
Robert Griebl1fca5582002-06-04 20:45:46 +000028
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 Andersenc7bda1c2004-03-15 08:29:22 +000033 /* stuff to do:
Robert Griebl1fca5582002-06-04 20:45:46 +000034 * 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 Andersenf0f754a2003-06-21 20:03:07 +000048 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 Griebl1fca5582002-06-04 20:45:46 +000052
Eric Andersenf0f754a2003-06-21 20:03:07 +000053 /* 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 Griebl1fca5582002-06-04 20:45:46 +000060
61 /* EDR bounds check */
62 if ((p->pw_uid > max) || (p->pw_uid < min))
63 return 2;
64
Robert Griebl1fca5582002-06-04 20:45:46 +000065 /* return 1; */
66 return 0;
67}
68
Rob Landley9a2dd512006-04-04 19:19:53 +000069static void addgroup_wrapper(struct passwd *p)
Robert Griebl1fca5582002-06-04 20:45:46 +000070{
Eric Andersenc38678d2002-09-16 06:22:25 +000071 char *cmd;
Robert Griebl1fca5582002-06-04 20:45:46 +000072
Rob Landleyd921b2e2006-08-03 15:41:12 +000073 cmd = xasprintf("addgroup -g %d \"%s\"", p->pw_gid, p->pw_name);
Robert Griebl2c0d0f02002-07-16 23:50:05 +000074 system(cmd);
75 free(cmd);
Robert Griebl1fca5582002-06-04 20:45:46 +000076}
77
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000078static void passwd_wrapper(const char *login) ATTRIBUTE_NORETURN;
Eric Andersenc38678d2002-09-16 06:22:25 +000079
Robert Griebl1fca5582002-06-04 20:45:46 +000080static void passwd_wrapper(const char *login)
81{
82 static const char prog[] = "passwd";
Denis Vlasenko1d76f432007-02-06 01:20:12 +000083 BB_EXECLP(prog, prog, login, NULL);
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +000084 bb_error_msg_and_die("failed to execute '%s', you must set the password for '%s' manually", prog, login);
Robert Griebl1fca5582002-06-04 20:45:46 +000085}
86
Robert Griebl1fca5582002-06-04 20:45:46 +000087/* putpwent(3) remix */
Rob Landley9a2dd512006-04-04 19:19:53 +000088static int adduser(struct passwd *p, unsigned long flags)
Robert Griebl1fca5582002-06-04 20:45:46 +000089{
Rob Landley9a2dd512006-04-04 19:19:53 +000090 FILE *file;
Rob Landley62103752006-07-16 18:58:18 +000091 int addgroup = !p->pw_gid;
Robert Griebl1fca5582002-06-04 20:45:46 +000092
93 /* make sure everything is kosher and setup uid && gid */
Rob Landleyd921b2e2006-08-03 15:41:12 +000094 file = xfopen(bb_path_passwd_file, "a");
Rob Landley9a2dd512006-04-04 19:19:53 +000095 fseek(file, 0, SEEK_END);
Robert Griebl1fca5582002-06-04 20:45:46 +000096
Rob Landley9a2dd512006-04-04 19:19:53 +000097 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 Griebl1fca5582002-06-04 20:45:46 +0000105
106 /* add to passwd */
Rob Landley9a2dd512006-04-04 19:19:53 +0000107 if (putpwent(p, file) == -1) {
108 bb_perror_nomsg_and_die();
Robert Griebl1fca5582002-06-04 20:45:46 +0000109 }
Rob Landley9a2dd512006-04-04 19:19:53 +0000110 fclose(file);
Robert Griebl1fca5582002-06-04 20:45:46 +0000111
Rob Landley9a2dd512006-04-04 19:19:53 +0000112#if ENABLE_FEATURE_SHADOWPASSWDS
Robert Griebl1fca5582002-06-04 20:45:46 +0000113 /* add to shadow if necessary */
Rob Landleyd921b2e2006-08-03 15:41:12 +0000114 file = xfopen(bb_path_shadow_file, "a");
Rob Landley9a2dd512006-04-04 19:19:53 +0000115 fseek(file, 0, SEEK_END);
116 fprintf(file, "%s:!:%ld:%d:%d:%d:::\n",
Denis Vlasenkoc345d8e2006-10-14 11:47:02 +0000117 p->pw_name, /* username */
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000118 time(NULL) / 86400, /* sp->sp_lstchg */
Denis Vlasenkoc345d8e2006-10-14 11:47:02 +0000119 0, /* sp->sp_min */
120 99999, /* sp->sp_max */
121 7); /* sp->sp_warn */
Rob Landley9a2dd512006-04-04 19:19:53 +0000122 fclose(file);
Robert Griebl1fca5582002-06-04 20:45:46 +0000123#endif
124
Rob Landley9a2dd512006-04-04 19:19:53 +0000125 /* add to group */
126 /* addgroup should be responsible for dealing w/ gshadow */
127 /* if using a pre-existing group, don't create one */
Rob Landley62103752006-07-16 18:58:18 +0000128 if (addgroup) addgroup_wrapper(p);
129
Robert Griebl1fca5582002-06-04 20:45:46 +0000130 /* Clear the umask for this process so it doesn't
131 * * screw up the permissions on the mkdir and chown. */
132 umask(0);
Rob Landley9a2dd512006-04-04 19:19:53 +0000133 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 Vlasenko9213a9e2006-09-17 16:28:10 +0000140 bb_perror_msg("%s", p->pw_dir);
141 }
Robert Griebl1fca5582002-06-04 20:45:46 +0000142 }
Eric Andersenf0f754a2003-06-21 20:03:07 +0000143
Rob Landley9a2dd512006-04-04 19:19:53 +0000144 if (!(flags & DONT_SET_PASS)) {
Eric Andersenf0f754a2003-06-21 20:03:07 +0000145 /* interactively set passwd */
146 passwd_wrapper(p->pw_name);
Robert Griebl1fca5582002-06-04 20:45:46 +0000147 }
Eric Andersenf0f754a2003-06-21 20:03:07 +0000148
149 return 0;
Robert Griebl1fca5582002-06-04 20:45:46 +0000150}
151
Robert Griebl1fca5582002-06-04 20:45:46 +0000152/*
153 * adduser will take a login_name as its first parameter.
154 *
155 * home
156 * shell
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000157 * gecos
Robert Griebl1fca5582002-06-04 20:45:46 +0000158 *
159 * can be customized via command-line parameters.
Denis Vlasenko9a44c4f2006-12-28 05:44:47 +0000160 */
Denis Vlasenko06af2162007-02-03 17:28:39 +0000161int adduser_main(int argc, char **argv);
Robert Griebl1fca5582002-06-04 20:45:46 +0000162int adduser_main(int argc, char **argv)
163{
Eric Andersenf0f754a2003-06-21 20:03:07 +0000164 struct passwd pw;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000165 const char *usegroup = NULL;
Rob Landley9a2dd512006-04-04 19:19:53 +0000166 unsigned long flags;
Robert Griebl1fca5582002-06-04 20:45:46 +0000167
Denis Vlasenko3ef70d42006-12-26 17:37:38 +0000168 /* got root? */
169 if (geteuid()) {
170 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
171 }
172
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000173 pw.pw_gecos = (char *)"Linux User,,,";
Rob Landley9a2dd512006-04-04 19:19:53 +0000174 pw.pw_shell = (char *)DEFAULT_SHELL;
175 pw.pw_dir = NULL;
Eric Andersenf0f754a2003-06-21 20:03:07 +0000176
Rob Landley9a2dd512006-04-04 19:19:53 +0000177 /* check for min, max and missing args and exit on error */
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000178 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 Griebl1fca5582002-06-04 20:45:46 +0000180
Robert Griebl1fca5582002-06-04 20:45:46 +0000181 /* create string for $HOME if not specified already */
Rob Landley9a2dd512006-04-04 19:19:53 +0000182 if (!pw.pw_dir) {
183 snprintf(bb_common_bufsiz1, BUFSIZ, "/home/%s", argv[optind]);
Denis Vlasenko3ef70d42006-12-26 17:37:38 +0000184 pw.pw_dir = bb_common_bufsiz1;
Robert Griebl1fca5582002-06-04 20:45:46 +0000185 }
Robert Griebl1fca5582002-06-04 20:45:46 +0000186
187 /* create a passwd struct */
Rob Landley9a2dd512006-04-04 19:19:53 +0000188 pw.pw_name = argv[optind];
Denis Vlasenko06c0a712007-01-29 22:51:44 +0000189 pw.pw_passwd = (char *)"x";
Robert Griebl1fca5582002-06-04 20:45:46 +0000190 pw.pw_uid = 0;
Denis Vlasenko9a44c4f2006-12-28 05:44:47 +0000191 pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */
Eric Andersenf0f754a2003-06-21 20:03:07 +0000192
Robert Griebl1fca5582002-06-04 20:45:46 +0000193 /* grand finale */
Rob Landley9a2dd512006-04-04 19:19:53 +0000194 return adduser(&pw, flags);
Robert Griebl1fca5582002-06-04 20:45:46 +0000195}