]> Zhao Yanbai Git Server - minix.git/commitdiff
Switch to NetBSD passwd format
authorBen Gras <ben@minix3.org>
Fri, 30 Sep 2011 10:18:10 +0000 (10:18 +0000)
committerBen Gras <ben@minix3.org>
Mon, 14 Nov 2011 14:47:42 +0000 (14:47 +0000)
Based on work by Vivek Prakash and Gianluca Guida.

See UPDATING about caveats on currently existing accounts.

. restores netbsd libc pwcache functions

96 files changed:
Makefile
commands/Makefile
commands/adduser/Makefile [deleted file]
commands/adduser/adduser.sh [deleted file]
commands/chown/chown.c
commands/ftpd200/Makefile
commands/login/Makefile
commands/login/login.c
commands/ls/Makefile
commands/passwd/Makefile [deleted file]
commands/passwd/passwd.c [deleted file]
commands/pwdauth/Makefile
commands/pwdauth/pwdauth.c
commands/su/Makefile
docs/UPDATING
drivers/ramdisk/Makefile
etc/Makefile
etc/group
etc/master.passwd [new file with mode: 0644]
etc/passwd [deleted file]
etc/shadow [deleted file]
lib/Makefile
lib/libc/other/Makefile.inc
lib/libc/other/crypt.c [deleted file]
lib/libutil/Makefile
lib/libutil/compat/Makefile.inc
lib/libutil/login_cap.c
lib/nbsd_libc/gen/pwcache.c
lib/nbsd_libcompat_minix/Makefile
lib/nbsd_libcompat_minix/getpwent.c [deleted file]
lib/nbsd_libcompat_minix/passwd.c [deleted file]
man/man1/Makefile
man/man1/passwd.1 [deleted file]
man/man8/Makefile
man/man8/adduser.8 [deleted file]
man/man8/pwdauth.8
nbsd_include/arch/i386/include/param.h
nbsd_include/login_cap.h
nbsd_include/pwd.h
nbsd_include/sys/dirent.h
tools/nbsd_ports
tools/release.sh
usr.bin/Makefile
usr.bin/Makefile.inc
usr.bin/chpass/Makefile [new file with mode: 0644]
usr.bin/chpass/chpass.1 [new file with mode: 0644]
usr.bin/chpass/chpass.c [new file with mode: 0644]
usr.bin/chpass/chpass.h [new file with mode: 0644]
usr.bin/chpass/edit.c [new file with mode: 0644]
usr.bin/chpass/field.c [new file with mode: 0644]
usr.bin/chpass/pathnames.h [new file with mode: 0644]
usr.bin/chpass/pw_yp.c [new file with mode: 0644]
usr.bin/chpass/table.c [new file with mode: 0644]
usr.bin/chpass/util.c [new file with mode: 0644]
usr.bin/m4/Makefile
usr.bin/newgrp/Makefile [new file with mode: 0644]
usr.bin/newgrp/grutil.c [new file with mode: 0644]
usr.bin/newgrp/grutil.h [new file with mode: 0644]
usr.bin/newgrp/newgrp.1 [new file with mode: 0644]
usr.bin/newgrp/newgrp.c [new file with mode: 0644]
usr.bin/passwd/Makefile [new file with mode: 0644]
usr.bin/passwd/extern.h [new file with mode: 0644]
usr.bin/passwd/kpasswd.1 [new file with mode: 0644]
usr.bin/passwd/krb5_passwd.c [new file with mode: 0644]
usr.bin/passwd/local_passwd.c [new file with mode: 0644]
usr.bin/passwd/pam_passwd.c [new file with mode: 0644]
usr.bin/passwd/passwd.1 [new file with mode: 0644]
usr.bin/passwd/passwd.c [new file with mode: 0644]
usr.bin/passwd/yp_passwd.c [new file with mode: 0644]
usr.bin/passwd/yppasswd.1 [new file with mode: 0644]
usr.bin/top/top.c
usr.sbin/Makefile [new file with mode: 0644]
usr.sbin/Makefile.inc [new file with mode: 0644]
usr.sbin/pwd_mkdb/Makefile [new file with mode: 0644]
usr.sbin/pwd_mkdb/pwd_mkdb.8 [new file with mode: 0644]
usr.sbin/pwd_mkdb/pwd_mkdb.c [new file with mode: 0644]
usr.sbin/user/Makefile [new file with mode: 0644]
usr.sbin/user/defs.h [new file with mode: 0644]
usr.sbin/user/group.8 [new file with mode: 0644]
usr.sbin/user/groupadd.8 [new file with mode: 0644]
usr.sbin/user/groupdel.8 [new file with mode: 0644]
usr.sbin/user/groupinfo.8 [new file with mode: 0644]
usr.sbin/user/groupmod.8 [new file with mode: 0644]
usr.sbin/user/main.c [new file with mode: 0644]
usr.sbin/user/out [new file with mode: 0644]
usr.sbin/user/user.8 [new file with mode: 0644]
usr.sbin/user/user.c [new file with mode: 0644]
usr.sbin/user/useradd.8 [new file with mode: 0644]
usr.sbin/user/userdel.8 [new file with mode: 0644]
usr.sbin/user/userinfo.8 [new file with mode: 0644]
usr.sbin/user/usermgmt.conf.5 [new file with mode: 0644]
usr.sbin/user/usermgmt.h [new file with mode: 0644]
usr.sbin/user/usermod.8 [new file with mode: 0644]
usr.sbin/vipw/Makefile [new file with mode: 0644]
usr.sbin/vipw/vipw.8 [new file with mode: 0644]
usr.sbin/vipw/vipw.c [new file with mode: 0644]

