Deleted Added
full compact
pw_user.c (286156) pw_user.c (286196)
1/*-
2 * Copyright (C) 1996
3 * David L. Nugent. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright

--- 13 unchanged lines hidden (view full) ---

22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#ifndef lint
29static const char rcsid[] =
1/*-
2 * Copyright (C) 1996
3 * David L. Nugent. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright

--- 13 unchanged lines hidden (view full) ---

22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#ifndef lint
29static const char rcsid[] =
30 "$FreeBSD: head/usr.sbin/pw/pw_user.c 286156 2015-08-01 12:18:48Z bapt $";
30 "$FreeBSD: head/usr.sbin/pw/pw_user.c 286196 2015-08-02 12:47:50Z bapt $";
31#endif /* not lint */
32
33#include <ctype.h>
34#include <err.h>
35#include <fcntl.h>
36#include <inttypes.h>
37#include <sys/param.h>
38#include <dirent.h>

--- 8 unchanged lines hidden (view full) ---

47#include <libutil.h>
48#include "pw.h"
49#include "bitmap.h"
50
51#define LOGNAMESIZE (MAXLOGNAME-1)
52
53static char locked_str[] = "*LOCKED*";
54
31#endif /* not lint */
32
33#include <ctype.h>
34#include <err.h>
35#include <fcntl.h>
36#include <inttypes.h>
37#include <sys/param.h>
38#include <dirent.h>

--- 8 unchanged lines hidden (view full) ---

47#include <libutil.h>
48#include "pw.h"
49#include "bitmap.h"
50
51#define LOGNAMESIZE (MAXLOGNAME-1)
52
53static char locked_str[] = "*LOCKED*";
54
55static int pw_userdel(char *name, long id);
56static int print_user(struct passwd * pwd);
57static uid_t pw_uidpolicy(struct userconf * cnf, long id);
58static uid_t pw_gidpolicy(struct cargs * args, char *nam, gid_t prefer);
59static time_t pw_pwdpolicy(struct userconf * cnf, struct cargs * args);
60static time_t pw_exppolicy(struct userconf * cnf, struct cargs * args);
61static char *pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user);
62static char *pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell);
63static char *pw_password(struct userconf * cnf, char const * user);
64static char *shell_path(char const * path, char *shells[], char *sh);
65static void rmat(uid_t uid);
66static void rmopie(char const * name);
55static struct passwd fakeuser = {
56 "nouser",
57 "*",
58 -1,
59 -1,
60 0,
61 "",
62 "User &",
63 "/nonexistent",
64 "/bin/sh",
65 0,
66 0
67};
67
68
69static int print_user(struct passwd *pwd, bool pretty, bool v7);
70static uid_t pw_uidpolicy(struct userconf *cnf, intmax_t id);
71static uid_t pw_gidpolicy(struct userconf *cnf, char *grname, char *nam,
72 gid_t prefer, bool dryrun);
73static char *pw_homepolicy(struct userconf * cnf, char *homedir,
74 const char *user);
75static char *pw_shellpolicy(struct userconf * cnf);
76static char *pw_password(struct userconf * cnf, char const * user,
77 bool dryrun);
78static char *shell_path(char const * path, char *shells[], char *sh);
79static void rmat(uid_t uid);
80static void rmopie(char const * name);
81
68static void
82static void
69create_and_populate_homedir(struct passwd *pwd)
83create_and_populate_homedir(struct userconf *cnf, struct passwd *pwd,
84 const char *skeldir, mode_t homemode, bool update)
70{
85{
71 struct userconf *cnf = conf.userconf;
72 const char *skeldir;
73 int skelfd = -1;
74
86 int skelfd = -1;
87
75 skeldir = cnf->dotdir;
76
77 if (skeldir != NULL && *skeldir != '\0') {
78 if (*skeldir == '/')
79 skeldir++;
80 skelfd = openat(conf.rootfd, skeldir, O_DIRECTORY|O_CLOEXEC);
81 }
82
88 if (skeldir != NULL && *skeldir != '\0') {
89 if (*skeldir == '/')
90 skeldir++;
91 skelfd = openat(conf.rootfd, skeldir, O_DIRECTORY|O_CLOEXEC);
92 }
93
83 copymkdir(conf.rootfd, pwd->pw_dir, skelfd, cnf->homemode, pwd->pw_uid,
94 copymkdir(conf.rootfd, pwd->pw_dir, skelfd, homemode, pwd->pw_uid,
84 pwd->pw_gid, 0);
95 pwd->pw_gid, 0);
85 pw_log(cnf, M_ADD, W_USER, "%s(%ju) home %s made", pwd->pw_name,
86 (uintmax_t)pwd->pw_uid, pwd->pw_dir);
96 pw_log(cnf, update ? M_UPDATE : M_ADD, W_USER, "%s(%ju) home %s made",
97 pwd->pw_name, (uintmax_t)pwd->pw_uid, pwd->pw_dir);
87}
88
89static int
98}
99
100static int
90set_passwd(struct passwd *pwd, bool update)
101pw_set_passwd(struct passwd *pwd, int fd, bool precrypted, bool update)
91{
92 int b, istty;
93 struct termios t, n;
94 login_cap_t *lc;
95 char line[_PASSWORD_LEN+1];
96 char *p;
97
102{
103 int b, istty;
104 struct termios t, n;
105 login_cap_t *lc;
106 char line[_PASSWORD_LEN+1];
107 char *p;
108
98 if (conf.fd == '-') {
109 if (fd == '-') {
99 if (!pwd->pw_passwd || *pwd->pw_passwd != '*') {
100 pwd->pw_passwd = "*"; /* No access */
101 return (1);
102 }
103 return (0);
104 }
105
110 if (!pwd->pw_passwd || *pwd->pw_passwd != '*') {
111 pwd->pw_passwd = "*"; /* No access */
112 return (1);
113 }
114 return (0);
115 }
116
106 if ((istty = isatty(conf.fd))) {
107 if (tcgetattr(conf.fd, &t) == -1)
117 if ((istty = isatty(fd))) {
118 if (tcgetattr(fd, &t) == -1)
108 istty = 0;
109 else {
110 n = t;
111 n.c_lflag &= ~(ECHO);
119 istty = 0;
120 else {
121 n = t;
122 n.c_lflag &= ~(ECHO);
112 tcsetattr(conf.fd, TCSANOW, &n);
123 tcsetattr(fd, TCSANOW, &n);
113 printf("%s%spassword for user %s:",
114 update ? "new " : "",
124 printf("%s%spassword for user %s:",
125 update ? "new " : "",
115 conf.precrypted ? "encrypted " : "",
126 precrypted ? "encrypted " : "",
116 pwd->pw_name);
117 fflush(stdout);
118 }
119 }
127 pwd->pw_name);
128 fflush(stdout);
129 }
130 }
120 b = read(conf.fd, line, sizeof(line) - 1);
131 b = read(fd, line, sizeof(line) - 1);
121 if (istty) { /* Restore state */
132 if (istty) { /* Restore state */
122 tcsetattr(conf.fd, TCSANOW, &t);
133 tcsetattr(fd, TCSANOW, &t);
123 fputc('\n', stdout);
124 fflush(stdout);
125 }
126
127 if (b < 0)
128 err(EX_IOERR, "-%c file descriptor",
134 fputc('\n', stdout);
135 fflush(stdout);
136 }
137
138 if (b < 0)
139 err(EX_IOERR, "-%c file descriptor",
129 conf.precrypted ? 'H' : 'h');
140 precrypted ? 'H' : 'h');
130 line[b] = '\0';
131 if ((p = strpbrk(line, "\r\n")) != NULL)
132 *p = '\0';
133 if (!*line)
134 errx(EX_DATAERR, "empty password read on file descriptor %d",
141 line[b] = '\0';
142 if ((p = strpbrk(line, "\r\n")) != NULL)
143 *p = '\0';
144 if (!*line)
145 errx(EX_DATAERR, "empty password read on file descriptor %d",
135 conf.fd);
136 if (conf.precrypted) {
146 fd);
147 if (precrypted) {
137 if (strchr(line, ':') != NULL)
138 errx(EX_DATAERR, "bad encrypted password");
148 if (strchr(line, ':') != NULL)
149 errx(EX_DATAERR, "bad encrypted password");
139 pwd->pw_passwd = line;
150 pwd->pw_passwd = strdup(line);
140 } else {
141 lc = login_getpwclass(pwd);
142 if (lc == NULL ||
143 login_setcryptfmt(lc, "sha512", NULL) == NULL)
144 warn("setting crypt(3) format");
145 login_close(lc);
146 pwd->pw_passwd = pw_pwcrypt(line);
147 }
148 return (1);
149}
150
151 } else {
152 lc = login_getpwclass(pwd);
153 if (lc == NULL ||
154 login_setcryptfmt(lc, "sha512", NULL) == NULL)
155 warn("setting crypt(3) format");
156 login_close(lc);
157 pwd->pw_passwd = pw_pwcrypt(line);
158 }
159 return (1);
160}
161
151int
152pw_usernext(struct userconf *cnf, bool quiet)
153{
154 uid_t next = pw_uidpolicy(cnf, -1);
155
156 if (quiet)
157 return (next);
158
159 printf("%ju:", (uintmax_t)next);
160 pw_groupnext(cnf, quiet);
161
162 return (EXIT_SUCCESS);
163}
164
165static int
166pw_usershow(char *name, long id, struct passwd *fakeuser)
167{
168 struct passwd *pwd = NULL;
169
170 if (id < 0 && name == NULL && !conf.all)
171 errx(EX_DATAERR, "username or id or '-a' required");
172
173 if (conf.all) {
174 SETPWENT();
175 while ((pwd = GETPWENT()) != NULL)
176 print_user(pwd);
177 ENDPWENT();
178 return (EXIT_SUCCESS);
179 }
180
181 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
182 if (pwd == NULL) {
183 if (conf.force) {
184 pwd = fakeuser;
185 } else {
186 if (name == NULL)
187 errx(EX_NOUSER, "no such uid `%ld'", id);
188 errx(EX_NOUSER, "no such user `%s'", name);
189 }
190 }
191
192 return (print_user(pwd));
193}
194
195static void
162static void
196perform_chgpwent(const char *name, struct passwd *pwd)
163perform_chgpwent(const char *name, struct passwd *pwd, char *nispasswd)
197{
198 int rc;
164{
165 int rc;
166 struct passwd *nispwd;
199
167
168 /* duplicate for nis so that chgpwent is not modifying before NIS */
169 if (nispasswd && *nispasswd == '/')
170 nispwd = pw_dup(pwd);
171
200 rc = chgpwent(name, pwd);
201 if (rc == -1)
202 errx(EX_IOERR, "user '%s' does not exist (NIS?)", pwd->pw_name);
203 else if (rc != 0)
204 err(EX_IOERR, "passwd file update");
205
172 rc = chgpwent(name, pwd);
173 if (rc == -1)
174 errx(EX_IOERR, "user '%s' does not exist (NIS?)", pwd->pw_name);
175 else if (rc != 0)
176 err(EX_IOERR, "passwd file update");
177
206 if (conf.userconf->nispasswd && *conf.userconf->nispasswd == '/') {
207 rc = chgnispwent(conf.userconf->nispasswd, name, pwd);
178 if (nispasswd && *nispasswd == '/') {
179 rc = chgnispwent(nispasswd, name, nispwd);
208 if (rc == -1)
209 warn("User '%s' not found in NIS passwd", pwd->pw_name);
210 else if (rc != 0)
211 warn("NIS passwd update");
212 /* NOTE: NIS-only update errors are not fatal */
213 }
214}
215
216/*
217 * The M_LOCK and M_UNLOCK functions simply add or remove
218 * a "*LOCKED*" prefix from in front of the password to
219 * prevent it decoding correctly, and therefore prevents
220 * access. Of course, this only prevents access via
221 * password authentication (not ssh, kerberos or any
222 * other method that does not use the UNIX password) but
223 * that is a known limitation.
224 */
225static int
180 if (rc == -1)
181 warn("User '%s' not found in NIS passwd", pwd->pw_name);
182 else if (rc != 0)
183 warn("NIS passwd update");
184 /* NOTE: NIS-only update errors are not fatal */
185 }
186}
187
188/*
189 * The M_LOCK and M_UNLOCK functions simply add or remove
190 * a "*LOCKED*" prefix from in front of the password to
191 * prevent it decoding correctly, and therefore prevents
192 * access. Of course, this only prevents access via
193 * password authentication (not ssh, kerberos or any
194 * other method that does not use the UNIX password) but
195 * that is a known limitation.
196 */
197static int
226pw_userlock(char *name, long id, int mode)
198pw_userlock(char *arg1, int mode)
227{
228 struct passwd *pwd = NULL;
229 char *passtmp = NULL;
199{
200 struct passwd *pwd = NULL;
201 char *passtmp = NULL;
202 char *name;
230 bool locked = false;
203 bool locked = false;
204 uid_t id;
231
205
232 if (id < 0 && name == NULL)
206 if (geteuid() != 0)
207 errx(EX_NOPERM, "you must be root");
208
209 if (arg1 == NULL)
233 errx(EX_DATAERR, "username or id required");
234
210 errx(EX_DATAERR, "username or id required");
211
212 if (strspn(arg1, "0123456789") == strlen(arg1))
213 id = pw_checkid(arg1, UID_MAX);
214 else
215 name = arg1;
216
235 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
236 if (pwd == NULL) {
237 if (name == NULL)
217 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
218 if (pwd == NULL) {
219 if (name == NULL)
238 errx(EX_NOUSER, "no such uid `%ld'", id);
220 errx(EX_NOUSER, "no such uid `%ju'", (uintmax_t) id);
239 errx(EX_NOUSER, "no such user `%s'", name);
240 }
241
242 if (name == NULL)
243 name = pwd->pw_name;
244
245 if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str) -1) == 0)
246 locked = true;

