Based on work by Vivek Prakash and Gianluca Guida.
See UPDATING about caveats on currently existing accounts.
. restores netbsd libc pwcache functions
$(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
$(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
$(MAKE) -C bin all
$(MAKE) -C usr.bin all
$(MAKE) -C libexec all
+ $(MAKE) -C usr.sbin all
$(MAKE) -C tools all
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
$(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
$(MAKE) -C bin cleandepend
$(MAKE) -C usr.bin cleandepend
$(MAKE) -C libexec cleandepend
+ $(MAKE) -C usr.sbin cleandepend
$(MAKE) -C tools cleandepend
.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 \
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 \
+++ /dev/null
-SCRIPTS= adduser.sh
-MAN=
-
-.include <bsd.prog.mk>
+++ /dev/null
-#!/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
#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;
* 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;
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++;
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;
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>
+.include <minix.newlibc.mk>
+
PROG= login
MAN=
.if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no")
* 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) {
PROG= ls
BINDIR= /bin
MAN=
+NEED_NBSDLIBC=yes
.include <bsd.prog.mk>
+++ /dev/null
-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>
+++ /dev/null
-/* 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);
-}
BINDIR= /usr/lib
BINMODE= 4755
MAN=
+NEED_NBSDLIBC=y
+
+LDADD+=-lcrypt
.include <bsd.prog.mk>
* 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>
#endif
#define LEN 1024
-char SHADOW[] = "/etc/shadow";
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
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;
-}
PROG= su
BINMODE= 4755
MAN=
-.if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no")
+NEED_NBSDLIBC=y
LDADD+= -lcrypt
-.endif
.include <bsd.prog.mk>
+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
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
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
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
@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 $> $@
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:
--- /dev/null
+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
+++ /dev/null
-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:
+++ /dev/null
-root::0:0:::
.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
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
bcopy.c \
bzero.c \
configfile.c \
- crypt.c \
ctermid.c \
cuserid.c \
dirname.c \
getpagesize.c \
getpass.c \
getprogname.c \
- getpwent.c \
getsubopt.c \
getttyent.c \
getw.c \
popen.c \
putenv.c \
putw.c \
- pwcache.c \
random.c \
read_tsc.S \
read_tsc_64.c \
vwarnx.c \
warn.c \
warnx.c
+
+# XXX To be removed after full
+# XXX switch to NetBSD passwd.
+SRCS+= \
+ getpwent.c \
+ pwcache.c
+++ /dev/null
-/* 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 $
- */
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
#getmaxpartitions.c
#stat_flags.c
#getrawpartition.c
- #login_cap.c
#ttymsg.c
#parsedate.y
.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
{ 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 }
};
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);
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.
* 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
#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"
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) { \
# 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
# 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>
+++ /dev/null
-/* 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);
-}
+++ /dev/null
-/*
- * 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;
-}
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 \
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
+++ /dev/null
-.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).
-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 \
+++ /dev/null
-.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).
-.TH PWDAUTH
.SH NAME
pwdauth \- password authentication program
.SH SYNOPSIS
#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
*/
#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 */
* 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_
};
#if defined(_NETBSD_SOURCE)
+#define MAXNAMLEN 511
#define d_fileno d_ino
#endif
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
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
# NetBSD imports
SUBDIR= indent m4 stat tic sed mkdep uniq seq man mdocml \
- apropos
+ apropos chpass newgrp passwd
# Non-NetBSD imports
SUBDIR+= ministat
.include <minix.newlibc.mk>
-CPPFLAGS+= -D_NETBSD_SOURCE -D__NBSD_LIBC=1
-
BINDIR?=/usr/bin
--- /dev/null
+# $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>
--- /dev/null
+.\" $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.
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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);
--- /dev/null
+/* $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));
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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"
--- /dev/null
+/* $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 */
--- /dev/null
+/* $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 },
+};
--- /dev/null
+/* $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);
+}
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
--- /dev/null
+# $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>
--- /dev/null
+/* $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
+}
--- /dev/null
+/* $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_ */
--- /dev/null
+.\" $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.
--- /dev/null
+/*-
+ * 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 */
+}
--- /dev/null
+# $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>
--- /dev/null
+/* $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 */
--- /dev/null
+.\" $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.
--- /dev/null
+/* $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 */
--- /dev/null
+/* $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 */
--- /dev/null
+/* $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);
+}
--- /dev/null
+.\" $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 .
--- /dev/null
+/* $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 */
--- /dev/null
+/* $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 */
--- /dev/null
+.\" $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.
/* 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>
--- /dev/null
+# Makefile for usr.bin
+
+.include <bsd.own.mk>
+
+# NetBSD imports
+SUBDIR= pwd_mkdb user vipw
+
+.include <bsd.subdir.mk>
--- /dev/null
+.include <minix.newlibc.mk>
+
+BINDIR?= /usr/sbin
--- /dev/null
+# 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>
--- /dev/null
+.\" $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.
--- /dev/null
+/* $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);
+}
--- /dev/null
+# $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"
--- /dev/null
+/* $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_ */
--- /dev/null
+.\" $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 .
--- /dev/null
+.\" $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 .
--- /dev/null
+.\" $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 .
--- /dev/null
+.\" $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 .
--- /dev/null
+.\" $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 .
--- /dev/null
+/* $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 */
+}
--- /dev/null
+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'
--- /dev/null
+.\" $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 .
--- /dev/null
+/* $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
--- /dev/null
+.\" $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.
--- /dev/null
+.\" $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 .
--- /dev/null
+.\" $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 .
--- /dev/null
+.\" $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 .
--- /dev/null
+/* $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
--- /dev/null
+.\" $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 .
--- /dev/null
+# $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>
--- /dev/null
+.\" $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 .
--- /dev/null
+/* $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);
+}