- add testcase for grep bug (http://busybox.net/bugs/view.php?id=887)
[oweals/busybox.git] / loginutils / adduser.c
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  *
8  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
9  */
10
11 #include <stdio.h>
12 #include <sys/types.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <time.h>
16 #include <getopt.h>
17 #include <sys/stat.h>
18
19 #include "busybox.h"
20
21 #define DONT_SET_PASS                   (1 << 4)
22 #define DONT_MAKE_HOME                  (1 << 6)
23
24
25 /* remix */
26 /* EDR recoded such that the uid may be passed in *p */
27 static int passwd_study(const char *filename, struct passwd *p)
28 {
29         struct passwd *pw;
30         FILE *passwd;
31
32         const int min = 500;
33         const int max = 65000;
34
35         passwd = bb_xfopen(filename, "r");
36
37         /* EDR if uid is out of bounds, set to min */
38         if ((p->pw_uid > max) || (p->pw_uid < min))
39                 p->pw_uid = min;
40
41         /* stuff to do:
42          * make sure login isn't taken;
43          * find free uid and gid;
44          */
45         while ((pw = fgetpwent(passwd))) {
46                 if (strcmp(pw->pw_name, p->pw_name) == 0) {
47                         /* return 0; */
48                         return 1;
49                 }
50                 if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max)
51                         && (pw->pw_uid >= min)) {
52                         p->pw_uid = pw->pw_uid + 1;
53                 }
54         }
55
56         if (p->pw_gid == 0) {
57                 /* EDR check for an already existing gid */
58                 while (getgrgid(p->pw_uid) != NULL)
59                         p->pw_uid++;
60
61                 /* EDR also check for an existing group definition */
62                 if (getgrnam(p->pw_name) != NULL)
63                         return 3;
64
65                 /* EDR create new gid always = uid */
66                 p->pw_gid = p->pw_uid;
67         }
68
69         /* EDR bounds check */
70         if ((p->pw_uid > max) || (p->pw_uid < min))
71                 return 2;
72
73         /* return 1; */
74         return 0;
75 }
76
77 static void addgroup_wrapper(struct passwd *p)
78 {
79         char *cmd;
80
81         cmd = bb_xasprintf("addgroup -g %d \"%s\"", p->pw_gid, p->pw_name);
82         system(cmd);
83         free(cmd);
84 }
85
86 static void passwd_wrapper(const char *login) ATTRIBUTE_NORETURN;
87
88 static void passwd_wrapper(const char *login)
89 {
90         static const char prog[] = "passwd";
91         execlp(prog, prog, login, NULL);
92         bb_error_msg_and_die("Failed to execute '%s', you must set the password for '%s' manually", prog, login);
93 }
94
95 /* putpwent(3) remix */
96 static int adduser(struct passwd *p, unsigned long flags)
97 {
98         FILE *file;
99
100         /* make sure everything is kosher and setup uid && gid */
101         file = bb_xfopen(bb_path_passwd_file, "a");
102         fseek(file, 0, SEEK_END);
103
104         switch (passwd_study(bb_path_passwd_file, p)) {
105                 case 1:
106                         bb_error_msg_and_die("%s: login already in use", p->pw_name);
107                 case 2:
108                         bb_error_msg_and_die("illegal uid or no uids left");
109                 case 3:
110                         bb_error_msg_and_die("%s: group name already in use", p->pw_name);
111         }
112
113         /* add to passwd */
114         if (putpwent(p, file) == -1) {
115                 bb_perror_nomsg_and_die();
116         }
117         fclose(file);
118
119 #if ENABLE_FEATURE_SHADOWPASSWDS
120         /* add to shadow if necessary */
121         file = bb_xfopen(bb_path_shadow_file, "a");
122         fseek(file, 0, SEEK_END);
123         fprintf(file, "%s:!:%ld:%d:%d:%d:::\n",
124                                         p->pw_name,                             /* username */
125                                         time(NULL) / 86400,             /* sp->sp_lstchg */ 
126                                         0,                                              /* sp->sp_min */
127                                         99999,                                  /* sp->sp_max */
128                                         7);                                             /* sp->sp_warn */
129         fclose(file);
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 (p->pw_gid == 0) {
136                 addgroup_wrapper(p);
137         }
138         /* Clear the umask for this process so it doesn't
139          * * screw up the permissions on the mkdir and chown. */
140         umask(0);
141         if (!(flags & DONT_MAKE_HOME)) {
142                 /* Set the owner and group so it is owned by the new user,
143                    then fix up the permissions to 2755. Can't do it before
144                    since chown will clear the setgid bit */
145                 if (mkdir(p->pw_dir, 0755)
146                 || chown(p->pw_dir, p->pw_uid, p->pw_gid)
147                 || chmod(p->pw_dir, 02755)) {
148                         bb_perror_msg("%s", p->pw_dir);
149                 }
150         }
151
152         if (!(flags & DONT_SET_PASS)) {
153                 /* interactively set passwd */
154                 passwd_wrapper(p->pw_name);
155         }
156
157         return 0;
158 }
159
160 /*
161  * adduser will take a login_name as its first parameter.
162  *
163  * home
164  * shell
165  * gecos
166  *
167  * can be customized via command-line parameters.
168  * ________________________________________________________________________ */
169 int adduser_main(int argc, char **argv)
170 {
171         struct passwd pw;
172         const char *usegroup = NULL;
173         unsigned long flags;
174
175         pw.pw_gecos = "Linux User,,,";
176         pw.pw_shell = (char *)DEFAULT_SHELL;
177         pw.pw_dir = NULL;
178
179         /* check for min, max and missing args and exit on error */
180         bb_opt_complementally = "-1:?1:?";
181         flags = bb_getopt_ulflags(argc, argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup);
182
183         /* got root? */
184         if(geteuid()) {
185                 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
186         }
187
188         /* create string for $HOME if not specified already */
189         if (!pw.pw_dir) {
190                 snprintf(bb_common_bufsiz1, BUFSIZ, "/home/%s", argv[optind]);
191                 pw.pw_dir =  &bb_common_bufsiz1[0];
192         }
193
194         /* create a passwd struct */
195         pw.pw_name = argv[optind];
196         pw.pw_passwd = "x";
197         pw.pw_uid = 0;
198         pw.pw_gid = (usegroup) ? bb_xgetgrnam(usegroup) : 0; /* exits on failure */
199
200         /* grand finale */
201         return adduser(&pw, flags);
202 }