diff options
Diffstat (limited to 'adduser.c')
-rw-r--r-- | adduser.c | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/adduser.c b/adduser.c new file mode 100644 index 000000000..6bd2c253e --- /dev/null +++ b/adduser.c | |||
@@ -0,0 +1,366 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * adduser - add users to /etc/passwd and /etc/shadow | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 1999 by Lineo, inc. | ||
7 | * Written by John Beppu <beppu@lineo.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include <errno.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <stdarg.h> | ||
28 | #include <stdio.h> | ||
29 | #include <stdlib.h> | ||
30 | #include <string.h> | ||
31 | #include <shadow.h> | ||
32 | #include <time.h> | ||
33 | #include <unistd.h> | ||
34 | #include <sys/param.h> | ||
35 | #include <sys/stat.h> | ||
36 | #include <sys/types.h> | ||
37 | #include "busybox.h" | ||
38 | #include "pwd_grp/pwd.h" | ||
39 | #include "pwd_grp/grp.h" | ||
40 | |||
41 | #define PASSWD_FILE "/etc/passwd" | ||
42 | #define SHADOW_FILE "/etc/gshadow" | ||
43 | |||
44 | #if 0 | ||
45 | # define PASSWD_FILE "passwd" | ||
46 | # define SHADOW_FILE "shadow" | ||
47 | #endif | ||
48 | |||
49 | /* structs __________________________ */ | ||
50 | |||
51 | typedef struct { | ||
52 | uid_t u; | ||
53 | gid_t g; | ||
54 | } Id; | ||
55 | |||
56 | /* data _____________________________ */ | ||
57 | |||
58 | /* defaults : should this be in an external file? */ | ||
59 | static char *default_passwd = "x"; | ||
60 | static char *default_gecos = "Embedix User,,,"; | ||
61 | static char *default_home_prefix = "/home"; | ||
62 | static char *default_shell = "/bin/sh"; | ||
63 | |||
64 | /* shadow in use? */ | ||
65 | static int shadow_enabled = 0; | ||
66 | |||
67 | /* I was doing this all over the place */ | ||
68 | static FILE *fopen_wrapper(const char *filename, const char *mode) | ||
69 | { | ||
70 | FILE *f; | ||
71 | |||
72 | f = fopen(filename, mode); | ||
73 | if (f == NULL) { | ||
74 | fprintf(stderr, "adduser: %s: %s\n", filename, strerror(errno)); | ||
75 | } | ||
76 | return f; | ||
77 | } | ||
78 | |||
79 | /* remix */ | ||
80 | /* EDR recoded such that the uid may be passed in *p */ | ||
81 | static int passwd_study(const char *filename, struct passwd *p) | ||
82 | { | ||
83 | struct passwd *pw; | ||
84 | FILE *passwd; | ||
85 | |||
86 | const int min = 500; | ||
87 | const int max = 65000; | ||
88 | |||
89 | passwd = fopen_wrapper(filename, "r"); | ||
90 | if (!passwd) | ||
91 | return 4; | ||
92 | |||
93 | /* EDR if uid is out of bounds, set to min */ | ||
94 | if ((p->pw_uid > max) || (p->pw_uid < min)) | ||
95 | p->pw_uid = min; | ||
96 | |||
97 | /* stuff to do: | ||
98 | * make sure login isn't taken; | ||
99 | * find free uid and gid; | ||
100 | */ | ||
101 | while ((pw = fgetpwent(passwd))) { | ||
102 | if (strcmp(pw->pw_name, p->pw_name) == 0) { | ||
103 | /* return 0; */ | ||
104 | return 1; | ||
105 | } | ||
106 | if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max) | ||
107 | && (pw->pw_uid >= min)) { | ||
108 | p->pw_uid = pw->pw_uid + 1; | ||
109 | } | ||
110 | } | ||
111 | |||
112 | /* EDR check for an already existing gid */ | ||
113 | while (getgrgid(p->pw_uid) != NULL) | ||
114 | p->pw_uid++; | ||
115 | |||
116 | /* EDR also check for an existing group definition */ | ||
117 | if (getgrnam(p->pw_name) != NULL) | ||
118 | return 3; | ||
119 | |||
120 | /* EDR bounds check */ | ||
121 | if ((p->pw_uid > max) || (p->pw_uid < min)) | ||
122 | return 2; | ||
123 | |||
124 | /* EDR create new gid always = uid */ | ||
125 | p->pw_gid = p->pw_uid; | ||
126 | |||
127 | /* return 1; */ | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static void addgroup_wrapper(const char *login, gid_t gid) | ||
132 | { | ||
133 | int argc = 4; | ||
134 | char *argv[] = { "addgroup", "-g", NULL, NULL }; | ||
135 | char group_id[8]; | ||
136 | char group_name[32]; | ||
137 | |||
138 | strncpy(group_name, login, 32); | ||
139 | argv[3] = group_name; | ||
140 | sprintf(group_id, "%d", gid); | ||
141 | argv[2] = group_id; | ||
142 | addgroup_main(argc, argv); | ||
143 | } | ||
144 | |||
145 | static void passwd_wrapper(const char *login) | ||
146 | { | ||
147 | char *prog = "passwd"; | ||
148 | execlp(prog, prog, login, NULL); | ||
149 | error_msg_and_die("Failed to execute 'passwd', you must set the password for '%s' manually\n", login); | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * pwd_to_spwd - create entries for new spwd structure | ||
154 | * | ||
155 | * pwd_to_spwd() creates a new (struct spwd) containing the | ||
156 | * information in the pointed-to (struct passwd). | ||
157 | */ | ||
158 | #define DAY (24L*3600L) | ||
159 | #define WEEK (7*DAY) | ||
160 | #define SCALE DAY | ||
161 | static struct spwd *pwd_to_spwd(const struct passwd *pw) | ||
162 | { | ||
163 | static struct spwd sp; | ||
164 | |||
165 | /* | ||
166 | * Nice, easy parts first. The name and passwd map directly | ||
167 | * from the old password structure to the new one. | ||
168 | */ | ||
169 | sp.sp_namp = pw->pw_name; | ||
170 | sp.sp_pwdp = pw->pw_passwd; | ||
171 | |||
172 | /* | ||
173 | * Defaults used if there is no pw_age information. | ||
174 | */ | ||
175 | sp.sp_min = 0; | ||
176 | sp.sp_max = (10000L * DAY) / SCALE; | ||
177 | sp.sp_lstchg = time((time_t *) 0) / SCALE; | ||
178 | |||
179 | /* | ||
180 | * These fields have no corresponding information in the password | ||
181 | * file. They are set to uninitialized values. | ||
182 | */ | ||
183 | sp.sp_warn = -1; | ||
184 | sp.sp_expire = -1; | ||
185 | sp.sp_inact = -1; | ||
186 | sp.sp_flag = -1; | ||
187 | |||
188 | return &sp; | ||
189 | } | ||
190 | |||
191 | /* putpwent(3) remix */ | ||
192 | static int adduser(const char *filename, struct passwd *p) | ||
193 | { | ||
194 | FILE *passwd; | ||
195 | int r; | ||
196 | FILE *shadow; | ||
197 | struct spwd *sp; | ||
198 | |||
199 | /* make sure everything is kosher and setup uid && gid */ | ||
200 | passwd = fopen_wrapper(filename, "a"); | ||
201 | if (passwd == NULL) { | ||
202 | /* return -1; */ | ||
203 | return 1; | ||
204 | } | ||
205 | fseek(passwd, 0, SEEK_END); | ||
206 | |||
207 | /* if (passwd_study(filename, p) == 0) { */ | ||
208 | r = passwd_study(filename, p); | ||
209 | if (r) { | ||
210 | if (r == 1) | ||
211 | error_msg("%s: login already in use\n", p->pw_name); | ||
212 | else if (r == 2) | ||
213 | error_msg("illegal uid or no uids left\n"); | ||
214 | else if (r == 3) | ||
215 | error_msg("group name %s already in use\n", p->pw_name); | ||
216 | else | ||
217 | error_msg("generic error.\n"); | ||
218 | /* return -1; */ | ||
219 | return 1; | ||
220 | } | ||
221 | |||
222 | /* add to passwd */ | ||
223 | if (putpwent(p, passwd) == -1) { | ||
224 | /* return -1; */ | ||
225 | return 1; | ||
226 | } | ||
227 | fclose(passwd); | ||
228 | |||
229 | /* add to shadow if necessary */ | ||
230 | if (shadow_enabled) { | ||
231 | shadow = fopen_wrapper(SHADOW_FILE, "a"); | ||
232 | if (shadow == NULL) { | ||
233 | /* return -1; */ | ||
234 | return 1; | ||
235 | } | ||
236 | fseek(shadow, 0, SEEK_END); | ||
237 | sp = pwd_to_spwd(p); | ||
238 | sp->sp_max = 99999; /* debianish */ | ||
239 | sp->sp_warn = 7; | ||
240 | fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n", | ||
241 | sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max, | ||
242 | sp->sp_warn); | ||
243 | fclose(shadow); | ||
244 | } | ||
245 | |||
246 | /* add to group */ | ||
247 | /* addgroup should be responsible for dealing w/ gshadow */ | ||
248 | addgroup_wrapper(p->pw_name, p->pw_gid); | ||
249 | |||
250 | /* Clear the umask for this process so it doesn't | ||
251 | * * screw up the permissions on the mkdir and chown. */ | ||
252 | umask(0); | ||
253 | |||
254 | /* mkdir */ | ||
255 | if (mkdir(p->pw_dir, 0755)) { | ||
256 | perror_msg("%s", p->pw_dir); | ||
257 | } | ||
258 | /* Set the owner and group so it is owned by the new user. */ | ||
259 | if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) { | ||
260 | perror_msg("%s", p->pw_dir); | ||
261 | } | ||
262 | /* Now fix up the permissions to 2755. Can't do it before now | ||
263 | * since chown will clear the setgid bit */ | ||
264 | if (chmod(p->pw_dir, 02755)) { | ||
265 | perror_msg("%s", p->pw_dir); | ||
266 | } | ||
267 | /* interactively set passwd */ | ||
268 | passwd_wrapper(p->pw_name); | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | |||
274 | /* return current uid (root is always uid == 0, right?) */ | ||
275 | static uid_t i_am_not_root() | ||
276 | { | ||
277 | return geteuid(); | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * adduser will take a login_name as its first parameter. | ||
282 | * | ||
283 | * home | ||
284 | * shell | ||
285 | * gecos | ||
286 | * | ||
287 | * can be customized via command-line parameters. | ||
288 | * ________________________________________________________________________ */ | ||
289 | int adduser_main(int argc, char **argv) | ||
290 | { | ||
291 | int i; | ||
292 | char opt; | ||
293 | char *login; | ||
294 | char *gecos; | ||
295 | char *home = NULL; | ||
296 | char *shell; | ||
297 | char path[MAXPATHLEN]; | ||
298 | |||
299 | struct passwd pw; | ||
300 | |||
301 | /* init */ | ||
302 | if (argc < 2) { | ||
303 | show_usage(); | ||
304 | } | ||
305 | gecos = default_gecos; | ||
306 | shell = default_shell; | ||
307 | |||
308 | /* get args */ | ||
309 | for (i = 1; i < argc; i++) { | ||
310 | if (argv[i][0] == '-') { | ||
311 | opt = argv[i][1]; | ||
312 | switch (opt) { | ||
313 | case 'h': | ||
314 | home = argv[++i]; | ||
315 | break; | ||
316 | case 'g': | ||
317 | gecos = argv[++i]; | ||
318 | break; | ||
319 | case 's': | ||
320 | shell = argv[++i]; | ||
321 | break; | ||
322 | default: | ||
323 | error_msg("invalid option -- %c\n", opt); | ||
324 | break; | ||
325 | } | ||
326 | } else { | ||
327 | break; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | /* got root? */ | ||
332 | if (i_am_not_root()) { | ||
333 | error_msg_and_die( "Only root may add a user or group to the system.\n"); | ||
334 | } | ||
335 | |||
336 | /* get login */ | ||
337 | if (i >= argc) { | ||
338 | error_msg_and_die( "adduser: no user specified\n"); | ||
339 | } | ||
340 | login = argv[i]; | ||
341 | |||
342 | /* create string for $HOME if not specified already */ | ||
343 | if (!home) { | ||
344 | snprintf(path, MAXPATHLEN, "%s/%s", default_home_prefix, login); | ||
345 | path[MAXPATHLEN - 1] = 0; | ||
346 | home = path; | ||
347 | } | ||
348 | /* is /etc/shadow in use? */ | ||
349 | shadow_enabled = (0 == access(SHADOW_FILE, F_OK)); | ||
350 | |||
351 | /* create a passwd struct */ | ||
352 | pw.pw_name = login; | ||
353 | pw.pw_passwd = default_passwd; | ||
354 | pw.pw_uid = 0; | ||
355 | pw.pw_gid = 0; | ||
356 | pw.pw_gecos = gecos; | ||
357 | pw.pw_dir = home; | ||
358 | pw.pw_shell = shell; | ||
359 | |||
360 | /* grand finale */ | ||
361 | i = adduser(PASSWD_FILE, &pw); | ||
362 | |||
363 | return (i); | ||
364 | } | ||
365 | |||
366 | /* $Id: adduser.c,v 1.1 2001/08/21 16:18:59 andersen Exp $ */ | ||