--- 6 unchanged lines hidden (view full) ---

253 asprintf(&passtmp, "%s%s", locked_str, pwd->pw_passwd);
254 if (passtmp == NULL) /* disaster */
255 errx(EX_UNAVAILABLE, "out of memory");
256 pwd->pw_passwd = passtmp;
257 } else {
258 pwd->pw_passwd += sizeof(locked_str)-1;
259 }
260
221 errx(EX_NOUSER, "no such user `%s'", name);
222 }
223
224 if (name == NULL)
225 name = pwd->pw_name;
226
227 if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str) -1) == 0)
228 locked = true;

--- 6 unchanged lines hidden (view full) ---

235 asprintf(&passtmp, "%s%s", locked_str, pwd->pw_passwd);
236 if (passtmp == NULL) /* disaster */
237 errx(EX_UNAVAILABLE, "out of memory");
238 pwd->pw_passwd = passtmp;
239 } else {
240 pwd->pw_passwd += sizeof(locked_str)-1;
241 }
242
261 perform_chgpwent(name, pwd);
243 perform_chgpwent(name, pwd, NULL);
262 free(passtmp);
263
264 return (EXIT_SUCCESS);
265}
266
244 free(passtmp);
245
246 return (EXIT_SUCCESS);
247}
248
267/*-
268 * -C config configuration file
269 * -q quiet operation
270 * -n name login name
271 * -u uid user id
272 * -c comment user name/comment
273 * -d directory home directory
274 * -e date account expiry date
275 * -p date password expiry date
276 * -g grp primary group
277 * -G grp1,grp2 additional groups
278 * -m [ -k dir ] create and set up home
279 * -s shell name of login shell
280 * -o duplicate uid ok
281 * -L class user class
282 * -l name new login name
283 * -h fd password filehandle
284 * -H fd encrypted password filehandle
285 * -F force print or add
286 * Setting defaults:
287 * -D set user defaults
288 * -b dir default home root dir
289 * -e period default expiry period
290 * -p period default password change period
291 * -g group default group
292 * -G grp1,grp2.. default additional groups
293 * -L class default login class
294 * -k dir default home skeleton
295 * -s shell default shell
296 * -w method default password method
297 */
298
299int
300pw_user(int mode, char *name, long id, struct cargs * args)
249static uid_t
250pw_uidpolicy(struct userconf * cnf, intmax_t id)
301{
251{
302 int rc, edited = 0;
303 char *p = NULL;
304 struct carg *arg;
305 struct passwd *pwd = NULL;
306 struct group *grp;
307 struct stat st;
308 struct userconf *cnf;
309 char line[_PASSWORD_LEN+1];
310 char path[MAXPATHLEN];
311 FILE *fp;
312 char *dmode_c;
313 void *set = NULL;
314 int valid_type = _PWF_FILES;
252 struct passwd *pwd;
253 struct bitmap bm;
254 uid_t uid = (uid_t) - 1;
315
255
316 static struct passwd fakeuser =
317 {
318 "nouser",
319 "*",
320 -1,
321 -1,
322 0,
323 "",
324 "User &",
325 "/nonexistent",
326 "/bin/sh",
327 0
328#if defined(__FreeBSD__)
329 ,0
330#endif
331 };
332
333 cnf = conf.userconf;
334
335 if (mode == M_NEXT)
336 return (pw_usernext(cnf, conf.quiet));
337
338 if (mode == M_PRINT)
339 return (pw_usershow(name, id, &fakeuser));
340
341 if (mode == M_DELETE)
342 return (pw_userdel(name, id));
343
344 if (mode == M_LOCK || mode == M_UNLOCK)
345 return (pw_userlock(name, id, mode));
346
347 /*
256 /*
348 * We can do all of the common legwork here
257 * Check the given uid, if any
349 */
258 */
259 if (id >= 0) {
260 uid = (uid_t) id;
350
261
351 if ((arg = getarg(args, 'b')) != NULL) {
352 cnf->home = arg->val;
262 if ((pwd = GETPWUID(uid)) != NULL && conf.checkduplicate)
263 errx(EX_DATAERR, "uid `%ju' has already been allocated",
264 (uintmax_t)pwd->pw_uid);
265 return (uid);
353 }
266 }
354
355 if ((arg = getarg(args, 'M')) != NULL) {
356 dmode_c = arg->val;
357 if ((set = setmode(dmode_c)) == NULL)
358 errx(EX_DATAERR, "invalid directory creation mode '%s'",
359 dmode_c);
360 cnf->homemode = getmode(set, _DEF_DIRMODE);
361 free(set);
362 }
363
364 /*
267 /*
365 * If we'll need to use it or we're updating it,
366 * then create the base home directory if necessary
268 * We need to allocate the next available uid under one of
269 * two policies a) Grab the first unused uid b) Grab the
270 * highest possible unused uid
367 */
271 */
368 if (arg != NULL || getarg(args, 'm') != NULL) {
369 int l = strlen(cnf->home);
370
371 if (l > 1 && cnf->home[l-1] == '/') /* Shave off any trailing path delimiter */
372 cnf->home[--l] = '\0';
373
374 if (l < 2 || *cnf->home != '/') /* Check for absolute path name */
375 errx(EX_DATAERR, "invalid base directory for home '%s'", cnf->home);
376
377 if (stat(cnf->home, &st) == -1) {
378 char dbuf[MAXPATHLEN];
379
380 /*
381 * This is a kludge especially for Joerg :)
382 * If the home directory would be created in the root partition, then
383 * we really create it under /usr which is likely to have more space.
384 * But we create a symlink from cnf->home -> "/usr" -> cnf->home
385 */
386 if (strchr(cnf->home+1, '/') == NULL) {
387 snprintf(dbuf, MAXPATHLEN, "/usr%s", cnf->home);
388 if (mkdir(dbuf, _DEF_DIRMODE) != -1 || errno == EEXIST) {
389 chown(dbuf, 0, 0);
390 /*
391 * Skip first "/" and create symlink:
392 * /home -> usr/home
393 */
394 symlink(dbuf+1, cnf->home);
395 }
396 /* If this falls, fall back to old method */
397 }
398 strlcpy(dbuf, cnf->home, sizeof(dbuf));
399 p = dbuf;
400 if (stat(dbuf, &st) == -1) {
401 while ((p = strchr(p + 1, '/')) != NULL) {
402 *p = '\0';
403 if (stat(dbuf, &st) == -1) {
404 if (mkdir(dbuf, _DEF_DIRMODE) == -1)
405 err(EX_OSFILE, "mkdir '%s'", dbuf);
406 chown(dbuf, 0, 0);
407 } else if (!S_ISDIR(st.st_mode))
408 errx(EX_OSFILE, "'%s' (root home parent) is not a directory", dbuf);
409 *p = '/';
410 }
411 }
412 if (stat(dbuf, &st) == -1) {
413 if (mkdir(dbuf, _DEF_DIRMODE) == -1)
414 err(EX_OSFILE, "mkdir '%s'", dbuf);
415 chown(dbuf, 0, 0);
416 }
417 } else if (!S_ISDIR(st.st_mode))
418 errx(EX_OSFILE, "root home `%s' is not a directory", cnf->home);
272 if (cnf->min_uid >= cnf->max_uid) { /* Sanity
273 * claus^H^H^H^Hheck */
274 cnf->min_uid = 1000;
275 cnf->max_uid = 32000;
419 }
276 }
277 bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1);
420
278
421 if ((arg = getarg(args, 'e')) != NULL)
422 cnf->expire_days = atoi(arg->val);
423
424 if ((arg = getarg(args, 'y')) != NULL)
425 cnf->nispasswd = arg->val;
426
427 if ((arg = getarg(args, 'p')) != NULL && arg->val)
428 cnf->password_days = atoi(arg->val);
429
430 if ((arg = getarg(args, 'g')) != NULL) {
431 if (!*(p = arg->val)) /* Handle empty group list specially */
432 cnf->default_group = "";
433 else {
434 if ((grp = GETGRNAM(p)) == NULL) {
435 if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL)
436 errx(EX_NOUSER, "group `%s' does not exist", p);
437 }
438 cnf->default_group = newstr(grp->gr_name);
439 }
440 }
441 if ((arg = getarg(args, 'L')) != NULL)
442 cnf->default_class = pw_checkname(arg->val, 0);
443
444 if ((arg = getarg(args, 'G')) != NULL && arg->val) {
445 for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
446 if ((grp = GETGRNAM(p)) == NULL) {
447 if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL)
448 errx(EX_NOUSER, "group `%s' does not exist", p);
449 }
450 sl_add(cnf->groups, newstr(grp->gr_name));
451 }
452 }
453
454 if ((arg = getarg(args, 'k')) != NULL) {
455 char *tmp = cnf->dotdir = arg->val;
456 if (*tmp == '/')
457 tmp++;
458 if ((fstatat(conf.rootfd, tmp, &st, 0) == -1) ||
459 !S_ISDIR(st.st_mode))
460 errx(EX_OSFILE, "skeleton `%s' is not a directory or "
461 "does not exist", cnf->dotdir);
462 }
463
464 if ((arg = getarg(args, 's')) != NULL)
465 cnf->shell_default = arg->val;
466
467 if ((arg = getarg(args, 'w')) != NULL)
468 cnf->default_password = boolean_val(arg->val, cnf->default_password);
469 if (mode == M_ADD && getarg(args, 'D')) {
470 if (name != NULL)
471 errx(EX_DATAERR, "can't combine `-D' with `-n name'");
472 if ((arg = getarg(args, 'u')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
473 if ((cnf->min_uid = (uid_t) atoi(p)) == 0)
474 cnf->min_uid = 1000;
475 if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_uid = (uid_t) atoi(p)) < cnf->min_uid)
476 cnf->max_uid = 32000;
477 }
478 if ((arg = getarg(args, 'i')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
479 if ((cnf->min_gid = (gid_t) atoi(p)) == 0)
480 cnf->min_gid = 1000;
481 if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_gid = (gid_t) atoi(p)) < cnf->min_gid)
482 cnf->max_gid = 32000;
483 }
484
485 if (write_userconfig(conf.config))
486 return (EXIT_SUCCESS);
487 err(EX_IOERR, "config udpate");
488 }
489
490 if (name != NULL)
491 pwd = GETPWNAM(pw_checkname(name, 0));
492
493 if (id < 0 && name == NULL)
494 errx(EX_DATAERR, "user name or id required");
495
496 /*
279 /*
497 * Update require that the user exists
280 * Now, let's fill the bitmap from the password file
498 */
281 */
499 if (mode == M_UPDATE) {
282 SETPWENT();
283 while ((pwd = GETPWENT()) != NULL)
284 if (pwd->pw_uid >= (uid_t) cnf->min_uid && pwd->pw_uid <= (uid_t) cnf->max_uid)
285 bm_setbit(&bm, pwd->pw_uid - cnf->min_uid);
286 ENDPWENT();
500
287
501 if (name == NULL && pwd == NULL) /* Try harder */
502 pwd = GETPWUID(id);
503
504 if (pwd == NULL) {
505 if (name == NULL)
506 errx(EX_NOUSER, "no such uid `%ld'", id);
507 errx(EX_NOUSER, "no such user `%s'", name);
508 }
509
510 if (conf.userconf->nispasswd && *conf.userconf->nispasswd == '/')
511 valid_type = _PWF_NIS;
512
513 if (PWF._altdir == PWF_REGULAR &&
514 ((pwd->pw_fields & _PWF_SOURCE) != valid_type))
515 errx(EX_NOUSER, "no such %s user `%s'",
516 valid_type == _PWF_FILES ? "local" : "NIS" , name);
517
518 if (name == NULL)
519 name = pwd->pw_name;
520
521 /*
522 * The rest is edit code
523 */
524 if (conf.newname != NULL) {
525 if (strcmp(pwd->pw_name, "root") == 0)
526 errx(EX_DATAERR, "can't rename `root' account");
527 pwd->pw_name = pw_checkname(conf.newname, 0);
528 edited = 1;
529 }
530
531 if (id > 0 && isdigit((unsigned char)*arg->val)) {
532 pwd->pw_uid = (uid_t)id;
533 edited = 1;
534 if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0)
535 errx(EX_DATAERR, "can't change uid of `root' account");
536 if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
537 warnx("WARNING: account `%s' will have a uid of 0 (superuser access!)", pwd->pw_name);
538 }
539
540 if ((arg = getarg(args, 'g')) != NULL && pwd->pw_uid != 0) { /* Already checked this */
541 gid_t newgid = (gid_t) GETGRNAM(cnf->default_group)->gr_gid;
542 if (newgid != pwd->pw_gid) {
543 edited = 1;
544 pwd->pw_gid = newgid;
545 }
546 }
547
548 if ((arg = getarg(args, 'p')) != NULL) {
549 if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) {
550 if (pwd->pw_change != 0) {
551 pwd->pw_change = 0;
552 edited = 1;
553 }
554 }
555 else {
556 time_t now = time(NULL);
557 time_t expire = parse_date(now, arg->val);
558
559 if (pwd->pw_change != expire) {
560 pwd->pw_change = expire;
561 edited = 1;
562 }
563 }
564 }
565
566 if ((arg = getarg(args, 'e')) != NULL) {
567 if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) {
568 if (pwd->pw_expire != 0) {
569 pwd->pw_expire = 0;
570 edited = 1;
571 }
572 }
573 else {
574 time_t now = time(NULL);
575 time_t expire = parse_date(now, arg->val);
576
577 if (pwd->pw_expire != expire) {
578 pwd->pw_expire = expire;
579 edited = 1;
580 }
581 }
582 }
583
584 if ((arg = getarg(args, 's')) != NULL) {
585 char *shell = shell_path(cnf->shelldir, cnf->shells, arg->val);
586 if (shell == NULL)
587 shell = "";
588 if (strcmp(shell, pwd->pw_shell) != 0) {
589 pwd->pw_shell = shell;
590 edited = 1;
591 }
592 }
593
594 if (getarg(args, 'L')) {
595 if (cnf->default_class == NULL)
596 cnf->default_class = "";
597 if (strcmp(pwd->pw_class, cnf->default_class) != 0) {
598 pwd->pw_class = cnf->default_class;
599 edited = 1;
600 }
601 }
602
603 if ((arg = getarg(args, 'd')) != NULL) {
604 if (strcmp(pwd->pw_dir, arg->val))
605 edited = 1;
606 if (stat(pwd->pw_dir = arg->val, &st) == -1) {
607 if (getarg(args, 'm') == NULL && strcmp(pwd->pw_dir, "/nonexistent") != 0)
608 warnx("WARNING: home `%s' does not exist", pwd->pw_dir);
609 } else if (!S_ISDIR(st.st_mode))
610 warnx("WARNING: home `%s' is not a directory", pwd->pw_dir);
611 }
612
613 if ((arg = getarg(args, 'w')) != NULL && conf.fd == -1) {
614 login_cap_t *lc;
615
616 lc = login_getpwclass(pwd);
617 if (lc == NULL ||
618 login_setcryptfmt(lc, "sha512", NULL) == NULL)
619 warn("setting crypt(3) format");
620 login_close(lc);
621 pwd->pw_passwd = pw_password(cnf, pwd->pw_name);
622 edited = 1;
623 }
624
625 } else {
626 login_cap_t *lc;
627
628 /*
629 * Add code
630 */
631
632 if (name == NULL) /* Required */
633 errx(EX_DATAERR, "login name required");
634 else if ((pwd = GETPWNAM(name)) != NULL) /* Exists */
635 errx(EX_DATAERR, "login name `%s' already exists", name);
636
637 /*
638 * Now, set up defaults for a new user
639 */
640 pwd = &fakeuser;
641 pwd->pw_name = name;
642 pwd->pw_class = cnf->default_class ? cnf->default_class : "";
643 pwd->pw_uid = pw_uidpolicy(cnf, id);
644 pwd->pw_gid = pw_gidpolicy(args, pwd->pw_name, (gid_t) pwd->pw_uid);
645 pwd->pw_change = pw_pwdpolicy(cnf, args);
646 pwd->pw_expire = pw_exppolicy(cnf, args);
647 pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name);
648 pwd->pw_shell = pw_shellpolicy(cnf, args, NULL);
649 lc = login_getpwclass(pwd);
650 if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL)
651 warn("setting crypt(3) format");
652 login_close(lc);
653 pwd->pw_passwd = pw_password(cnf, pwd->pw_name);
654 edited = 1;
655
656 if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
657 warnx("WARNING: new account `%s' has a uid of 0 (superuser access!)", pwd->pw_name);
658 }
659
660 /*
288 /*
661 * Shared add/edit code
289 * Then apply the policy, with fallback to reuse if necessary
662 */
290 */
663 if (conf.gecos != NULL) {
664 if (strcmp(pwd->pw_gecos, conf.gecos) != 0) {
665 pwd->pw_gecos = conf.gecos;
666 edited = 1;
667 }
668 }
291 if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid)
292 uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid);
669
293
670 if (conf.fd != -1)
671 edited = set_passwd(pwd, mode == M_UPDATE);
672
673 /*
294 /*
674 * Special case: -N only displays & exits
295 * Another sanity check
675 */
296 */
676 if (conf.dryrun)
677 return print_user(pwd);
678
679 if (mode == M_ADD) {
680 edited = 1; /* Always */
681 rc = addpwent(pwd);
682 if (rc == -1)
683 errx(EX_IOERR, "user '%s' already exists",
684 pwd->pw_name);
685 else if (rc != 0)
686 err(EX_IOERR, "passwd file update");
687 if (cnf->nispasswd && *cnf->nispasswd=='/') {
688 rc = addnispwent(cnf->nispasswd, pwd);
689 if (rc == -1)
690 warnx("User '%s' already exists in NIS passwd", pwd->pw_name);
691 else if (rc != 0)
692 warn("NIS passwd update");
693 /* NOTE: we treat NIS-only update errors as non-fatal */
694 }
695 } else if (mode == M_UPDATE && edited) /* Only updated this if required */
696 perform_chgpwent(name, pwd);
697
698 /*
699 * Ok, user is created or changed - now edit group file
700 */
701
702 if (mode == M_ADD || getarg(args, 'G') != NULL) {
703 int j;
704 size_t i;
705 /* First remove the user from all group */
706 SETGRENT();
707 while ((grp = GETGRENT()) != NULL) {
708 char group[MAXLOGNAME];
709 if (grp->gr_mem == NULL)
710 continue;
711 for (i = 0; grp->gr_mem[i] != NULL; i++) {
712 if (strcmp(grp->gr_mem[i] , pwd->pw_name) != 0)
713 continue;
714 for (j = i; grp->gr_mem[j] != NULL ; j++)
715 grp->gr_mem[j] = grp->gr_mem[j+1];
716 strlcpy(group, grp->gr_name, MAXLOGNAME);
717 chggrent(group, grp);
718 }
719 }
720 ENDGRENT();
721
722 /* now add to group where needed */
723 for (i = 0; i < cnf->groups->sl_cur; i++) {
724 grp = GETGRNAM(cnf->groups->sl_str[i]);
725 grp = gr_add(grp, pwd->pw_name);
726 /*
727 * grp can only be NULL in 2 cases:
728 * - the new member is already a member
729 * - a problem with memory occurs
730 * in both cases we want to skip now.
731 */
732 if (grp == NULL)
733 continue;
734 chggrent(grp->gr_name, grp);
735 free(grp);
736 }
737 }
738
739
740 /* go get a current version of pwd */
741 pwd = GETPWNAM(name);
742 if (pwd == NULL) {
743 /* This will fail when we rename, so special case that */
744 if (mode == M_UPDATE && conf.newname != NULL) {
745 name = conf.newname; /* update new name */
746 pwd = GETPWNAM(name); /* refetch renamed rec */
747 }
748 }
749 if (pwd == NULL) /* can't go on without this */
750 errx(EX_NOUSER, "user '%s' disappeared during update", name);
751
752 grp = GETGRGID(pwd->pw_gid);
753 pw_log(cnf, mode, W_USER, "%s(%ju):%s(%ju):%s:%s:%s",
754 pwd->pw_name, (uintmax_t)pwd->pw_uid,
755 grp ? grp->gr_name : "unknown", (uintmax_t)(grp ? grp->gr_gid : (uid_t)-1),
756 pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
757
758 /*
759 * If adding, let's touch and chown the user's mail file. This is not
760 * strictly necessary under BSD with a 0755 maildir but it also
761 * doesn't hurt anything to create the empty mailfile
762 */
763 if (mode == M_ADD) {
764 if (PWALTDIR() != PWF_ALT) {
765 snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR,
766 pwd->pw_name);
767 close(openat(conf.rootfd, path +1, O_RDWR | O_CREAT,
768 0600)); /* Preserve contents & mtime */
769 fchownat(conf.rootfd, path + 1, pwd->pw_uid,
770 pwd->pw_gid, AT_SYMLINK_NOFOLLOW);
771 }
772 }
773
774 /*
775 * Let's create and populate the user's home directory. Note
776 * that this also `works' for editing users if -m is used, but
777 * existing files will *not* be overwritten.
778 */
779 if (PWALTDIR() != PWF_ALT && getarg(args, 'm') != NULL && pwd->pw_dir &&
780 *pwd->pw_dir == '/' && pwd->pw_dir[1])
781 create_and_populate_homedir(pwd);
782
783 /*
784 * Finally, send mail to the new user as well, if we are asked to
785 */
786 if (mode == M_ADD && !PWALTDIR() && cnf->newmail && *cnf->newmail && (fp = fopen(cnf->newmail, "r")) != NULL) {
787 FILE *pfp = popen(_PATH_SENDMAIL " -t", "w");
788
789 if (pfp == NULL)
790 warn("sendmail");
791 else {
792 fprintf(pfp, "From: root\n" "To: %s\n" "Subject: Welcome!\n\n", pwd->pw_name);
793 while (fgets(line, sizeof(line), fp) != NULL) {
794 /* Do substitutions? */
795 fputs(line, pfp);
796 }
797 pclose(pfp);
798 pw_log(cnf, mode, W_USER, "%s(%ju) new user mail sent",
799 pwd->pw_name, (uintmax_t)pwd->pw_uid);
800 }
801 fclose(fp);
802 }
803
804 return EXIT_SUCCESS;
297 if (uid < cnf->min_uid || uid > cnf->max_uid)
298 errx(EX_SOFTWARE, "unable to allocate a new uid - range fully used");
299 bm_dealloc(&bm);
300 return (uid);
805}
806
301}
302
807
808static uid_t
809pw_uidpolicy(struct userconf * cnf, long id)
303static uid_t
304pw_gidpolicy(struct userconf *cnf, char *grname, char *nam, gid_t prefer, bool dryrun)
810{
305{
811 struct passwd *pwd;
812 uid_t uid = (uid_t) - 1;
813
814 /*
815 * Check the given uid, if any
816 */
817 if (id >= 0) {
818 uid = (uid_t) id;
819
820 if ((pwd = GETPWUID(uid)) != NULL && conf.checkduplicate)
821 errx(EX_DATAERR, "uid `%ju' has already been allocated",
822 (uintmax_t)pwd->pw_uid);
823 } else {
824 struct bitmap bm;
825
826 /*
827 * We need to allocate the next available uid under one of
828 * two policies a) Grab the first unused uid b) Grab the
829 * highest possible unused uid
830 */
831 if (cnf->min_uid >= cnf->max_uid) { /* Sanity
832 * claus^H^H^H^Hheck */
833 cnf->min_uid = 1000;
834 cnf->max_uid = 32000;
835 }
836 bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1);
837
838 /*
839 * Now, let's fill the bitmap from the password file
840 */
841 SETPWENT();
842 while ((pwd = GETPWENT()) != NULL)
843 if (pwd->pw_uid >= (uid_t) cnf->min_uid && pwd->pw_uid <= (uid_t) cnf->max_uid)
844 bm_setbit(&bm, pwd->pw_uid - cnf->min_uid);
845 ENDPWENT();
846
847 /*
848 * Then apply the policy, with fallback to reuse if necessary
849 */
850 if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid)
851 uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid);
852
853 /*
854 * Another sanity check
855 */
856 if (uid < cnf->min_uid || uid > cnf->max_uid)
857 errx(EX_SOFTWARE, "unable to allocate a new uid - range fully used");
858 bm_dealloc(&bm);
859 }
860 return uid;
861}
862
863
864static uid_t
865pw_gidpolicy(struct cargs * args, char *nam, gid_t prefer)
866{
867 struct group *grp;
868 gid_t gid = (uid_t) - 1;
306 struct group *grp;
307 gid_t gid = (uid_t) - 1;
869 struct carg *a_gid = getarg(args, 'g');
870 struct userconf *cnf = conf.userconf;
871
872 /*
308
309 /*
873 * If no arg given, see if default can help out
874 */
875 if (a_gid == NULL && cnf->default_group && *cnf->default_group)
876 a_gid = addarg(args, 'g', cnf->default_group);
877
878 /*
879 * Check the given gid, if any
880 */
881 SETGRENT();
310 * Check the given gid, if any
311 */
312 SETGRENT();
882 if (a_gid != NULL) {
883 if ((grp = GETGRNAM(a_gid->val)) == NULL) {
884 gid = (gid_t) atol(a_gid->val);
885 if ((gid == 0 && !isdigit((unsigned char)*a_gid->val)) || (grp = GETGRGID(gid)) == NULL)
886 errx(EX_NOUSER, "group `%s' is not defined", a_gid->val);
313 if (grname) {
314 if ((grp = GETGRNAM(grname)) == NULL) {
315 gid = pw_checkid(grname, GID_MAX);
316 grp = GETGRGID(gid);
887 }
888 gid = grp->gr_gid;
889 } else if ((grp = GETGRNAM(nam)) != NULL &&
890 (grp->gr_mem == NULL || grp->gr_mem[0] == NULL)) {
891 gid = grp->gr_gid; /* Already created? Use it anyway... */
892 } else {
317 }
318 gid = grp->gr_gid;
319 } else if ((grp = GETGRNAM(nam)) != NULL &&
320 (grp->gr_mem == NULL || grp->gr_mem[0] == NULL)) {
321 gid = grp->gr_gid; /* Already created? Use it anyway... */
322 } else {
893 gid_t grid = -1;
323 intmax_t grid = -1;
894
895 /*
896 * We need to auto-create a group with the user's name. We
897 * can send all the appropriate output to our sister routine
898 * bit first see if we can create a group with gid==uid so we
899 * can keep the user and group ids in sync. We purposely do
900 * NOT check the gid range if we can force the sync. If the
901 * user's name dups an existing group, then the group add
902 * function will happily handle that case for us and exit.
903 */
904 if (GETGRGID(prefer) == NULL)
905 grid = prefer;
324
325 /*
326 * We need to auto-create a group with the user's name. We
327 * can send all the appropriate output to our sister routine
328 * bit first see if we can create a group with gid==uid so we
329 * can keep the user and group ids in sync. We purposely do
330 * NOT check the gid range if we can force the sync. If the
331 * user's name dups an existing group, then the group add
332 * function will happily handle that case for us and exit.
333 */
334 if (GETGRGID(prefer) == NULL)
335 grid = prefer;
906 if (conf.dryrun) {
336 if (dryrun) {
907 gid = pw_groupnext(cnf, true);
908 } else {
337 gid = pw_groupnext(cnf, true);
338 } else {
909 pw_group(M_ADD, nam, grid, NULL);
339 if (grid == -1)
340 grid = pw_groupnext(cnf, true);
341 groupadd(cnf, nam, grid, NULL, -1, false, false, false);
910 if ((grp = GETGRNAM(nam)) != NULL)
911 gid = grp->gr_gid;
912 }
913 }
914 ENDGRENT();
342 if ((grp = GETGRNAM(nam)) != NULL)
343 gid = grp->gr_gid;
344 }
345 }
346 ENDGRENT();
915 return gid;
347 return (gid);
916}
917
348}
349
918
919static time_t
920pw_pwdpolicy(struct userconf * cnf, struct cargs * args)
350static char *
351pw_homepolicy(struct userconf * cnf, char *homedir, const char *user)
921{
352{
922 time_t result = 0;
923 time_t now = time(NULL);
924 struct carg *arg = getarg(args, 'p');
925
926 if (arg != NULL) {
927 if ((result = parse_date(now, arg->val)) == now)
928 errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
929 } else if (cnf->password_days > 0)
930 result = now + ((long) cnf->password_days * 86400L);
931 return result;
932}
933
934
935static time_t
936pw_exppolicy(struct userconf * cnf, struct cargs * args)
937{
938 time_t result = 0;
939 time_t now = time(NULL);
940 struct carg *arg = getarg(args, 'e');
941
942 if (arg != NULL) {
943 if ((result = parse_date(now, arg->val)) == now)
944 errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
945 } else if (cnf->expire_days > 0)
946 result = now + ((long) cnf->expire_days * 86400L);
947 return result;
948}
949
950
951static char *
952pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user)
953{
954 struct carg *arg = getarg(args, 'd');
955 static char home[128];
956
353 static char home[128];
354
957 if (arg)
958 return (arg->val);
355 if (homedir)
356 return (homedir);
959
960 if (cnf->home == NULL || *cnf->home == '\0')
961 errx(EX_CONFIG, "no base home directory set");
962 snprintf(home, sizeof(home), "%s/%s", cnf->home, user);
963
964 return (home);
965}
966
357
358 if (cnf->home == NULL || *cnf->home == '\0')
359 errx(EX_CONFIG, "no base home directory set");
360 snprintf(home, sizeof(home), "%s/%s", cnf->home, user);
361
362 return (home);
363}
364
967static char *
365static char *
968shell_path(char const * path, char *shells[], char *sh)
969{
970 if (sh != NULL && (*sh == '/' || *sh == '\0'))
971 return sh; /* specified full path or forced none */
972 else {
973 char *p;
974 char paths[_UC_MAXLINE];
975

--- 18 unchanged lines hidden (view full) ---

994 }
995 if (sh == NULL)
996 errx(EX_OSFILE, "can't find shell `%s' in shell paths", sh);
997 errx(EX_CONFIG, "no default shell available or defined");
998 return NULL;
999 }
1000}
1001
366shell_path(char const * path, char *shells[], char *sh)
367{
368 if (sh != NULL && (*sh == '/' || *sh == '\0'))
369 return sh; /* specified full path or forced none */
370 else {
371 char *p;
372 char paths[_UC_MAXLINE];
373

--- 18 unchanged lines hidden (view full) ---

392 }
393 if (sh == NULL)
394 errx(EX_OSFILE, "can't find shell `%s' in shell paths", sh);
395 errx(EX_CONFIG, "no default shell available or defined");
396 return NULL;
397 }
398}
399
1002
1003static char *
1004pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell)
400static char *
401pw_shellpolicy(struct userconf * cnf)
1005{
402{
1006 char *sh = newshell;
1007 struct carg *arg = getarg(args, 's');
1008
403
1009 if (newshell == NULL && arg != NULL)
1010 sh = arg->val;
1011 return shell_path(cnf->shelldir, cnf->shells, sh ? sh : cnf->shell_default);
404 return shell_path(cnf->shelldir, cnf->shells, cnf->shell_default);
1012}
1013
1014#define SALTSIZE 32
1015
1016static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
1017
405}
406
407#define SALTSIZE 32
408
409static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
410
1018char *
411char *
1019pw_pwcrypt(char *password)
1020{
1021 int i;
1022 char salt[SALTSIZE + 1];
1023 char *cryptpw;
412pw_pwcrypt(char *password)
413{
414 int i;
415 char salt[SALTSIZE + 1];
416 char *cryptpw;
1024
1025 static char buf[256];
1026
1027 /*
1028 * Calculate a salt value
1029 */
1030 for (i = 0; i < SALTSIZE; i++)
1031 salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)];
1032 salt[SALTSIZE] = '\0';
1033
1034 cryptpw = crypt(password, salt);
1035 if (cryptpw == NULL)
1036 errx(EX_CONFIG, "crypt(3) failure");
1037 return strcpy(buf, cryptpw);
1038}
1039
417 static char buf[256];
418
419 /*
420 * Calculate a salt value
421 */
422 for (i = 0; i < SALTSIZE; i++)
423 salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)];
424 salt[SALTSIZE] = '\0';
425
426 cryptpw = crypt(password, salt);
427 if (cryptpw == NULL)
428 errx(EX_CONFIG, "crypt(3) failure");
429 return strcpy(buf, cryptpw);
430}
431
1040
1041static char *
1042pw_password(struct userconf * cnf, char const * user)
432static char *
433pw_password(struct userconf * cnf, char const * user, bool dryrun)
1043{
1044 int i, l;
1045 char pwbuf[32];
1046
1047 switch (cnf->default_password) {
1048 case -1: /* Random password */
1049 l = (arc4random() % 8 + 8); /* 8 - 16 chars */
1050 for (i = 0; i < l; i++)
1051 pwbuf[i] = chars[arc4random_uniform(sizeof(chars)-1)];
1052 pwbuf[i] = '\0';
1053
1054 /*
1055 * We give this information back to the user
1056 */
434{
435 int i, l;
436 char pwbuf[32];
437
438 switch (cnf->default_password) {
439 case -1: /* Random password */
440 l = (arc4random() % 8 + 8); /* 8 - 16 chars */
441 for (i = 0; i < l; i++)
442 pwbuf[i] = chars[arc4random_uniform(sizeof(chars)-1)];
443 pwbuf[i] = '\0';
444
445 /*
446 * We give this information back to the user
447 */
1057 if (conf.fd == -1 && !conf.dryrun) {
448 if (conf.fd == -1 && !dryrun) {
1058 if (isatty(STDOUT_FILENO))
1059 printf("Password for '%s' is: ", user);
1060 printf("%s\n", pwbuf);
1061 fflush(stdout);
1062 }
1063 break;
1064
1065 case -2: /* No password at all! */

--- 6 unchanged lines hidden (view full) ---

1072 case 1: /* user's name */
1073 strlcpy(pwbuf, user, sizeof(pwbuf));
1074 break;
1075 }
1076 return pw_pwcrypt(pwbuf);
1077}
1078
1079static int
449 if (isatty(STDOUT_FILENO))
450 printf("Password for '%s' is: ", user);
451 printf("%s\n", pwbuf);
452 fflush(stdout);
453 }
454 break;
455
456 case -2: /* No password at all! */

--- 6 unchanged lines hidden (view full) ---

463 case 1: /* user's name */
464 strlcpy(pwbuf, user, sizeof(pwbuf));
465 break;
466 }
467 return pw_pwcrypt(pwbuf);
468}
469
470static int
1080pw_userdel(char *name, long id)
471print_user(struct passwd * pwd, bool pretty, bool v7)
1081{
472{
473 int j;
474 char *p;
475 struct group *grp = GETGRGID(pwd->pw_gid);
476 char uname[60] = "User &", office[60] = "[None]",
477 wphone[60] = "[None]", hphone[60] = "[None]";
478 char acexpire[32] = "[None]", pwexpire[32] = "[None]";
479 struct tm * tptr;
480
481 if (!pretty) {
482 p = v7 ? pw_make_v7(pwd) : pw_make(pwd);
483 printf("%s\n", p);
484 free(p);
485 return (EXIT_SUCCESS);
486 }
487
488 if ((p = strtok(pwd->pw_gecos, ",")) != NULL) {
489 strlcpy(uname, p, sizeof(uname));
490 if ((p = strtok(NULL, ",")) != NULL) {
491 strlcpy(office, p, sizeof(office));
492 if ((p = strtok(NULL, ",")) != NULL) {
493 strlcpy(wphone, p, sizeof(wphone));
494 if ((p = strtok(NULL, "")) != NULL) {
495 strlcpy(hphone, p, sizeof(hphone));
496 }
497 }
498 }
499 }
500 /*
501 * Handle '&' in gecos field
502 */
503 if ((p = strchr(uname, '&')) != NULL) {
504 int l = strlen(pwd->pw_name);
505 int m = strlen(p);
506
507 memmove(p + l, p + 1, m);
508 memmove(p, pwd->pw_name, l);
509 *p = (char) toupper((unsigned char)*p);
510 }
511 if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL)
512 strftime(acexpire, sizeof acexpire, "%c", tptr);
513 if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL)
514 strftime(pwexpire, sizeof pwexpire, "%c", tptr);
515 printf("Login Name: %-15s #%-12ju Group: %-15s #%ju\n"
516 " Full Name: %s\n"
517 " Home: %-26.26s Class: %s\n"
518 " Shell: %-26.26s Office: %s\n"
519 "Work Phone: %-26.26s Home Phone: %s\n"
520 "Acc Expire: %-26.26s Pwd Expire: %s\n",
521 pwd->pw_name, (uintmax_t)pwd->pw_uid,
522 grp ? grp->gr_name : "(invalid)", (uintmax_t)pwd->pw_gid,
523 uname, pwd->pw_dir, pwd->pw_class,
524 pwd->pw_shell, office, wphone, hphone,
525 acexpire, pwexpire);
526 SETGRENT();
527 j = 0;
528 while ((grp=GETGRENT()) != NULL) {
529 int i = 0;
530 if (grp->gr_mem != NULL) {
531 while (grp->gr_mem[i] != NULL) {
532 if (strcmp(grp->gr_mem[i], pwd->pw_name)==0) {
533 printf(j++ == 0 ? " Groups: %s" : ",%s", grp->gr_name);
534 break;
535 }
536 ++i;
537 }
538 }
539 }
540 ENDGRENT();
541 printf("%s", j ? "\n" : "");
542 return (EXIT_SUCCESS);
543}
544
545char *
546pw_checkname(char *name, int gecos)
547{
548 char showch[8];
549 const char *badchars, *ch, *showtype;
550 int reject;
551
552 ch = name;
553 reject = 0;
554 if (gecos) {
555 /* See if the name is valid as a gecos (comment) field. */
556 badchars = ":!@";
557 showtype = "gecos field";
558 } else {
559 /* See if the name is valid as a userid or group. */
560 badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\"";
561 showtype = "userid/group name";
562 /* Userids and groups can not have a leading '-'. */
563 if (*ch == '-')
564 reject = 1;
565 }
566 if (!reject) {
567 while (*ch) {
568 if (strchr(badchars, *ch) != NULL || *ch < ' ' ||
569 *ch == 127) {
570 reject = 1;
571 break;
572 }
573 /* 8-bit characters are only allowed in GECOS fields */
574 if (!gecos && (*ch & 0x80)) {
575 reject = 1;
576 break;
577 }
578 ch++;
579 }
580 }
581 /*
582 * A `$' is allowed as the final character for userids and groups,
583 * mainly for the benefit of samba.
584 */
585 if (reject && !gecos) {
586 if (*ch == '$' && *(ch + 1) == '\0') {
587 reject = 0;
588 ch++;
589 }
590 }
591 if (reject) {
592 snprintf(showch, sizeof(showch), (*ch >= ' ' && *ch < 127)
593 ? "`%c'" : "0x%02x", *ch);
594 errx(EX_DATAERR, "invalid character %s at position %td in %s",
595 showch, (ch - name), showtype);
596 }
597 if (!gecos && (ch - name) > LOGNAMESIZE)
598 errx(EX_USAGE, "name too long `%s' (max is %d)", name,
599 LOGNAMESIZE);
600
601 return (name);
602}
603
604static void
605rmat(uid_t uid)
606{
607 DIR *d = opendir("/var/at/jobs");
608
609 if (d != NULL) {
610 struct dirent *e;
611
612 while ((e = readdir(d)) != NULL) {
613 struct stat st;
614
615 if (strncmp(e->d_name, ".lock", 5) != 0 &&
616 stat(e->d_name, &st) == 0 &&
617 !S_ISDIR(st.st_mode) &&
618 st.st_uid == uid) {
619 char tmp[MAXPATHLEN];
620
621 snprintf(tmp, sizeof(tmp), "/usr/bin/atrm %s", e->d_name);
622 system(tmp);
623 }
624 }
625 closedir(d);
626 }
627}
628
629static void
630rmopie(char const * name)
631{
632 char tmp[1014];
633 FILE *fp;
634 int fd;
635 size_t len;
636 off_t atofs = 0;
637
638 if ((fd = openat(conf.rootfd, "etc/opiekeys", O_RDWR)) == -1)
639 return;
640
641 fp = fdopen(fd, "r+");
642 len = strlen(name);
643
644 while (fgets(tmp, sizeof(tmp), fp) != NULL) {
645 if (strncmp(name, tmp, len) == 0 && tmp[len]==' ') {
646 /* Comment username out */
647 if (fseek(fp, atofs, SEEK_SET) == 0)
648 fwrite("#", 1, 1, fp);
649 break;
650 }
651 atofs = ftell(fp);
652 }
653 /*
654 * If we got an error of any sort, don't update!
655 */
656 fclose(fp);
657}
658
659int
660pw_user_next(int argc, char **argv, char *name __unused)
661{
662 struct userconf *cnf = NULL;
663 const char *cfg = NULL;
664 int ch;
665 bool quiet = false;
666 uid_t next;
667
668 while ((ch = getopt(argc, argv, "Cq")) != -1) {
669 switch (ch) {
670 case 'C':
671 cfg = optarg;
672 break;
673 case 'q':
674 quiet;
675 break;
676 }
677 }
678
679 if (quiet)
680 freopen(_PATH_DEVNULL, "w", stderr);
681
682 cnf = get_userconfig(cfg);
683
684 next = pw_uidpolicy(cnf, -1);
685
686 printf("%ju:", (uintmax_t)next);
687 pw_groupnext(cnf, quiet);
688
689 return (EXIT_SUCCESS);
690}
691
692int
693pw_user_show(int argc, char **argv, char *arg1)
694{
1082 struct passwd *pwd = NULL;
695 struct passwd *pwd = NULL;
1083 char file[MAXPATHLEN];
1084 char home[MAXPATHLEN];
1085 uid_t uid;
1086 struct group *gr, *grp;
1087 char grname[LOGNAMESIZE];
1088 int rc;
1089 struct stat st;
1090 int valid_type = _PWF_FILES;
696 char *name = NULL;
697 uid_t id = -1;
698 int ch;
699 bool all = false;
700 bool pretty = false;
701 bool force = false;
702 bool v7 = false;
703 bool quiet = false;
1091
704
705 if (arg1 != NULL) {
706 if (strspn(arg1, "0123456789") == strlen(arg1))
707 id = pw_checkid(arg1, UID_MAX);
708 else
709 name = arg1;
710 }
711
712 while ((ch = getopt(argc, argv, "C:qn:u:FPa7")) != -1) {
713 switch (ch) {
714 case 'C':
715 /* ignore compatibility */
716 break;
717 case 'q':
718 quiet = true;
719 break;
720 case 'n':
721 name = optarg;
722 break;
723 case 'u':
724 id = pw_checkid(optarg, UID_MAX);
725 break;
726 case 'F':
727 force = true;
728 break;
729 case 'P':
730 pretty = true;
731 break;
732 case 'a':
733 all = true;
734 break;
735 case 7:
736 v7 = true;
737 break;
738 }
739 }
740
741 if (quiet)
742 freopen(_PATH_DEVNULL, "w", stderr);
743
744 if (all) {
745 SETPWENT();
746 while ((pwd = GETPWENT()) != NULL)
747 print_user(pwd, pretty, v7);
748 ENDPWENT();
749 return (EXIT_SUCCESS);
750 }
751
1092 if (id < 0 && name == NULL)
1093 errx(EX_DATAERR, "username or id required");
1094
1095 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
1096 if (pwd == NULL) {
752 if (id < 0 && name == NULL)
753 errx(EX_DATAERR, "username or id required");
754
755 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
756 if (pwd == NULL) {
757 if (force) {
758 pwd = &fakeuser;
759 } else {
760 if (name == NULL)
761 errx(EX_NOUSER, "no such uid `%ju'",
762 (uintmax_t) id);
763 errx(EX_NOUSER, "no such user `%s'", name);
764 }
765 }
766
767 return (print_user(pwd, pretty, v7));
768}
769
770int
771pw_user_del(int argc, char **argv, char *arg1)
772{
773 struct userconf *cnf = NULL;
774 struct passwd *pwd = NULL;
775 struct group *gr, *grp;
776 char *name = NULL;
777 char grname[MAXLOGNAME];
778 char *nispasswd = NULL;
779 char file[MAXPATHLEN];
780 char home[MAXPATHLEN];
781 const char *cfg = NULL;
782 struct stat st;
783 uid_t id;
784 int ch, rc;
785 bool nis = false;
786 bool deletehome = false;
787 bool quiet = false;
788
789 if (arg1 != NULL) {
790 if (strspn(arg1, "0123456789") == strlen(arg1))
791 id = pw_checkid(arg1, UID_MAX);
792 else
793 name = arg1;
794 }
795
796 while ((ch = getopt(argc, argv, "C:qn:u:rYy:")) != -1) {
797 switch (ch) {
798 case 'C':
799 cfg = optarg;
800 break;
801 case 'q':
802 quiet = true;
803 break;
804 case 'n':
805 name = optarg;
806 break;
807 case 'u':
808 id = pw_checkid(optarg, UID_MAX);
809 break;
810 case 'r':
811 deletehome = true;
812 break;
813 case 'y':
814 nispasswd = optarg;
815 break;
816 case 'Y':
817 nis = true;
818 break;
819 }
820 }
821
822 if (quiet)
823 freopen(_PATH_DEVNULL, "w", stderr);
824
825 if (id < 0 && name == NULL)
826 errx(EX_DATAERR, "username or id required");
827
828 cnf = get_userconfig(cfg);
829
830 if (nispasswd == NULL)
831 nispasswd = cnf->nispasswd;
832
833 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
834 if (pwd == NULL) {
1097 if (name == NULL)
835 if (name == NULL)
1098 errx(EX_NOUSER, "no such uid `%ld'", id);
836 errx(EX_NOUSER, "no such uid `%ju'", (uintmax_t) id);
1099 errx(EX_NOUSER, "no such user `%s'", name);
1100 }
1101
837 errx(EX_NOUSER, "no such user `%s'", name);
838 }
839
1102 if (conf.userconf->nispasswd && *conf.userconf->nispasswd == '/')
1103 valid_type = _PWF_NIS;
1104
1105 if (PWF._altdir == PWF_REGULAR &&
840 if (PWF._altdir == PWF_REGULAR &&
1106 ((pwd->pw_fields & _PWF_SOURCE) != valid_type))
1107 errx(EX_NOUSER, "no such %s user `%s'",
1108 valid_type == _PWF_FILES ? "local" : "NIS" , name);
841 ((pwd->pw_fields & _PWF_SOURCE) != _PWF_FILES)) {
842 if ((pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
843 if (!nis && nispasswd && *nispasswd != '/')
844 errx(EX_NOUSER, "Cannot remove NIS user `%s'",
845 name);
846 } else {
847 errx(EX_NOUSER, "Cannot remove non local user `%s'",
848 name);
849 }
850 }
1109
851
1110 uid = pwd->pw_uid;
852 id = pwd->pw_uid;
1111 if (name == NULL)
1112 name = pwd->pw_name;
1113
1114 if (strcmp(pwd->pw_name, "root") == 0)
1115 errx(EX_DATAERR, "cannot remove user 'root'");
1116
853 if (name == NULL)
854 name = pwd->pw_name;
855
856 if (strcmp(pwd->pw_name, "root") == 0)
857 errx(EX_DATAERR, "cannot remove user 'root'");
858
1117 /* Remove opie record from /etc/opiekeys */
1118
859 /* Remove opie record from /etc/opiekeys */
1119 if (PWALTDIR() != PWF_ALT)
1120 rmopie(pwd->pw_name);
1121
1122 if (!PWALTDIR()) {
1123 /* Remove crontabs */
1124 snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name);
1125 if (access(file, F_OK) == 0) {
1126 snprintf(file, sizeof(file), "crontab -u %s -r", pwd->pw_name);
1127 system(file);
1128 }
1129 }
860 if (PWALTDIR() != PWF_ALT)
861 rmopie(pwd->pw_name);
862
863 if (!PWALTDIR()) {
864 /* Remove crontabs */
865 snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name);
866 if (access(file, F_OK) == 0) {
867 snprintf(file, sizeof(file), "crontab -u %s -r", pwd->pw_name);
868 system(file);
869 }
870 }
871
1130 /*
1131 * Save these for later, since contents of pwd may be
1132 * invalidated by deletion
1133 */
1134 snprintf(file, sizeof(file), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
1135 strlcpy(home, pwd->pw_dir, sizeof(home));
1136 gr = GETGRGID(pwd->pw_gid);
1137 if (gr != NULL)
1138 strlcpy(grname, gr->gr_name, LOGNAMESIZE);
1139 else
1140 grname[0] = '\0';
1141
1142 rc = delpwent(pwd);
1143 if (rc == -1)
1144 err(EX_IOERR, "user '%s' does not exist", pwd->pw_name);
1145 else if (rc != 0)
1146 err(EX_IOERR, "passwd update");
1147
872 /*
873 * Save these for later, since contents of pwd may be
874 * invalidated by deletion
875 */
876 snprintf(file, sizeof(file), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
877 strlcpy(home, pwd->pw_dir, sizeof(home));
878 gr = GETGRGID(pwd->pw_gid);
879 if (gr != NULL)
880 strlcpy(grname, gr->gr_name, LOGNAMESIZE);
881 else
882 grname[0] = '\0';
883
884 rc = delpwent(pwd);
885 if (rc == -1)
886 err(EX_IOERR, "user '%s' does not exist", pwd->pw_name);
887 else if (rc != 0)
888 err(EX_IOERR, "passwd update");
889
1148 if (conf.userconf->nispasswd && *conf.userconf->nispasswd=='/') {
1149 rc = delnispwent(conf.userconf->nispasswd, name);
890 if (nis && nispasswd && *nispasswd=='/') {
891 rc = delnispwent(nispasswd, name);
1150 if (rc == -1)
1151 warnx("WARNING: user '%s' does not exist in NIS passwd",
1152 pwd->pw_name);
1153 else if (rc != 0)
1154 warn("WARNING: NIS passwd update");
892 if (rc == -1)
893 warnx("WARNING: user '%s' does not exist in NIS passwd",
894 pwd->pw_name);
895 else if (rc != 0)
896 warn("WARNING: NIS passwd update");
1155 /* non-fatal */
1156 }
1157
1158 grp = GETGRNAM(name);
1159 if (grp != NULL &&
1160 (grp->gr_mem == NULL || *grp->gr_mem == NULL) &&
1161 strcmp(name, grname) == 0)
1162 delgrent(GETGRNAM(name));
1163 SETGRENT();

--- 10 unchanged lines hidden (view full) ---

1174 for (j = i; grp->gr_mem[j] != NULL; j++)
1175 grp->gr_mem[j] = grp->gr_mem[j+1];
1176 strlcpy(group, grp->gr_name, MAXLOGNAME);
1177 chggrent(group, grp);
1178 }
1179 }
1180 ENDGRENT();
1181
897 }
898
899 grp = GETGRNAM(name);
900 if (grp != NULL &&
901 (grp->gr_mem == NULL || *grp->gr_mem == NULL) &&
902 strcmp(name, grname) == 0)
903 delgrent(GETGRNAM(name));
904 SETGRENT();

