3485611cc690f651f5b316f2697eb1e1abf1b895
[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  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 #include <unistd.h>
32 #include <sys/param.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include "busybox.h"
36
37
38
39 /* structs __________________________ */
40
41 typedef struct {
42         uid_t u;
43         gid_t g;
44 } Id;
45
46 /* data _____________________________ */
47
48 /* defaults : should this be in an external file? */
49 static const char default_passwd[] = "x";
50 static const char default_gecos[] = "Linux User,,,";
51 static const char default_home_prefix[] = "/home";
52 static const char default_shell[] = "/bin/sh";
53
54 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
55 /* shadow in use? */
56 static int shadow_enabled = 0;
57 #endif
58
59 /* remix */
60 /* EDR recoded such that the uid may be passed in *p */
61 static int passwd_study(const char *filename, struct passwd *p)
62 {
63         struct passwd *pw;
64         FILE *passwd;
65
66         const int min = 500;
67         const int max = 65000;
68
69         passwd = wfopen(filename, "r");
70         if (!passwd)
71                 return 4;
72
73         /* EDR if uid is out of bounds, set to min */
74         if ((p->pw_uid > max) || (p->pw_uid < min))
75                 p->pw_uid = min;
76
77         /* stuff to do:  
78          * make sure login isn't taken;
79          * find free uid and gid;
80          */
81         while ((pw = fgetpwent(passwd))) {
82                 if (strcmp(pw->pw_name, p->pw_name) == 0) {
83                         /* return 0; */
84                         return 1;
85                 }
86                 if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max)
87                         && (pw->pw_uid >= min)) {
88                         p->pw_uid = pw->pw_uid + 1;
89                 }
90         }
91
92         /* EDR check for an already existing gid */
93         while (getgrgid(p->pw_uid) != NULL)
94                 p->pw_uid++;
95
96         /* EDR also check for an existing group definition */
97         if (getgrnam(p->pw_name) != NULL)
98                 return 3;
99
100         /* EDR bounds check */
101         if ((p->pw_uid > max) || (p->pw_uid < min))
102                 return 2;
103
104         /* EDR create new gid always = uid */
105         p->pw_gid = p->pw_uid;
106
107         /* return 1; */
108         return 0;
109 }
110
111 static void addgroup_wrapper(const char *login, gid_t gid)
112 {
113         char *cmd;
114
115         bb_asprintf(&cmd, "addgroup -g %d %s", gid, login);
116         system(cmd);
117         free(cmd);
118 }
119
120 static void passwd_wrapper(const char *login) __attribute__ ((noreturn));
121
122 static void passwd_wrapper(const char *login)
123 {
124         static const char prog[] = "passwd";
125         execlp(prog, prog, login, NULL);
126         error_msg_and_die("Failed to execute '%s', you must set the password for '%s' manually", prog, login);
127 }
128
129 /* putpwent(3) remix */
130 static int adduser(const char *filename, struct passwd *p)
131 {
132         FILE *passwd;
133         int r;
134 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
135         FILE *shadow;
136         struct spwd *sp;
137 #endif
138
139         /* make sure everything is kosher and setup uid && gid */
140         passwd = wfopen(filename, "a");
141         if (passwd == NULL) {
142                 return 1;
143         }
144         fseek(passwd, 0, SEEK_END);
145
146         /* if (passwd_study(filename, p) == 0) { */
147         r = passwd_study(filename, p);
148         if (r) {
149                 if (r == 1)
150                         error_msg("%s: login already in use", p->pw_name);
151                 else if (r == 2)
152                         error_msg("illegal uid or no uids left");
153                 else if (r == 3)
154                         error_msg("group name %s already in use", p->pw_name);
155                 else
156                         error_msg("generic error.");
157                 return 1;
158         }
159
160         /* add to passwd */
161         if (putpwent(p, passwd) == -1) {
162                 return 1;
163         }
164         fclose(passwd);
165
166 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
167         /* add to shadow if necessary */
168         if (shadow_enabled) {
169                 shadow = wfopen(shadow_file, "a");
170                 if (shadow == NULL) {
171                         return 1;
172                 }
173                 fseek(shadow, 0, SEEK_END);
174                 sp = pwd_to_spwd(p);
175                 sp->sp_max = 99999;             /* debianish */
176                 sp->sp_warn = 7;
177                 fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n",
178                                 sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max,
179                                 sp->sp_warn);
180                 fclose(shadow);
181         }
182 #endif
183
184         /* add to group */
185         /* addgroup should be responsible for dealing w/ gshadow */
186         addgroup_wrapper(p->pw_name, p->pw_gid);
187
188         /* Clear the umask for this process so it doesn't
189          * * screw up the permissions on the mkdir and chown. */
190         umask(0);
191
192         /* mkdir */
193         if (mkdir(p->pw_dir, 0755)) {
194                 perror_msg("%s", p->pw_dir);
195         }
196         /* Set the owner and group so it is owned by the new user. */
197         if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) {
198                 perror_msg("%s", p->pw_dir);
199         }
200         /* Now fix up the permissions to 2755. Can't do it before now
201          * since chown will clear the setgid bit */
202         if (chmod(p->pw_dir, 02755)) {
203                 perror_msg("%s", p->pw_dir);
204         }
205         /* interactively set passwd */
206         passwd_wrapper(p->pw_name);
207 }
208
209
210 /* return current uid (root is always uid == 0, right?) */
211 static inline uid_t i_am_not_root(void)
212 {
213         return geteuid();
214 }
215
216 /*
217  * adduser will take a login_name as its first parameter.
218  *
219  * home
220  * shell
221  * gecos 
222  *
223  * can be customized via command-line parameters.
224  * ________________________________________________________________________ */
225 int adduser_main(int argc, char **argv)
226 {
227         int opt;
228         const char *login;
229         const char *gecos;
230         const char *home = NULL;
231         const char *shell;
232
233         struct passwd pw;
234
235         /* init */
236         if (argc < 2) {
237                 show_usage();
238         }
239         gecos = default_gecos;
240         shell = default_shell;
241
242         /* get args */
243         while ((opt = getopt (argc, argv, "h:g:s:")) != -1)
244                 switch (opt) {
245                         case 'h':
246                                 home = optarg;
247                                 break;
248                         case 'g':
249                                 gecos = optarg;
250                                 break;
251                         case 's':
252                                 shell = optarg;
253                                 break;
254                         default:
255                                 show_usage ();
256                                 break;
257                 }
258
259         /* got root? */
260         if (i_am_not_root()) {
261                 error_msg_and_die( "Only root may add a user or group to the system.");
262         }
263
264         /* get login */
265         if (optind >= argc) {
266                 error_msg_and_die( "no user specified");
267         }
268         login = argv[optind];
269
270         /* create string for $HOME if not specified already */
271         if (!home) {
272                 home = concat_path_file(default_home_prefix, login);
273         }
274 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
275         /* is /etc/shadow in use? */
276         shadow_enabled = (0 == access(shadow_file, F_OK));
277 #endif
278
279         /* create a passwd struct */
280         pw.pw_name = (char *)login;
281         pw.pw_passwd = (char *)default_passwd;
282         pw.pw_uid = 0;
283         pw.pw_gid = 0;
284         pw.pw_gecos = (char *)gecos;
285         pw.pw_dir = (char *)home;
286         pw.pw_shell = (char *)shell;
287
288         /* grand finale */
289         return adduser(passwd_file, &pw);
290 }
291
292 /* $Id: adduser.c,v 1.4 2002/09/16 06:22:24 andersen Exp $ */