1 /* $NetBSD: user.c,v 1.126 2011/01/04 10:30:21 wiz 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.126 2011/01/04 10:30:21 wiz 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"
156 #define DEF_SKELDIR "/usr/ast"
158 #define DEF_SKELDIR "/etc/skel"
163 #define DEF_SHELL _PATH_BSHELL
167 #define DEF_COMMENT ""
171 #define DEF_LOWUID 1000
175 #define DEF_HIGHUID 60000
179 #define DEF_INACTIVE 0
183 #define DEF_EXPIRE NULL
195 #define NOBODY_UID 32767
199 #define DEF_HOMEPERM 0755
202 /* some useful constants */
204 MaxShellNameLen
= 256,
205 MaxFileNameLen
= MAXPATHLEN
,
206 MaxUserNameLen
= LOGIN_NAME_MAX
- 1,
207 MaxCommandLen
= 2048,
209 PasswordLength
= 2048,
214 /* Full paths of programs used here */
215 #define CHMOD "/bin/chmod"
216 #define CHOWN "/usr/bin/chown"
217 #define MKDIR "/bin/mkdir"
219 #define NOLOGIN "/sbin/nologin"
220 #define PAX "/bin/pax"
223 #define UNSET_INACTIVE "Null (unset)"
224 #define UNSET_EXPIRY "Null (unset)"
226 static int asystem(const char *fmt
, ...)
227 __attribute__((__format__(__printf__
, 1, 2)));
228 static int is_number(const char *);
229 static struct group
*find_group_info(const char *);
235 for (; *s
&& isspace((unsigned char)*s
) ; s
++) {
241 check_numeric(const char *val
, const char *name
)
243 if (!is_number(val
)) {
244 errx(EXIT_FAILURE
, "When using [-%c %s], "
245 "the %s must be numeric", *name
, name
, name
);
250 /* resize *cpp appropriately then assign `n' chars of `s' to it */
252 memsave(char **cpp
, const char *s
, size_t n
)
254 RENEW(char, *cpp
, n
+ 1, exit(1));
255 (void)memcpy(*cpp
, s
, n
);
259 /* a replacement for system(3) */
261 asystem(const char *fmt
, ...)
264 char buf
[MaxCommandLen
];
268 (void)vsnprintf(buf
, sizeof(buf
), fmt
, vp
);
271 (void)printf("Command: %s\n", buf
);
273 if ((ret
= system(buf
)) != 0) {
274 warn("Error running `%s'", buf
);
279 /* remove a users home directory, returning 1 for success (ie, no problems encountered) */
281 removehomedir(struct passwd
*pwp
)
285 /* userid not root? */
286 if (pwp
->pw_uid
== 0) {
287 warnx("Not deleting home directory `%s'; userid is 0", pwp
->pw_dir
);
291 /* directory exists (and is a directory!) */
292 if (stat(pwp
->pw_dir
, &st
) < 0) {
293 warn("Cannot access home directory `%s'", pwp
->pw_dir
);
296 if (!S_ISDIR(st
.st_mode
)) {
297 warnx("Home directory `%s' is not a directory", pwp
->pw_dir
);
301 /* userid matches directory owner? */
302 if (st
.st_uid
!= pwp
->pw_uid
) {
303 warnx("User `%s' doesn't own directory `%s', not removed",
304 pwp
->pw_name
, pwp
->pw_dir
);
308 (void)seteuid(pwp
->pw_uid
);
309 /* we add the "|| true" to keep asystem() quiet if there is a non-zero exit status. */
310 (void)asystem("%s -rf %s > /dev/null 2>&1 || true", RM
, pwp
->pw_dir
);
312 if (rmdir(pwp
->pw_dir
) < 0) {
313 warn("Unable to remove all files in `%s'", pwp
->pw_dir
);
319 /* return 1 if all of `s' is numeric */
321 is_number(const char *s
)
324 if (!isdigit((unsigned char) *s
)) {
332 * check that the effective uid is 0 - called from funcs which will
333 * modify data and config files.
338 if (geteuid() != 0) {
339 errx(EXIT_FAILURE
, "Program must be run as root");
343 /* copy any dot files into the user's home directory */
345 copydotfiles(char *skeldir
, int uid
, int gid
, char *dir
, mode_t homeperm
)
351 if ((dirp
= opendir(skeldir
)) == NULL
) {
352 warn("Can't open source . files dir `%s'", skeldir
);
355 for (n
= 0; (dp
= readdir(dirp
)) != NULL
&& n
== 0 ; ) {
356 if (strcmp(dp
->d_name
, ".") == 0 ||
357 strcmp(dp
->d_name
, "..") == 0) {
362 (void)closedir(dirp
);
364 warnx("No \"dot\" initialisation files found");
366 (void)asystem("cd %s && %s -rw -pe %s . %s",
367 skeldir
, PAX
, (verbose
) ? "-v" : "", dir
);
369 (void)asystem("%s -R -h %d:%d %s", CHOWN
, uid
, gid
, dir
);
370 (void)asystem("%s -R u+w %s", CHMOD
, dir
);
372 (void)asystem("%s 0%o %s", CHMOD
, homeperm
, dir
);
377 /* create a group entry with gid `gid' */
379 creategid(char *group
, int gid
, const char *name
)
384 char buf
[MaxEntryLen
];
385 char f
[MaxFileNameLen
];
389 if (getgrnam(group
) != NULL
) {
390 warnx("Can't create group `%s': already exists", group
);
393 if ((from
= fopen(_PATH_GROUP
, "r+")) == NULL
) {
394 warn("Can't create group `%s': can't open `%s'", name
,
398 if (flock(fileno(from
), LOCK_EX
| LOCK_NB
) < 0) {
399 warn("Can't lock `%s'", _PATH_GROUP
);
403 (void)fstat(fileno(from
), &st
);
404 (void)snprintf(f
, sizeof(f
), "%s.XXXXXX", _PATH_GROUP
);
405 if ((fd
= mkstemp(f
)) < 0) {
406 warn("Can't create group `%s': mkstemp failed", group
);
410 if ((to
= fdopen(fd
, "w")) == NULL
) {
411 warn("Can't create group `%s': fdopen `%s' failed",
418 while ((cc
= fread(buf
, sizeof(char), sizeof(buf
), from
)) > 0) {
419 if (fwrite(buf
, sizeof(char), (unsigned) cc
, to
) != cc
) {
420 warn("Can't create group `%s': short write to `%s'",
428 (void)fprintf(to
, "%s:*:%d:%s\n", group
, gid
, name
);
431 if (rename(f
, _PATH_GROUP
) < 0) {
432 warn("Can't create group `%s': can't rename `%s' to `%s'",
433 group
, f
, _PATH_GROUP
);
437 (void)chmod(_PATH_GROUP
, st
.st_mode
& 07777);
438 syslog(LOG_INFO
, "New group added: name=%s, gid=%d", group
, gid
);
442 /* modify the group entry with name `group' to be newent */
444 modify_gid(char *group
, char *newent
)
449 char buf
[MaxEntryLen
];
450 char f
[MaxFileNameLen
];
457 if ((from
= fopen(_PATH_GROUP
, "r+")) == NULL
) {
458 warn("Can't modify group `%s': can't open `%s'",
462 if (flock(fileno(from
), LOCK_EX
| LOCK_NB
) < 0) {
463 warn("Can't modify group `%s': can't lock `%s'",
468 (void)fstat(fileno(from
), &st
);
469 (void)snprintf(f
, sizeof(f
), "%s.XXXXXX", _PATH_GROUP
);
470 if ((fd
= mkstemp(f
)) < 0) {
471 warn("Can't modify group `%s': mkstemp failed", group
);
475 if ((to
= fdopen(fd
, "w")) == NULL
) {
476 warn("Can't modify group `%s': fdopen `%s' failed", group
, f
);
482 groupc
= strlen(group
);
483 while (fgets(buf
, sizeof(buf
), from
) != NULL
) {
485 if ((colon
= strchr(buf
, ':')) == NULL
) {
486 warnx("Badly formed entry `%s'", buf
);
489 entc
= (int)(colon
- buf
);
490 if (entc
== groupc
&&
491 strncmp(group
, buf
, (unsigned) entc
) == 0) {
492 if (newent
== NULL
) {
493 struct group
*grp_rm
;
494 struct passwd
*user_pwd
;
497 * Check that the group being removed
498 * isn't any user's Primary group. Just
499 * warn if it is. This could cause problems
500 * if the group GID was reused for a
504 grp_rm
= find_group_info(group
);
505 while ((user_pwd
= getpwent()) != NULL
) {
506 if (user_pwd
->pw_gid
== grp_rm
->gr_gid
) {
507 warnx("Warning: group `%s'(%d)"
508 " is the primary group of"
509 " `%s'. Use caution if you"
510 " later add this GID.",
512 grp_rm
->gr_gid
, user_pwd
->pw_name
);
519 (void)strlcpy(buf
, newent
, sizeof(buf
));
522 if (fwrite(buf
, sizeof(char), (unsigned) cc
, to
) != cc
) {
523 warn("Can't modify group `%s': short write to `%s'",
533 if (rename(f
, _PATH_GROUP
) < 0) {
534 warn("Can't modify group `%s': can't rename `%s' to `%s'",
535 group
, f
, _PATH_GROUP
);
539 (void)chmod(_PATH_GROUP
, st
.st_mode
& 07777);
540 if (newent
== NULL
) {
541 syslog(LOG_INFO
, "group deleted: name=%s", group
);
543 syslog(LOG_INFO
, "group information modified: name=%s", group
);
548 /* modify the group entries for all `groups', by adding `user' */
550 append_group(char *user
, int ngroups
, const char **groups
)
556 char buf
[MaxEntryLen
];
557 char f
[MaxFileNameLen
];
567 for (i
= 0 ; i
< ngroups
; i
++) {
568 if ((grp
= getgrnam(groups
[i
])) == NULL
) {
569 warnx("Can't append group `%s' for user `%s'",
572 for (j
= 0 ; grp
->gr_mem
[j
] ; j
++) {
573 if (strcmp(user
, grp
->gr_mem
[j
]) == 0) {
580 if ((from
= fopen(_PATH_GROUP
, "r+")) == NULL
) {
581 warn("Can't append group(s) for `%s': can't open `%s'",
585 if (flock(fileno(from
), LOCK_EX
| LOCK_NB
) < 0) {
586 warn("Can't append group(s) for `%s': can't lock `%s'",
591 (void)fstat(fileno(from
), &st
);
592 (void)snprintf(f
, sizeof(f
), "%s.XXXXXX", _PATH_GROUP
);
593 if ((fd
= mkstemp(f
)) < 0) {
594 warn("Can't append group(s) for `%s': mkstemp failed",
599 if ((to
= fdopen(fd
, "w")) == NULL
) {
600 warn("Can't append group(s) for `%s': fdopen `%s' failed",
607 while (fgets(buf
, sizeof(buf
), from
) != NULL
) {
609 if ((colon
= strchr(buf
, ':')) == NULL
) {
610 warnx("Badly formed entry `%s'", buf
);
613 entc
= (int)(colon
- buf
);
614 for (i
= 0 ; i
< ngroups
; i
++) {
615 if ((groupc
= strlen(groups
[i
])) == 0) {
618 if (entc
== groupc
&&
619 strncmp(groups
[i
], buf
, (unsigned) entc
) == 0) {
620 if ((nc
= snprintf(&buf
[cc
- 1],
621 sizeof(buf
) - cc
+ 1, "%s%s\n",
622 (buf
[cc
- 2] == ':') ? "" : ",", user
)) < 0) {
623 warnx("Warning: group `%s' "
624 "entry too long", groups
[i
]);
629 if (fwrite(buf
, sizeof(char), (unsigned) cc
, to
) != cc
) {
630 warn("Can't append group(s) for `%s':"
631 " short write to `%s'", user
, f
);
640 if (rename(f
, _PATH_GROUP
) < 0) {
641 warn("Can't append group(s) for `%s': "
642 "can't rename `%s' to `%s'", user
, f
, _PATH_GROUP
);
646 (void)chmod(_PATH_GROUP
, st
.st_mode
& 07777);
650 /* the valid characters for login and group names */
651 #define VALID_CHAR(c) (isalnum(c) || (c) == '.' || (c) == '_' || (c) == '-')
653 /* return 1 if `login' is a valid login name */
655 valid_login(char *login_name
, int allow_samba
)
659 /* First character of a login name cannot be '-'. */
660 if (*login_name
== '-') {
663 if (strlen(login_name
) >= LOGIN_NAME_MAX
) {
666 for (cp
= (unsigned char *)login_name
; *cp
; cp
++) {
667 if (!VALID_CHAR(*cp
)) {
669 /* check for a trailing '$' in a Samba user name */
670 if (allow_samba
&& *cp
== '$' && *(cp
+ 1) == 0x0) {
680 /* return 1 if `group' is a valid group name */
682 valid_group(char *group
)
686 for (cp
= (unsigned char *)group
; *cp
; cp
++) {
687 if (!VALID_CHAR(*cp
)) {
694 /* find the next gid in the range lo .. hi */
696 getnextgid(int *gidp
, int lo
, int hi
)
698 for (*gidp
= lo
; *gidp
< hi
; *gidp
+= 1) {
699 if (getgrgid((gid_t
)*gidp
) == NULL
) {
707 /* save a range of uids */
709 save_range(rangelist_t
*rlp
, char *cp
)
715 if (rlp
->rl_rsize
== 0) {
717 NEWARRAY(range_t
, rlp
->rl_rv
, rlp
->rl_rsize
, return(0));
718 } else if (rlp
->rl_rc
== rlp
->rl_rsize
) {
720 RENEW(range_t
, rlp
->rl_rv
, rlp
->rl_rsize
, return(0));
722 if (rlp
->rl_rv
&& sscanf(cp
, "%d..%d", &from
, &to
) == 2) {
723 for (i
= rlp
->rl_defrc
; i
< rlp
->rl_rc
; i
++) {
724 if (rlp
->rl_rv
[i
].r_from
== from
&&
725 rlp
->rl_rv
[i
].r_to
== to
) {
729 if (i
== rlp
->rl_rc
) {
730 rlp
->rl_rv
[rlp
->rl_rc
].r_from
= from
;
731 rlp
->rl_rv
[rlp
->rl_rc
].r_to
= to
;
735 warnx("Bad range `%s'", cp
);
742 /* set the defaults in the defaults file */
744 setdefaults(user_t
*up
)
746 char template[MaxFileNameLen
];
754 (void)snprintf(template, sizeof(template), "%s.XXXXXX",
755 _PATH_USERMGMT_CONF
);
756 if ((fd
= mkstemp(template)) < 0) {
757 warn("Can't set defaults: can't mkstemp `%s' for writing",
758 _PATH_USERMGMT_CONF
);
761 if ((fp
= fdopen(fd
, "w")) == NULL
) {
762 warn("Can't set defaults: can't fdopen `%s' for writing",
763 _PATH_USERMGMT_CONF
);
767 if (fprintf(fp
, "group\t\t%s\n", up
->u_primgrp
) <= 0 ||
768 fprintf(fp
, "base_dir\t%s\n", up
->u_basedir
) <= 0 ||
769 fprintf(fp
, "skel_dir\t%s\n", up
->u_skeldir
) <= 0 ||
770 fprintf(fp
, "shell\t\t%s\n", up
->u_shell
) <= 0 ||
772 fprintf(fp
, "class\t\t%s\n", up
->u_class
) <= 0 ||
773 fprintf(fp
, "homeperm\t0%o\n", up
->u_homeperm
) <= 0 ||
775 fprintf(fp
, "inactive\t%s\n", (up
->u_inactive
== NULL
) ?
776 UNSET_INACTIVE
: up
->u_inactive
) <= 0 ||
777 fprintf(fp
, "expire\t\t%s\n", (up
->u_expire
== NULL
) ?
778 UNSET_EXPIRY
: up
->u_expire
) <= 0 ||
779 fprintf(fp
, "preserve\t%s\n", (up
->u_preserve
== 0) ?
780 "false" : "true") <= 0) {
781 warn("Can't write to `%s'", _PATH_USERMGMT_CONF
);
785 for (i
= (up
->u_defrc
!= up
->u_rc
) ? up
->u_defrc
: 0;
786 i
< up
->u_rc
; i
++) {
787 if (fprintf(fp
, "range\t\t%d..%d\n", up
->u_rv
[i
].r_from
,
788 up
->u_rv
[i
].r_to
) <= 0) {
789 warn("Can't set defaults: can't write to `%s'",
790 _PATH_USERMGMT_CONF
);
797 ret
= ((rename(template, _PATH_USERMGMT_CONF
) == 0) &&
798 (chmod(_PATH_USERMGMT_CONF
, 0644) == 0));
803 /* read the defaults file */
805 read_defaults(def_t
*dp
)
813 user_t
*up
= &dp
->user
;
814 group_t
*gp
= &dp
->group
;
816 (void)memset(dp
, 0, sizeof(*dp
));
818 memsave(&up
->u_primgrp
, DEF_GROUP
, strlen(DEF_GROUP
));
819 memsave(&up
->u_basedir
, DEF_BASEDIR
, strlen(DEF_BASEDIR
));
820 memsave(&up
->u_skeldir
, DEF_SKELDIR
, strlen(DEF_SKELDIR
));
821 memsave(&up
->u_shell
, DEF_SHELL
, strlen(DEF_SHELL
));
822 memsave(&up
->u_comment
, DEF_COMMENT
, strlen(DEF_COMMENT
));
824 memsave(&up
->u_class
, DEF_CLASS
, strlen(DEF_CLASS
));
828 NEWARRAY(range_t
, up
->u_rv
, up
->u_rsize
, exit(1));
829 up
->u_inactive
= DEF_INACTIVE
;
830 up
->u_expire
= DEF_EXPIRE
;
833 NEWARRAY(range_t
, gp
->g_rv
, gp
->g_rsize
, exit(1));
834 if ((fp
= fopen(_PATH_USERMGMT_CONF
, "r")) == NULL
) {
835 if (stat(_PATH_USERMGMT_CONF
, &st
) < 0 && !setdefaults(up
)) {
836 warn("Can't create `%s' defaults file",
837 _PATH_USERMGMT_CONF
);
839 fp
= fopen(_PATH_USERMGMT_CONF
, "r");
842 while ((s
= fparseln(fp
, &len
, &lineno
, NULL
, 0)) != NULL
) {
843 if (strncmp(s
, "group", 5) == 0) {
844 cp
= skipspace(s
+ 5);
845 memsave(&up
->u_primgrp
, (char *)cp
, strlen(cp
));
846 } else if (strncmp(s
, "base_dir", 8) == 0) {
847 cp
= skipspace(s
+ 8);
848 memsave(&up
->u_basedir
, (char *)cp
, strlen(cp
));
849 } else if (strncmp(s
, "skel_dir", 8) == 0) {
850 cp
= skipspace(s
+ 8);
851 memsave(&up
->u_skeldir
, (char *)cp
, strlen(cp
));
852 } else if (strncmp(s
, "shell", 5) == 0) {
853 cp
= skipspace(s
+ 5);
854 memsave(&up
->u_shell
, cp
, strlen(cp
));
856 } else if (strncmp((char *)s
, "class", 5) == 0) {
857 cp
= skipspace(s
+ 5);
858 memsave(&up
->u_class
, cp
, strlen(cp
));
861 } else if (strncmp(s
, "homeperm", 8) == 0) {
862 for (cp
= s
+ 8; *cp
&&
863 isspace((unsigned char)*cp
); cp
++)
865 up
->u_homeperm
= strtoul(cp
, NULL
, 8);
867 } else if (strncmp(s
, "inactive", 8) == 0) {
868 cp
= skipspace(s
+ 8);
869 if (strcmp(cp
, UNSET_INACTIVE
) == 0) {
870 if (up
->u_inactive
) {
871 FREE(up
->u_inactive
);
873 up
->u_inactive
= NULL
;
875 memsave(&up
->u_inactive
, cp
, strlen(cp
));
878 } else if (strncmp(s
, "range", 5) == 0) {
879 cp
= skipspace(s
+ 5);
880 (void)save_range(&up
->u_r
, cp
);
883 } else if (strncmp(s
, "preserve", 8) == 0) {
884 cp
= skipspace(s
+ 8);
886 (strncmp(cp
, "true", 4) == 0) ? 1 :
887 (strncmp(cp
, "yes", 3) == 0) ? 1 : atoi(cp
);
889 } else if (strncmp(s
, "expire", 6) == 0) {
890 cp
= skipspace(s
+ 6);
891 if (strcmp(cp
, UNSET_EXPIRY
) == 0) {
897 memsave(&up
->u_expire
, cp
, strlen(cp
));
900 } else if (strncmp(s
, "gid_range", 9) == 0) {
901 cp
= skipspace(s
+ 9);
902 (void)save_range(&gp
->g_r
, cp
);
910 up
->u_rv
[up
->u_rc
].r_from
= DEF_LOWUID
;
911 up
->u_rv
[up
->u_rc
].r_to
= DEF_HIGHUID
;
914 up
->u_defrc
= up
->u_rc
;
915 up
->u_homeperm
= DEF_HOMEPERM
;
918 /* return the next valid unused uid */
920 getnextuid(int sync_uid_gid
, int *uid
, int low_uid
, int high_uid
)
922 for (*uid
= low_uid
; *uid
<= high_uid
; (*uid
)++) {
923 if (getpwuid((uid_t
)(*uid
)) == NULL
&& *uid
!= NOBODY_UID
) {
925 if (getgrgid((gid_t
)(*uid
)) == NULL
) {
936 /* structure which defines a password type */
937 typedef struct passwd_type_t
{
938 const char *type
; /* optional type descriptor */
939 size_t desc_length
; /* length of type descriptor */
940 size_t length
; /* length of password */
941 const char *regex
; /* regexp to output the password */
942 size_t re_sub
; /* subscript of regexp to use */
945 static passwd_type_t passwd_types
[] = {
946 { "$sha1", 5, 28, "\\$[^$]+\\$[^$]+\\$[^$]+\\$(.*)", 1 }, /* SHA1 */
947 { "$2a", 3, 53, "\\$[^$]+\\$[^$]+\\$(.*)", 1 }, /* Blowfish */
948 { "$1", 2, 34, NULL
, 0 }, /* MD5 */
949 { "", 0, DES_Len
,NULL
, 0 }, /* standard DES */
950 { NULL
, (size_t)~0, (size_t)~0, NULL
, 0 }
951 /* none - terminate search */
954 /* return non-zero if it's a valid password - check length for cipher type */
956 valid_password_length(char *newpasswd
)
959 regmatch_t matchv
[10];
962 for (pwtp
= passwd_types
; pwtp
->desc_length
!= (size_t)~0; pwtp
++) {
963 if (strncmp(newpasswd
, pwtp
->type
, pwtp
->desc_length
) == 0) {
964 if (pwtp
->regex
== NULL
) {
965 return strlen(newpasswd
) == pwtp
->length
;
967 (void)regcomp(&r
, pwtp
->regex
, REG_EXTENDED
);
968 if (regexec(&r
, newpasswd
, 10, matchv
, 0) == 0) {
970 return (int)(matchv
[pwtp
->re_sub
].rm_eo
-
971 matchv
[pwtp
->re_sub
].rm_so
) ==
981 /* return 1 if `class' is a valid login class */
983 valid_class(char *class)
987 if (class == NULL
|| *class == '\0') {
991 * Check if /etc/login.conf exists. login_getclass() will
992 * return 1 due to it not existing, so not informing the
993 * user the actual login class does not exist.
996 if (access(PATH_LOGINCONF
, R_OK
) == -1) {
997 warn("Access failed for `%s'; will not validate class `%s'",
998 PATH_LOGINCONF
, class);
1002 if ((lc
= login_getclass(class)) != NULL
) {
1009 /* return 1 if the `shellname' is a valid user shell */
1011 valid_shell(const char *shellname
)
1015 if (access(_PATH_SHELLS
, R_OK
) == -1) {
1017 warn("Access failed for `%s'; will not validate shell `%s'",
1018 _PATH_SHELLS
, shellname
);
1022 /* if nologin is used as a shell, consider it a valid shell */
1023 if (strcmp(shellname
, NOLOGIN
) == 0)
1026 while ((shellp
= getusershell()) != NULL
)
1027 if (strcmp(shellp
, shellname
) == 0)
1030 warnx("Shell `%s' not found in `%s'", shellname
, _PATH_SHELLS
);
1032 return access(shellname
, X_OK
) != -1;
1036 /* look for a valid time, return 0 if it was specified but bad */
1038 scantime(time_t *tp
, char *s
)
1046 (void)memset(&tm
, 0, sizeof(tm
));
1047 if (strptime(s
, "%c", &tm
) != NULL
) {
1049 return (*tp
== -1) ? 0 : 1;
1050 } else if (strptime(s
, "%B %d %Y", &tm
) != NULL
) {
1052 return (*tp
== -1) ? 0 : 1;
1055 *tp
= val
= strtol(s
, &ep
, 10);
1056 if (*ep
!= '\0' || *tp
< -1 || errno
== ERANGE
) {
1070 adduser(char *login_name
, user_t
*up
)
1076 char password
[PasswordLength
+ 1];
1077 char home
[MaxFileNameLen
];
1078 char buf
[MaxFileNameLen
];
1086 if (!valid_login(login_name
, up
->u_allow_samba
)) {
1087 errx(EXIT_FAILURE
, "Can't add user `%s': invalid login name", login_name
);
1090 if (!valid_class(up
->u_class
)) {
1091 errx(EXIT_FAILURE
, "Can't add user `%s': no such login class `%s'",
1092 login_name
, up
->u_class
);
1095 if ((masterfd
= open(_PATH_MASTERPASSWD
, O_RDWR
)) < 0) {
1096 err(EXIT_FAILURE
, "Can't add user `%s': can't open `%s'",
1097 login_name
, _PATH_MASTERPASSWD
);
1099 if (flock(masterfd
, LOCK_EX
| LOCK_NB
) < 0) {
1100 err(EXIT_FAILURE
, "Can't add user `%s': can't lock `%s'",
1101 login_name
, _PATH_MASTERPASSWD
);
1104 if ((ptmpfd
= pw_lock(WAITSECS
)) < 0) {
1106 (void)close(masterfd
);
1108 err(EXIT_FAILURE
, "Can't add user `%s': can't obtain pw_lock",
1111 while ((cc
= read(masterfd
, buf
, sizeof(buf
))) > 0) {
1112 if (write(ptmpfd
, buf
, (size_t)(cc
)) != cc
) {
1114 (void)close(masterfd
);
1115 (void)close(ptmpfd
);
1118 err(EXIT_FAILURE
, "Can't add user `%s': "
1119 "short write to /etc/ptmp", login_name
);
1122 (void)close(masterfd
);
1123 /* if no uid was specified, get next one in [low_uid..high_uid] range */
1124 sync_uid_gid
= (strcmp(up
->u_primgrp
, "=uid") == 0);
1125 if (up
->u_uid
== -1) {
1129 * Look for a free UID in the command line ranges (if any).
1130 * These start after the ranges specified in the config file.
1132 for (i
= up
->u_defrc
; !got_id
&& i
< up
->u_rc
; i
++) {
1133 got_id
= getnextuid(sync_uid_gid
, &up
->u_uid
,
1134 up
->u_rv
[i
].r_from
, up
->u_rv
[i
].r_to
);
1137 * If there were no free UIDs in the command line ranges,
1138 * try the ranges from the config file (there will always
1139 * be at least one default).
1141 for (i
= 0; !got_id
&& i
< up
->u_defrc
; i
++) {
1142 got_id
= getnextuid(sync_uid_gid
, &up
->u_uid
,
1143 up
->u_rv
[i
].r_from
, up
->u_rv
[i
].r_to
);
1146 (void)close(ptmpfd
);
1148 errx(EXIT_FAILURE
, "Can't add user `%s': "
1149 "can't get next uid for %d", login_name
,
1153 /* check uid isn't already allocated */
1154 if (!(up
->u_flags
& F_DUPUID
) && getpwuid((uid_t
)(up
->u_uid
)) != NULL
) {
1155 (void)close(ptmpfd
);
1157 errx(EXIT_FAILURE
, "Can't add user `%s': "
1158 "uid %d is already in use", login_name
, up
->u_uid
);
1160 /* if -g=uid was specified, check gid is unused */
1162 if (getgrgid((gid_t
)(up
->u_uid
)) != NULL
) {
1163 (void)close(ptmpfd
);
1165 errx(EXIT_FAILURE
, "Can't add user `%s': "
1166 "gid %d is already in use", login_name
,
1170 } else if ((grp
= getgrnam(up
->u_primgrp
)) != NULL
) {
1172 } else if (is_number(up
->u_primgrp
) &&
1173 (grp
= getgrgid((gid_t
)atoi(up
->u_primgrp
))) != NULL
) {
1176 (void)close(ptmpfd
);
1178 errx(EXIT_FAILURE
, "Can't add user `%s': group %s not found",
1179 login_name
, up
->u_primgrp
);
1181 /* check name isn't already in use */
1182 if (!(up
->u_flags
& F_DUPUID
) && getpwnam(login_name
) != NULL
) {
1183 (void)close(ptmpfd
);
1185 errx(EXIT_FAILURE
, "Can't add user `%s': "
1186 "`%s' is already a user", login_name
, login_name
);
1188 if (up
->u_flags
& F_HOMEDIR
) {
1189 (void)strlcpy(home
, up
->u_home
, sizeof(home
));
1191 /* if home directory hasn't been given, make it up */
1192 (void)snprintf(home
, sizeof(home
), "%s/%s", up
->u_basedir
,
1195 if (up
->u_flags
& F_SHELL
) {
1197 if (!valid_shell(up
->u_shell
)) {
1199 (void)close(ptmpfd
);
1202 errx(EXIT_FAILURE
, "Can't add user `%s': "
1203 "Cannot access shell `%s'",
1204 login_name
, up
->u_shell
);
1209 if (!scantime(&inactive
, up
->u_inactive
)) {
1210 warnx("Warning: inactive time `%s' invalid, password expiry off",
1213 if (!scantime(&expire
, up
->u_expire
) || expire
== -1) {
1214 warnx("Warning: expire time `%s' invalid, account expiry off",
1216 expire
= 0; /* Just in case. */
1218 if (lstat(home
, &st
) < 0 && !(up
->u_flags
& F_MKDIR
)) {
1219 warnx("Warning: home directory `%s' doesn't exist, "
1220 "and -m was not specified", home
);
1222 password
[sizeof(password
) - 1] = '\0';
1223 if (up
->u_password
!= NULL
&& valid_password_length(up
->u_password
)) {
1224 (void)strlcpy(password
, up
->u_password
, sizeof(password
));
1226 (void)memset(password
, '*', DES_Len
);
1227 password
[DES_Len
] = 0;
1228 if (up
->u_password
!= NULL
) {
1229 warnx("Password `%s' is invalid: setting it to `%s'",
1230 up
->u_password
, password
);
1233 cc
= snprintf(buf
, sizeof(buf
), "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
1248 if (write(ptmpfd
, buf
, (size_t) cc
) != cc
) {
1250 (void)close(ptmpfd
);
1253 err(EXIT_FAILURE
, "Can't add user `%s': write failed",
1256 if (up
->u_flags
& F_MKDIR
) {
1257 if (lstat(home
, &st
) == 0) {
1258 (void)close(ptmpfd
);
1261 "Can't add user `%s': home directory `%s' "
1262 "already exists", login_name
, home
);
1264 if (asystem("%s -p %s", MKDIR
, home
) != 0) {
1265 (void)close(ptmpfd
);
1267 errx(EXIT_FAILURE
, "Can't add user `%s': "
1268 "can't mkdir `%s'", login_name
, home
);
1270 (void)copydotfiles(up
->u_skeldir
, up
->u_uid
, gid
, home
,
1274 if (strcmp(up
->u_primgrp
, "=uid") == 0 &&
1275 getgrnam(login_name
) == NULL
&&
1276 !creategid(login_name
, gid
, login_name
)) {
1277 (void)close(ptmpfd
);
1279 errx(EXIT_FAILURE
, "Can't add user `%s': can't create gid %d ",
1282 if (up
->u_groupc
> 0 &&
1283 !append_group(login_name
, up
->u_groupc
, up
->u_groupv
)) {
1284 (void)close(ptmpfd
);
1286 errx(EXIT_FAILURE
, "Can't add user `%s': can't append "
1287 "to new groups", login_name
);
1289 (void)close(ptmpfd
);
1290 #if PW_MKDB_ARGC == 2
1291 if (pw_mkdb(login_name
, 0) < 0)
1297 errx(EXIT_FAILURE
, "Can't add user `%s': pw_mkdb failed",
1300 syslog(LOG_INFO
, "New user added: name=%s, uid=%d, gid=%d, home=%s, "
1301 "shell=%s", login_name
, up
->u_uid
, gid
, home
, up
->u_shell
);
1305 /* remove a user from the groups file */
1307 rm_user_from_groups(char *login_name
)
1310 regmatch_t matchv
[10];
1314 char line
[MaxEntryLen
];
1315 char buf
[MaxEntryLen
];
1316 char f
[MaxFileNameLen
];
1321 (void)snprintf(line
, sizeof(line
), "(:|,)(%s)(,|$)", login_name
);
1322 if ((sc
= regcomp(&r
, line
, REG_EXTENDED
|REG_NEWLINE
)) != 0) {
1323 (void)regerror(sc
, &r
, buf
, sizeof(buf
));
1324 warnx("Can't compile regular expression `%s' (%s)", line
,
1328 if ((from
= fopen(_PATH_GROUP
, "r+")) == NULL
) {
1329 warn("Can't remove user `%s' from `%s': can't open `%s'",
1330 login_name
, _PATH_GROUP
, _PATH_GROUP
);
1333 if (flock(fileno(from
), LOCK_EX
| LOCK_NB
) < 0) {
1334 warn("Can't remove user `%s' from `%s': can't lock `%s'",
1335 login_name
, _PATH_GROUP
, _PATH_GROUP
);
1339 (void)fstat(fileno(from
), &st
);
1340 (void)snprintf(f
, sizeof(f
), "%s.XXXXXX", _PATH_GROUP
);
1341 if ((fd
= mkstemp(f
)) < 0) {
1342 warn("Can't remove user `%s' from `%s': mkstemp failed",
1343 login_name
, _PATH_GROUP
);
1347 if ((to
= fdopen(fd
, "w")) == NULL
) {
1348 warn("Can't remove user `%s' from `%s': fdopen `%s' failed",
1349 login_name
, _PATH_GROUP
, f
);
1355 while (fgets(buf
, sizeof(buf
), from
) != NULL
) {
1357 if (regexec(&r
, buf
, 10, matchv
, 0) == 0) {
1358 if (buf
[(int)matchv
[1].rm_so
] == ',') {
1359 matchv
[2].rm_so
= matchv
[1].rm_so
;
1360 } else if (matchv
[2].rm_eo
!= matchv
[3].rm_eo
) {
1361 matchv
[2].rm_eo
= matchv
[3].rm_eo
;
1363 cc
-= (int) matchv
[2].rm_eo
;
1364 sc
= (int) matchv
[2].rm_so
;
1365 if (fwrite(buf
, sizeof(char), (size_t)sc
, to
) != sc
||
1366 fwrite(&buf
[(int)matchv
[2].rm_eo
], sizeof(char),
1367 (size_t)cc
, to
) != cc
) {
1368 warn("Can't remove user `%s' from `%s': "
1369 "short write to `%s'", login_name
,
1376 } else if (fwrite(buf
, sizeof(char), (unsigned) cc
, to
) != cc
) {
1377 warn("Can't remove user `%s' from `%s': "
1378 "short write to `%s'", login_name
, _PATH_GROUP
, f
);
1387 if (rename(f
, _PATH_GROUP
) < 0) {
1388 warn("Can't remove user `%s' from `%s': "
1389 "can't rename `%s' to `%s'",
1390 login_name
, _PATH_GROUP
, f
, _PATH_GROUP
);
1394 (void)chmod(_PATH_GROUP
, st
.st_mode
& 07777);
1398 /* check that the user or group is local, not from YP/NIS */
1400 is_local(char *name
, const char *file
)
1403 char buf
[MaxEntryLen
];
1407 if ((fp
= fopen(file
, "r")) == NULL
) {
1408 err(EXIT_FAILURE
, "Can't open `%s'", file
);
1411 for (ret
= 0 ; fgets(buf
, sizeof(buf
), fp
) != NULL
; ) {
1412 if (strncmp(buf
, name
, len
) == 0 && buf
[len
] == ':') {
1423 moduser(char *login_name
, char *newlogin
, user_t
*up
, int allow_samba
)
1425 struct passwd
*pwp
, pw
;
1427 const char *homedir
;
1433 char newdir
[MaxFileNameLen
];
1434 char buf
[MaxEntryLen
];
1435 char pwbuf
[MaxEntryLen
];
1441 if (!valid_login(newlogin
, allow_samba
)) {
1442 errx(EXIT_FAILURE
, "Can't modify user `%s': invalid login name",
1445 if (getpwnam_r(login_name
, &pw
, pwbuf
, sizeof(pwbuf
), &pwp
) != 0
1447 errx(EXIT_FAILURE
, "Can't modify user `%s': no such user",
1450 if (!is_local(login_name
, _PATH_MASTERPASSWD
)) {
1451 errx(EXIT_FAILURE
, "Can't modify user `%s': must be a local user",
1454 /* keep dir name in case we need it for '-m' */
1455 homedir
= pwp
->pw_dir
;
1457 if ((masterfd
= open(_PATH_MASTERPASSWD
, O_RDWR
)) < 0) {
1458 err(EXIT_FAILURE
, "Can't modify user `%s': can't open `%s'",
1459 login_name
, _PATH_MASTERPASSWD
);
1461 if (flock(masterfd
, LOCK_EX
| LOCK_NB
) < 0) {
1462 err(EXIT_FAILURE
, "Can't modify user `%s': can't lock `%s'",
1463 login_name
, _PATH_MASTERPASSWD
);
1466 if ((ptmpfd
= pw_lock(WAITSECS
)) < 0) {
1468 (void)close(masterfd
);
1470 err(EXIT_FAILURE
, "Can't modify user `%s': "
1471 "can't obtain pw_lock", login_name
);
1473 if ((master
= fdopen(masterfd
, "r")) == NULL
) {
1475 (void)close(masterfd
);
1476 (void)close(ptmpfd
);
1479 err(EXIT_FAILURE
, "Can't modify user `%s': "
1480 "fdopen fd for %s", login_name
, _PATH_MASTERPASSWD
);
1483 if (up
->u_flags
& F_USERNAME
) {
1486 * check new name isn't already in use
1488 if (strcmp(login_name
, newlogin
) != 0 &&
1489 getpwnam(newlogin
) != NULL
) {
1490 (void)close(ptmpfd
);
1492 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1493 "`%s' is already a user", login_name
,
1496 pwp
->pw_name
= newlogin
;
1499 * Provide a new directory name in case the
1500 * home directory is to be moved.
1502 if (up
->u_flags
& F_MKDIR
) {
1503 (void)snprintf(newdir
, sizeof(newdir
), "%s/%s",
1504 up
->u_basedir
, newlogin
);
1505 pwp
->pw_dir
= newdir
;
1508 if (up
->u_flags
& F_PASSWORD
) {
1509 if (up
->u_password
!= NULL
) {
1510 if (!valid_password_length(up
->u_password
)) {
1511 (void)close(ptmpfd
);
1514 "Can't modify user `%s': "
1515 "invalid password: `%s'",
1516 login_name
, up
->u_password
);
1519 strstr(pwp
->pw_passwd
, LOCKED
)) != NULL
) {
1521 * account is locked - keep it locked
1522 * and just change the password.
1524 if (asprintf(&locked_pwd
, "%s%s",
1525 LOCKED
, up
->u_password
) == -1) {
1526 (void)close(ptmpfd
);
1529 "Can't modify user `%s': "
1533 pwp
->pw_passwd
= locked_pwd
;
1535 pwp
->pw_passwd
= up
->u_password
;
1540 /* check whether we should lock the account. */
1541 if (up
->u_locked
== LOCK
) {
1542 /* check to see account if already locked. */
1543 if ((locked_pwd
= strstr(pwp
->pw_passwd
, LOCKED
))
1545 warnx("Account is already locked");
1547 if (asprintf(&locked_pwd
, "%s%s", LOCKED
,
1548 pwp
->pw_passwd
) == -1) {
1549 (void)close(ptmpfd
);
1552 "Can't modify user `%s': "
1553 "asprintf failed", login_name
);
1555 pwp
->pw_passwd
= locked_pwd
;
1557 } else if (up
->u_locked
== UNLOCK
) {
1558 if ((locked_pwd
= strstr(pwp
->pw_passwd
, LOCKED
))
1560 warnx("Can't modify user `%s': "
1561 "account is not locked", login_name
);
1563 pwp
->pw_passwd
= locked_pwd
+ strlen(LOCKED
);
1567 if (up
->u_flags
& F_UID
) {
1568 /* check uid isn't already allocated */
1569 if (!(up
->u_flags
& F_DUPUID
) &&
1570 getpwuid((uid_t
)(up
->u_uid
)) != NULL
) {
1571 (void)close(ptmpfd
);
1573 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1574 "uid `%d' is already in use", login_name
,
1577 pwp
->pw_uid
= up
->u_uid
;
1579 if (up
->u_flags
& F_GROUP
) {
1580 /* if -g=uid was specified, check gid is unused */
1581 if (strcmp(up
->u_primgrp
, "=uid") == 0) {
1582 if (getgrgid((gid_t
)(pwp
->pw_uid
)) != NULL
) {
1583 (void)close(ptmpfd
);
1586 "Can't modify user `%s': "
1587 "gid %d is already in use",
1588 login_name
, up
->u_uid
);
1590 pwp
->pw_gid
= pwp
->pw_uid
;
1591 } else if ((grp
= getgrnam(up
->u_primgrp
)) != NULL
) {
1592 pwp
->pw_gid
= grp
->gr_gid
;
1593 } else if (is_number(up
->u_primgrp
) &&
1595 (gid_t
)atoi(up
->u_primgrp
))) != NULL
) {
1596 pwp
->pw_gid
= grp
->gr_gid
;
1598 (void)close(ptmpfd
);
1600 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1601 "group %s not found", login_name
,
1605 if (up
->u_flags
& F_INACTIVE
) {
1606 if (!scantime(&pwp
->pw_change
, up
->u_inactive
)) {
1607 warnx("Warning: inactive time `%s' invalid, "
1608 "password expiry off",
1612 if (up
->u_flags
& F_EXPIRE
) {
1613 if (!scantime(&pwp
->pw_expire
, up
->u_expire
) ||
1614 pwp
->pw_expire
== -1) {
1615 warnx("Warning: expire time `%s' invalid, "
1616 "account expiry off",
1621 if (up
->u_flags
& F_COMMENT
) {
1622 pwp
->pw_gecos
= up
->u_comment
;
1624 if (up
->u_flags
& F_HOMEDIR
) {
1625 pwp
->pw_dir
= up
->u_home
;
1627 if (up
->u_flags
& F_SHELL
) {
1629 if (!valid_shell(up
->u_shell
)) {
1631 (void)close(ptmpfd
);
1634 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1635 "Cannot access shell `%s'",
1636 login_name
, up
->u_shell
);
1638 pwp
->pw_shell
= up
->u_shell
;
1640 pwp
->pw_shell
= up
->u_shell
;
1644 if (up
->u_flags
& F_CLASS
) {
1645 if (!valid_class(up
->u_class
)) {
1646 (void)close(ptmpfd
);
1648 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1649 "no such login class `%s'", login_name
,
1652 pwp
->pw_class
= up
->u_class
;
1656 loginc
= strlen(login_name
);
1657 while (fgets(buf
, sizeof(buf
), master
) != NULL
) {
1658 if ((colon
= strchr(buf
, ':')) == NULL
) {
1659 warnx("Malformed entry `%s'. Skipping", buf
);
1662 colonc
= (size_t)(colon
- buf
);
1663 if (strncmp(login_name
, buf
, loginc
) == 0 && loginc
== colonc
) {
1665 len
= snprintf(buf
, sizeof(buf
), "%s:%s:%d:%d:"
1669 ":%ld:%ld:%s:%s:%s\n",
1677 (long)pwp
->pw_change
,
1678 (long)pwp
->pw_expire
,
1682 if (write(ptmpfd
, buf
, len
) != len
) {
1684 (void)close(ptmpfd
);
1687 err(EXIT_FAILURE
, "Can't modify user "
1688 "`%s': write", login_name
);
1693 if (write(ptmpfd
, buf
, len
) != len
) {
1695 (void)close(masterfd
);
1696 (void)close(ptmpfd
);
1699 err(EXIT_FAILURE
, "Can't modify `%s': "
1700 "write", login_name
);
1705 if ((up
->u_flags
& F_MKDIR
) &&
1706 asystem("%s %s %s", MV
, homedir
, pwp
->pw_dir
) != 0) {
1707 (void)close(ptmpfd
);
1709 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1710 "can't move `%s' to `%s'",
1711 login_name
, homedir
, pwp
->pw_dir
);
1713 if (up
->u_groupc
> 0 &&
1714 !append_group(newlogin
, up
->u_groupc
, up
->u_groupv
)) {
1715 (void)close(ptmpfd
);
1717 errx(EXIT_FAILURE
, "Can't modify user `%s': "
1718 "can't append `%s' to new groups",
1719 login_name
, newlogin
);
1722 (void)close(ptmpfd
);
1723 (void)fclose(master
);
1724 #if PW_MKDB_ARGC == 2
1725 if (up
!= NULL
&& strcmp(login_name
, newlogin
) == 0) {
1726 error
= pw_mkdb(login_name
, 0);
1728 error
= pw_mkdb(NULL
, 0);
1735 errx(EXIT_FAILURE
, "Can't modify user `%s': pw_mkdb failed",
1739 syslog(LOG_INFO
, "User removed: name=%s", login_name
);
1740 } else if (strcmp(login_name
, newlogin
) == 0) {
1741 syslog(LOG_INFO
, "User information modified: name=%s, uid=%d, "
1742 "gid=%d, home=%s, shell=%s",
1743 login_name
, pwp
->pw_uid
, pwp
->pw_gid
, pwp
->pw_dir
,
1746 syslog(LOG_INFO
, "User information modified: name=%s, "
1747 "new name=%s, uid=%d, gid=%d, home=%s, shell=%s",
1748 login_name
, newlogin
, pwp
->pw_uid
, pwp
->pw_gid
,
1749 pwp
->pw_dir
, pwp
->pw_shell
);
1755 /* see if we can find out the user struct */
1756 static struct passwd
*
1757 find_user_info(const char *name
)
1761 if ((pwp
= getpwnam(name
)) != NULL
) {
1764 if (is_number(name
) && (pwp
= getpwuid((uid_t
)atoi(name
))) != NULL
) {
1771 /* see if we can find out the group struct */
1772 static struct group
*
1773 find_group_info(const char *name
)
1777 if ((grp
= getgrnam(name
)) != NULL
) {
1780 if (is_number(name
) && (grp
= getgrgid((gid_t
)atoi(name
))) != NULL
) {
1786 /* print out usage message, and then exit */
1788 usermgmt_usage(const char *prog
)
1790 if (strcmp(prog
, "useradd") == 0) {
1791 (void)fprintf(stderr
, "usage: %s -D [-F] [-b base-dir] "
1792 "[-e expiry-time] [-f inactive-time]\n"
1793 "\t[-g gid | name | =uid] [-k skel-dir] [-L login-class]\n"
1794 "\t[-M homeperm] [-r lowuid..highuid] [-s shell]\n", prog
);
1795 (void)fprintf(stderr
, "usage: %s [-moSv] [-b base-dir] "
1796 "[-c comment] [-d home-dir] [-e expiry-time]\n"
1797 "\t[-f inactive-time] [-G secondary-group] "
1798 "[-g gid | name | =uid]\n"
1799 "\t[-k skeletondir] [-L login-class] [-M homeperm] "
1801 "\t[-r lowuid..highuid] [-s shell] [-u uid] user\n",
1803 } else if (strcmp(prog
, "usermod") == 0) {
1804 (void)fprintf(stderr
, "usage: %s [-FmoSv] [-C yes/no] "
1805 "[-c comment] [-d home-dir] [-e expiry-time]\n"
1806 "\t[-f inactive] [-G secondary-group] "
1807 "[-g gid | name | =uid]\n"
1808 "\t[-L login-class] [-l new-login] [-p password] "
1809 "[-s shell] [-u uid]\n"
1811 } else if (strcmp(prog
, "userdel") == 0) {
1812 (void)fprintf(stderr
, "usage: %s -D [-p preserve-value]\n", prog
);
1813 (void)fprintf(stderr
,
1814 "usage: %s [-rSv] [-p preserve-value] user\n", prog
);
1816 } else if (strcmp(prog
, "userinfo") == 0) {
1817 (void)fprintf(stderr
, "usage: %s [-e] user\n", prog
);
1819 } else if (strcmp(prog
, "groupadd") == 0) {
1820 (void)fprintf(stderr
, "usage: %s [-ov] [-g gid]"
1821 " [-r lowgid..highgid] group\n", prog
);
1822 } else if (strcmp(prog
, "groupdel") == 0) {
1823 (void)fprintf(stderr
, "usage: %s [-v] group\n", prog
);
1824 } else if (strcmp(prog
, "groupmod") == 0) {
1825 (void)fprintf(stderr
,
1826 "usage: %s [-ov] [-g gid] [-n newname] group\n", prog
);
1827 } else if (strcmp(prog
, "user") == 0 || strcmp(prog
, "group") == 0) {
1828 (void)fprintf(stderr
,
1829 "usage: %s ( add | del | mod | info ) ...\n", prog
);
1831 } else if (strcmp(prog
, "groupinfo") == 0) {
1832 (void)fprintf(stderr
, "usage: %s [-ev] group\n", prog
);
1840 #define ADD_OPT_EXTENSIONS "M:p:r:vL:S"
1842 #define ADD_OPT_EXTENSIONS
1846 useradd(int argc
, char **argv
)
1849 user_t
*up
= &def
.user
;
1857 read_defaults(&def
);
1859 defaultfield
= bigD
= 0;
1860 while ((c
= getopt(argc
, argv
, "DFG:b:c:d:e:f:g:k:mou:s:"
1861 ADD_OPT_EXTENSIONS
)) != -1) {
1868 * Setting -1 will force the new user to
1869 * change their password as soon as they
1870 * next log in - passwd(5).
1873 memsave(&up
->u_inactive
, "-1", strlen("-1"));
1876 while (up
->u_groupc
< NGROUPS_MAX
&&
1877 (up
->u_groupv
[up
->u_groupc
] = strsep(&optarg
, ",")) != NULL
) {
1878 if (up
->u_groupv
[up
->u_groupc
][0] != 0) {
1882 if (optarg
!= NULL
) {
1883 warnx("Truncated list of secondary groups "
1884 "to %d entries", NGROUPS_MAX
);
1889 up
->u_allow_samba
= 1;
1894 memsave(&up
->u_basedir
, optarg
, strlen(optarg
));
1897 memsave(&up
->u_comment
, optarg
, strlen(optarg
));
1900 memsave(&up
->u_home
, optarg
, strlen(optarg
));
1901 up
->u_flags
|= F_HOMEDIR
;
1905 memsave(&up
->u_expire
, optarg
, strlen(optarg
));
1909 memsave(&up
->u_inactive
, optarg
, strlen(optarg
));
1913 memsave(&up
->u_primgrp
, optarg
, strlen(optarg
));
1917 memsave(&up
->u_skeldir
, optarg
, strlen(optarg
));
1922 memsave(&up
->u_class
, optarg
, strlen(optarg
));
1926 up
->u_flags
|= F_MKDIR
;
1931 up
->u_homeperm
= strtoul(optarg
, NULL
, 8);
1935 up
->u_flags
|= F_DUPUID
;
1939 memsave(&up
->u_password
, optarg
, strlen(optarg
));
1945 (void)save_range(&up
->u_r
, optarg
);
1949 up
->u_flags
|= F_SHELL
;
1951 memsave(&up
->u_shell
, optarg
, strlen(optarg
));
1954 up
->u_uid
= check_numeric(optarg
, "uid");
1962 usermgmt_usage("useradd");
1969 return setdefaults(up
) ? EXIT_SUCCESS
: EXIT_FAILURE
;
1971 (void)printf("group\t\t%s\n", up
->u_primgrp
);
1972 (void)printf("base_dir\t%s\n", up
->u_basedir
);
1973 (void)printf("skel_dir\t%s\n", up
->u_skeldir
);
1974 (void)printf("shell\t\t%s\n", up
->u_shell
);
1976 (void)printf("class\t\t%s\n", up
->u_class
);
1977 (void)printf("homeperm\t0%o\n", up
->u_homeperm
);
1979 (void)printf("inactive\t%s\n", (up
->u_inactive
== NULL
) ?
1980 UNSET_INACTIVE
: up
->u_inactive
);
1981 (void)printf("expire\t\t%s\n", (up
->u_expire
== NULL
) ?
1982 UNSET_EXPIRY
: up
->u_expire
);
1984 for (i
= 0 ; i
< up
->u_rc
; i
++) {
1985 (void)printf("range\t\t%d..%d\n",
1986 up
->u_rv
[i
].r_from
, up
->u_rv
[i
].r_to
);
1989 return EXIT_SUCCESS
;
1994 usermgmt_usage("useradd");
1997 openlog("useradd", LOG_PID
, LOG_USER
);
1998 return adduser(*argv
, up
) ? EXIT_SUCCESS
: EXIT_FAILURE
;
2002 #define MOD_OPT_EXTENSIONS "p:vL:S"
2004 #define MOD_OPT_EXTENSIONS
2008 usermod(int argc
, char **argv
)
2011 user_t
*up
= &def
.user
;
2012 char newuser
[MaxUserNameLen
+ 1];
2013 int c
, have_new_user
;
2015 (void)memset(newuser
, 0, sizeof(newuser
));
2016 read_defaults(&def
);
2019 while ((c
= getopt(argc
, argv
, "C:FG:c:d:e:f:g:l:mos:u:"
2020 MOD_OPT_EXTENSIONS
)) != -1) {
2023 while (up
->u_groupc
< NGROUPS_MAX
&&
2024 (up
->u_groupv
[up
->u_groupc
] =
2025 strsep(&optarg
, ",")) != NULL
) {
2026 if (up
->u_groupv
[up
->u_groupc
][0] != 0) {
2030 if (optarg
!= NULL
) {
2031 warnx("Truncated list of secondary groups "
2032 "to %d entries", NGROUPS_MAX
);
2034 up
->u_flags
|= F_SECGROUP
;
2038 up
->u_allow_samba
= 1;
2042 memsave(&up
->u_comment
, optarg
, strlen(optarg
));
2043 up
->u_flags
|= F_COMMENT
;
2046 if (strcasecmp(optarg
, "yes") == 0) {
2047 up
->u_locked
= LOCK
;
2048 } else if (strcasecmp(optarg
, "no") == 0) {
2049 up
->u_locked
= UNLOCK
;
2053 "Please type 'yes' or 'no'");
2057 memsave(&up
->u_inactive
, "-1", strlen("-1"));
2058 up
->u_flags
|= F_INACTIVE
;
2061 memsave(&up
->u_home
, optarg
, strlen(optarg
));
2062 up
->u_flags
|= F_HOMEDIR
;
2065 memsave(&up
->u_expire
, optarg
, strlen(optarg
));
2066 up
->u_flags
|= F_EXPIRE
;
2069 memsave(&up
->u_inactive
, optarg
, strlen(optarg
));
2070 up
->u_flags
|= F_INACTIVE
;
2073 memsave(&up
->u_primgrp
, optarg
, strlen(optarg
));
2074 up
->u_flags
|= F_GROUP
;
2077 (void)strlcpy(newuser
, optarg
, sizeof(newuser
));
2079 up
->u_flags
|= F_USERNAME
;
2083 memsave(&up
->u_class
, optarg
, strlen(optarg
));
2084 up
->u_flags
|= F_CLASS
;
2088 up
->u_flags
|= F_MKDIR
;
2091 up
->u_flags
|= F_DUPUID
;
2095 memsave(&up
->u_password
, optarg
, strlen(optarg
));
2096 up
->u_flags
|= F_PASSWORD
;
2100 memsave(&up
->u_shell
, optarg
, strlen(optarg
));
2101 up
->u_flags
|= F_SHELL
;
2104 up
->u_uid
= check_numeric(optarg
, "uid");
2105 up
->u_flags
|= F_UID
;
2113 usermgmt_usage("usermod");
2117 if ((up
->u_flags
& F_MKDIR
) && !(up
->u_flags
& F_HOMEDIR
) &&
2118 !(up
->u_flags
& F_USERNAME
)) {
2119 warnx("Option 'm' useless without 'd' or 'l' -- ignored");
2120 up
->u_flags
&= ~F_MKDIR
;
2125 usermgmt_usage("usermod");
2128 openlog("usermod", LOG_PID
, LOG_USER
);
2129 return moduser(*argv
, (have_new_user
) ? newuser
: *argv
, up
,
2130 up
->u_allow_samba
) ? EXIT_SUCCESS
: EXIT_FAILURE
;
2134 #define DEL_OPT_EXTENSIONS "Dp:vS"
2136 #define DEL_OPT_EXTENSIONS
2140 userdel(int argc
, char **argv
)
2144 user_t
*up
= &def
.user
;
2145 char password
[PasswordLength
+ 1];
2151 read_defaults(&def
);
2152 defaultfield
= bigD
= rmhome
= 0;
2153 while ((c
= getopt(argc
, argv
, "r" DEL_OPT_EXTENSIONS
)) != -1) {
2162 up
->u_allow_samba
= 1;
2168 up
->u_preserve
= (strcmp(optarg
, "true") == 0) ? 1 :
2169 (strcmp(optarg
, "yes") == 0) ? 1 :
2182 usermgmt_usage("userdel");
2190 return setdefaults(up
) ? EXIT_SUCCESS
: EXIT_FAILURE
;
2192 (void)printf("preserve\t%s\n", (up
->u_preserve
) ? "true" :
2194 return EXIT_SUCCESS
;
2200 usermgmt_usage("userdel");
2203 if ((pwp
= getpwnam(*argv
)) == NULL
) {
2204 warnx("No such user `%s'", *argv
);
2205 return EXIT_FAILURE
;
2208 (void)removehomedir(pwp
);
2210 if (up
->u_preserve
) {
2211 up
->u_flags
|= F_SHELL
;
2212 memsave(&up
->u_shell
, NOLOGIN
, strlen(NOLOGIN
));
2213 (void)memset(password
, '*', DES_Len
);
2214 password
[DES_Len
] = 0;
2215 memsave(&up
->u_password
, password
, strlen(password
));
2216 up
->u_flags
|= F_PASSWORD
;
2217 openlog("userdel", LOG_PID
, LOG_USER
);
2218 return moduser(*argv
, *argv
, up
, up
->u_allow_samba
) ?
2219 EXIT_SUCCESS
: EXIT_FAILURE
;
2221 if (!rm_user_from_groups(*argv
)) {
2224 openlog("userdel", LOG_PID
, LOG_USER
);
2225 return moduser(*argv
, *argv
, NULL
, up
->u_allow_samba
) ?
2226 EXIT_SUCCESS
: EXIT_FAILURE
;
2230 #define GROUP_ADD_OPT_EXTENSIONS "r:v"
2232 #define GROUP_ADD_OPT_EXTENSIONS
2237 groupadd(int argc
, char **argv
)
2240 group_t
*gp
= &def
.group
;
2247 read_defaults(&def
);
2248 while ((c
= getopt(argc
, argv
, "g:o" GROUP_ADD_OPT_EXTENSIONS
)) != -1) {
2251 gid
= check_numeric(optarg
, "gid");
2258 (void)save_range(&gp
->g_r
, optarg
);
2265 usermgmt_usage("groupadd");
2272 usermgmt_usage("groupadd");
2274 if (gp
->g_rc
== 0) {
2275 gp
->g_rv
[gp
->g_rc
].r_from
= DEF_LOWUID
;
2276 gp
->g_rv
[gp
->g_rc
].r_to
= DEF_HIGHUID
;
2279 gp
->g_defrc
= gp
->g_rc
;
2286 * Look for a free GID in the command line ranges (if any).
2287 * These start after the ranges specified in the config file.
2289 for (i
= gp
->g_defrc
; !got_id
&& i
< gp
->g_rc
; i
++) {
2290 got_id
= getnextgid(&gid
,
2291 gp
->g_rv
[i
].r_from
, gp
->g_rv
[i
].r_to
);
2294 * If there were no free GIDs in the command line ranges,
2295 * try the ranges from the config file (there will always
2296 * be at least one default).
2298 for (i
= 0; !got_id
&& i
< gp
->g_defrc
; i
++) {
2299 got_id
= getnextgid(&gid
,
2300 gp
->g_rv
[i
].r_from
, gp
->g_rv
[i
].r_to
);
2303 errx(EXIT_FAILURE
, "Can't add group: can't get next gid");
2305 if (!dupgid
&& getgrgid((gid_t
) gid
) != NULL
) {
2306 errx(EXIT_FAILURE
, "Can't add group: gid %d is a duplicate",
2309 if (!valid_group(*argv
)) {
2310 warnx("Invalid group name `%s'", *argv
);
2312 openlog("groupadd", LOG_PID
, LOG_USER
);
2313 if (!creategid(*argv
, gid
, ""))
2316 return EXIT_SUCCESS
;
2320 #define GROUP_DEL_OPT_EXTENSIONS "v"
2322 #define GROUP_DEL_OPT_EXTENSIONS
2325 /* remove a group */
2327 groupdel(int argc
, char **argv
)
2331 while ((c
= getopt(argc
, argv
, "" GROUP_DEL_OPT_EXTENSIONS
)) != -1) {
2339 usermgmt_usage("groupdel");
2346 usermgmt_usage("groupdel");
2348 if (getgrnam(*argv
) == NULL
) {
2349 errx(EXIT_FAILURE
, "No such group `%s'", *argv
);
2352 openlog("groupdel", LOG_PID
, LOG_USER
);
2353 if (!modify_gid(*argv
, NULL
))
2356 return EXIT_SUCCESS
;
2360 #define GROUP_MOD_OPT_EXTENSIONS "v"
2362 #define GROUP_MOD_OPT_EXTENSIONS
2365 /* modify a group */
2367 groupmod(int argc
, char **argv
)
2370 char buf
[MaxEntryLen
];
2381 while ((c
= getopt(argc
, argv
, "g:on:" GROUP_MOD_OPT_EXTENSIONS
)) != -1) {
2384 gid
= check_numeric(optarg
, "gid");
2390 memsave(&newname
, optarg
, strlen(optarg
));
2398 usermgmt_usage("groupmod");
2405 usermgmt_usage("groupmod");
2408 if (gid
< 0 && newname
== NULL
) {
2409 errx(EXIT_FAILURE
, "Nothing to change");
2411 if (dupgid
&& gid
< 0) {
2412 errx(EXIT_FAILURE
, "Duplicate which gid?");
2414 if (!dupgid
&& getgrgid((gid_t
) gid
) != NULL
) {
2415 errx(EXIT_FAILURE
, "Can't modify group: gid %d is a duplicate",
2418 if ((grp
= find_group_info(*argv
)) == NULL
) {
2419 errx(EXIT_FAILURE
, "Can't find group `%s' to modify", *argv
);
2421 if (!is_local(*argv
, _PATH_GROUP
)) {
2422 errx(EXIT_FAILURE
, "Group `%s' must be a local group", *argv
);
2424 if (newname
!= NULL
&& !valid_group(newname
)) {
2425 warnx("Invalid group name `%s'", newname
);
2427 cc
= snprintf(buf
, sizeof(buf
), "%s:%s:%d:",
2428 (newname
) ? newname
: grp
->gr_name
,
2430 (gid
< 0) ? grp
->gr_gid
: gid
);
2431 for (cpp
= grp
->gr_mem
; *cpp
&& cc
< sizeof(buf
) ; cpp
++) {
2432 cc
+= snprintf(&buf
[cc
], sizeof(buf
) - cc
, "%s%s", *cpp
,
2433 (cpp
[1] == NULL
) ? "" : ",");
2435 cc
+= snprintf(&buf
[cc
], sizeof(buf
) - cc
, "\n");
2436 if (newname
!= NULL
)
2438 openlog("groupmod", LOG_PID
, LOG_USER
);
2439 if (!modify_gid(*argv
, buf
))
2442 return EXIT_SUCCESS
;
2446 /* display user information */
2448 userinfo(int argc
, char **argv
)
2452 char buf
[MaxEntryLen
];
2460 while ((i
= getopt(argc
, argv
, "e")) != -1) {
2466 usermgmt_usage("userinfo");
2473 usermgmt_usage("userinfo");
2475 pwp
= find_user_info(*argv
);
2477 exit((pwp
) ? EXIT_SUCCESS
: EXIT_FAILURE
);
2480 errx(EXIT_FAILURE
, "Can't find user `%s'", *argv
);
2482 (void)printf("login\t%s\n", pwp
->pw_name
);
2483 (void)printf("passwd\t%s\n", pwp
->pw_passwd
);
2484 (void)printf("uid\t%d\n", pwp
->pw_uid
);
2485 for (cc
= 0 ; (grp
= getgrent()) != NULL
; ) {
2486 for (cpp
= grp
->gr_mem
; *cpp
; cpp
++) {
2487 if (strcmp(*cpp
, *argv
) == 0 &&
2488 grp
->gr_gid
!= pwp
->pw_gid
) {
2489 cc
+= snprintf(&buf
[cc
], sizeof(buf
) - cc
,
2490 "%s ", grp
->gr_name
);
2494 if ((grp
= getgrgid(pwp
->pw_gid
)) == NULL
) {
2495 (void)printf("groups\t%d %s\n", pwp
->pw_gid
, buf
);
2497 (void)printf("groups\t%s %s\n", grp
->gr_name
, buf
);
2499 (void)printf("change\t%s", pwp
->pw_change
> 0 ?
2500 ctime(&pwp
->pw_change
) : pwp
->pw_change
== -1 ?
2501 "NEXT LOGIN\n" : "NEVER\n");
2502 (void)printf("class\t%s\n", pwp
->pw_class
);
2503 (void)printf("gecos\t%s\n", pwp
->pw_gecos
);
2504 (void)printf("dir\t%s\n", pwp
->pw_dir
);
2505 (void)printf("shell\t%s\n", pwp
->pw_shell
);
2506 (void)printf("expire\t%s", pwp
->pw_expire
?
2507 ctime(&pwp
->pw_expire
) : "NEVER\n");
2508 return EXIT_SUCCESS
;
2513 /* display user information */
2515 groupinfo(int argc
, char **argv
)
2523 while ((i
= getopt(argc
, argv
, "ev")) != -1) {
2532 usermgmt_usage("groupinfo");
2539 usermgmt_usage("groupinfo");
2541 grp
= find_group_info(*argv
);
2543 exit((grp
) ? EXIT_SUCCESS
: EXIT_FAILURE
);
2546 errx(EXIT_FAILURE
, "Can't find group `%s'", *argv
);
2548 (void)printf("name\t%s\n", grp
->gr_name
);
2549 (void)printf("passwd\t%s\n", grp
->gr_passwd
);
2550 (void)printf("gid\t%d\n", grp
->gr_gid
);
2551 (void)printf("members\t");
2552 for (cpp
= grp
->gr_mem
; *cpp
; cpp
++) {
2553 (void)printf("%s", *cpp
);
2555 (void) printf(", ");
2558 (void)fputc('\n', stdout
);
2559 return EXIT_SUCCESS
;