index 462f30779f7c7d1092345436a88054fefea36512..120c700f21481a47c18023b8ce7c76fb467e0b6c 100644 (file)
--- 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
index 73f36e48c0dd869905ecea65acbcdbabd0a517ae..32f0e93c9fc0e98bd24014167bf969f499146b30 100644 (file)
@@ -2,7 +2,7 @@
 
 .include <bsd.own.mk>
 
-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 (file)
index a922aa5..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-SCRIPTS= adduser.sh
-MAN=
-
-.include <bsd.prog.mk>
diff --git a/commands/adduser/adduser.sh b/commands/adduser/adduser.sh
deleted file mode 100644 (file)
index 886d0fb..0000000
+++ /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
index a6a0f04ff28e340277e03c5caf212f3307eecb7e..5fe644cca97891d18059dbc72345b9d976b044fc 100644 (file)
@@ -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;
index d70c7b8b9a8cb5d5f0755c4e3c76284fcb4f147d..85436e956c0a5b5a332ab377e5c26620500cb9bc 100644 (file)
@@ -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 <bsd.prog.mk>
index 6dd5f37a3c97eff6ce1d73318b2282ae451bc526..37f07bca20901735f1298942fb4660ac7ad945e1 100644 (file)
@@ -1,3 +1,5 @@
+.include <minix.newlibc.mk>
+
 PROG=  login
 MAN=
 .if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no")
index 9fbabcd947edab2e15daa8cc3eccf4e796f747b8..a4b25770db647d7de80710688076ec9a7be3a35b 100644 (file)
@@ -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) {
index d3d12a065aee4b5b6ff1ef52769b6adee0ce4fe7..bb9142692f81e696cdfffd78b893b3d4446c85ca 100644 (file)
@@ -1,5 +1,6 @@
 PROG=  ls
 BINDIR=        /bin
 MAN=
+NEED_NBSDLIBC=yes
 
 .include <bsd.prog.mk>
diff --git a/commands/passwd/Makefile b/commands/passwd/Makefile
deleted file mode 100644 (file)
index b804eb5..0000000
+++ /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 <bsd.prog.mk>
diff --git a/commands/passwd/passwd.c b/commands/passwd/passwd.c
deleted file mode 100644 (file)
index 3226475..0000000
+++ /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 <sys/types.h>
-#include <fcntl.h>
-#include <string.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <time.h>
-#include <sys/stat.h>
-#include <minix/minlib.h>
-#include <stdio.h>
-
-_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);
-}
index 8f6dbe745f442c33dc05cbd08967b83140269865..c420b7ac18936470ca4e98accf1ff7e41f4ffe1d 100644 (file)
@@ -2,5 +2,8 @@ PROG=   pwdauth
 BINDIR=        /usr/lib
 BINMODE= 4755
 MAN=
+NEED_NBSDLIBC=y
+
+LDADD+=-lcrypt
 
 .include <bsd.prog.mk>
index e1e047199de0d5fb854b5b557468e2e591403456..aef1fb4a6887d1aaaa2991c31a3e06c4f03b5e17 100644 (file)
@@ -16,7 +16,6 @@
  * a no-password login.
  */
 #define nil 0
-#define crypt  CRYPT   /* The true crypt is included here. */
 #include <sys/types.h>
 #include <pwd.h>
 #include <stdlib.h>
