Announce 1.2.1
[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         int addgroup = !p->pw_gid;
100
101         /* make sure everything is kosher and setup uid && gid */
102         file = bb_xfopen(bb_path_passwd_file, "a");
103         fseek(file, 0, SEEK_END);
104
105         switch (passwd_study(bb_path_passwd_file, p)) {
106                 case 1:
107                         bb_error_msg_and_die("%s: login already in use", p->pw_name);
108                 case 2:
109                         bb_error_msg_and_die("illegal uid or no uids left");
110                 case 3:
111                         bb_error_msg_and_die("%s: group name already in use", p->pw_name);
112         }
113
114         /* add to passwd */
115         if (putpwent(p, file) == -1) {
116                 bb_perror_nomsg_and_die();
117         }
118         fclose(file);
119
120 #if ENABLE_FEATURE_SHADOWPASSWDS
121         /* add to shadow if necessary */
122         file = bb_xfopen(bb_path_shadow_file, "a");
123         fseek(file, 0, SEEK_END);
124         fprintf(file, "%s:!:%ld:%d:%d:%d:::\n",
125                                         p->pw_name,                             /* username */
126                                         time(NULL) / 86400,             /* sp->sp_lstchg */ 
127                                         0,                                              /* sp->sp_min */
128                                         99999,                                  /* sp->sp_max */
129                                         7);                                             /* sp->sp_warn */
130         fclose(file);
131 #endif
132
133         /* add to group */
134         /* addgroup should be responsible for dealing w/ gshadow */
135         /* if using a pre-existing group, don't create one */
136         if (addgroup) 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 }