1 /* $NetBSD: user.c,v 1.124 2009/10/15 23:03:02 hubertf Exp $ */
4 * Copyright (c) 1999 Alistair G. Crooks. All rights reserved.
5 * Copyright (c) 2005 Liam J. Foy. 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.
15 * 3. The name of the author may not be used to endorse or promote
16 * products derived from this software without specific prior written
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/cdefs.h>
34 __COPYRIGHT("@(#) Copyright (c) 1999\
35 The NetBSD Foundation, Inc. All rights reserved.");
36 __RCSID("$NetBSD: user.c,v 1.124 2009/10/15 23:03:02 hubertf Exp $");
39 #include <sys/types.h>
40 #include <sys/param.h>
49 #include <login_cap.h>
68 /* this struct describes a uid range */
69 typedef struct range_t
{
70 int r_from
; /* low uid */
71 int r_to
; /* high uid */
74 typedef struct rangelist_t
{
75 unsigned rl_rsize
; /* size of range array */
76 unsigned rl_rc
; /* # of ranges */
77 range_t
*rl_rv
; /* the ranges */
78 unsigned rl_defrc
; /* # of ranges in defaults */
81 /* this struct encapsulates the user and group information */
82 typedef struct user_t
{
83 int u_flags
; /* see below */
84 int u_uid
; /* uid of user */
85 char *u_password
; /* encrypted password */
86 char *u_comment
; /* comment field */
87 char *u_home
; /* home directory */
88 mode_t u_homeperm
; /* permissions of home dir */
89 char *u_primgrp
; /* primary group */
90 int u_groupc
; /* # of secondary groups */
91 const char *u_groupv
[NGROUPS_MAX
]; /* secondary groups */
92 char *u_shell
; /* user's shell */
93 char *u_basedir
; /* base directory for home */
94 char *u_expire
; /* when password will expire */
95 char *u_inactive
; /* when account will expire */
96 char *u_skeldir
; /* directory for startup files */
97 char *u_class
; /* login class */
98 rangelist_t u_r
; /* list of ranges */
99 unsigned u_defrc
; /* # of ranges in defaults */
100 int u_preserve
; /* preserve uids on deletion */
101 int u_allow_samba
; /* allow trailing '$' for samba login names */
102 int u_locked
; /* user account lock */
104 #define u_rsize u_r.rl_rsize
105 #define u_rc u_r.rl_rc
106 #define u_rv u_r.rl_rv
107 #define u_defrc u_r.rl_defrc
109 /* this struct encapsulates the user and group information */
110 typedef struct group_t
{
111 rangelist_t g_r
; /* list of ranges */
113 #define g_rsize g_r.rl_rsize
114 #define g_rc g_r.rl_rc
115 #define g_rv g_r.rl_rv
116 #define g_defrc g_r.rl_defrc
118 typedef struct def_t
{
123 /* flags for which fields of the user_t replace the passwd entry */
142 #define LOCKED "*LOCKED*"
144 #define PATH_LOGINCONF "/etc/login.conf"
147 #define DEF_GROUP "users"
151 #define DEF_BASEDIR "/home"
155 #define DEF_SKELDIR "/etc/skel"
159 #define DEF_SHELL _PATH_BSHELL
163 #define DEF_COMMENT ""
167 #define DEF_LOWUID 1000
171 #define DEF_HIGHUID 60000
175 #define DEF_INACTIVE 0
179 #define DEF_EXPIRE NULL
191 #define NOBODY_UID 32767
195 #define DEF_HOMEPERM 0755
198 /* some useful constants */
200 MaxShellNameLen
= 256,
201 MaxFileNameLen
= MAXPATHLEN
,
202 MaxUserNameLen
= LOGIN_NAME_MAX
- 1,
203 MaxCommandLen
= 2048,
205 PasswordLength
= 2048,
210 /* Full paths of programs used here */
211 #define CHMOD "/bin/chmod"
212 #define CHOWN "/usr/sbin/chown"
213 #define MKDIR "/bin/mkdir"
215 #define NOLOGIN "/sbin/nologin"
216 #define PAX "/bin/pax"
219 #define UNSET_INACTIVE "Null (unset)"
220 #define UNSET_EXPIRY "Null (unset)"
222 static int asystem(const char *fmt
, ...)
223 __attribute__((__format__(__printf__
, 1, 2)));
224 static int is_number(const char *);
225 static struct group
*find_group_info(const char *);
231 for (; *s
&& isspace((unsigned char)*s
) ; s
++) {
237 check_numeric(const char *val
, const char *name
)
239 if (!is_number(val
)) {
240 errx(EXIT_FAILURE
, "When using [-%c %s], "
241 "the %s must be numeric", *name
, name
, name
);
246 /* resize *cpp appropriately then assign `n' chars of `s' to it */
248 memsave(char **cpp
, const char *s
, size_t n
)
250 RENEW(char, *cpp
, n
+ 1, exit(1));
251 (void)memcpy(*cpp
, s
, n
);
255 /* a replacement for system(3) */
257 asystem(const char *fmt
, ...)
260 char buf
[MaxCommandLen
];
264 (void)vsnprintf(buf
, sizeof(buf
), fmt
, vp
);
267 (void)printf("Command: %s\n", buf
);
269 if ((ret
= system(buf
)) != 0) {
270 warn("Error running `%s'", buf
);
275 /* remove a users home directory, returning 1 for success (ie, no problems encountered) */
277 removehomedir(struct passwd
*pwp
)
281 /* userid not root? */
282 if (pwp
->pw_uid
== 0) {
283 warnx("Not deleting home directory `%s'; userid is 0", pwp
->pw_dir
);
287 /* directory exists (and is a directory!) */
288 if (stat(pwp
->pw_dir
, &st
) < 0) {
289 warn("Cannot access home directory `%s'", pwp
->pw_dir
);
292 if (!S_ISDIR(st
.st_mode
)) {
293 warnx("Home directory `%s' is not a directory", pwp
->pw_dir
);
297 /* userid matches directory owner? */
298 if (st
.st_uid
!= pwp
->pw_uid
) {
299 warnx("User `%s' doesn't own directory `%s', not removed",
300 pwp
->pw_name
, pwp
->pw_dir
);
304 (void)seteuid(pwp
->pw_uid
);
305 /* we add the "|| true" to keep asystem() quiet if there is a non-zero exit status. */
306 (void)asystem("%s -rf %s > /dev/null 2>&1 || true", RM
, pwp
->pw_dir
);
308 if (rmdir(pwp
->pw_dir
) < 0) {
309 warn("Unable to remove all files in `%s'", pwp
->pw_dir
);
315 /* return 1 if all of `s' is numeric */
317 is_number(const char *s
)
320 if (!isdigit((unsigned char) *s
)) {
328 * check that the effective uid is 0 - called from funcs which will
329 * modify data and config files.
334 if (geteuid() != 0) {
335 errx(EXIT_FAILURE
, "Program must be run as root");
339 /* copy any dot files into the user's home directory */
341 copydotfiles(char *skeldir
, int uid
, int gid
, char *dir
, mode_t homeperm
)
347 if ((dirp
= opendir(skeldir
)) == NULL
) {
348 warn("Can't open source . files dir `%s'", skeldir
);
351 for (n
= 0; (dp
= readdir(dirp
)) != NULL
&& n
== 0 ; ) {
352 if (strcmp(dp
->d_name
, ".") == 0 ||
353 strcmp(dp
->d_name
, "..") == 0) {
358 (void)closedir(dirp
);
360 warnx("No \"dot\" initialisation files found");
362 (void)asystem("cd %s && %s -rw -pe %s . %s",
363 skeldir
, PAX
, (verbose
) ? "-v" : "", dir
);
365 (void)asystem("%s -R -h %d:%d %s", CHOWN
, uid
, gid
, dir
);
366 (void)asystem("%s -R u+w %s", CHMOD
, dir
);
368 (void)asystem("%s 0%o %s", CHMOD
, homeperm
, dir
);
373 /* create a group entry with gid `gid' */
375 creategid(char *group
, int gid
, const char *name
)
380 char buf
[MaxEntryLen
];
381 char f
[MaxFileNameLen
];
385 if (getgrnam(group
) != NULL
) {
386 warnx("Can't create group `%s': already exists", group
);
389 if ((from
= fopen(_PATH_GROUP
, "r")) == NULL
) {
390 warn("Can't create group `%s': can't open `%s'", name
,
394 if (flock(fileno(from
), LOCK_EX
| LOCK_NB
) < 0) {
395 warn("Can't lock `%s'", _PATH_GROUP
);
399 (void)fstat(fileno(from
), &st
);
400 (void)snprintf(f
, sizeof(f
), "%s.XXXXXX", _PATH_GROUP
);
401 if ((fd
= mkstemp(f
)) < 0) {
402 warn("Can't create group `%s': mkstemp failed", group
);
406 if ((to
= fdopen(fd
, "w")) == NULL
) {
407 warn("Can't create group `%s': fdopen `%s' failed",
414 while ((cc
= fread(buf
, sizeof(char), sizeof(buf
), from
)) > 0) {
415 if (fwrite(buf
, sizeof(char), (unsigned) cc
, to
) != cc
) {
416 warn("Can't create group `%s': short write to `%s'",
424 (void)fprintf(to
, "%s:*:%d:%s\n", group
, gid
, name
);
427 if (rename(f
, _PATH_GROUP
) < 0) {
428 warn("Can't create group `%s': can't rename `%s' to `%s'",
429 group
, f
, _PATH_GROUP
);
433 (void)chmod(_PATH_GROUP
, st
.st_mode
& 07777);
434 syslog(LOG_INFO
, "New group added: name=%s, gid=%d", group
, gid
);
438 /* modify the group entry with name `group' to be newent */
440 modify_gid(char *group
, char *newent
)
445 char buf
[MaxEntryLen
];
446 char f
[MaxFileNameLen
];
453 if ((from
= fopen(_PATH_GROUP
, "r")) == NULL
) {
454 warn("Can't modify group `%s': can't open `%s'",
458 if (flock(fileno(from
), LOCK_EX
| LOCK_NB
) < 0) {
459 warn("Can't modify group `%s': can't lock `%s'",
464 (void)fstat(fileno(from
), &st
);
465 (void)snprintf(f
, sizeof(f
), "%s.XXXXXX", _PATH_GROUP
);
466 if ((fd
= mkstemp(f
)) < 0) {
467 warn("Can't modify group `%s': mkstemp failed", group
);
471 if ((to
= fdopen(fd
, "w")) == NULL
) {
472 warn("Can't modify group `%s': fdopen `%s' failed", group
, f
);
478 groupc
= strlen(group
);
479 while (fgets(buf
, sizeof(buf
), from
) != NULL
) {
481 if ((colon
= strchr(buf
, ':')) == NULL
) {
482 warnx("Badly formed entry `%s'", buf
);
485 entc
= (int)(colon
- buf
);
486 if (entc
== groupc
&&
487 strncmp(group
, buf
, (unsigned) entc
) == 0) {
488 if (newent
== NULL
) {
489 struct group
*grp_rm
;
490 struct passwd
*user_pwd
;
493 * Check that the group being removed
494 * isn't any user's Primary group. Just
495 * warn if it is. This could cause problems
496 * if the group GID was reused for a
500 grp_rm
= find_group_info(group
);
501 while ((user_pwd
= getpwent()) != NULL
) {
502 if (user_pwd
->pw_gid
== grp_rm
->gr_gid
) {
503 warnx("Warning: group `%s'(%d)"
504 " is the primary group of"
505 " `%s'. Use caution if you"
506 " later add this GID.",
508 grp_rm
->gr_gid
, user_pwd
->pw_name
);
515 (void)strlcpy(buf
, newent
, sizeof(buf
));
518 if (fwrite(buf
, sizeof(char), (unsigned) cc
, to
) != cc
) {
519 warn("Can't modify group `%s': short write to `%s'",
529 if (rename(f
, _PATH_GROUP
) < 0) {
530 warn("Can't modify group `%s': can't rename `%s' to `%s'",
531 group
, f
, _PATH_GROUP
);
535 (void)chmod(_PATH_GROUP
, st
.st_mode
& 07777);
536 if (newent
== NULL
) {
537 syslog(LOG_INFO
, "group deleted: name=%s", group
);
539 syslog(LOG_INFO
, "group information modified: name=%s", group
);
544 /* modify the group entries for all `groups', by adding `user' */
546 append_group(char *user
, int ngroups
, const char **groups
)
552 char buf
[MaxEntryLen
];
553 char f
[MaxFileNameLen
];
563 for (i
= 0 ; i
< ngroups
; i
++) {
564 if ((grp
= getgrnam(groups
[i
])) == NULL
) {
565 warnx("Can't append group `%s' for user `%s'",
568 for (j
= 0 ; grp
->gr_mem
[j
] ; j
++) {
569 if (strcmp(user
, grp
->gr_mem
[j
]) == 0) {
576 if ((from
= fopen(_PATH_GROUP
, "r")) == NULL
) {
577 warn("Can't append group(s) for `%s': can't open `%s'",
581 if (flock(fileno(from
), LOCK_EX
| LOCK_NB
) < 0) {
582 warn("Can't append group(s) for `%s': can't lock `%s'",
587 (void)fstat(fileno(from
), &st
);
588 (void)snprintf(f
, sizeof(f
), "%s.XXXXXX", _PATH_GROUP
);
589 if ((fd
= mkstemp(f
)) < 0) {
590 warn("Can't append group(s) for `%s': mkstemp failed",
595 if ((to
= fdopen(fd
, "w")) == NULL
) {
596 warn("Can't append group(s) for `%s': fdopen `%s' failed",
603 while (fgets(buf
, sizeof(buf
), from
) != NULL
) {
605 if ((colon
= strchr(buf
, ':')) == NULL
) {
606 warnx("Badly formed entry `%s'", buf
);
609 entc
= (int)(colon
- buf
);
610 for (i
= 0 ; i
< ngroups
; i
++) {
611 if ((groupc
= strlen(groups
[i
])) == 0) {
614 if (entc
== groupc
&&
615 strncmp(groups
[i
], buf
, (unsigned) entc
) == 0) {
616 if ((nc
= snprintf(&buf
[cc
- 1],
617 sizeof(buf
) - cc
+ 1, "%s%s\n",
618 (buf
[cc
- 2] == ':') ? "" : ",", user
)) < 0) {
619 warnx("Warning: group `%s' "
620 "entry too long", groups
[i
]);
625 if (fwrite(buf
, sizeof(char), (unsigned) cc
, to
) != cc
) {
626 warn("Can't append group(s) for `%s':"
627 " short write to `%s'", user
, f
);
636 if (rename(f
, _PATH_GROUP
) < 0) {
637 warn("Can't append group(s) for `%s': "
638 "can't rename `%s' to `%s'", user
, f
, _PATH_GROUP
);
642 (void)chmod(_PATH_GROUP
, st
.st_mode
& 07777);
646 /* the valid characters for login and group names */
647 #define VALID_CHAR(c) (isalnum(c) || (c) == '.' || (c) == '_' || (c) == '-')
649 /* return 1 if `login' is a valid login name */
651 valid_login(char *login_name
, int allow_samba
)
655 /* First character of a login name cannot be '-'. */
656 if (*login_name
== '-') {
659 if (strlen(login_name
) >= LOGIN_NAME_MAX
) {
662 for (cp
= (unsigned char *)login_name
; *cp
; cp
++) {
663 if (!VALID_CHAR(*cp
)) {
665 /* check for a trailing '$' in a Samba user name */
666 if (allow_samba
&& *cp
== '$' && *(cp
+ 1) == 0x0) {
676 /* return 1 if `group' is a valid group name */
678 valid_group(char *group
)
682 for (cp
= (unsigned char *)group
; *cp
; cp
++) {
683 if (!VALID_CHAR(*cp
)) {
690 /* find the next gid in the range lo .. hi */
692 getnextgid(int *gidp
, int lo
, int hi
)
694 for (*gidp
= lo
; *gidp
< hi
; *gidp
+= 1) {
695 if (getgrgid((gid_t
)*gidp
) == NULL
) {
703 /* save a range of uids */
705 save_range(rangelist_t
*rlp
, char *cp
)
711 if (rlp
->rl_rsize
== 0) {
713 NEWARRAY(range_t
, rlp
->rl_rv
, rlp
->rl_rsize
, return(0));
714 } else if (rlp
->rl_rc
== rlp
->rl_rsize
) {
716 RENEW(range_t
, rlp
->rl_rv
, rlp
->rl_rsize
, return(0));
718 if (rlp
->rl_rv
&& sscanf(cp
, "%d..%d", &from
, &to
) == 2) {
719 for (i
= rlp
->rl_defrc
; i
< rlp
->rl_rc
; i
++) {
720 if (rlp
->rl_rv
[i
].r_from
== from
&&
721 rlp
->rl_rv
[i
].r_to
== to
) {
725 if (i
== rlp
->rl_rc
) {
726 rlp
->rl_rv
[rlp
->rl_rc
].r_from
= from
;
727 rlp
->rl_rv
[rlp
->rl_rc
].r_to
= to
;
731 warnx("Bad range `%s'", cp
);
738 /* set the defaults in the defaults file */
740 setdefaults(user_t
*up
)
742 char template[MaxFileNameLen
];
750 (void)snprintf(template, sizeof(template), "%s.XXXXXX",
751 _PATH_USERMGMT_CONF
);
752 if ((fd
= mkstemp(template)) < 0) {
753 warn("Can't set defaults: can't mkstemp `%s' for writing",
754 _PATH_USERMGMT_CONF
);
757 if ((fp
= fdopen(fd
, "w")) == NULL
) {
758 warn("Can't set defaults: can't fdopen `%s' for writing",
759 _PATH_USERMGMT_CONF
);
763 if (fprintf(fp
, "group\t\t%s\n", up
->u_primgrp
) <= 0 ||
764 fprintf(fp
, "base_dir\t%s\n", up
->u_basedir
) <= 0 ||
765 fprintf(fp
, "skel_dir\t%s\n", up
->u_skeldir
) <= 0 ||
766 fprintf(fp
, "shell\t\t%s\n", up
->u_shell
) <= 0 ||
768 fprintf(fp
, "class\t\t%s\n", up
->u_class
) <= 0 ||
769 fprintf(fp
, "homeperm\t0%o\n", up
->u_homeperm
) <= 0 ||
771 fprintf(fp
, "inactive\t%s\n", (up
->u_inactive
== NULL
) ?
772 UNSET_INACTIVE
: up
->u_inactive
) <= 0 ||
773 fprintf(fp
, "expire\t\t%s\n", (up
->u_expire
== NULL
) ?
774 UNSET_EXPIRY
: up
->u_expire
) <= 0 ||
775 fprintf(fp
, "preserve\t%s\n", (up
->u_preserve
== 0) ?
776 "false" : "true") <= 0) {
777 warn("Can't write to `%s'", _PATH_USERMGMT_CONF
);
781 for (i
= (up
->u_defrc
!= up
->u_rc
) ? up
->u_defrc
: 0;
782 i
< up
->u_rc
; i
++) {
783 if (fprintf(fp
, "range\t\t%d..%d\n", up
->u_rv
[i
].r_from
,
784 up
->u_rv
[i
].r_to
) <= 0) {
785 warn("Can't set defaults: can't write to `%s'",
786 _PATH_USERMGMT_CONF
);
793 ret
= ((rename(template, _PATH_USERMGMT_CONF
) == 0) &&
794 (chmod(_PATH_USERMGMT_CONF
, 0644) == 0));
799 /* read the defaults file */
801 read_defaults(def_t
*dp
)
809 user_t
*up
= &dp
->user
;
810 group_t
*gp
= &dp
->group
;
812 (void)memset(dp
, 0, sizeof(*dp
));
814 memsave(&up
->u_primgrp
, DEF_GROUP
, strlen(DEF_GROUP
));
815 memsave(&up
->u_basedir
, DEF_BASEDIR
, strlen(DEF_BASEDIR
));
816 memsave(&up
->u_skeldir
, DEF_SKELDIR
, strlen(DEF_SKELDIR
));
817 memsave(&up
->u_shell
, DEF_SHELL
, strlen(DEF_SHELL
));
818 memsave(&up
->u_comment
, DEF_COMMENT
, strlen(DEF_COMMENT
));
820 memsave(&up
->u_class
, DEF_CLASS
, strlen(DEF_CLASS
));
824 NEWARRAY(range_t
, up
->u_rv
, up
->u_rsize
, exit(1));
825 up
->u_inactive
= DEF_INACTIVE
;
826 up
->u_expire
= DEF_EXPIRE
;
829 NEWARRAY(range_t
, gp
->g_rv
, gp
->g_rsize
, exit(1));
830 if ((fp
= fopen(_PATH_USERMGMT_CONF
, "r")) == NULL
) {
831 if (stat(_PATH_USERMGMT_CONF
, &st
) < 0 && !setdefaults(up
)) {
832 warn("Can't create `%s' defaults file",
833 _PATH_USERMGMT_CONF
);
835 fp
= fopen(_PATH_USERMGMT_CONF
, "r");
838 while ((s
= fparseln(fp
, &len
, &lineno
, NULL
, 0)) != NULL
) {
839 if (strncmp(s
, "group", 5) == 0) {
840 cp
= skipspace(s
+ 5);
841 memsave(&up
->u_primgrp
, (char *)cp
, strlen(cp
));
842 } else if (strncmp(s
, "base_dir", 8) == 0) {
843 cp
= skipspace(s
+ 8);
844 memsave(&up
->u_basedir
, (char *)cp
, strlen(cp
));
845 } else if (strncmp(s
, "skel_dir", 8) == 0) {
846 cp
= skipspace(s
+ 8);
847 memsave(&up
->u_skeldir
, (char *)cp
, strlen(cp
));
848 } else if (strncmp(s
, "shell", 5) == 0) {
849 cp
= skipspace(s
+ 5);
850 memsave(&up
->u_shell
, cp
, strlen(cp
));
852 } else if (strncmp((char *)s
, "class", 5) == 0) {
853 cp
= skipspace(s
+ 5);
854 memsave(&up
->u_class
, cp
, strlen(cp
));
857 } else if (strncmp(s
, "homeperm", 8) == 0) {
858 for (cp
= s
+ 8; *cp
&&
859 isspace((unsigned char)*cp
); cp
++)
861 up
->u_homeperm
= strtoul(cp
, NULL
, 8);
863 } else if (strncmp(s
, "inactive", 8) == 0) {
864 cp
= skipspace(s
+ 8);
865 if (strcmp(cp
, UNSET_INACTIVE
) == 0) {
866 if (up
->u_inactive
) {
867 FREE(up
->u_inactive
);
869 up
->u_inactive
= NULL
;
871 memsave(&up
->u_inactive
, cp
, strlen(cp
));
874 } else if (strncmp(s
, "range", 5) == 0) {
875 cp
= skipspace(s
+ 5);
876 (void)save_range(&up
->u_r
, cp
);
879 } else if (strncmp(s
, "preserve", 8) == 0) {
880 cp
= skipspace(s
+ 8);
882 (strncmp(cp
, "true", 4) == 0) ? 1 :
883 (strncmp(cp
, "yes", 3) == 0) ? 1 : atoi(cp
);
885 } else if (strncmp(s
, "expire", 6) == 0) {
886 cp
= skipspace(s
+ 6);
887 if (strcmp(cp
, UNSET_EXPIRY
) == 0) {
893 memsave(&up
->u_expire
, cp
, strlen(cp
));
896 } else if (strncmp(s
, "gid_range", 9) == 0) {
897 cp
= skipspace(s
+ 9);
898 (void)save_range(&gp
->g_r
, cp
);
906 up
->u_rv
[up
->u_rc
].r_from
= DEF_LOWUID
;
907 up
->u_rv
[up
->u_rc
].r_to
= DEF_HIGHUID
;
910 up
->u_defrc
= up
->u_rc
;
911 up
->u_homeperm
= DEF_HOMEPERM
;
914 /* return the next valid unused uid */
916 getnextuid(int sync_uid_gid
, int *uid
, int low_uid
, int high_uid
)
918 for (*uid
= low_uid
; *uid
<= high_uid
; (*uid
)++) {
919 if (getpwuid((uid_t
)(*uid
)) == NULL
&& *uid
!= NOBODY_UID
) {
921 if (getgrgid((gid_t
)(*uid
)) == NULL
) {
932 /* structure which defines a password type */
933 typedef struct passwd_type_t
{
934 const char *type
; /* optional type descriptor */
935 size_t desc_length
; /* length of type descriptor */
936 size_t length
; /* length of password */
937 const char *regex
; /* regexp to output the password */
938 size_t re_sub
; /* subscript of regexp to use */
941 static passwd_type_t passwd_types
[] = {
942 { "$sha1", 5, 28, "\\$[^$]+\\$[^$]+\\$[^$]+\\$(.*)", 1 }, /* SHA1 */
943 { "$2a", 3, 53, "\\$[^$]+\\$[^$]+\\$(.*)", 1 }, /* Blowfish */
944 { "$1", 2, 34, NULL
, 0 }, /* MD5 */
945 { "", 0, DES_Len
,NULL
, 0 }, /* standard DES */
946 { NULL
, (size_t)~0, (size_t)~0, NULL
, 0 }
947 /* none - terminate search */
950 /* return non-zero if it's a valid password - check length for cipher type */
952 valid_password_length(char *newpasswd
)
955 regmatch_t matchv
[10];
958 for (pwtp
= passwd_types
; pwtp
->desc_length
!= (size_t)~0; pwtp
++) {
959 if (strncmp(newpasswd
, pwtp
->type
, pwtp
->desc_length
) == 0) {
960 if (pwtp
->regex
== NULL
) {
961 return strlen(newpasswd
) == pwtp
->length
;
963 (void)regcomp(&r
, pwtp
->regex
, REG_EXTENDED
);
964 if (regexec(&r
, newpasswd
, 10, matchv
, 0) == 0) {
966 return (int)(matchv
[pwtp
->re_sub
].rm_eo
-
967 matchv
[pwtp
->re_sub
].rm_so
) ==
977 /* return 1 if `class' is a valid login class */
979 valid_class(char *class)
983 if (class == NULL
|| *class == '\0') {
987 * Check if /etc/login.conf exists. login_getclass() will
988 * return 1 due to it not existing, so not informing the
989 * user the actual login class does not exist.
992 if (access(PATH_LOGINCONF
, R_OK
) == -1) {
993 warn("Access failed for `%s'; will not validate class `%s'",
994 PATH_LOGINCONF
, class);
998 if ((lc
= login_getclass(class)) != NULL
) {
1005 /* return 1 if the `shellname' is a valid user shell */
1007 valid_shell(const char *shellname
)
1011 if (access(_PATH_SHELLS
, R_OK
) == -1) {
1013 warn("Access failed for `%s'; will not validate shell `%s'",
1014 _PATH_SHELLS
, shellname
);
1018 /* if nologin is used as a shell, consider it a valid shell */
1019 if (strcmp(shellname
, NOLOGIN
) == 0)
1022 while ((shellp
= getusershell()) != NULL
)
1023 if (strcmp(shellp
, shellname
) == 0)
1026 warnx("Shell `%s' not found in `%s'", shellname
, _PATH_SHELLS
);
1028 return access(shellname
, X_OK
) != -1;
1032 /* look for a valid time, return 0 if it was specified but bad */
1034 scantime(time_t *tp
, char *s
)
1042 (void)memset(&tm
, 0, sizeof(tm
));
1043 if (strptime(s
, "%c", &tm
) != NULL
) {
1045 return (*tp
== -1) ? 0 : 1;
1046 } else if (strptime(s
, "%B %d %Y", &tm
) != NULL
) {
1048 return (*tp
== -1) ? 0 : 1;
1051 *tp
= val
= strtol(s
, &ep
, 10);
1052 if (*ep
!= '\0' || *tp
< -1 || errno
== ERANGE
) {
1066 adduser(char *login_name
, user_t
*up
)
1072 char password
[PasswordLength
+ 1];
1073 char home
[MaxFileNameLen
];
1074 char buf
[MaxFileNameLen
];
1082 if (!valid_login(login_name
, up
->u_allow_samba
)) {
1083 errx(EXIT_FAILURE
, "Can't add user `%s': invalid login name", login_name
);
1086 if (!valid_class(up
->u_class
)) {
1087 errx(EXIT_FAILURE
, "Can't add user `%s': no such login class `%s'",
1088 login_name
, up
->u_class
);
1091 if ((masterfd
= open(_PATH_MASTERPASSWD
, O_RDONLY
)) < 0) {
1092 err(EXIT_FAILURE
, "Can't add user `%s': can't open `%s'",
1093 login_name
, _PATH_MASTERPASSWD
);
1095 if (flock(masterfd
, LOCK_EX
| LOCK_NB
) < 0) {
1096 err(EXIT_FAILURE
, "Can't add user `%s': can't lock `%s'",
1097 login_name
, _PATH_MASTERPASSWD
);
1100 if ((ptmpfd
= pw_lock(WAITSECS
)) < 0) {
1102 (void)close(masterfd
);
1104 err(EXIT_FAILURE
, "Can't add user `%s': can't obtain pw_lock",
1107 while ((cc
= read(masterfd
, buf
, sizeof(buf
))) > 0) {
1108 if (write(ptmpfd
, buf
, (size_t)(cc
)) != cc
) {
1110 (void)close(masterfd
);
1111 (void)close(ptmpfd
);
1114 err(EXIT_FAILURE
, "Can't add user `%s': "
1115 "short write to /etc/ptmp", login_name
);
1118 /* if no uid was specified, get next one in [low_uid..high_uid] range */
1119 sync_uid_gid
= (strcmp(up
->u_primgrp
, "=uid") == 0);
1120 if (up
->u_uid
== -1) {
1124 * Look for a free UID in the command line ranges (if any).
1125 * These start after the ranges specified in the config file.
1127 for (i
= up
->u_defrc
; !got_id
&& i
< up
->u_rc
; i
++) {
1128 got_id
= getnextuid(sync_uid_gid
, &up
->u_uid
,
1129 up
->u_rv
[i
].r_from
, up
->u_rv
[i
].r_to
);
1132 * If there were no free UIDs in the command line ranges,
1133 * try the ranges from the config file (there will always
1134 * be at least one default).
1136 for (i
= 0; !got_id
&& i
< up
->u_defrc
; i
++) {
1137 got_id
= getnextuid(sync_uid_gid
, &up
->u_uid
,
1138 up
->u_rv
[i
].r_from
, up
->u_rv
[i
].r_to
);
1141 (void)close(ptmpfd
);
1143 errx(EXIT_FAILURE
, "Can't add user `%s': "
1144 "can't get next uid for %d", login_name
,
1148 /* check uid isn't already allocated */
1149 if (!(up
->u_flags
& F_DUPUID
) && getpwuid((uid_t
)(up
->u_uid
)) != NULL
) {
1150 (void)close(ptmpfd
);
1152 errx(EXIT_FAILURE
, "Can't add user `%s': "
1153 "uid %d is already in use", login_name
, up
->u_uid
);
1155 /* if -g=uid was specified, check gid is unused */
1157 if (getgrgid((gid_t
)(up
->u_uid
)) != NULL
) {
1158 (void)close(ptmpfd
);
1160 errx(EXIT_FAILURE
, "Can't add user `%s': "
1161 "gid %d is already in use", login_name
,
1165 } else if ((grp
= getgrnam(up
->u_primgrp
)) != NULL
) {
1167 } else if (is_number(up
->u_primgrp
) &&
1168 (grp
= getgrgid((gid_t
)atoi(up
->u_primgrp
))) != NULL
) {
1171 (void)close(ptmpfd
);
1173 errx(EXIT_FAILURE
, "Can't add user `%s': group %s not found",
1174 login_name
, up
->u_primgrp
);
1176 /* check name isn't already in use */
1177 if (!(up
->u_flags
& F_DUPUID
) && getpwnam(login_name
) != NULL
) {
1178 (void)close(ptmpfd
);
1180 errx(EXIT_FAILURE
, "Can't add user `%s': "
1181 "`%s' is already a user", login_name
, login_name
);
1183 if (up
->u_flags
& F_HOMEDIR
) {
1184 (void)strlcpy(home
, up
->u_home
, sizeof(home
));
1186 /* if home directory hasn't been given, make it up */
1187 (void)snprintf(home
, sizeof(home
), "%s/%s", up
->u_basedir
,
1190 if (up
->u_flags
& F_SHELL
) {
1192 if (!valid_shell(up
->u_shell
)) {
1194 (void)close(ptmpfd
);
1197 errx(EXIT_FAILURE
, "Can't add user `%s': "
1198 "Cannot access shell `%s'",
1199 login_name
, up
->u_shell
);
1204 if (!scantime(&inactive
, up
->u_inactive
)) {
1205 warnx("Warning: inactive time `%s' invalid, password expiry off",
1208 if (!scantime(&expire
, up
->u_expire
) || expire
== -1) {
1209 warnx("Warning: expire time `%s' invalid, account expiry off",
1211 expire
= 0; /* Just in case. */
1213 if (lstat(home
, &st
) < 0 && !(up
->u_flags
& F_MKDIR
)) {
1214 warnx("Warning: home directory `%s' doesn't exist, "
1215 "and -m was not specified", home
);
1217 password
[sizeof(password
) - 1] = '\0';
1218 if (up
->u_password
!= NULL
&& valid_password_length(up
->u_password
)) {
1219 (void)strlcpy(password
, up
->u_password
, sizeof(password
));
1221 (void)memset(password
, '*', DES_Len
);
1222 password
[DES_Len
] = 0;
1223 if (up
->u_password
!= NULL
) {
1224 warnx("Password `%s' is invalid: setting it to `%s'",
1225 up
->u_password
, password
);
1228 cc
= snprintf(buf
, sizeof(buf
), "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
1243 if (write(ptmpfd
, buf
, (size_t) cc
) != cc
) {
1245 (void)close(ptmpfd
);
1248 err(EXIT_FAILURE
, "Can't add user `%s': write failed",
1251 if (up
->u_flags
& F_MKDIR
) {
1252 if (lstat(home
, &st
) == 0) {
1253 (void)close(ptmpfd
);
1256 "Can't add user `%s': home directory `%s' "
1257 "already exists", login_name
, home
);
1259 if (asystem("%s -p %s", MKDIR
, home
) != 0) {
1260 (void)close(ptmpfd
);
1262 errx(EXIT_FAILURE
, "Can't add user `%s': "
1263 "can't mkdir `%s'", login_name
, home
);
1265 (void)copydotfiles(up
->u_skeldir
, up
->u_uid
, gid
, home
,
1269 if (strcmp(up
->u_primgrp
, "=uid") == 0 &&
1270 getgrnam(login_name
) == NULL
&&
1271 !creategid(login_name
, gid
, login_name
)) {
1272 (void)close(ptmpfd
);
1274 errx(EXIT_FAILURE
, "Can't add user `%s': can't create gid %d ",
1277 if (up
->u_groupc
> 0 &&
1278 !append_group(login_name
, up
->u_groupc
, up
->u_groupv
)) {
1279 (void)close(ptmpfd
);
1281 errx(EXIT_FAILURE
, "Can't add user `%s': can't append "
1282 "to new groups", login_name
);
1284 (void)close(ptmpfd
);
1285 #if PW_MKDB_ARGC == 2
1286 if (pw_mkdb(login_name
, 0) < 0)
1292 errx(EXIT_FAILURE
, "Can't add user `%s': pw_mkdb failed",
1295 syslog(LOG_INFO
, "New user added: name=%s, uid=%d, gid=%d, home=%s, "
1296 "shell=%s", login_name
, up
->u_uid
, gid
, home
, up
->u_shell
);
1300 /* remove a user from the groups file */
1302 rm_user_from_groups(char *login_name
)
1305 regmatch_t matchv
[10];
1309 char line
[MaxEntryLen
];
1310 char buf
[MaxEntryLen
];
1311 char f
[MaxFileNameLen
];
1316 (void)snprintf(line
, sizeof(line
), "(:|,)(%s)(,|$)", login_name
);
1317 if ((sc
= regcomp(&r
, line
, REG_EXTENDED
|REG_NEWLINE
)) != 0) {
1318 (void)regerror(sc
, &r
, buf
, sizeof(buf
));
1319 warnx("Can't compile regular expression `%s' (%s)", line
,
1323 if ((from
= fopen(_PATH_GROUP
, "r")) == NULL
) {
1324 warn("Can't remove user `%s' from `%s': can't open `%s'",
1325 login_name
, _PATH_GROUP
, _PATH_GROUP
);
1328 if (flock(fileno(from
), LOCK_EX
| LOCK_NB
) < 0) {
1329 warn("Can't remove user `%s' from `%s': can't lock `%s'",
1330 login_name
, _PATH_GROUP
, _PATH_GROUP
);
1334 (void)fstat(fileno(from
), &st
);
1335 (void)snprintf(f
, sizeof(f
), "%s.XXXXXX", _PATH_GROUP
);
1336 if ((fd
= mkstemp(f
)) < 0) {
1337 warn("Can't remove user `%s' from `%s': mkstemp failed",
1338 login_name
, _PATH_GROUP
);
1342 if ((to
= fdopen(fd
, "w")) == NULL
) {
1343 warn("Can't remove user `%s' from `%s': fdopen `%s' failed",
1344 login_name
, _PATH_GROUP
, f
);
1350 while (fgets(buf
, sizeof(buf
), from
) != NULL
) {
1352 if (regexec(&r
, buf
, 10, matchv
, 0) == 0) {
1353 if (buf
[(int)matchv
[1].rm_so
] == ',') {
1354 matchv
[2].rm_so
= matchv
[1].rm_so
;
1355 } else if (matchv
[2].rm_eo
!= matchv
[3].rm_eo
) {
1356 matchv
[2].rm_eo
= matchv
[3].rm_eo
;
1358 cc
-= (int) matchv
[2].rm_eo
;
1359 sc
= (int) matchv
[2].rm_so
;
1360 if (fwrite(buf
, sizeof(char), (size_t)sc
, to
) != sc
||
1361 fwrite(&buf
[(int)matchv
[2].rm_eo
], sizeof(char),
1362 (size_t)cc
, to
) != cc
) {
1363 warn("Can't remove user `%s' from `%s': "
1364 "short write to `%s'", login_name
,
1371 } else if (fwrite(buf
, sizeof(char), (unsigned) cc
, to
) != cc
) {
1372 warn("Can't remove user `%s' from `%s': "
1373 "short write to `%s'", login_name
, _PATH_GROUP
, f
);
1382 if (rename(f
, _PATH_GROUP
) < 0) {
1383 warn("Can't remove user `%s' from `%s': "
1384 "can't rename `%s' to `%s'",
1385 login_name
, _PATH_GROUP
, f
, _PATH_GROUP
);
1389 (void)chmod(_PATH_GROUP
, st
.st_mode
& 07777);
1393 /* check that the user or group is local, not from YP/NIS */
1395 is_local(char *name
, const char *file
)
1398 char buf
[MaxEntryLen
];
1402 if ((fp
= fopen(file
, "r")) == NULL
) {
1403 err(EXIT_FAILURE
, "Can't open `%s'", file
);
1406 for (ret
= 0 ; fgets(buf
, sizeof(buf
), fp
) != NULL
; ) {
1407 if (strncmp(buf
, name
, len
) == 0 && buf
[len
] == ':') {
1418 moduser(char *login_name
, char *newlogin
, user_t
*up
, int allow_samba
)
1420 struct passwd
*pwp
, pw
;
1422 const char *homedir
;
1428 char newdir
[MaxFileNameLen
];
1429 char buf
[MaxEntryLen
];
1430 char pwbuf
[MaxEntryLen
];
1436 if (!valid_login(newlogin
, allow_samba
)) {
1437 errx(EXIT_FAILURE
, "Can't modify user `%s': invalid login name",
1440 if (getpwnam_r(login_name
, &pw
, pwbuf
, sizeof(pwbuf
), &pwp
) != 0
1442 errx(EXIT_FAILURE
, "Can't modify user `%s': no such user",
1445 if (!is_local(login_name
, _PATH_MASTERPASSWD
)) {
1446 errx(EXIT_FAILURE
, "Can't modify user `%s': must be a local user",
1449 /* keep dir name in case we need it for '-m' */
1450 homedir
= pwp
->pw_dir
;
1452 if ((masterfd
= open(_PATH_MASTERPASSWD
, O_RDONLY
)) < 0) {
1453 err(EXIT_FAILURE
, "Can't modify user `%s': can't open `%s'",
1454 login_name
, _PATH_MASTERPASSWD
);
1456 if (flock(masterfd
, LOCK_EX
| LOCK_NB
) < 0) {
1457 err(EXIT_FAILURE
, "Can't modify user `%s': can't lock `%s'",
1458 login_name
, _PATH_MASTERPASSWD
);
1461 if ((ptmpfd
= pw_lock(WAITSECS
)) < 0) {
1463 (void)close(masterfd
);
1465 err(EXIT_FAILURE
, "Can't modify user `%s': "
1466 "can't obtain pw_lock", login_name
);
1468 if ((master
= fdopen(masterfd
, "r")) == NULL
) {
1470 (void)close(masterfd
);
1471 (void)close(ptmpfd
);
1474 err(EXIT_FAILURE
, "Can't modify user `%s': "
1475 "fdopen fd for %s", login_name
, _PATH_MASTERPASSWD
);
1478 if (up
->u_flags
& F_USERNAME
) {
1481 * check new name isn't already in use
1483 if (strcmp(login_name
, newlogin
) != 0 &&
1484 getpwnam(newlogin
) != NULL
) {
1485 (void)close(ptmpfd
);
1487 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1488 "`%s' is already a user", login_name
,
1491 pwp
->pw_name
= newlogin
;
1494 * Provide a new directory name in case the
1495 * home directory is to be moved.
1497 if (up
->u_flags
& F_MKDIR
) {
1498 (void)snprintf(newdir
, sizeof(newdir
), "%s/%s",
1499 up
->u_basedir
, newlogin
);
1500 pwp
->pw_dir
= newdir
;
1503 if (up
->u_flags
& F_PASSWORD
) {
1504 if (up
->u_password
!= NULL
) {
1505 if (!valid_password_length(up
->u_password
)) {
1506 (void)close(ptmpfd
);
1509 "Can't modify user `%s': "
1510 "invalid password: `%s'",
1511 login_name
, up
->u_password
);
1514 strstr(pwp
->pw_passwd
, LOCKED
)) != NULL
) {
1516 * account is locked - keep it locked
1517 * and just change the password.
1519 if (asprintf(&locked_pwd
, "%s%s",
1520 LOCKED
, up
->u_password
) == -1) {
1521 (void)close(ptmpfd
);
1524 "Can't modify user `%s': "
1528 pwp
->pw_passwd
= locked_pwd
;
1530 pwp
->pw_passwd
= up
->u_password
;
1535 /* check whether we should lock the account. */
1536 if (up
->u_locked
== LOCK
) {
1537 /* check to see account if already locked. */
1538 if ((locked_pwd
= strstr(pwp
->pw_passwd
, LOCKED
))
1540 warnx("Account is already locked");
1542 if (asprintf(&locked_pwd
, "%s%s", LOCKED
,
1543 pwp
->pw_passwd
) == -1) {
1544 (void)close(ptmpfd
);
1547 "Can't modify user `%s': "
1548 "asprintf failed", login_name
);
1550 pwp
->pw_passwd
= locked_pwd
;
1552 } else if (up
->u_locked
== UNLOCK
) {
1553 if ((locked_pwd
= strstr(pwp
->pw_passwd
, LOCKED
))
1555 warnx("Can't modify user `%s': "
1556 "account is not locked", login_name
);
1558 pwp
->pw_passwd
= locked_pwd
+ strlen(LOCKED
);
1562 if (up
->u_flags
& F_UID
) {
1563 /* check uid isn't already allocated */
1564 if (!(up
->u_flags
& F_DUPUID
) &&
1565 getpwuid((uid_t
)(up
->u_uid
)) != NULL
) {
1566 (void)close(ptmpfd
);
1568 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1569 "uid `%d' is already in use", login_name
,
1572 pwp
->pw_uid
= up
->u_uid
;
1574 if (up
->u_flags
& F_GROUP
) {
1575 /* if -g=uid was specified, check gid is unused */
1576 if (strcmp(up
->u_primgrp
, "=uid") == 0) {
1577 if (getgrgid((gid_t
)(pwp
->pw_uid
)) != NULL
) {
1578 (void)close(ptmpfd
);
1581 "Can't modify user `%s': "
1582 "gid %d is already in use",
1583 login_name
, up
->u_uid
);
1585 pwp
->pw_gid
= pwp
->pw_uid
;
1586 } else if ((grp
= getgrnam(up
->u_primgrp
)) != NULL
) {
1587 pwp
->pw_gid
= grp
->gr_gid
;
1588 } else if (is_number(up
->u_primgrp
) &&
1590 (gid_t
)atoi(up
->u_primgrp
))) != NULL
) {
1591 pwp
->pw_gid
= grp
->gr_gid
;
1593 (void)close(ptmpfd
);
1595 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1596 "group %s not found", login_name
,
1600 if (up
->u_flags
& F_INACTIVE
) {
1601 if (!scantime(&pwp
->pw_change
, up
->u_inactive
)) {
1602 warnx("Warning: inactive time `%s' invalid, "
1603 "password expiry off",
1607 if (up
->u_flags
& F_EXPIRE
) {
1608 if (!scantime(&pwp
->pw_expire
, up
->u_expire
) ||
1609 pwp
->pw_expire
== -1) {
1610 warnx("Warning: expire time `%s' invalid, "
1611 "account expiry off",
1616 if (up
->u_flags
& F_COMMENT
) {
1617 pwp
->pw_gecos
= up
->u_comment
;
1619 if (up
->u_flags
& F_HOMEDIR
) {
1620 pwp
->pw_dir
= up
->u_home
;
1622 if (up
->u_flags
& F_SHELL
) {
1624 if (!valid_shell(up
->u_shell
)) {
1626 (void)close(ptmpfd
);
1629 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1630 "Cannot access shell `%s'",
1631 login_name
, up
->u_shell
);
1633 pwp
->pw_shell
= up
->u_shell
;
1635 pwp
->pw_shell
= up
->u_shell
;
1639 if (up
->u_flags
& F_CLASS
) {
1640 if (!valid_class(up
->u_class
)) {
1641 (void)close(ptmpfd
);
1643 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1644 "no such login class `%s'", login_name
,
1647 pwp
->pw_class
= up
->u_class
;
1651 loginc
= strlen(login_name
);
1652 while (fgets(buf
, sizeof(buf
), master
) != NULL
) {
1653 if ((colon
= strchr(buf
, ':')) == NULL
) {
1654 warnx("Malformed entry `%s'. Skipping", buf
);
1657 colonc
= (size_t)(colon
- buf
);
1658 if (strncmp(login_name
, buf
, loginc
) == 0 && loginc
== colonc
) {
1660 len
= snprintf(buf
, sizeof(buf
), "%s:%s:%d:%d:"
1664 ":%ld:%ld:%s:%s:%s\n",
1672 (long)pwp
->pw_change
,
1673 (long)pwp
->pw_expire
,
1677 if (write(ptmpfd
, buf
, len
) != len
) {
1679 (void)close(ptmpfd
);
1682 err(EXIT_FAILURE
, "Can't modify user "
1683 "`%s': write", login_name
);
1688 if (write(ptmpfd
, buf
, len
) != len
) {
1690 (void)close(masterfd
);
1691 (void)close(ptmpfd
);
1694 err(EXIT_FAILURE
, "Can't modify `%s': "
1695 "write", login_name
);
1700 if ((up
->u_flags
& F_MKDIR
) &&
1701 asystem("%s %s %s", MV
, homedir
, pwp
->pw_dir
) != 0) {
1702 (void)close(ptmpfd
);
1704 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1705 "can't move `%s' to `%s'",
1706 login_name
, homedir
, pwp
->pw_dir
);
1708 if (up
->u_groupc
> 0 &&
1709 !append_group(newlogin
, up
->u_groupc
, up
->u_groupv
)) {
1710 (void)close(ptmpfd
);
1712 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1713 "can't append `%s' to new groups",
1714 login_name
, newlogin
);
1717 (void)close(ptmpfd
);
1718 (void)fclose(master
);
1719 #if PW_MKDB_ARGC == 2
1720 if (up
!= NULL
&& strcmp(login_name
, newlogin
) == 0) {
1721 error
= pw_mkdb(login_name
, 0);
1723 error
= pw_mkdb(NULL
, 0);
1730 errx(EXIT_FAILURE
, "Can't modify user `%s': pw_mkdb failed",
1734 syslog(LOG_INFO
, "User removed: name=%s", login_name
);
1735 } else if (strcmp(login_name
, newlogin
) == 0) {
1736 syslog(LOG_INFO
, "User information modified: name=%s, uid=%d, "
1737 "gid=%d, home=%s, shell=%s",
1738 login_name
, pwp
->pw_uid
, pwp
->pw_gid
, pwp
->pw_dir
,
1741 syslog(LOG_INFO
, "User information modified: name=%s, "
1742 "new name=%s, uid=%d, gid=%d, home=%s, shell=%s",
1743 login_name
, newlogin
, pwp
->pw_uid
, pwp
->pw_gid
,
1744 pwp
->pw_dir
, pwp
->pw_shell
);
1750 /* see if we can find out the user struct */
1751 static struct passwd
*
1752 find_user_info(const char *name
)
1756 if ((pwp
= getpwnam(name
)) != NULL
) {
1759 if (is_number(name
) && (pwp
= getpwuid((uid_t
)atoi(name
))) != NULL
) {
1766 /* see if we can find out the group struct */
1767 static struct group
*
1768 find_group_info(const char *name
)
1772 if ((grp
= getgrnam(name
)) != NULL
) {
1775 if (is_number(name
) && (grp
= getgrgid((gid_t
)atoi(name
))) != NULL
) {
1781 /* print out usage message, and then exit */
1783 usermgmt_usage(const char *prog
)
1785 if (strcmp(prog
, "useradd") == 0) {
1786 (void)fprintf(stderr
, "usage: %s -D [-F] [-b base-dir] "
1787 "[-e expiry-time] [-f inactive-time]\n"
1788 "\t[-g gid | name | =uid] [-k skel-dir] [-L login-class]\n"
1789 "\t[-M homeperm] [-r lowuid..highuid] [-s shell]\n", prog
);
1790 (void)fprintf(stderr
, "usage: %s [-moSv] [-b base-dir] "
1791 "[-c comment] [-d home-dir] [-e expiry-time]\n"
1792 "\t[-f inactive-time] [-G secondary-group] "
1793 "[-g gid | name | =uid]\n"
1794 "\t[-k skeletondir] [-L login-class] [-M homeperm] "
1796 "\t[-r lowuid..highuid] [-s shell] [-u uid] user\n",
1798 } else if (strcmp(prog
, "usermod") == 0) {
1799 (void)fprintf(stderr
, "usage: %s [-FmoSv] [-C yes/no] "
1800 "[-c comment] [-d home-dir] [-e expiry-time]\n"
1801 "\t[-f inactive] [-G secondary-group] "
1802 "[-g gid | name | =uid]\n"
1803 "\t[-L login-class] [-l new-login] [-p password] "
1804 "[-s shell] [-u uid]\n"
1806 } else if (strcmp(prog
, "userdel") == 0) {
1807 (void)fprintf(stderr
, "usage: %s -D [-p preserve-value]\n", prog
);
1808 (void)fprintf(stderr
,
1809 "usage: %s [-rSv] [-p preserve-value] user\n", prog
);
1811 } else if (strcmp(prog
, "userinfo") == 0) {
1812 (void)fprintf(stderr
, "usage: %s [-e] user\n", prog
);
1814 } else if (strcmp(prog
, "groupadd") == 0) {
1815 (void)fprintf(stderr
, "usage: %s [-ov] [-g gid]"
1816 " [-r lowgid..highgid] group\n", prog
);
1817 } else if (strcmp(prog
, "groupdel") == 0) {
1818 (void)fprintf(stderr
, "usage: %s [-v] group\n", prog
);
1819 } else if (strcmp(prog
, "groupmod") == 0) {
1820 (void)fprintf(stderr
,
1821 "usage: %s [-ov] [-g gid] [-n newname] group\n", prog
);
1822 } else if (strcmp(prog
, "user") == 0 || strcmp(prog
, "group") == 0) {
1823 (void)fprintf(stderr
,
1824 "usage: %s ( add | del | mod | info ) ...\n", prog
);
1826 } else if (strcmp(prog
, "groupinfo") == 0) {
1827 (void)fprintf(stderr
, "usage: %s [-ev] group\n", prog
);
1835 #define ADD_OPT_EXTENSIONS "M:p:r:vL:S"
1837 #define ADD_OPT_EXTENSIONS
1841 useradd(int argc
, char **argv
)
1844 user_t
*up
= &def
.user
;
1852 read_defaults(&def
);
1854 defaultfield
= bigD
= 0;
1855 while ((c
= getopt(argc
, argv
, "DFG:b:c:d:e:f:g:k:mou:s:"
1856 ADD_OPT_EXTENSIONS
)) != -1) {
1863 * Setting -1 will force the new user to
1864 * change their password as soon as they
1865 * next log in - passwd(5).
1868 memsave(&up
->u_inactive
, "-1", strlen("-1"));
1871 while (up
->u_groupc
< NGROUPS_MAX
&&
1872 (up
->u_groupv
[up
->u_groupc
] = strsep(&optarg
, ",")) != NULL
) {
1873 if (up
->u_groupv
[up
->u_groupc
][0] != 0) {
1877 if (optarg
!= NULL
) {
1878 warnx("Truncated list of secondary groups "
1879 "to %d entries", NGROUPS_MAX
);
1884 up
->u_allow_samba
= 1;
1889 memsave(&up
->u_basedir
, optarg
, strlen(optarg
));
1892 memsave(&up
->u_comment
, optarg
, strlen(optarg
));
1895 memsave(&up
->u_home
, optarg
, strlen(optarg
));
1896 up
->u_flags
|= F_HOMEDIR
;
1900 memsave(&up
->u_expire
, optarg
, strlen(optarg
));
1904 memsave(&up
->u_inactive
, optarg
, strlen(optarg
));
1908 memsave(&up
->u_primgrp
, optarg
, strlen(optarg
));
1912 memsave(&up
->u_skeldir
, optarg
, strlen(optarg
));
1917 memsave(&up
->u_class
, optarg
, strlen(optarg
));
1921 up
->u_flags
|= F_MKDIR
;
1926 up
->u_homeperm
= strtoul(optarg
, NULL
, 8);
1930 up
->u_flags
|= F_DUPUID
;
1934 memsave(&up
->u_password
, optarg
, strlen(optarg
));
1940 (void)save_range(&up
->u_r
, optarg
);
1944 up
->u_flags
|= F_SHELL
;
1946 memsave(&up
->u_shell
, optarg
, strlen(optarg
));
1949 up
->u_uid
= check_numeric(optarg
, "uid");
1957 usermgmt_usage("useradd");
1964 return setdefaults(up
) ? EXIT_SUCCESS
: EXIT_FAILURE
;
1966 (void)printf("group\t\t%s\n", up
->u_primgrp
);
1967 (void)printf("base_dir\t%s\n", up
->u_basedir
);
1968 (void)printf("skel_dir\t%s\n", up
->u_skeldir
);
1969 (void)printf("shell\t\t%s\n", up
->u_shell
);
1971 (void)printf("class\t\t%s\n", up
->u_class
);
1972 (void)printf("homeperm\t0%o\n", up
->u_homeperm
);
1974 (void)printf("inactive\t%s\n", (up
->u_inactive
== NULL
) ?
1975 UNSET_INACTIVE
: up
->u_inactive
);
1976 (void)printf("expire\t\t%s\n", (up
->u_expire
== NULL
) ?
1977 UNSET_EXPIRY
: up
->u_expire
);
1979 for (i
= 0 ; i
< up
->u_rc
; i
++) {
1980 (void)printf("range\t\t%d..%d\n",
1981 up
->u_rv
[i
].r_from
, up
->u_rv
[i
].r_to
);
1984 return EXIT_SUCCESS
;
1989 usermgmt_usage("useradd");
1992 openlog("useradd", LOG_PID
, LOG_USER
);
1993 return adduser(*argv
, up
) ? EXIT_SUCCESS
: EXIT_FAILURE
;
1997 #define MOD_OPT_EXTENSIONS "p:vL:S"
1999 #define MOD_OPT_EXTENSIONS
2003 usermod(int argc
, char **argv
)
2006 user_t
*up
= &def
.user
;
2007 char newuser
[MaxUserNameLen
+ 1];
2008 int c
, have_new_user
;
2010 (void)memset(newuser
, 0, sizeof(newuser
));
2011 read_defaults(&def
);
2014 while ((c
= getopt(argc
, argv
, "C:FG:c:d:e:f:g:l:mos:u:"
2015 MOD_OPT_EXTENSIONS
)) != -1) {
2018 while (up
->u_groupc
< NGROUPS_MAX
&&
2019 (up
->u_groupv
[up
->u_groupc
] =
2020 strsep(&optarg
, ",")) != NULL
) {
2021 if (up
->u_groupv
[up
->u_groupc
][0] != 0) {
2025 if (optarg
!= NULL
) {
2026 warnx("Truncated list of secondary groups "
2027 "to %d entries", NGROUPS_MAX
);
2029 up
->u_flags
|= F_SECGROUP
;
2033 up
->u_allow_samba
= 1;
2037 memsave(&up
->u_comment
, optarg
, strlen(optarg
));
2038 up
->u_flags
|= F_COMMENT
;
2041 if (strcasecmp(optarg
, "yes") == 0) {
2042 up
->u_locked
= LOCK
;
2043 } else if (strcasecmp(optarg
, "no") == 0) {
2044 up
->u_locked
= UNLOCK
;
2048 "Please type 'yes' or 'no'");
2052 memsave(&up
->u_inactive
, "-1", strlen("-1"));
2053 up
->u_flags
|= F_INACTIVE
;
2056 memsave(&up
->u_home
, optarg
, strlen(optarg
));
2057 up
->u_flags
|= F_HOMEDIR
;
2060 memsave(&up
->u_expire
, optarg
, strlen(optarg
));
2061 up
->u_flags
|= F_EXPIRE
;
2064 memsave(&up
->u_inactive
, optarg
, strlen(optarg
));
2065 up
->u_flags
|= F_INACTIVE
;
2068 memsave(&up
->u_primgrp
, optarg
, strlen(optarg
));
2069 up
->u_flags
|= F_GROUP
;
2072 (void)strlcpy(newuser
, optarg
, sizeof(newuser
));
2074 up
->u_flags
|= F_USERNAME
;
2078 memsave(&up
->u_class
, optarg
, strlen(optarg
));
2079 up
->u_flags
|= F_CLASS
;
2083 up
->u_flags
|= F_MKDIR
;
2086 up
->u_flags
|= F_DUPUID
;
2090 memsave(&up
->u_password
, optarg
, strlen(optarg
));
2091 up
->u_flags
|= F_PASSWORD
;
2095 memsave(&up
->u_shell
, optarg
, strlen(optarg
));
2096 up
->u_flags
|= F_SHELL
;
2099 up
->u_uid
= check_numeric(optarg
, "uid");
2100 up
->u_flags
|= F_UID
;
2108 usermgmt_usage("usermod");
2112 if ((up
->u_flags
& F_MKDIR
) && !(up
->u_flags
& F_HOMEDIR
) &&
2113 !(up
->u_flags
& F_USERNAME
)) {
2114 warnx("Option 'm' useless without 'd' or 'l' -- ignored");
2115 up
->u_flags
&= ~F_MKDIR
;
2120 usermgmt_usage("usermod");
2123 openlog("usermod", LOG_PID
, LOG_USER
);
2124 return moduser(*argv
, (have_new_user
) ? newuser
: *argv
, up
,
2125 up
->u_allow_samba
) ? EXIT_SUCCESS
: EXIT_FAILURE
;
2129 #define DEL_OPT_EXTENSIONS "Dp:vS"
2131 #define DEL_OPT_EXTENSIONS
2135 userdel(int argc
, char **argv
)
2139 user_t
*up
= &def
.user
;
2140 char password
[PasswordLength
+ 1];
2146 read_defaults(&def
);
2147 defaultfield
= bigD
= rmhome
= 0;
2148 while ((c
= getopt(argc
, argv
, "r" DEL_OPT_EXTENSIONS
)) != -1) {
2157 up
->u_allow_samba
= 1;
2163 up
->u_preserve
= (strcmp(optarg
, "true") == 0) ? 1 :
2164 (strcmp(optarg
, "yes") == 0) ? 1 :
2177 usermgmt_usage("userdel");
2185 return setdefaults(up
) ? EXIT_SUCCESS
: EXIT_FAILURE
;
2187 (void)printf("preserve\t%s\n", (up
->u_preserve
) ? "true" :
2189 return EXIT_SUCCESS
;
2195 usermgmt_usage("userdel");
2198 if ((pwp
= getpwnam(*argv
)) == NULL
) {
2199 warnx("No such user `%s'", *argv
);
2200 return EXIT_FAILURE
;
2203 (void)removehomedir(pwp
);
2205 if (up
->u_preserve
) {
2206 up
->u_flags
|= F_SHELL
;
2207 memsave(&up
->u_shell
, NOLOGIN
, strlen(NOLOGIN
));
2208 (void)memset(password
, '*', DES_Len
);
2209 password
[DES_Len
] = 0;
2210 memsave(&up
->u_password
, password
, strlen(password
));
2211 up
->u_flags
|= F_PASSWORD
;
2212 openlog("userdel", LOG_PID
, LOG_USER
);
2213 return moduser(*argv
, *argv
, up
, up
->u_allow_samba
) ?
2214 EXIT_SUCCESS
: EXIT_FAILURE
;
2216 if (!rm_user_from_groups(*argv
)) {
2219 openlog("userdel", LOG_PID
, LOG_USER
);
2220 return moduser(*argv
, *argv
, NULL
, up
->u_allow_samba
) ?
2221 EXIT_SUCCESS
: EXIT_FAILURE
;
2225 #define GROUP_ADD_OPT_EXTENSIONS "r:v"
2227 #define GROUP_ADD_OPT_EXTENSIONS
2232 groupadd(int argc
, char **argv
)
2235 group_t
*gp
= &def
.group
;
2242 read_defaults(&def
);
2243 while ((c
= getopt(argc
, argv
, "g:o" GROUP_ADD_OPT_EXTENSIONS
)) != -1) {
2246 gid
= check_numeric(optarg
, "gid");
2253 (void)save_range(&gp
->g_r
, optarg
);
2260 usermgmt_usage("groupadd");
2267 usermgmt_usage("groupadd");
2269 if (gp
->g_rc
== 0) {
2270 gp
->g_rv
[gp
->g_rc
].r_from
= DEF_LOWUID
;
2271 gp
->g_rv
[gp
->g_rc
].r_to
= DEF_HIGHUID
;
2274 gp
->g_defrc
= gp
->g_rc
;
2281 * Look for a free GID in the command line ranges (if any).
2282 * These start after the ranges specified in the config file.
2284 for (i
= gp
->g_defrc
; !got_id
&& i
< gp
->g_rc
; i
++) {
2285 got_id
= getnextgid(&gid
,
2286 gp
->g_rv
[i
].r_from
, gp
->g_rv
[i
].r_to
);
2289 * If there were no free GIDs in the command line ranges,
2290 * try the ranges from the config file (there will always
2291 * be at least one default).
2293 for (i
= 0; !got_id
&& i
< gp
->g_defrc
; i
++) {
2294 got_id
= getnextgid(&gid
,
2295 gp
->g_rv
[i
].r_from
, gp
->g_rv
[i
].r_to
);
2298 errx(EXIT_FAILURE
, "Can't add group: can't get next gid");
2300 if (!dupgid
&& getgrgid((gid_t
) gid
) != NULL
) {
2301 errx(EXIT_FAILURE
, "Can't add group: gid %d is a duplicate",
2304 if (!valid_group(*argv
)) {
2305 warnx("Invalid group name `%s'", *argv
);
2307 openlog("groupadd", LOG_PID
, LOG_USER
);
2308 if (!creategid(*argv
, gid
, ""))
2311 return EXIT_SUCCESS
;
2315 #define GROUP_DEL_OPT_EXTENSIONS "v"
2317 #define GROUP_DEL_OPT_EXTENSIONS
2320 /* remove a group */
2322 groupdel(int argc
, char **argv
)
2326 while ((c
= getopt(argc
, argv
, "" GROUP_DEL_OPT_EXTENSIONS
)) != -1) {
2334 usermgmt_usage("groupdel");
2341 usermgmt_usage("groupdel");
2343 if (getgrnam(*argv
) == NULL
) {
2344 errx(EXIT_FAILURE
, "No such group `%s'", *argv
);
2347 openlog("groupdel", LOG_PID
, LOG_USER
);
2348 if (!modify_gid(*argv
, NULL
))
2351 return EXIT_SUCCESS
;
2355 #define GROUP_MOD_OPT_EXTENSIONS "v"
2357 #define GROUP_MOD_OPT_EXTENSIONS
2360 /* modify a group */
2362 groupmod(int argc
, char **argv
)
2365 char buf
[MaxEntryLen
];
2376 while ((c
= getopt(argc
, argv
, "g:on:" GROUP_MOD_OPT_EXTENSIONS
)) != -1) {
2379 gid
= check_numeric(optarg
, "gid");
2385 memsave(&newname
, optarg
, strlen(optarg
));
2393 usermgmt_usage("groupmod");
2400 usermgmt_usage("groupmod");
2403 if (gid
< 0 && newname
== NULL
) {
2404 errx(EXIT_FAILURE
, "Nothing to change");
2406 if (dupgid
&& gid
< 0) {
2407 errx(EXIT_FAILURE
, "Duplicate which gid?");
2409 if (!dupgid
&& getgrgid((gid_t
) gid
) != NULL
) {
2410 errx(EXIT_FAILURE
, "Can't modify group: gid %d is a duplicate",
2413 if ((grp
= find_group_info(*argv
)) == NULL
) {
2414 errx(EXIT_FAILURE
, "Can't find group `%s' to modify", *argv
);
2416 if (!is_local(*argv
, _PATH_GROUP
)) {
2417 errx(EXIT_FAILURE
, "Group `%s' must be a local group", *argv
);
2419 if (newname
!= NULL
&& !valid_group(newname
)) {
2420 warnx("Invalid group name `%s'", newname
);
2422 cc
= snprintf(buf
, sizeof(buf
), "%s:%s:%d:",
2423 (newname
) ? newname
: grp
->gr_name
,
2425 (gid
< 0) ? grp
->gr_gid
: gid
);
2426 for (cpp
= grp
->gr_mem
; *cpp
&& cc
< sizeof(buf
) ; cpp
++) {
2427 cc
+= snprintf(&buf
[cc
], sizeof(buf
) - cc
, "%s%s", *cpp
,
2428 (cpp
[1] == NULL
) ? "" : ",");
2430 cc
+= snprintf(&buf
[cc
], sizeof(buf
) - cc
, "\n");
2431 if (newname
!= NULL
)
2433 openlog("groupmod", LOG_PID
, LOG_USER
);
2434 if (!modify_gid(*argv
, buf
))
2437 return EXIT_SUCCESS
;
2441 /* display user information */
2443 userinfo(int argc
, char **argv
)
2447 char buf
[MaxEntryLen
];
2455 while ((i
= getopt(argc
, argv
, "e")) != -1) {
2461 usermgmt_usage("userinfo");
2468 usermgmt_usage("userinfo");
2470 pwp
= find_user_info(*argv
);
2472 exit((pwp
) ? EXIT_SUCCESS
: EXIT_FAILURE
);
2475 errx(EXIT_FAILURE
, "Can't find user `%s'", *argv
);
2477 (void)printf("login\t%s\n", pwp
->pw_name
);
2478 (void)printf("passwd\t%s\n", pwp
->pw_passwd
);
2479 (void)printf("uid\t%d\n", pwp
->pw_uid
);
2480 for (cc
= 0 ; (grp
= getgrent()) != NULL
; ) {
2481 for (cpp
= grp
->gr_mem
; *cpp
; cpp
++) {
2482 if (strcmp(*cpp
, *argv
) == 0 &&
2483 grp
->gr_gid
!= pwp
->pw_gid
) {
2484 cc
+= snprintf(&buf
[cc
], sizeof(buf
) - cc
,
2485 "%s ", grp
->gr_name
);
2489 if ((grp
= getgrgid(pwp
->pw_gid
)) == NULL
) {
2490 (void)printf("groups\t%d %s\n", pwp
->pw_gid
, buf
);
2492 (void)printf("groups\t%s %s\n", grp
->gr_name
, buf
);
2494 (void)printf("change\t%s", pwp
->pw_change
> 0 ?
2495 ctime(&pwp
->pw_change
) : pwp
->pw_change
== -1 ?
2496 "NEXT LOGIN\n" : "NEVER\n");
2497 (void)printf("class\t%s\n", pwp
->pw_class
);
2498 (void)printf("gecos\t%s\n", pwp
->pw_gecos
);
2499 (void)printf("dir\t%s\n", pwp
->pw_dir
);
2500 (void)printf("shell\t%s\n", pwp
->pw_shell
);
2501 (void)printf("expire\t%s", pwp
->pw_expire
?
2502 ctime(&pwp
->pw_expire
) : "NEVER\n");
2503 return EXIT_SUCCESS
;
2508 /* display user information */
2510 groupinfo(int argc
, char **argv
)
2518 while ((i
= getopt(argc
, argv
, "ev")) != -1) {
2527 usermgmt_usage("groupinfo");
2534 usermgmt_usage("groupinfo");
2536 grp
= find_group_info(*argv
);
2538 exit((grp
) ? EXIT_SUCCESS
: EXIT_FAILURE
);
2541 errx(EXIT_FAILURE
, "Can't find group `%s'", *argv
);
2543 (void)printf("name\t%s\n", grp
->gr_name
);
2544 (void)printf("passwd\t%s\n", grp
->gr_passwd
);
2545 (void)printf("gid\t%d\n", grp
->gr_gid
);
2546 (void)printf("members\t");
2547 for (cpp
= grp
->gr_mem
; *cpp
; cpp
++) {
2548 (void)printf("%s", *cpp
);
2550 (void) printf(", ");
2553 (void)fputc('\n', stdout
);
2554 return EXIT_SUCCESS
;