@@ -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;
-}
index 0cb23931251e4581b97eefb186a047975029ec33..634e1bfe86a27e03352bf340582eeafd29badb44 100644 (file)
@@ -1,9 +1,8 @@
 PROG=  su
 BINMODE= 4755
 MAN=
-.if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no")
+NEED_NBSDLIBC=y
 LDADD+=        -lcrypt
-.endif
 
 
 .include <bsd.prog.mk>
index 45be2aba947dabdc4a63dcc38e4872758dba096f..acf2e2e7f0c232982976cbd247800a86d6612cdc 100644 (file)
@@ -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
index a7bbab5ce9d23cb6446162dc5e6718d76b59a6c3..1dcbcfad12d1a8d3b8731367e7ef88a108467774 100644 (file)
@@ -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
index 2624e603f3f60cde63d2c4094d709aa9af5351f6..008cb9b8eb65daffa2df7c79493bc780c68de4a4 100644 (file)
@@ -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 $> $@
index 6d10fa8d8627df7c1b3bc837b9bd6cd1361311f0..4be3bee20cae4176d1b7c80c67bdf63c8c1d28c1 100755 (executable)
--- 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 (file)
index 0000000..c008a01
--- /dev/null
@@ -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 (executable)
index bd0d87f..0000000
+++ /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 (executable)
index 3895e9e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-root::0:0:::
index 1e9879eb48ecd47ffa75399333755a84ba514391..16b9ed77e31180c2f8cad549fcdb5f1adcf8d0a7 100644 (file)
@@ -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
index 5950d97f1b2577b58984fc6c7646b8ebe7457995..a35894d169b127a0a1f050e0bba6771c33881c21 100644 (file)
@@ -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 (file)
index a82b3b0..0000000
+++ /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 <sys/types.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <sys/wait.h>
-
-/* 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 $
- */
index d26e038577ecc922ae4edb7e3317d68bc87010cf..36accd2462030e179245e32f621fe06ff38082ef 100644 (file)
@@ -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
 
index 9e0f550a15f1d83c923bc3edfb7c3f8d870bf5b0..982ebe3a4afe87112ffe2b5f83986309185f598a 100644 (file)
@@ -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
index 729235f65ffe1b52b8bcf615fb87d1666be1af3b..8e8745a49354ac8df2543a2193677f5f9a1884a4 100644 (file)
@@ -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.
index 1d35623682597bba535a29ad99dcbb324168c70f..2caf96a515a78dafddd6c04d9ac85387b9cbdb9b 100644 (file)
  * 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 <pwd.h> by mistake!
+ */
+#undef group_from_gid
+#undef user_from_uid
+#endif
+
 #include <sys/cdefs.h>
 #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 <string.h>
 #include <unistd.h>
 
+#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) {                      \
index 619fdcb5a58938000ef766beccbd3bfeb1fc714b..05db97c6a553527e17e7e01241c20440642c41da 100644 (file)
@@ -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 <bsd.own.mk>
diff --git a/lib/nbsd_libcompat_minix/getpwent.c b/lib/nbsd_libcompat_minix/getpwent.c
deleted file mode 100644 (file)
index 22598b4..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*     getpwent(), getpwuid(), getpwnam() - password file routines
- *
- *                                                     Author: Kees J. Bot
- *                                                             31 Jan 1994
- */
-#include <sys/types.h>
-#include <compat/pwd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#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 (file)
index 86890ed..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * grotesque hack to get these functions working.
- */
-
-#include <sys/types.h>
-#include <compat/pwd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <pwd.h>
-#include <grp.h>
-#include <stdio.h>
-
-/*
- * 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;
-}
index 10e8d89ae9552bfc0af242b474a4aae69a318fc7..f1276d25c263f8c6b3856cb77016c3706fa7c590 100644 (file)
@@ -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 (file)
index 17fb8ce..0000000
+++ /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).
index 2728030744d6ab90e084c70bce9fd3713f8fd275..8dde810a4115c8931e85607845a1f30c68e3f4d7 100644 (file)
@@ -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 (file)
index 2e2c792..0000000
+++ /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).
index 2567be52a536583d3bd5ebe7a1831aa7609118f4..c1e55dad0ca24f61b16a3c2bb4b7e6cdfda01cb1 100644 (file)
@@ -1,4 +1,3 @@
-.TH PWDAUTH
 .SH NAME
 pwdauth \- password authentication program
 .SH SYNOPSIS
index f37db3dca54742b7f185f298b460ac57d66fb9f8..4a42fa9a9552c62ed41869082a656d8d465d2c4f 100644 (file)
 #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
  */
