]>
git.cameronkatri.com Git - pw-darwin.git/blob - pw/pw_user.c
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
5 * David L. Nugent. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 static const char rcsid
[] =
35 #include <sys/param.h>
36 #include <sys/types.h>
47 #include <login_cap.h>
58 #define LOGNAMESIZE (MAXLOGNAME-1)
60 static char locked_str
[] = "*LOCKED*";
62 static struct passwd fakeuser
= {
76 static int print_user(struct passwd
*pwd
, bool pretty
, bool v7
);
77 static uid_t
pw_uidpolicy(struct userconf
*cnf
, intmax_t id
);
78 static uid_t
pw_gidpolicy(struct userconf
*cnf
, char *grname
, char *nam
,
79 gid_t prefer
, bool dryrun
);
80 static char *pw_homepolicy(struct userconf
* cnf
, char *homedir
,
82 static char *pw_shellpolicy(struct userconf
* cnf
);
83 static char *pw_password(struct userconf
* cnf
, char const * user
,
85 static char *shell_path(char const * path
, char *shells
[], char *sh
);
86 static void rmat(uid_t uid
);
87 static void rmopie(char const * name
);
90 mkdir_home_parents(int dfd
, const char *dir
)
96 errx(EX_DATAERR
, "invalid base directory for home '%s'", dir
);
100 if (fstatat(dfd
, dir
, &st
, 0) != -1) {
101 if (S_ISDIR(st
.st_mode
))
103 errx(EX_OSFILE
, "root home `/%s' is not a directory", dir
);
108 errx(EX_UNAVAILABLE
, "out of memory");
110 tmp
= strrchr(dirs
, '/');
118 * This is a kludge especially for Joerg :)
119 * If the home directory would be created in the root partition, then
120 * we really create it under /usr which is likely to have more space.
121 * But we create a symlink from cnf->home -> "/usr" -> cnf->home
123 if (strchr(dirs
, '/') == NULL
) {
124 asprintf(&tmp
, "usr/%s", dirs
);
126 errx(EX_UNAVAILABLE
, "out of memory");
127 if (mkdirat(dfd
, tmp
, _DEF_DIRMODE
) != -1 || errno
== EEXIST
) {
128 fchownat(dfd
, tmp
, 0, 0, 0);
129 symlinkat(tmp
, dfd
, dirs
);
134 if (fstatat(dfd
, dirs
, &st
, 0) == -1) {
135 while ((tmp
= strchr(tmp
+ 1, '/')) != NULL
) {
137 if (fstatat(dfd
, dirs
, &st
, 0) == -1) {
138 if (mkdirat(dfd
, dirs
, _DEF_DIRMODE
) == -1)
139 err(EX_OSFILE
, "'%s' (root home parent) is not a directory", dirs
);
144 if (fstatat(dfd
, dirs
, &st
, 0) == -1) {
145 if (mkdirat(dfd
, dirs
, _DEF_DIRMODE
) == -1)
146 err(EX_OSFILE
, "'%s' (root home parent) is not a directory", dirs
);
147 fchownat(dfd
, dirs
, 0, 0, 0);
154 create_and_populate_homedir(struct userconf
*cnf
, struct passwd
*pwd
,
155 const char *skeldir
, mode_t homemode
, bool update
)
159 /* Create home parents directories */
160 mkdir_home_parents(conf
.rootfd
, pwd
->pw_dir
);
162 if (skeldir
!= NULL
&& *skeldir
!= '\0') {
165 skelfd
= openat(conf
.rootfd
, skeldir
, O_DIRECTORY
|O_CLOEXEC
);
168 copymkdir(conf
.rootfd
, pwd
->pw_dir
, skelfd
, homemode
, pwd
->pw_uid
,
170 pw_log(cnf
, update
? M_UPDATE
: M_ADD
, W_USER
, "%s(%ju) home %s made",
171 pwd
->pw_name
, (uintmax_t)pwd
->pw_uid
, pwd
->pw_dir
);
175 pw_set_passwd(struct passwd
*pwd
, int fd
, bool precrypted
, bool update
)
180 char line
[_PASSWORD_LEN
+1];
184 if (!pwd
->pw_passwd
|| *pwd
->pw_passwd
!= '*') {
185 pwd
->pw_passwd
= "*"; /* No access */
191 if ((istty
= isatty(fd
))) {
192 if (tcgetattr(fd
, &t
) == -1)
196 n
.c_lflag
&= ~(ECHO
);
197 tcsetattr(fd
, TCSANOW
, &n
);
198 printf("%s%spassword for user %s:",
199 update
? "new " : "",
200 precrypted
? "encrypted " : "",
205 b
= read(fd
, line
, sizeof(line
) - 1);
206 if (istty
) { /* Restore state */
207 tcsetattr(fd
, TCSANOW
, &t
);
213 err(EX_IOERR
, "-%c file descriptor",
214 precrypted
? 'H' : 'h');
216 if ((p
= strpbrk(line
, "\r\n")) != NULL
)
219 errx(EX_DATAERR
, "empty password read on file descriptor %d",
222 if (strchr(line
, ':') != NULL
)
223 errx(EX_DATAERR
, "bad encrypted password");
224 pwd
->pw_passwd
= strdup(line
);
226 lc
= login_getpwclass(pwd
);
228 login_setcryptfmt(lc
, "sha512", NULL
) == NULL
)
229 warn("setting crypt(3) format");
231 pwd
->pw_passwd
= pw_pwcrypt(line
);
237 perform_chgpwent(const char *name
, struct passwd
*pwd
, char *nispasswd
)
240 struct passwd
*nispwd
;
242 /* duplicate for nis so that chgpwent is not modifying before NIS */
243 if (nispasswd
&& *nispasswd
== '/')
244 nispwd
= pw_dup(pwd
);
246 rc
= chgpwent(name
, pwd
);
248 errx(EX_IOERR
, "user '%s' does not exist (NIS?)", pwd
->pw_name
);
250 err(EX_IOERR
, "passwd file update");
252 if (nispasswd
&& *nispasswd
== '/') {
253 rc
= chgnispwent(nispasswd
, name
, nispwd
);
255 warn("User '%s' not found in NIS passwd", pwd
->pw_name
);
257 warn("NIS passwd update");
258 /* NOTE: NIS-only update errors are not fatal */
263 * The M_LOCK and M_UNLOCK functions simply add or remove
264 * a "*LOCKED*" prefix from in front of the password to
265 * prevent it decoding correctly, and therefore prevents
266 * access. Of course, this only prevents access via
267 * password authentication (not ssh, kerberos or any
268 * other method that does not use the UNIX password) but
269 * that is a known limitation.
272 pw_userlock(char *arg1
, int mode
)
274 struct passwd
*pwd
= NULL
;
275 char *passtmp
= NULL
;
278 uid_t id
= (uid_t
)-1;
281 errx(EX_NOPERM
, "you must be root");
284 errx(EX_DATAERR
, "username or id required");
287 if (arg1
[strspn(name
, "0123456789")] == '\0')
288 id
= pw_checkid(name
, UID_MAX
);
290 pwd
= GETPWNAM(pw_checkname(name
, 0));
291 if (pwd
== NULL
&& id
!= (uid_t
)-1) {
298 errx(EX_NOUSER
, "no such name or uid `%ju'", (uintmax_t) id
);
299 errx(EX_NOUSER
, "no such user `%s'", name
);
305 if (strncmp(pwd
->pw_passwd
, locked_str
, sizeof(locked_str
) -1) == 0)
307 if (mode
== M_LOCK
&& locked
)
308 errx(EX_DATAERR
, "user '%s' is already locked", pwd
->pw_name
);
309 if (mode
== M_UNLOCK
&& !locked
)
310 errx(EX_DATAERR
, "user '%s' is not locked", pwd
->pw_name
);
312 if (mode
== M_LOCK
) {
313 asprintf(&passtmp
, "%s%s", locked_str
, pwd
->pw_passwd
);
314 if (passtmp
== NULL
) /* disaster */
315 errx(EX_UNAVAILABLE
, "out of memory");
316 pwd
->pw_passwd
= passtmp
;
318 pwd
->pw_passwd
+= sizeof(locked_str
)-1;
321 perform_chgpwent(name
, pwd
, NULL
);
324 return (EXIT_SUCCESS
);
328 pw_uidpolicy(struct userconf
* cnf
, intmax_t id
)
332 uid_t uid
= (uid_t
) - 1;
335 * Check the given uid, if any
340 if ((pwd
= GETPWUID(uid
)) != NULL
&& conf
.checkduplicate
)
341 errx(EX_DATAERR
, "uid `%ju' has already been allocated",
342 (uintmax_t)pwd
->pw_uid
);
346 * We need to allocate the next available uid under one of
347 * two policies a) Grab the first unused uid b) Grab the
348 * highest possible unused uid
350 if (cnf
->min_uid
>= cnf
->max_uid
) { /* Sanity
351 * claus^H^H^H^Hheck */
353 cnf
->max_uid
= 32000;
355 bm
= bm_alloc(cnf
->max_uid
- cnf
->min_uid
+ 1);
358 * Now, let's fill the bitmap from the password file
361 while ((pwd
= GETPWENT()) != NULL
)
362 if (pwd
->pw_uid
>= (uid_t
) cnf
->min_uid
&& pwd
->pw_uid
<= (uid_t
) cnf
->max_uid
)
363 bm_setbit(&bm
, pwd
->pw_uid
- cnf
->min_uid
);
367 * Then apply the policy, with fallback to reuse if necessary
369 if (cnf
->reuse_uids
|| (uid
= (uid_t
) (bm_lastset(&bm
) + cnf
->min_uid
+ 1)) > cnf
->max_uid
)
370 uid
= (uid_t
) (bm_firstunset(&bm
) + cnf
->min_uid
);
373 * Another sanity check
375 if (uid
< cnf
->min_uid
|| uid
> cnf
->max_uid
)
376 errx(EX_SOFTWARE
, "unable to allocate a new uid - range fully used");
382 pw_gidpolicy(struct userconf
*cnf
, char *grname
, char *nam
, gid_t prefer
, bool dryrun
)
385 gid_t gid
= (uid_t
) - 1;
388 * Check the given gid, if any
392 if ((grp
= GETGRNAM(grname
)) == NULL
) {
393 gid
= pw_checkid(grname
, GID_MAX
);
397 } else if ((grp
= GETGRNAM(nam
)) != NULL
&&
398 (grp
->gr_mem
== NULL
|| grp
->gr_mem
[0] == NULL
)) {
399 gid
= grp
->gr_gid
; /* Already created? Use it anyway... */
404 * We need to auto-create a group with the user's name. We
405 * can send all the appropriate output to our sister routine
406 * bit first see if we can create a group with gid==uid so we
407 * can keep the user and group ids in sync. We purposely do
408 * NOT check the gid range if we can force the sync. If the
409 * user's name dups an existing group, then the group add
410 * function will happily handle that case for us and exit.
412 if (GETGRGID(prefer
) == NULL
)
415 gid
= pw_groupnext(cnf
, true);
418 grid
= pw_groupnext(cnf
, true);
419 groupadd(cnf
, nam
, grid
, NULL
, -1, false, false, false);
420 if ((grp
= GETGRNAM(nam
)) != NULL
)
429 pw_homepolicy(struct userconf
* cnf
, char *homedir
, const char *user
)
431 static char home
[128];
436 if (cnf
->home
== NULL
|| *cnf
->home
== '\0')
437 errx(EX_CONFIG
, "no base home directory set");
438 snprintf(home
, sizeof(home
), "%s/%s", cnf
->home
, user
);
444 shell_path(char const * path
, char *shells
[], char *sh
)
446 if (sh
!= NULL
&& (*sh
== '/' || *sh
== '\0'))
447 return sh
; /* specified full path or forced none */
450 char paths
[_UC_MAXLINE
];
453 * We need to search paths
455 strlcpy(paths
, path
, sizeof(paths
));
456 for (p
= strtok(paths
, ": \t\r\n"); p
!= NULL
; p
= strtok(NULL
, ": \t\r\n")) {
458 static char shellpath
[256];
461 snprintf(shellpath
, sizeof(shellpath
), "%s/%s", p
, sh
);
462 if (access(shellpath
, X_OK
) == 0)
465 for (i
= 0; i
< _UC_MAXSHELLS
&& shells
[i
] != NULL
; i
++) {
466 snprintf(shellpath
, sizeof(shellpath
), "%s/%s", p
, shells
[i
]);
467 if (access(shellpath
, X_OK
) == 0)
472 errx(EX_OSFILE
, "can't find shell `%s' in shell paths", sh
);
473 errx(EX_CONFIG
, "no default shell available or defined");
479 pw_shellpolicy(struct userconf
* cnf
)
482 return shell_path(cnf
->shelldir
, cnf
->shells
, cnf
->shell_default
);
487 static char const chars
[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
490 pw_pwcrypt(char *password
)
493 char salt
[SALTSIZE
+ 1];
495 static char buf
[256];
499 * Calculate a salt value
501 for (i
= 0; i
< SALTSIZE
; i
++)
502 salt
[i
] = chars
[arc4random_uniform(sizeof(chars
) - 1)];
503 salt
[SALTSIZE
] = '\0';
505 cryptpw
= crypt(password
, salt
);
507 errx(EX_CONFIG
, "crypt(3) failure");
508 pwlen
= strlcpy(buf
, cryptpw
, sizeof(buf
));
509 assert(pwlen
< sizeof(buf
));
514 pw_password(struct userconf
* cnf
, char const * user
, bool dryrun
)
519 switch (cnf
->default_password
) {
520 case P_NONE
: /* No password at all! */
522 case P_RANDOM
: /* Random password */
523 l
= (arc4random() % 8 + 8); /* 8 - 16 chars */
524 for (i
= 0; i
< l
; i
++)
525 pwbuf
[i
] = chars
[arc4random_uniform(sizeof(chars
)-1)];
529 * We give this information back to the user
531 if (conf
.fd
== -1 && !dryrun
) {
532 if (isatty(STDOUT_FILENO
))
533 printf("Password for '%s' is: ", user
);
534 printf("%s\n", pwbuf
);
538 case P_YES
: /* user's name */
539 strlcpy(pwbuf
, user
, sizeof(pwbuf
));
541 case P_NO
: /* No login - default */
546 return pw_pwcrypt(pwbuf
);
550 print_user(struct passwd
* pwd
, bool pretty
, bool v7
)
554 struct group
*grp
= GETGRGID(pwd
->pw_gid
);
555 char uname
[60] = "User &", office
[60] = "[None]",
556 wphone
[60] = "[None]", hphone
[60] = "[None]";
557 char acexpire
[32] = "[None]", pwexpire
[32] = "[None]";
561 p
= v7
? pw_make_v7(pwd
) : pw_make(pwd
);
564 return (EXIT_SUCCESS
);
567 if ((p
= strtok(pwd
->pw_gecos
, ",")) != NULL
) {
568 strlcpy(uname
, p
, sizeof(uname
));
569 if ((p
= strtok(NULL
, ",")) != NULL
) {
570 strlcpy(office
, p
, sizeof(office
));
571 if ((p
= strtok(NULL
, ",")) != NULL
) {
572 strlcpy(wphone
, p
, sizeof(wphone
));
573 if ((p
= strtok(NULL
, "")) != NULL
) {
574 strlcpy(hphone
, p
, sizeof(hphone
));
580 * Handle '&' in gecos field
582 if ((p
= strchr(uname
, '&')) != NULL
) {
583 int l
= strlen(pwd
->pw_name
);
586 memmove(p
+ l
, p
+ 1, m
);
587 memmove(p
, pwd
->pw_name
, l
);
588 *p
= (char) toupper((unsigned char)*p
);
590 if (pwd
->pw_expire
> (time_t)0 && (tptr
= localtime(&pwd
->pw_expire
)) != NULL
)
591 strftime(acexpire
, sizeof acexpire
, "%c", tptr
);
592 if (pwd
->pw_change
> (time_t)0 && (tptr
= localtime(&pwd
->pw_change
)) != NULL
)
593 strftime(pwexpire
, sizeof pwexpire
, "%c", tptr
);
594 printf("Login Name: %-15s #%-12ju Group: %-15s #%ju\n"
596 " Home: %-26.26s Class: %s\n"
597 " Shell: %-26.26s Office: %s\n"
598 "Work Phone: %-26.26s Home Phone: %s\n"
599 "Acc Expire: %-26.26s Pwd Expire: %s\n",
600 pwd
->pw_name
, (uintmax_t)pwd
->pw_uid
,
601 grp
? grp
->gr_name
: "(invalid)", (uintmax_t)pwd
->pw_gid
,
602 uname
, pwd
->pw_dir
, pwd
->pw_class
,
603 pwd
->pw_shell
, office
, wphone
, hphone
,
607 while ((grp
=GETGRENT()) != NULL
) {
609 if (grp
->gr_mem
!= NULL
) {
610 while (grp
->gr_mem
[i
] != NULL
) {
611 if (strcmp(grp
->gr_mem
[i
], pwd
->pw_name
)==0) {
612 printf(j
++ == 0 ? " Groups: %s" : ",%s", grp
->gr_name
);
620 printf("%s", j
? "\n" : "");
621 return (EXIT_SUCCESS
);
625 pw_checkname(char *name
, int gecos
)
628 const char *badchars
, *ch
, *showtype
;
634 /* See if the name is valid as a gecos (comment) field. */
636 showtype
= "gecos field";
638 /* See if the name is valid as a userid or group. */
639 badchars
= " ,\t:+&#%$^()!@~*?<>=|\\/\"";
640 showtype
= "userid/group name";
641 /* Userids and groups can not have a leading '-'. */
647 if (strchr(badchars
, *ch
) != NULL
||
648 (!gecos
&& *ch
< ' ') ||
653 /* 8-bit characters are only allowed in GECOS fields */
654 if (!gecos
&& (*ch
& 0x80)) {
662 * A `$' is allowed as the final character for userids and groups,
663 * mainly for the benefit of samba.
665 if (reject
&& !gecos
) {
666 if (*ch
== '$' && *(ch
+ 1) == '\0') {
672 snprintf(showch
, sizeof(showch
), (*ch
>= ' ' && *ch
< 127)
673 ? "`%c'" : "0x%02x", *ch
);
674 errx(EX_DATAERR
, "invalid character %s at position %td in %s",
675 showch
, (ch
- name
), showtype
);
677 if (!gecos
&& (ch
- name
) > LOGNAMESIZE
)
678 errx(EX_USAGE
, "name too long `%s' (max is %d)", name
,
687 DIR *d
= opendir("/var/at/jobs");
692 while ((e
= readdir(d
)) != NULL
) {
695 if (strncmp(e
->d_name
, ".lock", 5) != 0 &&
696 stat(e
->d_name
, &st
) == 0 &&
697 !S_ISDIR(st
.st_mode
) &&
699 char tmp
[MAXPATHLEN
];
701 snprintf(tmp
, sizeof(tmp
), "/usr/bin/atrm %s",
711 rmopie(char const * name
)
719 if ((fd
= openat(conf
.rootfd
, "etc/opiekeys", O_RDWR
)) == -1)
722 fp
= fdopen(fd
, "r+");
725 while (fgets(tmp
, sizeof(tmp
), fp
) != NULL
) {
726 if (strncmp(name
, tmp
, len
) == 0 && tmp
[len
]==' ') {
727 /* Comment username out */
728 if (fseek(fp
, atofs
, SEEK_SET
) == 0)
729 fwrite("#", 1, 1, fp
);
735 * If we got an error of any sort, don't update!
741 pw_user_next(int argc
, char **argv
, char *name __unused
)
743 struct userconf
*cnf
= NULL
;
744 const char *cfg
= NULL
;
749 while ((ch
= getopt(argc
, argv
, "C:q")) != -1) {
761 freopen(_PATH_DEVNULL
, "w", stderr
);
763 cnf
= get_userconfig(cfg
);
765 next
= pw_uidpolicy(cnf
, -1);
767 printf("%ju:", (uintmax_t)next
);
768 pw_groupnext(cnf
, quiet
);
770 return (EXIT_SUCCESS
);
774 pw_user_show(int argc
, char **argv
, char *arg1
)
776 struct passwd
*pwd
= NULL
;
787 if (arg1
[strspn(arg1
, "0123456789")] == '\0')
788 id
= pw_checkid(arg1
, UID_MAX
);
793 while ((ch
= getopt(argc
, argv
, "C:qn:u:FPa7")) != -1) {
796 /* ignore compatibility */
805 id
= pw_checkid(optarg
, UID_MAX
);
823 freopen(_PATH_DEVNULL
, "w", stderr
);
827 while ((pwd
= GETPWENT()) != NULL
)
828 print_user(pwd
, pretty
, v7
);
830 return (EXIT_SUCCESS
);
833 if (id
< 0 && name
== NULL
)
834 errx(EX_DATAERR
, "username or id required");
836 pwd
= (name
!= NULL
) ? GETPWNAM(pw_checkname(name
, 0)) : GETPWUID(id
);
842 errx(EX_NOUSER
, "no such uid `%ju'",
844 errx(EX_NOUSER
, "no such user `%s'", name
);
848 return (print_user(pwd
, pretty
, v7
));
852 pw_user_del(int argc
, char **argv
, char *arg1
)
854 struct userconf
*cnf
= NULL
;
855 struct passwd
*pwd
= NULL
;
856 struct group
*gr
, *grp
;
858 char grname
[MAXLOGNAME
];
859 char *nispasswd
= NULL
;
860 char file
[MAXPATHLEN
];
861 char home
[MAXPATHLEN
];
862 const char *cfg
= NULL
;
867 bool deletehome
= false;
871 if (arg1
[strspn(arg1
, "0123456789")] == '\0')
872 id
= pw_checkid(arg1
, UID_MAX
);
877 while ((ch
= getopt(argc
, argv
, "C:qn:u:rYy:")) != -1) {
889 id
= pw_checkid(optarg
, UID_MAX
);
904 freopen(_PATH_DEVNULL
, "w", stderr
);
906 if (id
< 0 && name
== NULL
)
907 errx(EX_DATAERR
, "username or id required");
909 cnf
= get_userconfig(cfg
);
911 if (nispasswd
== NULL
)
912 nispasswd
= cnf
->nispasswd
;
914 pwd
= (name
!= NULL
) ? GETPWNAM(pw_checkname(name
, 0)) : GETPWUID(id
);
917 errx(EX_NOUSER
, "no such uid `%ju'", (uintmax_t) id
);
918 errx(EX_NOUSER
, "no such user `%s'", name
);
921 if (PWF
._altdir
== PWF_REGULAR
&&
922 ((pwd
->pw_fields
& _PWF_SOURCE
) != _PWF_FILES
)) {
923 if ((pwd
->pw_fields
& _PWF_SOURCE
) == _PWF_NIS
) {
924 if (!nis
&& nispasswd
&& *nispasswd
!= '/')
925 errx(EX_NOUSER
, "Cannot remove NIS user `%s'",
928 errx(EX_NOUSER
, "Cannot remove non local user `%s'",
937 if (strcmp(pwd
->pw_name
, "root") == 0)
938 errx(EX_DATAERR
, "cannot remove user 'root'");
940 /* Remove opie record from /etc/opiekeys */
941 if (PWALTDIR() != PWF_ALT
)
942 rmopie(pwd
->pw_name
);
945 /* Remove crontabs */
946 snprintf(file
, sizeof(file
), "/var/cron/tabs/%s", pwd
->pw_name
);
947 if (access(file
, F_OK
) == 0) {
948 snprintf(file
, sizeof(file
), "crontab -u %s -r",
955 * Save these for later, since contents of pwd may be
956 * invalidated by deletion
958 snprintf(file
, sizeof(file
), "%s/%s", _PATH_MAILDIR
, pwd
->pw_name
);
959 strlcpy(home
, pwd
->pw_dir
, sizeof(home
));
960 gr
= GETGRGID(pwd
->pw_gid
);
962 strlcpy(grname
, gr
->gr_name
, LOGNAMESIZE
);
968 err(EX_IOERR
, "user '%s' does not exist", pwd
->pw_name
);
970 err(EX_IOERR
, "passwd update");
972 if (nis
&& nispasswd
&& *nispasswd
=='/') {
973 rc
= delnispwent(nispasswd
, name
);
975 warnx("WARNING: user '%s' does not exist in NIS passwd",
978 warn("WARNING: NIS passwd update");
981 grp
= GETGRNAM(name
);
983 (grp
->gr_mem
== NULL
|| *grp
->gr_mem
== NULL
) &&
984 strcmp(name
, grname
) == 0)
985 delgrent(GETGRNAM(name
));
987 while ((grp
= GETGRENT()) != NULL
) {
989 char group
[MAXLOGNAME
];
990 if (grp
->gr_mem
== NULL
)
993 for (i
= 0; grp
->gr_mem
[i
] != NULL
; i
++) {
994 if (strcmp(grp
->gr_mem
[i
], name
) != 0)
997 for (j
= i
; grp
->gr_mem
[j
] != NULL
; j
++)
998 grp
->gr_mem
[j
] = grp
->gr_mem
[j
+1];
999 strlcpy(group
, grp
->gr_name
, MAXLOGNAME
);
1000 chggrent(group
, grp
);
1005 pw_log(cnf
, M_DELETE
, W_USER
, "%s(%ju) account removed", name
,
1008 /* Remove mail file */
1009 if (PWALTDIR() != PWF_ALT
)
1010 unlinkat(conf
.rootfd
, file
+ 1, 0);
1012 /* Remove at jobs */
1013 if (!PWALTDIR() && getpwuid(id
) == NULL
)
1016 /* Remove home directory and contents */
1017 if (PWALTDIR() != PWF_ALT
&& deletehome
&& *home
== '/' &&
1018 GETPWUID(id
) == NULL
&&
1019 fstatat(conf
.rootfd
, home
+ 1, &st
, 0) != -1) {
1020 rm_r(conf
.rootfd
, home
, id
);
1021 pw_log(cnf
, M_DELETE
, W_USER
, "%s(%ju) home '%s' %s"
1022 "removed", name
, (uintmax_t)id
, home
,
1023 fstatat(conf
.rootfd
, home
+ 1, &st
, 0) == -1 ? "" : "not "
1027 return (EXIT_SUCCESS
);
1031 pw_user_lock(int argc
, char **argv
, char *arg1
)
1035 while ((ch
= getopt(argc
, argv
, "Cq")) != -1) {
1044 return (pw_userlock(arg1
, M_LOCK
));
1048 pw_user_unlock(int argc
, char **argv
, char *arg1
)
1052 while ((ch
= getopt(argc
, argv
, "Cq")) != -1) {
1061 return (pw_userlock(arg1
, M_UNLOCK
));
1064 static struct group
*
1065 group_from_name_or_id(char *name
)
1067 const char *errstr
= NULL
;
1071 if ((grp
= GETGRNAM(name
)) == NULL
) {
1072 id
= strtounum(name
, 0, GID_MAX
, &errstr
);
1074 errx(EX_NOUSER
, "group `%s' does not exist", name
);
1077 errx(EX_NOUSER
, "group `%s' does not exist", name
);
1084 split_groups(StringList
**groups
, char *groupsstr
)
1088 char tok
[] = ", \t";
1090 if (*groups
== NULL
)
1091 *groups
= sl_init();
1092 for (p
= strtok(groupsstr
, tok
); p
!= NULL
; p
= strtok(NULL
, tok
)) {
1093 grp
= group_from_name_or_id(p
);
1094 sl_add(*groups
, newstr(grp
->gr_name
));
1099 validate_grname(struct userconf
*cnf
, char *group
)
1103 if (group
== NULL
|| *group
== '\0') {
1104 cnf
->default_group
= "";
1107 grp
= group_from_name_or_id(group
);
1108 cnf
->default_group
= newstr(grp
->gr_name
);
1112 validate_mode(char *mode
)
1117 if ((set
= setmode(mode
)) == NULL
)
1118 errx(EX_DATAERR
, "invalid directory creation mode '%s'", mode
);
1120 m
= getmode(set
, _DEF_DIRMODE
);
1126 validate_expire(char *str
, int opt
)
1129 errx(EX_DATAERR
, "-%c argument must be numeric "
1130 "when setting defaults: %s", (char)opt
, str
);
1131 return strtol(str
, NULL
, 0);
1135 mix_config(struct userconf
*cmdcnf
, struct userconf
*cfg
)
1138 if (cmdcnf
->default_password
< 0)
1139 cmdcnf
->default_password
= cfg
->default_password
;
1140 if (cmdcnf
->reuse_uids
== 0)
1141 cmdcnf
->reuse_uids
= cfg
->reuse_uids
;
1142 if (cmdcnf
->reuse_gids
== 0)
1143 cmdcnf
->reuse_gids
= cfg
->reuse_gids
;
1144 if (cmdcnf
->nispasswd
== NULL
)
1145 cmdcnf
->nispasswd
= cfg
->nispasswd
;
1146 if (cmdcnf
->dotdir
== NULL
)
1147 cmdcnf
->dotdir
= cfg
->dotdir
;
1148 if (cmdcnf
->newmail
== NULL
)
1149 cmdcnf
->newmail
= cfg
->newmail
;
1150 if (cmdcnf
->logfile
== NULL
)
1151 cmdcnf
->logfile
= cfg
->logfile
;
1152 if (cmdcnf
->home
== NULL
)
1153 cmdcnf
->home
= cfg
->home
;
1154 if (cmdcnf
->homemode
== 0)
1155 cmdcnf
->homemode
= cfg
->homemode
;
1156 if (cmdcnf
->shelldir
== NULL
)
1157 cmdcnf
->shelldir
= cfg
->shelldir
;
1158 if (cmdcnf
->shells
== NULL
)
1159 cmdcnf
->shells
= cfg
->shells
;
1160 if (cmdcnf
->shell_default
== NULL
)
1161 cmdcnf
->shell_default
= cfg
->shell_default
;
1162 if (cmdcnf
->default_group
== NULL
)
1163 cmdcnf
->default_group
= cfg
->default_group
;
1164 if (cmdcnf
->groups
== NULL
)
1165 cmdcnf
->groups
= cfg
->groups
;
1166 if (cmdcnf
->default_class
== NULL
)
1167 cmdcnf
->default_class
= cfg
->default_class
;
1168 if (cmdcnf
->min_uid
== 0)
1169 cmdcnf
->min_uid
= cfg
->min_uid
;
1170 if (cmdcnf
->max_uid
== 0)
1171 cmdcnf
->max_uid
= cfg
->max_uid
;
1172 if (cmdcnf
->min_gid
== 0)
1173 cmdcnf
->min_gid
= cfg
->min_gid
;
1174 if (cmdcnf
->max_gid
== 0)
1175 cmdcnf
->max_gid
= cfg
->max_gid
;
1176 if (cmdcnf
->expire_days
< 0)
1177 cmdcnf
->expire_days
= cfg
->expire_days
;
1178 if (cmdcnf
->password_days
< 0)
1179 cmdcnf
->password_days
= cfg
->password_days
;
1183 pw_user_add(int argc
, char **argv
, char *arg1
)
1185 struct userconf
*cnf
, *cmdcnf
;
1189 char args
[] = "C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y";
1190 char line
[_PASSWORD_LEN
+1], path
[MAXPATHLEN
];
1191 char *gecos
, *homedir
, *skel
, *walk
, *userid
, *groupid
, *grname
;
1192 char *default_passwd
, *name
, *p
;
1193 const char *cfg
= NULL
;
1198 int rc
, ch
, fd
= -1;
1200 bool dryrun
, nis
, pretty
, quiet
, createhome
, precrypted
, genconf
;
1202 dryrun
= nis
= pretty
= quiet
= createhome
= precrypted
= false;
1204 gecos
= homedir
= skel
= userid
= groupid
= default_passwd
= NULL
;
1205 grname
= name
= NULL
;
1207 if ((cmdcnf
= calloc(1, sizeof(struct userconf
))) == NULL
)
1208 err(EXIT_FAILURE
, "calloc()");
1210 cmdcnf
->default_password
= cmdcnf
->expire_days
= cmdcnf
->password_days
= -1;
1214 if (arg1
[strspn(arg1
, "0123456789")] == '\0')
1215 id
= pw_checkid(arg1
, UID_MAX
);
1217 name
= pw_checkname(arg1
, 0);
1220 while ((ch
= getopt(argc
, argv
, args
)) != -1) {
1229 name
= pw_checkname(optarg
, 0);
1235 gecos
= pw_checkname(optarg
, 1);
1242 cmdcnf
->expire_days
= validate_expire(optarg
, ch
);
1244 cmdcnf
->expire_days
= parse_date(now
, optarg
);
1248 cmdcnf
->password_days
= validate_expire(optarg
, ch
);
1250 cmdcnf
->password_days
= parse_date(now
, optarg
);
1253 validate_grname(cmdcnf
, optarg
);
1257 split_groups(&cmdcnf
->groups
, optarg
);
1263 cmdcnf
->homemode
= validate_mode(optarg
);
1266 walk
= skel
= optarg
;
1269 if (fstatat(conf
.rootfd
, walk
, &st
, 0) == -1)
1270 errx(EX_OSFILE
, "skeleton `%s' does not "
1272 if (!S_ISDIR(st
.st_mode
))
1273 errx(EX_OSFILE
, "skeleton `%s' is not a "
1275 cmdcnf
->dotdir
= skel
;
1278 cmdcnf
->shell_default
= optarg
;
1281 conf
.checkduplicate
= false;
1284 cmdcnf
->default_class
= pw_checkname(optarg
, 0);
1290 default_passwd
= optarg
;
1294 errx(EX_USAGE
, "'-h' and '-H' are mutually "
1295 "exclusive options");
1296 fd
= pw_checkfd(optarg
);
1299 errx(EX_USAGE
, "-H expects a file descriptor");
1303 errx(EX_USAGE
, "'-h' and '-H' are mutually "
1304 "exclusive options");
1305 fd
= pw_checkfd(optarg
);
1311 cmdcnf
->home
= optarg
;
1320 cmdcnf
->nispasswd
= optarg
;
1328 if (geteuid() != 0 && ! dryrun
)
1329 errx(EX_NOPERM
, "you must be root");
1332 freopen(_PATH_DEVNULL
, "w", stderr
);
1334 cnf
= get_userconfig(cfg
);
1336 mix_config(cmdcnf
, cnf
);
1338 cmdcnf
->default_password
= passwd_val(default_passwd
,
1339 cnf
->default_password
);
1342 errx(EX_DATAERR
, "can't combine `-D' with `-n name'");
1343 if (userid
!= NULL
) {
1344 if ((p
= strtok(userid
, ", \t")) != NULL
)
1345 cmdcnf
->min_uid
= pw_checkid(p
, UID_MAX
);
1346 if (cmdcnf
->min_uid
== 0)
1347 cmdcnf
->min_uid
= 1000;
1348 if ((p
= strtok(NULL
, " ,\t")) != NULL
)
1349 cmdcnf
->max_uid
= pw_checkid(p
, UID_MAX
);
1350 if (cmdcnf
->max_uid
== 0)
1351 cmdcnf
->max_uid
= 32000;
1353 if (groupid
!= NULL
) {
1354 if ((p
= strtok(groupid
, ", \t")) != NULL
)
1355 cmdcnf
->min_gid
= pw_checkid(p
, GID_MAX
);
1356 if (cmdcnf
->min_gid
== 0)
1357 cmdcnf
->min_gid
= 1000;
1358 if ((p
= strtok(NULL
, " ,\t")) != NULL
)
1359 cmdcnf
->max_gid
= pw_checkid(p
, GID_MAX
);
1360 if (cmdcnf
->max_gid
== 0)
1361 cmdcnf
->max_gid
= 32000;
1363 if (write_userconfig(cmdcnf
, cfg
))
1364 return (EXIT_SUCCESS
);
1365 err(EX_IOERR
, "config update");
1369 id
= pw_checkid(userid
, UID_MAX
);
1370 if (id
< 0 && name
== NULL
)
1371 errx(EX_DATAERR
, "user name or id required");
1374 errx(EX_DATAERR
, "login name required");
1376 if (GETPWNAM(name
) != NULL
)
1377 errx(EX_DATAERR
, "login name `%s' already exists", name
);
1380 grname
= cmdcnf
->default_group
;
1383 pwd
->pw_name
= name
;
1384 pwd
->pw_class
= cmdcnf
->default_class
? cmdcnf
->default_class
: "";
1385 pwd
->pw_uid
= pw_uidpolicy(cmdcnf
, id
);
1386 pwd
->pw_gid
= pw_gidpolicy(cnf
, grname
, pwd
->pw_name
,
1387 (gid_t
) pwd
->pw_uid
, dryrun
);
1389 /* cmdcnf->password_days and cmdcnf->expire_days hold unixtime here */
1390 if (cmdcnf
->password_days
> 0)
1391 pwd
->pw_change
= cmdcnf
->password_days
;
1392 if (cmdcnf
->expire_days
> 0)
1393 pwd
->pw_expire
= cmdcnf
->expire_days
;
1395 pwd
->pw_dir
= pw_homepolicy(cmdcnf
, homedir
, pwd
->pw_name
);
1396 pwd
->pw_shell
= pw_shellpolicy(cmdcnf
);
1397 lc
= login_getpwclass(pwd
);
1398 if (lc
== NULL
|| login_setcryptfmt(lc
, "sha512", NULL
) == NULL
)
1399 warn("setting crypt(3) format");
1401 pwd
->pw_passwd
= pw_password(cmdcnf
, pwd
->pw_name
, dryrun
);
1402 if (pwd
->pw_uid
== 0 && strcmp(pwd
->pw_name
, "root") != 0)
1403 warnx("WARNING: new account `%s' has a uid of 0 "
1404 "(superuser access!)", pwd
->pw_name
);
1406 pwd
->pw_gecos
= gecos
;
1409 pw_set_passwd(pwd
, fd
, precrypted
, false);
1412 return (print_user(pwd
, pretty
, false));
1414 if ((rc
= addpwent(pwd
)) != 0) {
1416 errx(EX_IOERR
, "user '%s' already exists",
1419 err(EX_IOERR
, "passwd file update");
1421 if (nis
&& cmdcnf
->nispasswd
&& *cmdcnf
->nispasswd
== '/') {
1422 printf("%s\n", cmdcnf
->nispasswd
);
1423 rc
= addnispwent(cmdcnf
->nispasswd
, pwd
);
1425 warnx("User '%s' already exists in NIS passwd",
1428 warn("NIS passwd update");
1429 /* NOTE: we treat NIS-only update errors as non-fatal */
1432 if (cmdcnf
->groups
!= NULL
) {
1433 for (i
= 0; i
< cmdcnf
->groups
->sl_cur
; i
++) {
1434 grp
= GETGRNAM(cmdcnf
->groups
->sl_str
[i
]);
1435 grp
= gr_add(grp
, pwd
->pw_name
);
1437 * grp can only be NULL in 2 cases:
1438 * - the new member is already a member
1439 * - a problem with memory occurs
1440 * in both cases we want to skip now.
1444 chggrent(grp
->gr_name
, grp
);
1449 pwd
= GETPWNAM(name
);
1451 errx(EX_NOUSER
, "user '%s' disappeared during update", name
);
1453 grp
= GETGRGID(pwd
->pw_gid
);
1454 pw_log(cnf
, M_ADD
, W_USER
, "%s(%ju):%s(%ju):%s:%s:%s",
1455 pwd
->pw_name
, (uintmax_t)pwd
->pw_uid
,
1456 grp
? grp
->gr_name
: "unknown",
1457 (uintmax_t)(grp
? grp
->gr_gid
: (uid_t
)-1),
1458 pwd
->pw_gecos
, pwd
->pw_dir
, pwd
->pw_shell
);
1461 * let's touch and chown the user's mail file. This is not
1462 * strictly necessary under BSD with a 0755 maildir but it also
1463 * doesn't hurt anything to create the empty mailfile
1465 if (PWALTDIR() != PWF_ALT
) {
1466 snprintf(path
, sizeof(path
), "%s/%s", _PATH_MAILDIR
,
1468 /* Preserve contents & mtime */
1469 close(openat(conf
.rootfd
, path
+1, O_RDWR
| O_CREAT
, 0600));
1470 fchownat(conf
.rootfd
, path
+ 1, pwd
->pw_uid
, pwd
->pw_gid
,
1471 AT_SYMLINK_NOFOLLOW
);
1475 * Let's create and populate the user's home directory. Note
1476 * that this also `works' for editing users if -m is used, but
1477 * existing files will *not* be overwritten.
1479 if (PWALTDIR() != PWF_ALT
&& createhome
&& pwd
->pw_dir
&&
1480 *pwd
->pw_dir
== '/' && pwd
->pw_dir
[1])
1481 create_and_populate_homedir(cmdcnf
, pwd
, cmdcnf
->dotdir
,
1482 cmdcnf
->homemode
, false);
1484 if (!PWALTDIR() && cmdcnf
->newmail
&& *cmdcnf
->newmail
&&
1485 (fp
= fopen(cnf
->newmail
, "r")) != NULL
) {
1486 if ((pfp
= popen(_PATH_SENDMAIL
" -t", "w")) == NULL
)
1489 fprintf(pfp
, "From: root\n" "To: %s\n"
1490 "Subject: Welcome!\n\n", pwd
->pw_name
);
1491 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
1492 /* Do substitutions? */
1496 pw_log(cnf
, M_ADD
, W_USER
, "%s(%ju) new user mail sent",
1497 pwd
->pw_name
, (uintmax_t)pwd
->pw_uid
);
1502 if (nis
&& nis_update() == 0)
1503 pw_log(cnf
, M_ADD
, W_USER
, "NIS maps updated");
1505 return (EXIT_SUCCESS
);
1509 pw_user_mod(int argc
, char **argv
, char *arg1
)
1511 struct userconf
*cnf
;
1514 StringList
*groups
= NULL
;
1515 char args
[] = "C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:NPYy:";
1516 const char *cfg
= NULL
;
1517 char *gecos
, *homedir
, *grname
, *name
, *newname
, *walk
, *skel
, *shell
;
1518 char *passwd
, *class, *nispasswd
;
1524 bool quiet
, createhome
, pretty
, dryrun
, nis
, edited
;
1526 mode_t homemode
= 0;
1527 time_t expire_time
, password_time
, now
;
1529 expire_time
= password_time
= -1;
1530 gecos
= homedir
= grname
= name
= newname
= skel
= shell
=NULL
;
1532 class = nispasswd
= NULL
;
1533 quiet
= createhome
= pretty
= dryrun
= nis
= precrypted
= false;
1538 if (arg1
[strspn(arg1
, "0123456789")] == '\0')
1539 id
= pw_checkid(arg1
, UID_MAX
);
1544 while ((ch
= getopt(argc
, argv
, args
)) != -1) {
1556 id
= pw_checkid(optarg
, UID_MAX
);
1559 gecos
= pw_checkname(optarg
, 1);
1565 expire_time
= parse_date(now
, optarg
);
1568 password_time
= parse_date(now
, optarg
);
1571 group_from_name_or_id(optarg
);
1575 split_groups(&groups
, optarg
);
1581 homemode
= validate_mode(optarg
);
1587 walk
= skel
= optarg
;
1590 if (fstatat(conf
.rootfd
, walk
, &st
, 0) == -1)
1591 errx(EX_OSFILE
, "skeleton `%s' does not "
1593 if (!S_ISDIR(st
.st_mode
))
1594 errx(EX_OSFILE
, "skeleton `%s' is not a "
1604 class = pw_checkname(optarg
, 0);
1608 errx(EX_USAGE
, "'-h' and '-H' are mutually "
1609 "exclusive options");
1610 fd
= pw_checkfd(optarg
);
1613 errx(EX_USAGE
, "-H expects a file descriptor");
1617 errx(EX_USAGE
, "'-h' and '-H' are mutually "
1618 "exclusive options");
1619 fd
= pw_checkfd(optarg
);
1636 if (geteuid() != 0 && ! dryrun
)
1637 errx(EX_NOPERM
, "you must be root");
1640 freopen(_PATH_DEVNULL
, "w", stderr
);
1642 cnf
= get_userconfig(cfg
);
1644 if (id
< 0 && name
== NULL
)
1645 errx(EX_DATAERR
, "username or id required");
1647 pwd
= (name
!= NULL
) ? GETPWNAM(pw_checkname(name
, 0)) : GETPWUID(id
);
1650 errx(EX_NOUSER
, "no such uid `%ju'",
1652 errx(EX_NOUSER
, "no such user `%s'", name
);
1656 name
= pwd
->pw_name
;
1658 if (nis
&& nispasswd
== NULL
)
1659 nispasswd
= cnf
->nispasswd
;
1661 if (PWF
._altdir
== PWF_REGULAR
&&
1662 ((pwd
->pw_fields
& _PWF_SOURCE
) != _PWF_FILES
)) {
1663 if ((pwd
->pw_fields
& _PWF_SOURCE
) == _PWF_NIS
) {
1664 if (!nis
&& nispasswd
&& *nispasswd
!= '/')
1665 errx(EX_NOUSER
, "Cannot modify NIS user `%s'",
1668 errx(EX_NOUSER
, "Cannot modify non local user `%s'",
1674 if (strcmp(pwd
->pw_name
, "root") == 0)
1675 errx(EX_DATAERR
, "can't rename `root' account");
1676 if (strcmp(pwd
->pw_name
, newname
) != 0) {
1677 pwd
->pw_name
= pw_checkname(newname
, 0);
1682 if (id
>= 0 && pwd
->pw_uid
!= id
) {
1685 if (pwd
->pw_uid
!= 0 && strcmp(pwd
->pw_name
, "root") == 0)
1686 errx(EX_DATAERR
, "can't change uid of `root' account");
1687 if (pwd
->pw_uid
== 0 && strcmp(pwd
->pw_name
, "root") != 0)
1688 warnx("WARNING: account `%s' will have a uid of 0 "
1689 "(superuser access!)", pwd
->pw_name
);
1692 if (grname
&& pwd
->pw_uid
!= 0) {
1693 grp
= GETGRNAM(grname
);
1695 grp
= GETGRGID(pw_checkid(grname
, GID_MAX
));
1696 if (grp
->gr_gid
!= pwd
->pw_gid
) {
1697 pwd
->pw_gid
= grp
->gr_gid
;
1703 if (password_time
>= 0 && pwd
->pw_change
!= password_time
) {
1704 pwd
->pw_change
= password_time
;
1708 if (expire_time
>= 0 && pwd
->pw_expire
!= expire_time
) {
1709 pwd
->pw_expire
= expire_time
;
1714 shell
= shell_path(cnf
->shelldir
, cnf
->shells
, shell
);
1717 if (strcmp(shell
, pwd
->pw_shell
) != 0) {
1718 pwd
->pw_shell
= shell
;
1723 if (class && strcmp(pwd
->pw_class
, class) != 0) {
1724 pwd
->pw_class
= class;
1728 if (homedir
&& strcmp(pwd
->pw_dir
, homedir
) != 0) {
1729 pwd
->pw_dir
= homedir
;
1731 if (fstatat(conf
.rootfd
, pwd
->pw_dir
, &st
, 0) == -1) {
1733 warnx("WARNING: home `%s' does not exist",
1735 } else if (!S_ISDIR(st
.st_mode
)) {
1736 warnx("WARNING: home `%s' is not a directory",
1741 if (passwd
&& conf
.fd
== -1) {
1742 lc
= login_getpwclass(pwd
);
1743 if (lc
== NULL
|| login_setcryptfmt(lc
, "sha512", NULL
) == NULL
)
1744 warn("setting crypt(3) format");
1746 cnf
->default_password
= passwd_val(passwd
,
1747 cnf
->default_password
);
1748 pwd
->pw_passwd
= pw_password(cnf
, pwd
->pw_name
, dryrun
);
1752 if (gecos
&& strcmp(pwd
->pw_gecos
, gecos
) != 0) {
1753 pwd
->pw_gecos
= gecos
;
1758 edited
= pw_set_passwd(pwd
, fd
, precrypted
, true);
1761 return (print_user(pwd
, pretty
, false));
1763 if (edited
) /* Only updated this if required */
1764 perform_chgpwent(name
, pwd
, nis
? nispasswd
: NULL
);
1765 /* Now perform the needed changes concern groups */
1766 if (groups
!= NULL
) {
1767 /* Delete User from groups using old name */
1769 while ((grp
= GETGRENT()) != NULL
) {
1770 if (grp
->gr_mem
== NULL
)
1772 for (i
= 0; grp
->gr_mem
[i
] != NULL
; i
++) {
1773 if (strcmp(grp
->gr_mem
[i
] , name
) != 0)
1775 for (j
= i
; grp
->gr_mem
[j
] != NULL
; j
++)
1776 grp
->gr_mem
[j
] = grp
->gr_mem
[j
+1];
1777 chggrent(grp
->gr_name
, grp
);
1782 /* Add the user to the needed groups */
1783 for (i
= 0; i
< groups
->sl_cur
; i
++) {
1784 grp
= GETGRNAM(groups
->sl_str
[i
]);
1785 grp
= gr_add(grp
, pwd
->pw_name
);
1788 chggrent(grp
->gr_name
, grp
);
1792 /* In case of rename we need to walk over the different groups */
1795 while ((grp
= GETGRENT()) != NULL
) {
1796 if (grp
->gr_mem
== NULL
)
1798 for (i
= 0; grp
->gr_mem
[i
] != NULL
; i
++) {
1799 if (strcmp(grp
->gr_mem
[i
], name
) != 0)
1801 grp
->gr_mem
[i
] = newname
;
1802 chggrent(grp
->gr_name
, grp
);
1808 /* go get a current version of pwd */
1811 pwd
= GETPWNAM(name
);
1813 errx(EX_NOUSER
, "user '%s' disappeared during update", name
);
1814 grp
= GETGRGID(pwd
->pw_gid
);
1815 pw_log(cnf
, M_UPDATE
, W_USER
, "%s(%ju):%s(%ju):%s:%s:%s",
1816 pwd
->pw_name
, (uintmax_t)pwd
->pw_uid
,
1817 grp
? grp
->gr_name
: "unknown",
1818 (uintmax_t)(grp
? grp
->gr_gid
: (uid_t
)-1),
1819 pwd
->pw_gecos
, pwd
->pw_dir
, pwd
->pw_shell
);
1822 * Let's create and populate the user's home directory. Note
1823 * that this also `works' for editing users if -m is used, but
1824 * existing files will *not* be overwritten.
1826 if (PWALTDIR() != PWF_ALT
&& createhome
&& pwd
->pw_dir
&&
1827 *pwd
->pw_dir
== '/' && pwd
->pw_dir
[1]) {
1831 homemode
= cnf
->homemode
;
1832 create_and_populate_homedir(cnf
, pwd
, skel
, homemode
, true);
1835 if (nis
&& nis_update() == 0)
1836 pw_log(cnf
, M_UPDATE
, W_USER
, "NIS maps updated");
1838 return (EXIT_SUCCESS
);