From: Ben Gras Date: Fri, 30 Sep 2011 10:18:10 +0000 (+0000) Subject: Switch to NetBSD passwd format X-Git-Tag: v3.2.0~255 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/Bv9ARM.ch10.html?a=commitdiff_plain;h=5c00743626989a611dd2966296c00a8cc4abca6f;p=minix.git Switch to NetBSD passwd format Based on work by Vivek Prakash and Gianluca Guida. See UPDATING about caveats on currently existing accounts. . restores netbsd libc pwcache functions --- diff --git a/Makefile b/Makefile index 462f30779..120c700f2 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,7 @@ commands: includes libraries $(MAKE) -C bin all $(MAKE) -C usr.bin all $(MAKE) -C libexec all + $(MAKE) -C usr.sbin all dep-all: $(MAKE) CC=cc -C boot dependall @@ -69,6 +70,7 @@ dep-all: $(MAKE) -C bin dependall $(MAKE) -C usr.bin dependall $(MAKE) -C libexec dependall + $(MAKE) -C usr.sbin dependall $(MAKE) -C kernel dependall $(MAKE) -C servers dependall $(MAKE) -C drivers dependall @@ -85,6 +87,7 @@ all: $(MAKE) -C bin all $(MAKE) -C usr.bin all $(MAKE) -C libexec all + $(MAKE) -C usr.sbin all $(MAKE) -C tools all install: @@ -94,6 +97,7 @@ install: $(MAKE) -C commands install $(MAKE) -C bin install $(MAKE) -C usr.bin install + $(MAKE) -C usr.sbin install $(MAKE) -C servers install $(MAKE) -C share install $(MAKE) -C tools install @@ -104,6 +108,7 @@ clean: mkfiles $(MAKE) -C bin clean $(MAKE) -C usr.bin clean $(MAKE) -C libexec clean + $(MAKE) -C usr.sbin clean $(MAKE) -C tools clean $(MAKE) -C lib clean_all $(MAKE) -C test clean @@ -115,4 +120,5 @@ cleandepend: mkfiles $(MAKE) -C bin cleandepend $(MAKE) -C usr.bin cleandepend $(MAKE) -C libexec cleandepend + $(MAKE) -C usr.sbin cleandepend $(MAKE) -C tools cleandepend diff --git a/commands/Makefile b/commands/Makefile index 73f36e48c..32f0e93c9 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -2,7 +2,7 @@ .include -SUBDIR= aal add_route adduser arp ash at autil awk \ +SUBDIR= aal add_route arp ash at autil awk \ backup badblocks banner basename binpackage \ binpackages bzip2 bzip2recover cal calendar \ cat cawf cd cdprobe checkhier chmem \ @@ -19,7 +19,7 @@ SUBDIR= aal add_route adduser arp ash at autil awk \ lpd ls lspci M mail make MAKEDEV \ mdb mesg mined ackmkdep mkfifo mkfs.mfs mknod \ mkproto modem mount mt netconf newroot nice acknm nohup \ - nonamed od passwd paste patch pax \ + nonamed od paste patch pax \ ping postinstall poweroff pr prep printf printroot \ profile progressbar proto pr_routes ps pwd pwdauth \ ramdisk rarpd rawspeed rcp rdate readall readclock \ diff --git a/commands/adduser/Makefile b/commands/adduser/Makefile deleted file mode 100644 index a922aa548..000000000 --- a/commands/adduser/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -SCRIPTS= adduser.sh -MAN= - -.include diff --git a/commands/adduser/adduser.sh b/commands/adduser/adduser.sh deleted file mode 100644 index 886d0fb5f..000000000 --- a/commands/adduser/adduser.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/bin/sh -# -# adduser 1.0 - add a new user to the system Author: Kees J. Bot -# 16 Jan 1996 - -# Check arguments. -case "$#" in -3) user="$1"; group="$2"; home="$3" - ;; -*) echo "Usage: adduser user group home-dir" >&2; exit 1 -esac - -# We need to be root. -case "`id`" in -'uid=0('*) - ;; -*) echo "adduser: you must be root to add users" >&2; exit 1 -esac - -# User and group names must be alphanumeric and no longer than 8 characters. -len=`expr "$user" : '[a-z][a-z0-9]*$'` -if [ "$len" -eq 0 -o "$len" -gt 8 ] -then - echo >&2 \ -"adduser: the user name must be alphanumeric and no longer than 8 characters" - exit 1 -fi - -len=`expr "$group" : '[a-z][a-z0-9]*$'` -if [ "$len" -eq 0 -o "$len" -gt 8 ] -then - echo >&2 \ -"adduser: the group name must be alphanumeric and no longer than 8 characters" - exit 1 -fi - -# The new user name must not exist, but the group must exist. -if grep "^$user:" /etc/passwd >/dev/null -then - echo "adduser: user $user already exists" >&2 - exit 1 -fi - -gid=`sed -e "/^$group:/!d" -e 's/^[^:]*:[^:]*:\\([^:]*\\):.*/\\1/' /etc/group` -if [ `expr "$gid" : '[0-9]*$'` -eq 0 ] -then - echo "adduser: group $group does not exist" >&2 - exit 1 -fi - -# Find the first free user-id of 10 or higher. -uid=10 -while grep "^[^:]*:[^:]*:$uid:.*" /etc/passwd >/dev/null -do - uid=`expr $uid + 1` -done - -# No interruptions. -trap '' 1 2 3 15 - -# Lock the password file. -ln /etc/passwd /etc/ptmp || { - echo "adduser: password file busy, try again later" - exit 1 -} - -# Make the new home directory, it should not exist already. -mkdir "$home" || { - rm -rf /etc/ptmp - exit 1 -} - -# Make the new home directory by copying the honorary home directory of our -# fearless leader. -echo cpdir /usr/ast "$home" -cpdir /usr/ast "$home" || { - rm -rf /etc/ptmp "$home" - exit 1 -} - -# Change the ownership to the new user. -echo chown -R $uid:$gid "$home" -chown -R $uid:$group "$home" || { - rm -rf /etc/ptmp "$home" - exit 1 -} - -# Is there a shadow password file? If so add an entry. -if [ -f /etc/shadow ] -then - echo "echo $user::0:0::: >>/etc/shadow" - echo "$user::0:0:::" >>/etc/shadow || { - rm -rf /etc/ptmp "$home" - exit 1 - } - pwd="##$user" -else - pwd= -fi - -# Finish up by adding a password file entry. -echo "echo $user:$pwd:$uid:$gid:$user:$home: >>/etc/passwd" -echo "$user:$pwd:$uid:$gid:$user:$home:" >>/etc/passwd || { - rm -rf /etc/ptmp "$home" - exit 1 -} - -# Remove the lock. -rm /etc/ptmp || exit - -echo " -The new user $user has been added to the system. Note that the password, -full name, and shell may be changed with the commands passwd(1), chfn(1), -and chsh(1). The password is now empty, so only console logins are possible." -if [ $gid = 0 ] -then - echo "\ -Also note that a new operator needs an executable search path (\$PATH) that -does not contain the current directory (an empty field or "." in \$PATH)." -fi -exit 0 diff --git a/commands/chown/chown.c b/commands/chown/chown.c index a6a0f04ff..5fe644cca 100644 --- a/commands/chown/chown.c +++ b/commands/chown/chown.c @@ -31,7 +31,7 @@ #define S_IUGID (S_ISUID|S_ISGID) /* Global variables, such as flags and path names */ -int gflag, oflag, rflag, error; +int gflag, oflag, rflag, error, hflag = 0; char *pgmname, path[PATH_MAX + 1]; uid_t nuid; gid_t ngid; @@ -44,9 +44,6 @@ _PROTOTYPE(void usage, (void)); * identical, except that the default when a single name is given as an * argument is to take a group id rather than an user id. This allow the * non-Posix "chgrp user:group file". - * The single option switch used by chown/chgrp (-R) does not warrant a - * call to the getopt stuff. The two others flags (-g, -u) are set from - * the program name and arguments. */ int main(argc, argv) int argc; @@ -55,20 +52,30 @@ char *argv[]; char *id, *id2; struct group *grp; struct passwd *pwp; + int ch; if (pgmname = strrchr(*argv, '/')) pgmname++; else pgmname = *argv; - argc--; - argv++; gflag = strcmp(pgmname, "chgrp"); - if (argc && **argv == '-' && argv[0][1] == 'R') { - argc--; - argv++; - rflag = 1; + while((ch = getopt(argc, argv, "Rh")) != -1) { + switch(ch) { + case 'R': + rflag = 1; + break; + case 'h': + hflag = 1; + break; + default: + usage(); + } } + + argc -= optind; + argv += optind; + if (argc < 2) usage(); id = *argv++; @@ -131,6 +138,11 @@ char *file; if (S_ISLNK(st.st_mode) && rflag) return; /* Note: violates POSIX. */ + if (S_ISLNK(st.st_mode) && hflag) { + fprintf(stderr, "chown: cannot lchown %s\n", file); + error = 1; + } + if (chown(file, oflag ? nuid : st.st_uid, gflag ? ngid : st.st_gid)) { perror(file); error = 1; diff --git a/commands/ftpd200/Makefile b/commands/ftpd200/Makefile index d70c7b8b9..85436e956 100644 --- a/commands/ftpd200/Makefile +++ b/commands/ftpd200/Makefile @@ -8,8 +8,7 @@ SRCS= ftpd.c access.c file.c net.c MAN= ftpd.8 SCRIPTS= ftpdsh FILES= setup.anonftp -.if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no") +NEED_NBSDLIBC=y LDADD+= -lcrypt -.endif .include diff --git a/commands/login/Makefile b/commands/login/Makefile index 6dd5f37a3..37f07bca2 100644 --- a/commands/login/Makefile +++ b/commands/login/Makefile @@ -1,3 +1,5 @@ +.include + PROG= login MAN= .if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no") diff --git a/commands/login/login.c b/commands/login/login.c index 9fbabcd94..a4b25770d 100644 --- a/commands/login/login.c +++ b/commands/login/login.c @@ -339,9 +339,8 @@ char *argv[]; * pre-authorized users. */ } else - if (pwd && secure && strcmp(crypt("", pwd->pw_passwd), - pwd->pw_passwd) == 0) { - check_pw= 0; /* empty password */ + if (pwd && secure && (pwd->pw_passwd[0] == '\0')) { + check_pw= 0; /* empty password, pretend password okay */ } if (check_pw) { diff --git a/commands/ls/Makefile b/commands/ls/Makefile index d3d12a065..bb9142692 100644 --- a/commands/ls/Makefile +++ b/commands/ls/Makefile @@ -1,5 +1,6 @@ PROG= ls BINDIR= /bin MAN= +NEED_NBSDLIBC=yes .include diff --git a/commands/passwd/Makefile b/commands/passwd/Makefile deleted file mode 100644 index b804eb5ce..000000000 --- a/commands/passwd/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -PROG= passwd -BINMODE= 4755 -MAN= -.if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no") -LDADD+= -lcrypt -.endif - -LINKS+= ${BINDIR}/passwd ${BINDIR}/chsh -LINKS+= ${BINDIR}/passwd ${BINDIR}/chfn - -.include diff --git a/commands/passwd/passwd.c b/commands/passwd/passwd.c deleted file mode 100644 index 322647518..000000000 --- a/commands/passwd/passwd.c +++ /dev/null @@ -1,260 +0,0 @@ -/* passwd - change a passwd Author: Adri Koppes */ - -/* chfn, chsh - change full name, shell Added by: Kees J. Bot */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -_PROTOTYPE(void report, (char *label)); -_PROTOTYPE(void quit, (int ex_stat)); -_PROTOTYPE(void fatal, (char *label)); -_PROTOTYPE(void usage, (void)); -_PROTOTYPE(int goodchars, (char *s)); -_PROTOTYPE(int main, (int argc, char **argv)); - -char pw_file[] = "/etc/passwd"; -char sh_file[] = "/etc/shadow"; -char pw_tmp[] = "/etc/ptmp"; -char bad[] = "Permission denied\n"; -char buf[1024]; - -enum action { - PASSWD, CHFN, CHSH -} action = PASSWD; - -char *arg0; - -void report(label) -char *label; -{ - int e = errno; - fprintf(stderr, "%s: ", arg0); - fflush(stderr); - errno = e; - perror(label); -} - -void quit(ex_stat) -int ex_stat; -{ - if (unlink(pw_tmp) < 0 && errno != ENOENT) { - report(pw_tmp); - ex_stat = 1; - } - exit(ex_stat); -} - -void fatal(label) -char *label; -{ - report(label); - quit(1); -} - -void usage() -{ - static char *usages[] = { - "passwd [user]\n", - "chfn [user] fullname\n", - "chsh [user] shell\n" - }; - std_err(usages[(int) action]); - exit(1); -} - -int goodchars(s) -char *s; -{ - int c; - - while ((c = *s++) != 0) { - if (c == ':' || c < ' ' || c >= 127) return(0); - } - return(1); -} - -int main(argc, argv) -int argc; -char *argv[]; -{ - int uid, cn, n; - int fd_pwd, fd_tmp; - FILE *fp_tmp; - time_t salt; - struct passwd *pwd; - char *name, pwname[9], oldpwd[9], newpwd[9], newcrypted[14], sl[2]; - char *argn; - int shadow = 0; - - if ((arg0 = strrchr(argv[0], '/')) != 0) - arg0++; - else - arg0 = argv[0]; - - if (strcmp(arg0, "chfn") == 0) - action = CHFN; - else if (strcmp(arg0, "chsh") == 0) - action = CHSH; - - uid = getuid(); - - n = action == PASSWD ? 1 : 2; - - if (argc != n && argc != n + 1) usage(); - - if (argc == n) { - pwd = getpwuid(uid); - strcpy(pwname, pwd->pw_name); - name = pwname; - } else { - name = argv[1]; - pwd = getpwnam(name); - } - if (pwd == NULL || ((uid != pwd->pw_uid) && uid != 0)) { - std_err(bad); - exit(1); - } - - switch (action) { - case PASSWD: - if (pwd->pw_passwd[0] == '#' && pwd->pw_passwd[1] == '#') { - /* The password is found in the shadow password file. */ - shadow = 1; - strncpy(pwname, pwd->pw_passwd + 2, 8); - pwname[8] = 0; - name = pwname; - setpwfile(sh_file); - if ((pwd= getpwnam(name)) == NULL) { - std_err(bad); - exit(1); - } - printf("Changing the shadow password of %s\n", name); - } else { - printf("Changing the password of %s\n", name); - } - - oldpwd[0] = 0; - if (pwd->pw_passwd[0] != '\0' && uid != 0) { - strcpy(oldpwd, getpass("Old password:")); - if (strcmp(pwd->pw_passwd, crypt(oldpwd, pwd->pw_passwd)) != 0) - { - std_err(bad); - exit(1); - } - } - for (;;) { - strcpy(newpwd, getpass("New password:")); - - if (newpwd[0] == '\0') - std_err("Password cannot be null"); - else if (strcmp(newpwd, getpass("Retype password:")) != 0) - std_err("Passwords don't match"); - else - break; - - std_err(", try again\n"); - } - time(&salt); - sl[0] = (salt & 077) + '.'; - sl[1] = ((salt >> 6) & 077) + '.'; - for (cn = 0; cn < 2; cn++) { - if (sl[cn] > '9') sl[cn] += 7; - if (sl[cn] > 'Z') sl[cn] += 6; - } - strcpy(newcrypted, crypt(newpwd, sl)); - break; - - case CHFN: - case CHSH: - argn = argv[argc - 1]; - - if (strlen(argn) > (action == CHFN ? 80 : 60) || !goodchars(argn)) { - std_err(bad); - exit(1); - } - } - - signal(SIGHUP, SIG_IGN); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - signal(SIGTERM, SIG_IGN); - - umask(0); - n = 10; - while ((fd_tmp = open(pw_tmp, O_RDWR | O_CREAT | O_EXCL, 0400)) < 0) { - if (errno != EEXIST) fatal("Can't create temporary file"); - - if (n-- > 0) { - sleep(2); - } else { - fprintf(stderr, "Password file busy, try again later.\n"); - exit(1); - } - } - - if ((fp_tmp = fdopen(fd_tmp, "w+")) == NULL) fatal(pw_tmp); - - setpwent(); - while ((pwd = getpwent()) != 0) { - if (strcmp(name, pwd->pw_name) == 0) { - switch (action) { - case PASSWD: - pwd->pw_passwd = newcrypted; - break; - case CHFN: - pwd->pw_gecos = argn; - break; - case CHSH: - pwd->pw_shell = argn; - break; - } - } - if (strcmp(pwd->pw_shell, "/bin/sh") == 0 - || strcmp(pwd->pw_shell, "/usr/bin/sh") == 0 - ) - pwd->pw_shell = ""; - - fprintf(fp_tmp, "%s:%s:%s:", - pwd->pw_name, - pwd->pw_passwd, - itoa(pwd->pw_uid) - ); - if (ferror(fp_tmp)) fatal(pw_tmp); - - fprintf(fp_tmp, "%s:%s:%s:%s\n", - itoa(pwd->pw_gid), - pwd->pw_gecos, - pwd->pw_dir, - pwd->pw_shell - ); - if (ferror(fp_tmp)) fatal(pw_tmp); - } - endpwent(); - if (fflush(fp_tmp) == EOF) fatal(pw_tmp); - - if (lseek(fd_tmp, (off_t) 0, SEEK_SET) != 0) - fatal("Can't reread temp file"); - - if ((fd_pwd = open(shadow ? sh_file : pw_file, O_WRONLY | O_TRUNC)) < 0) - fatal("Can't recreate password file"); - - while ((n = read(fd_tmp, buf, sizeof(buf))) != 0) { - if (n < 0 || write(fd_pwd, buf, n) != n) { - report("Error rewriting password file, tell root!"); - exit(1); - } - } - close(fd_tmp); - close(fd_pwd); - quit(0); -} diff --git a/commands/pwdauth/Makefile b/commands/pwdauth/Makefile index 8f6dbe745..c420b7ac1 100644 --- a/commands/pwdauth/Makefile +++ b/commands/pwdauth/Makefile @@ -2,5 +2,8 @@ PROG= pwdauth BINDIR= /usr/lib BINMODE= 4755 MAN= +NEED_NBSDLIBC=y + +LDADD+=-lcrypt .include diff --git a/commands/pwdauth/pwdauth.c b/commands/pwdauth/pwdauth.c index e1e047199..aef1fb4a6 100644 --- a/commands/pwdauth/pwdauth.c +++ b/commands/pwdauth/pwdauth.c @@ -16,7 +16,6 @@ * a no-password login. */ #define nil 0 -#define crypt CRYPT /* The true crypt is included here. */ #include #include #include @@ -30,7 +29,6 @@ #endif #define LEN 1024 -char SHADOW[] = "/etc/shadow"; int main(int argc, char **argv) { @@ -51,11 +49,6 @@ int main(int argc, char **argv) salt = key + strlen(key) + 1; if (salt[0] == '#' && salt[1] == '#') { - /* Get the encrypted password from the shadow password file, - * encrypt key and compare. - */ - setpwfile(SHADOW); - if ((pw= getpwnam(salt + 2)) == nil) return 2; /* A null encrypted password matches a null key, otherwise @@ -80,283 +73,3 @@ int main(int argc, char **argv) if (write(1, salt, strlen(salt) + 1) < 0) return 1; return 0; } - -/* The one and only crypt(3) function. */ - -/* From Andy Tanenbaum's book "Computer Networks", - rewritten in C -*/ - -struct block { - unsigned char b_data[64]; -}; - -struct ordering { - unsigned char o_data[64]; -}; - -static struct block key; - -static struct ordering InitialTr = { - 58,50,42,34,26,18,10, 2,60,52,44,36,28,20,12, 4, - 62,54,46,38,30,22,14, 6,64,56,48,40,32,24,16, 8, - 57,49,41,33,25,17, 9, 1,59,51,43,35,27,19,11, 3, - 61,53,45,37,29,21,13, 5,63,55,47,39,31,23,15, 7, -}; - -static struct ordering FinalTr = { - 40, 8,48,16,56,24,64,32,39, 7,47,15,55,23,63,31, - 38, 6,46,14,54,22,62,30,37, 5,45,13,53,21,61,29, - 36, 4,44,12,52,20,60,28,35, 3,43,11,51,19,59,27, - 34, 2,42,10,50,18,58,26,33, 1,41, 9,49,17,57,25, -}; - -static struct ordering swap = { - 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, - 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64, - 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16, - 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, -}; - -static struct ordering KeyTr1 = { - 57,49,41,33,25,17, 9, 1,58,50,42,34,26,18, - 10, 2,59,51,43,35,27,19,11, 3,60,52,44,36, - 63,55,47,39,31,23,15, 7,62,54,46,38,30,22, - 14, 6,61,53,45,37,29,21,13, 5,28,20,12, 4, -}; - -static struct ordering KeyTr2 = { - 14,17,11,24, 1, 5, 3,28,15, 6,21,10, - 23,19,12, 4,26, 8,16, 7,27,20,13, 2, - 41,52,31,37,47,55,30,40,51,45,33,48, - 44,49,39,56,34,53,46,42,50,36,29,32, -}; - -static struct ordering etr = { - 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, - 8, 9,10,11,12,13,12,13,14,15,16,17, - 16,17,18,19,20,21,20,21,22,23,24,25, - 24,25,26,27,28,29,28,29,30,31,32, 1, -}; - -static struct ordering ptr = { - 16, 7,20,21,29,12,28,17, 1,15,23,26, 5,18,31,10, - 2, 8,24,14,32,27, 3, 9,19,13,30, 6,22,11, 4,25, -}; - -static unsigned char s_boxes[8][64] = { -{ 14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7, - 0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8, - 4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0, - 15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13, -}, - -{ 15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10, - 3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5, - 0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15, - 13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9, -}, - -{ 10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8, - 13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1, - 13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7, - 1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12, -}, - -{ 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15, - 13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9, - 10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4, - 3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14, -}, - -{ 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9, - 14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6, - 4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14, - 11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3, -}, - -{ 12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11, - 10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8, - 9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6, - 4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13, -}, - -{ 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1, - 13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6, - 1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2, - 6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12, -}, - -{ 13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7, - 1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2, - 7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8, - 2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11, -}, -}; - -static int rots[] = { - 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1, -}; - -static void transpose(struct block *data, struct ordering *t, int n) -{ - struct block x; - - x = *data; - - while (n-- > 0) { - data->b_data[n] = x.b_data[t->o_data[n] - 1]; - } -} - -static void rotate(struct block *key) -{ - unsigned char *p = key->b_data; - unsigned char *ep = &(key->b_data[55]); - int data0 = key->b_data[0], data28 = key->b_data[28]; - - while (p++ < ep) *(p-1) = *p; - key->b_data[27] = data0; - key->b_data[55] = data28; -} - -static struct ordering *EP = &etr; - -static void f(int i, struct block *key, struct block *a, struct block *x) -{ - struct block e, ikey, y; - int k; - unsigned char *p, *q, *r; - - e = *a; - transpose(&e, EP, 48); - for (k = rots[i]; k; k--) rotate(key); - ikey = *key; - transpose(&ikey, &KeyTr2, 48); - p = &(y.b_data[48]); - q = &(e.b_data[48]); - r = &(ikey.b_data[48]); - while (p > y.b_data) { - *--p = *--q ^ *--r; - } - q = x->b_data; - for (k = 0; k < 8; k++) { - int xb, r; - - r = *p++ << 5; - r += *p++ << 3; - r += *p++ << 2; - r += *p++ << 1; - r += *p++; - r += *p++ << 4; - - xb = s_boxes[k][r]; - - *q++ = (xb >> 3) & 1; - *q++ = (xb>>2) & 1; - *q++ = (xb>>1) & 1; - *q++ = (xb & 1); - } - transpose(x, &ptr, 32); -} - -static void setkey(char *k) -{ - - key = *((struct block *) k); - transpose(&key, &KeyTr1, 56); -} - -static void encrypt(char *blck, int edflag) -{ - struct block *p = (struct block *) blck; - int i; - - transpose(p, &InitialTr, 64); - for (i = 15; i>= 0; i--) { - int j = edflag ? i : 15 - i; - int k; - struct block b, x; - - b = *p; - for (k = 31; k >= 0; k--) { - p->b_data[k] = b.b_data[k + 32]; - } - f(j, &key, p, &x); - for (k = 31; k >= 0; k--) { - p->b_data[k+32] = b.b_data[k] ^ x.b_data[k]; - } - } - transpose(p, &swap, 64); - transpose(p, &FinalTr, 64); -} - -char *crypt(const char *pw, const char *salt) -{ - char pwb[66]; - char *cp; - static char result[16]; - char *p = pwb; - struct ordering new_etr; - int i; - - while (*pw && p < &pwb[64]) { - int j = 7; - - while (j--) { - *p++ = (*pw >> j) & 01; - } - pw++; - *p++ = 0; - } - while (p < &pwb[64]) *p++ = 0; - - setkey(p = pwb); - - while (p < &pwb[66]) *p++ = 0; - - new_etr = etr; - EP = &new_etr; - if (salt[0] == 0 || salt[1] == 0) salt = "**"; - for (i = 0; i < 2; i++) { - char c = *salt++; - int j; - - result[i] = c; - if ( c > 'Z') c -= 6 + 7 + '.'; /* c was a lower case letter */ - else if ( c > '9') c -= 7 + '.';/* c was upper case letter */ - else c -= '.'; /* c was digit, '.' or '/'. */ - /* now, 0 <= c <= 63 */ - for (j = 0; j < 6; j++) { - if ((c >> j) & 01) { - int t = 6*i + j; - int temp = new_etr.o_data[t]; - new_etr.o_data[t] = new_etr.o_data[t+24]; - new_etr.o_data[t+24] = temp; - } - } - } - - if (result[1] == 0) result[1] = result[0]; - - for (i = 0; i < 25; i++) encrypt(pwb,0); - EP = &etr; - - p = pwb; - cp = result+2; - while (p < &pwb[66]) { - int c = 0; - int j = 6; - - while (j--) { - c <<= 1; - c |= *p++; - } - c += '.'; /* becomes >= '.' */ - if (c > '9') c += 7; /* not in [./0-9], becomes upper */ - if (c > 'Z') c += 6; /* not in [A-Z], becomes lower */ - *cp++ = c; - } - *cp = 0; - return result; -} diff --git a/commands/su/Makefile b/commands/su/Makefile index 0cb239312..634e1bfe8 100644 --- a/commands/su/Makefile +++ b/commands/su/Makefile @@ -1,9 +1,8 @@ PROG= su BINMODE= 4755 MAN= -.if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no") +NEED_NBSDLIBC=y LDADD+= -lcrypt -.endif .include diff --git a/docs/UPDATING b/docs/UPDATING index 45be2aba9..acf2e2e7f 100644 --- a/docs/UPDATING +++ b/docs/UPDATING @@ -1,3 +1,37 @@ +20111109: + Switch to NetBSD passwd system. + + You have to bootstrap pwd_mkdb: + # make clean includes elf-libraries + # make -C usr.sbin/pwd_mkdb install + + Now build world. WARNING: this will blind your system to + /etc/shadow, making current user accounts vanish. Updating the + group file is necessary to add a 'users' group so the new stock + useradd will work. + + # make clean world + # cp etc/group /etc/group + + The new shadow file is /etc/master.passwd. Add your old user + accounts back with useradd(8), groups with groupadd(8), and + set a root pw with passwd(1) if you want. Use vipw(8) to edit + /etc/master.passwd if you want. See useradd(8) to get started + with the new pw format. Test your new system now by logging in. + + Once you're satisfied your new system works, remove the old + adduser, and rely exclusively on the new useradd and + master.passwd. + # rm /usr/bin/adduser + # mv /etc/shadow /etc/shadow.orig + + pwdauth is updated so that current binaries (e.g. sshd) will + work with the new pw db. + + By default your new users are in the 'users' group. Add yourself + to the 'operator' group if you want to be able to su without + typing in the password. + 20111109: fstab format change. /etc/rc reads both formats for a while. Please convert your /etc/fstab to the new format though as diff --git a/drivers/ramdisk/Makefile b/drivers/ramdisk/Makefile index a7bbab5ce..1dcbcfad1 100644 --- a/drivers/ramdisk/Makefile +++ b/drivers/ramdisk/Makefile @@ -25,7 +25,7 @@ PROTO= proto.small STRIPFLAG+= -s .endif -EXTRA=system.conf passwd rs.single +EXTRA=system.conf master.passwd passwd pwd.db rs.single CPPFLAGS+= -I${MINIXSRCDIR}/servers -I${MINIXSRCDIR} CLEANFILES += $(PROGRAMS) $(SCRIPTS) $(EXTRA) bintoc image image.c t proto.gen @@ -150,7 +150,16 @@ ext2: ../../servers/ext2/ext2 system.conf: ../../etc/system.conf install ${STRIPFLAG} ../../etc/$@ $@ -passwd: ../../etc/passwd +passwd: ../../etc/master.passwd + rm -f ../../etc/master.passwd.orig ../../etc/passwd.orig + rm -f ../../etc/pwd.db.tmp ../../etc/spwd.db.tmp + ../../usr.sbin/pwd_mkdb/pwd_mkdb -V 0 -p -d ../../ ../../etc/master.passwd + install ${STRIPFLAG} ../../etc/$@ $@ + +master.passwd: ../../etc/master.passwd + install ${STRIPFLAG} ../../etc/$@ $@ + +pwd.db: passwd install ${STRIPFLAG} ../../etc/$@ $@ rs.single: ../../etc/rs.single diff --git a/etc/Makefile b/etc/Makefile index 2624e603f..008cb9b8e 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -1,23 +1,24 @@ ETC=/etc/ USR=/usr/ USRETC=/usr/etc/ -FILES1=group hostname.file inet.conf motd.install mtab passwd profile \ +FILES1=group hostname.file inet.conf motd.install mtab profile \ protocols rc services termcap ttytab utmp rc.cd binary_sizes \ binary_sizes.big binary_sizes.xxl syslog.conf rc.daemons.dist \ rs.inet rs.single make.conf system.conf ttys resolv.conf rc.conf \ rc.subr rc.subr.minix man.conf -FILES2=shadow +PWFILES=master.passwd FILES3=daily dhcptags.conf rc USRFILES=Makefile +TOOL_PWD_MKDB= pwd_mkdb + clean:: -install:: +install:: installpw # installpw needed to bootstrap pw db @echo "Installing /etc, /usr/etc and /usr/lib.." mkdir -p $(ETC) $(USRLIB) @for f in $(FILES1); do if [ -f $(ETC)/$$f ]; then :; else cp $$f $(ETC)/$$f; chmod 755 $(ETC)/$$f; fi; done - @for f in $(FILES2); do if [ -f $(ETC)/$$f ]; then :; else cp $$f $(ETC)/$$f; chmod 600 $(ETC)/$$f; fi; done @for f in $(USRFILES); do cp usr/$$f $(USR)/$$f; chmod 644 $(USR)/$$f; done @echo "Making hierarchy.." sh mtree.sh mtree/minix.tree @@ -33,7 +34,13 @@ install:: @echo "Installing /usr/lib/descr.." install -m 644 -o root -g operator descr /usr/lib/ -installforce:: $(ETC)/rc $(ETC)/rs.inet $(ETC)/rs.single $(ETC)/system.conf $(USRETC)/rc $(USR)/Makefile + +installforce:: $(ETC)/rc $(ETC)/rs.inet $(ETC)/rs.single $(ETC)/system.conf $(USRETC)/rc $(USR)/Makefile installpw + +installpw:: + if [ ! -d $(ETC) ]; then mkdir $(ETC); chmod 755 $(ETC); fi + @for f in $(PWFILES); do if [ -f $(ETC)/$$f ]; then :; else cp $$f $(ETC)/$$f; chmod 600 $(ETC)/$$f; fi; done + touch /etc/pwd.db; touch /etc/spwd.db; ${TOOL_PWD_MKDB} -p -V 0 /etc/master.passwd $(ETC)/rc: rc install -m 755 -o root -g operator $> $@ diff --git a/etc/group b/etc/group index 6d10fa8d8..4be3bee20 100755 --- a/etc/group +++ b/etc/group @@ -11,9 +11,19 @@ www:*:9: driver:*:10: server:*:11: games:*:13: -sshd:*:23: -smtpd:*:26: -postfix:*:27: -maildrop:*:28: -mail:*:29: +_pflogd:*:18: +_rwhod:*:19: +_proxy:*:21: +_timedc:*:22: +_sdpd:*:23: +_httpd:*:24: +_mdnsd:*:25: +_tests:*:26: +_tcpdump:*:27: +smtpd:*:40: +postfix:*:41: +maildrop:*:42: +mail:*:43: +sshd:*:44: nogroup:*:99: +users:*:100: diff --git a/etc/master.passwd b/etc/master.passwd new file mode 100644 index 000000000..c008a01b7 --- /dev/null +++ b/etc/master.passwd @@ -0,0 +1,17 @@ +root::0:0::0:0:Big Brother:/root:/bin/sh +daemon:*:1:1::0:0:The Deuce:/etc:/bin/sh +bin:*:2:0::0:0:Binaries:/home/bin:/bin/sh +uucp:*:5:5::0:0:UNIX to UNIX copy:/usr/spool/uucp:/usr/bin/uucico +news:*:6:6::0:0:Usenet news:/usr/spool/news:/bin/sh +ftp:*:7:7::0:0:Anonymous FTP:/usr/ftp:/bin/sh +ast:*:8:3::0:0:Andrew S. Tanenbaum:/home/ast:/bin/sh +www:*:9:9::0:0:World Wide Web:/usr/www:/bin/sh +driver:*:10:10::0:0:Device Drivers:/:/bin/sh +server:*:11:11::0:0:OS Servers:/:/bin/sh +service:*:12:12::0:0:System Services:/:/bin/sh +sshd:*:22:22::0:0:sshd:/:/bin/sh +smtpd:*:25:25::0:0:smtpd:/:/bin/sh +postfix:*:27:27::0:0:postfix:/usr/var/spool/postfix:/bin/false +postgres:*:30:30::0:0:postgresql:/:/bin/false +games:*:9998:13::0:0:games:/:/bin/sh +nobody:*:9999:39::0:0:Unprivileged user:/tmp:/bin/sh diff --git a/etc/passwd b/etc/passwd deleted file mode 100755 index bd0d87f1b..000000000 --- a/etc/passwd +++ /dev/null @@ -1,17 +0,0 @@ -root:##root:0:0:Big Brother:/root: -daemon:*:1:1:The Deuce:/etc: -bin:##root:2:0:Binaries:/home/bin: -uucp:*:5:5:UNIX to UNIX copy:/usr/spool/uucp:/usr/bin/uucico -news:*:6:6:Usenet news:/usr/spool/news: -ftp:*:7:7:Anonymous FTP:/usr/ftp: -ast:*:8:3:Andrew S. Tanenbaum:/home/ast: -www:*:9:9:World Wide Web:/usr/www: -driver:*:10:10:Device Drivers:/: -server:*:11:11:OS Servers:/: -service:*:12:12:System Services:/: -sshd:*:22:22:sshd:/: -smtpd:*:25:25:smtpd:/: -postfix:*:27:27:postfix:/usr/var/spool/postfix:/bin/false -postgres:*:30:30:postgresql:/:/bin/false -games:*:9998:98::/: -nobody:*:9999:99::/tmp: diff --git a/etc/shadow b/etc/shadow deleted file mode 100755 index 3895e9ec4..000000000 --- a/etc/shadow +++ /dev/null @@ -1 +0,0 @@ -root::0:0::: diff --git a/lib/Makefile b/lib/Makefile index 1e9879eb4..16b9ed77e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -3,6 +3,7 @@ .if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no") LIBC_DIR= nbsd_libc LIBM_DIR= nbsd_libm +LIBUTIL_DIR= libutil LIBCOMPAT_DIR= nbsd_libcompat_minix LIBMINLIB_DIR= nbsd_libminlib LIBASYN_DIR= nbsd_libasyn @@ -10,12 +11,13 @@ LIBASYN_DIR= nbsd_libasyn LIBC_DIR?= libc LIBM_DIR?= libm +LIBUTIL_DIR?= libminixutil LIBCOMPAT_DIR?= LIBMINLIB_DIR?= LIBASYN_DIR?= SUBDIR= csu ${LIBCOMPAT_DIR} ${LIBC_DIR} libdriver libnetdriver \ - libedit ${LIBM_DIR} libsys libtimers libminixutil libbz2 libl libhgfs \ + libedit ${LIBM_DIR} libsys libtimers ${LIBUTIL_DIR} libbz2 libl libhgfs \ libz libfetch libarchive libvtreefs libaudiodriver libmthread \ libexec libdevman libusb ${LIBMINLIB_DIR} ${LIBASYN_DIR} \ libddekit libminixfs libbdev diff --git a/lib/libc/other/Makefile.inc b/lib/libc/other/Makefile.inc index 5950d97f1..a35894d16 100644 --- a/lib/libc/other/Makefile.inc +++ b/lib/libc/other/Makefile.inc @@ -41,7 +41,6 @@ SRCS+= \ bcopy.c \ bzero.c \ configfile.c \ - crypt.c \ ctermid.c \ cuserid.c \ dirname.c \ @@ -64,7 +63,6 @@ SRCS+= \ getpagesize.c \ getpass.c \ getprogname.c \ - getpwent.c \ getsubopt.c \ getttyent.c \ getw.c \ @@ -85,7 +83,6 @@ SRCS+= \ popen.c \ putenv.c \ putw.c \ - pwcache.c \ random.c \ read_tsc.S \ read_tsc_64.c \ @@ -125,3 +122,9 @@ SRCS+= \ vwarnx.c \ warn.c \ warnx.c + +# XXX To be removed after full +# XXX switch to NetBSD passwd. +SRCS+= \ + getpwent.c \ + pwcache.c diff --git a/lib/libc/other/crypt.c b/lib/libc/other/crypt.c deleted file mode 100644 index a82b3b0c2..000000000 --- a/lib/libc/other/crypt.c +++ /dev/null @@ -1,115 +0,0 @@ -/* crypt() - one-way password encryption function Author: Kees J. Bot - * 7 Feb 1994 - * This routine does not encrypt anything, it uses the pwdauth - * program to do the hard work. - */ -#define nil ((void*)0) -#define pipe _pipe -#define fork _fork -#define close _close -#define dup2 _dup2 -#define execl _execl -#define read _read -#define _exit __exit -#define write _write -#define waitpid _waitpid -#include -#include -#include -#include -#include -#include -#include - -/* Set-uid root program to read /etc/shadow or encrypt passwords. */ -static char PWDAUTH[] = "/usr/lib/pwdauth"; -#define LEN 1024 - -static void tell(const char *s0, ...) -{ - va_list ap; - const char *s; - - va_start(ap, s0); - s= s0; - while (s != nil) { - (void) write(2, s, strlen(s)); - s= va_arg(ap, const char *); - } - va_end(ap); -} - -char *crypt(const char *key, const char *salt) -{ - pid_t pid; - int status; - int pfd[2]; - static char pwdata[LEN]; - char *p= pwdata; - const char *k= key; - const char *s= salt; - int n; - - /* Fill pwdata[] with the key and salt. */ - while ((*p++ = *k++) != 0) if (p == pwdata+LEN-1) goto fail; - while ((*p++ = *s++) != 0) if (p == pwdata+LEN-0) goto fail; - - if (pipe(pfd) < 0) goto fail; - - /* Prefill the pipe. */ - (void) write(pfd[1], pwdata, p - pwdata); - - switch ((pid= fork())) { - case -1: - close(pfd[0]); - close(pfd[1]); - goto fail; - case 0: - /* Connect both input and output to the pipe. */ - if (pfd[0] != 0) { - dup2(pfd[0], 0); - close(pfd[0]); - } - if (pfd[1] != 1) { - dup2(pfd[1], 1); - close(pfd[1]); - } - - execl(PWDAUTH, PWDAUTH, (char *) nil); - - tell("crypt(): ", PWDAUTH, ": ", strerror(errno), "\r\n", - (char *) nil); - /* No pwdauth? Fail! */ - (void) read(0, pwdata, LEN); - _exit(1); - } - close(pfd[1]); - - status= -1; - while (waitpid(pid, &status, 0) == -1 && errno == EINTR) {} - if (status != 0) { - close(pfd[0]); - goto fail; - } - - /* Read and return the result. Check if it contains exactly one - * string. - */ - n= read(pfd[0], pwdata, LEN); - close(pfd[0]); - if (n < 0) goto fail; - p = pwdata + n; - n = 0; - while (p > pwdata) if (*--p == 0) n++; - if (n != 1) goto fail; - return pwdata; - -fail: - pwdata[0] = salt[0] ^ 1; /* make result != salt */ - pwdata[1] = 0; - return pwdata; -} - -/* - * $PchId: crypt.c,v 1.5 1996/04/11 07:46:11 philip Exp $ - */ diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index d26e03857..36accd246 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -17,7 +17,7 @@ SRCS= efun.c getbootfile.c \ passwd.c pw_scan.c pidfile.c pidlock.c pty.c \ raise_default_signal.c \ secure_path.c snprintb.c \ - ttyaction.c + ttyaction.c login_cap.c #disklabel_dkcksum.c disklabel_scan.c \ #if_media.c \ #sockaddr_snprintf.c @@ -25,7 +25,6 @@ SRCS= efun.c getbootfile.c \ #getmaxpartitions.c #stat_flags.c #getrawpartition.c - #login_cap.c #ttymsg.c #parsedate.y diff --git a/lib/libutil/compat/Makefile.inc b/lib/libutil/compat/Makefile.inc index 9e0f550a1..982ebe3a4 100644 --- a/lib/libutil/compat/Makefile.inc +++ b/lib/libutil/compat/Makefile.inc @@ -3,5 +3,6 @@ .PATH: ${.CURDIR}/compat CPPFLAGS+=-I${.CURDIR}/../libc -I${.CURDIR}/../../sys -SRCS+=compat_passwd.c compat_loginx.c compat_login.c compat_parsedate.c \ +SRCS+=compat_passwd.c compat_loginx.c compat_parsedate.c \ compat_login_cap.c + # compat_login.c diff --git a/lib/libutil/login_cap.c b/lib/libutil/login_cap.c index 729235f65..8e8745a49 100644 --- a/lib/libutil/login_cap.c +++ b/lib/libutil/login_cap.c @@ -417,12 +417,16 @@ static struct { { RLIMIT_FSIZE, R_CSIZE, "filesize", }, { RLIMIT_DATA, R_CSIZE, "datasize", }, { RLIMIT_STACK, R_CSIZE, "stacksize", }, +#ifndef __minix { RLIMIT_RSS, R_CSIZE, "memoryuse", }, { RLIMIT_MEMLOCK, R_CSIZE, "memorylocked", }, { RLIMIT_NPROC, R_CNUMB, "maxproc", }, +#endif { RLIMIT_NOFILE, R_CNUMB, "openfiles", }, { RLIMIT_CORE, R_CSIZE, "coredumpsize", }, +#ifdef RLIMIT_SBSIZE { RLIMIT_SBSIZE, R_CSIZE, "sbsize", }, +#endif { -1, 0, 0 } }; @@ -472,11 +476,13 @@ gsetrl(login_cap_t *lc, int what, const char *name, int type) return (-1); } +#ifndef __minix if (setrlimit(what, &rl)) { syslog(LOG_ERR, "%s: setting resource limit %s: %m", lc->lc_class, name); return (-1); } +#endif #undef RCUR #undef RMAX return (0); @@ -570,6 +576,7 @@ setusercontext(login_cap_t *lc, struct passwd *pwd, uid_t uid, u_int flags) if (!lc) flc = lc = login_getclass(pwd ? pwd->pw_class : NULL); +#define LOGIN_SETLOGIN 0 /* * Without the pwd entry being passed we cannot set either * the group or the login. We could complain about it. diff --git a/lib/nbsd_libc/gen/pwcache.c b/lib/nbsd_libc/gen/pwcache.c index 1d3562368..2caf96a51 100644 --- a/lib/nbsd_libc/gen/pwcache.c +++ b/lib/nbsd_libc/gen/pwcache.c @@ -59,6 +59,16 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +/* + * XXX Undefine the renames of these functions so that we don't + * XXX rename the versions found in the host's by mistake! + */ +#undef group_from_gid +#undef user_from_uid +#endif + #include #if defined(LIBC_SCCS) && !defined(lint) #if 0 @@ -81,6 +91,18 @@ __RCSID("$NetBSD: pwcache.c,v 1.31 2010/03/23 20:28:59 drochner Exp $"); #include #include +#if HAVE_NBTOOL_CONFIG_H +/* XXX Now, re-apply the renaming that we undid above. */ +#define group_from_gid __nbcompat_group_from_gid +#define user_from_uid __nbcompat_user_from_uid +#endif + +#ifdef __weak_alias +__weak_alias(user_from_uid,_user_from_uid) +__weak_alias(group_from_gid,_group_from_gid) +__weak_alias(pwcache_groupdb,_pwcache_groupdb) +#endif + #if !HAVE_PWCACHE_USERDB || HAVE_NBTOOL_CONFIG_H #include "pwcache.h" @@ -226,6 +248,273 @@ grptb_start(void) return (0); } +/* + * user_from_uid() + * caches the name (if any) for the uid. If noname clear, we always + * return the stored name (if valid or invalid match). + * We use a simple hash table. + * Return + * Pointer to stored name (or a empty string) + */ +const char * +user_from_uid(uid_t uid, int noname) +{ + struct passwd *pw; + UIDC *ptr, **pptr; + + if ((uidtb == NULL) && (uidtb_start() < 0)) + return (NULL); + + /* + * see if we have this uid cached + */ + pptr = uidtb + (uid % UID_SZ); + ptr = *pptr; + + if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) { + /* + * have an entry for this uid + */ + if (!noname || (ptr->valid == VALID)) + return (ptr->name); + return (NULL); + } + + /* + * No entry for this uid, we will add it + */ + if (!pwopn) { + if (_pwcache_setpassent != NULL) + (*_pwcache_setpassent)(1); + ++pwopn; + } + + if (ptr == NULL) + *pptr = ptr = (UIDC *)malloc(sizeof(UIDC)); + + if ((pw = (*_pwcache_getpwuid)(uid)) == NULL) { + /* + * no match for this uid in the local password file + * a string that is the uid in numeric format + */ + if (ptr == NULL) + return (NULL); + ptr->uid = uid; + (void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid); + ptr->valid = INVALID; + if (noname) + return (NULL); + } else { + /* + * there is an entry for this uid in the password file + */ + if (ptr == NULL) + return (pw->pw_name); + ptr->uid = uid; + (void)strlcpy(ptr->name, pw->pw_name, UNMLEN); + ptr->valid = VALID; + } + return (ptr->name); +} + +/* + * group_from_gid() + * caches the name (if any) for the gid. If noname clear, we always + * return the stored name (if valid or invalid match). + * We use a simple hash table. + * Return + * Pointer to stored name (or a empty string) + */ +const char * +group_from_gid(gid_t gid, int noname) +{ + struct group *gr; + GIDC *ptr, **pptr; + + if ((gidtb == NULL) && (gidtb_start() < 0)) + return (NULL); + + /* + * see if we have this gid cached + */ + pptr = gidtb + (gid % GID_SZ); + ptr = *pptr; + + if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) { + /* + * have an entry for this gid + */ + if (!noname || (ptr->valid == VALID)) + return (ptr->name); + return (NULL); + } + + /* + * No entry for this gid, we will add it + */ + if (!gropn) { + if (_pwcache_setgroupent != NULL) + (*_pwcache_setgroupent)(1); + ++gropn; + } + + if (ptr == NULL) + *pptr = ptr = (GIDC *)malloc(sizeof(GIDC)); + + if ((gr = (*_pwcache_getgrgid)(gid)) == NULL) { + /* + * no match for this gid in the local group file, put in + * a string that is the gid in numberic format + */ + if (ptr == NULL) + return (NULL); + ptr->gid = gid; + (void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid); + ptr->valid = INVALID; + if (noname) + return (NULL); + } else { + /* + * there is an entry for this group in the group file + */ + if (ptr == NULL) + return (gr->gr_name); + ptr->gid = gid; + (void)strlcpy(ptr->name, gr->gr_name, GNMLEN); + ptr->valid = VALID; + } + return (ptr->name); +} + +/* + * uid_from_user() + * caches the uid for a given user name. We use a simple hash table. + * Return + * the uid (if any) for a user name, or a -1 if no match can be found + */ +int +uid_from_user(const char *name, uid_t *uid) +{ + struct passwd *pw; + UIDC *ptr, **pptr; + size_t namelen; + + /* + * return -1 for mangled names + */ + if (name == NULL || ((namelen = strlen(name)) == 0)) + return (-1); + if ((usrtb == NULL) && (usrtb_start() < 0)) + return (-1); + + /* + * look up in hash table, if found and valid return the uid, + * if found and invalid, return a -1 + */ + pptr = usrtb + st_hash(name, namelen, UNM_SZ); + ptr = *pptr; + + if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { + if (ptr->valid == INVALID) + return (-1); + *uid = ptr->uid; + return (0); + } + + if (!pwopn) { + if (_pwcache_setpassent != NULL) + (*_pwcache_setpassent)(1); + ++pwopn; + } + + if (ptr == NULL) + *pptr = ptr = (UIDC *)malloc(sizeof(UIDC)); + + /* + * no match, look it up, if no match store it as an invalid entry, + * or store the matching uid + */ + if (ptr == NULL) { + if ((pw = (*_pwcache_getpwnam)(name)) == NULL) + return (-1); + *uid = pw->pw_uid; + return (0); + } + (void)strlcpy(ptr->name, name, UNMLEN); + if ((pw = (*_pwcache_getpwnam)(name)) == NULL) { + ptr->valid = INVALID; + return (-1); + } + ptr->valid = VALID; + *uid = ptr->uid = pw->pw_uid; + return (0); +} + +/* + * gid_from_group() + * caches the gid for a given group name. We use a simple hash table. + * Return + * the gid (if any) for a group name, or a -1 if no match can be found + */ +int +gid_from_group(const char *name, gid_t *gid) +{ + struct group *gr; + GIDC *ptr, **pptr; + size_t namelen; + + /* + * return -1 for mangled names + */ + if (name == NULL || ((namelen = strlen(name)) == 0)) + return (-1); + if ((grptb == NULL) && (grptb_start() < 0)) + return (-1); + + /* + * look up in hash table, if found and valid return the uid, + * if found and invalid, return a -1 + */ + pptr = grptb + st_hash(name, namelen, GID_SZ); + ptr = *pptr; + + if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { + if (ptr->valid == INVALID) + return (-1); + *gid = ptr->gid; + return (0); + } + + if (!gropn) { + if (_pwcache_setgroupent != NULL) + (*_pwcache_setgroupent)(1); + ++gropn; + } + + if (ptr == NULL) + *pptr = ptr = (GIDC *)malloc(sizeof(GIDC)); + + /* + * no match, look it up, if no match store it as an invalid entry, + * or store the matching gid + */ + if (ptr == NULL) { + if ((gr = (*_pwcache_getgrnam)(name)) == NULL) + return (-1); + *gid = gr->gr_gid; + return (0); + } + + (void)strlcpy(ptr->name, name, GNMLEN); + if ((gr = (*_pwcache_getgrnam)(name)) == NULL) { + ptr->valid = INVALID; + return (-1); + } + ptr->valid = VALID; + *gid = ptr->gid = gr->gr_gid; + return (0); +} + #define FLUSHTB(arr, len, fail) \ do { \ if (arr != NULL) { \ diff --git a/lib/nbsd_libcompat_minix/Makefile b/lib/nbsd_libcompat_minix/Makefile index 619fdcb5a..05db97c6a 100644 --- a/lib/nbsd_libcompat_minix/Makefile +++ b/lib/nbsd_libcompat_minix/Makefile @@ -24,11 +24,6 @@ SRCS+= nlist.c # NetBSD's 'mtab' format. SRCS+= mtab.c -# Minix legacy passwd format -# These should be removed when we switch to -# NetBSD's 'passwd' db-based format. -SRCS+= getpwent.c - # fttyslot(fd), a Minix-specific extension SRCS+= fttyslot.c @@ -36,11 +31,6 @@ SRCS+= fttyslot.c # Now considered "compat" feature in NetBSD. SRCS+= cuserid.c -# XXX: hack -# user_from_uid(), uid_from_user() -# group_from_gid(), gid_from_group() -SRCS+= passwd.c - .include "include/Makefile.inc" .include diff --git a/lib/nbsd_libcompat_minix/getpwent.c b/lib/nbsd_libcompat_minix/getpwent.c deleted file mode 100644 index 22598b4e5..000000000 --- a/lib/nbsd_libcompat_minix/getpwent.c +++ /dev/null @@ -1,156 +0,0 @@ -/* getpwent(), getpwuid(), getpwnam() - password file routines - * - * Author: Kees J. Bot - * 31 Jan 1994 - */ -#include -#include -#include -#include -#include -#include - -#define arraysize(a) (sizeof(a) / sizeof((a)[0])) -#define arraylimit(a) ((a) + arraysize(a)) - -static char PASSWD[]= "/etc/passwd"; /* The password file. */ -static const char *pwfile; /* Current password file. */ - -static char buf[1024]; /* Read buffer. */ -static char pwline[256]; /* One line from the password file. */ -static struct passwd entry; /* Entry to fill and return. */ -static int pwfd= -1; /* Filedescriptor to the file. */ -static char *bufptr; /* Place in buf. */ -static ssize_t buflen= 0; /* Remaining characters in buf. */ -static char *lineptr; /* Place in the line. */ - -void endpwent(void) -/* Close the password file. */ -{ - if (pwfd >= 0) { - (void) close(pwfd); - pwfd= -1; - buflen= 0; - } -} - -int setpwent(void) -/* Open the password file. */ -{ - if (pwfd >= 0) endpwent(); - - if (pwfile == NULL) pwfile= PASSWD; - - if ((pwfd= open(pwfile, O_RDONLY)) < 0) return -1; - (void) fcntl(pwfd, F_SETFD, fcntl(pwfd, F_GETFD) | FD_CLOEXEC); - return 0; -} - -void setpwfile(const char *file) -/* Prepare for reading an alternate password file. */ -{ - endpwent(); - pwfile= file; -} - -static int getline(void) -/* Get one line from the password file, return 0 if bad or EOF. */ -{ - lineptr= pwline; - - do { - if (buflen == 0) { - if ((buflen= read(pwfd, buf, sizeof(buf))) <= 0) - return 0; - bufptr= buf; - } - - if (lineptr == arraylimit(pwline)) return 0; - buflen--; - } while ((*lineptr++ = *bufptr++) != '\n'); - - lineptr= pwline; - return 1; -} - -static char *scan_colon(void) -/* Scan for a field separator in a line, return the start of the field. */ -{ - char *field= lineptr; - char *last; - - for (;;) { - last= lineptr; - if (*lineptr == 0) return NULL; - if (*lineptr == '\n') break; - if (*lineptr++ == ':') break; - } - *last= 0; - return field; -} - -struct passwd *getpwent(void) -/* Read one entry from the password file. */ -{ - char *p; - - /* Open the file if not yet open. */ - if (pwfd < 0 && setpwent() < 0) return NULL; - - /* Until a good line is read. */ - for (;;) { - if (!getline()) return NULL; /* EOF or corrupt. */ - - if ((entry.pw_name= scan_colon()) == NULL) continue; - if ((entry.pw_passwd= scan_colon()) == NULL) continue; - if ((p= scan_colon()) == NULL) continue; - entry.pw_uid= strtol(p, NULL, 0); - if ((p= scan_colon()) == NULL) continue; - entry.pw_gid= strtol(p, NULL, 0); - if ((entry.pw_gecos= scan_colon()) == NULL) continue; - if ((entry.pw_dir= scan_colon()) == NULL) continue; - if ((entry.pw_shell= scan_colon()) == NULL) continue; - - if (*lineptr == 0) return &entry; - } -} - -struct passwd *getpwuid(uid_t uid) -/* Return the password file entry belonging to the user-id. */ -{ - struct passwd *pw; - - endpwent(); - while ((pw= getpwent()) != NULL && pw->pw_uid != uid) {} - endpwent(); - return pw; -} - -struct passwd *getpwnam(const char *name) -/* Return the password file entry belonging to the user name. */ -{ - struct passwd *pw; - - endpwent(); - while ((pw= getpwent()) != NULL && strcmp(pw->pw_name, name) != 0) {} - endpwent(); - return pw; -} - -#ifndef L_cuserid -#define L_cuserid 9 -#endif - -char *getlogin() -{ - static char userid[L_cuserid]; - struct passwd *pw_entry; - - pw_entry = getpwuid(getuid()); - - if (pw_entry == (struct passwd *)NULL) return((char *)NULL); - - strcpy(userid, pw_entry->pw_name); - - return(userid); -} diff --git a/lib/nbsd_libcompat_minix/passwd.c b/lib/nbsd_libcompat_minix/passwd.c deleted file mode 100644 index 86890ed12..000000000 --- a/lib/nbsd_libcompat_minix/passwd.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * grotesque hack to get these functions working. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * group_from_gid() - * caches the name (if any) for the gid. If noname clear, we always - * return the stored name (if valid or invalid match). - * We use a simple hash table. - * Return - * Pointer to stored name (or a empty string) - */ -const char * -group_from_gid(gid_t gid, int noname) -{ - static char buf[16]; - struct group *g = getgrgid(gid); - if (g == NULL) { - if (noname) { - return NULL; - } else { - sprintf(buf, "%d", gid); - return buf; - } - } - return g->gr_name; -} - -/* - * user_from_uid() - * caches the name (if any) for the uid. If noname clear, we always - * return the stored name (if valid or invalid match). - * We use a simple hash table. - * Return - * Pointer to stored name (or a empty string) - */ -const char * -user_from_uid(uid_t uid, int noname) -{ - static char buf[16]; - struct passwd *p = getpwuid(uid); - if (p == NULL) { - if (noname) { - return NULL; - } else { - sprintf(buf, "%d", uid); - return buf; - } - } - return p->pw_name; -} - -/* - * uid_from_user() - * caches the uid for a given user name. We use a simple hash table. - * Return - * the uid (if any) for a user name, or a -1 if no match can be found - */ -int -uid_from_user(const char *name, uid_t *uid) -{ - struct passwd *p = getpwnam(name); - if (p == NULL) { - return -1; - } - *uid = p->pw_uid; - return *uid; -} - -/* - * gid_from_group() - * caches the gid for a given group name. We use a simple hash table. - * Return - * the gid (if any) for a group name, or a -1 if no match can be found - */ -int -gid_from_group(const char *name, gid_t *gid) -{ - struct group *g = getgrnam(name); - if (g == NULL) { - return -1; - } - *gid = g->gr_gid; - return *gid; -} diff --git a/man/man1/Makefile b/man/man1/Makefile index 10e8d89ae..f1276d25c 100644 --- a/man/man1/Makefile +++ b/man/man1/Makefile @@ -13,7 +13,6 @@ MAN= acd.1 anm.1 ar.1 ash.1 asize.1 at.1 banner.1 basename.1 \ look.1 lp.1 ls.1 lspci.1 M.1 mail.1 \ mesg.1 mixer.1 ackmkdep.1 mkfs.1 \ mkproto.1 modem.1 mount.1 mt.1 nice.1 nm.1 nohup.1 od.1 \ - passwd.1 \ paste.1 ping.1 playwave.1 postmort.1 pr.1 prep.1 \ profile.1 ps.1 pwd.1 rcp.1 readall.1 recwave.1 \ ref.1 remsync.1 rget.1 rlogin.1 rmdir.1 rsh.1 rz.1 \ @@ -61,7 +60,6 @@ MLINKS += cp.1 cpdir.1 MLINKS += elvis.1 ex.1 MLINKS += expr.1 test.1 MLINKS += expr.1 [.1 -MLINKS += passwd.1 chfn.1 MLINKS += svc.1 ci.1 MLINKS += svc.1 co.1 diff --git a/man/man1/passwd.1 b/man/man1/passwd.1 deleted file mode 100644 index 17fb8ce67..000000000 --- a/man/man1/passwd.1 +++ /dev/null @@ -1,44 +0,0 @@ -.TH PASSWD 1 -.SH NAME -passwd, chfn, chsh \- change a login password, full name or shell -.SH SYNOPSIS -\fBpasswd\fR [\fIuser\fR]\fR -.br -\fBchfn\fR [\fIuser\fR] \fIfullname\fR\fR -.br -\fBchsh\fR [\fIuser\fR] \fIshell\fR\fR -.br -.de FL -.TP -\\fB\\$1\\fR -\\$2 -.. -.de EX -.TP 20 -\\fB\\$1\\fR -# \\$2 -.. -.SH EXAMPLES -.EX "passwd" "Change current user's password" -.EX "passwd ast" "Change ast's password (super\-user only)" -.EX "chsh /usr/bin/mail" "For those who only read mail" -.EX "chfn 'Jane Doe'" "Current user is Jane Doe" -.SH DESCRIPTION -.PP -.I Passwd -is used to change your password. -It prompts for the old and new passwords. -It asks for the new password twice, to reduce the effect of a typing error. -.I Chfn -changes the full name (GECOS field) in the password file. -.I Chsh -changes your login shell. -Do not forget to copy the modified password file back to the root file system, -or the changes will be lost when the system is rebooted. -.SH "SEE ALSO" -.BR login (1), -.BR su (1), -.BR crypt (3), -.BR getpwent (3), -.BR passwd (5), -.BR adduser (8). diff --git a/man/man8/Makefile b/man/man8/Makefile index 272803074..8dde810a4 100644 --- a/man/man8/Makefile +++ b/man/man8/Makefile @@ -1,4 +1,4 @@ -MAN= add_route.8 adduser.8 backup.8 badblocks.8 boot.8 \ +MAN= add_route.8 backup.8 badblocks.8 boot.8 \ cdprobe.8 checkhier.8 chown.8 cleantmp.8 config.8 cron.8 \ dhcpd.8 diskctl.8 dosminix.8 elvprsv.8 fdisk.8 fingerd.8 ftpd.8 \ getty.8 halt.8 hgfs.8 httpd.8 ifconfig.8 inet.8 init.8 \ diff --git a/man/man8/adduser.8 b/man/man8/adduser.8 deleted file mode 100644 index 2e2c79212..000000000 --- a/man/man8/adduser.8 +++ /dev/null @@ -1,43 +0,0 @@ -.TH ADDUSER 8 -.SH NAME -adduser \- add a new user to the system -.SH SYNOPSIS -\fBadduser \fIuser group home-dir\fR\fR -.br -.de FL -.TP -\\fB\\$1\\fR -\\$2 -.. -.de EX -.TP 20 -\\fB\\$1\\fR -# \\$2 -.. -.SH EXAMPLES -.EX "adduser ast other /usr/ast" "How user ast could be added" -.EX "adduser bin operator /usr/src" "How user bin could be added" -.SH DESCRIPTION -.PP -.I Adduser -adds a new user to the system by making new entries in -.B /etc/passwd -and -.B /etc/shadow -for the new user, creating a new home directory, and copying the contents -of the template home directory -.B /usr/ast -into it. The user-id of this new user will be the first free number not less -than 10. The password is initially empty, the full name must be set, and -the shell is the Bourne Shell, -.B /bin/sh . -Use -.I passwd , -.I chfn , -and -.I chsh -to change. -.SH "SEE ALSO" -.BR login (1), -.BR passwd (1), -.BR passwd (5). diff --git a/man/man8/pwdauth.8 b/man/man8/pwdauth.8 index 2567be52a..c1e55dad0 100644 --- a/man/man8/pwdauth.8 +++ b/man/man8/pwdauth.8 @@ -1,4 +1,3 @@ -.TH PWDAUTH .SH NAME pwdauth \- password authentication program .SH SYNOPSIS diff --git a/nbsd_include/arch/i386/include/param.h b/nbsd_include/arch/i386/include/param.h index f37db3dca..4a42fa9a9 100644 --- a/nbsd_include/arch/i386/include/param.h +++ b/nbsd_include/arch/i386/include/param.h @@ -62,6 +62,10 @@ #define PGOFSET (NBPG-1) /* byte offset into page */ #define NPTEPG (NBPG/(sizeof (pt_entry_t))) +#ifndef MAXPHYS +#define MAXPHYS (64 * 1024) /* max raw I/O transfer size */ +#endif + /* * Mach derived conversion macros */ diff --git a/nbsd_include/login_cap.h b/nbsd_include/login_cap.h index d1dc7d054..007e98e93 100644 --- a/nbsd_include/login_cap.h +++ b/nbsd_include/login_cap.h @@ -43,7 +43,9 @@ #define _PATH_LOGIN_CONF "/etc/login.conf" #define LOGIN_OSETGROUP 0x0001 /* Obsolete setgroup */ +#ifndef __minix #define LOGIN_SETLOGIN 0x0002 /* Set login */ +#endif #define LOGIN_SETPATH 0x0004 /* Set path */ #define LOGIN_SETPRIORITY 0x0008 /* Set priority */ #define LOGIN_SETRESOURCES 0x0010 /* Set resource limits */ diff --git a/nbsd_include/pwd.h b/nbsd_include/pwd.h index eec6b9cf0..4a471f027 100644 --- a/nbsd_include/pwd.h +++ b/nbsd_include/pwd.h @@ -61,14 +61,6 @@ * SUCH DAMAGE. */ -#if defined(__minix) && defined(_MINIX_COMPAT) -#include -/* Avoid inclusion of the rest of the header. */ -#ifndef _PWD_H_ -#define _PWD_H_ -#endif -#endif /* __minix && _MINIX_COMPAT */ - #ifndef _PWD_H_ #define _PWD_H_ diff --git a/nbsd_include/sys/dirent.h b/nbsd_include/sys/dirent.h index 4fd459a3e..3bdc05006 100644 --- a/nbsd_include/sys/dirent.h +++ b/nbsd_include/sys/dirent.h @@ -18,6 +18,7 @@ struct dirent { /* Largest entry (8 slots) */ }; #if defined(_NETBSD_SOURCE) +#define MAXNAMLEN 511 #define d_fileno d_ino #endif diff --git a/tools/nbsd_ports b/tools/nbsd_ports index 0f4681af1..701faa270 100644 --- a/tools/nbsd_ports +++ b/tools/nbsd_ports @@ -8,15 +8,20 @@ lib/libutil src/lib/libutil common/lib/libutil src/common/lib/libutil nbsd_include src/include bin/mkdir src/bin/mkdir +usr.bin/chpass src/usr.bin/chpass usr.bin/m4 src/usr.bin/m4 usr.bin/indent src/usr.bin/indent usr.bin/sed src/usr.bin/sed usr.bin/stat src/usr.bin/stat usr.bin/tic src/usr.bin/tic usr.bin/mkdep src/usr.bin/mkdep +usr.bin/newgrp src/usr.bin/newgrp usr.bin/uniq src/usr.bin/uniq usr.bin/seq src/usr.bin/seq usr.bin/man src/usr.bin/man usr.bin/apropos src/usr.bin/apropos usr.bin/mdocml src/external/bsd/mdocml +usr.sbin/pwd_mkdb src/usr.sbin/pwd_mkdb +usr.sbin/user src/usr.sbin/user +usr.sbin/vipw src/usr.sbin/vipw libexec/makewhatis src/libexec/makewhatis diff --git a/tools/release.sh b/tools/release.sh index 3fc599619..5b7d1e40d 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -179,7 +179,7 @@ mkdir -p $RELEASEDIR/bin mkdir -p $RELEASEPACKAGE echo " * Transfering bootstrap dirs to $RELEASEDIR" -cp -p /bin/* /usr/bin/* /sbin/* $RELEASEDIR/$XBIN +cp -p /bin/* /usr/bin/* /usr/sbin/* /sbin/* $RELEASEDIR/$XBIN cp -rp /usr/lib $RELEASEDIR/usr cp -rp /bin/sh /bin/echo /bin/install /bin/rm \ /bin/date /bin/ls $RELEASEDIR/bin diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 11bf1ce00..036d6c34b 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -4,7 +4,7 @@ # NetBSD imports SUBDIR= indent m4 stat tic sed mkdep uniq seq man mdocml \ - apropos + apropos chpass newgrp passwd # Non-NetBSD imports SUBDIR+= ministat diff --git a/usr.bin/Makefile.inc b/usr.bin/Makefile.inc index 708a7b326..cfde616e6 100644 --- a/usr.bin/Makefile.inc +++ b/usr.bin/Makefile.inc @@ -1,6 +1,4 @@ .include -CPPFLAGS+= -D_NETBSD_SOURCE -D__NBSD_LIBC=1 - BINDIR?=/usr/bin diff --git a/usr.bin/chpass/Makefile b/usr.bin/chpass/Makefile new file mode 100644 index 000000000..ccd0096ca --- /dev/null +++ b/usr.bin/chpass/Makefile @@ -0,0 +1,42 @@ +# $NetBSD: Makefile,v 1.15 2007/05/28 12:06:25 tls Exp $ +# @(#)Makefile 8.2 (Berkeley) 4/2/94 + +.include + +USE_FORT?= yes # setuid +PROG= chpass +SRCS= chpass.c edit.c field.c table.c util.c +BINOWN= root +BINMODE=4555 +.ifdef __MINIX +.PATH: ${NETBSDSRCDIR}/lib/nbsd_libc/gen +.else +.PATH: ${NETBSDSRCDIR}/lib/libc/gen +.endif +LINKS= ${BINDIR}/chpass ${BINDIR}/chfn ${BINDIR}/chpass ${BINDIR}/chsh +MLINKS= chpass.1 chfn.1 chpass.1 chsh.1 + +.ifdef __MINIX +CPPFLAGS+=-I${NETBSDSRCDIR}/lib/nbsd_libc/include +.else +CPPFLAGS+=-I${NETBSDSRCDIR}/lib/libc/include +.endif + +.if defined(__MINIX) +USE_YP= no +.endif + +.if (${USE_YP} != "no") +SRCS+= pw_yp.c +CPPFLAGS+=-DYP +DPADD+= ${LIBRPCSVC} +LDADD+= -lrpcsvc +.else +SRCS+= getpwent.c +CPPFLAGS.getpwent.c=-UYP +.endif + +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +.include diff --git a/usr.bin/chpass/chpass.1 b/usr.bin/chpass/chpass.1 new file mode 100644 index 000000000..911b6106a --- /dev/null +++ b/usr.bin/chpass/chpass.1 @@ -0,0 +1,268 @@ +.\" $NetBSD: chpass.1,v 1.23 2006/10/07 20:09:09 elad Exp $ +.\" +.\" Copyright (c) 1988, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)chpass.1 8.2 (Berkeley) 12/30/93 +.\" +.Dd October 7, 2006 +.Dt CHPASS 1 +.Os +.Sh NAME +.Nm chpass , +.Nm chfn , +.Nm chsh +.Nd add or change user database information +.Sh SYNOPSIS +.Nm +.Op Fl a Ar list +.Op Fl s Ar newshell +.Op Fl l +.Op user +.Nm chpass +.Op Fl a Ar list +.Op Fl s Ar newshell +.Op Fl y +.Op user +.Sh DESCRIPTION +.Nm +allows editing of the user database information associated +with +.Ar user +or, by default, the current user. +The information is formatted and supplied to an editor for changes. +.Pp +Only the information that the user is allowed to change is displayed. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl a +The super-user is allowed to directly supply a user database +entry, in the format specified by +.Xr passwd 5 , +as an argument. +This argument must be a colon +.Pq Dq \&: +separated list of all the +user database fields, although they may be empty. +.It Fl s +The +.Fl s +option attempts to change the user's shell to +.Ar newshell . +.It Fl l +This option causes the password to be updated only in the local +password file. +When changing only the local password, +.Xr pwd_mkdb 8 +is used to update the password databases. +.It Fl y +This forces the YP password database entry to be changed, even if +the user has an entry in the local database. +The +.Xr rpc.yppasswdd 8 +daemon should be running on the YP master server. +.El +.Pp +Possible display items are as follows: +.Pp +.Bl -tag -width "Home Directory:" -compact -offset indent +.It Login : +user's login name +.It Password : +user's encrypted password +.It Uid : +user's login +.It Gid : +user's login group +.It Change : +password change time +.It Expire : +account expiration time +.It Class : +user's general classification +.It Home Directory : +user's home directory +.It Shell : +user's login shell +.It Full Name : +user's real name +.It Location : +user's normal location +.It Home Phone : +user's home phone +.It Office Phone : +user's office phone +.El +.Pp +The +.Ar login +field is the user name used to access the computer account. +.Pp +The +.Ar password +field contains the encrypted form of the user's password. +.Pp +The +.Ar uid +field is the number associated with the +.Ar login +field. +Both of these fields should be unique across the system (and often +across a group of systems) as they control file access. +.Pp +While it is possible to have multiple entries with identical login names +and/or identical user id's, it is usually a mistake to do so. +Routines +that manipulate these files will often return only one of the multiple +entries, and that one by random selection. +.Pp +The +.Ar group +field is the group that the user will be placed in at login. +Since +.Bx +supports multiple groups (see +.Xr groups 1 ) +this field currently has little special meaning. +This field may be filled in with either a number or a group name (see +.Xr group 5 ) . +.Pp +The +.Ar change +field is the date by which the password must be changed. +.Pp +The +.Ar expire +field is the date on which the account expires. +.Pp +Both the +.Ar change +and +.Ar expire +fields should be entered in the form +.Dq month day year +where +.Ar month +is the month name (the first three characters are sufficient), +.Ar day +is the day of the month, and +.Ar year +is the year. +.Pp +The +.Ar class +field is a key for a user's login class. +Login classes are defined in +.Xr login.conf 5 , +which is a +.Xr termcap 5 +style database of user attributes, accounting, resource and +environment settings. +.Pp +The user's +.Ar home directory +is the full +.Ux +path name where the user will be placed at login. +.Pp +The +.Ar shell +field is the command interpreter the user prefers. +If the +.Ar shell +field is empty, the Bourne shell, +.Pa /bin/sh , +is assumed. +When altering a login shell, and not the super-user, the user +may not change from a non-standard shell or to a non-standard +shell. +Non-standard is defined as a shell not found in +.Pa /etc/shells . +.Pp +The last four fields are for storing the user's +.Ar full name , office location , +and +.Ar home +and +.Ar work telephone +numbers. +.Pp +Once the information has been verified, +.Nm +uses +.Xr pwd_mkdb 8 +to update the user database. +.Sh ENVIRONMENT +The +.Xr vi 1 +editor will be used unless the environment variable +.Ev EDITOR +is set to an alternative editor. +When the editor terminates, the information is re-read and used to +update the user database itself. +Only the user, or the super-user, may edit the information associated +with the user. +.Sh FILES +.Bl -tag -width /etc/master.passwd -compact +.It Pa /etc/master.passwd +The user database +.It Pa /etc/passwd +A Version 7 format password file +.It Pa /etc/ptmp +Lock file for the passwd database +.It Pa /tmp/pw.XXXXXX +Temporary copy of the user passwd information +.It Pa /etc/shells +The list of approved shells +.El +.Sh SEE ALSO +.Xr finger 1 , +.Xr login 1 , +.Xr passwd 1 , +.Xr pwhash 1 , +.Xr getusershell 3 , +.Xr passwd 5 , +.Xr passwd.conf 5 , +.Xr pwd_mkdb 8 , +.Xr vipw 8 +.Rs +.%A Robert Morris +.%A Ken Thompson +.%T "UNIX Password Security" +.Re +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 Reno . +.Sh BUGS +This program's interface is poorly suited to cryptographic systems such as +Kerberos, and consequently Kerberos password changing is not a feature of +this program. +.Pp +User information should (and eventually will) be stored elsewhere. diff --git a/usr.bin/chpass/chpass.c b/usr.bin/chpass/chpass.c new file mode 100644 index 000000000..b50f99d8b --- /dev/null +++ b/usr.bin/chpass/chpass.c @@ -0,0 +1,316 @@ +/* $NetBSD: chpass.c,v 1.33 2008/07/21 14:19:21 lukem Exp $ */ + +/*- + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)chpass.c 8.4 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: chpass.c,v 1.33 2008/07/21 14:19:21 lukem Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chpass.h" +#include "pathnames.h" + +static char tempname[] = "/tmp/pw.XXXXXX"; +uid_t uid; +int use_yp; + +void (*Pw_error)(const char *, int, int); + +#ifdef YP +extern int _yp_check(char **); /* buried deep inside libc */ +#endif + +void baduser(void); +void cleanup(void); +void usage(void); + +int +main(int argc, char **argv) +{ + enum { NEWSH, LOADENTRY, EDITENTRY } op; + struct passwd *pw, lpw, old_pw; + int ch, dfd, pfd, tfd; +#ifdef YP + int yflag = 0; +#endif + char *arg, *username = NULL; + +#ifdef __GNUC__ + pw = NULL; /* XXX gcc -Wuninitialized */ + arg = NULL; +#endif +#ifdef YP + use_yp = _yp_check(NULL); +#endif + + op = EDITENTRY; + while ((ch = getopt(argc, argv, "a:s:ly")) != -1) + switch (ch) { + case 'a': + op = LOADENTRY; + arg = optarg; + break; + case 's': + op = NEWSH; + arg = optarg; + break; + case 'l': + use_yp = 0; + break; + case 'y': +#ifdef YP + if (!use_yp) + errx(1, "YP not in use."); + yflag = 1; +#else + errx(1, "YP support not compiled in."); +#endif + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + uid = getuid(); + switch (argc) { + case 0: + /* nothing */ + break; + + case 1: + username = argv[0]; + break; + + default: + usage(); + } + +#ifdef YP + /* + * We need to determine if we _really_ want to use YP. + * If we defaulted to YP (i.e. were not given the -y flag), + * and the master is not running rpc.yppasswdd, we check + * to see if the user exists in the local passwd database. + * If so, we use it, otherwise we error out. + */ + if (use_yp && yflag == 0) { + if (check_yppasswdd()) { + /* + * We weren't able to contact rpc.yppasswdd. + * Check to see if we're in the local + * password database. If we are, use it. + */ + if (username != NULL) + pw = getpwnam(username); + else + pw = getpwuid(uid); + if (pw != NULL) + use_yp = 0; + else { + warnx("master YP server not running yppasswd" + " daemon."); + errx(1, "Can't change password."); + } + } + } +#endif + +#ifdef YP + if (use_yp) + Pw_error = yppw_error; + else +#endif + Pw_error = pw_error; + +#ifdef YP + if (op == LOADENTRY && use_yp) + errx(1, "cannot load entry using YP.\n" + "\tUse the -l flag to load local."); +#endif + + if (op == EDITENTRY || op == NEWSH) { + if (username != NULL) { + pw = getpwnam(username); + if (pw == NULL) + errx(1, "unknown user: %s", username); + if (uid && uid != pw->pw_uid) + baduser(); + } else { + pw = getpwuid(uid); + if (pw == NULL) + errx(1, "unknown user: uid %u", uid); + } + + /* Make a copy for later verification */ + old_pw = *pw; + old_pw.pw_gecos = strdup(old_pw.pw_gecos); + if (!old_pw.pw_gecos) { + err(1, "strdup"); + /*NOTREACHED*/ + } + } + + if (op == NEWSH) { + /* protect p_shell -- it thinks NULL is /bin/sh */ + if (!arg[0]) + usage(); + if (p_shell(arg, pw, NULL)) + (*Pw_error)(NULL, 0, 1); + } + + if (op == LOADENTRY) { + if (uid) + baduser(); + pw = &lpw; + if (!pw_scan(arg, pw, NULL)) + exit(1); + } + + /* Edit the user passwd information if requested. */ + if (op == EDITENTRY) { + struct stat sb; + + dfd = mkstemp(tempname); + if (dfd < 0 || fcntl(dfd, F_SETFD, 1) < 0) + (*Pw_error)(tempname, 1, 1); + if (atexit(cleanup)) { + cleanup(); + errx(1, "couldn't register cleanup"); + } + if (stat(dirname(tempname), &sb) == -1) + err(1, "couldn't stat `%s'", dirname(tempname)); + if (!(sb.st_mode & S_ISTXT)) + errx(1, "temporary directory `%s' is not sticky", + dirname(tempname)); + + display(tempname, dfd, pw); + edit(tempname, pw); + } + +#ifdef YP + if (use_yp) { + if (pw_yp(pw, uid)) + yppw_error((char *)NULL, 0, 1); + else + exit(0); + /* Will not exit from this if. */ + } +#endif /* YP */ + + + /* + * Get the passwd lock file and open the passwd file for + * reading. + */ + pw_init(); + tfd = pw_lock(0); + if (tfd < 0) { + if (errno != EEXIST) + err(1, "%s", _PATH_MASTERPASSWD_LOCK); + warnx("The passwd file is busy, waiting..."); + tfd = pw_lock(10); + if (tfd < 0) { + if (errno != EEXIST) + err(1, "%s", _PATH_MASTERPASSWD_LOCK); + errx(1, "The passwd file is still busy, " + "try again later."); + } + } + if (fcntl(tfd, F_SETFD, 1) < 0) + pw_error(_PATH_MASTERPASSWD_LOCK, 1, 1); + + pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0); + if (pfd < 0 || fcntl(pfd, F_SETFD, 1) < 0) + pw_error(_PATH_MASTERPASSWD, 1, 1); + + /* Copy the passwd file to the lock file, updating pw. */ + pw_copy(pfd, tfd, pw, (op == LOADENTRY) ? NULL : &old_pw); + + close(pfd); + close(tfd); + + /* Now finish the passwd file update. */ + if (pw_mkdb(username, 0) < 0) + pw_error(NULL, 0, 1); + + exit(0); +} + +void +baduser(void) +{ + + errx(1, "%s", strerror(EACCES)); +} + +void +usage(void) +{ + + (void)fprintf(stderr, + "usage: %s [-a list] [-s shell] [-l] [user]\n" + " %s [-a list] [-s shell] [-y] [user]\n", + getprogname(), getprogname()); + exit(1); +} + +void +cleanup(void) +{ + + (void)unlink(tempname); +} diff --git a/usr.bin/chpass/chpass.h b/usr.bin/chpass/chpass.h new file mode 100644 index 000000000..ea5fa34f8 --- /dev/null +++ b/usr.bin/chpass/chpass.h @@ -0,0 +1,81 @@ +/* $NetBSD: chpass.h,v 1.12 2005/02/17 17:09:48 xtraeme Exp $ */ + +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)chpass.h 8.4 (Berkeley) 4/2/94 + */ + +struct passwd; + +typedef struct _entry { + const char *prompt; + int (*func)(const char *, struct passwd *, struct _entry *), restricted, len; + const char *except, *save; +} ENTRY; + +extern int use_yp; + +/* Field numbers. */ +#define E_BPHONE 8 +#define E_HPHONE 9 +#define E_LOCATE 10 +#define E_NAME 7 +#define E_SHELL 12 + +extern ENTRY list[]; +extern uid_t uid; + +int atot(const char *, time_t *); +void display(char *, int, struct passwd *); +void edit(char *, struct passwd *); +const char * + ok_shell(const char *); +int p_change(const char *, struct passwd *, ENTRY *); +int p_class(const char *, struct passwd *, ENTRY *); +int p_expire(const char *, struct passwd *, ENTRY *); +int p_gecos(const char *, struct passwd *, ENTRY *); +int p_gid(const char *, struct passwd *, ENTRY *); +int p_hdir(const char *, struct passwd *, ENTRY *); +int p_login(const char *, struct passwd *, ENTRY *); +int p_passwd(const char *, struct passwd *, ENTRY *); +int p_shell(const char *, struct passwd *, ENTRY *); +int p_uid(const char *, struct passwd *, ENTRY *); +char *ttoa(char *, size_t, time_t); +int verify(char *, struct passwd *); + +#ifdef YP +int check_yppasswdd(void); +int pw_yp(struct passwd *, uid_t); +void yppw_error(const char *name, int, int); +void yppw_prompt(void); +struct passwd *ypgetpwnam(const char *); +struct passwd *ypgetpwuid(uid_t); +#endif + +extern void (*Pw_error)(const char *name, int, int); diff --git a/usr.bin/chpass/edit.c b/usr.bin/chpass/edit.c new file mode 100644 index 000000000..102a3c2d3 --- /dev/null +++ b/usr.bin/chpass/edit.c @@ -0,0 +1,227 @@ +/* $NetBSD: edit.c,v 1.20 2009/04/11 12:10:02 lukem Exp $ */ + +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)edit.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: edit.c,v 1.20 2009/04/11 12:10:02 lukem Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chpass.h" + +void +edit(char *tempname, struct passwd *pw) +{ + struct stat begin, end; + + for (;;) { + if (stat(tempname, &begin)) + (*Pw_error)(tempname, 1, 1); + pw_edit(1, tempname); + if (stat(tempname, &end)) + (*Pw_error)(tempname, 1, 1); + if (begin.st_mtime == end.st_mtime) { + warnx("no changes made"); + (*Pw_error)(NULL, 0, 0); + } + if (verify(tempname, pw)) + break; +#ifdef YP + if (use_yp) + yppw_prompt(); + else +#endif + pw_prompt(); + } +} + +/* + * display -- + * print out the file for the user to edit; strange side-effect: + * set conditional flag if the user gets to edit the shell. + */ +void +display(char *tempname, int fd, struct passwd *pw) +{ + FILE *fp; + char *bp, *p; + char chgstr[256], expstr[256]; + + if (!(fp = fdopen(fd, "w"))) + (*Pw_error)(tempname, 1, 1); + + (void)fprintf(fp, + "#Changing user %sdatabase information for %s.\n", + use_yp ? "YP " : "", pw->pw_name); + if (!uid) { + (void)fprintf(fp, "Login: %s\n", pw->pw_name); + (void)fprintf(fp, "Password: %s\n", pw->pw_passwd); + (void)fprintf(fp, "Uid [#]: %d\n", pw->pw_uid); + (void)fprintf(fp, "Gid [# or name]: %d\n", pw->pw_gid); + (void)fprintf(fp, "Change [month day year]: %s\n", + ttoa(chgstr, sizeof chgstr, pw->pw_change)); + (void)fprintf(fp, "Expire [month day year]: %s\n", + ttoa(expstr, sizeof expstr, pw->pw_expire)); + (void)fprintf(fp, "Class: %s\n", pw->pw_class); + (void)fprintf(fp, "Home directory: %s\n", pw->pw_dir); + (void)fprintf(fp, "Shell: %s\n", + *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); + } + /* Only admin can change "restricted" shells. */ + else if (ok_shell(pw->pw_shell)) + /* + * Make shell a restricted field. Ugly with a + * necklace, but there's not much else to do. + */ + (void)fprintf(fp, "Shell: %s\n", + *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); + else + list[E_SHELL].restricted = 1; + bp = strdup(pw->pw_gecos); + if (!bp) { + err(1, "strdup"); + /*NOTREACHED*/ + } + p = strsep(&bp, ","); + (void)fprintf(fp, "Full Name: %s\n", p ? p : ""); + p = strsep(&bp, ","); + (void)fprintf(fp, "Location: %s\n", p ? p : ""); + p = strsep(&bp, ","); + (void)fprintf(fp, "Office Phone: %s\n", p ? p : ""); + p = strsep(&bp, ","); + (void)fprintf(fp, "Home Phone: %s\n", p ? p : ""); + + (void)fchown(fd, getuid(), getgid()); + (void)fclose(fp); +} + +int +verify(char *tempname, struct passwd *pw) +{ + ENTRY *ep; + char *p; + struct stat sb; + FILE *fp = NULL; + int len, fd; + static char buf[LINE_MAX]; + +#ifdef __minix + if ((fd = open(tempname, O_RDONLY)) == -1 || + (fp = fdopen(fd, "r")) == NULL) +#else + if ((fd = open(tempname, O_RDONLY|O_NOFOLLOW)) == -1 || + (fp = fdopen(fd, "r")) == NULL) +#endif + (*Pw_error)(tempname, 1, 1); + if (fstat(fd, &sb)) + (*Pw_error)(tempname, 1, 1); + if (sb.st_size == 0 || sb.st_nlink != 1) { + warnx("corrupted temporary file"); + goto bad; + } + while (fgets(buf, sizeof(buf), fp)) { + if (!buf[0] || buf[0] == '#') + continue; + if (!(p = strchr(buf, '\n'))) { + warnx("line too long"); + goto bad; + } + *p = '\0'; + for (ep = list;; ++ep) { + if (!ep->prompt) { + warnx("unrecognized field"); + goto bad; + } + if (!strncasecmp(buf, ep->prompt, ep->len)) { + if (ep->restricted && uid) { + warnx( + "you may not change the %s field", + ep->prompt); + goto bad; + } + if (!(p = strchr(buf, ':'))) { + warnx("line corrupted"); + goto bad; + } + while (isspace((unsigned char)*++p)); + if (ep->except && strpbrk(p, ep->except)) { + warnx( + "illegal character in the \"%s\" field", + ep->prompt); + goto bad; + } + if ((ep->func)(p, pw, ep)) { +bad: (void)fclose(fp); + return (0); + } + break; + } + } + } + (void)fclose(fp); + + /* Build the gecos field. */ + len = strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) + + strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save) + 4; + if (!(p = malloc(len))) + err(1, "malloc"); + (void)snprintf(p, len, "%s,%s,%s,%s", list[E_NAME].save, + list[E_LOCATE].save, list[E_BPHONE].save, list[E_HPHONE].save); + pw->pw_gecos = p; + + if (snprintf(buf, sizeof(buf), + "%s:%s:%d:%d:%s:%lu:%lu:%s:%s:%s", + pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, pw->pw_class, + (u_long)pw->pw_change, (u_long)pw->pw_expire, pw->pw_gecos, + pw->pw_dir, pw->pw_shell) >= (int)sizeof(buf)) { + warnx("entries too long"); + return (0); + } + return (pw_scan(buf, pw, (int *)NULL)); +} diff --git a/usr.bin/chpass/field.c b/usr.bin/chpass/field.c new file mode 100644 index 000000000..6de6baa21 --- /dev/null +++ b/usr.bin/chpass/field.c @@ -0,0 +1,253 @@ +/* $NetBSD: field.c,v 1.12 2009/04/11 12:10:02 lukem Exp $ */ + +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)field.c 8.4 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: field.c,v 1.12 2009/04/11 12:10:02 lukem Exp $"); +#endif +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chpass.h" +#include "pathnames.h" + +/* ARGSUSED */ +int +p_login(const char *p, struct passwd *pw, ENTRY *ep) +{ + + if (!*p) { + warnx("empty login field"); + return (1); + } + if (*p == '-') { + warnx("login names may not begin with a hyphen"); + return (1); + } + if (!(pw->pw_name = strdup(p))) { + warnx("can't save entry"); + return (1); + } + if (strchr(p, '.')) + warnx("\'.\' is dangerous in a login name"); + for (; *p; ++p) + if (isupper((unsigned char)*p)) { + warnx("upper-case letters are dangerous in a login name"); + break; + } + return (0); +} + +/* ARGSUSED */ +int +p_passwd(const char *p, struct passwd *pw, ENTRY *ep) +{ + + if (!(pw->pw_passwd = strdup(p))) { + warnx("can't save password entry"); + return (1); + } + + return (0); +} + +/* ARGSUSED */ +int +p_uid(const char *p, struct passwd *pw, ENTRY *ep) +{ + unsigned long id; + char *np; + + if (!*p) { + warnx("empty uid field"); + return (1); + } + if (!isdigit((unsigned char)*p)) { + warnx("illegal uid"); + return (1); + } + errno = 0; + id = strtoul(p, &np, 10); + /* + * We don't need to check the return value of strtoul() + * since ULONG_MAX is greater than UID_MAX. + */ + if (*np || id > UID_MAX) { + warnx("illegal uid"); + return (1); + } + pw->pw_uid = (uid_t)id; + return (0); +} + +/* ARGSUSED */ +int +p_gid(const char *p, struct passwd *pw, ENTRY *ep) +{ + struct group *gr; + unsigned long id; + char *np; + + if (!*p) { + warnx("empty gid field"); + return (1); + } + if (!isdigit((unsigned char)*p)) { + if (!(gr = getgrnam(p))) { + warnx("unknown group %s", p); + return (1); + } + pw->pw_gid = gr->gr_gid; + return (0); + } + errno = 0; + id = strtoul(p, &np, 10); + /* + * We don't need to check the return value of strtoul() + * since ULONG_MAX is greater than GID_MAX. + */ + if (*np || id > GID_MAX) { + warnx("illegal gid"); + return (1); + } + pw->pw_gid = (gid_t)id; + return (0); +} + +/* ARGSUSED */ +int +p_class(const char *p, struct passwd *pw, ENTRY *ep) +{ + + if (!(pw->pw_class = strdup(p))) { + warnx("can't save entry"); + return (1); + } + + return (0); +} + +/* ARGSUSED */ +int +p_change(const char *p, struct passwd *pw, ENTRY *ep) +{ + + if (!atot(p, &pw->pw_change)) + return (0); + warnx("illegal date for change field"); + return (1); +} + +/* ARGSUSED */ +int +p_expire(const char *p, struct passwd *pw, ENTRY *ep) +{ + + if (!atot(p, &pw->pw_expire)) + return (0); + warnx("illegal date for expire field"); + return (1); +} + +/* ARGSUSED */ +int +p_gecos(const char *p, struct passwd *pw, ENTRY *ep) +{ + + if (!(ep->save = strdup(p))) { + warnx("can't save entry"); + return (1); + } + return (0); +} + +/* ARGSUSED */ +int +p_hdir(const char *p, struct passwd *pw, ENTRY *ep) +{ + + if (!*p) { + warnx("empty home directory field"); + return (1); + } + if (!(pw->pw_dir = strdup(p))) { + warnx("can't save entry"); + return (1); + } + return (0); +} + +/* ARGSUSED */ +int +p_shell(const char *p, struct passwd *pw, ENTRY *ep) +{ + const char *t; + + if (!*p) { + if (!(pw->pw_shell = strdup(_PATH_BSHELL))) { + warnx("can't save entry"); + return (1); + } + return (0); + } + /* only admin can change from or to "restricted" shells */ + if (uid && pw->pw_shell && !ok_shell(pw->pw_shell)) { + warnx("%s: current shell non-standard", pw->pw_shell); + return (1); + } + if (!(t = ok_shell(p))) { + if (uid) { + warnx("%s: non-standard shell", p); + return (1); + } + } + else + p = t; + if (!(pw->pw_shell = strdup(p))) { + warnx("can't save entry"); + return (1); + } + return (0); +} diff --git a/usr.bin/chpass/pathnames.h b/usr.bin/chpass/pathnames.h new file mode 100644 index 000000000..f867a34d4 --- /dev/null +++ b/usr.bin/chpass/pathnames.h @@ -0,0 +1,37 @@ +/* $NetBSD: pathnames.h,v 1.4 2003/08/07 11:13:19 agc Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +#include + +#undef _PATH_TMP +#define _PATH_TMP "/tmp/chpass.XXXXXX" diff --git a/usr.bin/chpass/pw_yp.c b/usr.bin/chpass/pw_yp.c new file mode 100644 index 000000000..d209d0cd5 --- /dev/null +++ b/usr.bin/chpass/pw_yp.c @@ -0,0 +1,239 @@ +/* $NetBSD: pw_yp.c,v 1.22 2009/04/11 12:10:02 lukem Exp $ */ + +/* + * Copyright (c) 1988 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)pw_yp.c 1.0 2/2/93"; +#else +__RCSID("$NetBSD: pw_yp.c,v 1.22 2009/04/11 12:10:02 lukem Exp $"); +#endif +#endif /* not lint */ + +#ifdef YP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define passwd yp_passwd_rec +#include +#undef passwd + +#include "chpass.h" + +static char *domain; + +/* + * Check if rpc.yppasswdd is running on the master YP server. + * XXX this duplicates some code, but is much less complex + * than the alternative. + */ +int +check_yppasswdd(void) +{ + char *master; + int rpcport; + + /* + * Get local domain + */ + if (!domain && yp_get_default_domain(&domain) != 0) + return (1); + + /* + * Find the host for the passwd map; it should be running + * the daemon. + */ + master = NULL; + if (yp_master(domain, "passwd.byname", &master) != 0) { + if (master != NULL) + free (master); + return (1); + } + + /* + * Ask the portmapper for the port of the daemon. + */ + if ((rpcport = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, + IPPROTO_UDP)) == 0) + return (1); + + /* + * Successful contact with rpc.yppasswdd. + */ + return (0); +} + +int +pw_yp(struct passwd *pw, uid_t ypuid) +{ + char *master; + int r, rpcport, status; + struct yppasswd yppw; + struct timeval tv; + CLIENT *client; + + /* + * Get local domain + */ + if (!domain && (r = yp_get_default_domain(&domain))) + errx(1, "can't get local YP domain. Reason: %s", + yperr_string(r)); + + /* + * Find the host for the passwd map; it should be running + * the daemon. + */ + master = NULL; + if ((r = yp_master(domain, "passwd.byname", &master)) != 0) { + if (master) + free (master); + warnx("can't find the master YP server. Reason: %s", + yperr_string(r)); + return (1); + } + + /* + * Ask the portmapper for the port of the daemon. + */ + if ((rpcport = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, + IPPROTO_UDP)) == 0) { + warnx("master YP server not running yppasswd daemon.\n\t%s\n", + "Can't change password."); + return (1); + } + + /* + * Be sure the port is privileged + */ + if (rpcport >= IPPORT_RESERVED) { + warnx("yppasswd daemon is on an invalid port."); + return (1); + } + + /* prompt for old password */ + memset(&yppw, 0, sizeof yppw); + yppw.oldpass = getpass("Old password:"); + if (!yppw.oldpass) { + warnx("Cancelled."); + return (1); + } + + /* tell rpc.yppasswdd */ + yppw.newpw.pw_name = strdup(pw->pw_name); + if (!yppw.newpw.pw_name) { + err(1, "strdup"); + /*NOTREACHED*/ + } + yppw.newpw.pw_passwd = strdup(pw->pw_passwd); + if (!yppw.newpw.pw_passwd) { + err(1, "strdup"); + /*NOTREACHED*/ + } + yppw.newpw.pw_uid = pw->pw_uid; + yppw.newpw.pw_gid = pw->pw_gid; + yppw.newpw.pw_gecos = strdup(pw->pw_gecos); + if (!yppw.newpw.pw_gecos) { + err(1, "strdup"); + /*NOTREACHED*/ + } + yppw.newpw.pw_dir = strdup(pw->pw_dir); + if (!yppw.newpw.pw_dir) { + err(1, "strdup"); + /*NOTREACHED*/ + } + yppw.newpw.pw_shell = strdup(pw->pw_shell); + if (!yppw.newpw.pw_shell) { + err(1, "strdup"); + /*NOTREACHED*/ + } + + client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp"); + if (client == NULL) { + warnx("cannot contact yppasswdd on %s: Reason: %s", + master, yperr_string(YPERR_YPBIND)); + return (1); + } + client->cl_auth = authunix_create_default(); + tv.tv_sec = 5; + tv.tv_usec = 0; + r = clnt_call(client, YPPASSWDPROC_UPDATE, + xdr_yppasswd, &yppw, xdr_int, &status, tv); + if (r) { + warnx("rpc to yppasswdd failed."); + return (1); + } else if (status) + printf("Couldn't change YP password.\n"); + else + printf("%s %s, %s\n", + "The YP password information has been changed on", + master, "the master YP passwd server."); + return (0); +} + +void +yppw_error(const char *name, int yperr, int eval) +{ + + if (yperr) { + if (name) + warn("%s", name); + else + warn(NULL); + } + + errx(eval, "YP passwd information unchanged"); +} + +void +yppw_prompt(void) +{ + int c; + + (void)printf("re-edit the password file? [y]: "); + (void)fflush(stdout); + c = getchar(); + if (c != EOF && c != '\n') + while (getchar() != '\n'); + if (c == 'n') + yppw_error(NULL, 0, 0); +} +#endif /* YP */ diff --git a/usr.bin/chpass/table.c b/usr.bin/chpass/table.c new file mode 100644 index 000000000..1d80e221e --- /dev/null +++ b/usr.bin/chpass/table.c @@ -0,0 +1,63 @@ +/* $NetBSD: table.c,v 1.7 2009/04/11 12:10:02 lukem Exp $ */ + +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)table.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: table.c,v 1.7 2009/04/11 12:10:02 lukem Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include "chpass.h" + +char e1[] = ": "; +char e2[] = ":,"; + +ENTRY list[] = { + { "login", p_login, 1, 5, e1, NULL }, + { "password", p_passwd, 1, 8, e1, NULL }, + { "uid", p_uid, 1, 3, e1, NULL }, + { "gid", p_gid, 1, 3, e1, NULL }, + { "class", p_class, 1, 5, e1, NULL }, + { "change", p_change, 1, 6, NULL, NULL }, + { "expire", p_expire, 1, 6, NULL, NULL }, + { "full name", p_gecos, 0, 9, e2, ""}, + { "office phone", p_gecos, 0, 12, e2, ""}, + { "home phone", p_gecos, 0, 10, e2, ""}, + { "location", p_gecos, 0, 8, e2, ""}, + { "home directory", p_hdir, 1, 14, e1, NULL }, + { "shell", p_shell, 0, 5, e1, NULL }, + { NULL, NULL, 0, 0, NULL, NULL }, +}; diff --git a/usr.bin/chpass/util.c b/usr.bin/chpass/util.c new file mode 100644 index 000000000..8c15da42d --- /dev/null +++ b/usr.bin/chpass/util.c @@ -0,0 +1,113 @@ +/* $NetBSD: util.c,v 1.12 2005/02/17 17:09:48 xtraeme Exp $ */ + +/*- + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)util.c 8.4 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: util.c,v 1.12 2005/02/17 17:09:48 xtraeme Exp $"); +#endif +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chpass.h" +#include "pathnames.h" + +char * +ttoa(char *buf, size_t len, time_t tval) +{ + + if (tval) { + struct tm *tp = localtime(&tval); + + (void) strftime(buf, len, "%B %d, %Y", tp); + buf[len - 1] = '\0'; + } + else if (len > 0) + *buf = '\0'; + return (buf); +} + +int +atot(const char *p, time_t *store) +{ + static struct tm *lt; + struct tm tm; + char *t; + time_t tval; + + if (!*p) { + *store = 0; + return (0); + } + if (!lt) { + unsetenv("TZ"); + (void)time(&tval); + lt = localtime(&tval); + } + (void) memset(&tm, 0, sizeof(tm)); + while ((t = strchr(p, ',')) != NULL) + *t = ' '; + t = strptime(p, "%B %d %Y", &tm); + if (t == NULL || (*t != '\0' && *t != '\n')) + return 1; + if ((*store = mktime(&tm)) == (time_t) -1) + return 1; + return (0); +} + +const char * +ok_shell(const char *name) +{ + char *p; + const char *sh; + + setusershell(); + while ((sh = getusershell()) != NULL) { + if (!strcmp(name, sh)) + return (name); + /* allow just shell name, but use "real" path */ + if ((p = strrchr(sh, '/')) && strcmp(name, p + 1) == 0) + return (sh); + } + return (NULL); +} diff --git a/usr.bin/m4/Makefile b/usr.bin/m4/Makefile index 1b3270788..3f266373a 100644 --- a/usr.bin/m4/Makefile +++ b/usr.bin/m4/Makefile @@ -16,8 +16,8 @@ SRCS+= ohash_create_entry.c ohash_delete.c ohash_do.c ohash_entries.c \ ohash_qlookupi.c strtonum.c YHEADER=1 .if (${HOSTPROG:U} == "") -DPADD+= ${LIBMINIXUTIL} ${LIBL} -LDADD+= -lminixutil -ll +DPADD+= ${LIBUTIL} ${LIBL} +LDADD+= -lutil -ll .endif tokenizer.o: parser.h diff --git a/usr.bin/newgrp/Makefile b/usr.bin/newgrp/Makefile new file mode 100644 index 000000000..0b7dc876b --- /dev/null +++ b/usr.bin/newgrp/Makefile @@ -0,0 +1,26 @@ +# $NetBSD: Makefile,v 1.4 2009/04/14 22:15:24 lukem Exp $ +# + +.include + +.if defined(__MINIX) +USE_KERBEROS= no +.endif + +PROG= newgrp +SRCS= newgrp.c grutil.c +BINOWN= root +BINMODE=4555 + +CPPFLAGS+=-DGRUTIL_ACCEPT_GROUP_NUMBERS +CPPFLAGS+=-DGRUTIL_ALLOW_GROUP_ERRORS # for POSIX.1 compliance +CPPFLAGS+= #-DLOGIN_CAP + +DPADD+= ${LIBCRYPT} ${LIBUTIL} +LDADD+= -lcrypt -lutil + +.if (${USE_KERBEROS} != "no") +CPPFLAGS+=-DKERBEROS +.endif + +.include diff --git a/usr.bin/newgrp/grutil.c b/usr.bin/newgrp/grutil.c new file mode 100644 index 000000000..825374615 --- /dev/null +++ b/usr.bin/newgrp/grutil.c @@ -0,0 +1,337 @@ +/* $NetBSD: grutil.c,v 1.2 2008/04/28 20:24:14 martin Exp $ */ + +/*- + * Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Brian Ginsbach. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +__RCSID("$NetBSD: grutil.c,v 1.2 2008/04/28 20:24:14 martin Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef LOGIN_CAP +#include +#endif + +#include "grutil.h" + +typedef enum { + ADDGRP_NOERROR = 0, /* must be zero */ + ADDGRP_EMALLOC = 1, + ADDGRP_EGETGROUPS = 2, + ADDGRP_ESETGROUPS = 3 +} addgrp_ret_t; + +static void +free_groups(void *groups) +{ + int oerrno; + + oerrno = errno; + free(groups); + errno = oerrno; +} + +static addgrp_ret_t +alloc_groups(int *ngroups, gid_t **groups, int *ngroupsmax) +{ + *ngroupsmax = (int)sysconf(_SC_NGROUPS_MAX); + if (*ngroupsmax < 0) + *ngroupsmax = NGROUPS_MAX; + + *groups = malloc(*ngroupsmax * sizeof(**groups)); + if (*groups == NULL) + return ADDGRP_EMALLOC; + + *ngroups = getgroups(*ngroupsmax, *groups); + if (*ngroups == -1) { + free_groups(*groups); + return ADDGRP_ESETGROUPS; + } + return ADDGRP_NOERROR; +} + +static addgrp_ret_t +addgid(gid_t *groups, int ngroups, int ngroupsmax, gid_t gid, int makespace) +{ + int i; + + /* search for gid in supplemental group list */ + for (i = 0; i < ngroups && groups[i] != gid; i++) + continue; + + /* add the gid to the supplemental group list */ + if (i == ngroups) { + if (ngroups < ngroupsmax) + groups[ngroups++] = gid; + else { /* + * setgroups(2) will fail with errno = EINVAL + * if ngroups > nmaxgroups. If makespace is + * set, replace the last group with the new + * one. Otherwise, fail the way setgroups(2) + * would if we passed the larger groups array. + */ + if (makespace) { + /* + * Find a slot that doesn't contain + * the primary group. + */ + struct passwd *pwd; + gid_t pgid; + pwd = getpwuid(getuid()); + if (pwd == NULL) + goto error; + pgid = pwd->pw_gid; + for (i = ngroupsmax - 1; i >= 0; i--) + if (groups[i] != pgid) + break; + if (i < 0) + goto error; + groups[i] = gid; + } + else { + error: + errno = EINVAL; + return ADDGRP_ESETGROUPS; + } + } + if (setgroups(ngroups, groups) < 0) + return ADDGRP_ESETGROUPS; + } + return ADDGRP_NOERROR; +} + +static addgrp_ret_t +addgrp(gid_t newgid, int makespace) +{ + int ngroups, ngroupsmax, rval; + gid_t *groups; + gid_t oldgid; + + oldgid = getgid(); + if (oldgid == newgid) /* nothing to do */ + return ADDGRP_NOERROR; + + rval = alloc_groups(&ngroups, &groups, &ngroupsmax); + if (rval != 0) + return rval; + + /* + * BSD based systems normally have the egid in the supplemental + * group list. + */ +#if (defined(BSD) && BSD >= 199306) + /* + * According to POSIX/XPG6: + * On system where the egid is normally in the supplemental group list + * (or whenever the old egid actually is in the supplemental group + * list): + * o If the new egid is in the supplemental group list, + * just change the egid. + * o If the new egid is not in the supplemental group list, + * add the new egid to the list if there is room. + */ + + rval = addgid(groups, ngroups, ngroupsmax, newgid, makespace); +#else + /* + * According to POSIX/XPG6: + * On systems where the egid is not normally in the supplemental group + * list (or whenever the old egid is not in the supplemental group + * list): + * o If the new egid is in the supplemental group list, delete + * it from the list. + * o If the old egid is not in the supplemental group list, + * add the old egid to the list if there is room. + */ + { + int i; + + /* search for new egid in supplemental group list */ + for (i = 0; i < ngroups && groups[i] != newgid; i++) + continue; + + /* remove new egid from supplemental group list */ + if (i != ngroups) + for (--ngroups; i < ngroups; i++) + groups[i] = groups[i + 1]; + + rval = addgid(groups, ngroups, ngroupsmax, oldgid, makespace); + } +#endif + free_groups(groups); + return rval; +} + +/* + * If newgrp fails, it returns (gid_t)-1 and the errno variable is + * set to: + * [EINVAL] Unknown group. + * [EPERM] Bad password. + */ +static gid_t +newgrp(const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt) +{ + struct group *grp; + char **ap; + char *p; + gid_t *groups; + int ngroups, ngroupsmax; + + if (gname == NULL) + return pwd->pw_gid; + + grp = getgrnam(gname); + +#ifdef GRUTIL_ACCEPT_GROUP_NUMBERS + if (grp == NULL) { + gid_t gid; + if (*gname != '-') { + gid = (gid_t)strtol(gname, &p, 10); + if (*p == '\0') + grp = getgrgid(gid); + } + } +#endif + if (grp == NULL) { + errno = EINVAL; + return (gid_t)-1; + } + + if (ruid == 0 || pwd->pw_gid == grp->gr_gid) + return grp->gr_gid; + + if (alloc_groups(&ngroups, &groups, &ngroupsmax) == 0) { + int i; + for (i = 0; i < ngroups; i++) + if (groups[i] == grp->gr_gid) { + free_groups(groups); + return grp->gr_gid; + } + free_groups(groups); + } + + /* + * Check the group membership list in case the groups[] array + * was maxed out or the user has been added to it since login. + */ + for (ap = grp->gr_mem; *ap != NULL; ap++) + if (strcmp(*ap, pwd->pw_name) == 0) + return grp->gr_gid; + + if (*grp->gr_passwd != '\0') { + p = getpass(prompt); + if (strcmp(grp->gr_passwd, crypt(p, grp->gr_passwd)) == 0) { + (void)memset(p, '\0', _PASSWORD_LEN); + return grp->gr_gid; + } + (void)memset(p, '\0', _PASSWORD_LEN); + } + + errno = EPERM; + return (gid_t)-1; +} + +#ifdef GRUTIL_SETGROUPS_MAKESPACE +# define ADDGRP_MAKESPACE 1 +#else +# define ADDGRP_MAKESPACE 0 +#endif + +#ifdef GRUTIL_ALLOW_GROUP_ERRORS +# define maybe_exit(e) +#else +# define maybe_exit(e) exit(e); +#endif + +void +addgroup( +#ifdef LOGIN_CAP + login_cap_t *lc, +#endif + const char *gname, struct passwd *pwd, uid_t ruid, const char *prompt) +{ + pwd->pw_gid = newgrp(gname, pwd, ruid, prompt); + if (pwd->pw_gid == (gid_t)-1) { + switch (errno) { + case EINVAL: + warnx("Unknown group `%s'", gname); + maybe_exit(EXIT_FAILURE); + break; + case EPERM: /* password failure */ + warnx("Sorry"); + maybe_exit(EXIT_FAILURE); + break; + default: /* XXX - should never happen */ + err(EXIT_FAILURE, "unknown error"); + break; + } + pwd->pw_gid = getgid(); + } + + switch (addgrp(pwd->pw_gid, ADDGRP_MAKESPACE)) { + case ADDGRP_NOERROR: + break; + case ADDGRP_EMALLOC: + err(EXIT_FAILURE, "malloc"); + break; + case ADDGRP_EGETGROUPS: + err(EXIT_FAILURE, "getgroups"); + break; + case ADDGRP_ESETGROUPS: + switch(errno) { + case EINVAL: + warnx("setgroups: ngroups > ngroupsmax"); + maybe_exit(EXIT_FAILURE); + break; + case EPERM: + case EFAULT: + default: + warn("setgroups"); + maybe_exit(EXIT_FAILURE); + break; + } + break; + } + +#ifdef LOGIN_CAP + if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGID) == -1) + err(EXIT_FAILURE, "setting user context"); +#else + if (setgid(pwd->pw_gid) == -1) + err(EXIT_FAILURE, "setgid"); +#endif +} diff --git a/usr.bin/newgrp/grutil.h b/usr.bin/newgrp/grutil.h new file mode 100644 index 000000000..957f7985b --- /dev/null +++ b/usr.bin/newgrp/grutil.h @@ -0,0 +1,40 @@ +/* $NetBSD: grutil.h,v 1.2 2008/04/28 20:24:14 martin Exp $ */ + +/*- + * Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Brian Ginsbach. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _GRUTIL_H_ +#define _GRUTIL_H_ + +void addgroup( +#ifdef LOGIN_CAP + login_cap_t *, +#endif + const char *, struct passwd *, uid_t, const char *); + +#endif /* _GRUTIL_H_ */ diff --git a/usr.bin/newgrp/newgrp.1 b/usr.bin/newgrp/newgrp.1 new file mode 100644 index 000000000..3a00aa1aa --- /dev/null +++ b/usr.bin/newgrp/newgrp.1 @@ -0,0 +1,121 @@ +.\" $NetBSD: newgrp.1,v 1.4 2010/05/14 17:28:23 joerg Exp $ +.\" +.\" Copyright (c) 2007, The NetBSD Foundation. +.\" All Rights Reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Brian Ginsbach. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd June 6, 2007 +.Dt NEWGRP 1 +.Os +.Sh NAME +.Nm newgrp +.Nd change to a new primary group +.Sh SYNOPSIS +.Nm +.Op Fl l +.Op Ar group +.Sh DESCRIPTION +The +.Nm +command changes a user to a new primary group +.Pq real and effective group ID +by starting a new shell. +The user remains logged in and the current directory +and file creation mask remain unchanged. +The user is always given a new shell even if +the primary group change fails. +.Pp +The +.Nm +command accepts the following options: +.Bl -tag -width indent +.It Fl l +The environment is changed to what would be expected if the user +actually logged in again. +This simulates a full login. +.El +.Pp +The +.Ar group +is a group name or non-negative numeric group ID from the group database. +The real and effective group IDs are set to +.Ar group +or the group ID associated with the group name. +.Pp +If +.Ar group +is not specified, +.Nm +restores the user's real and effective group IDs to the user's +primary group specified in the password database. +The user's supplementary group IDs are restored to the set specified +for the user in the group database. +.Pp +If the user is not a member of the specified group, and the group +requires a password, the user will be prompted for the group password. +.Sh FILES +.Bl -tag -width /etc/master.passwd -compact +.It Pa /etc/group +The group database +.It Pa /etc/master.passwd +The user database +.It Pa /etc/passwd +A Version 7 format password file +.El +.Sh EXIT STATUS +If a new shell is started the exit status is the exit status of the shell. +Otherwise the exit status will be \*[Gt]0. +.Sh SEE ALSO +.Xr csh 1 , +.Xr groups 1 , +.Xr login 1 , +.Xr sh 1 , +.Xr su 1 , +.Xr umask 2 , +.Xr group 5 , +.Xr passwd 5 , +.Xr environ 7 +.Sh STANDARDS +The +.Nm +command conforms to +.St -p1003.1-2001 . +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . +A +.Nm +command appeared in +.Nx 5.0 . +.Sh BUGS +There is no convenient way to enter a password into +.Pa /etc/group . +The use of group passwords is strongly discouraged +since they are inherently insecure. +It is not possible to stop users from obtaining the encrypted +password from the group database. diff --git a/usr.bin/newgrp/newgrp.c b/usr.bin/newgrp/newgrp.c new file mode 100644 index 000000000..27b2335a4 --- /dev/null +++ b/usr.bin/newgrp/newgrp.c @@ -0,0 +1,195 @@ +/*- + * Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Brian Ginsbach. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#ifndef lint +__RCSID("$NetBSD: newgrp.c,v 1.6 2008/04/28 20:24:14 martin Exp $"); +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef LOGIN_CAP +#include +#endif + +#include "grutil.h" + +static void +usage(void) +{ + (void)fprintf(stderr, "usage: %s [-l] [group]\n", getprogname()); + exit(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ + extern char **environ; + struct passwd *pwd; + int c, lflag; + char *shell, sbuf[MAXPATHLEN + 2]; + uid_t uid; +#ifdef LOGIN_CAP + login_cap_t *lc; + u_int flags = LOGIN_SETUSER; +#endif + + uid = getuid(); + pwd = getpwuid(uid); + if (pwd == NULL) + errx(EXIT_FAILURE, "who are you?"); + +#ifdef LOGIN_CAP + if ((lc = login_getclass(pwd->pw_class)) == NULL) + errx(EXIT_FAILURE, "%s: unknown login class", pwd->pw_class); +#endif + + (void)setprogname(argv[0]); + lflag = 0; + while ((c = getopt(argc, argv, "-l")) != -1) { + switch (c) { + case '-': + case 'l': + if (lflag) + usage(); + lflag = 1; + break; + default: + usage(); + break; + } + } + + argc -= optind; + argv += optind; + + if (argc > 0) { +#if 0 + pwd->pw_gid = newgrp(*argv, pwd); + addgrp(pwd->pw_gid); + if (setgid(pwd->pw_gid) < 0) + err(1, "setgid"); +#endif +#ifdef LOGIN_CAP + addgroup(lc, *argv, pwd, getuid(), "Password:"); +#else + addgroup(*argv, pwd, getuid(), "Password:"); +#endif + } else { +#ifdef LOGIN_CAP + flags |= LOGIN_SETGROUP; +#else + if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) + err(EXIT_FAILURE, "initgroups"); + if (setgid(pwd->pw_gid) == -1) + err(EXIT_FAILURE, "setgid"); +#endif + } + +#ifdef LOGIN_CAP + if (setusercontext(lc, pwd, uid, flags) == -1) + err(EXIT_FAILURE, "setusercontext"); + if (!lflag) + login_close(lc); +#else + if (setuid(pwd->pw_uid) == -1) + err(EXIT_FAILURE, "setuid"); +#endif + + if (*pwd->pw_shell == '\0') { +#ifdef TRUST_ENV_SHELL + shell = getenv("SHELL"); + if (shell != NULL) + pwd->pw_shell = shell; + else +#endif + pwd->pw_shell = __UNCONST(_PATH_BSHELL); + } + + shell = pwd->pw_shell; + + if (lflag) { + char *term; +#ifdef KERBEROS + char *krbtkfile; +#endif + + if (chdir(pwd->pw_dir) == -1) + warn("%s", pwd->pw_dir); + + term = getenv("TERM"); +#ifdef KERBEROS + krbtkfile = getenv("KRBTKFILE"); +#endif + + /* create an empty environment */ + if ((environ = malloc(sizeof(char *))) == NULL) + err(EXIT_FAILURE, NULL); + environ[0] = NULL; +#ifdef LOGIN_CAP + if (setusercontext(lc, pwd, uid, LOGIN_SETENV | LOGIN_SETPATH) == -1) + err(EXIT_FAILURE, "setusercontext"); + login_close(lc); +#else + (void)setenv("PATH", _PATH_DEFPATH, 1); +#endif + if (term != NULL) + (void)setenv("TERM", term, 1); +#ifdef KERBEROS + if (krbtkfile != NULL) + (void)setenv("KRBTKFILE", krbtkfile, 1); +#endif + + (void)setenv("LOGNAME", pwd->pw_name, 1); + (void)setenv("USER", pwd->pw_name, 1); + (void)setenv("HOME", pwd->pw_dir, 1); + (void)setenv("SHELL", pwd->pw_shell, 1); + + sbuf[0] = '-'; + (void)strlcpy(sbuf + 1, basename(pwd->pw_shell), + sizeof(sbuf) - 1); + shell = sbuf; + } + + (void)execl(pwd->pw_shell, shell, NULL); + err(EXIT_FAILURE, "%s", pwd->pw_shell); + /* NOTREACHED */ +} diff --git a/usr.bin/passwd/Makefile b/usr.bin/passwd/Makefile new file mode 100644 index 000000000..799944186 --- /dev/null +++ b/usr.bin/passwd/Makefile @@ -0,0 +1,51 @@ +# $NetBSD: Makefile,v 1.41 2007/05/28 12:06:29 tls Exp $ +# from: @(#)Makefile 8.3 (Berkeley) 4/2/94 + +.include + +.if defined(__MINIX) +USE_YP= no +USE_KERBEROS= no +USE_PAM= no +.endif + +USE_FORT?= yes # setuid +PROG= passwd +SRCS= local_passwd.c passwd.c +MAN= passwd.1 + +CPPFLAGS+=-I${.CURDIR} #-DLOGIN_CAP + +.if (${USE_YP} != "no") +SRCS+= yp_passwd.c +CPPFLAGS+=-DYP +DPADD+= ${LIBRPCSVC} +LDADD+= -lrpcsvc +LINKS+= ${BINDIR}/passwd ${BINDIR}/yppasswd +MAN+= yppasswd.1 +.endif + +DPADD+= ${LIBCRYPT} ${LIBUTIL} +LDADD+= -lcrypt -lutil + +BINOWN= root +BINMODE=4555 + +.if (${USE_KERBEROS} != "no") +CPPFLAGS+= -DKERBEROS5 -I${DESTDIR}/usr/include/krb5 +SRCS+= krb5_passwd.c + +DPADD+= ${LIBKRB5} ${LIBCRYPTO} ${LIBASN1} ${LIBCOM_ERR} ${LIBROKEN} ${LIBCRYPT} +LDADD+= -lkrb5 -lcrypto -lasn1 -lcom_err -lroken -lcrypt +LINKS+= ${BINDIR}/passwd ${BINDIR}/kpasswd +MAN+= kpasswd.1 +.endif + +.if (${USE_PAM} != "no") +CPPFLAGS+=-DUSE_PAM +SRCS+= pam_passwd.c +LDADD+=-lpam ${PAM_STATIC_LDADD} +DPADD+=${LIBPAM} ${PAM_STATIC_DPADD} +.endif + +.include diff --git a/usr.bin/passwd/extern.h b/usr.bin/passwd/extern.h new file mode 100644 index 000000000..11a4d746d --- /dev/null +++ b/usr.bin/passwd/extern.h @@ -0,0 +1,85 @@ +/* $NetBSD: extern.h,v 1.13 2006/03/23 23:37:07 wiz Exp $ */ + +/* + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 4/2/94 + */ + +#ifdef USE_PAM + +void usage(void); + +#ifdef KERBEROS5 +void pwkrb5_usage(const char *); +void pwkrb5_argv0_usage(const char *); +void pwkrb5_process(const char *, int, char **); +#endif + +#ifdef YP +void pwyp_usage(const char *); +void pwyp_argv0_usage(const char *); +void pwyp_process(const char *, int, char **); +#endif + +void pwlocal_usage(const char *); +void pwlocal_process(const char *, int, char **); + +void pwpam_process(const char *, int, char **); + +#else /* ! USE_PAM */ + +/* return values from pw_init() and pw_arg_end() */ +enum { + PW_USE_FORCE, + PW_USE, + PW_DONT_USE +}; + +#ifdef KERBEROS5 +int krb5_init __P((const char *)); +int krb5_arg __P((char, const char *)); +int krb5_arg_end __P((void)); +void krb5_end __P((void)); +int krb5_chpw __P((const char *)); +#endif +#ifdef YP +int yp_init __P((const char *)); +int yp_arg __P((char, const char *)); +int yp_arg_end __P((void)); +void yp_end __P((void)); +int yp_chpw __P((const char *)); +#endif +/* local */ +int local_init __P((const char *)); +int local_arg __P((char, const char *)); +int local_arg_end __P((void)); +void local_end __P((void)); +int local_chpw __P((const char *)); + +#endif /* USE_PAM */ diff --git a/usr.bin/passwd/kpasswd.1 b/usr.bin/passwd/kpasswd.1 new file mode 100644 index 000000000..8e23270b0 --- /dev/null +++ b/usr.bin/passwd/kpasswd.1 @@ -0,0 +1,48 @@ +.\" $NetBSD: kpasswd.1,v 1.3 2008/04/30 13:11:01 martin Exp $ +.\" +.\" Copyright (c) 2005 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Jason R. Thorpe. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd February 25, 2005 +.Dt KPASSWD 1 +.Os +.Sh NAME +.Nm kpasswd +.Nd modify a user's Kerberos 5 password +.Sh SYNOPSIS +.Nm +.Op Ar principal +.Sh DESCRIPTION +.Nm +changes the user's Kerberos 5 password. +.Pp +The +.Nm +command is deprecated. +See +.Xr passwd 1 +for more information. diff --git a/usr.bin/passwd/krb5_passwd.c b/usr.bin/passwd/krb5_passwd.c new file mode 100644 index 000000000..eefdbe458 --- /dev/null +++ b/usr.bin/passwd/krb5_passwd.c @@ -0,0 +1,351 @@ +/* $NetBSD: krb5_passwd.c,v 1.18 2009/04/18 09:04:34 mlelstv Exp $ */ + +/* + * Copyright (c) 2000, 2005 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Johan Danielsson; and by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* uses the `Kerberos Change Password Protocol' */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "extern.h" + +#ifdef USE_PAM + +void +pwkrb5_usage(const char *prefix) +{ + + (void) fprintf(stderr, "%s %s [-d krb5 | -k] [principal]\n", + prefix, getprogname()); +} + +void +pwkrb5_argv0_usage(const char *prefix) +{ + + (void) fprintf(stderr, "%s %s [principal]\n", + prefix, getprogname()); +} + +void +pwkrb5_process(const char *username, int argc, char **argv) +{ + krb5_context context; + krb5_error_code ret; + krb5_get_init_creds_opt opt; + krb5_principal principal; + krb5_creds cred; + int result_code; + krb5_data result_code_string, result_string; + char pwbuf[BUFSIZ]; + int ch; + + while ((ch = getopt(argc, argv, "5ku:")) != -1) { + switch (ch) { + case '5': + /* + * Compatibility option that historically + * specified to use Kerberos 5. Silently + * ignore it. + */ + break; + + case 'k': + /* + * Absorb the -k that may have gotten us here. + */ + break; + + case 'u': + /* + * Historical option to specify principal. + */ + username = optarg; + break; + + default: + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + switch (argc) { + case 0: + /* username already provided */ + break; + case 1: + /* overrides -u */ + username = argv[0]; + break; + default: + usage(); + /* NOTREACHED */ + } + + ret = krb5_init_context(&context); + if (ret != 0) { + if (ret == ENXIO) + errx(1, "Kerberos 5 not in use."); + warnx("Unable to initialize Kerberos 5: %s", + krb5_get_err_text(context, ret)); + goto bad; + } + + krb5_get_init_creds_opt_init(&opt); + + krb5_get_init_creds_opt_set_tkt_life(&opt, 300L); + krb5_get_init_creds_opt_set_forwardable(&opt, FALSE); + krb5_get_init_creds_opt_set_proxiable(&opt, FALSE); + + ret = krb5_parse_name(context, username, &principal); + if (ret) { + warnx("failed to parse principal: %s", + krb5_get_err_text(context, ret)); + goto bad; + } + + ret = krb5_get_init_creds_password(context, + &cred, + principal, + NULL, + krb5_prompter_posix, + NULL, + 0L, + "kadmin/changepw", + &opt); + + + switch (ret) { + case 0: + break; + + case KRB5_LIBOS_PWDINTR : + /* XXX */ + goto bad; + + case KRB5KRB_AP_ERR_BAD_INTEGRITY : + case KRB5KRB_AP_ERR_MODIFIED : + fprintf(stderr, "Password incorrect\n"); + goto bad; + + default: + warnx("failed to get credentials: %s", + krb5_get_err_text(context, ret)); + goto bad; + } + + krb5_data_zero(&result_code_string); + krb5_data_zero(&result_string); + + /* XXX use getpass? It has a broken interface. */ + if (UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf), + "New password: ", 1) != 0) + goto bad; + + ret = krb5_set_password(context, &cred, pwbuf, NULL, + &result_code, + &result_code_string, + &result_string); + if (ret) { + warnx("unable to set password: %s", + krb5_get_err_text(context, ret)); + goto bad; + } + + printf("%s%s%.*s\n", + krb5_passwd_result_to_string(context, result_code), + result_string.length > 0 ? " : " : "", + (int)result_string.length, + result_string.length > 0 ? (char *)result_string.data : ""); + + krb5_data_free(&result_code_string); + krb5_data_free(&result_string); + + krb5_free_cred_contents(context, &cred); + krb5_free_context(context); + if (result_code) + exit(1); + return; + + bad: + krb5_free_context(context); + exit(1); +} + +#else /* ! USE_PAM */ + +static krb5_context defcontext; +static krb5_principal defprinc; +static int kusage = PW_USE; + +int +krb5_init(const char *progname) +{ + return krb5_init_context(&defcontext); +} + +int +krb5_arg (char ch, const char *opt) +{ + krb5_error_code ret; + switch(ch) { + case '5': + case 'k': + kusage = PW_USE_FORCE; + return 1; + case 'u': + ret = krb5_parse_name(defcontext, opt, &defprinc); + if(ret) { + krb5_warn(defcontext, ret, "%s", opt); + return 0; + } + return 1; + } + return 0; +} + +int +krb5_arg_end(void) +{ + return kusage; +} + +void +krb5_end(void) +{ + if (defcontext == NULL) + return; + if(defprinc) + krb5_free_principal(defcontext, defprinc); + krb5_free_context(defcontext); +} + + +int +krb5_chpw(const char *username) +{ + krb5_error_code ret; + krb5_context context; + krb5_principal principal; + krb5_get_init_creds_opt opt; + krb5_creds cred; + int result_code; + krb5_data result_code_string, result_string; + char pwbuf[BUFSIZ]; + + ret = krb5_init_context (&context); + if (ret) { + warnx("failed kerberos initialisation: %s", + krb5_get_err_text(context, ret)); + return 1; + } + + krb5_get_init_creds_opt_init (&opt); + + krb5_get_init_creds_opt_set_tkt_life (&opt, 300); + krb5_get_init_creds_opt_set_forwardable (&opt, FALSE); + krb5_get_init_creds_opt_set_proxiable (&opt, FALSE); + + if(username != NULL) { + ret = krb5_parse_name (context, username, &principal); + if (ret) { + warnx("failed to parse principal: %s", + krb5_get_err_text(context, ret)); + return 1; + } + } else + principal = defprinc; + + ret = krb5_get_init_creds_password (context, + &cred, + principal, + NULL, + krb5_prompter_posix, + NULL, + 0, + "kadmin/changepw", + &opt); + + switch (ret) { + case 0: + break; + case KRB5_LIBOS_PWDINTR : + /* XXX */ + return 1; + case KRB5KRB_AP_ERR_BAD_INTEGRITY : + case KRB5KRB_AP_ERR_MODIFIED : + fprintf(stderr, "Password incorrect\n"); + return 1; + break; + default: + warnx("failed to get credentials: %s", + krb5_get_err_text(context, ret)); + return 1; + } + krb5_data_zero (&result_code_string); + krb5_data_zero (&result_string); + + /* XXX use getpass? It has a broken interface. */ + if(UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf), "New password: ", 1) != 0) + return 1; + + ret = krb5_set_password (context, &cred, pwbuf, NULL, + &result_code, + &result_code_string, + &result_string); + if (ret) + krb5_err (context, 1, ret, "krb5_set_password"); + + printf ("%s%s%.*s\n", krb5_passwd_result_to_string(context, result_code), + result_string.length > 0 ? " : " : "", + (int)result_string.length, + result_string.length > 0 ? (char *)result_string.data : ""); + + krb5_data_free (&result_code_string); + krb5_data_free (&result_string); + + krb5_free_cred_contents (context, &cred); + krb5_free_context (context); + return result_code; +} + +#endif /* USE_PAM */ diff --git a/usr.bin/passwd/local_passwd.c b/usr.bin/passwd/local_passwd.c new file mode 100644 index 000000000..5f161be88 --- /dev/null +++ b/usr.bin/passwd/local_passwd.c @@ -0,0 +1,345 @@ +/* $NetBSD: local_passwd.c,v 1.34 2010/03/02 16:19:13 gdt Exp $ */ + +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)local_passwd.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: local_passwd.c,v 1.34 2010/03/02 16:19:13 gdt Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +static uid_t uid; + +static char * +getnewpasswd(struct passwd *pw, int min_pw_len) +{ + int tries; + char *p, *t; + char buf[_PASSWORD_LEN+1], salt[_PASSWORD_LEN+1]; + char option[LINE_MAX], *key, *opt; + + (void)printf("Changing local password for %s.\n", pw->pw_name); + + if (uid && pw->pw_passwd[0] && + strcmp(crypt(getpass("Old password:"), pw->pw_passwd), + pw->pw_passwd)) { + errno = EACCES; + syslog(LOG_AUTH | LOG_NOTICE, + "user %s (UID %lu) failed to change the " + "local password of user %s: %m", + pw->pw_name, (unsigned long)uid, pw->pw_name); + pw_error(NULL, 1, 1); + } + + for (buf[0] = '\0', tries = 0;;) { + p = getpass("New password:"); + if (!*p) { + (void)printf("Password unchanged.\n"); + pw_error(NULL, 0, 0); + } + if (min_pw_len > 0 && (int)strlen(p) < min_pw_len) { + (void) printf("Password is too short.\n"); + continue; + } + if (strlen(p) <= 5 && ++tries < 2) { + (void)printf("Please enter a longer password.\n"); + continue; + } + for (t = p; *t && islower((unsigned char)*t); ++t); + if (!*t && ++tries < 2) { + (void)printf("Please don't use an all-lower case " + "password.\nUnusual capitalization, " + "control characters or digits are " + "suggested.\n"); + continue; + } + (void)strlcpy(buf, p, sizeof(buf)); + if (!strcmp(buf, getpass("Retype new password:"))) + break; + (void)printf("Mismatch; try again, EOF to quit.\n"); + } + + pw_getpwconf(option, sizeof(option), pw, "localcipher"); + opt = option; + key = strsep(&opt, ","); + if(pw_gensalt(salt, _PASSWORD_LEN, key, opt) == -1) { + warn("Couldn't generate salt"); + pw_error(NULL, 0, 0); + } + return(crypt(buf, salt)); +} + +#ifdef USE_PAM + +void +pwlocal_usage(const char *prefix) +{ + + (void) fprintf(stderr, "%s %s [-d files | -l] [user]\n", + prefix, getprogname()); +} + +void +pwlocal_process(const char *username, int argc, char **argv) +{ + struct passwd *pw; + struct passwd old_pw; + time_t old_change; + int pfd, tfd; + int min_pw_len = 0; + int pw_expiry = 0; + int ch; +#ifdef LOGIN_CAP + login_cap_t *lc; +#endif + + while ((ch = getopt(argc, argv, "l")) != -1) { + switch (ch) { + case 'l': + /* + * Aborb the -l that may have gotten us here. + */ + break; + + default: + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + switch (argc) { + case 0: + /* username already provided */ + break; + case 1: + username = argv[0]; + break; + default: + usage(); + /* NOTREACHED */ + } + + if (!(pw = getpwnam(username))) + errx(1, "unknown user %s", username); + + uid = getuid(); + if (uid && uid != pw->pw_uid) + errx(1, "%s", strerror(EACCES)); + + /* Save the old pw information for comparing on pw_copy(). */ + old_pw = *pw; + + /* + * Get class restrictions for this user, then get the new password. + */ +#ifdef LOGIN_CAP + if((lc = login_getclass(pw->pw_class)) != NULL) { + min_pw_len = (int) login_getcapnum(lc, "minpasswordlen", 0, 0); + pw_expiry = (int) login_getcaptime(lc, "passwordtime", 0, 0); + login_close(lc); + } +#endif +#if 0 + printf("AAA: pw_expiry = %x\n", pw_expiry); +#endif + pw->pw_passwd = getnewpasswd(pw, min_pw_len); + old_change = pw->pw_change; + pw->pw_change = pw_expiry ? pw_expiry + time(NULL) : 0; + + /* + * Now that the user has given us a new password, let us + * change the database. + */ + pw_init(); + tfd = pw_lock(0); + if (tfd < 0) { + warnx ("The passwd file is busy, waiting..."); + tfd = pw_lock(10); + if (tfd < 0) + errx(1, "The passwd file is still busy, " + "try again later."); + } + + pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0); + if (pfd < 0) + pw_error(_PATH_MASTERPASSWD, 1, 1); + + pw_copy(pfd, tfd, pw, &old_pw); + + if (pw_mkdb(username, old_change == pw->pw_change) < 0) + pw_error((char *)NULL, 0, 1); + + syslog(LOG_AUTH | LOG_INFO, + "user %s (UID %lu) successfully changed " + "the local password of user %s", + uid ? username : "root", (unsigned long)uid, username); +} + +#else /* ! USE_PAM */ + +static int force_local; + +int +local_init(progname) + const char *progname; +{ + force_local = 0; + return (0); +} + +int +local_arg(char ch, const char *arg) +{ + switch (ch) { + case 'l': + force_local = 1; + break; + default: + return(0); + } + return(1); +} + +int +local_arg_end() +{ + if (force_local) + return(PW_USE_FORCE); + return(PW_USE); +} + +void +local_end() +{ + /* NOOP */ +} + +int +local_chpw(uname) + const char *uname; +{ + struct passwd *pw; + struct passwd old_pw; + time_t old_change; + int pfd, tfd; + int min_pw_len = 0; + int pw_expiry = 0; +#ifdef LOGIN_CAP + login_cap_t *lc; +#endif + + if (!(pw = getpwnam(uname))) { + warnx("unknown user %s", uname); + return (1); + } + + uid = getuid(); + if (uid && uid != pw->pw_uid) { + warnx("%s", strerror(EACCES)); + return (1); + } + + /* Save the old pw information for comparing on pw_copy(). */ + old_pw = *pw; + + /* + * Get class restrictions for this user, then get the new password. + */ +#ifdef LOGIN_CAP + if((lc = login_getclass(pw->pw_class))) { + min_pw_len = (int) login_getcapnum(lc, "minpasswordlen", 0, 0); + pw_expiry = (int) login_getcaptime(lc, "passwordtime", 0, 0); + login_close(lc); + } +#endif +#if 0 + printf("pw_expiry = %x, pw->pw_expire = %x\n", pw_expiry, pw->pw_expire); +#endif + pw->pw_passwd = getnewpasswd(pw, min_pw_len); + old_change = pw->pw_change; + pw->pw_change = pw_expiry ? pw_expiry + time(NULL) : 0; + + /* + * Now that the user has given us a new password, let us + * change the database. + */ + pw_init(); + tfd = pw_lock(0); + if (tfd < 0) { + warnx ("The passwd file is busy, waiting..."); + tfd = pw_lock(10); + if (tfd < 0) + errx(1, "The passwd file is still busy, " + "try again later."); + } + + pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0); + if (pfd < 0) + pw_error(_PATH_MASTERPASSWD, 1, 1); + + pw_copy(pfd, tfd, pw, &old_pw); + + if (pw_mkdb(uname, old_change == pw->pw_change) < 0) + pw_error((char *)NULL, 0, 1); + + syslog(LOG_AUTH | LOG_INFO, + "user %s (UID %lu) successfully changed " + "the local password of user %s", + uid ? uname : "root", (unsigned long)uid, uname); + + return (0); +} + +#endif /* USE_PAM */ diff --git a/usr.bin/passwd/pam_passwd.c b/usr.bin/passwd/pam_passwd.c new file mode 100644 index 000000000..57cba406d --- /dev/null +++ b/usr.bin/passwd/pam_passwd.c @@ -0,0 +1,149 @@ +/* $NetBSD: pam_passwd.c,v 1.6 2010/03/09 16:14:08 joerg Exp $ */ + +/*- + * Copyright (c) 2002 Networks Associates Technologies, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by ThinkSec AS and + * NAI Labs, the Security Research Division of Network Associates, Inc. + * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the + * DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD: src/usr.bin/passwd/passwd.c,v 1.23 2003/04/18 21:27:09 nectar Exp $"); +#else +__RCSID("$NetBSD: pam_passwd.c,v 1.6 2010/03/09 16:14:08 joerg Exp $"); +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +#include +#include + +static pam_handle_t *pamh; +static struct pam_conv pamc = { + openpam_ttyconv, + NULL +}; + +#define pam_check(msg) \ +do { \ + if (pam_err != PAM_SUCCESS) { \ + warnx("%s: %s", (msg), pam_strerror(pamh, pam_err)); \ + goto end; \ + } \ +} while (/*CONSTCOND*/0) + +void +pwpam_process(const char *username, int argc, char **argv) +{ + int ch, pam_err; + char hostname[MAXHOSTNAMELEN + 1]; + + /* details about the invoking user for logging */ + const uid_t i_uid = getuid(); + const struct passwd *const i_pwd = getpwuid(i_uid); + const char *const i_username = (i_pwd && i_pwd->pw_name) + ? i_pwd->pw_name : "(null)"; + + while ((ch = getopt(argc, argv, "")) != -1) { + switch (ch) { + default: + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + switch (argc) { + case 0: + /* username already provided */ + break; + case 1: + username = argv[0]; + break; + default: + usage(); + /* NOTREACHED */ + } + + (void)printf("Changing password for %s.\n", username); + + /* initialize PAM -- always use the program name "passwd" */ + pam_err = pam_start("passwd", username, &pamc, &pamh); + if (pam_err != PAM_SUCCESS) + errx(1, "unable to start PAM session: %s", + pam_strerror(NULL, pam_err)); + + pam_err = pam_set_item(pamh, PAM_TTY, ttyname(STDERR_FILENO)); + pam_check("unable to set TTY"); + + (void)gethostname(hostname, sizeof hostname); + pam_err = pam_set_item(pamh, PAM_RHOST, hostname); + pam_check("unable to set RHOST"); + + pam_err = pam_set_item(pamh, PAM_RUSER, getlogin()); + pam_check("unable to set RUSER"); + + /* set new password */ + pam_err = pam_chauthtok(pamh, 0); + if (pam_err != PAM_SUCCESS) { + if (pam_err == PAM_PERM_DENIED) { + syslog(LOG_AUTH | LOG_NOTICE, + "user %s (UID %lu) failed to change the " + "PAM authentication token of user %s: %s", + i_username, (unsigned long)i_uid, username, + pam_strerror(pamh, pam_err)); + } + printf("Unable to change auth token: %s\n", + pam_strerror(pamh, pam_err)); + } else { + syslog(LOG_AUTH | LOG_INFO, + "user %s (UID %lu) successfully changed the " + "PAM authentication token of user %s", + i_username, (unsigned long)i_uid, username); + } + + end: + pam_end(pamh, pam_err); + if (pam_err == PAM_SUCCESS) + return; + exit(1); +} diff --git a/usr.bin/passwd/passwd.1 b/usr.bin/passwd/passwd.1 new file mode 100644 index 000000000..3f64f1fd0 --- /dev/null +++ b/usr.bin/passwd/passwd.1 @@ -0,0 +1,142 @@ +.\" $NetBSD: passwd.1,v 1.28 2006/03/07 01:52:09 hubertf Exp $ +.\" +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)passwd.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd February 25, 2005 +.Dt PASSWD 1 +.Os +.Sh NAME +.Nm passwd +.Nd modify a user's password +.Sh SYNOPSIS +.Nm +.Op Ar user +.Nm +.Oo Fl d Ar files | Fl l Oc +.Op Ar user +.Nm +.Oo Fl d Ar nis | Fl y Oc +.Op Ar user +.Nm +.Oo Fl d Ar krb5 | Fl k Oc +.Op Ar principal +.Sh DESCRIPTION +.Nm +changes the user's password. +First, the user is +prompted for their current password. +If the current password is correctly typed, a new password is +requested. +The new password must be entered twice to avoid typing errors. +.Pp +The new password should be at least six characters long and not +purely alphabetic. +Its total length must be less than +.Dv _PASSWORD_LEN +(currently 128 characters). +Numbers, upper case letters and meta characters +are encouraged. +.Pp +All options may not be available on all systems. +.Bl -tag -width flag +.It Fl d Ar database +This option specifies the password database that should be updated. The +following databases are supported: +.Bl -tag -width files +.It files +This specifies that the password change should be applied to the local +password file. +When changing only the local password, +.Nm +uses +.Xr pwd_mkdb 8 +to update the password databases. +.It nis +This specifies that the password change should be applied to the NIS +password database. +The +.Xr rpc.yppasswdd 8 +daemon should be running on the master NIS server. +.It krb5 +This specifies that the user's Kerberos 5 password should be changed. +The host must be configured to use Kerberos. +See +.Xr krb5.conf 5 . +.El +.It Fl l +This is the equivalent of +.Fl d Ar files . +.It Fl y +This is the equivalent of +.Fl d Ar nis . +.It Fl k +This is the equivalent of +.Fl d Ar krb5 . +.El +.Pp +If a password database is not specified, +.Nm +will change the password database as determined by the +Pluggable Authentication Module +.Pq PAM +library. +.Pp +The type of cipher used to encrypt the password depends on the configuration +in +.Xr passwd.conf 5 . +It can be different for local and NIS passwords. +.Sh FILES +.Bl -tag -width /etc/master.passwd -compact +.It Pa /etc/master.passwd +The user database +.It Pa /etc/passwd +A Version 7 format password file +.It Pa /etc/passwd.XXXXXX +Temporary copy of the password file +.El +.Sh SEE ALSO +.Xr chpass 1 , +.Xr login 1 , +.Xr pwhash 1 , +.Xr passwd 5 , +.Xr passwd.conf 5 , +.Xr pam 8 , +.Xr pwd_mkdb 8 , +.Xr vipw 8 +.Rs +.%A Robert Morris +.%A Ken Thompson +.%T "UNIX password security" +.Re +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . diff --git a/usr.bin/passwd/passwd.c b/usr.bin/passwd/passwd.c new file mode 100644 index 000000000..0da7e9bbf --- /dev/null +++ b/usr.bin/passwd/passwd.c @@ -0,0 +1,405 @@ +/* $NetBSD: passwd.c,v 1.30 2009/04/17 20:25:08 dyoung Exp $ */ + +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)passwd.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: passwd.c,v 1.30 2009/04/17 20:25:08 dyoung Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +#ifdef USE_PAM + +static void global_usage(const char *); + +static const struct pw_module_s { + const char *argv0; + const char *dbname; + char compat_opt; + void (*pw_usage)(const char *); + void (*pw_process)(const char *, int, char **); +} pw_modules[] = { + /* "files" -- local password database */ + { NULL, "files", 'l', pwlocal_usage, pwlocal_process }, +#ifdef YP + /* "nis" -- YP/NIS password database */ + { NULL, "nis", 'y', pwyp_usage, pwyp_process }, + { "yppasswd", NULL, 0, pwyp_argv0_usage, pwyp_process }, +#endif +#ifdef KERBEROS5 + /* "krb5" -- Kerberos 5 password database */ + { NULL, "krb5", 'k', pwkrb5_usage, pwkrb5_process }, + { "kpasswd", NULL, 0, pwkrb5_argv0_usage, pwkrb5_process }, +#endif + /* default -- use whatever PAM decides */ + { NULL, NULL, 0, NULL, pwpam_process }, + + { NULL, NULL, 0, NULL, NULL } +}; + +static const struct pw_module_s *personality; + +static void +global_usage(const char *prefix) +{ + const struct pw_module_s *pwm; + + (void) fprintf(stderr, "%s %s [user]\n", prefix, getprogname()); + for (pwm = pw_modules; pwm->pw_process != NULL; pwm++) { + if (pwm->argv0 == NULL && pwm->pw_usage != NULL) + (*pwm->pw_usage)(" "); + } +} + +void +usage(void) +{ + + if (personality != NULL && personality->pw_usage != NULL) + (*personality->pw_usage)("usage:"); + else + global_usage("usage:"); + exit(1); +} + +int +main(int argc, char **argv) +{ + const struct pw_module_s *pwm; + const char *username; + int ch, i; + char opts[16]; + + /* Build opts string from module compat_opts */ + i = 0; + opts[i++] = 'd'; + opts[i++] = ':'; + for (pwm = pw_modules; pwm->pw_process != NULL; pwm++) { + if (pwm->compat_opt != 0) + opts[i++] = pwm->compat_opt; + } + opts[i++] = '\0'; + + /* First, look for personality based on argv[0]. */ + for (pwm = pw_modules; pwm->pw_process != NULL; pwm++) { + if (pwm->argv0 != NULL && + strcmp(pwm->argv0, getprogname()) == 0) + goto got_personality; + } + + /* Try based on compat_opt or -d. */ + for (ch = 0, pwm = pw_modules; pwm->pw_process != NULL; pwm++) { + if (pwm->argv0 == NULL && pwm->dbname == NULL && + pwm->compat_opt == 0) { + /* + * We have reached the default personality case. + * Make sure the user didn't provide a bogus + * personality name. + */ + if (ch == 'd') + usage(); + break; + } + + ch = getopt(argc, argv, opts); + if (ch == '?') + usage(); + + if (ch == 'd' && pwm->dbname != NULL && + strcmp(pwm->dbname, optarg) == 0) { + /* + * "passwd -d dbname" matches; this is our + * chosen personality. + */ + break; + } + + if (pwm->compat_opt != 0 && ch == pwm->compat_opt) { + /* + * Legacy "passwd -l" or similar matches; this + * is our chosen personality. + */ + break; + } + + /* Reset getopt() and go around again. */ + optind = 1; + optreset = 1; + } + + got_personality: + personality = pwm; + + /* + * At this point, optind should be either 1 ("passwd"), + * 2 ("passwd -l"), or 3 ("passwd -d files"). Consume + * these arguments and reset getopt() for the modules to use. + */ + assert(optind >= 1 && optind <= 3); + argc -= optind; + argv += optind; + optind = 0; + optreset = 1; + + username = getlogin(); + if (username == NULL) + errx(1, "who are you ??"); + + (*personality->pw_process)(username, argc, argv); + return 0; +} + +#else /* ! USE_PAM */ + +static struct pw_module_s { + const char *argv0; + const char *args; + const char *usage; + int (*pw_init) __P((const char *)); + int (*pw_arg) __P((char, const char *)); + int (*pw_arg_end) __P((void)); + void (*pw_end) __P((void)); + + int (*pw_chpw) __P((const char*)); + int invalid; +#define INIT_INVALID 1 +#define ARG_INVALID 2 + int use_class; +} pw_modules[] = { +#ifdef KERBEROS5 + { NULL, "5ku:", "[-5] [-k] [-u principal]", + krb5_init, krb5_arg, krb5_arg_end, krb5_end, krb5_chpw, 0, 0 }, + { "kpasswd", "5ku:", "[-5] [-k] [-u principal]", + krb5_init, krb5_arg, krb5_arg_end, krb5_end, krb5_chpw, 0, 0 }, +#endif +#ifdef YP + { NULL, "y", "[-y]", + yp_init, yp_arg, yp_arg_end, yp_end, yp_chpw, 0, 0 }, + { "yppasswd", "", "[-y]", + yp_init, yp_arg, yp_arg_end, yp_end, yp_chpw, 0, 0 }, +#endif + /* local */ + { NULL, "l", "[-l]", + local_init, local_arg, local_arg_end, local_end, local_chpw, 0, 0 }, + + /* terminator */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +static void +usage(void) +{ + int i; + + fprintf(stderr, "usage:\n"); + for (i = 0; pw_modules[i].pw_init != NULL; i++) + if (! (pw_modules[i].invalid & INIT_INVALID)) + fprintf(stderr, "\t%s %s [user]\n", getprogname(), + pw_modules[i].usage); + exit(1); +} + +int +main(int argc, char **argv) +{ + int ch; + char *username; + char optstring[64]; /* if we ever get more than 64 args, shoot me. */ + const char *curopt, *oopt; + int i, j; + int valid; + int use_always; + + /* allow passwd modules to do argv[0] specific processing */ + use_always = 0; + valid = 0; + for (i = 0; pw_modules[i].pw_init != NULL; i++) { + pw_modules[i].invalid = 0; + if (pw_modules[i].argv0) { + /* + * If we have a module that matches this progname, be + * sure that no modules but those that match this + * progname can be used. If we have a module that + * matches against a particular progname, but does NOT + * match this one, don't use that module. + */ + if ((strcmp(getprogname(), pw_modules[i].argv0) == 0) && + use_always == 0) { + for (j = 0; j < i; j++) { + pw_modules[j].invalid |= INIT_INVALID; + (*pw_modules[j].pw_end)(); + } + use_always = 1; + } else if (use_always == 0) + pw_modules[i].invalid |= INIT_INVALID; + } else if (use_always) + pw_modules[i].invalid |= INIT_INVALID; + + if (pw_modules[i].invalid) + continue; + + pw_modules[i].invalid |= + (*pw_modules[i].pw_init)(getprogname()) ? + /* zero on success, non-zero on error */ + INIT_INVALID : 0; + + if (! pw_modules[i].invalid) + valid = 1; + } + + if (valid == 0) + errx(1, "Can't change password."); + + /* Build the option string from the individual modules' option + * strings. Note that two modules can share a single option + * letter. */ + optstring[0] = '\0'; + j = 0; + for (i = 0; pw_modules[i].pw_init != NULL; i++) { + if (pw_modules[i].invalid) + continue; + + curopt = pw_modules[i].args; + while (*curopt != '\0') { + if ((oopt = strchr(optstring, *curopt)) == NULL) { + optstring[j++] = *curopt; + if (curopt[1] == ':') { + curopt++; + optstring[j++] = *curopt; + } + optstring[j] = '\0'; + } else if ((oopt[1] == ':' && curopt[1] != ':') || + (oopt[1] != ':' && curopt[1] == ':')) { + errx(1, "NetBSD ERROR! Different password " + "modules have two different ideas about " + "%c argument format.", curopt[0]); + } + curopt++; + } + } + + while ((ch = getopt(argc, argv, optstring)) != -1) + { + valid = 0; + for (i = 0; pw_modules[i].pw_init != NULL; i++) { + if (pw_modules[i].invalid) + continue; + if ((oopt = strchr(pw_modules[i].args, ch)) != NULL) { + j = (oopt[1] == ':') ? + ! (*pw_modules[i].pw_arg)(ch, optarg) : + ! (*pw_modules[i].pw_arg)(ch, NULL); + if (j != 0) + pw_modules[i].invalid |= ARG_INVALID; + if (pw_modules[i].invalid) + (*pw_modules[i].pw_end)(); + } else { + /* arg doesn't match this module */ + pw_modules[i].invalid |= ARG_INVALID; + (*pw_modules[i].pw_end)(); + } + if (! pw_modules[i].invalid) + valid = 1; + } + if (! valid) { + usage(); + exit(1); + } + } + + /* select which module to use to actually change the password. */ + use_always = 0; + valid = 0; + for (i = 0; pw_modules[i].pw_init != NULL; i++) + if (! pw_modules[i].invalid) { + pw_modules[i].use_class = (*pw_modules[i].pw_arg_end)(); + if (pw_modules[i].use_class != PW_DONT_USE) + valid = 1; + if (pw_modules[i].use_class == PW_USE_FORCE) + use_always = 1; + } + + + if (! valid) + /* hang the DJ */ + errx(1, "No valid password module specified."); + + argc -= optind; + argv += optind; + + username = getlogin(); + if (username == NULL) + errx(1, "who are you ??"); + + switch(argc) { + case 0: + break; + case 1: + username = argv[0]; + break; + default: + usage(); + exit(1); + } + + /* allow for fallback to other chpw() methods. */ + for (i = 0; pw_modules[i].pw_init != NULL; i++) { + if (pw_modules[i].invalid) + continue; + if ((use_always && pw_modules[i].use_class == PW_USE_FORCE) || + (!use_always && pw_modules[i].use_class == PW_USE)) { + valid = (*pw_modules[i].pw_chpw)(username); + (*pw_modules[i].pw_end)(); + if (valid >= 0) + exit(valid); + /* return value < 0 indicates continuation. */ + } + } + exit(1); +} + +#endif /* USE_PAM */ diff --git a/usr.bin/passwd/yp_passwd.c b/usr.bin/passwd/yp_passwd.c new file mode 100644 index 000000000..0d11ff1a3 --- /dev/null +++ b/usr.bin/passwd/yp_passwd.c @@ -0,0 +1,468 @@ +/* $NetBSD: yp_passwd.c,v 1.35 2010/09/08 13:58:46 christos Exp $ */ + +/* + * Copyright (c) 1988, 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)local_passwd.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: yp_passwd.c,v 1.35 2010/09/08 13:58:46 christos Exp $"); +#endif +#endif /* not lint */ + +#ifdef YP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "extern.h" + +#define passwd yp_passwd_rec +#include +#undef passwd + +#ifndef _PASSWORD_LEN +#define _PASSWORD_LEN PASS_MAX +#endif + +static uid_t uid; +static char *domain; + +static void +pwerror(const char *name, int show_err, int eval) +{ + + if (show_err) + warn("%s", name); + errx(eval, "NIS passwd database unchanged"); +} + +static char * +getnewpasswd(struct passwd *pw, char **old_pass) +{ + int tries; + const char *p, *t; + char *result; + static char buf[_PASSWORD_LEN + 1]; + char salt[_PASSWORD_LEN + 1]; + char option[LINE_MAX], *key, *opt; + + (void)printf("Changing NIS password for %s.\n", pw->pw_name); + + if (old_pass) { + *old_pass = NULL; + + if (pw->pw_passwd[0]) { + if (strcmp(crypt(p = getpass("Old password:"), + pw->pw_passwd), pw->pw_passwd)) { + (void)printf("Sorry.\n"); + pwerror(NULL, 0, 1); + } + } else { + p = ""; + } + + *old_pass = strdup(p); + if (!*old_pass) { + (void)printf("not enough core.\n"); + pwerror(NULL, 0, 1); + } + } + for (buf[0] = '\0', tries = 0;;) { + p = getpass("New password:"); + if (!*p) { + (void)printf("Password unchanged.\n"); + pwerror(NULL, 0, 0); + } + if (strlen(p) <= 5 && ++tries < 2) { + (void)printf("Please enter a longer password.\n"); + continue; + } + for (t = p; *t && islower((unsigned char)*t); ++t); + if (!*t && ++tries < 2) { + (void)printf("Please don't use an all-lower case " + "password.\nUnusual capitalization, " + "control characters or digits are " + "suggested.\n"); + continue; + } + (void)strlcpy(buf, p, sizeof(buf)); + if (!strcmp(buf, getpass("Retype new password:"))) + break; + (void)printf("Mismatch; try again, EOF to quit.\n"); + } + + pw_getpwconf(option, sizeof(option), pw, "ypcipher"); + opt = option; + key = strsep(&opt, ","); + if (pw_gensalt(salt, _PASSWORD_LEN, key, opt) == -1) { + warn("Couldn't generate salt"); + pwerror(NULL, 0, 0); + } + result = strdup(crypt(buf, salt)); + if (!result) { + (void)printf("not enough core.\n"); + pwerror(NULL, 0, 0); + } + return result; +} + +static void +makeypp(struct yppasswd *ypp, struct passwd *pw) +{ + /* prompt for new password */ + ypp->newpw.pw_passwd = getnewpasswd(pw, &ypp->oldpass); + + /* tell rpc.yppasswdd */ + ypp->newpw.pw_name = estrdup(pw->pw_name); + ypp->newpw.pw_uid = pw->pw_uid; + ypp->newpw.pw_gid = pw->pw_gid; + ypp->newpw.pw_gecos = estrdup(pw->pw_gecos); + ypp->newpw.pw_dir = estrdup(pw->pw_dir); + ypp->newpw.pw_shell = estrdup(pw->pw_shell); +} + +static int +ypgetpwnam(const char *nam, struct passwd *pwd) +{ + char *val; + int reason, vallen, namlen = (int)strlen(nam); + int flags; + int ok; + + flags = ok = 0; + val = NULL; + reason = yp_match(domain, "master.passwd.byname", nam, namlen, + &val, &vallen); + if (reason == YPERR_MAP) { + reason = yp_match(domain, "passwd.byname", nam, namlen, + &val, &vallen); + flags = _PASSWORD_OLDFMT; + } + if (reason != 0) + goto out; + + if (pw_scan(val, pwd, &flags) == 0) + goto out; + + ok = 1; + val = NULL; /* Don't free the memory, it is still in use */ +out: + if (val) + free(val); + return ok; +} + +#ifdef USE_PAM + +void +pwyp_usage(const char *prefix) +{ + + (void)fprintf(stderr, "%s %s [-d nis | -y] [user]\n", + prefix, getprogname()); +} + +void +pwyp_argv0_usage(const char *prefix) +{ + + (void)fprintf(stderr, "%s %s [user]\n", + prefix, getprogname()); +} + +void +pwyp_process(const char *username, int argc, char **argv) +{ + char *master; + int ch, r, rpcport, status; + enum clnt_stat yr; + struct yppasswd ypp; + struct passwd pwb, pwb2, *pw; + char pwbuf[1024]; + struct timeval tv; + CLIENT *client; + + while ((ch = getopt(argc, argv, "y")) != -1) { + switch (ch) { + case 'y': + /* + * Abosrb the -y that may have gotten us here. + */ + break; + + default: + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + switch (argc) { + case 0: + /* username already provided */ + break; + case 1: + username = argv[0]; + break; + default: + usage(); + /*NOTREACHED*/ + } + + if (_yp_check(NULL) == 0) { + /* can't use YP. */ + errx(EXIT_FAILURE, "NIS not in use."); + } + + uid = getuid(); + + /* + * Get local domain + */ + if ((r = yp_get_default_domain(&domain)) != 0) + errx(EXIT_FAILURE, "Can't get local NIS domain (%s)", + yperr_string(r)); + + /* + * Find the host for the passwd map; it should be running + * the daemon. + */ + if ((r = yp_master(domain, "passwd.byname", &master)) != 0) + errx(EXIT_FAILURE, "Can't find the master NIS server (%s)", + yperr_string(r)); + + /* + * Ask the portmapper for the port of the daemon. + */ + if ((rpcport = getrpcport(master, YPPASSWDPROG, + YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) + errx(EXIT_FAILURE, "Master NIS server not running yppasswd " + "daemon"); + + /* + * Be sure the port is privileged + */ + if (rpcport >= IPPORT_RESERVED) + errx(EXIT_FAILURE, "Yppasswd daemon is on an invalid port"); + + /* Bail out if this is a local (non-yp) user, */ + /* then get user's login identity */ + if (!ypgetpwnam(username, &pwb) || + getpwnam_r(username, &pwb2, pwbuf, sizeof(pwbuf), &pw) || + pw == NULL) + errx(EXIT_FAILURE, "NIS unknown user %s", username); + + if (uid && uid != pwb.pw_uid) { + errno = EACCES; + err(EXIT_FAILURE, "You may only change your own password"); + } + + makeypp(&ypp, &pwb); + + client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp"); + if (client == NULL) + errx(EXIT_FAILURE, "Cannot contact yppasswdd on %s (%s)", + master, yperr_string(YPERR_YPBIND)); + + client->cl_auth = authunix_create_default(); + tv.tv_sec = 2; + tv.tv_usec = 0; + yr = clnt_call(client, YPPASSWDPROC_UPDATE, + xdr_yppasswd, &ypp, xdr_int, &status, tv); + if (yr != RPC_SUCCESS) + errx(EXIT_FAILURE, "RPC to yppasswdd failed (%s)", + clnt_sperrno(yr)); + else if (status) + printf("Couldn't change NIS password.\n"); + else + printf("The NIS password has been changed on %s, %s\n", + master, "the master NIS passwd server."); +} + +#else /* ! USE_PAM */ + +static int yflag; + +int +yp_init(progname) + const char *progname; +{ + int yppwd; + + if (strcmp(progname, "yppasswd") == 0) { + yppwd = 1; + } else + yppwd = 0; + yflag = 0; + if (_yp_check(NULL) == 0) { + /* can't use YP. */ + if (yppwd) + errx(EXIT_FAILURE, "NIS not in use"); + return -1; + } + return 0; +} + +int +yp_arg(char ch, const char *arg) +{ + switch (ch) { + case 'y': + yflag = 1; + break; + default: + return 0; + } + return 1; +} + +int +yp_arg_end(void) +{ + if (yflag) + return PW_USE_FORCE; + return PW_USE; +} + +void +yp_end(void) +{ + /* NOOP */ +} + +int +yp_chpw(const char *username) +{ + char *master; + int r, rpcport, status; + enum clnt_stat yr; + struct yppasswd ypp; + struct passwd *pw, pwb; + char pwbuf[1024]; + struct timeval tv; + CLIENT *client; + + uid = getuid(); + + /* + * Get local domain + */ + if ((r = yp_get_default_domain(&domain)) != 0) + errx(EXIT_FAILURE, "can't get local NIS domain. Reason: %s", + yperr_string(r)); + + /* + * Find the host for the passwd map; it should be running + * the daemon. + */ + if ((r = yp_master(domain, "passwd.byname", &master)) != 0) { + warnx("can't find the master NIS server. Reason: %s", + yperr_string(r)); + /* continuation */ + return -1; + } + + /* + * Ask the portmapper for the port of the daemon. + */ + if ((rpcport = getrpcport(master, YPPASSWDPROG, + YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) { + warnx("Master NIS server not running yppasswd daemon"); + /* continuation */ + return -1; + } + + /* + * Be sure the port is privileged + */ + if (rpcport >= IPPORT_RESERVED) + errx(EXIT_FAILURE, "Yppasswd daemon is on an invalid port"); + + /* Bail out if this is a local (non-yp) user, */ + /* then get user's login identity */ + if (!ypgetpwnam(username, pw = &pwb) || + getpwnam_r(username, &pwb, pwbuf, sizeof(pwbuf), &pw) || + pw == NULL) { + warnx("NIS unknown user %s", username); + /* continuation */ + return -1; + } + + if (uid && uid != pw->pw_uid) { + errno = EACCES; + err(EXIT_FAILURE, "You may only change your own password"); + } + + makeypp(&ypp, pw); + + client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp"); + if (client == NULL) { + warnx("Cannot contact yppasswdd on %s (%s)", + master, yperr_string(YPERR_YPBIND)); + return YPERR_YPBIND; + } + + client->cl_auth = authunix_create_default(); + tv.tv_sec = 2; + tv.tv_usec = 0; + yr = clnt_call(client, YPPASSWDPROC_UPDATE, + xdr_yppasswd, &ypp, xdr_int, &status, tv); + if (yr != RPC_SUCCESS) + errx(EXIT_FAILURE, "RPC to yppasswdd failed (%s)", + clnt_sperrno(yr)); + else if (status) + printf("Couldn't change NIS password.\n"); + else + printf("The NIS password has been changed on %s, %s\n", + master, "the master NIS passwd server."); + return 0; +} + +#endif /* USE_PAM */ + +#endif /* YP */ diff --git a/usr.bin/passwd/yppasswd.1 b/usr.bin/passwd/yppasswd.1 new file mode 100644 index 000000000..1ba259031 --- /dev/null +++ b/usr.bin/passwd/yppasswd.1 @@ -0,0 +1,48 @@ +.\" $NetBSD: yppasswd.1,v 1.3 2008/04/30 13:11:01 martin Exp $ +.\" +.\" Copyright (c) 2005 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Jason R. Thorpe. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd February 25, 2005 +.Dt YPPASSWD 1 +.Os +.Sh NAME +.Nm yppasswd +.Nd modify a user's NIS password +.Sh SYNOPSIS +.Nm +.Op Ar user +.Sh DESCRIPTION +.Nm +changes the user's NIS password. +.Pp +The +.Nm +command is deprecated. +See +.Xr passwd 1 +for more information. diff --git a/usr.bin/top/top.c b/usr.bin/top/top.c index 4043c3ae3..f9be0a12b 100644 --- a/usr.bin/top/top.c +++ b/usr.bin/top/top.c @@ -3,7 +3,9 @@ /* Modified for ProcFS by Alen Stojanov and David van Moolenbroek */ #define _MINIX 1 +#ifndef __NBSD_LIBC #define _POSIX_SOURCE 1 +#endif #include #include diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile new file mode 100644 index 000000000..7601387b9 --- /dev/null +++ b/usr.sbin/Makefile @@ -0,0 +1,8 @@ +# Makefile for usr.bin + +.include + +# NetBSD imports +SUBDIR= pwd_mkdb user vipw + +.include diff --git a/usr.sbin/Makefile.inc b/usr.sbin/Makefile.inc new file mode 100644 index 000000000..873a29712 --- /dev/null +++ b/usr.sbin/Makefile.inc @@ -0,0 +1,3 @@ +.include + +BINDIR?= /usr/sbin diff --git a/usr.sbin/pwd_mkdb/Makefile b/usr.sbin/pwd_mkdb/Makefile new file mode 100644 index 000000000..2dfe37aef --- /dev/null +++ b/usr.sbin/pwd_mkdb/Makefile @@ -0,0 +1,19 @@ +# from: @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $NetBSD: Makefile,v 1.19 2009/01/14 23:18:57 christos Exp $ + +.include + +PROG= pwd_mkdb +MAN= pwd_mkdb.8 +.if defined(__MINIX) +CPPFLAGS+= -I${NETBSDSRCDIR}/lib/nbsd_libc/include +.else +CPPFLAGS+= -I${NETBSDSRCDIR}/lib/libc/include +.endif + +.ifndef HOSTPROG +LDADD+= -lutil +DPADD+= ${LIBUTIL} +.endif + +.include diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.8 b/usr.sbin/pwd_mkdb/pwd_mkdb.8 new file mode 100644 index 000000000..5e3ab7c95 --- /dev/null +++ b/usr.sbin/pwd_mkdb/pwd_mkdb.8 @@ -0,0 +1,219 @@ +.\" $NetBSD: pwd_mkdb.8,v 1.28 2010/08/18 10:00:49 wiz Exp $ +.\" +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)pwd_mkdb.8 8.2 (Berkeley) 4/27/95 +.\" +.Dd August 18, 2010 +.Dt PWD_MKDB 8 +.Os +.Sh NAME +.Nm pwd_mkdb +.Nd generate the password databases +.Sh SYNOPSIS +.Nm +.Op Fl BLlpsvw +.Op Fl c Ar cachesize +.Op Fl d Ar directory +.Op Fl u Ar username +.Op Fl V Ar version +.Ar file +.Sh DESCRIPTION +.Nm +creates +.Xr db 3 +style secure and insecure databases for the specified file. +These databases are then installed into +.Dq Pa /etc/spwd.db +and +.Dq Pa /etc/pwd.db +respectively. +The file is installed into +.Dq Pa /etc/master.passwd . +The file must be in the correct format (see +.Xr passwd 5 ) . +It is important to note that the format used in this system is +different from the historic Version 7 style format. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl B +Store data in big-endian format (see also +.Fl L ) . +.It Fl c Ar cachesize +Specify the size of the memory cache in megabytes used by the +hashing library. +On systems with a large user base, a small cache size can lead to +prohibitively long database file rebuild times. +As a rough guide, the memory usage of +.Nm +in megabytes will be a little bit more than twice the figure +specified here. +If unspecified, this value will be calculated based on the size of +the input file up to a maximum of 8 megabytes. +.It Fl d Ar directory +Change the root directory of the generated files from +.Dq Pa / +to +.Ar directory . +.It Fl L +Store data in little-endian format (see also +.Fl B ) . +.It Fl l +Use +.Xr syslog 3 +to report errors. +.It Fl p +Create a Version 7 style password file and install it into +.Dq Pa /etc/passwd . +.It Fl s +Update the secure database only. +This is useful when only encrypted passwords have changed. +This option negates the effect of any +.Fl p +option. +.It Fl u Ar name +Don't re-build the database files, but instead modify or add entries +for the specified user only. +This option may only be used when the line number and user name in +the password file have not changed, or when adding a new user from +the last line in the password file. +.It Fl V Ar version +Upgrade or downgrade databases to the numbered version. +Version +.Dv 0 +is the old format (up to and including +.Nx 5.0 ) +with the 4 byte time fields and version +.Dv 1 +is the new format with the 8 byte time fields (greater than +.Nx 5.0 ) . +.Nx 5.0 +cannot read version +.Dv 1 +databases. +All versions above +.Nx 5.0 +can read and write both version +.Dv 0 +and version +.Dv 1 +databases. +By default the databases stay in the version they were before the command +was run. +.It Fl v +Mention when a version change occurs. +.It Fl w +Print a warning if the system is using old style databases. +.El +.Pp +The two databases differ in that the secure version contains the user's +encrypted password and the insecure version has an asterisk +.Pq Dq * . +.Pp +The databases are used by the C library password routines (see +.Xr getpwent 3 ) . +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /etc/master.passwd +The current password file. +.It Pa /etc/passwd +A Version 7 format password file. +.It Pa /etc/pwd.db +The insecure password database file. +.It Pa /etc/pwd.db.tmp +A temporary file. +.It Pa /etc/spwd.db +The secure password database file. +.It Pa /etc/spwd.db.tmp +A temporary file. +.El +.Sh EXIT STATUS +.Nm +exits zero on success, non-zero on failure. +.Sh COMPATIBILITY +Previous versions of the system had a program similar to +.Nm +which built +.Em dbm +style databases for the password file but depended on the calling programs +to install them. +The program was renamed in order that previous users of the program +not be surprised by the changes in functionality. +.Sh SEE ALSO +.Xr chpass 1 , +.Xr passwd 1 , +.Xr pwhash 1 , +.Xr db 3 , +.Xr getpwent 3 , +.Xr pw_mkdb 3 , +.Xr syslog 3 , +.Xr passwd 5 , +.Xr useradd 8 , +.Xr userdel 8 , +.Xr usermod 8 , +.Xr vipw 8 +.Sh BUGS +Because of the necessity for atomic update of the password files, +.Nm +uses +.Xr rename 2 +to install them. +This, however, requires that the file specified on the command line live +on the same file system as the +.Dq Pa /etc +directory. +.Pp +There are the obvious races with multiple people running +.Nm +on different password files at the same time. +The front-ends to +.Xr chpass 1 , +.Xr passwd 1 , +.Xr useradd 8 , +.Xr userdel 8 , +.Xr usermod 8 , +and +.Xr vipw 8 +handle the locking necessary to avoid this problem. +.Pp +The database files are copied when the +.Fl u +option is used. +Real locking would make this unnecessary. +.Pp +Although the DB format is endian-transparent, the data stored in +the DB is not. +Also, the format doesn't lend itself to insertion or removal of +records from arbitrary locations in the password file. +This is difficult to fix without breaking compatibility. +.Pp +Using the +.Fl u +option on a system where multiple users share the same UID can have +unexpected results. diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.c b/usr.sbin/pwd_mkdb/pwd_mkdb.c new file mode 100644 index 000000000..d07f06ab4 --- /dev/null +++ b/usr.sbin/pwd_mkdb/pwd_mkdb.c @@ -0,0 +1,1065 @@ +/* $NetBSD: pwd_mkdb.c,v 1.53 2011/01/04 10:01:51 wiz Exp $ */ + +/* + * Copyright (c) 2000, 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Portions Copyright(C) 1994, Jason Downs. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if !defined(lint) +__COPYRIGHT("@(#) Copyright (c) 2000, 2009\ + The NetBSD Foundation, Inc. All rights reserved.\ + Copyright (c) 1991, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +__RCSID("$NetBSD: pwd_mkdb.c,v 1.53 2011/01/04 10:01:51 wiz Exp $"); +#endif /* not lint */ + +#if HAVE_NBTOOL_CONFIG_H +#include "compat_pwd.h" +#else +#include +#endif + +#include +#include +#include + +#ifndef HAVE_NBTOOL_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CACHESIZE 8*1024*1024 +#define MIN_CACHESIZE 2*1024*1024 + +#define PERM_INSECURE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) +#define PERM_SECURE (S_IRUSR | S_IWUSR) + +#if HAVE_NBTOOL_CONFIG_H +static const char __yp_token[] = "__YP!"; +#else +/* Pull this out of the C library. */ +extern const char __yp_token[]; +#endif + +static HASHINFO openinfo = { + 4096, /* bsize */ + 32, /* ffactor */ + 256, /* nelem */ + 0, /* cachesize */ + NULL, /* hash() */ + 0 /* lorder */ +}; + +#define FILE_INSECURE 0x01 +#define FILE_SECURE 0x02 +#define FILE_ORIG 0x04 + + +struct pwddb { + DB *db; + char dbname[MAX(MAXPATHLEN, LINE_MAX * 2)]; + const char *fname; + uint32_t rversion; + uint32_t wversion; +}; + +static char *pname; /* password file name */ +static char prefix[MAXPATHLEN]; +static char oldpwdfile[MAX(MAXPATHLEN, LINE_MAX * 2)]; +static int lorder = BYTE_ORDER; +static int logsyslog; +static int clean; +static int verbose; +static int warning; +static struct pwddb sdb, idb; + + +void bailout(void) __attribute__((__noreturn__)); +void cp(const char *, const char *, mode_t); +void deldbent(struct pwddb *, int, void *); +void mkpw_error(const char *, ...); +void mkpw_warning(const char *, ...); +int getdbent(struct pwddb *, int, void *, struct passwd **); +void inconsistency(void); +void install(const char *, const char *); +int main(int, char **); +void putdbents(struct pwddb *, struct passwd *, const char *, int, int, + u_int, u_int); +void putyptoken(struct pwddb *); +void rm(const char *); +int scan(FILE *, struct passwd *, int *, int *); +void usage(void) __attribute__((__noreturn__)); +void wr_error(const char *); +uint32_t getversion(const char *); +void setversion(struct pwddb *); + +#ifndef __lint__ +#define SWAP(sw) \ + ((sizeof(sw) == 2 ? (typeof(sw))bswap16((uint16_t)sw) : \ + (sizeof(sw) == 4 ? (typeof(sw))bswap32((uint32_t)sw) : \ + (sizeof(sw) == 8 ? (typeof(sw))bswap64((uint64_t)sw) : (abort(), 0))))) +#else +#define SWAP(sw) sw +#endif + +static void +closedb(struct pwddb *db) +{ + if ((*db->db->close)(db->db) < 0) + wr_error(db->dbname); +} + +static void +opendb(struct pwddb *db, const char *dbname, const char *username, + uint32_t req_version, int flags, mode_t perm) +{ + char buf[MAXPATHLEN]; + + (void)snprintf(db->dbname, sizeof(db->dbname), "%s%s.tmp", prefix, + dbname); + + if (username != NULL) { + (void)snprintf(buf, sizeof(buf), "%s%s", prefix, dbname); + cp(buf, db->dbname, perm); + } + + db->db = dbopen(db->dbname, flags, perm, DB_HASH, &openinfo); + if (db->db == NULL) + mkpw_error("Cannot open `%s'", db->dbname); + + db->fname = dbname; + db->rversion = getversion(dbname); + if (req_version == ~0U) + db->wversion = db->rversion; + else + db->wversion = req_version; + + if (warning && db->rversion == 0 && db->wversion == 0) { + mkpw_warning("Database %s is a version %u database.", + db->fname, db->rversion); + mkpw_warning("Use %s -V 1 to upgrade once you've recompiled " + "all your binaries.", getprogname()); + } + if (db->wversion != db->rversion) { + if (username != NULL) { + mkpw_warning("You cannot change a single " + "record from version %u to version %u\n", + db->rversion, db->wversion); + bailout(); + } else if (verbose) { + mkpw_warning("Changing %s from version %u to version %u", + db->fname, db->rversion, db->wversion); + } + } else { + if (verbose) + mkpw_warning("File `%s' version %u requested %u", + db->fname, db->rversion, db->wversion); + } + + setversion(db); +} + +int +main(int argc, char *argv[]) +{ + int ch, makeold, tfd, lineno, found, rv, hasyp, secureonly; + struct passwd pwd, *tpwd; + char *username; + FILE *fp, *oldfp; + sigset_t set; + u_int dbflg, uid_dbflg; + int newuser, olduid, flags; + struct stat st; + u_int cachesize; + uint32_t req_version; + + prefix[0] = '\0'; + makeold = 0; + oldfp = NULL; + username = NULL; + hasyp = 0; + secureonly = 0; + found = 0; + newuser = 0; + cachesize = 0; + verbose = 0; + warning = 0; + logsyslog = 0; + req_version = ~0U; + + while ((ch = getopt(argc, argv, "BLc:d:lpsu:V:vw")) != -1) + switch (ch) { + case 'B': /* big-endian output */ + lorder = BIG_ENDIAN; + break; + case 'L': /* little-endian output */ + lorder = LITTLE_ENDIAN; + break; + case 'c': + cachesize = atoi(optarg) * 1024 * 1024; + break; + case 'd': /* set prefix */ + (void)strlcpy(prefix, optarg, sizeof(prefix)); + break; + case 'l': + openlog(getprogname(), LOG_PID, LOG_AUTH); + logsyslog = 1; + break; + case 'p': /* create V7 "file.orig" */ + makeold = 1; + break; + case 's': /* modify secure db only */ + secureonly = 1; + break; + case 'u': /* modify one user only */ + username = optarg; + break; + case 'V': + req_version = (uint32_t)atoi(optarg); + if (req_version > 1) { + mkpw_warning("Unknown version %u", req_version); + return EXIT_FAILURE; + } + break; + case 'v': + verbose++; + break; + case 'w': + warning++; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + if (username != NULL) + if (username[0] == '+' || username[0] == '-') + usage(); + if (secureonly) + makeold = 0; + + /* + * This could be changed to allow the user to interrupt. + * Probably not worth the effort. + */ + (void)sigemptyset(&set); + (void)sigaddset(&set, SIGTSTP); + (void)sigaddset(&set, SIGHUP); + (void)sigaddset(&set, SIGINT); + (void)sigaddset(&set, SIGQUIT); + (void)sigaddset(&set, SIGTERM); + (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL); + + /* We don't care what the user wants. */ + (void)umask(0); + + if (username == NULL) + flags = O_RDWR | O_CREAT | O_EXCL; + else + flags = O_RDWR; + + pname = *argv; + /* Open the original password file */ + if ((fp = fopen(pname, "r")) == NULL) + mkpw_error("Cannot open `%s'", pname); + + openinfo.lorder = lorder; + + if (fstat(fileno(fp), &st) == -1) + mkpw_error("Cannot stat `%s'", pname); + + if (cachesize) { + openinfo.cachesize = cachesize; + } else { + /* Tweak openinfo values for large passwd files. */ + cachesize = (u_int)(st.st_size * 20); + if (cachesize > MAX_CACHESIZE) + cachesize = MAX_CACHESIZE; + else if (cachesize < MIN_CACHESIZE) + cachesize = MIN_CACHESIZE; + openinfo.cachesize = cachesize; + } + + /* Open the temporary insecure password database. */ + if (!secureonly) { + opendb(&idb, _PATH_MP_DB, username, req_version, + flags, PERM_INSECURE); + clean |= FILE_INSECURE; + } + + + /* Open the temporary encrypted password database. */ + opendb(&sdb, _PATH_SMP_DB, username, req_version, flags, PERM_SECURE); + clean |= FILE_SECURE; + + /* + * Open file for old password file. Minor trickiness -- don't want to + * chance the file already existing, since someone (stupidly) might + * still be using this for permission checking. So, open it first and + * fdopen the resulting fd. The resulting file should be readable by + * everyone. + */ + if (makeold) { + (void)snprintf(oldpwdfile, sizeof(oldpwdfile), "%s.orig", + pname); + if ((tfd = open(oldpwdfile, O_WRONLY | O_CREAT | O_EXCL, + PERM_INSECURE)) < 0) + mkpw_error("Cannot create `%s'", oldpwdfile); + clean |= FILE_ORIG; + if ((oldfp = fdopen(tfd, "w")) == NULL) + mkpw_error("Cannot fdopen `%s'", oldpwdfile); + } + + if (username != NULL) { + uid_dbflg = 0; + dbflg = 0; + + /* + * Determine if this is a new entry. + */ + if (getdbent(&sdb, _PW_KEYBYNAME, username, &tpwd)) + newuser = 1; + else { + newuser = 0; + olduid = tpwd->pw_uid; + } + + } else { + uid_dbflg = R_NOOVERWRITE; + dbflg = R_NOOVERWRITE; + } + + /* + * If we see something go by that looks like YP, we save a special + * pointer record, which if YP is enabled in the C lib, will speed + * things up. + */ + for (lineno = 0; scan(fp, &pwd, &flags, &lineno);) { + /* + * Create original format password file entry. + */ + if (makeold) { +#ifdef __minix + (void)fprintf(oldfp, "%s:##%s:%d:%d:%s:%s:%s\n", + pwd.pw_name, pwd.pw_name, pwd.pw_uid, pwd.pw_gid, + pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell); +#else + (void)fprintf(oldfp, "%s:*:%d:%d:%s:%s:%s\n", + pwd.pw_name, pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos, + pwd.pw_dir, pwd.pw_shell); +#endif + if (ferror(oldfp)) + wr_error(oldpwdfile); + } + + if (username == NULL) { + /* Look like YP? */ + if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') + hasyp++; + + /* Warn about potentially unsafe uid/gid overrides. */ + if (pwd.pw_name[0] == '+') { + if ((flags & _PASSWORD_NOUID) == 0 && + pwd.pw_uid == 0) + mkpw_warning("line %d: superuser " + "override in YP inclusion", lineno); + if ((flags & _PASSWORD_NOGID) == 0 && + pwd.pw_gid == 0) + mkpw_warning("line %d: wheel override " + "in YP inclusion", lineno); + } + + /* Write the database entry out. */ + if (!secureonly) + putdbents(&idb, &pwd, "*", flags, lineno, dbflg, + uid_dbflg); + continue; + } else if (strcmp(username, pwd.pw_name) != 0) + continue; + + if (found) { + mkpw_warning("user `%s' listed twice in password file", + username); + bailout(); + } + + /* + * Ensure that the text file and database agree on + * which line the record is from. + */ + rv = getdbent(&sdb, _PW_KEYBYNUM, &lineno, &tpwd); + if (newuser) { + if (rv == 0) + inconsistency(); + } else if (rv == 1 || strcmp(username, tpwd->pw_name) != 0) + inconsistency(); + else if ((uid_t)olduid != pwd.pw_uid) { + /* + * If we're changing UID, remove the BYUID + * record for the old UID only if it has the + * same username. + */ + if (!getdbent(&sdb, _PW_KEYBYUID, &olduid, &tpwd)) { + if (strcmp(username, tpwd->pw_name) == 0) { + if (!secureonly) + deldbent(&idb, _PW_KEYBYUID, + &olduid); + deldbent(&sdb, _PW_KEYBYUID, &olduid); + } + } else + inconsistency(); + } + + /* + * If there's an existing BYUID record for the new UID and + * the username doesn't match then be sure not to overwrite + * it. + */ + if (!getdbent(&sdb, _PW_KEYBYUID, &pwd.pw_uid, &tpwd)) + if (strcmp(username, tpwd->pw_name) != 0) + uid_dbflg = R_NOOVERWRITE; + + /* Write the database entries out */ + if (!secureonly) + putdbents(&idb, &pwd, "*", flags, lineno, dbflg, + uid_dbflg); + putdbents(&sdb, &pwd, pwd.pw_passwd, flags, lineno, dbflg, + uid_dbflg); + + found = 1; + if (!makeold) + break; + } + + if (!secureonly) { + /* Store YP token if needed. */ + if (hasyp) + putyptoken(&idb); + + /* Close the insecure database. */ + closedb(&idb); + } + + /* + * If rebuilding the databases, we re-parse the text file and write + * the secure entries out in a separate pass. + */ + if (username == NULL) { + rewind(fp); + for (lineno = 0; scan(fp, &pwd, &flags, &lineno);) + putdbents(&sdb, &pwd, pwd.pw_passwd, flags, + lineno, dbflg, uid_dbflg); + + /* Store YP token if needed. */ + if (hasyp) + putyptoken(&sdb); + } else if (!found) { + mkpw_warning("user `%s' not found in password file", username); + bailout(); + } + + /* Close the secure database. */ + closedb(&sdb); + + /* Install as the real password files. */ + if (!secureonly) + install(idb.dbname, idb.fname); + install(sdb.dbname, sdb.fname); + + /* Install the V7 password file. */ + if (makeold) { + if (fflush(oldfp) == EOF) + wr_error(oldpwdfile); + if (fclose(oldfp) == EOF) + wr_error(oldpwdfile); + install(oldpwdfile, _PATH_PASSWD); + } + + /* Set master.passwd permissions, in case caller forgot. */ + (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR); + if (fclose(fp) == EOF) + wr_error(pname); + + /* + * Move the temporary master password file LAST -- chpass(1), + * passwd(1), vipw(8) and friends all use its existence to block + * other incarnations of themselves. The rename means that + * everything is unlocked, as the original file can no longer be + * accessed. + */ + install(pname, _PATH_MASTERPASSWD); + exit(EXIT_SUCCESS); + /* NOTREACHED */ +} + +int +scan(FILE *fp, struct passwd *pw, int *flags, int *lineno) +{ + static char line[LINE_MAX]; + char *p; + int oflags; + + if (fgets(line, (int)sizeof(line), fp) == NULL) + return (0); + (*lineno)++; + + /* + * ``... if I swallow anything evil, put your fingers down my + * throat...'' + * -- The Who + */ + if ((p = strchr(line, '\n')) == NULL) { + errno = EFTYPE; /* XXX */ + mkpw_error("%s, %d: line too long", pname, *lineno); + } + *p = '\0'; + if (strcmp(line, "+") == 0) { + /* pw_scan() can't handle "+" */ + (void)strcpy(line, "+:::::::::"); + } + oflags = 0; + if (!pw_scan(line, pw, &oflags)) { + errno = EFTYPE; /* XXX */ + mkpw_error("%s, %d: Syntax mkpw_error", pname, *lineno); + } + *flags = oflags; + + return (1); +} + +void +install(const char *from, const char *to) +{ + char buf[MAXPATHLEN]; + + (void)snprintf(buf, sizeof(buf), "%s%s", prefix, to); + if (rename(from, buf)) + mkpw_error("Cannot rename `%s' to `%s'", from, buf); +} + +void +rm(const char *victim) +{ + + if (unlink(victim) < 0) + warn("unlink(%s)", victim); +} + +void +cp(const char *from, const char *to, mode_t mode) +{ + static char buf[MAXBSIZE]; + int from_fd, to_fd; + ssize_t rcount, wcount; + + if ((from_fd = open(from, O_RDONLY, 0)) < 0) + mkpw_error("Cannot open `%s'", from); + if ((to_fd = open(to, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) { + (void)close(from_fd); + mkpw_error("Cannot open `%s'", to); + } + while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { + wcount = write(to_fd, buf, (size_t)rcount); + if (rcount != wcount || wcount == -1) { + (void)close(from_fd); + (void)close(to_fd); + goto on_error; + } + } + + close(from_fd); + if (close(to_fd)) + goto on_error; + if (rcount < 0) + goto on_error; + return; + +on_error: + mkpw_error("Cannot copy `%s' to `%s'", from, to); +} + +void +wr_error(const char *str) +{ + mkpw_error("Cannot write `%s'", str); +} + +void +mkpw_error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (logsyslog) { + int sverrno = errno; + char efmt[BUFSIZ]; + snprintf(efmt, sizeof(efmt), "%s (%%m)", fmt); + errno = sverrno; + vsyslog(LOG_ERR, efmt, ap); + } else + vwarn(fmt, ap); + va_end(ap); + bailout(); +} + +void +mkpw_warning(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (logsyslog) + vsyslog(LOG_WARNING, fmt, ap); + else + vwarnx(fmt, ap); + va_end(ap); +} + +void +inconsistency(void) +{ + + mkpw_warning("text files and databases are inconsistent"); + mkpw_warning("re-build the databases without -u"); + bailout(); +} + +void +bailout(void) +{ + + if ((clean & FILE_ORIG) != 0) + rm(oldpwdfile); + if ((clean & FILE_SECURE) != 0) + rm(sdb.dbname); + if ((clean & FILE_INSECURE) != 0) + rm(idb.dbname); + + exit(EXIT_FAILURE); +} + +uint32_t +getversion(const char *fname) +{ + DBT data, key; + int ret; + uint32_t version = 0; + DB *db; + + db = dbopen(fname, O_RDONLY, PERM_INSECURE, DB_HASH, NULL); + if (db == NULL) { + /* If we are building on a separate root, assume version 1 */ + if ((errno == EACCES || errno == ENOENT) && prefix[0]) + return 1; + mkpw_warning("Cannot open database `%s'", fname); + bailout(); + } + key.data = __UNCONST("VERSION"); + key.size = strlen((const char *)key.data) + 1; + + switch (ret = (*db->get)(db, &key, &data, 0)) { + case -1: /* Error */ + mkpw_warning("Cannot get VERSION record from database `%s'", + fname); + goto out; + case 0: + if (data.size != sizeof(version)) { + mkpw_warning("Bad VERSION record in database `%s'", fname); + goto out; + } + (void)memcpy(&version, data.data, sizeof(version)); + /*FALLTHROUGH*/ + case 1: + if (ret == 1) + mkpw_warning("Database `%s' has no version info", + fname); + (*db->close)(db); + return version; + default: + mkpw_warning("internal mkpw_error db->get returns %d", ret); + goto out; + } +out: + (*db->close)(db); + bailout(); + /*NOTREACHED*/ +} + +void +setversion(struct pwddb *db) +{ + DBT data, key; + key.data = __UNCONST("VERSION"); + key.size = strlen((const char *)key.data) + 1; + + data.data = &db->wversion; + data.size = sizeof(uint32_t); + + if ((*db->db->put)(db->db, &key, &data, 0) != 0) { + mkpw_warning("Can't write VERSION record to `%s'", db->dbname); + bailout(); + } +} + + +/* + * Write entries to a database for a single user. + * + * The databases actually contain three copies of the original data. Each + * password file entry is converted into a rough approximation of a ``struct + * passwd'', with the strings placed inline. This object is then stored as + * the data for three separate keys. The first key * is the pw_name field + * prepended by the _PW_KEYBYNAME character. The second key is the pw_uid + * field prepended by the _PW_KEYBYUID character. The third key is the line + * number in the original file prepended by the _PW_KEYBYNUM character. + * (The special characters are prepended to ensure that the keys do not + * collide.) + */ +#define COMPACT(e) for (t = e; (*p++ = *t++) != '\0';) + +void +putdbents(struct pwddb *db, struct passwd *pw, const char *passwd, int flags, + int lineno, u_int dbflg, u_int uid_dbflg) +{ + struct passwd pwd; + char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024], *p; + DBT data, key; + const char *t; + u_int32_t x; + size_t len; + + (void)memcpy(&pwd, pw, sizeof(pwd)); + data.data = (u_char *)buf; + key.data = (u_char *)tbuf; + + if (lorder != BYTE_ORDER) { + pwd.pw_uid = SWAP(pwd.pw_uid); + pwd.pw_gid = SWAP(pwd.pw_gid); + } + +#define WRITEPWTIMEVAR(pwvar) \ + do { \ + if (db->wversion == 0 && \ + /*CONSTCOND*/sizeof(pwvar) == sizeof(uint64_t)) { \ + uint32_t tmp = (uint32_t)pwvar; \ + if (lorder != BYTE_ORDER) \ + tmp = SWAP(tmp); \ + (void)memmove(p, &tmp, sizeof(tmp)); \ + p += sizeof(tmp); \ + } else if (db->wversion == 1 && \ + /*CONSTCOND*/sizeof(pwvar) == sizeof(uint32_t)) { \ + uint64_t tmp = pwvar; \ + if (lorder != BYTE_ORDER) \ + tmp = SWAP(tmp); \ + (void)memmove(p, &tmp, sizeof(tmp)); \ + p += sizeof(tmp); \ + } else { \ + if (lorder != BYTE_ORDER) \ + pwvar = SWAP(pwvar); \ + (void)memmove(p, &pwvar, sizeof(pwvar)); \ + p += sizeof(pwvar); \ + } \ + } while (/*CONSTCOND*/0) + + /* Create insecure data. */ + p = buf; + COMPACT(pwd.pw_name); + COMPACT(passwd); + (void)memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid)); + p += sizeof(pwd.pw_uid); + (void)memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid)); + p += sizeof(pwd.pw_gid); + WRITEPWTIMEVAR(pwd.pw_change); + COMPACT(pwd.pw_class); + COMPACT(pwd.pw_gecos); + COMPACT(pwd.pw_dir); + COMPACT(pwd.pw_shell); + WRITEPWTIMEVAR(pwd.pw_expire); + x = flags; + if (lorder != BYTE_ORDER) + x = SWAP(x); + (void)memmove(p, &x, sizeof(x)); + p += sizeof(x); + data.size = p - buf; + + /* Store insecure by name. */ + tbuf[0] = _PW_KEYBYNAME; + len = strlen(pwd.pw_name); + (void)memmove(tbuf + 1, pwd.pw_name, len); + key.size = len + 1; + if ((*db->db->put)(db->db, &key, &data, dbflg) == -1) + wr_error(db->dbname); + + /* Store insecure by number. */ + tbuf[0] = _PW_KEYBYNUM; + x = lineno; + if (lorder != BYTE_ORDER) + x = SWAP(x); + (void)memmove(tbuf + 1, &x, sizeof(x)); + key.size = sizeof(x) + 1; + if ((*db->db->put)(db->db, &key, &data, dbflg) == -1) + wr_error(db->dbname); + + /* Store insecure by uid. */ + tbuf[0] = _PW_KEYBYUID; + (void)memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid)); + key.size = sizeof(pwd.pw_uid) + 1; + if ((*db->db->put)(db->db, &key, &data, uid_dbflg) == -1) + wr_error(db->dbname); +} + +void +deldbent(struct pwddb *db, int type, void *keyp) +{ + char tbuf[1024]; + DBT key; + u_int32_t x; + size_t len; + + key.data = (u_char *)tbuf; + + switch (tbuf[0] = type) { + case _PW_KEYBYNAME: + len = strlen((char *)keyp); + (void)memcpy(tbuf + 1, keyp, len); + key.size = len + 1; + break; + + case _PW_KEYBYNUM: + case _PW_KEYBYUID: + x = *(int *)keyp; + if (lorder != BYTE_ORDER) + x = SWAP(x); + (void)memmove(tbuf + 1, &x, sizeof(x)); + key.size = sizeof(x) + 1; + break; + } + + if ((*db->db->del)(db->db, &key, 0) == -1) + wr_error(db->dbname); +} + +int +getdbent(struct pwddb *db, int type, void *keyp, struct passwd **tpwd) +{ + static char buf[MAX(MAXPATHLEN, LINE_MAX * 2)]; + static struct passwd pwd; + char tbuf[1024], *p; + DBT key, data; + u_int32_t x; + size_t len; + int rv; + + data.data = (u_char *)buf; + data.size = sizeof(buf); + key.data = (u_char *)tbuf; + + switch (tbuf[0] = type) { + case _PW_KEYBYNAME: + len = strlen((char *)keyp); + (void)memcpy(tbuf + 1, keyp, len); + key.size = len + 1; + break; + + case _PW_KEYBYNUM: + case _PW_KEYBYUID: + x = *(int *)keyp; + if (lorder != BYTE_ORDER) + x = SWAP(x); + (void)memmove(tbuf + 1, &x, sizeof(x)); + key.size = sizeof(x) + 1; + break; + } + + if ((rv = (*db->db->get)(db->db, &key, &data, 0)) == 1) + return (rv); + if (rv == -1) + mkpw_error("Error getting record from `%s'", db->dbname); + + p = (char *)data.data; + + pwd.pw_name = p; + while (*p++ != '\0') + continue; + pwd.pw_passwd = p; + while (*p++ != '\0') + continue; + + (void)memcpy(&pwd.pw_uid, p, sizeof(pwd.pw_uid)); + p += sizeof(pwd.pw_uid); + (void)memcpy(&pwd.pw_gid, p, sizeof(pwd.pw_gid)); + p += sizeof(pwd.pw_gid); + +#define READPWTIMEVAR(pwvar) \ + do { \ + if (db->rversion == 0 && \ + /*CONSTCOND*/sizeof(pwvar) == sizeof(uint64_t)) { \ + uint32_t tmp; \ + (void)memcpy(&tmp, p, sizeof(tmp)); \ + p += sizeof(tmp); \ + if (lorder != BYTE_ORDER) \ + pwvar = SWAP(tmp); \ + else \ + pwvar = tmp; \ + } else if (db->rversion == 1 && \ + /*CONSTCOND*/sizeof(pwvar) == sizeof(uint32_t)) { \ + uint64_t tmp; \ + (void)memcpy(&tmp, p, sizeof(tmp)); \ + p += sizeof(tmp); \ + if (lorder != BYTE_ORDER) \ + pwvar = (uint32_t)SWAP(tmp); \ + else \ + pwvar = (uint32_t)tmp; \ + } else { \ + (void)memcpy(&pwvar, p, sizeof(pwvar)); \ + p += sizeof(pwvar); \ + if (lorder != BYTE_ORDER) \ + pwvar = SWAP(pwvar); \ + } \ + } while (/*CONSTCOND*/0) + + READPWTIMEVAR(pwd.pw_change); + + pwd.pw_class = p; + while (*p++ != '\0') + continue; + pwd.pw_gecos = p; + while (*p++ != '\0') + continue; + pwd.pw_dir = p; + while (*p++ != '\0') + continue; + pwd.pw_shell = p; + while (*p++ != '\0') + continue; + + READPWTIMEVAR(pwd.pw_expire); + + if (lorder != BYTE_ORDER) { + pwd.pw_uid = SWAP(pwd.pw_uid); + pwd.pw_gid = SWAP(pwd.pw_gid); + } + + *tpwd = &pwd; + return (0); +} + +void +putyptoken(struct pwddb *db) +{ + DBT data, key; + + key.data = __UNCONST(__yp_token); + key.size = strlen(__yp_token); + data.data = (u_char *)NULL; + data.size = 0; + + if ((*db->db->put)(db->db, &key, &data, R_NOOVERWRITE) == -1) + wr_error(db->dbname); +} + +void +usage(void) +{ + + (void)fprintf(stderr, + "Usage: %s [-BLlpsvw] [-c cachesize] [-d directory] [-u user] " + "[-V version] file\n", + getprogname()); + exit(EXIT_FAILURE); +} diff --git a/usr.sbin/user/Makefile b/usr.sbin/user/Makefile new file mode 100644 index 000000000..be63a3336 --- /dev/null +++ b/usr.sbin/user/Makefile @@ -0,0 +1,81 @@ +# $NetBSD: Makefile,v 1.12 2009/04/22 15:23:09 lukem Exp $ +# + +WARNS?= 1 # XXX: -Wsign-compare -Wcast-qual + +.include + +CPPFLAGS+= -DEXTENSIONS -DPW_MKDB_ARGC=2 + +PROG= user +SRCS+= user.c main.c +LINKS+= ${BINDIR}/user ${BINDIR}/useradd +LINKS+= ${BINDIR}/user ${BINDIR}/userdel +LINKS+= ${BINDIR}/user ${BINDIR}/usermod +LINKS+= ${BINDIR}/user ${BINDIR}/group +LINKS+= ${BINDIR}/user ${BINDIR}/groupadd +LINKS+= ${BINDIR}/user ${BINDIR}/groupdel +LINKS+= ${BINDIR}/user ${BINDIR}/groupmod +LINKS+= ${BINDIR}/user ${BINDIR}/userinfo +LINKS+= ${BINDIR}/user ${BINDIR}/groupinfo +LDADD+= -lutil +DPADD+= ${LIBUTIL} +MAN= user.8 useradd.8 userdel.8 usermod.8 userinfo.8 usermgmt.conf.5 +MAN+= group.8 groupadd.8 groupdel.8 groupmod.8 groupinfo.8 +MLINKS= useradd.8 adduser.8 + +# this target checks the built-in default group, and, if it doesn't exist, +# creates it +default-group: + @ln -fs ${.OBJDIR}/user ${.OBJDIR}/group; \ + defgrp=`${.OBJDIR}/user add -D | \ + ${TOOL_AWK} '/^group/ { print $$2 }'`; \ + if ${.OBJDIR}/group info -e $$defgrp; then \ + defgid=`${.OBJDIR}/group info $$defgrp | \ + ${TOOL_AWK} '/^gid/ { print $$2 }'`; \ + else \ + defgid=99; \ + while [ $$defgid -gt 0 ]; do \ + ${.OBJDIR}/group info -e $$defgid || break; \ + defgid=`expr $$defgid - 1`; \ + done; \ + if [ $$defgid -eq 0 ]; then \ + defgid=100; \ + while [ $$defgid -lt 60000 ]; do \ + ${.OBJDIR}/group info -e $$defgid || break; \ + defgid=`expr $$defgid + 1`; \ + done; \ + if [ $$defgid -eq 60000 ]; then \ + echo "No gids left"; \ + exit 1; \ + fi; \ + fi; \ + ${.OBJDIR}/group add -g $$defgid $$defgrp; \ + fi; \ + echo "Default group is $$defgrp ($$defgid):"; \ + ${.OBJDIR}/group info $$defgrp + +.include + +test: ${PROG} + @echo "No news is good news" + @echo "1. Adding new user" + @rm -f useradd + @ln -s user useradd + -./useradd -m -g=uid test1.1 + @echo "2. Modifying new user" + -./${PROG} mod -l test1.2 test1.1 + @echo "3. Deleting new user" + -./${PROG} del -r test1.2 + @echo "4. Attempting to add an invalid user name - IGNORE ANY ERROR" + -./${PROG} add -m test1%1 + @echo "5. Bad usage - IGNORE ANY ERROR" + -./${PROG} add -m + @echo "6. Set range defaults" + -./${PROG} add -D -r4000..6000 + -./${PROG} add -D + @echo "7. Get user information" + -./${PROG} info root + @echo "8. Bad user name - IGNORE ANY ERROR" + -./${PROG} info test1%1 || echo "User not found" + @echo "All tests completed" diff --git a/usr.sbin/user/defs.h b/usr.sbin/user/defs.h new file mode 100644 index 000000000..578bc40a7 --- /dev/null +++ b/usr.sbin/user/defs.h @@ -0,0 +1,59 @@ +/* $NetBSD: defs.h,v 1.6 2005/11/25 08:00:18 agc Exp $ */ + +/* + * Copyright (c) 1999 Alistair G. Crooks. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef DEFS_H_ +#define DEFS_H_ + +#define NEWARRAY(type,ptr,size,action) do { \ + if ((ptr = (type *) calloc(sizeof(type), size)) == (type *) NULL) { \ + warn("can't allocate %ld bytes", (long)(size * sizeof(type))); \ + action; \ + } \ +} while( /* CONSTCOND */ 0) + +#define RENEW(type,ptr,size,action) do { \ + if ((ptr = (type *) realloc(ptr, sizeof(type) * size)) == (type *) NULL) { \ + warn("can't realloc %ld bytes", (long)(size * sizeof(type))); \ + action; \ + } \ +} while( /* CONSTCOND */ 0) + +#define NEW(type, ptr, action) NEWARRAY(type, ptr, 1, action) + +#define FREE(ptr) (void) free(ptr) + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#endif /* !DEFS_H_ */ diff --git a/usr.sbin/user/group.8 b/usr.sbin/user/group.8 new file mode 100644 index 000000000..c49c1de74 --- /dev/null +++ b/usr.sbin/user/group.8 @@ -0,0 +1,89 @@ +.\" $NetBSD: group.8,v 1.17 2005/11/25 08:00:18 agc Exp $ */ +.\" +.\" Copyright (c) 1999 Alistair G. Crooks. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +.\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd November 7, 2005 +.Dt GROUP 8 +.Os +.Sh NAME +.Nm group +.Nd manage group information on the system +.Sh SYNOPSIS +.Nm +.Cm add +.Op options +.Ar group +.Nm +.Cm del +.Op options +.Ar group +.Nm +.Cm info +.Op options +.Ar group +.Nm +.Cm mod +.Op options +.Ar group +.Sh DESCRIPTION +The +.Nm +utility acts as a frontend to the +.Xr groupadd 8 , +.Xr groupmod 8 , +.Xr groupinfo 8 , +and +.Xr groupdel 8 +commands. +The utilities by default are built with +.Dv EXTENSIONS . +This allows for further functionality. +.Pp +For a full explanation of the options available, please see the relevant manual page. +.Sh EXIT STATUS +.Ex -std group +.Sh SEE ALSO +.Xr group 5 , +.Xr groupadd 8 , +.Xr groupdel 8 , +.Xr groupinfo 8 , +.Xr groupmod 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 1.5 . +It is based on the +.Ar addnerd +package by the same author. +.Sh AUTHORS +The +.Nm +utility was written by +.An Alistair G. Crooks +.Aq agc@NetBSD.org . diff --git a/usr.sbin/user/groupadd.8 b/usr.sbin/user/groupadd.8 new file mode 100644 index 000000000..24eb0e153 --- /dev/null +++ b/usr.sbin/user/groupadd.8 @@ -0,0 +1,87 @@ +.\" $NetBSD: groupadd.8,v 1.17 2006/05/29 01:38:33 hubertf Exp $ */ +.\" +.\" Copyright (c) 1999 Alistair G. Crooks. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +.\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd November 7, 2005 +.Dt GROUPADD 8 +.Os +.Sh NAME +.Nm groupadd +.Nd add a group to the system +.Sh SYNOPSIS +.Nm +.Op Fl ov +.Op Fl g Ar gid +.Op Fl r Ar lowgid Ns Li .. Ns Ar highgid +.Ar group +.Sh DESCRIPTION +The +.Nm +utility adds a group to the system. +See +.Xr group 8 +for more information about +.Dv EXTENSIONS . +The options are as follows: +.Bl -tag -width Ds +.It Fl g Ar gid +Give the numeric group identifier to be used for the new group. +.It Fl o +Allow the new group to have a gid which is already in use for +another group. +.It Fl r Ar lowgid Ns Li .. Ns Ar highgid +Set the low and high bounds of a gid range for new groups. +A new group can only be created if there are gids which can be +assigned inside the range. +This option is included if built with +.Dv EXTENSIONS . +.It Fl v +Enable verbose mode - explain the commands as they are executed. +This option is included if built with +.Dv EXTENSIONS . +.El +.Sh EXIT STATUS +.Ex -std groupadd +.Sh SEE ALSO +.Xr group 5 , +.Xr group 8 , +.Xr user 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 1.5 . +It is based on the +.Ar addnerd +package by the same author. +.Sh AUTHORS +The +.Nm +utility was written by +.An Alistair G. Crooks +.Aq agc@NetBSD.org . diff --git a/usr.sbin/user/groupdel.8 b/usr.sbin/user/groupdel.8 new file mode 100644 index 000000000..dd5bb194b --- /dev/null +++ b/usr.sbin/user/groupdel.8 @@ -0,0 +1,74 @@ +.\" $NetBSD: groupdel.8,v 1.14 2006/05/29 01:38:33 hubertf Exp $ */ +.\" +.\" Copyright (c) 1999 Alistair G. Crooks. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +.\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd November 7, 2005 +.Dt GROUPDEL 8 +.Os +.Sh NAME +.Nm groupdel +.Nd remove a group from the system +.Sh SYNOPSIS +.Nm +.Op Fl v +.Ar group +.Sh DESCRIPTION +The +.Nm +utility removes a group from the system. +See +.Xr group 8 +for more information about +.Dv EXTENSIONS . +The options are as follows: +.Bl -tag -width Ds +.It Fl v +Enable verbose mode - explain the commands as they are executed. +This option is included if built with +.Dv EXTENSIONS . +.El +.Sh EXIT STATUS +.Ex -std groupdel +.Sh SEE ALSO +.Xr group 5 , +.Xr group 8 , +.Xr user 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 1.5 . +It is based on the +.Ar addnerd +package by the same author. +.Sh AUTHORS +The +.Nm +utility was written by +.An Alistair G. Crooks +.Aq agc@NetBSD.org . diff --git a/usr.sbin/user/groupinfo.8 b/usr.sbin/user/groupinfo.8 new file mode 100644 index 000000000..b031a3a82 --- /dev/null +++ b/usr.sbin/user/groupinfo.8 @@ -0,0 +1,90 @@ +.\" $NetBSD: groupinfo.8,v 1.12 2006/05/29 01:38:33 hubertf Exp $ */ +.\" +.\" Copyright (c) 1999 Alistair G. Crooks. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +.\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd November 7, 2005 +.Dt GROUPINFO 8 +.Os +.Sh NAME +.Nm groupinfo +.Nd displays group information +.Sh SYNOPSIS +.Nm +.Op Fl ev +.Ar group +.Sh DESCRIPTION +The +.Nm +utility retrieves the group information from the system. +The +.Nm +utility is only available if built with +.Dv EXTENSIONS . +See +.Xr group 8 +for more information. +.Pp +The following command line options are recognised: +.Bl -tag -width Ds +.It Fl e +Return 0 if the group exists, and non-zero if the +group does not exist, on the system. +No information is displayed. +This form of the command is useful for +scripts which need to check whether a particular group +name or gid is already in use on the system. +.It Fl v +Perform any actions in a verbose manner. +.El +.Pp +The +.Ar group +argument may either be a group's name, or a gid. +.Sh EXIT STATUS +.Ex -std groupinfo +.Sh FILES +.Bl -tag -width /etc/usermgmt.conf -compact +.It Pa /etc/usermgmt.conf +.El +.Sh SEE ALSO +.Xr passwd 5 , +.Xr group 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 1.5 . +It is based on the +.Ar addnerd +package by the same author. +.Sh AUTHORS +The +.Nm +utility was written by +.An Alistair G. Crooks +.Aq agc@NetBSD.org . diff --git a/usr.sbin/user/groupmod.8 b/usr.sbin/user/groupmod.8 new file mode 100644 index 000000000..339c354b8 --- /dev/null +++ b/usr.sbin/user/groupmod.8 @@ -0,0 +1,83 @@ +.\" $NetBSD: groupmod.8,v 1.14 2005/11/25 08:00:18 agc Exp $ */ +.\" +.\" Copyright (c) 1999 Alistair G. Crooks. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +.\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd November 7, 2005 +.Dt GROUPMOD 8 +.Os +.Sh NAME +.Nm groupmod +.Nd modify an existing group on the system +.Sh SYNOPSIS +.Nm +.Op Fl ov +.Op Fl g Ar gid +.Op Fl n Ar newname +.Ar group +.Sh DESCRIPTION +The +.Nm +utility modifies an existing group on the system. +See +.Xr group 8 +for more information about +.Dv EXTENSIONS . +The options are as follows: +.Bl -tag -width Ds +.It Fl g Ar gid +Give the numeric group identifier to be used for the new group. +.It Fl n Ar new-group-name +Give the new name which the group shall have. +.It Fl o +Allow the new group to have a gid which is already in use for +another group. +.It Fl v +Enable verbose mode - explain the commands as they are executed. +This option is included if built with +.Dv EXTENSIONS . +.El +.Sh EXIT STATUS +.Ex -std groupmod +.Sh SEE ALSO +.Xr group 5 , +.Xr group 8 , +.Xr user 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 1.5 . +It is based on the +.Ar addnerd +package by the same author. +.Sh AUTHORS +The +.Nm +utility was written by +.An Alistair G. Crooks +.Aq agc@NetBSD.org . diff --git a/usr.sbin/user/main.c b/usr.sbin/user/main.c new file mode 100644 index 000000000..b90f2961c --- /dev/null +++ b/usr.sbin/user/main.c @@ -0,0 +1,99 @@ +/* $NetBSD: main.c,v 1.5 2006/10/22 21:16:58 christos Exp $ */ + +/* + * Copyright (c) 1999 Alistair G. Crooks. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include + +#include "usermgmt.h" + +enum { + MaxCmdWords = 2 +}; + +/* this struct describes a command */ +typedef struct cmd_t { + int c_wc; /* word count */ + const char *c_word[MaxCmdWords]; /* command words */ + int (*c_func)(int, char **); /* called function */ +} cmd_t; + +/* despatch table for commands */ +static cmd_t cmds[] = { + { 1, { "useradd", NULL }, useradd }, + { 2, { "user", "add" }, useradd }, + { 1, { "usermod", NULL }, usermod }, + { 2, { "user", "mod" }, usermod }, + { 1, { "userdel", NULL }, userdel }, + { 2, { "user", "del" }, userdel }, +#ifdef EXTENSIONS + { 1, { "userinfo", NULL }, userinfo }, + { 2, { "user", "info" }, userinfo }, +#endif + { 1, { "groupadd", NULL }, groupadd }, + { 2, { "group", "add" }, groupadd }, + { 1, { "groupmod", NULL }, groupmod }, + { 2, { "group", "mod" }, groupmod }, + { 1, { "groupdel", NULL }, groupdel }, + { 2, { "group", "del" }, groupdel }, +#ifdef EXTENSIONS + { 1, { "groupinfo", NULL }, groupinfo }, + { 2, { "group", "info" }, groupinfo }, +#endif + { .c_wc = 0 } +}; + +int +main(int argc, char **argv) +{ + cmd_t *cmdp; + int matched; + int i; + + for (cmdp = cmds ; cmdp->c_wc > 0 ; cmdp++) { + for (matched = i = 0 ; i < cmdp->c_wc && i < MaxCmdWords ; i++) { + if (argc > i) { + if (strcmp((i == 0) ? getprogname() : argv[i], + cmdp->c_word[i]) == 0) { + matched += 1; + } else { + break; + } + } + } + if (matched == cmdp->c_wc) { + return (*cmdp->c_func)(argc - (matched - 1), argv + (matched - 1)); + } + } + usermgmt_usage(getprogname()); + errx(EXIT_FAILURE, "Program `%s' not recognised", getprogname()); + /* NOTREACHED */ +} diff --git a/usr.sbin/user/out b/usr.sbin/user/out new file mode 100644 index 000000000..98788a912 --- /dev/null +++ b/usr.sbin/user/out @@ -0,0 +1,17 @@ +chown: /home/test1.1/.ashrc: invalid argument +chown: /home/test1.1/.ellepro.b1: invalid argument +chown: /home/test1.1/.ellepro.e: invalid argument +chown: /home/test1.1/.exrc: invalid argument +chown: /home/test1.1/.profile: invalid argument +chown: /home/test1.1: invalid argument +useradd: Error running `/usr/sbin/chown -R -h 4000:4000 /home/test1.1': no such file or directory +user: User `test1.2' doesn't own directory `/home/test1.1', not removed +user: Can't add user `test1%1': invalid login name +usage: useradd -D [-F] [-b base-dir] [-e expiry-time] [-f inactive-time] + [-g gid | name | =uid] [-k skel-dir] [-L login-class] + [-M homeperm] [-r lowuid..highuid] [-s shell] +usage: useradd [-moSv] [-b base-dir] [-c comment] [-d home-dir] [-e expiry-time] + [-f inactive-time] [-G secondary-group] [-g gid | name | =uid] + [-k skeletondir] [-L login-class] [-M homeperm] [-p password] + [-r lowuid..highuid] [-s shell] [-u uid] user +user: Can't find user `test1%1' diff --git a/usr.sbin/user/user.8 b/usr.sbin/user/user.8 new file mode 100644 index 000000000..5570628ca --- /dev/null +++ b/usr.sbin/user/user.8 @@ -0,0 +1,115 @@ +.\" $NetBSD: user.8,v 1.23 2005/11/25 08:00:18 agc Exp $ */ +.\" +.\" Copyright (c) 1999 Alistair G. Crooks. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +.\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd November 16, 2005 +.Dt USER 8 +.Os +.Sh NAME +.Nm user +.Nd manage user login information on the system +.Sh SYNOPSIS +.Nm +.Cm add +.Fl D +.Op options +.Nm +.Cm add +.Op options +.Ar user +.Nm +.Cm del +.Fl D +.Op options +.Nm +.Cm del +.Op options +.Ar user +.Nm +.Cm info +.Op options +.Ar user +.Nm +.Cm mod +.Op options +.Ar user +.Sh DESCRIPTION +The +.Nm +utility acts as a frontend to the +.Xr useradd 8 , +.Xr usermod 8 , +.Xr userinfo 8 , +and +.Xr userdel 8 +commands. +The utilities by default are built with +.Dv EXTENSIONS . +This allows for further functionality. +.Pp +For a full explanation of the options available, please see the relevant manual page. +.Sh EXIT STATUS +.Ex -std user +.Sh FILES +.Bl -tag -width /usr/share/examples/usermgmt -compact +.It Pa /etc/skel/.[A-z]* +Skeleton files for new user +.It Pa /etc/usermgmt.conf +Configuration file for +.Nm , +.Xr group 8 +and the backend commands mentioned above. +.\" .It Pa /usr/share/examples/usermgmt +.\" A directory containing examples for +.\" .Nm +.\" and +.\" .Xr group 8 . +.El +.Sh SEE ALSO +.Xr chpass 1 , +.Xr group 5 , +.Xr passwd 5 , +.Xr usermgmt.conf 5 , +.Xr useradd 8 , +.Xr userdel 8 , +.Xr userinfo 8 , +.Xr usermod 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 1.5 . +It is based on the +.Ar addnerd +package by the same author. +.Sh AUTHORS +The +.Nm +utility was written by +.An Alistair G. Crooks +.Aq agc@NetBSD.org . diff --git a/usr.sbin/user/user.c b/usr.sbin/user/user.c new file mode 100644 index 000000000..045ff1aa6 --- /dev/null +++ b/usr.sbin/user/user.c @@ -0,0 +1,2561 @@ +/* $NetBSD: user.c,v 1.126 2011/01/04 10:30:21 wiz Exp $ */ + +/* + * Copyright (c) 1999 Alistair G. Crooks. All rights reserved. + * Copyright (c) 2005 Liam J. Foy. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1999\ + The NetBSD Foundation, Inc. All rights reserved."); +__RCSID("$NetBSD: user.c,v 1.126 2011/01/04 10:30:21 wiz Exp $"); +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef EXTENSIONS +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "usermgmt.h" + + +/* this struct describes a uid range */ +typedef struct range_t { + int r_from; /* low uid */ + int r_to; /* high uid */ +} range_t; + +typedef struct rangelist_t { + unsigned rl_rsize; /* size of range array */ + unsigned rl_rc; /* # of ranges */ + range_t *rl_rv; /* the ranges */ + unsigned rl_defrc; /* # of ranges in defaults */ +} rangelist_t; + +/* this struct encapsulates the user and group information */ +typedef struct user_t { + int u_flags; /* see below */ + int u_uid; /* uid of user */ + char *u_password; /* encrypted password */ + char *u_comment; /* comment field */ + char *u_home; /* home directory */ + mode_t u_homeperm; /* permissions of home dir */ + char *u_primgrp; /* primary group */ + int u_groupc; /* # of secondary groups */ + const char *u_groupv[NGROUPS_MAX]; /* secondary groups */ + char *u_shell; /* user's shell */ + char *u_basedir; /* base directory for home */ + char *u_expire; /* when password will expire */ + char *u_inactive; /* when account will expire */ + char *u_skeldir; /* directory for startup files */ + char *u_class; /* login class */ + rangelist_t u_r; /* list of ranges */ + unsigned u_defrc; /* # of ranges in defaults */ + int u_preserve; /* preserve uids on deletion */ + int u_allow_samba; /* allow trailing '$' for samba login names */ + int u_locked; /* user account lock */ +} user_t; +#define u_rsize u_r.rl_rsize +#define u_rc u_r.rl_rc +#define u_rv u_r.rl_rv +#define u_defrc u_r.rl_defrc + +/* this struct encapsulates the user and group information */ +typedef struct group_t { + rangelist_t g_r; /* list of ranges */ +} group_t; +#define g_rsize g_r.rl_rsize +#define g_rc g_r.rl_rc +#define g_rv g_r.rl_rv +#define g_defrc g_r.rl_defrc + +typedef struct def_t { + user_t user; + group_t group; +} def_t; + +/* flags for which fields of the user_t replace the passwd entry */ +enum { + F_COMMENT = 0x0001, + F_DUPUID = 0x0002, + F_EXPIRE = 0x0004, + F_GROUP = 0x0008, + F_HOMEDIR = 0x0010, + F_MKDIR = 0x0020, + F_INACTIVE = 0x0040, + F_PASSWORD = 0x0080, + F_SECGROUP = 0x0100, + F_SHELL = 0x0200, + F_UID = 0x0400, + F_USERNAME = 0x0800, + F_CLASS = 0x1000 +}; + +#define UNLOCK 0 +#define LOCK 1 +#define LOCKED "*LOCKED*" + +#define PATH_LOGINCONF "/etc/login.conf" + +#ifndef DEF_GROUP +#define DEF_GROUP "users" +#endif + +#ifndef DEF_BASEDIR +#define DEF_BASEDIR "/home" +#endif + +#ifndef DEF_SKELDIR +#ifdef __minix +#define DEF_SKELDIR "/usr/ast" +#else +#define DEF_SKELDIR "/etc/skel" +#endif +#endif + +#ifndef DEF_SHELL +#define DEF_SHELL _PATH_BSHELL +#endif + +#ifndef DEF_COMMENT +#define DEF_COMMENT "" +#endif + +#ifndef DEF_LOWUID +#define DEF_LOWUID 1000 +#endif + +#ifndef DEF_HIGHUID +#define DEF_HIGHUID 60000 +#endif + +#ifndef DEF_INACTIVE +#define DEF_INACTIVE 0 +#endif + +#ifndef DEF_EXPIRE +#define DEF_EXPIRE NULL +#endif + +#ifndef DEF_CLASS +#define DEF_CLASS "" +#endif + +#ifndef WAITSECS +#define WAITSECS 10 +#endif + +#ifndef NOBODY_UID +#define NOBODY_UID 32767 +#endif + +#ifndef DEF_HOMEPERM +#define DEF_HOMEPERM 0755 +#endif + +/* some useful constants */ +enum { + MaxShellNameLen = 256, + MaxFileNameLen = MAXPATHLEN, + MaxUserNameLen = LOGIN_NAME_MAX - 1, + MaxCommandLen = 2048, + MaxEntryLen = 2048, + PasswordLength = 2048, + + DES_Len = 13, +}; + +/* Full paths of programs used here */ +#define CHMOD "/bin/chmod" +#define CHOWN "/usr/bin/chown" +#define MKDIR "/bin/mkdir" +#define MV "/bin/mv" +#define NOLOGIN "/sbin/nologin" +#define PAX "/bin/pax" +#define RM "/bin/rm" + +#define UNSET_INACTIVE "Null (unset)" +#define UNSET_EXPIRY "Null (unset)" + +static int asystem(const char *fmt, ...) + __attribute__((__format__(__printf__, 1, 2))); +static int is_number(const char *); +static struct group *find_group_info(const char *); +static int verbose; + +static char * +skipspace(char *s) +{ + for (; *s && isspace((unsigned char)*s) ; s++) { + } + return s; +} + +static int +check_numeric(const char *val, const char *name) +{ + if (!is_number(val)) { + errx(EXIT_FAILURE, "When using [-%c %s], " + "the %s must be numeric", *name, name, name); + } + return atoi(val); +} + +/* resize *cpp appropriately then assign `n' chars of `s' to it */ +static void +memsave(char **cpp, const char *s, size_t n) +{ + RENEW(char, *cpp, n + 1, exit(1)); + (void)memcpy(*cpp, s, n); + (*cpp)[n] = '\0'; +} + +/* a replacement for system(3) */ +static int +asystem(const char *fmt, ...) +{ + va_list vp; + char buf[MaxCommandLen]; + int ret; + + va_start(vp, fmt); + (void)vsnprintf(buf, sizeof(buf), fmt, vp); + va_end(vp); + if (verbose) { + (void)printf("Command: %s\n", buf); + } + if ((ret = system(buf)) != 0) { + warn("Error running `%s'", buf); + } + return ret; +} + +/* remove a users home directory, returning 1 for success (ie, no problems encountered) */ +static int +removehomedir(struct passwd *pwp) +{ + struct stat st; + + /* userid not root? */ + if (pwp->pw_uid == 0) { + warnx("Not deleting home directory `%s'; userid is 0", pwp->pw_dir); + return 0; + } + + /* directory exists (and is a directory!) */ + if (stat(pwp->pw_dir, &st) < 0) { + warn("Cannot access home directory `%s'", pwp->pw_dir); + return 0; + } + if (!S_ISDIR(st.st_mode)) { + warnx("Home directory `%s' is not a directory", pwp->pw_dir); + return 0; + } + + /* userid matches directory owner? */ + if (st.st_uid != pwp->pw_uid) { + warnx("User `%s' doesn't own directory `%s', not removed", + pwp->pw_name, pwp->pw_dir); + return 0; + } + + (void)seteuid(pwp->pw_uid); + /* we add the "|| true" to keep asystem() quiet if there is a non-zero exit status. */ + (void)asystem("%s -rf %s > /dev/null 2>&1 || true", RM, pwp->pw_dir); + (void)seteuid(0); + if (rmdir(pwp->pw_dir) < 0) { + warn("Unable to remove all files in `%s'", pwp->pw_dir); + return 0; + } + return 1; +} + +/* return 1 if all of `s' is numeric */ +static int +is_number(const char *s) +{ + for ( ; *s ; s++) { + if (!isdigit((unsigned char) *s)) { + return 0; + } + } + return 1; +} + +/* + * check that the effective uid is 0 - called from funcs which will + * modify data and config files. + */ +static void +checkeuid(void) +{ + if (geteuid() != 0) { + errx(EXIT_FAILURE, "Program must be run as root"); + } +} + +/* copy any dot files into the user's home directory */ +static int +copydotfiles(char *skeldir, int uid, int gid, char *dir, mode_t homeperm) +{ + struct dirent *dp; + DIR *dirp; + int n; + + if ((dirp = opendir(skeldir)) == NULL) { + warn("Can't open source . files dir `%s'", skeldir); + return 0; + } + for (n = 0; (dp = readdir(dirp)) != NULL && n == 0 ; ) { + if (strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0) { + continue; + } + n = 1; + } + (void)closedir(dirp); + if (n == 0) { + warnx("No \"dot\" initialisation files found"); + } else { + (void)asystem("cd %s && %s -rw -pe %s . %s", + skeldir, PAX, (verbose) ? "-v" : "", dir); + } + (void)asystem("%s -R -h %d:%d %s", CHOWN, uid, gid, dir); + (void)asystem("%s -R u+w %s", CHMOD, dir); +#ifdef EXTENSIONS + (void)asystem("%s 0%o %s", CHMOD, homeperm, dir); +#endif + return n; +} + +/* create a group entry with gid `gid' */ +static int +creategid(char *group, int gid, const char *name) +{ + struct stat st; + FILE *from; + FILE *to; + char buf[MaxEntryLen]; + char f[MaxFileNameLen]; + int fd; + int cc; + + if (getgrnam(group) != NULL) { + warnx("Can't create group `%s': already exists", group); + return 0; + } + if ((from = fopen(_PATH_GROUP, "r+")) == NULL) { + warn("Can't create group `%s': can't open `%s'", name, + _PATH_GROUP); + return 0; + } + if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { + warn("Can't lock `%s'", _PATH_GROUP); + (void)fclose(from); + return 0; + } + (void)fstat(fileno(from), &st); + (void)snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP); + if ((fd = mkstemp(f)) < 0) { + warn("Can't create group `%s': mkstemp failed", group); + (void)fclose(from); + return 0; + } + if ((to = fdopen(fd, "w")) == NULL) { + warn("Can't create group `%s': fdopen `%s' failed", + group, f); + (void)fclose(from); + (void)close(fd); + (void)unlink(f); + return 0; + } + while ((cc = fread(buf, sizeof(char), sizeof(buf), from)) > 0) { + if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) { + warn("Can't create group `%s': short write to `%s'", + group, f); + (void)fclose(from); + (void)close(fd); + (void)unlink(f); + return 0; + } + } + (void)fprintf(to, "%s:*:%d:%s\n", group, gid, name); + (void)fclose(from); + (void)fclose(to); + if (rename(f, _PATH_GROUP) < 0) { + warn("Can't create group `%s': can't rename `%s' to `%s'", + group, f, _PATH_GROUP); + (void)unlink(f); + return 0; + } + (void)chmod(_PATH_GROUP, st.st_mode & 07777); + syslog(LOG_INFO, "New group added: name=%s, gid=%d", group, gid); + return 1; +} + +/* modify the group entry with name `group' to be newent */ +static int +modify_gid(char *group, char *newent) +{ + struct stat st; + FILE *from; + FILE *to; + char buf[MaxEntryLen]; + char f[MaxFileNameLen]; + char *colon; + int groupc; + int entc; + int fd; + int cc; + + if ((from = fopen(_PATH_GROUP, "r+")) == NULL) { + warn("Can't modify group `%s': can't open `%s'", + group, _PATH_GROUP); + return 0; + } + if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { + warn("Can't modify group `%s': can't lock `%s'", + group, _PATH_GROUP); + (void)fclose(from); + return 0; + } + (void)fstat(fileno(from), &st); + (void)snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP); + if ((fd = mkstemp(f)) < 0) { + warn("Can't modify group `%s': mkstemp failed", group); + (void)fclose(from); + return 0; + } + if ((to = fdopen(fd, "w")) == NULL) { + warn("Can't modify group `%s': fdopen `%s' failed", group, f); + (void)fclose(from); + (void)close(fd); + (void)unlink(f); + return 0; + } + groupc = strlen(group); + while (fgets(buf, sizeof(buf), from) != NULL) { + cc = strlen(buf); + if ((colon = strchr(buf, ':')) == NULL) { + warnx("Badly formed entry `%s'", buf); + continue; + } + entc = (int)(colon - buf); + if (entc == groupc && + strncmp(group, buf, (unsigned) entc) == 0) { + if (newent == NULL) { + struct group *grp_rm; + struct passwd *user_pwd; + + /* + * Check that the group being removed + * isn't any user's Primary group. Just + * warn if it is. This could cause problems + * if the group GID was reused for a + * different purpose. + */ + + grp_rm = find_group_info(group); + while ((user_pwd = getpwent()) != NULL) { + if (user_pwd->pw_gid == grp_rm->gr_gid) { + warnx("Warning: group `%s'(%d)" + " is the primary group of" + " `%s'. Use caution if you" + " later add this GID.", + grp_rm->gr_name, + grp_rm->gr_gid, user_pwd->pw_name); + } + } + endpwent(); + continue; + } else { + cc = strlen(newent); + (void)strlcpy(buf, newent, sizeof(buf)); + } + } + if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) { + warn("Can't modify group `%s': short write to `%s'", + group, f); + (void)fclose(from); + (void)close(fd); + (void)unlink(f); + return 0; + } + } + (void)fclose(from); + (void)fclose(to); + if (rename(f, _PATH_GROUP) < 0) { + warn("Can't modify group `%s': can't rename `%s' to `%s'", + group, f, _PATH_GROUP); + (void)unlink(f); + return 0; + } + (void)chmod(_PATH_GROUP, st.st_mode & 07777); + if (newent == NULL) { + syslog(LOG_INFO, "group deleted: name=%s", group); + } else { + syslog(LOG_INFO, "group information modified: name=%s", group); + } + return 1; +} + +/* modify the group entries for all `groups', by adding `user' */ +static int +append_group(char *user, int ngroups, const char **groups) +{ + struct group *grp; + struct stat st; + FILE *from; + FILE *to; + char buf[MaxEntryLen]; + char f[MaxFileNameLen]; + char *colon; + int groupc; + int entc; + int fd; + int nc; + int cc; + int i; + int j; + + for (i = 0 ; i < ngroups ; i++) { + if ((grp = getgrnam(groups[i])) == NULL) { + warnx("Can't append group `%s' for user `%s'", + groups[i], user); + } else { + for (j = 0 ; grp->gr_mem[j] ; j++) { + if (strcmp(user, grp->gr_mem[j]) == 0) { + /* already in it */ + groups[i] = ""; + } + } + } + } + if ((from = fopen(_PATH_GROUP, "r+")) == NULL) { + warn("Can't append group(s) for `%s': can't open `%s'", + user, _PATH_GROUP); + return 0; + } + if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { + warn("Can't append group(s) for `%s': can't lock `%s'", + user, _PATH_GROUP); + (void)fclose(from); + return 0; + } + (void)fstat(fileno(from), &st); + (void)snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP); + if ((fd = mkstemp(f)) < 0) { + warn("Can't append group(s) for `%s': mkstemp failed", + user); + (void)fclose(from); + return 0; + } + if ((to = fdopen(fd, "w")) == NULL) { + warn("Can't append group(s) for `%s': fdopen `%s' failed", + user, f); + (void)fclose(from); + (void)close(fd); + (void)unlink(f); + return 0; + } + while (fgets(buf, sizeof(buf), from) != NULL) { + cc = strlen(buf); + if ((colon = strchr(buf, ':')) == NULL) { + warnx("Badly formed entry `%s'", buf); + continue; + } + entc = (int)(colon - buf); + for (i = 0 ; i < ngroups ; i++) { + if ((groupc = strlen(groups[i])) == 0) { + continue; + } + if (entc == groupc && + strncmp(groups[i], buf, (unsigned) entc) == 0) { + if ((nc = snprintf(&buf[cc - 1], + sizeof(buf) - cc + 1, "%s%s\n", + (buf[cc - 2] == ':') ? "" : ",", user)) < 0) { + warnx("Warning: group `%s' " + "entry too long", groups[i]); + } + cc += nc - 1; + } + } + if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) { + warn("Can't append group(s) for `%s':" + " short write to `%s'", user, f); + (void)fclose(from); + (void)close(fd); + (void)unlink(f); + return 0; + } + } + (void)fclose(from); + (void)fclose(to); + if (rename(f, _PATH_GROUP) < 0) { + warn("Can't append group(s) for `%s': " + "can't rename `%s' to `%s'", user, f, _PATH_GROUP); + (void)unlink(f); + return 0; + } + (void)chmod(_PATH_GROUP, st.st_mode & 07777); + return 1; +} + +/* the valid characters for login and group names */ +#define VALID_CHAR(c) (isalnum(c) || (c) == '.' || (c) == '_' || (c) == '-') + +/* return 1 if `login' is a valid login name */ +static int +valid_login(char *login_name, int allow_samba) +{ + unsigned char *cp; + + /* First character of a login name cannot be '-'. */ + if (*login_name == '-') { + return 0; + } + if (strlen(login_name) >= LOGIN_NAME_MAX) { + return 0; + } + for (cp = (unsigned char *)login_name ; *cp ; cp++) { + if (!VALID_CHAR(*cp)) { +#ifdef EXTENSIONS + /* check for a trailing '$' in a Samba user name */ + if (allow_samba && *cp == '$' && *(cp + 1) == 0x0) { + return 1; + } +#endif + return 0; + } + } + return 1; +} + +/* return 1 if `group' is a valid group name */ +static int +valid_group(char *group) +{ + unsigned char *cp; + + for (cp = (unsigned char *)group; *cp; cp++) { + if (!VALID_CHAR(*cp)) { + return 0; + } + } + return 1; +} + +/* find the next gid in the range lo .. hi */ +static int +getnextgid(int *gidp, int lo, int hi) +{ + for (*gidp = lo ; *gidp < hi ; *gidp += 1) { + if (getgrgid((gid_t)*gidp) == NULL) { + return 1; + } + } + return 0; +} + +#ifdef EXTENSIONS +/* save a range of uids */ +static int +save_range(rangelist_t *rlp, char *cp) +{ + int from; + int to; + int i; + + if (rlp->rl_rsize == 0) { + rlp->rl_rsize = 32; + NEWARRAY(range_t, rlp->rl_rv, rlp->rl_rsize, return(0)); + } else if (rlp->rl_rc == rlp->rl_rsize) { + rlp->rl_rsize *= 2; + RENEW(range_t, rlp->rl_rv, rlp->rl_rsize, return(0)); + } + if (rlp->rl_rv && sscanf(cp, "%d..%d", &from, &to) == 2) { + for (i = rlp->rl_defrc ; i < rlp->rl_rc ; i++) { + if (rlp->rl_rv[i].r_from == from && + rlp->rl_rv[i].r_to == to) { + break; + } + } + if (i == rlp->rl_rc) { + rlp->rl_rv[rlp->rl_rc].r_from = from; + rlp->rl_rv[rlp->rl_rc].r_to = to; + rlp->rl_rc += 1; + } + } else { + warnx("Bad range `%s'", cp); + return 0; + } + return 1; +} +#endif + +/* set the defaults in the defaults file */ +static int +setdefaults(user_t *up) +{ + char template[MaxFileNameLen]; + FILE *fp; + int ret; + int fd; +#ifdef EXTENSIONS + int i; +#endif + + (void)snprintf(template, sizeof(template), "%s.XXXXXX", + _PATH_USERMGMT_CONF); + if ((fd = mkstemp(template)) < 0) { + warn("Can't set defaults: can't mkstemp `%s' for writing", + _PATH_USERMGMT_CONF); + return 0; + } + if ((fp = fdopen(fd, "w")) == NULL) { + warn("Can't set defaults: can't fdopen `%s' for writing", + _PATH_USERMGMT_CONF); + return 0; + } + ret = 1; + if (fprintf(fp, "group\t\t%s\n", up->u_primgrp) <= 0 || + fprintf(fp, "base_dir\t%s\n", up->u_basedir) <= 0 || + fprintf(fp, "skel_dir\t%s\n", up->u_skeldir) <= 0 || + fprintf(fp, "shell\t\t%s\n", up->u_shell) <= 0 || +#ifdef EXTENSIONS + fprintf(fp, "class\t\t%s\n", up->u_class) <= 0 || + fprintf(fp, "homeperm\t0%o\n", up->u_homeperm) <= 0 || +#endif + fprintf(fp, "inactive\t%s\n", (up->u_inactive == NULL) ? + UNSET_INACTIVE : up->u_inactive) <= 0 || + fprintf(fp, "expire\t\t%s\n", (up->u_expire == NULL) ? + UNSET_EXPIRY : up->u_expire) <= 0 || + fprintf(fp, "preserve\t%s\n", (up->u_preserve == 0) ? + "false" : "true") <= 0) { + warn("Can't write to `%s'", _PATH_USERMGMT_CONF); + ret = 0; + } +#ifdef EXTENSIONS + for (i = (up->u_defrc != up->u_rc) ? up->u_defrc : 0; + i < up->u_rc ; i++) { + if (fprintf(fp, "range\t\t%d..%d\n", up->u_rv[i].r_from, + up->u_rv[i].r_to) <= 0) { + warn("Can't set defaults: can't write to `%s'", + _PATH_USERMGMT_CONF); + ret = 0; + } + } +#endif + (void)fclose(fp); + if (ret) { + ret = ((rename(template, _PATH_USERMGMT_CONF) == 0) && + (chmod(_PATH_USERMGMT_CONF, 0644) == 0)); + } + return ret; +} + +/* read the defaults file */ +static void +read_defaults(def_t *dp) +{ + struct stat st; + size_t lineno; + size_t len; + FILE *fp; + char *cp; + char *s; + user_t *up = &dp->user; + group_t *gp = &dp->group; + + (void)memset(dp, 0, sizeof(*dp)); + + memsave(&up->u_primgrp, DEF_GROUP, strlen(DEF_GROUP)); + memsave(&up->u_basedir, DEF_BASEDIR, strlen(DEF_BASEDIR)); + memsave(&up->u_skeldir, DEF_SKELDIR, strlen(DEF_SKELDIR)); + memsave(&up->u_shell, DEF_SHELL, strlen(DEF_SHELL)); + memsave(&up->u_comment, DEF_COMMENT, strlen(DEF_COMMENT)); +#ifdef EXTENSIONS + memsave(&up->u_class, DEF_CLASS, strlen(DEF_CLASS)); +#endif + up->u_rsize = 16; + up->u_defrc = 0; + NEWARRAY(range_t, up->u_rv, up->u_rsize, exit(1)); + up->u_inactive = DEF_INACTIVE; + up->u_expire = DEF_EXPIRE; + gp->g_rsize = 16; + gp->g_defrc = 0; + NEWARRAY(range_t, gp->g_rv, gp->g_rsize, exit(1)); + if ((fp = fopen(_PATH_USERMGMT_CONF, "r")) == NULL) { + if (stat(_PATH_USERMGMT_CONF, &st) < 0 && !setdefaults(up)) { + warn("Can't create `%s' defaults file", + _PATH_USERMGMT_CONF); + } + fp = fopen(_PATH_USERMGMT_CONF, "r"); + } + if (fp != NULL) { + while ((s = fparseln(fp, &len, &lineno, NULL, 0)) != NULL) { + if (strncmp(s, "group", 5) == 0) { + cp = skipspace(s + 5); + memsave(&up->u_primgrp, (char *)cp, strlen(cp)); + } else if (strncmp(s, "base_dir", 8) == 0) { + cp = skipspace(s + 8); + memsave(&up->u_basedir, (char *)cp, strlen(cp)); + } else if (strncmp(s, "skel_dir", 8) == 0) { + cp = skipspace(s + 8); + memsave(&up->u_skeldir, (char *)cp, strlen(cp)); + } else if (strncmp(s, "shell", 5) == 0) { + cp = skipspace(s + 5); + memsave(&up->u_shell, cp, strlen(cp)); +#ifdef EXTENSIONS + } else if (strncmp((char *)s, "class", 5) == 0) { + cp = skipspace(s + 5); + memsave(&up->u_class, cp, strlen(cp)); +#endif +#ifdef EXTENSIONS + } else if (strncmp(s, "homeperm", 8) == 0) { + for (cp = s + 8; *cp && + isspace((unsigned char)*cp); cp++) + ; + up->u_homeperm = strtoul(cp, NULL, 8); +#endif + } else if (strncmp(s, "inactive", 8) == 0) { + cp = skipspace(s + 8); + if (strcmp(cp, UNSET_INACTIVE) == 0) { + if (up->u_inactive) { + FREE(up->u_inactive); + } + up->u_inactive = NULL; + } else { + memsave(&up->u_inactive, cp, strlen(cp)); + } +#ifdef EXTENSIONS + } else if (strncmp(s, "range", 5) == 0) { + cp = skipspace(s + 5); + (void)save_range(&up->u_r, cp); +#endif +#ifdef EXTENSIONS + } else if (strncmp(s, "preserve", 8) == 0) { + cp = skipspace(s + 8); + up->u_preserve = + (strncmp(cp, "true", 4) == 0) ? 1 : + (strncmp(cp, "yes", 3) == 0) ? 1 : atoi(cp); +#endif + } else if (strncmp(s, "expire", 6) == 0) { + cp = skipspace(s + 6); + if (strcmp(cp, UNSET_EXPIRY) == 0) { + if (up->u_expire) { + FREE(up->u_expire); + } + up->u_expire = NULL; + } else { + memsave(&up->u_expire, cp, strlen(cp)); + } +#ifdef EXTENSIONS + } else if (strncmp(s, "gid_range", 9) == 0) { + cp = skipspace(s + 9); + (void)save_range(&gp->g_r, cp); +#endif + } + (void)free(s); + } + (void)fclose(fp); + } + if (up->u_rc == 0) { + up->u_rv[up->u_rc].r_from = DEF_LOWUID; + up->u_rv[up->u_rc].r_to = DEF_HIGHUID; + up->u_rc += 1; + } + up->u_defrc = up->u_rc; + up->u_homeperm = DEF_HOMEPERM; +} + +/* return the next valid unused uid */ +static int +getnextuid(int sync_uid_gid, int *uid, int low_uid, int high_uid) +{ + for (*uid = low_uid ; *uid <= high_uid ; (*uid)++) { + if (getpwuid((uid_t)(*uid)) == NULL && *uid != NOBODY_UID) { + if (sync_uid_gid) { + if (getgrgid((gid_t)(*uid)) == NULL) { + return 1; + } + } else { + return 1; + } + } + } + return 0; +} + +/* structure which defines a password type */ +typedef struct passwd_type_t { + const char *type; /* optional type descriptor */ + size_t desc_length; /* length of type descriptor */ + size_t length; /* length of password */ + const char *regex; /* regexp to output the password */ + size_t re_sub; /* subscript of regexp to use */ +} passwd_type_t; + +static passwd_type_t passwd_types[] = { + { "$sha1", 5, 28, "\\$[^$]+\\$[^$]+\\$[^$]+\\$(.*)", 1 }, /* SHA1 */ + { "$2a", 3, 53, "\\$[^$]+\\$[^$]+\\$(.*)", 1 }, /* Blowfish */ + { "$1", 2, 34, NULL, 0 }, /* MD5 */ + { "", 0, DES_Len,NULL, 0 }, /* standard DES */ + { NULL, (size_t)~0, (size_t)~0, NULL, 0 } + /* none - terminate search */ +}; + +/* return non-zero if it's a valid password - check length for cipher type */ +static int +valid_password_length(char *newpasswd) +{ + passwd_type_t *pwtp; + regmatch_t matchv[10]; + regex_t r; + + for (pwtp = passwd_types; pwtp->desc_length != (size_t)~0; pwtp++) { + if (strncmp(newpasswd, pwtp->type, pwtp->desc_length) == 0) { + if (pwtp->regex == NULL) { + return strlen(newpasswd) == pwtp->length; + } + (void)regcomp(&r, pwtp->regex, REG_EXTENDED); + if (regexec(&r, newpasswd, 10, matchv, 0) == 0) { + regfree(&r); + return (int)(matchv[pwtp->re_sub].rm_eo - + matchv[pwtp->re_sub].rm_so) == + pwtp->length; + } + regfree(&r); + } + } + return 0; +} + +#ifdef EXTENSIONS +/* return 1 if `class' is a valid login class */ +static int +valid_class(char *class) +{ + login_cap_t *lc; + + if (class == NULL || *class == '\0') { + return 1; + } + /* + * Check if /etc/login.conf exists. login_getclass() will + * return 1 due to it not existing, so not informing the + * user the actual login class does not exist. + */ + + if (access(PATH_LOGINCONF, R_OK) == -1) { + warn("Access failed for `%s'; will not validate class `%s'", + PATH_LOGINCONF, class); + return 1; + } + + if ((lc = login_getclass(class)) != NULL) { + login_close(lc); + return 1; + } + return 0; +} + +/* return 1 if the `shellname' is a valid user shell */ +static int +valid_shell(const char *shellname) +{ + char *shellp; + + if (access(_PATH_SHELLS, R_OK) == -1) { + /* Don't exit */ + warn("Access failed for `%s'; will not validate shell `%s'", + _PATH_SHELLS, shellname); + return 1; + } + + /* if nologin is used as a shell, consider it a valid shell */ + if (strcmp(shellname, NOLOGIN) == 0) + return 1; + + while ((shellp = getusershell()) != NULL) + if (strcmp(shellp, shellname) == 0) + return 1; + + warnx("Shell `%s' not found in `%s'", shellname, _PATH_SHELLS); + + return access(shellname, X_OK) != -1; +} +#endif + +/* look for a valid time, return 0 if it was specified but bad */ +static int +scantime(time_t *tp, char *s) +{ + struct tm tm; + char *ep; + long val; + + *tp = 0; + if (s != NULL) { + (void)memset(&tm, 0, sizeof(tm)); + if (strptime(s, "%c", &tm) != NULL) { + *tp = mktime(&tm); + return (*tp == -1) ? 0 : 1; + } else if (strptime(s, "%B %d %Y", &tm) != NULL) { + *tp = mktime(&tm); + return (*tp == -1) ? 0 : 1; + } else { + errno = 0; + *tp = val = strtol(s, &ep, 10); + if (*ep != '\0' || *tp < -1 || errno == ERANGE) { + *tp = 0; + return 0; + } + if (*tp != val) { + return 0; + } + } + } + return 1; +} + +/* add a user */ +static int +adduser(char *login_name, user_t *up) +{ + struct group *grp; + struct stat st; + time_t expire; + time_t inactive; + char password[PasswordLength + 1]; + char home[MaxFileNameLen]; + char buf[MaxFileNameLen]; + int sync_uid_gid; + int masterfd; + int ptmpfd; + int gid; + int cc; + int i; + + if (!valid_login(login_name, up->u_allow_samba)) { + errx(EXIT_FAILURE, "Can't add user `%s': invalid login name", login_name); + } +#ifdef EXTENSIONS + if (!valid_class(up->u_class)) { + errx(EXIT_FAILURE, "Can't add user `%s': no such login class `%s'", + login_name, up->u_class); + } +#endif + if ((masterfd = open(_PATH_MASTERPASSWD, O_RDWR)) < 0) { + err(EXIT_FAILURE, "Can't add user `%s': can't open `%s'", + login_name, _PATH_MASTERPASSWD); + } + if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) { + err(EXIT_FAILURE, "Can't add user `%s': can't lock `%s'", + login_name, _PATH_MASTERPASSWD); + } + pw_init(); + if ((ptmpfd = pw_lock(WAITSECS)) < 0) { + int serrno = errno; + (void)close(masterfd); + errno = serrno; + err(EXIT_FAILURE, "Can't add user `%s': can't obtain pw_lock", + login_name); + } + while ((cc = read(masterfd, buf, sizeof(buf))) > 0) { + if (write(ptmpfd, buf, (size_t)(cc)) != cc) { + int serrno = errno; + (void)close(masterfd); + (void)close(ptmpfd); + (void)pw_abort(); + errno = serrno; + err(EXIT_FAILURE, "Can't add user `%s': " + "short write to /etc/ptmp", login_name); + } + } + (void)close(masterfd); + /* if no uid was specified, get next one in [low_uid..high_uid] range */ + sync_uid_gid = (strcmp(up->u_primgrp, "=uid") == 0); + if (up->u_uid == -1) { + int got_id = 0; + + /* + * Look for a free UID in the command line ranges (if any). + * These start after the ranges specified in the config file. + */ + for (i = up->u_defrc; !got_id && i < up->u_rc ; i++) { + got_id = getnextuid(sync_uid_gid, &up->u_uid, + up->u_rv[i].r_from, up->u_rv[i].r_to); + } + /* + * If there were no free UIDs in the command line ranges, + * try the ranges from the config file (there will always + * be at least one default). + */ + for (i = 0; !got_id && i < up->u_defrc; i++) { + got_id = getnextuid(sync_uid_gid, &up->u_uid, + up->u_rv[i].r_from, up->u_rv[i].r_to); + } + if (!got_id) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't add user `%s': " + "can't get next uid for %d", login_name, + up->u_uid); + } + } + /* check uid isn't already allocated */ + if (!(up->u_flags & F_DUPUID) && getpwuid((uid_t)(up->u_uid)) != NULL) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't add user `%s': " + "uid %d is already in use", login_name, up->u_uid); + } + /* if -g=uid was specified, check gid is unused */ + if (sync_uid_gid) { + if (getgrgid((gid_t)(up->u_uid)) != NULL) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't add user `%s': " + "gid %d is already in use", login_name, + up->u_uid); + } + gid = up->u_uid; + } else if ((grp = getgrnam(up->u_primgrp)) != NULL) { + gid = grp->gr_gid; + } else if (is_number(up->u_primgrp) && + (grp = getgrgid((gid_t)atoi(up->u_primgrp))) != NULL) { + gid = grp->gr_gid; + } else { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't add user `%s': group %s not found", + login_name, up->u_primgrp); + } + /* check name isn't already in use */ + if (!(up->u_flags & F_DUPUID) && getpwnam(login_name) != NULL) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't add user `%s': " + "`%s' is already a user", login_name, login_name); + } + if (up->u_flags & F_HOMEDIR) { + (void)strlcpy(home, up->u_home, sizeof(home)); + } else { + /* if home directory hasn't been given, make it up */ + (void)snprintf(home, sizeof(home), "%s/%s", up->u_basedir, + login_name); + } + if (up->u_flags & F_SHELL) { +#ifdef EXTENSIONS + if (!valid_shell(up->u_shell)) { + int oerrno = errno; + (void)close(ptmpfd); + (void)pw_abort(); + errno = oerrno; + errx(EXIT_FAILURE, "Can't add user `%s': " + "Cannot access shell `%s'", + login_name, up->u_shell); + } +#endif + } + + if (!scantime(&inactive, up->u_inactive)) { + warnx("Warning: inactive time `%s' invalid, password expiry off", + up->u_inactive); + } + if (!scantime(&expire, up->u_expire) || expire == -1) { + warnx("Warning: expire time `%s' invalid, account expiry off", + up->u_expire); + expire = 0; /* Just in case. */ + } + if (lstat(home, &st) < 0 && !(up->u_flags & F_MKDIR)) { + warnx("Warning: home directory `%s' doesn't exist, " + "and -m was not specified", home); + } + password[sizeof(password) - 1] = '\0'; + if (up->u_password != NULL && valid_password_length(up->u_password)) { + (void)strlcpy(password, up->u_password, sizeof(password)); + } else { + (void)memset(password, '*', DES_Len); + password[DES_Len] = 0; + if (up->u_password != NULL) { + warnx("Password `%s' is invalid: setting it to `%s'", + up->u_password, password); + } + } + cc = snprintf(buf, sizeof(buf), "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", + login_name, + password, + up->u_uid, + gid, +#ifdef EXTENSIONS + up->u_class, +#else + "", +#endif + (long) inactive, + (long) expire, + up->u_comment, + home, + up->u_shell); + if (write(ptmpfd, buf, (size_t) cc) != cc) { + int serrno = errno; + (void)close(ptmpfd); + (void)pw_abort(); + errno = serrno; + err(EXIT_FAILURE, "Can't add user `%s': write failed", + login_name); + } + if (up->u_flags & F_MKDIR) { + if (lstat(home, &st) == 0) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, + "Can't add user `%s': home directory `%s' " + "already exists", login_name, home); + } else { + if (asystem("%s -p %s", MKDIR, home) != 0) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't add user `%s': " + "can't mkdir `%s'", login_name, home); + } + (void)copydotfiles(up->u_skeldir, up->u_uid, gid, home, + up->u_homeperm); + } + } + if (strcmp(up->u_primgrp, "=uid") == 0 && + getgrnam(login_name) == NULL && + !creategid(login_name, gid, login_name)) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't add user `%s': can't create gid %d ", + login_name, gid); + } + if (up->u_groupc > 0 && + !append_group(login_name, up->u_groupc, up->u_groupv)) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't add user `%s': can't append " + "to new groups", login_name); + } + (void)close(ptmpfd); +#if PW_MKDB_ARGC == 2 + if (pw_mkdb(login_name, 0) < 0) +#else + if (pw_mkdb() < 0) +#endif + { + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't add user `%s': pw_mkdb failed", + login_name); + } + syslog(LOG_INFO, "New user added: name=%s, uid=%d, gid=%d, home=%s, " + "shell=%s", login_name, up->u_uid, gid, home, up->u_shell); + return 1; +} + +/* remove a user from the groups file */ +static int +rm_user_from_groups(char *login_name) +{ + struct stat st; + regmatch_t matchv[10]; + regex_t r; + FILE *from; + FILE *to; + char line[MaxEntryLen]; + char buf[MaxEntryLen]; + char f[MaxFileNameLen]; + int fd; + int cc; + int sc; + + (void)snprintf(line, sizeof(line), "(:|,)(%s)(,|$)", login_name); + if ((sc = regcomp(&r, line, REG_EXTENDED|REG_NEWLINE)) != 0) { + (void)regerror(sc, &r, buf, sizeof(buf)); + warnx("Can't compile regular expression `%s' (%s)", line, + buf); + return 0; + } + if ((from = fopen(_PATH_GROUP, "r+")) == NULL) { + warn("Can't remove user `%s' from `%s': can't open `%s'", + login_name, _PATH_GROUP, _PATH_GROUP); + return 0; + } + if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { + warn("Can't remove user `%s' from `%s': can't lock `%s'", + login_name, _PATH_GROUP, _PATH_GROUP); + (void)fclose(from); + return 0; + } + (void)fstat(fileno(from), &st); + (void)snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP); + if ((fd = mkstemp(f)) < 0) { + warn("Can't remove user `%s' from `%s': mkstemp failed", + login_name, _PATH_GROUP); + (void)fclose(from); + return 0; + } + if ((to = fdopen(fd, "w")) == NULL) { + warn("Can't remove user `%s' from `%s': fdopen `%s' failed", + login_name, _PATH_GROUP, f); + (void)fclose(from); + (void)close(fd); + (void)unlink(f); + return 0; + } + while (fgets(buf, sizeof(buf), from) != NULL) { + cc = strlen(buf); + if (regexec(&r, buf, 10, matchv, 0) == 0) { + if (buf[(int)matchv[1].rm_so] == ',') { + matchv[2].rm_so = matchv[1].rm_so; + } else if (matchv[2].rm_eo != matchv[3].rm_eo) { + matchv[2].rm_eo = matchv[3].rm_eo; + } + cc -= (int) matchv[2].rm_eo; + sc = (int) matchv[2].rm_so; + if (fwrite(buf, sizeof(char), (size_t)sc, to) != sc || + fwrite(&buf[(int)matchv[2].rm_eo], sizeof(char), + (size_t)cc, to) != cc) { + warn("Can't remove user `%s' from `%s': " + "short write to `%s'", login_name, + _PATH_GROUP, f); + (void)fclose(from); + (void)close(fd); + (void)unlink(f); + return 0; + } + } else if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) { + warn("Can't remove user `%s' from `%s': " + "short write to `%s'", login_name, _PATH_GROUP, f); + (void)fclose(from); + (void)close(fd); + (void)unlink(f); + return 0; + } + } + (void)fclose(from); + (void)fclose(to); + if (rename(f, _PATH_GROUP) < 0) { + warn("Can't remove user `%s' from `%s': " + "can't rename `%s' to `%s'", + login_name, _PATH_GROUP, f, _PATH_GROUP); + (void)unlink(f); + return 0; + } + (void)chmod(_PATH_GROUP, st.st_mode & 07777); + return 1; +} + +/* check that the user or group is local, not from YP/NIS */ +static int +is_local(char *name, const char *file) +{ + FILE *fp; + char buf[MaxEntryLen]; + size_t len; + int ret; + + if ((fp = fopen(file, "r")) == NULL) { + err(EXIT_FAILURE, "Can't open `%s'", file); + } + len = strlen(name); + for (ret = 0 ; fgets(buf, sizeof(buf), fp) != NULL ; ) { + if (strncmp(buf, name, len) == 0 && buf[len] == ':') { + ret = 1; + break; + } + } + (void)fclose(fp); + return ret; +} + +/* modify a user */ +static int +moduser(char *login_name, char *newlogin, user_t *up, int allow_samba) +{ + struct passwd *pwp, pw; + struct group *grp; + const char *homedir; + char *locked_pwd; + size_t colonc; + size_t loginc; + size_t len; + FILE *master; + char newdir[MaxFileNameLen]; + char buf[MaxEntryLen]; + char pwbuf[MaxEntryLen]; + char *colon; + int masterfd; + int ptmpfd; + int error; + + if (!valid_login(newlogin, allow_samba)) { + errx(EXIT_FAILURE, "Can't modify user `%s': invalid login name", + login_name); + } + if (getpwnam_r(login_name, &pw, pwbuf, sizeof(pwbuf), &pwp) != 0 + || pwp == NULL) { + errx(EXIT_FAILURE, "Can't modify user `%s': no such user", + login_name); + } + if (!is_local(login_name, _PATH_MASTERPASSWD)) { + errx(EXIT_FAILURE, "Can't modify user `%s': must be a local user", + login_name); + } + /* keep dir name in case we need it for '-m' */ + homedir = pwp->pw_dir; + + if ((masterfd = open(_PATH_MASTERPASSWD, O_RDWR)) < 0) { + err(EXIT_FAILURE, "Can't modify user `%s': can't open `%s'", + login_name, _PATH_MASTERPASSWD); + } + if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) { + err(EXIT_FAILURE, "Can't modify user `%s': can't lock `%s'", + login_name, _PATH_MASTERPASSWD); + } + pw_init(); + if ((ptmpfd = pw_lock(WAITSECS)) < 0) { + int serrno = errno; + (void)close(masterfd); + errno = serrno; + err(EXIT_FAILURE, "Can't modify user `%s': " + "can't obtain pw_lock", login_name); + } + if ((master = fdopen(masterfd, "r")) == NULL) { + int serrno = errno; + (void)close(masterfd); + (void)close(ptmpfd); + (void)pw_abort(); + errno = serrno; + err(EXIT_FAILURE, "Can't modify user `%s': " + "fdopen fd for %s", login_name, _PATH_MASTERPASSWD); + } + if (up != NULL) { + if (up->u_flags & F_USERNAME) { + /* + * If changing name, + * check new name isn't already in use + */ + if (strcmp(login_name, newlogin) != 0 && + getpwnam(newlogin) != NULL) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't modify user `%s': " + "`%s' is already a user", login_name, + newlogin); + } + pwp->pw_name = newlogin; + + /* + * Provide a new directory name in case the + * home directory is to be moved. + */ + if (up->u_flags & F_MKDIR) { + (void)snprintf(newdir, sizeof(newdir), "%s/%s", + up->u_basedir, newlogin); + pwp->pw_dir = newdir; + } + } + if (up->u_flags & F_PASSWORD) { + if (up->u_password != NULL) { + if (!valid_password_length(up->u_password)) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, + "Can't modify user `%s': " + "invalid password: `%s'", + login_name, up->u_password); + } + if ((locked_pwd = + strstr(pwp->pw_passwd, LOCKED)) != NULL) { + /* + * account is locked - keep it locked + * and just change the password. + */ + if (asprintf(&locked_pwd, "%s%s", + LOCKED, up->u_password) == -1) { + (void)close(ptmpfd); + (void)pw_abort(); + err(EXIT_FAILURE, + "Can't modify user `%s': " + "asprintf failed", + login_name); + } + pwp->pw_passwd = locked_pwd; + } else { + pwp->pw_passwd = up->u_password; + } + } + } + + /* check whether we should lock the account. */ + if (up->u_locked == LOCK) { + /* check to see account if already locked. */ + if ((locked_pwd = strstr(pwp->pw_passwd, LOCKED)) + != NULL) { + warnx("Account is already locked"); + } else { + if (asprintf(&locked_pwd, "%s%s", LOCKED, + pwp->pw_passwd) == -1) { + (void)close(ptmpfd); + (void)pw_abort(); + err(EXIT_FAILURE, + "Can't modify user `%s': " + "asprintf failed", login_name); + } + pwp->pw_passwd = locked_pwd; + } + } else if (up->u_locked == UNLOCK) { + if ((locked_pwd = strstr(pwp->pw_passwd, LOCKED)) + == NULL) { + warnx("Can't modify user `%s': " + "account is not locked", login_name); + } else { + pwp->pw_passwd = locked_pwd + strlen(LOCKED); + } + } + + if (up->u_flags & F_UID) { + /* check uid isn't already allocated */ + if (!(up->u_flags & F_DUPUID) && + getpwuid((uid_t)(up->u_uid)) != NULL) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't modify user `%s': " + "uid `%d' is already in use", login_name, + up->u_uid); + } + pwp->pw_uid = up->u_uid; + } + if (up->u_flags & F_GROUP) { + /* if -g=uid was specified, check gid is unused */ + if (strcmp(up->u_primgrp, "=uid") == 0) { + if (getgrgid((gid_t)(pwp->pw_uid)) != NULL) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, + "Can't modify user `%s': " + "gid %d is already in use", + login_name, up->u_uid); + } + pwp->pw_gid = pwp->pw_uid; + } else if ((grp = getgrnam(up->u_primgrp)) != NULL) { + pwp->pw_gid = grp->gr_gid; + } else if (is_number(up->u_primgrp) && + (grp = getgrgid( + (gid_t)atoi(up->u_primgrp))) != NULL) { + pwp->pw_gid = grp->gr_gid; + } else { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't modify user `%s': " + "group %s not found", login_name, + up->u_primgrp); + } + } + if (up->u_flags & F_INACTIVE) { + if (!scantime(&pwp->pw_change, up->u_inactive)) { + warnx("Warning: inactive time `%s' invalid, " + "password expiry off", + up->u_inactive); + } + } + if (up->u_flags & F_EXPIRE) { + if (!scantime(&pwp->pw_expire, up->u_expire) || + pwp->pw_expire == -1) { + warnx("Warning: expire time `%s' invalid, " + "account expiry off", + up->u_expire); + pwp->pw_expire = 0; + } + } + if (up->u_flags & F_COMMENT) { + pwp->pw_gecos = up->u_comment; + } + if (up->u_flags & F_HOMEDIR) { + pwp->pw_dir = up->u_home; + } + if (up->u_flags & F_SHELL) { +#ifdef EXTENSIONS + if (!valid_shell(up->u_shell)) { + int oerrno = errno; + (void)close(ptmpfd); + (void)pw_abort(); + errno = oerrno; + errx(EXIT_FAILURE, "Can't modify user `%s': " + "Cannot access shell `%s'", + login_name, up->u_shell); + } + pwp->pw_shell = up->u_shell; +#else + pwp->pw_shell = up->u_shell; +#endif + } +#ifdef EXTENSIONS + if (up->u_flags & F_CLASS) { + if (!valid_class(up->u_class)) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't modify user `%s': " + "no such login class `%s'", login_name, + up->u_class); + } + pwp->pw_class = up->u_class; + } +#endif + } + loginc = strlen(login_name); + while (fgets(buf, sizeof(buf), master) != NULL) { + if ((colon = strchr(buf, ':')) == NULL) { + warnx("Malformed entry `%s'. Skipping", buf); + continue; + } + colonc = (size_t)(colon - buf); + if (strncmp(login_name, buf, loginc) == 0 && loginc == colonc) { + if (up != NULL) { + len = snprintf(buf, sizeof(buf), "%s:%s:%d:%d:" +#ifdef EXTENSIONS + "%s" +#endif + ":%ld:%ld:%s:%s:%s\n", + newlogin, + pwp->pw_passwd, + pwp->pw_uid, + pwp->pw_gid, +#ifdef EXTENSIONS + pwp->pw_class, +#endif + (long)pwp->pw_change, + (long)pwp->pw_expire, + pwp->pw_gecos, + pwp->pw_dir, + pwp->pw_shell); + if (write(ptmpfd, buf, len) != len) { + int serrno = errno; + (void)close(ptmpfd); + (void)pw_abort(); + errno = serrno; + err(EXIT_FAILURE, "Can't modify user " + "`%s': write", login_name); + } + } + } else { + len = strlen(buf); + if (write(ptmpfd, buf, len) != len) { + int serrno = errno; + (void)close(masterfd); + (void)close(ptmpfd); + (void)pw_abort(); + errno = serrno; + err(EXIT_FAILURE, "Can't modify `%s': " + "write", login_name); + } + } + } + if (up != NULL) { + if ((up->u_flags & F_MKDIR) && + asystem("%s %s %s", MV, homedir, pwp->pw_dir) != 0) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't modify user `%s': " + "can't move `%s' to `%s'", + login_name, homedir, pwp->pw_dir); + } + if (up->u_groupc > 0 && + !append_group(newlogin, up->u_groupc, up->u_groupv)) { + (void)close(ptmpfd); + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't modify user `%s': " + "can't append `%s' to new groups", + login_name, newlogin); + } + } + (void)close(ptmpfd); + (void)fclose(master); +#if PW_MKDB_ARGC == 2 + if (up != NULL && strcmp(login_name, newlogin) == 0) { + error = pw_mkdb(login_name, 0); + } else { + error = pw_mkdb(NULL, 0); + } +#else + error = pw_mkdb(); +#endif + if (error < 0) { + (void)pw_abort(); + errx(EXIT_FAILURE, "Can't modify user `%s': pw_mkdb failed", + login_name); + } + if (up == NULL) { + syslog(LOG_INFO, "User removed: name=%s", login_name); + } else if (strcmp(login_name, newlogin) == 0) { + syslog(LOG_INFO, "User information modified: name=%s, uid=%d, " + "gid=%d, home=%s, shell=%s", + login_name, pwp->pw_uid, pwp->pw_gid, pwp->pw_dir, + pwp->pw_shell); + } else { + syslog(LOG_INFO, "User information modified: name=%s, " + "new name=%s, uid=%d, gid=%d, home=%s, shell=%s", + login_name, newlogin, pwp->pw_uid, pwp->pw_gid, + pwp->pw_dir, pwp->pw_shell); + } + return 1; +} + +#ifdef EXTENSIONS +/* see if we can find out the user struct */ +static struct passwd * +find_user_info(const char *name) +{ + struct passwd *pwp; + + if ((pwp = getpwnam(name)) != NULL) { + return pwp; + } + if (is_number(name) && (pwp = getpwuid((uid_t)atoi(name))) != NULL) { + return pwp; + } + return NULL; +} +#endif + +/* see if we can find out the group struct */ +static struct group * +find_group_info(const char *name) +{ + struct group *grp; + + if ((grp = getgrnam(name)) != NULL) { + return grp; + } + if (is_number(name) && (grp = getgrgid((gid_t)atoi(name))) != NULL) { + return grp; + } + return NULL; +} + +/* print out usage message, and then exit */ +void +usermgmt_usage(const char *prog) +{ + if (strcmp(prog, "useradd") == 0) { + (void)fprintf(stderr, "usage: %s -D [-F] [-b base-dir] " + "[-e expiry-time] [-f inactive-time]\n" + "\t[-g gid | name | =uid] [-k skel-dir] [-L login-class]\n" + "\t[-M homeperm] [-r lowuid..highuid] [-s shell]\n", prog); + (void)fprintf(stderr, "usage: %s [-moSv] [-b base-dir] " + "[-c comment] [-d home-dir] [-e expiry-time]\n" + "\t[-f inactive-time] [-G secondary-group] " + "[-g gid | name | =uid]\n" + "\t[-k skeletondir] [-L login-class] [-M homeperm] " + "[-p password]\n" + "\t[-r lowuid..highuid] [-s shell] [-u uid] user\n", + prog); + } else if (strcmp(prog, "usermod") == 0) { + (void)fprintf(stderr, "usage: %s [-FmoSv] [-C yes/no] " + "[-c comment] [-d home-dir] [-e expiry-time]\n" + "\t[-f inactive] [-G secondary-group] " + "[-g gid | name | =uid]\n" + "\t[-L login-class] [-l new-login] [-p password] " + "[-s shell] [-u uid]\n" + "\tuser\n", prog); + } else if (strcmp(prog, "userdel") == 0) { + (void)fprintf(stderr, "usage: %s -D [-p preserve-value]\n", prog); + (void)fprintf(stderr, + "usage: %s [-rSv] [-p preserve-value] user\n", prog); +#ifdef EXTENSIONS + } else if (strcmp(prog, "userinfo") == 0) { + (void)fprintf(stderr, "usage: %s [-e] user\n", prog); +#endif + } else if (strcmp(prog, "groupadd") == 0) { + (void)fprintf(stderr, "usage: %s [-ov] [-g gid]" + " [-r lowgid..highgid] group\n", prog); + } else if (strcmp(prog, "groupdel") == 0) { + (void)fprintf(stderr, "usage: %s [-v] group\n", prog); + } else if (strcmp(prog, "groupmod") == 0) { + (void)fprintf(stderr, + "usage: %s [-ov] [-g gid] [-n newname] group\n", prog); + } else if (strcmp(prog, "user") == 0 || strcmp(prog, "group") == 0) { + (void)fprintf(stderr, + "usage: %s ( add | del | mod | info ) ...\n", prog); +#ifdef EXTENSIONS + } else if (strcmp(prog, "groupinfo") == 0) { + (void)fprintf(stderr, "usage: %s [-ev] group\n", prog); +#endif + } + exit(EXIT_FAILURE); + /* NOTREACHED */ +} + +#ifdef EXTENSIONS +#define ADD_OPT_EXTENSIONS "M:p:r:vL:S" +#else +#define ADD_OPT_EXTENSIONS +#endif + +int +useradd(int argc, char **argv) +{ + def_t def; + user_t *up = &def.user; + int defaultfield; + int bigD; + int c; +#ifdef EXTENSIONS + int i; +#endif + + read_defaults(&def); + up->u_uid = -1; + defaultfield = bigD = 0; + while ((c = getopt(argc, argv, "DFG:b:c:d:e:f:g:k:mou:s:" + ADD_OPT_EXTENSIONS)) != -1) { + switch(c) { + case 'D': + bigD = 1; + break; + case 'F': + /* + * Setting -1 will force the new user to + * change their password as soon as they + * next log in - passwd(5). + */ + defaultfield = 1; + memsave(&up->u_inactive, "-1", strlen("-1")); + break; + case 'G': + while (up->u_groupc < NGROUPS_MAX && + (up->u_groupv[up->u_groupc] = strsep(&optarg, ",")) != NULL) { + if (up->u_groupv[up->u_groupc][0] != 0) { + up->u_groupc++; + } + } + if (optarg != NULL) { + warnx("Truncated list of secondary groups " + "to %d entries", NGROUPS_MAX); + } + break; +#ifdef EXTENSIONS + case 'S': + up->u_allow_samba = 1; + break; +#endif + case 'b': + defaultfield = 1; + memsave(&up->u_basedir, optarg, strlen(optarg)); + break; + case 'c': + memsave(&up->u_comment, optarg, strlen(optarg)); + break; + case 'd': + memsave(&up->u_home, optarg, strlen(optarg)); + up->u_flags |= F_HOMEDIR; + break; + case 'e': + defaultfield = 1; + memsave(&up->u_expire, optarg, strlen(optarg)); + break; + case 'f': + defaultfield = 1; + memsave(&up->u_inactive, optarg, strlen(optarg)); + break; + case 'g': + defaultfield = 1; + memsave(&up->u_primgrp, optarg, strlen(optarg)); + break; + case 'k': + defaultfield = 1; + memsave(&up->u_skeldir, optarg, strlen(optarg)); + break; +#ifdef EXTENSIONS + case 'L': + defaultfield = 1; + memsave(&up->u_class, optarg, strlen(optarg)); + break; +#endif + case 'm': + up->u_flags |= F_MKDIR; + break; +#ifdef EXTENSIONS + case 'M': + defaultfield = 1; + up->u_homeperm = strtoul(optarg, NULL, 8); + break; +#endif + case 'o': + up->u_flags |= F_DUPUID; + break; +#ifdef EXTENSIONS + case 'p': + memsave(&up->u_password, optarg, strlen(optarg)); + break; +#endif +#ifdef EXTENSIONS + case 'r': + defaultfield = 1; + (void)save_range(&up->u_r, optarg); + break; +#endif + case 's': + up->u_flags |= F_SHELL; + defaultfield = 1; + memsave(&up->u_shell, optarg, strlen(optarg)); + break; + case 'u': + up->u_uid = check_numeric(optarg, "uid"); + break; +#ifdef EXTENSIONS + case 'v': + verbose = 1; + break; +#endif + default: + usermgmt_usage("useradd"); + /* NOTREACHED */ + } + } + if (bigD) { + if (defaultfield) { + checkeuid(); + return setdefaults(up) ? EXIT_SUCCESS : EXIT_FAILURE; + } + (void)printf("group\t\t%s\n", up->u_primgrp); + (void)printf("base_dir\t%s\n", up->u_basedir); + (void)printf("skel_dir\t%s\n", up->u_skeldir); + (void)printf("shell\t\t%s\n", up->u_shell); +#ifdef EXTENSIONS + (void)printf("class\t\t%s\n", up->u_class); + (void)printf("homeperm\t0%o\n", up->u_homeperm); +#endif + (void)printf("inactive\t%s\n", (up->u_inactive == NULL) ? + UNSET_INACTIVE : up->u_inactive); + (void)printf("expire\t\t%s\n", (up->u_expire == NULL) ? + UNSET_EXPIRY : up->u_expire); +#ifdef EXTENSIONS + for (i = 0 ; i < up->u_rc ; i++) { + (void)printf("range\t\t%d..%d\n", + up->u_rv[i].r_from, up->u_rv[i].r_to); + } +#endif + return EXIT_SUCCESS; + } + argc -= optind; + argv += optind; + if (argc != 1) { + usermgmt_usage("useradd"); + } + checkeuid(); + openlog("useradd", LOG_PID, LOG_USER); + return adduser(*argv, up) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#ifdef EXTENSIONS +#define MOD_OPT_EXTENSIONS "p:vL:S" +#else +#define MOD_OPT_EXTENSIONS +#endif + +int +usermod(int argc, char **argv) +{ + def_t def; + user_t *up = &def.user; + char newuser[MaxUserNameLen + 1]; + int c, have_new_user; + + (void)memset(newuser, 0, sizeof(newuser)); + read_defaults(&def); + have_new_user = 0; + up->u_locked = -1; + while ((c = getopt(argc, argv, "C:FG:c:d:e:f:g:l:mos:u:" + MOD_OPT_EXTENSIONS)) != -1) { + switch(c) { + case 'G': + while (up->u_groupc < NGROUPS_MAX && + (up->u_groupv[up->u_groupc] = + strsep(&optarg, ",")) != NULL) { + if (up->u_groupv[up->u_groupc][0] != 0) { + up->u_groupc++; + } + } + if (optarg != NULL) { + warnx("Truncated list of secondary groups " + "to %d entries", NGROUPS_MAX); + } + up->u_flags |= F_SECGROUP; + break; +#ifdef EXTENSIONS + case 'S': + up->u_allow_samba = 1; + break; +#endif + case 'c': + memsave(&up->u_comment, optarg, strlen(optarg)); + up->u_flags |= F_COMMENT; + break; + case 'C': + if (strcasecmp(optarg, "yes") == 0) { + up->u_locked = LOCK; + } else if (strcasecmp(optarg, "no") == 0) { + up->u_locked = UNLOCK; + } else { + /* No idea. */ + errx(EXIT_FAILURE, + "Please type 'yes' or 'no'"); + } + break; + case 'F': + memsave(&up->u_inactive, "-1", strlen("-1")); + up->u_flags |= F_INACTIVE; + break; + case 'd': + memsave(&up->u_home, optarg, strlen(optarg)); + up->u_flags |= F_HOMEDIR; + break; + case 'e': + memsave(&up->u_expire, optarg, strlen(optarg)); + up->u_flags |= F_EXPIRE; + break; + case 'f': + memsave(&up->u_inactive, optarg, strlen(optarg)); + up->u_flags |= F_INACTIVE; + break; + case 'g': + memsave(&up->u_primgrp, optarg, strlen(optarg)); + up->u_flags |= F_GROUP; + break; + case 'l': + (void)strlcpy(newuser, optarg, sizeof(newuser)); + have_new_user = 1; + up->u_flags |= F_USERNAME; + break; +#ifdef EXTENSIONS + case 'L': + memsave(&up->u_class, optarg, strlen(optarg)); + up->u_flags |= F_CLASS; + break; +#endif + case 'm': + up->u_flags |= F_MKDIR; + break; + case 'o': + up->u_flags |= F_DUPUID; + break; +#ifdef EXTENSIONS + case 'p': + memsave(&up->u_password, optarg, strlen(optarg)); + up->u_flags |= F_PASSWORD; + break; +#endif + case 's': + memsave(&up->u_shell, optarg, strlen(optarg)); + up->u_flags |= F_SHELL; + break; + case 'u': + up->u_uid = check_numeric(optarg, "uid"); + up->u_flags |= F_UID; + break; +#ifdef EXTENSIONS + case 'v': + verbose = 1; + break; +#endif + default: + usermgmt_usage("usermod"); + /* NOTREACHED */ + } + } + if ((up->u_flags & F_MKDIR) && !(up->u_flags & F_HOMEDIR) && + !(up->u_flags & F_USERNAME)) { + warnx("Option 'm' useless without 'd' or 'l' -- ignored"); + up->u_flags &= ~F_MKDIR; + } + argc -= optind; + argv += optind; + if (argc != 1) { + usermgmt_usage("usermod"); + } + checkeuid(); + openlog("usermod", LOG_PID, LOG_USER); + return moduser(*argv, (have_new_user) ? newuser : *argv, up, + up->u_allow_samba) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#ifdef EXTENSIONS +#define DEL_OPT_EXTENSIONS "Dp:vS" +#else +#define DEL_OPT_EXTENSIONS +#endif + +int +userdel(int argc, char **argv) +{ + struct passwd *pwp; + def_t def; + user_t *up = &def.user; + char password[PasswordLength + 1]; + int defaultfield; + int rmhome; + int bigD; + int c; + + read_defaults(&def); + defaultfield = bigD = rmhome = 0; + while ((c = getopt(argc, argv, "r" DEL_OPT_EXTENSIONS)) != -1) { + switch(c) { +#ifdef EXTENSIONS + case 'D': + bigD = 1; + break; +#endif +#ifdef EXTENSIONS + case 'S': + up->u_allow_samba = 1; + break; +#endif +#ifdef EXTENSIONS + case 'p': + defaultfield = 1; + up->u_preserve = (strcmp(optarg, "true") == 0) ? 1 : + (strcmp(optarg, "yes") == 0) ? 1 : + atoi(optarg); + break; +#endif + case 'r': + rmhome = 1; + break; +#ifdef EXTENSIONS + case 'v': + verbose = 1; + break; +#endif + default: + usermgmt_usage("userdel"); + /* NOTREACHED */ + } + } +#ifdef EXTENSIONS + if (bigD) { + if (defaultfield) { + checkeuid(); + return setdefaults(up) ? EXIT_SUCCESS : EXIT_FAILURE; + } + (void)printf("preserve\t%s\n", (up->u_preserve) ? "true" : + "false"); + return EXIT_SUCCESS; + } +#endif + argc -= optind; + argv += optind; + if (argc != 1) { + usermgmt_usage("userdel"); + } + checkeuid(); + if ((pwp = getpwnam(*argv)) == NULL) { + warnx("No such user `%s'", *argv); + return EXIT_FAILURE; + } + if (rmhome) { + (void)removehomedir(pwp); + } + if (up->u_preserve) { + up->u_flags |= F_SHELL; + memsave(&up->u_shell, NOLOGIN, strlen(NOLOGIN)); + (void)memset(password, '*', DES_Len); + password[DES_Len] = 0; + memsave(&up->u_password, password, strlen(password)); + up->u_flags |= F_PASSWORD; + openlog("userdel", LOG_PID, LOG_USER); + return moduser(*argv, *argv, up, up->u_allow_samba) ? + EXIT_SUCCESS : EXIT_FAILURE; + } + if (!rm_user_from_groups(*argv)) { + return 0; + } + openlog("userdel", LOG_PID, LOG_USER); + return moduser(*argv, *argv, NULL, up->u_allow_samba) ? + EXIT_SUCCESS : EXIT_FAILURE; +} + +#ifdef EXTENSIONS +#define GROUP_ADD_OPT_EXTENSIONS "r:v" +#else +#define GROUP_ADD_OPT_EXTENSIONS +#endif + +/* add a group */ +int +groupadd(int argc, char **argv) +{ + def_t def; + group_t *gp = &def.group; + int dupgid; + int gid; + int c; + + gid = -1; + dupgid = 0; + read_defaults(&def); + while ((c = getopt(argc, argv, "g:o" GROUP_ADD_OPT_EXTENSIONS)) != -1) { + switch(c) { + case 'g': + gid = check_numeric(optarg, "gid"); + break; + case 'o': + dupgid = 1; + break; +#ifdef EXTENSIONS + case 'r': + (void)save_range(&gp->g_r, optarg); + break; + case 'v': + verbose = 1; + break; +#endif + default: + usermgmt_usage("groupadd"); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + if (argc != 1) { + usermgmt_usage("groupadd"); + } + if (gp->g_rc == 0) { + gp->g_rv[gp->g_rc].r_from = DEF_LOWUID; + gp->g_rv[gp->g_rc].r_to = DEF_HIGHUID; + gp->g_rc += 1; + } + gp->g_defrc = gp->g_rc; + checkeuid(); + if (gid == -1) { + int got_id = 0; + int i; + + /* + * Look for a free GID in the command line ranges (if any). + * These start after the ranges specified in the config file. + */ + for (i = gp->g_defrc; !got_id && i < gp->g_rc ; i++) { + got_id = getnextgid(&gid, + gp->g_rv[i].r_from, gp->g_rv[i].r_to); + } + /* + * If there were no free GIDs in the command line ranges, + * try the ranges from the config file (there will always + * be at least one default). + */ + for (i = 0; !got_id && i < gp->g_defrc; i++) { + got_id = getnextgid(&gid, + gp->g_rv[i].r_from, gp->g_rv[i].r_to); + } + if (!got_id) + errx(EXIT_FAILURE, "Can't add group: can't get next gid"); + } + if (!dupgid && getgrgid((gid_t) gid) != NULL) { + errx(EXIT_FAILURE, "Can't add group: gid %d is a duplicate", + gid); + } + if (!valid_group(*argv)) { + warnx("Invalid group name `%s'", *argv); + } + openlog("groupadd", LOG_PID, LOG_USER); + if (!creategid(*argv, gid, "")) + exit(EXIT_FAILURE); + + return EXIT_SUCCESS; +} + +#ifdef EXTENSIONS +#define GROUP_DEL_OPT_EXTENSIONS "v" +#else +#define GROUP_DEL_OPT_EXTENSIONS +#endif + +/* remove a group */ +int +groupdel(int argc, char **argv) +{ + int c; + + while ((c = getopt(argc, argv, "" GROUP_DEL_OPT_EXTENSIONS)) != -1) { + switch(c) { +#ifdef EXTENSIONS + case 'v': + verbose = 1; + break; +#endif + default: + usermgmt_usage("groupdel"); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + if (argc != 1) { + usermgmt_usage("groupdel"); + } + if (getgrnam(*argv) == NULL) { + errx(EXIT_FAILURE, "No such group `%s'", *argv); + } + checkeuid(); + openlog("groupdel", LOG_PID, LOG_USER); + if (!modify_gid(*argv, NULL)) + exit(EXIT_FAILURE); + + return EXIT_SUCCESS; +} + +#ifdef EXTENSIONS +#define GROUP_MOD_OPT_EXTENSIONS "v" +#else +#define GROUP_MOD_OPT_EXTENSIONS +#endif + +/* modify a group */ +int +groupmod(int argc, char **argv) +{ + struct group *grp; + char buf[MaxEntryLen]; + char *newname; + char **cpp; + int dupgid; + int gid; + int cc; + int c; + + gid = -1; + dupgid = 0; + newname = NULL; + while ((c = getopt(argc, argv, "g:on:" GROUP_MOD_OPT_EXTENSIONS)) != -1) { + switch(c) { + case 'g': + gid = check_numeric(optarg, "gid"); + break; + case 'o': + dupgid = 1; + break; + case 'n': + memsave(&newname, optarg, strlen(optarg)); + break; +#ifdef EXTENSIONS + case 'v': + verbose = 1; + break; +#endif + default: + usermgmt_usage("groupmod"); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + if (argc != 1) { + usermgmt_usage("groupmod"); + } + checkeuid(); + if (gid < 0 && newname == NULL) { + errx(EXIT_FAILURE, "Nothing to change"); + } + if (dupgid && gid < 0) { + errx(EXIT_FAILURE, "Duplicate which gid?"); + } + if (!dupgid && getgrgid((gid_t) gid) != NULL) { + errx(EXIT_FAILURE, "Can't modify group: gid %d is a duplicate", + gid); + } + if ((grp = find_group_info(*argv)) == NULL) { + errx(EXIT_FAILURE, "Can't find group `%s' to modify", *argv); + } + if (!is_local(*argv, _PATH_GROUP)) { + errx(EXIT_FAILURE, "Group `%s' must be a local group", *argv); + } + if (newname != NULL && !valid_group(newname)) { + warnx("Invalid group name `%s'", newname); + } + cc = snprintf(buf, sizeof(buf), "%s:%s:%d:", + (newname) ? newname : grp->gr_name, + grp->gr_passwd, + (gid < 0) ? grp->gr_gid : gid); + for (cpp = grp->gr_mem ; *cpp && cc < sizeof(buf) ; cpp++) { + cc += snprintf(&buf[cc], sizeof(buf) - cc, "%s%s", *cpp, + (cpp[1] == NULL) ? "" : ","); + } + cc += snprintf(&buf[cc], sizeof(buf) - cc, "\n"); + if (newname != NULL) + free(newname); + openlog("groupmod", LOG_PID, LOG_USER); + if (!modify_gid(*argv, buf)) + exit(EXIT_FAILURE); + + return EXIT_SUCCESS; +} + +#ifdef EXTENSIONS +/* display user information */ +int +userinfo(int argc, char **argv) +{ + struct passwd *pwp; + struct group *grp; + char buf[MaxEntryLen]; + char **cpp; + int exists; + int cc; + int i; + + exists = 0; + buf[0] = '\0'; + while ((i = getopt(argc, argv, "e")) != -1) { + switch(i) { + case 'e': + exists = 1; + break; + default: + usermgmt_usage("userinfo"); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + if (argc != 1) { + usermgmt_usage("userinfo"); + } + pwp = find_user_info(*argv); + if (exists) { + exit((pwp) ? EXIT_SUCCESS : EXIT_FAILURE); + } + if (pwp == NULL) { + errx(EXIT_FAILURE, "Can't find user `%s'", *argv); + } + (void)printf("login\t%s\n", pwp->pw_name); + (void)printf("passwd\t%s\n", pwp->pw_passwd); + (void)printf("uid\t%d\n", pwp->pw_uid); + for (cc = 0 ; (grp = getgrent()) != NULL ; ) { + for (cpp = grp->gr_mem ; *cpp ; cpp++) { + if (strcmp(*cpp, *argv) == 0 && + grp->gr_gid != pwp->pw_gid) { + cc += snprintf(&buf[cc], sizeof(buf) - cc, + "%s ", grp->gr_name); + } + } + } + if ((grp = getgrgid(pwp->pw_gid)) == NULL) { + (void)printf("groups\t%d %s\n", pwp->pw_gid, buf); + } else { + (void)printf("groups\t%s %s\n", grp->gr_name, buf); + } + (void)printf("change\t%s", pwp->pw_change > 0 ? + ctime(&pwp->pw_change) : pwp->pw_change == -1 ? + "NEXT LOGIN\n" : "NEVER\n"); + (void)printf("class\t%s\n", pwp->pw_class); + (void)printf("gecos\t%s\n", pwp->pw_gecos); + (void)printf("dir\t%s\n", pwp->pw_dir); + (void)printf("shell\t%s\n", pwp->pw_shell); + (void)printf("expire\t%s", pwp->pw_expire ? + ctime(&pwp->pw_expire) : "NEVER\n"); + return EXIT_SUCCESS; +} +#endif + +#ifdef EXTENSIONS +/* display user information */ +int +groupinfo(int argc, char **argv) +{ + struct group *grp; + char **cpp; + int exists; + int i; + + exists = 0; + while ((i = getopt(argc, argv, "ev")) != -1) { + switch(i) { + case 'e': + exists = 1; + break; + case 'v': + verbose = 1; + break; + default: + usermgmt_usage("groupinfo"); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + if (argc != 1) { + usermgmt_usage("groupinfo"); + } + grp = find_group_info(*argv); + if (exists) { + exit((grp) ? EXIT_SUCCESS : EXIT_FAILURE); + } + if (grp == NULL) { + errx(EXIT_FAILURE, "Can't find group `%s'", *argv); + } + (void)printf("name\t%s\n", grp->gr_name); + (void)printf("passwd\t%s\n", grp->gr_passwd); + (void)printf("gid\t%d\n", grp->gr_gid); + (void)printf("members\t"); + for (cpp = grp->gr_mem ; *cpp ; cpp++) { + (void)printf("%s", *cpp); + if (*(cpp + 1)) { + (void) printf(", "); + } + } + (void)fputc('\n', stdout); + return EXIT_SUCCESS; +} +#endif diff --git a/usr.sbin/user/useradd.8 b/usr.sbin/user/useradd.8 new file mode 100644 index 000000000..d5e27546c --- /dev/null +++ b/usr.sbin/user/useradd.8 @@ -0,0 +1,318 @@ +.\" $NetBSD: useradd.8,v 1.42 2009/01/14 02:18:28 reed Exp $ */ +.\" +.\" +.\" Copyright (c) 1999 Alistair G. Crooks. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +.\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd January 13, 2009 +.Dt USERADD 8 +.Os +.Sh NAME +.Nm useradd +.Nd add a user to the system +.Sh SYNOPSIS +.Nm +.Fl D +.Op Fl F +.Op Fl b Ar base-dir +.Op Fl e Ar expiry-time +.Op Fl f Ar inactive-time +.Op Fl g Ar gid | name | Li =uid +.Op Fl k Ar skel-dir +.Op Fl L Ar login-class +.Op Fl M Ar home-perm +.Op Fl r Ar lowuid Ns Li .. Ns Ar highuid +.Op Fl s Ar shell +.Nm +.Op Fl moSv +.Op Fl b Ar base-dir +.Op Fl c Ar comment +.Op Fl d Ar home-dir +.Op Fl e Ar expiry-time +.Op Fl f Ar inactive-time +.Op Fl G Ar secondary-group +.Op Fl g Ar gid | name | Li =uid +.Op Fl k Ar skel-dir +.Op Fl L Ar login-class +.Op Fl M Ar home-perm +.Op Fl p Ar password +.Op Fl r Ar lowuid Ns Li .. Ns Ar highuid +.Op Fl s Ar shell +.Op Fl u Ar uid +.Ar user +.Sh DESCRIPTION +The +.Nm useradd +utility adds a user to the system, creating and +populating a home directory if necessary. +Any skeleton files will be provided +for the new user if they exist in the +.Ar skel-dir +directory (see the +.Fl k +option). +Default values for +the base directory, +the time of password expiry, +the time of account expiry, +primary group, +the skeleton directory, +the range from which the uid will be allocated, +and default login shell +can be provided in the +.Pa /etc/usermgmt.conf +file, which, if running as root, is created using the built-in defaults if +it does not exist. +.Pp +The first form of the command shown above (using the +.Fl D +option) +sets and displays the defaults for the +.Nm +utility. +.Pp +See +.Xr user 8 +for more information about +.Dv EXTENSIONS . +.Bl -tag -width Ds +.It Fl b Ar base-dir +Set the default base directory. +This is the directory to which the +user directory is added, which will be created if the +.Fl m +option is specified and no +.Fl d +option is specified. +.It Fl D +without any further options, +.Fl D +will show the current defaults which +will be used by the +.Nm +utility. +Together with one of the options shown for the first version +of the command, +.Fl D +will set the default to be the new value. +See +.Xr usermgmt.conf 5 +for more information. +.It Fl e Ar expiry-time +Set the time at which the new user accounts will expire. +It should be entered in the form +.Dq month day year , +where month is the month name (the first three characters are +sufficient), day is the day of the month, and year is the year. +Time in seconds since the epoch (UTC) is also valid. +A value of 0 can be used to disable this feature. +.It Fl F +Force the user to change their password upon next login. +.It Fl f Ar inactive-time +Set the time at which passwords for the new user accounts will +expire. +Also see the +.Fl e +option above. +.It Fl g Ar gid | groupname | Li =uid +Set the default group for new users. +.It Fl k Ar skel-dir +Set the skeleton directory in which to find files with +which to populate new users' home directories. +.It Fl L Ar login-class +Set the default login class for new users. +See +.Xr login.conf 5 +for more information on user login classes. +This option is included if built with +.Dv EXTENSIONS . +.It Fl M Ar home-perm +sets the default permissions of the newly created home directory if +.Fl m +is given. +The permission is specified as an octal number, with or without a leading zero. +.It Fl r Ar lowuid Ns Li .. Ns Ar highuid +Set the low and high bounds of uid ranges for new users. +A new user can only be created if there are uids which can be +assigned from one of the free ranges. +This option is included if built with +.Dv EXTENSIONS . +.It Fl s Ar shell +Set the default login shell for new users. +.El +.Pp +In the second form of the command, +after setting any defaults, and then reading values from +.Pa /etc/usermgmt.conf , +the following command line options are processed: +.Bl -tag -width Ds +.It Fl b Ar base-directory +Set the base directory name, in which the user's new home +directory will be created, should the +.Fl m +option be specified. +.It Fl c Ar comment +Set the comment field (also, for historical reasons known as the +GECOS field) which will be added for the user, and typically will include +the user's full name, and, perhaps, contact information for the user. +.It Fl d Ar home-directory +Set the home directory which will be created and populated for the user, +should the +.Fl m +option be specified. +.It Fl e Ar expiry-time +Set the time at which the current password will expire for new +users. +It should be entered in the form +.Dq month day year , +where month is the month name (the first three characters are +sufficient), day is the day of the month, and year is the year. +Time in seconds since the epoch (UTC) is also valid. +A value of 0 can be used to disable this feature. +See +.Xr passwd 5 +for more details. +.It Fl f Ar inactive-time +Set the time at which new user accounts will expire. +Also see the +.Fl e +option above. +.It Fl G Ar secondary-group +Add the user to the secondary group +.Ar secondary-group +in the +.Pa /etc/group +file. +The +.Ar secondary-group +may be a comma-delimited list for multiple groups. +Or the option may be repeated for multiple groups. +(16 groups maximum.) +.It Fl g Ar gid | name | Li =uid +Give the group name or identifier to be used for the new user's primary group. +If this is +.Ql =uid , +then a uid and gid will be picked which are both unique +and the same, and a line added to +.Pa /etc/group +to describe the new group. +.It Fl k Ar skeleton directory +Give the skeleton directory in which to find files +with which to populate the new user's home directory. +.It Fl L Ar login-class +Set the login class for the user being created. +See +.Xr login.conf 5 +for more information on user login classes. +This option is included if built with +.Dv EXTENSIONS . +.It Fl M Ar home-perm +sets the permissions of the newly created home directory if +.Fl m +is given. +The permission is specified as an octal number, with or without a leading zero. +.It Fl m +Create a new home directory for the new user. +.It Fl o +Allow the new user to have a uid which is already in use for another user. +.It Fl p Ar password +Specify an already-encrypted password for the new user. +Encrypted passwords can be generated with +.Xr pwhash 1 . +The password can be changed later by using +.Xr chpass 1 +or +.Xr passwd 1 . +This option is included if built with +.Dv EXTENSIONS . +.It Fl S +Allow samba user names with a trailing dollar sign to be +added to the system. +This option is included if built with +.Dv EXTENSIONS . +.It Fl s Ar shell +Specify the login shell for the new user. +.It Fl u Ar uid +Specify a uid for the new user. +Boundaries for this value can be preset for all users +by using the +.Ar range +field in the +.Pa /etc/usermgmt.conf +file. +.It Fl v +Enable verbose mode - explain the commands as they are executed. +This option is included if built with +.Dv EXTENSIONS . +.El +.Pp +Once the information has been verified, +.Nm +uses +.Xr pwd_mkdb 8 +to update the user database. +This is run in the background, and, +at very large sites could take several minutes. +Until this update +is completed, the password file is unavailable for other updates +and the new information is not available to programs. +.Sh EXIT STATUS +.Ex -std useradd +.Sh FILES +.Bl -tag -width /etc/usermgmt.conf -compact +.It Pa /etc/usermgmt.conf +.It Pa /etc/skel/* +.It Pa /etc/login.conf +.El +.Sh SEE ALSO +.Xr chpass 1 , +.Xr passwd 1 , +.Xr pwhash 1 , +.Xr group 5 , +.Xr login.conf 5 , +.Xr passwd 5 , +.Xr usermgmt.conf 5 , +.Xr pwd_mkdb 8 , +.Xr user 8 , +.Xr userdel 8 , +.Xr usermod 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 1.5 . +It is based on the +.Ar addnerd +package by the same author. +.Sh AUTHORS +The +.Nm +utility was written by +.An Alistair G. Crooks +.Aq agc@NetBSD.org . +.Pp +Support for setting permissions of home directories was added by Hubert Feyrer. diff --git a/usr.sbin/user/userdel.8 b/usr.sbin/user/userdel.8 new file mode 100644 index 000000000..9c7203004 --- /dev/null +++ b/usr.sbin/user/userdel.8 @@ -0,0 +1,168 @@ +.\" $NetBSD: userdel.8,v 1.32 2006/05/29 01:38:33 hubertf Exp $ */ +.\" +.\" Copyright (c) 1999 Alistair G. Crooks. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +.\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd November 16, 2005 +.Dt USERDEL 8 +.Os +.Sh NAME +.Nm userdel +.Nd remove a user from the system +.Sh SYNOPSIS +.Nm +.Fl D +.Op Fl p Ar preserve-value +.Nm +.Op Fl rSv +.Op Fl p Ar preserve-value +.Ar user +.Sh DESCRIPTION +The +.Nm +utility removes a user from the system, optionally +removing that user's home directory and any subdirectories. +.Pp +Default values are taken from the information provided in the +.Pa /etc/usermgmt.conf +file, which, if running as root, is created using the built-in +defaults if it does not exist. +.Pp +The first form of the command shown above (using the +.Fl D +option) sets and displays the defaults for the +.Nm +utility. +.Pp +See +.Xr user 8 +for more information about +.Dv EXTENSIONS . +.Bl -tag -width Ds +.It Fl D +Without any further options, +.Fl D +will show the current defaults which will be used by the +.Nm +utility. +Together with one of the options shown for the first version +of the command, +.Fl D +will set the default to be the new value. +This option is included if built with +.Dv EXTENSIONS . +.It Fl p Ar preserve-value +Set the preservation value. +If this value is one of +.Ql true , +.Ql yes , +or a non-zero number, then the user login information will be +preserved. +This option is included if built with +.Dv EXTENSIONS . +.El +.Pp +In the second form of the command, +after setting any defaults, and then reading values from +.Pa /etc/usermgmt.conf , +the following command line options are processed: +.Bl -tag -width Ds +.It Fl p Ar preserve-value +Preserve the user information in the password file, +but do not allow the user to login, by switching the +password to an +.Dq impossible +one, and by setting the user's shell to the +.Xr nologin 8 +program. +This option can be helpful in preserving a user's +files for later use by members of that person's +group after the user has moved on. +This value can also be set in the +.Pa /etc/usermgmt.conf +file, using the +.Ql preserve +field. +If the field has any of the values +.Ql true , +.Ql yes , +or a non-zero number, then user information preservation will take +place. +This option is included if built with +.Dv EXTENSIONS . +.It Fl r +Remove the user's home directory, any subdirectories, +and any files and other entries in them. +.It Fl S +Allow a samba user name (with a trailing dollar sign) +to be deleted. +This option is included if built with +.Dv EXTENSIONS . +.It Fl v +Perform any actions in a verbose manner. +This option is included if built with +.Dv EXTENSIONS . +.El +.Pp +Once the information has been verified, +.Nm +uses +.Xr pwd_mkdb 8 +to update the user database. +This is run in the background, and, +at very large sites could take several minutes. +Until this update +is completed, the password file is unavailable for other updates +and the new information is not available to programs. +.Sh EXIT STATUS +.Ex -std userdel +.Sh FILES +.Bl -tag -width /etc/usermgmt.conf -compact +.It Pa /etc/usermgmt.conf +.El +.Sh SEE ALSO +.Xr passwd 5 , +.Xr usermgmt.conf 5 , +.Xr group 8 , +.Xr nologin 8 , +.Xr pwd_mkdb 8 , +.Xr user 8 , +.Xr useradd 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 1.5 . +It is based on the +.Ar addnerd +package by the same author. +.Sh AUTHORS +The +.Nm +utility was written by +.An Alistair G. Crooks +.Aq agc@NetBSD.org . diff --git a/usr.sbin/user/userinfo.8 b/usr.sbin/user/userinfo.8 new file mode 100644 index 000000000..205084626 --- /dev/null +++ b/usr.sbin/user/userinfo.8 @@ -0,0 +1,87 @@ +.\" $NetBSD: userinfo.8,v 1.15 2008/02/27 19:12:56 reed Exp $ */ +.\" +.\" Copyright (c) 1999 Alistair G. Crooks. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +.\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd November 16, 2005 +.Dt USERINFO 8 +.Os +.Sh NAME +.Nm userinfo +.Nd displays user information +.Sh SYNOPSIS +.Nm +.Op Fl e +.Ar user +.Sh DESCRIPTION +The +.Nm +utility retrieves the user information from the system. +The +.Nm +utility is only available if built with +.Dv EXTENSIONS . +See +.Xr user 8 +for more information. +.Pp +The following command line option is recognised: +.Bl -tag -width Ds +.It Fl e +Return 0 if the user exists, and non-zero if the +user does not exist, on the system. +No information is displayed. +This form of the command is useful for +scripts which need to check whether a particular user +name or uid is already in use on the system. +.El +.Pp +The +.Ar user +argument may either be a user's name, or a uid. +.Sh EXIT STATUS +.Ex -std userinfo +.Sh SEE ALSO +.Xr passwd 5 , +.Xr group 8 , +.Xr user 8 , +.Xr useradd 8 , +.Xr userdel 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 1.5 . +It is based on the +.Ar addnerd +package by the same author. +.Sh AUTHORS +The +.Nm +utility was written by +.An Alistair G. Crooks +.Aq agc@NetBSD.org . diff --git a/usr.sbin/user/usermgmt.conf.5 b/usr.sbin/user/usermgmt.conf.5 new file mode 100644 index 000000000..e6553ec5a --- /dev/null +++ b/usr.sbin/user/usermgmt.conf.5 @@ -0,0 +1,155 @@ +.\" $NetBSD: usermgmt.conf.5,v 1.7 2009/12/31 20:14:19 wiz Exp $ +.\" +.\" Copyright (c) 2002 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This document is derived from works contributed to The NetBSD Foundation +.\" by Grant Beattie. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd December 31, 2009 +.Dt USERMGMT.CONF 5 +.Os +.\" turn off hyphenation +.hym 999 +.Sh NAME +.Nm usermgmt.conf +.Nd user management tools configuration file +.Sh SYNOPSIS +.Nm usermgmt.conf +.Sh DESCRIPTION +The +.Nm usermgmt.conf +file defines the default values used by the user management tools, +.Xr useradd 8 +and friends. +.Pp +Options in this file can be set by manually editing +.Pa /etc/usermgmt.conf +or using the +.Fl D +option to +.Xr useradd 8 . +.Pp +.Bl -tag -width preserveX +.It Ic base_dir +sets the base directory name, in which new users' home directories +are created when using the +.Fl m +option to +.Xr useradd 8 . +.It Ic class +sets the default login class for new users. +See +.Xr login.conf 5 +for more information on user login classes. +.It Ic expire +sets the default time at which the current password expires. +This can be used to implement password aging. +Both the +.Ar expire +and +.Ar inactive +fields should be entered in the form +.Dq month day year , +where month is the month name (the first three characters are +sufficient), day is the day of the month, and year is the year. +Time in seconds since the epoch (UTC) is also valid. +A value of 0 can be used to disable this feature. +.It Ic group +sets the default primary group for new users. +If this is +.Ql =uid , +then a uid and gid will be picked which are both unique +and the same, and a line will be added to +.Pa /etc/group +to describe the new group. +It has the format: +.br +.Bd -ragged -offset indent -compact +.Ic group +.Ar gid | name | Li =uid +.Ed +.It Ic homeperm +sets the default permissions of the newly created home directory if +.Fl m +is given to +.Xr useradd 8 . +The permission is specified as an octal number, with or without a leading zero. +.It Ic inactive +sets the default time at which new accounts expire. +A value of 0 can be used to disable this feature. +Also see the +.Ar expire +field. +.It Ic password +specifies an already-encrypted default password. +.It Ic preserve +If this value is one of +.Ql true , +.Ql yes , +or a non-zero number, then the user login information will be +preserved when removing a user with +.Xr userdel 8 . +.It Ic range +specifies the uid boundaries for new users. +If unspecified, the default is +.Dq 1000..60000 . +It has the format: +.Bd -unfilled -offset indent -compact +.Ic range Ar starting-uid Ns Li .. Ns Ar ending-uid +.Ed +.It Ic gid_range +specifies the gid boundaries for new groups. +If unspecified, the default is +.Dq 1000..60000 . +It has the format: +.Bd -unfilled -offset indent -compact +.Ic gid_range Ar starting-gid Ns Li .. Ns Ar ending-gid +.Ed +.It Ic shell +sets the default login shell for new users. +.It Ic skel_dir +sets the default skeleton directory in which to find files +with which to populate the new user's home directory. +.El +.Sh FILES +.Bl -tag -width /etc/usermgmt.conf -compact +.It Pa /etc/usermgmt.conf +.It Pa /etc/skel/* +.It Pa /etc/login.conf +.El +.Sh SEE ALSO +.Xr login.conf 5 , +.Xr passwd 5 , +.Xr user 8 , +.Xr useradd 8 , +.Xr userdel 8 , +.Xr usermod 8 +.Sh HISTORY +The +.Nm +configuration file first appeared in +.Nx 1.5 . diff --git a/usr.sbin/user/usermgmt.h b/usr.sbin/user/usermgmt.h new file mode 100644 index 000000000..45042acf3 --- /dev/null +++ b/usr.sbin/user/usermgmt.h @@ -0,0 +1,47 @@ +/* $NetBSD: usermgmt.h,v 1.8 2005/11/25 08:00:18 agc Exp $ */ + +/* + * Copyright (c) 1999 Alistair G. Crooks. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef USERMGMT_H_ +#define USERMGMT_H_ + +int useradd(int, char **); +int usermod(int, char **); +int userdel(int, char **); +int groupadd(int, char **); +int groupdel(int, char **); +int groupmod(int, char **); + +#ifdef EXTENSIONS +int userinfo(int, char **); +int groupinfo(int, char **); +#endif + +void usermgmt_usage(const char *); + +#endif diff --git a/usr.sbin/user/usermod.8 b/usr.sbin/user/usermod.8 new file mode 100644 index 000000000..42ff97996 --- /dev/null +++ b/usr.sbin/user/usermod.8 @@ -0,0 +1,259 @@ +.\" $NetBSD: usermod.8,v 1.32 2009/03/11 17:54:03 dyoung Exp $ */ +.\" +.\" Copyright (c) 1999 Alistair G. Crooks. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +.\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd January 13, 2009 +.Dt USERMOD 8 +.Os +.Sh NAME +.Nm usermod +.Nd modify user login information +.Sh SYNOPSIS +.Nm +.Op Fl FmoSv +.Op Fl C Ar yes/no +.Op Fl c Ar comment +.Op Fl d Ar home-dir +.Op Fl e Ar expiry-time +.Op Fl f Ar inactive-time +.Op Fl G Ar secondary-group +.Op Fl g Ar gid | name | Li =uid +.Op Fl L Ar login-class +.Op Fl l Ar new-login +.Op Fl p Ar password +.Op Fl s Ar shell +.Op Fl u Ar uid +.Ar user +.Sh DESCRIPTION +The +.Nm +utility modifies user login information on the system. +.Pp +Default values are taken from the information provided in the +.Pa /etc/usermgmt.conf +file, which, if running as root, is created using the built-in defaults if +it does not exist. +.Pp +See +.Xr user 8 +for more information about +.Dv EXTENSIONS . +.Pp +After setting any defaults, and then reading values from +.Pa /etc/usermgmt.conf , +the following command line options are processed: +.Bl -tag -width Ds +.It Fl C Ar yes/no +Enable user accounts to be temporary locked/closed. +The +.Ar yes/no +operand can be given as +.Dq Ar yes +to lock the account or +.Dq Ar no +to unlock the account. +.It Fl c Ar comment +Set the comment field (also, for historical reasons known as the +GECOS field) for the user. +The comment field will typically include +the user's full name and, perhaps, contact information for the user. +.It Fl d Ar home-directory +Set the home directory without populating it; if the +.Fl m +option is specified, tries to move the old home directory to +.Ar home-directory . +.It Fl e Ar expiry-time +Set the time at which the account expires. +This can be used to implement password aging. +It should be entered in the form +.Dq month day year , +where month is the month name (the first three characters are +sufficient), day is the day of the month, and year is the year. +Time in seconds since the epoch (UTC) is also valid. +A value of 0 can be used to disable this feature. +This value can be preset for all users using the +.Ar expire +field in the +.Pa /etc/usermgmt.conf +file. +See +.Xr usermgmt.conf 5 +for more details. +.It Fl F +Force the user to change their password upon next login. +.It Fl f Ar inactive-time +Set the time at which the password expires. +See the +.Fl e +option. +.It Fl G Ar secondary-group +Specify a secondary group to which the user will be added in the +.Pa /etc/group +file. +The +.Ar secondary-group +may be a comma-delimited list for multiple groups. +Or the option may be repeated for multiple groups. +(16 groups maximum.) +.It Fl g Ar gid | name | Li =uid +Give the group name or identifier to be used for the user's primary group. +If this is +.Ql =uid , +then a uid and gid will be picked which are both unique +and the same, and a line will be added to +.Pa /etc/group +to describe the new group. +This value can be preset for all users by using the +.Ar group +field in the +.Pa /etc/usermgmt.conf +file. +See +.Xr usermgmt.conf 5 +for more details. +.It Fl L Ar login-class +Set the login class for the user. +See +.Xr login.conf 5 +for more information on user login classes. +This value can be preset for all users by using the +.Ar class +field in the +.Pa /etc/usermgmt.conf +file. +See +.Xr usermgmt.conf 5 +for more details. +This option is included if built with +.Dv EXTENSIONS . +.It Fl l Ar new-user +Give the new user name. +It can consist of alphanumeric characters and the characters +.Ql \&. , +.Ql \&- , +and +.Ql \&_ . +.It Fl m +Move the home directory from its old position to the new one. +If +.Fl d +is not specified, the +.Ar new-user +argument of the +.Fl l +option is used; one of +.Fl d +and +.Fl l +is needed. +.It Fl o +Allow duplicate uids to be given. +.It Fl p Ar password +Specify an already-encrypted password for the user. +This password can then be changed by using the +.Xr chpass 1 +utility. +This value can be preset for all users by using the +.Ar password +field in the +.Pa /etc/usermgmt.conf +file. +See +.Xr usermgmt.conf 5 +for more details. +This option is included if built with +.Dv EXTENSIONS . +.It Fl S +Allow samba user names with a trailing dollar sign to be modified. +This option is included if built with +.Dv EXTENSIONS . +.It Fl s Ar shell +Specify the login shell for the user. +This value can be preset for all users by using the +.Ar shell +field in the +.Pa /etc/usermgmt.conf +file. +See +.Xr usermgmt.conf 5 +for more details. +.It Fl u Ar uid +Specify a new uid for the user. +Boundaries for this value can be preset for all users by using the +.Ar range +field in the +.Pa /etc/usermgmt.conf +file. +See +.Xr usermgmt.conf 5 +for more details. +.It Fl v +Enable verbose mode - explain the commands as they are executed. +This option is included if built with +.Dv EXTENSIONS . +.El +.Pp +Once the information has been verified, +.Nm +uses +.Xr pwd_mkdb 8 +to update the user database. +This is run in the background. +At very large sites this can take several minutes. +Until this update +is completed, the password file is unavailable for other updates +and the new information is not available to programs. +.Sh EXIT STATUS +.Ex -std usermod +.Sh FILES +.Bl -tag -width /etc/usermgmt.conf -compact +.It Pa /etc/usermgmt.conf +.El +.Sh SEE ALSO +.Xr chpass 1 , +.Xr group 5 , +.Xr passwd 5 , +.Xr usermgmt.conf 5 , +.Xr pwd_mkdb 8 , +.Xr user 8 , +.Xr useradd 8 , +.Xr userdel 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 1.5 . +It is based on the +.Ar addnerd +package by the same author. +.Sh AUTHORS +The +.Nm +utility was written by +.An Alistair G. Crooks +.Aq agc@NetBSD.org . diff --git a/usr.sbin/vipw/Makefile b/usr.sbin/vipw/Makefile new file mode 100644 index 000000000..8b1123cc1 --- /dev/null +++ b/usr.sbin/vipw/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.5 1996/05/15 23:23:45 jtc Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= vipw +SRCS= vipw.c +DPADD= ${LIBUTIL} +LDADD= -lutil +MAN= vipw.8 + +.include diff --git a/usr.sbin/vipw/vipw.8 b/usr.sbin/vipw/vipw.8 new file mode 100644 index 000000000..2f7b6e46f --- /dev/null +++ b/usr.sbin/vipw/vipw.8 @@ -0,0 +1,122 @@ +.\" $NetBSD: vipw.8,v 1.15 2005/09/05 03:37:15 hubertf Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)vipw.8 8.1 (Berkeley) 6/6/93 +.\" +.Dd September 4, 2005 +.Dt VIPW 8 +.Os +.Sh NAME +.Nm vipw +.Nd edit the password file +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl d Ar directory +.Ek +.Sh DESCRIPTION +.Nm +edits the password file after setting the appropriate locks, +and does any necessary processing after the password file is unlocked. +If the password file is already locked for editing by another user, +.Nm +will ask you +to try again later. The default editor for +.Nm +is +.Xr vi 1 . +.Pp +.Nm +performs a number of consistency checks on the password entries, +and will not allow a password file with a +.Dq mangled +entry to be +installed. +If +.Nm +rejects the new password file, the user is prompted to re-enter +the edit session. +.Pp +Once the information has been verified, +.Nm +uses +.Xr pwd_mkdb 8 +to update the user database. This is run in the background, and, +at very large sites could take several minutes. Until this update +is completed, the password file is unavailable for other updates +and the new information is not available to programs. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl d Ar directory +Change the root directory of the password file from +.Dq Pa / +to +.Ar directory . +.El +.Pp +If a +.Nm +session is killed it may leave +.Dq Pa /etc/ptmp , +which will cause future +.Nm +executions to fail with +.Dq Pa vipw: the passwd file is busy , +until it is removed. +.Sh ENVIRONMENT +If the following environment variable exists it will be used by +.Nm : +.Bl -tag -width EDITOR +.It Ev EDITOR +The editor specified by the string +.Ev EDITOR +will be invoked instead of the default editor +.Xr vi 1 . +.El +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /etc/master.passwd +The current password file. +.It Pa /etc/ptmp +Temporary copy of the password file used while editing. +.El +.Sh SEE ALSO +.Xr chpass 1 , +.Xr passwd 1 , +.Xr pwhash 1 , +.Xr passwd 5 , +.Xr passwd.conf 5 , +.Xr pwd_mkdb 8 , +.Xr user 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.0 . diff --git a/usr.sbin/vipw/vipw.c b/usr.sbin/vipw/vipw.c new file mode 100644 index 000000000..a6f97b2fd --- /dev/null +++ b/usr.sbin/vipw/vipw.c @@ -0,0 +1,150 @@ +/* $NetBSD: vipw.c,v 1.14 2009/04/19 00:44:49 lukem Exp $ */ + +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)vipw.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: vipw.c,v 1.14 2009/04/19 00:44:49 lukem Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main __P((int, char **)); +static void copyfile __P((int, int)); +static void usage __P((void)); + +char mpwd[MAXPATHLEN], mpwdl[MAXPATHLEN]; + +int +main(int argc, char *argv[]) +{ + const char *prefix; + int pfd, tfd; + struct stat begin, end; + int ch; + + prefix = ""; + while ((ch = getopt(argc, argv, "d:")) != -1) { + switch (ch) { + case 'd': + prefix = optarg; + if (pw_setprefix(prefix) < 0) + err(1, "%s", prefix); + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 0) + usage(); + + (void)snprintf(mpwd, sizeof(mpwd), "%s%s", prefix, _PATH_MASTERPASSWD); + (void)snprintf(mpwdl, sizeof(mpwdl), "%s%s", prefix, + _PATH_MASTERPASSWD_LOCK); + + pw_init(); + tfd = pw_lock(0); + if (tfd < 0) { + if (errno == EEXIST) + errx(1, "the passwd file is busy."); + else + err(1, "%s", mpwdl); + } + + pfd = open(mpwd, O_RDONLY, 0); + if (pfd < 0) + pw_error(mpwd, 1, 1); + copyfile(pfd, tfd); + (void)close(tfd); + + for (;;) { + if (stat(mpwdl, &begin)) + pw_error(mpwdl, 1, 1); + pw_edit(0, NULL); + if (stat(mpwdl, &end)) + pw_error(mpwdl, 1, 1); + if (begin.st_mtime == end.st_mtime && + begin.st_mtimensec == end.st_mtimensec) { + warnx("no changes made"); + pw_error((char *)NULL, 0, 0); + } + if (pw_mkdb(NULL, 0) == 0) + break; + pw_prompt(); + } + return 0; +} + +static void +copyfile(int from, int to) +{ + int nr, nw, off; + char buf[8*1024]; + + while ((nr = read(from, buf, sizeof(buf))) > 0) + for (off = 0; off < nr; nr -= nw, off += nw) + if ((nw = write(to, buf + off, nr)) < 0) + pw_error(mpwdl, 1, 1); + if (nr < 0) + pw_error(mpwd, 1, 1); +} + +static void +usage(void) +{ + + (void)fprintf(stderr, "usage: %s [-d directory]\n", getprogname()); + exit(1); +}