index d1dc7d054891eaa732441028354f68b974dfa597..007e98e931e8b11bb5b802e8c57c4ec6d6b5b06c 100644 (file)
@@ -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 */
index eec6b9cf024e30ca2a5e9137be3c61eda2e15f8d..4a471f027488cb0eb2192acb396f0828bac63fa2 100644 (file)
  * SUCH DAMAGE.
  */
 
-#if defined(__minix) && defined(_MINIX_COMPAT)
-#include <compat/pwd.h>
-/* 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_
 
index 4fd459a3edb74357b9bd09a35724a83fdda3b1c5..3bdc05006343969d41ffab62b9fadfb5a62d16bf 100644 (file)
@@ -18,6 +18,7 @@ struct dirent {               /* Largest entry (8 slots) */
 };
 
 #if defined(_NETBSD_SOURCE)
+#define MAXNAMLEN      511
 #define        d_fileno        d_ino
 #endif
 
index 0f4681af1163201b7cd88cab856281463ab00f9d..701faa27006f48655a832303f364dc45f2054728 100644 (file)
@@ -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
index 3fc5996191696cbc7692e745ea02f45a03af6381..5b7d1e40d61f93e628afeea03bf7bf2b76d767e7 100755 (executable)
@@ -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
index 11bf1ce00960a75c1265d5cf2cd70ff04ee297be..036d6c34b90010efe4f53087854a12cf43e1d237 100644 (file)
@@ -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
index 708a7b3268c2d8c3d7cb47f3d99ed74109e15176..cfde616e69238caa08b2329180e819fb82de4f24 100644 (file)
@@ -1,6 +1,4 @@
 .include <minix.newlibc.mk>
 