--- 10 unchanged lines hidden (view full) ---

915 for (j = i; grp->gr_mem[j] != NULL; j++)
916 grp->gr_mem[j] = grp->gr_mem[j+1];
917 strlcpy(group, grp->gr_name, MAXLOGNAME);
918 chggrent(group, grp);
919 }
920 }
921 ENDGRENT();
922
1182 pw_log(conf.userconf, M_DELETE, W_USER, "%s(%ju) account removed", name,
1183 (uintmax_t)uid);
923 pw_log(cnf, M_DELETE, W_USER, "%s(%ju) account removed", name,
924 (uintmax_t)id);
1184
1185 /* Remove mail file */
1186 if (PWALTDIR() != PWF_ALT)
1187 unlinkat(conf.rootfd, file + 1, 0);
1188
925
926 /* Remove mail file */
927 if (PWALTDIR() != PWF_ALT)
928 unlinkat(conf.rootfd, file + 1, 0);
929
1189 /* Remove at jobs */
1190 if (!PWALTDIR() && getpwuid(uid) == NULL)
1191 rmat(uid);
930 /* Remove at jobs */
931 if (!PWALTDIR() && getpwuid(id) == NULL)
932 rmat(id);
1192
1193 /* Remove home directory and contents */
933
934 /* Remove home directory and contents */
1194 if (PWALTDIR() != PWF_ALT && conf.deletehome && *home == '/' &&
1195 getpwuid(uid) == NULL &&
935 if (PWALTDIR() != PWF_ALT && deletehome && *home == '/' &&
936 GETPWUID(id) == NULL &&
1196 fstatat(conf.rootfd, home + 1, &st, 0) != -1) {
937 fstatat(conf.rootfd, home + 1, &st, 0) != -1) {
1197 rm_r(conf.rootfd, home, uid);
1198 pw_log(conf.userconf, M_DELETE, W_USER, "%s(%ju) home '%s' %s"
1199 "removed", name, (uintmax_t)uid, home,
938 rm_r(conf.rootfd, home, id);
939 pw_log(cnf, M_DELETE, W_USER, "%s(%ju) home '%s' %s"
940 "removed", name, (uintmax_t)id, home,
1200 fstatat(conf.rootfd, home + 1, &st, 0) == -1 ? "" : "not "
1201 "completely ");
1202 }
1203
1204 return (EXIT_SUCCESS);
1205}
1206
941 fstatat(conf.rootfd, home + 1, &st, 0) == -1 ? "" : "not "
942 "completely ");
943 }
944
945 return (EXIT_SUCCESS);
946}
947
1207static int
1208print_user(struct passwd * pwd)
948int
949pw_user_lock(int argc, char **argv, char *arg1)
1209{
950{
1210 if (!conf.pretty) {
1211 char *buf;
951 int ch;
1212
952
1213 buf = conf.v7 ? pw_make_v7(pwd) : pw_make(pwd);
1214 printf("%s\n", buf);
1215 free(buf);
1216 } else {
1217 int j;
1218 char *p;
1219 struct group *grp = GETGRGID(pwd->pw_gid);
1220 char uname[60] = "User &", office[60] = "[None]",
1221 wphone[60] = "[None]", hphone[60] = "[None]";
1222 char acexpire[32] = "[None]", pwexpire[32] = "[None]";
1223 struct tm * tptr;
1224
1225 if ((p = strtok(pwd->pw_gecos, ",")) != NULL) {
1226 strlcpy(uname, p, sizeof(uname));
1227 if ((p = strtok(NULL, ",")) != NULL) {
1228 strlcpy(office, p, sizeof(office));
1229 if ((p = strtok(NULL, ",")) != NULL) {
1230 strlcpy(wphone, p, sizeof(wphone));
1231 if ((p = strtok(NULL, "")) != NULL) {
1232 strlcpy(hphone, p,
1233 sizeof(hphone));
1234 }
1235 }
1236 }
953 while ((ch = getopt(argc, argv, "Cq")) != -1) {
954 switch (ch) {
955 case 'C':
956 case 'q':
957 /* compatibility */
958 break;
1237 }
959 }
1238 /*
1239 * Handle '&' in gecos field
1240 */
1241 if ((p = strchr(uname, '&')) != NULL) {
1242 int l = strlen(pwd->pw_name);
1243 int m = strlen(p);
960 }
1244
961
1245 memmove(p + l, p + 1, m);
1246 memmove(p, pwd->pw_name, l);
1247 *p = (char) toupper((unsigned char)*p);
962 return (pw_userlock(arg1, M_LOCK));
963}
964
965int
966pw_user_unlock(int argc, char **argv, char *arg1)
967{
968 int ch;
969
970 while ((ch = getopt(argc, argv, "Cq")) != -1) {
971 switch (ch) {
972 case 'C':
973 case 'q':
974 /* compatibility */
975 break;
1248 }
976 }
1249 if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL)
1250 strftime(acexpire, sizeof acexpire, "%c", tptr);
1251 if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL)
1252 strftime(pwexpire, sizeof pwexpire, "%c", tptr);
1253 printf("Login Name: %-15s #%-12ju Group: %-15s #%ju\n"
1254 " Full Name: %s\n"
1255 " Home: %-26.26s Class: %s\n"
1256 " Shell: %-26.26s Office: %s\n"
1257 "Work Phone: %-26.26s Home Phone: %s\n"
1258 "Acc Expire: %-26.26s Pwd Expire: %s\n",
1259 pwd->pw_name, (uintmax_t)pwd->pw_uid,
1260 grp ? grp->gr_name : "(invalid)", (uintmax_t)pwd->pw_gid,
1261 uname, pwd->pw_dir, pwd->pw_class,
1262 pwd->pw_shell, office, wphone, hphone,
1263 acexpire, pwexpire);
1264 SETGRENT();
1265 j = 0;
1266 while ((grp=GETGRENT()) != NULL)
1267 {
1268 int i = 0;
1269 if (grp->gr_mem != NULL) {
1270 while (grp->gr_mem[i] != NULL)
1271 {
1272 if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
1273 {
1274 printf(j++ == 0 ? " Groups: %s" : ",%s", grp->gr_name);
1275 break;
1276 }
1277 ++i;
1278 }
1279 }
1280 }
1281 ENDGRENT();
1282 printf("%s", j ? "\n" : "");
1283 }
977 }
1284 return EXIT_SUCCESS;
978
979 return (pw_userlock(arg1, M_UNLOCK));
1285}
1286
980}
981
1287char *
1288pw_checkname(char *name, int gecos)
982static struct group *
983group_from_name_or_id(char *name)
1289{
984{
1290 char showch[8];
1291 const char *badchars, *ch, *showtype;
1292 int reject;
985 const char *errstr = NULL;
986 struct group *grp;
987 uintmax_t id;
1293
988
1294 ch = name;
1295 reject = 0;
1296 if (gecos) {
1297 /* See if the name is valid as a gecos (comment) field. */
1298 badchars = ":!@";
1299 showtype = "gecos field";
1300 } else {
1301 /* See if the name is valid as a userid or group. */
1302 badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\"";
1303 showtype = "userid/group name";
1304 /* Userids and groups can not have a leading '-'. */
1305 if (*ch == '-')
1306 reject = 1;
989 if ((grp = GETGRNAM(name)) == NULL) {
990 id = strtounum(name, 0, GID_MAX, &errstr);
991 if (errstr)
992 errx(EX_NOUSER, "group `%s' does not exist", name);
993 grp = GETGRGID(id);
994 if (grp == NULL)
995 errx(EX_NOUSER, "group `%s' does not exist", name);
1307 }
996 }
1308 if (!reject) {
1309 while (*ch) {
1310 if (strchr(badchars, *ch) != NULL || *ch < ' ' ||
1311 *ch == 127) {
1312 reject = 1;
1313 break;
1314 }
1315 /* 8-bit characters are only allowed in GECOS fields */
1316 if (!gecos && (*ch & 0x80)) {
1317 reject = 1;
1318 break;
1319 }
1320 ch++;
1321 }
997
998 return (grp);
999}
1000
1001static void
1002split_groups(StringList **groups, char *groupsstr)
1003{
1004 struct group *grp;
1005 char *p;
1006 char tok[] = ", \t";
1007
1008 for (p = strtok(groupsstr, tok); p != NULL; p = strtok(NULL, tok)) {
1009 grp = group_from_name_or_id(p);
1010 if (*groups == NULL)
1011 *groups = sl_init();
1012 sl_add(*groups, newstr(grp->gr_name));
1322 }
1013 }
1323 /*
1324 * A `$' is allowed as the final character for userids and groups,
1325 * mainly for the benefit of samba.
1326 */
1327 if (reject && !gecos) {
1328 if (*ch == '$' && *(ch + 1) == '\0') {
1329 reject = 0;
1330 ch++;
1331 }
1332 }
1333 if (reject) {
1334 snprintf(showch, sizeof(showch), (*ch >= ' ' && *ch < 127)
1335 ? "`%c'" : "0x%02x", *ch);
1336 errx(EX_DATAERR, "invalid character %s at position %td in %s",
1337 showch, (ch - name), showtype);
1338 }
1339 if (!gecos && (ch - name) > LOGNAMESIZE)
1340 errx(EX_DATAERR, "name too long `%s' (max is %d)", name,
1341 LOGNAMESIZE);
1014}
1342
1015
1343 return (name);
1016static void
1017validate_grname(struct userconf *cnf, char *group)
1018{
1019 struct group *grp;
1020
1021 if (group == NULL || *group == '\0') {
1022 cnf->default_group = "";
1023 return;
1024 }
1025 grp = group_from_name_or_id(group);
1026 cnf->default_group = newstr(grp->gr_name);
1344}
1345
1027}
1028
1029static mode_t
1030validate_mode(char *mode)
1031{
1032 mode_t m;
1033 void *set;
1346
1034
1035 if ((set = setmode(mode)) == NULL)
1036 errx(EX_DATAERR, "invalid directory creation mode '%s'", mode);
1037
1038 m = getmode(set, _DEF_DIRMODE);
1039 free(set);
1040 return (m);
1041}
1042
1347static void
1043static void
1348rmat(uid_t uid)
1044mix_config(struct userconf *cmdcnf, struct userconf *cfg)
1349{
1045{
1350 DIR *d = opendir("/var/at/jobs");
1351
1046
1352 if (d != NULL) {
1353 struct dirent *e;
1047 if (cmdcnf->default_password == 0)
1048 cmdcnf->default_password = cfg->default_password;
1049 if (cmdcnf->reuse_uids == 0)
1050 cmdcnf->reuse_uids = cfg->reuse_uids;
1051 if (cmdcnf->reuse_gids == 0)
1052 cmdcnf->reuse_gids = cfg->reuse_gids;
1053 if (cmdcnf->nispasswd == NULL)
1054 cmdcnf->nispasswd = cfg->nispasswd;
1055 if (cmdcnf->dotdir == NULL)
1056 cmdcnf->dotdir = cfg->dotdir;
1057 if (cmdcnf->newmail == NULL)
1058 cmdcnf->newmail = cfg->newmail;
1059 if (cmdcnf->logfile == NULL)
1060 cmdcnf->logfile = cfg->logfile;
1061 if (cmdcnf->home == NULL)
1062 cmdcnf->home = cfg->home;
1063 if (cmdcnf->homemode == 0)
1064 cmdcnf->homemode = cfg->homemode;
1065 if (cmdcnf->shelldir == NULL)
1066 cmdcnf->shelldir = cfg->shelldir;
1067 if (cmdcnf->shells == NULL)
1068 cmdcnf->shells = cfg->shells;
1069 if (cmdcnf->shell_default == NULL)
1070 cmdcnf->shell_default = cfg->shell_default;
1071 if (cmdcnf->default_group == NULL)
1072 cmdcnf->default_group = cfg->default_group;
1073 if (cmdcnf->groups == NULL)
1074 cmdcnf->groups = cfg->groups;
1075 if (cmdcnf->default_class == NULL)
1076 cmdcnf->default_class = cfg->default_class;
1077 if (cmdcnf->min_uid == 0)
1078 cmdcnf->min_uid = cfg->min_uid;
1079 if (cmdcnf->max_uid == 0)
1080 cmdcnf->max_uid = cfg->max_uid;
1081 if (cmdcnf->min_gid == 0)
1082 cmdcnf->min_gid = cfg->min_gid;
1083 if (cmdcnf->max_gid == 0)
1084 cmdcnf->max_gid = cfg->max_gid;
1085 if (cmdcnf->expire_days == 0)
1086 cmdcnf->expire_days = cfg->expire_days;
1087 if (cmdcnf->password_days == 0)
1088 cmdcnf->password_days = cfg->password_days;
1089}
1354
1090
1355 while ((e = readdir(d)) != NULL) {
1356 struct stat st;
1091int
1092pw_user_add(int argc, char **argv, char *arg1)
1093{
1094 struct userconf *cnf, *cmdcnf;
1095 struct passwd *pwd;
1096 struct group *grp;
1097 struct stat st;
1098 char args[] = "C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y";
1099 char line[_PASSWORD_LEN+1], path[MAXPATHLEN];
1100 char *gecos, *homedir, *skel, *walk, *userid, *groupid, *grname;
1101 char *default_passwd, *name, *p;
1102 const char *cfg;
1103 login_cap_t *lc;
1104 FILE *pfp, *fp;
1105 intmax_t id = -1;
1106 time_t now;
1107 int rc, ch, fd = -1;
1108 size_t i;
1109 bool dryrun, nis, pretty, quiet, createhome, precrypted, genconf;
1357
1110
1358 if (strncmp(e->d_name, ".lock", 5) != 0 &&
1359 stat(e->d_name, &st) == 0 &&
1360 !S_ISDIR(st.st_mode) &&
1361 st.st_uid == uid) {
1362 char tmp[MAXPATHLEN];
1111 dryrun = nis = pretty = quiet = createhome = precrypted = false;
1112 genconf = false;
1113 gecos = homedir = skel = userid = groupid = default_passwd = NULL;
1114 grname = name = NULL;
1363
1115
1364 snprintf(tmp, sizeof(tmp), "/usr/bin/atrm %s", e->d_name);
1365 system(tmp);
1116 if ((cmdcnf = calloc(1, sizeof(struct userconf))) == NULL)
1117 err(EXIT_FAILURE, "calloc()");
1118
1119 if (arg1 != NULL) {
1120 if (strspn(arg1, "0123456789") == strlen(arg1))
1121 id = pw_checkid(arg1, UID_MAX);
1122 else
1123 name = arg1;
1124 }
1125
1126 while ((ch = getopt(argc, argv, args)) != -1) {
1127 switch (ch) {
1128 case 'C':
1129 cfg = optarg;
1130 break;
1131 case 'q':
1132 quiet = true;
1133 break;
1134 case 'n':
1135 name = optarg;
1136 break;
1137 case 'u':
1138 userid = optarg;
1139 break;
1140 case 'c':
1141 gecos = pw_checkname(optarg, 1);
1142 break;
1143 case 'd':
1144 homedir = optarg;
1145 break;
1146 case 'e':
1147 now = time(NULL);
1148 cmdcnf->expire_days = parse_date(now, optarg);
1149 break;
1150 case 'p':
1151 now = time(NULL);
1152 cmdcnf->password_days = parse_date(now, optarg);
1153 break;
1154 case 'g':
1155 validate_grname(cmdcnf, optarg);
1156 grname = optarg;
1157 break;
1158 case 'G':
1159 split_groups(&cmdcnf->groups, optarg);
1160 break;
1161 case 'm':
1162 createhome = true;
1163 break;
1164 case 'M':
1165 cmdcnf->homemode = validate_mode(optarg);
1166 break;
1167 case 'k':
1168 walk = skel = optarg;
1169 if (*walk == '/')
1170 walk++;
1171 if (fstatat(conf.rootfd, walk, &st, 0) == -1)
1172 errx(EX_OSFILE, "skeleton `%s' does not "
1173 "exists", skel);
1174 if (!S_ISDIR(st.st_mode))
1175 errx(EX_OSFILE, "skeleton `%s' is not a "
1176 "directory", skel);
1177 cmdcnf->dotdir = skel;
1178 break;
1179 case 's':
1180 cmdcnf->shell_default = optarg;
1181 break;
1182 case 'o':
1183 conf.checkduplicate = false;
1184 break;
1185 case 'L':
1186 cmdcnf->default_class = pw_checkname(optarg, 0);
1187 break;
1188 case 'i':
1189 groupid = optarg;
1190 break;
1191 case 'w':
1192 default_passwd = optarg;
1193 break;
1194 case 'H':
1195 if (fd != -1)
1196 errx(EX_USAGE, "'-h' and '-H' are mutually "
1197 "exclusive options");
1198 fd = pw_checkfd(optarg);
1199 precrypted = true;
1200 if (fd == '-')
1201 errx(EX_USAGE, "-H expects a file descriptor");
1202 break;
1203 case 'h':
1204 if (fd != -1)
1205 errx(EX_USAGE, "'-h' and '-H' are mutually "
1206 "exclusive options");
1207 fd = pw_checkfd(optarg);
1208 break;
1209 case 'D':
1210 genconf = true;
1211 break;
1212 case 'b':
1213 cmdcnf->home = optarg;
1214 break;
1215 case 'N':
1216 dryrun = true;
1217 break;
1218 case 'P':
1219 pretty = true;
1220 break;
1221 case 'y':
1222 cmdcnf->nispasswd = optarg;
1223 break;
1224 case 'Y':
1225 nis = true;
1226 break;
1227 }
1228 }
1229
1230 if (geteuid() != 0 && ! dryrun)
1231 errx(EX_NOPERM, "you must be root");
1232
1233 if (quiet)
1234 freopen(_PATH_DEVNULL, "w", stderr);
1235
1236 cnf = get_userconfig(cfg);
1237
1238 mix_config(cmdcnf, cnf);
1239 if (default_passwd)
1240 cmdcnf->default_password = boolean_val(default_passwd,
1241 cnf->default_password);
1242 if (genconf) {
1243 if (name != NULL)
1244 errx(EX_DATAERR, "can't combine `-D' with `-n name'");
1245 if (userid != NULL) {
1246 if ((p = strtok(userid, ", \t")) != NULL)
1247 cmdcnf->min_uid = pw_checkid(p, UID_MAX);
1248 if (cmdcnf->min_uid == 0)
1249 cmdcnf->min_uid = 1000;
1250 if ((p = strtok(NULL, " ,\t")) != NULL)
1251 cmdcnf->max_uid = pw_checkid(p, UID_MAX);
1252 if (cmdcnf->max_uid == 0)
1253 cmdcnf->max_uid = 32000;
1254 }
1255 if (groupid != NULL) {
1256 if ((p = strtok(groupid, ", \t")) != NULL)
1257 cmdcnf->min_gid = pw_checkid(p, GID_MAX);
1258 if (cmdcnf->min_gid == 0)
1259 cmdcnf->min_gid = 1000;
1260 if ((p = strtok(NULL, " ,\t")) != NULL)
1261 cmdcnf->max_gid = pw_checkid(p, GID_MAX);
1262 if (cmdcnf->max_gid == 0)
1263 cmdcnf->max_gid = 32000;
1264 }
1265 if (write_userconfig(cmdcnf, cfg))
1266 return (EXIT_SUCCESS);
1267 err(EX_IOERR, "config update");
1268 }
1269
1270 if (userid)
1271 id = pw_checkid(userid, UID_MAX);
1272 if (id < 0 && name == NULL)
1273 errx(EX_DATAERR, "user name or id required");
1274
1275 if (name == NULL)
1276 errx(EX_DATAERR, "login name required");
1277
1278 pwd = &fakeuser;
1279 pwd->pw_name = name;
1280 pwd->pw_class = cmdcnf->default_class ? cmdcnf->default_class : "";
1281 pwd->pw_uid = pw_uidpolicy(cmdcnf, id);
1282 pwd->pw_gid = pw_gidpolicy(cnf, grname, pwd->pw_name,
1283 (gid_t) pwd->pw_uid, dryrun);
1284 pwd->pw_change = cmdcnf->password_days;
1285 pwd->pw_expire = cmdcnf->expire_days;
1286 pwd->pw_dir = pw_homepolicy(cmdcnf, homedir, pwd->pw_name);
1287 pwd->pw_shell = pw_shellpolicy(cmdcnf);
1288 lc = login_getpwclass(pwd);
1289 if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL)
1290 warn("setting crypt(3) format");
1291 login_close(lc);
1292 pwd->pw_passwd = pw_password(cmdcnf, pwd->pw_name, dryrun);
1293 if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
1294 warnx("WARNING: new account `%s' has a uid of 0 "
1295 "(superuser access!)", pwd->pw_name);
1296 if (gecos)
1297 pwd->pw_gecos = gecos;
1298
1299 if (fd != -1)
1300 pw_set_passwd(pwd, fd, precrypted, false);
1301
1302 if (dryrun)
1303 return (print_user(pwd, pretty, false));
1304
1305 if ((rc = addpwent(pwd)) != 0) {
1306 if (rc == -1)
1307 errx(EX_IOERR, "user '%s' already exists",
1308 pwd->pw_name);
1309 else if (rc != 0)
1310 err(EX_IOERR, "passwd file update");
1311 }
1312 if (nis && cmdcnf->nispasswd && *cmdcnf->nispasswd == '/') {
1313 printf("%s\n", cmdcnf->nispasswd);
1314 rc = addnispwent(cmdcnf->nispasswd, pwd);
1315 if (rc == -1)
1316 warnx("User '%s' already exists in NIS passwd", pwd->pw_name);
1317 else if (rc != 0)
1318 warn("NIS passwd update");
1319 /* NOTE: we treat NIS-only update errors as non-fatal */
1320 }
1321
1322 if (cmdcnf->groups != NULL) {
1323 for (i = 0; i < cmdcnf->groups->sl_cur; i++) {
1324 grp = GETGRNAM(cmdcnf->groups->sl_str[i]);
1325 grp = gr_add(grp, pwd->pw_name);
1326 /*
1327 * grp can only be NULL in 2 cases:
1328 * - the new member is already a member
1329 * - a problem with memory occurs
1330 * in both cases we want to skip now.
1331 */
1332 if (grp == NULL)
1333 continue;
1334 chggrent(grp->gr_name, grp);
1335 free(grp);
1336 }
1337 }
1338
1339 pwd = GETPWNAM(name);
1340 if (pwd == NULL)
1341 errx(EX_NOUSER, "user '%s' disappeared during update", name);
1342
1343 grp = GETGRGID(pwd->pw_gid);
1344 pw_log(cnf, M_ADD, W_USER, "%s(%ju):%s(%ju):%s:%s:%s",
1345 pwd->pw_name, (uintmax_t)pwd->pw_uid,
1346 grp ? grp->gr_name : "unknown", (uintmax_t)(grp ? grp->gr_gid : (uid_t)-1),
1347 pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
1348
1349 /*
1350 * let's touch and chown the user's mail file. This is not
1351 * strictly necessary under BSD with a 0755 maildir but it also
1352 * doesn't hurt anything to create the empty mailfile
1353 */
1354 if (PWALTDIR() != PWF_ALT) {
1355 snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR,
1356 pwd->pw_name);
1357 /* Preserve contents & mtime */
1358 close(openat(conf.rootfd, path +1, O_RDWR | O_CREAT, 0600));
1359 fchownat(conf.rootfd, path + 1, pwd->pw_uid, pwd->pw_gid,
1360 AT_SYMLINK_NOFOLLOW);
1361 }
1362
1363 /*
1364 * Let's create and populate the user's home directory. Note
1365 * that this also `works' for editing users if -m is used, but
1366 * existing files will *not* be overwritten.
1367 */
1368 if (PWALTDIR() != PWF_ALT && createhome && pwd->pw_dir &&
1369 *pwd->pw_dir == '/' && pwd->pw_dir[1])
1370 create_and_populate_homedir(cmdcnf, pwd, cmdcnf->dotdir,
1371 cmdcnf->homemode, false);
1372
1373 if (!PWALTDIR() && cmdcnf->newmail && *cmdcnf->newmail &&
1374 (fp = fopen(cnf->newmail, "r")) != NULL) {
1375 if ((pfp = popen(_PATH_SENDMAIL " -t", "w")) == NULL)
1376 warn("sendmail");
1377 else {
1378 fprintf(pfp, "From: root\n" "To: %s\n"
1379 "Subject: Welcome!\n\n", pwd->pw_name);
1380 while (fgets(line, sizeof(line), fp) != NULL) {
1381 /* Do substitutions? */
1382 fputs(line, pfp);
1366 }
1383 }
1384 pclose(pfp);
1385 pw_log(cnf, M_ADD, W_USER, "%s(%ju) new user mail sent",
1386 pwd->pw_name, (uintmax_t)pwd->pw_uid);
1367 }
1387 }
1368 closedir(d);
1388 fclose(fp);
1369 }
1389 }
1390
1391 if (nis && nis_update() == 0)
1392 pw_log(cnf, M_ADD, W_USER, "NIS maps updated");
1393
1394 return (EXIT_SUCCESS);
1370}
1371
1395}
1396
1372static void
1373rmopie(char const * name)
1397int
1398pw_user_mod(int argc, char **argv, char *arg1)
1374{
1399{
1375 char tmp[1014];
1376 FILE *fp;
1377 int fd;
1378 size_t len;
1379 off_t atofs = 0;
1380
1381 if ((fd = openat(conf.rootfd, "etc/opiekeys", O_RDWR)) == -1)
1382 return;
1400 struct userconf *cnf;
1401 struct passwd *pwd;
1402 struct group *grp;
1403 StringList *groups = NULL;
1404 char args[] = "C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:NPYy:";
1405 const char *cfg;
1406 char *gecos, *homedir, *grname, *name, *newname, *walk, *skel, *shell;
1407 char *passwd, *class, *nispasswd;
1408 login_cap_t *lc;
1409 struct stat st;
1410 intmax_t id = -1;
1411 int ch, fd = -1;
1412 size_t i, j;
1413 bool quiet, createhome, pretty, dryrun, nis, edited, docreatehome;
1414 mode_t homemode = 0;
1415 time_t expire_days, password_days, now, precrypted;
1383
1416
1384 fp = fdopen(fd, "r+");
1385 len = strlen(name);
1417 expire_days = password_days = -1;
1418 gecos = homedir = grname = name = newname = skel = shell =NULL;
1419 passwd = NULL;
1420 class = nispasswd = NULL;
1421 quiet = createhome = pretty = dryrun = nis = precrypted = false;
1422 edited = docreatehome = false;
1386
1423
1387 while (fgets(tmp, sizeof(tmp), fp) != NULL) {
1388 if (strncmp(name, tmp, len) == 0 && tmp[len]==' ') {
1389 /* Comment username out */
1390 if (fseek(fp, atofs, SEEK_SET) == 0)
1391 fwrite("#", 1, 1, fp);
1424 if (arg1 != NULL) {
1425 if (strspn(arg1, "0123456789") == strlen(arg1))
1426 id = pw_checkid(arg1, UID_MAX);
1427 else
1428 name = arg1;
1429 }
1430
1431 while ((ch = getopt(argc, argv, args)) != -1) {
1432 switch (ch) {
1433 case 'C':
1434 cfg = optarg;
1392 break;
1435 break;
1436 case 'q':
1437 quiet = true;
1438 break;
1439 case 'n':
1440 name = optarg;
1441 break;
1442 case 'u':
1443 id = pw_checkid(optarg, UID_MAX);
1444 break;
1445 case 'c':
1446 gecos = pw_checkname(optarg, 1);
1447 break;
1448 case 'd':
1449 homedir = optarg;
1450 break;
1451 case 'e':
1452 now = time(NULL);
1453 expire_days = parse_date(now, optarg);
1454 break;
1455 case 'p':
1456 now = time(NULL);
1457 password_days = parse_date(now, optarg);
1458 break;
1459 case 'g':
1460 group_from_name_or_id(optarg);
1461 grname = optarg;
1462 break;
1463 case 'G':
1464 split_groups(&groups, optarg);
1465 break;
1466 case 'm':
1467 createhome = true;
1468 break;
1469 case 'M':
1470 homemode = validate_mode(optarg);
1471 break;
1472 case 'l':
1473 newname = optarg;
1474 break;
1475 case 'k':
1476 walk = skel = optarg;
1477 if (*walk == '/')
1478 walk++;
1479 if (fstatat(conf.rootfd, walk, &st, 0) == -1)
1480 errx(EX_OSFILE, "skeleton `%s' does not "
1481 "exists", skel);
1482 if (!S_ISDIR(st.st_mode))
1483 errx(EX_OSFILE, "skeleton `%s' is not a "
1484 "directory", skel);
1485 break;
1486 case 's':
1487 shell = optarg;
1488 break;
1489 case 'w':
1490 passwd = optarg;
1491 break;
1492 case 'L':
1493 class = pw_checkname(optarg, 0);
1494 break;
1495 case 'H':
1496 if (fd != -1)
1497 errx(EX_USAGE, "'-h' and '-H' are mutually "
1498 "exclusive options");
1499 fd = pw_checkfd(optarg);
1500 precrypted = true;
1501 if (fd == '-')
1502 errx(EX_USAGE, "-H expects a file descriptor");
1503 break;
1504 case 'h':
1505 if (fd != -1)
1506 errx(EX_USAGE, "'-h' and '-H' are mutually "
1507 "exclusive options");
1508 fd = pw_checkfd(optarg);
1509 break;
1510 case 'N':
1511 dryrun = true;
1512 break;
1513 case 'P':
1514 pretty = true;
1515 break;
1516 case 'y':
1517 nispasswd = optarg;
1518 break;
1519 case 'Y':
1520 nis = true;
1521 break;
1393 }
1522 }
1394 atofs = ftell(fp);
1395 }
1523 }
1524
1525 if (geteuid() != 0 && ! dryrun)
1526 errx(EX_NOPERM, "you must be root");
1527
1528 if (quiet)
1529 freopen(_PATH_DEVNULL, "w", stderr);
1530
1531 cnf = get_userconfig(cfg);
1532
1533 if (id < 0 && name == NULL)
1534 errx(EX_DATAERR, "username or id required");
1535
1536 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
1537 if (pwd == NULL) {
1538 if (name == NULL)
1539 errx(EX_NOUSER, "no such uid `%ju'",
1540 (uintmax_t) id);
1541 errx(EX_NOUSER, "no such user `%s'", name);
1542 }
1543
1544 if (name == NULL)
1545 name = pwd->pw_name;
1546
1547 if (nis && nispasswd == NULL)
1548 nispasswd = cnf->nispasswd;
1549
1550 if (PWF._altdir == PWF_REGULAR &&
1551 ((pwd->pw_fields & _PWF_SOURCE) != _PWF_FILES)) {
1552 if ((pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
1553 if (!nis && nispasswd && *nispasswd != '/')
1554 errx(EX_NOUSER, "Cannot modify NIS user `%s'",
1555 name);
1556 } else {
1557 errx(EX_NOUSER, "Cannot modify non local user `%s'",
1558 name);
1559 }
1560 }
1561
1562 if (newname) {
1563 if (strcmp(pwd->pw_name, "root") == 0)
1564 errx(EX_DATAERR, "can't rename `root' account");
1565 if (strcmp(pwd->pw_name, newname) != 0) {
1566 pwd->pw_name = pw_checkname(newname, 0);
1567 edited = true;
1568 }
1569 }
1570
1571 if (id > 0 && pwd->pw_uid != id) {
1572 pwd->pw_uid = id;
1573 edited = true;
1574 if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0)
1575 errx(EX_DATAERR, "can't change uid of `root' account");
1576 if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
1577 warnx("WARNING: account `%s' will have a uid of 0 (superuser access!)", pwd->pw_name);
1578 }
1579
1580 if (grname && pwd->pw_uid != 0) {
1581 grp = GETGRNAM(grname);
1582 if (grp == NULL)
1583 grp = GETGRGID(pw_checkid(grname, GID_MAX));
1584 if (grp->gr_gid != pwd->pw_gid) {
1585 pwd->pw_gid = grp->gr_gid;
1586 edited = true;
1587 }
1588 }
1589
1590 if (password_days >= 0 && pwd->pw_change != password_days) {
1591 pwd->pw_change = password_days;
1592 edited = true;
1593 }
1594
1595 if (expire_days >= 0 && pwd->pw_expire != expire_days) {
1596 pwd->pw_expire = expire_days;
1597 edited = true;
1598 }
1599
1600 if (shell) {
1601 shell = shell_path(cnf->shelldir, cnf->shells, shell);
1602 if (shell == NULL)
1603 shell = "";
1604 if (strcmp(shell, pwd->pw_shell) != 0) {
1605 pwd->pw_shell = shell;
1606 edited = true;
1607 }
1608 }
1609
1610 if (class && strcmp(pwd->pw_class, class) != 0) {
1611 pwd->pw_class = class;
1612 edited = true;
1613 }
1614
1615 if (homedir && strcmp(pwd->pw_dir, homedir) != 0) {
1616 pwd->pw_dir = homedir;
1617 if (fstatat(conf.rootfd, pwd->pw_dir, &st, 0) == -1) {
1618 if (!createhome)
1619 warnx("WARNING: home `%s' does not exist",
1620 pwd->pw_dir);
1621 else
1622 docreatehome = true;
1623 } else if (!S_ISDIR(st.st_mode)) {
1624 warnx("WARNING: home `%s' is not a directory",
1625 pwd->pw_dir);
1626 }
1627 }
1628
1629 if (passwd && conf.fd == -1) {
1630 lc = login_getpwclass(pwd);
1631 if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL)
1632 warn("setting crypt(3) format");
1633 login_close(lc);
1634 pwd->pw_passwd = pw_password(cnf, pwd->pw_name, dryrun);
1635 edited = true;
1636 }
1637
1638 if (gecos && strcmp(pwd->pw_gecos, gecos) != 0) {
1639 pwd->pw_gecos = gecos;
1640 edited = true;
1641 }
1642
1643 if (fd != -1)
1644 edited = pw_set_passwd(pwd, fd, precrypted, true);
1645
1646 if (dryrun)
1647 return (print_user(pwd, pretty, false));
1648
1649 if (edited) /* Only updated this if required */
1650 perform_chgpwent(name, pwd, nis ? nispasswd : NULL);
1651 /* Now perform the needed changes concern groups */
1652 if (groups != NULL) {
1653 /* Delete User from groups using old name */
1654 SETGRENT();
1655 while ((grp = GETGRENT()) != NULL) {
1656 if (grp->gr_mem == NULL)
1657 continue;
1658 for (i = 0; grp->gr_mem[i] != NULL; i++) {
1659 if (strcmp(grp->gr_mem[i] , name) != 0)
1660 continue;
1661 for (j = i; grp->gr_mem[j] != NULL ; j++)
1662 grp->gr_mem[j] = grp->gr_mem[j+1];
1663 chggrent(grp->gr_name, grp);
1664 break;
1665 }
1666 }
1667 ENDGRENT();
1668 /* Add the user to the needed groups */
1669 for (i = 0; i < groups->sl_cur; i++) {
1670 grp = GETGRNAM(groups->sl_str[i]);
1671 grp = gr_add(grp, pwd->pw_name);
1672 if (grp == NULL)
1673 continue;
1674 chggrent(grp->gr_name, grp);
1675 free(grp);
1676 }
1677 }
1678 /* In case of rename we need to walk over the different groups */
1679 if (newname) {
1680 SETGRENT();
1681 while ((grp = GETGRENT()) != NULL) {
1682 if (grp->gr_mem == NULL)
1683 continue;
1684 for (i = 0; grp->gr_mem[i] != NULL; i++) {
1685 if (strcmp(grp->gr_mem[i], name) != 0)
1686 continue;
1687 grp->gr_mem[i] = newname;
1688 chggrent(grp->gr_name, grp);
1689 break;
1690 }
1691 }
1692 }
1693
1694 /* go get a current version of pwd */
1695 if (newname)
1696 name = newname;
1697 pwd = GETPWNAM(name);
1698 if (pwd == NULL)
1699 errx(EX_NOUSER, "user '%s' disappeared during update", name);
1700 grp = GETGRGID(pwd->pw_gid);
1701 pw_log(cnf, M_UPDATE, W_USER, "%s(%ju):%s(%ju):%s:%s:%s",
1702 pwd->pw_name, (uintmax_t)pwd->pw_uid,
1703 grp ? grp->gr_name : "unknown",
1704 (uintmax_t)(grp ? grp->gr_gid : (uid_t)-1),
1705 pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
1706
1396 /*
1707 /*
1397 * If we got an error of any sort, don't update!
1708 * Let's create and populate the user's home directory. Note
1709 * that this also `works' for editing users if -m is used, but
1710 * existing files will *not* be overwritten.
1398 */
1711 */
1399 fclose(fp);
1712 if (PWALTDIR() != PWF_ALT && docreatehome && pwd->pw_dir &&
1713 *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
1714 if (!skel)
1715 skel = cnf->dotdir;
1716 if (homemode == 0)
1717 homemode = cnf->homemode;
1718 create_and_populate_homedir(cnf, pwd, skel, homemode, true);
1719 }
1720
1721 if (nis && nis_update() == 0)
1722 pw_log(cnf, M_UPDATE, W_USER, "NIS maps updated");
1723
1724 return (EXIT_SUCCESS);
1400}
1725}