-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 (file)
index 0000000..ccd0096
--- /dev/null
@@ -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 <bsd.own.mk>
+
+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 <bsd.prog.mk>
diff --git a/usr.bin/chpass/chpass.1 b/usr.bin/chpass/chpass.1
new file mode 100644 (file)
index 0000000..911b610
--- /dev/null
@@ -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 (file)
index 0000000..b50f99d
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+#include <libgen.h>
+
+#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 (file)
index 0000000..ea5fa34
--- /dev/null
@@ -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 (file)
index 0000000..102a3c2
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <util.h>
+
+#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 (file)
index 0000000..6de6baa
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <sys/param.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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 (file)
index 0000000..f867a34
--- /dev/null
@@ -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 <paths.h>
+
+#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 (file)
index 0000000..d209d0c
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+#define passwd yp_passwd_rec
+#include <rpcsvc/yppasswd.h>
+#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 (file)
index 0000000..1d80e22
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <sys/types.h>
+#include <stddef.h>
+#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 (file)
index 0000000..8c15da4
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <sys/types.h>
+
+#include <ctype.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <tzfile.h>
+#include <unistd.h>
+
+#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);
+}
index 1b32707883b1cc407c35cd59e648ca062823b363..3f266373a8f547bb66f852a1bc9bc6a8ed3b77bd 100644 (file)
@@ -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 (file)
index 0000000..0b7dc87
--- /dev/null
@@ -0,0 +1,26 @@
+#      $NetBSD: Makefile,v 1.4 2009/04/14 22:15:24 lukem Exp $
+#
+
+.include <bsd.own.mk>
+
+.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 <bsd.prog.mk>
diff --git a/usr.bin/newgrp/grutil.c b/usr.bin/newgrp/grutil.c
new file mode 100644 (file)
index 0000000..8253746
--- /dev/null
@@ -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 <sys/cdefs.h>
+__RCSID("$NetBSD: grutil.c,v 1.2 2008/04/28 20:24:14 martin Exp $");
+
+#include <sys/param.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#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 (file)
index 0000000..957f798
--- /dev/null
@@ -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 (file)
index 0000000..3a00aa1
--- /dev/null
@@ -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 (file)
index 0000000..27b2335
--- /dev/null
@@ -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 <sys/cdefs.h>
+
+#ifndef lint
+__RCSID("$NetBSD: newgrp.c,v 1.6 2008/04/28 20:24:14 martin Exp $");
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <err.h>
+#include <grp.h>
+#include <libgen.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#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 (file)
index 0000000..7999441
--- /dev/null
@@ -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 <bsd.own.mk>
+
+.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 <bsd.prog.mk>
diff --git a/usr.bin/passwd/extern.h b/usr.bin/passwd/extern.h
new file mode 100644 (file)
index 0000000..11a4d74
--- /dev/null
@@ -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 (file)
index 0000000..8e23270
--- /dev/null
@@ -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 (file)
index 0000000..eefdbe4
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include <openssl/ui.h>
+#include <krb5.h>
+
+#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 <principal> */
+               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 (file)
index 0000000..5f161be
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+#include <unistd.h>
+#include <util.h>
+#include <login_cap.h>
+#include <syslog.h>
+
+#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 (file)
index 0000000..57cba40
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <sys/param.h>
+
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+#include <security/pam_appl.h>
+#include <security/openpam.h>
+
+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 (file)
index 0000000..3f64f1f
--- /dev/null
@@ -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 (file)
index 0000000..0da7e9b
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <assert.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#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 (file)
index 0000000..0d11ff1
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <limits.h>
+#include <util.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+#include "extern.h"
+
+#define passwd yp_passwd_rec
+#include <rpcsvc/yppasswd.h>
+#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 (file)
index 0000000..1ba2590
--- /dev/null
@@ -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.
index 4043c3ae3b46c4b0c09fb1d15d864d350714ccb0..f9be0a12b765756c7c2bc126f64a4f60fda67c3f 100644 (file)
@@ -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 <stdio.h>
 #include <unistd.h>
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
new file mode 100644 (file)
index 0000000..7601387
--- /dev/null
@@ -0,0 +1,8 @@
+# Makefile for usr.bin
+
+.include <bsd.own.mk>
+
+# NetBSD imports
+SUBDIR= pwd_mkdb user vipw
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/Makefile.inc b/usr.sbin/Makefile.inc
new file mode 100644 (file)
index 0000000..873a297
--- /dev/null
@@ -0,0 +1,3 @@
+.include <minix.newlibc.mk>
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/pwd_mkdb/Makefile b/usr.sbin/pwd_mkdb/Makefile
new file mode 100644 (file)
index 0000000..2dfe37a
--- /dev/null
@@ -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 <bsd.own.mk>
+
+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 <bsd.prog.mk>
diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.8 b/usr.sbin/pwd_mkdb/pwd_mkdb.8
new file mode 100644 (file)
index 0000000..5e3ab7c
--- /dev/null
@@ -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 (file)
index 0000000..d07f06a
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <pwd.h>
+#endif
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifndef HAVE_NBTOOL_CONFIG_H
+#include <machine/bswap.h>
+#endif
+
+#include <db.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#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 (file)
index 0000000..be63a33
--- /dev/null
@@ -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 <bsd.own.mk>
+
+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 <bsd.prog.mk>
+
+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 (file)
index 0000000..578bc40
--- /dev/null
@@ -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 (file)
index 0000000..c49c1de
--- /dev/null
@@ -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 (file)
index 0000000..24eb0e1
--- /dev/null
@@ -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 (file)
index 0000000..dd5bb19
--- /dev/null
@@ -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 (file)
index 0000000..b031a3a
--- /dev/null
@@ -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 (file)
index 0000000..339c354
--- /dev/null
@@ -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 (file)
index 0000000..b90f296
--- /dev/null
@@ -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 <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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 (file)
index 0000000..98788a9
--- /dev/null
@@ -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 (file)
index 0000000..5570628
--- /dev/null
@@ -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 (file)
index 0000000..045ff1a
--- /dev/null
@@ -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 <sys/cdefs.h>
+
+#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 <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <fcntl.h>
+#include <grp.h>
+#ifdef EXTENSIONS
+#include <login_cap.h>
+#endif
+#include <paths.h>
+#include <pwd.h>
+#include <regex.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <util.h>
+#include <errno.h>
+
+#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 (file)
index 0000000..d5e2754
--- /dev/null
@@ -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 (file)
index 0000000..9c72030
--- /dev/null
@@ -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 (file)
index 0000000..2050846
--- /dev/null
@@ -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 (file)
index 0000000..e6553ec
--- /dev/null
@@ -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 (file)
index 0000000..45042ac
--- /dev/null
@@ -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 (file)
index 0000000..42ff979
--- /dev/null
@@ -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 (file)
index 0000000..8b1123c
--- /dev/null
@@ -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 <bsd.prog.mk>
diff --git a/usr.sbin/vipw/vipw.8 b/usr.sbin/vipw/vipw.8
new file mode 100644 (file)
index 0000000..2f7b6e4
--- /dev/null
@@ -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 (file)
index 0000000..a6f97b2
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <util.h>
+
+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);
+}