summaryrefslogtreecommitdiffstats
path: root/pw
diff options
context:
space:
mode:
authorBaptiste Daroussin <bapt@FreeBSD.org>2012-10-30 08:00:53 +0000
committerBaptiste Daroussin <bapt@FreeBSD.org>2012-10-30 08:00:53 +0000
commit32215e1fded83eb454a0ec2522dbd53877e5a384 (patch)
tree39860234c3deda7de976cccc795de42ae7b0aa77 /pw
parent955a095f02cda3bf4ac1e3bd760317addf54b19a (diff)
downloadpw-darwin-32215e1fded83eb454a0ec2522dbd53877e5a384.tar.gz
pw-darwin-32215e1fded83eb454a0ec2522dbd53877e5a384.tar.zst
pw-darwin-32215e1fded83eb454a0ec2522dbd53877e5a384.zip
Teach pw(8) about how to use pw/gr API to reduce code duplication
MFC after: 2 months
Diffstat (limited to 'pw')
-rw-r--r--pw/Makefile14
-rw-r--r--pw/fileupd.c68
-rw-r--r--pw/grupd.c129
-rw-r--r--pw/pw.88
-rw-r--r--pw/pw.c452
-rw-r--r--pw/pw.h124
-rw-r--r--pw/pw_group.c424
-rw-r--r--pw/pw_log.c68
-rw-r--r--pw/pw_nis.c94
-rw-r--r--pw/pw_user.c58
-rw-r--r--pw/pw_vpw.c302
-rw-r--r--pw/pwupd.c114
-rw-r--r--pw/pwupd.h121
13 files changed, 1888 insertions, 88 deletions
diff --git a/pw/Makefile b/pw/Makefile
new file mode 100644
index 0000000..eae0b87
--- /dev/null
+++ b/pw/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= pw
+MAN= pw.conf.5 pw.8
+SRCS= pw.c pw_conf.c pw_user.c pw_group.c pw_log.c pw_nis.c pw_vpw.c \
+ grupd.c pwupd.c fileupd.c psdate.c \
+ bitmap.c cpdir.c rm_r.c
+
+WARNS?= 2
+
+DPADD= ${LIBCRYPT} ${LIBUTIL}
+LDADD= -lcrypt -lutil
+
+.include <bsd.prog.mk>
diff --git a/pw/fileupd.c b/pw/fileupd.c
new file mode 100644
index 0000000..7df4bb1
--- /dev/null
+++ b/pw/fileupd.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "pwupd.h"
+
+int
+extendline(char **buf, int * buflen, int needed)
+{
+ if (needed > *buflen) {
+ char *tmp = realloc(*buf, needed);
+ if (tmp == NULL)
+ return -1;
+ *buf = tmp;
+ *buflen = needed;
+ }
+ return *buflen;
+}
+
+int
+extendarray(char ***buf, int * buflen, int needed)
+{
+ if (needed > *buflen) {
+ char **tmp = realloc(*buf, needed * sizeof(char *));
+ if (tmp == NULL)
+ return -1;
+ *buf = tmp;
+ *buflen = needed;
+ }
+ return *buflen;
+}
diff --git a/pw/grupd.c b/pw/grupd.c
new file mode 100644
index 0000000..e9f6b5e
--- /dev/null
+++ b/pw/grupd.c
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <grp.h>
+#include <libutil.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include "pwupd.h"
+
+static char * grpath = _PATH_PWD;
+
+int
+setgrdir(const char * dir)
+{
+ if (dir == NULL)
+ return -1;
+ else {
+ char * d = malloc(strlen(dir)+1);
+ if (d == NULL)
+ return -1;
+ grpath = strcpy(d, dir);
+ }
+ return 0;
+}
+
+char *
+getgrpath(const char * file)
+{
+ static char pathbuf[MAXPATHLEN];
+
+ snprintf(pathbuf, sizeof pathbuf, "%s/%s", grpath, file);
+ return pathbuf;
+}
+
+static int
+gr_update(struct group * grp, char const * group)
+{
+ int pfd, tfd;
+ struct group *gr = NULL;
+ struct group *old_gr = NULL;
+
+ if (grp != NULL)
+ gr = gr_dup(grp);
+
+ if (group != NULL)
+ old_gr = GETGRNAM(group);
+
+ if (gr_init(grpath, NULL))
+ err(1, "gr_init()");
+
+ if ((pfd = gr_lock()) == -1) {
+ gr_fini();
+ err(1, "gr_lock()");
+ }
+ if ((tfd = gr_tmp(-1)) == -1) {
+ gr_fini();
+ err(1, "gr_tmp()");
+ }
+ if (gr_copy(pfd, tfd, gr, old_gr) == -1) {
+ gr_fini();
+ err(1, "gr_copy()");
+ }
+ if (gr_mkdb() == -1) {
+ gr_fini();
+ err(1, "gr_mkdb()");
+ }
+ free(gr);
+ gr_fini();
+ return 0;
+}
+
+
+int
+addgrent(struct group * grp)
+{
+ return gr_update(grp, NULL);
+}
+
+int
+chggrent(char const * login, struct group * grp)
+{
+ return gr_update(grp, login);
+}
+
+int
+delgrent(struct group * grp)
+{
+ char group[MAXLOGNAME];
+
+ strlcpy(group, grp->gr_name, MAXLOGNAME);
+
+ return gr_update(NULL, group);
+}
diff --git a/pw/pw.8 b/pw/pw.8
index 8b21107..076f2eb 100644
--- a/pw/pw.8
+++ b/pw/pw.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd December 21, 2011
+.Dd October 29, 2012
.Dt PW 8
.Os
.Sh NAME
@@ -904,12 +904,6 @@ A Version 7 format password file
The user capabilities database
.It Pa /etc/group
The group database
-.It Pa /etc/master.passwd.new
-Temporary copy of the master password file
-.It Pa /etc/passwd.new
-Temporary copy of the Version 7 password file
-.It Pa /etc/group.new
-Temporary copy of the group file
.It Pa /etc/pw.conf
Pw default options file
.It Pa /var/log/userlog
diff --git a/pw/pw.c b/pw/pw.c
new file mode 100644
index 0000000..b0ac728
--- /dev/null
+++ b/pw/pw.c
@@ -0,0 +1,452 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <paths.h>
+#include <sys/wait.h>
+#include "pw.h"
+
+#if !defined(_PATH_YP)
+#define _PATH_YP "/var/yp/"
+#endif
+const char *Modes[] = {
+ "add", "del", "mod", "show", "next",
+ NULL};
+const char *Which[] = {"user", "group", NULL};
+static const char *Combo1[] = {
+ "useradd", "userdel", "usermod", "usershow", "usernext",
+ "lock", "unlock",
+ "groupadd", "groupdel", "groupmod", "groupshow", "groupnext",
+ NULL};
+static const char *Combo2[] = {
+ "adduser", "deluser", "moduser", "showuser", "nextuser",
+ "lock", "unlock",
+ "addgroup", "delgroup", "modgroup", "showgroup", "nextgroup",
+ NULL};
+
+struct pwf PWF =
+{
+ 0,
+ setpwent,
+ endpwent,
+ getpwent,
+ getpwuid,
+ getpwnam,
+ setgrent,
+ endgrent,
+ getgrent,
+ getgrgid,
+ getgrnam,
+
+};
+struct pwf VPWF =
+{
+ 1,
+ vsetpwent,
+ vendpwent,
+ vgetpwent,
+ vgetpwuid,
+ vgetpwnam,
+ vsetgrent,
+ vendgrent,
+ vgetgrent,
+ vgetgrgid,
+ vgetgrnam,
+};
+
+static struct cargs arglist;
+
+static int getindex(const char *words[], const char *word);
+static void cmdhelp(int mode, int which);
+
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ int mode = -1;
+ int which = -1;
+ char *config = NULL;
+ struct userconf *cnf;
+
+ static const char *opts[W_NUM][M_NUM] =
+ {
+ { /* user */
+ "V:C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y",
+ "V:C:qn:u:rY",
+ "V:C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:FNPY",
+ "V:C:qn:u:FPa7",
+ "V:C:q",
+ "V:C:q",
+ "V:C:q"
+ },
+ { /* grp */
+ "V:C:qn:g:h:H:M:opNPY",
+ "V:C:qn:g:Y",
+ "V:C:qn:d:g:l:h:H:FM:m:NPY",
+ "V:C:qn:g:FPa",
+ "V:C:q"
+ }
+ };
+
+ static int (*funcs[W_NUM]) (struct userconf * _cnf, int _mode, struct cargs * _args) =
+ { /* Request handlers */
+ pw_user,
+ pw_group
+ };
+
+ LIST_INIT(&arglist);
+
+ (void)setlocale(LC_ALL, "");
+
+ /*
+ * Break off the first couple of words to determine what exactly
+ * we're being asked to do
+ */
+ while (argc > 1) {
+ int tmp;
+
+ if (*argv[1] == '-') {
+ /*
+ * Special case, allow pw -V<dir> <operation> [args] for scripts etc.
+ */
+ if (argv[1][1] == 'V') {
+ optarg = &argv[1][2];
+ if (*optarg == '\0') {
+ optarg = argv[2];
+ ++argv;
+ --argc;
+ }
+ addarg(&arglist, 'V', optarg);
+ } else
+ break;
+ }
+ else if (mode == -1 && (tmp = getindex(Modes, argv[1])) != -1)
+ mode = tmp;
+ else if (which == -1 && (tmp = getindex(Which, argv[1])) != -1)
+ which = tmp;
+ else if ((mode == -1 && which == -1) &&
+ ((tmp = getindex(Combo1, argv[1])) != -1 ||
+ (tmp = getindex(Combo2, argv[1])) != -1)) {
+ which = tmp / M_NUM;
+ mode = tmp % M_NUM;
+ } else if (strcmp(argv[1], "help") == 0 && argv[2] == NULL)
+ cmdhelp(mode, which);
+ else if (which != -1 && mode != -1)
+ addarg(&arglist, 'n', argv[1]);
+ else
+ errx(EX_USAGE, "unknown keyword `%s'", argv[1]);
+ ++argv;
+ --argc;
+ }
+
+ /*
+ * Bail out unless the user is specific!
+ */
+ if (mode == -1 || which == -1)
+ cmdhelp(mode, which);
+
+ /*
+ * We know which mode we're in and what we're about to do, so now
+ * let's dispatch the remaining command line args in a genric way.
+ */
+ optarg = NULL;
+
+ while ((ch = getopt(argc, argv, opts[which][mode])) != -1) {
+ if (ch == '?')
+ errx(EX_USAGE, "unknown switch");
+ else
+ addarg(&arglist, ch, optarg);
+ optarg = NULL;
+ }
+
+ /*
+ * Must be root to attempt an update
+ */
+ if (geteuid() != 0 && mode != M_PRINT && mode != M_NEXT && getarg(&arglist, 'N')==NULL)
+ errx(EX_NOPERM, "you must be root to run this program");
+
+ /*
+ * We should immediately look for the -q 'quiet' switch so that we
+ * don't bother with extraneous errors
+ */
+ if (getarg(&arglist, 'q') != NULL)
+ freopen(_PATH_DEVNULL, "w", stderr);
+
+ /*
+ * Set our base working path if not overridden
+ */
+
+ config = getarg(&arglist, 'C') ? getarg(&arglist, 'C')->val : NULL;
+
+ if (getarg(&arglist, 'V') != NULL) {
+ char * etcpath = getarg(&arglist, 'V')->val;
+ if (*etcpath) {
+ if (config == NULL) { /* Only override config location if -C not specified */
+ config = malloc(MAXPATHLEN);
+ snprintf(config, MAXPATHLEN, "%s/pw.conf", etcpath);
+ }
+ memcpy(&PWF, &VPWF, sizeof PWF);
+ setpwdir(etcpath);
+ setgrdir(etcpath);
+ }
+ }
+
+ /*
+ * Now, let's do the common initialisation
+ */
+ cnf = read_userconfig(config);
+
+ ch = funcs[which] (cnf, mode, &arglist);
+
+ /*
+ * If everything went ok, and we've been asked to update
+ * the NIS maps, then do it now
+ */
+ if (ch == EXIT_SUCCESS && getarg(&arglist, 'Y') != NULL) {
+ pid_t pid;
+
+ fflush(NULL);
+ if (chdir(_PATH_YP) == -1)
+ warn("chdir(" _PATH_YP ")");
+ else if ((pid = fork()) == -1)
+ warn("fork()");
+ else if (pid == 0) {
+ /* Is make anywhere else? */
+ execlp("/usr/bin/make", "make", (char *)NULL);
+ _exit(1);
+ } else {
+ int i;
+ waitpid(pid, &i, 0);
+ if ((i = WEXITSTATUS(i)) != 0)
+ errx(ch, "make exited with status %d", i);
+ else
+ pw_log(cnf, mode, which, "NIS maps updated");
+ }
+ }
+ return ch;
+}
+
+
+static int
+getindex(const char *words[], const char *word)
+{
+ int i = 0;
+
+ while (words[i]) {
+ if (strcmp(words[i], word) == 0)
+ return i;
+ i++;
+ }
+ return -1;
+}
+
+
+/*
+ * This is probably an overkill for a cmdline help system, but it reflects
+ * the complexity of the command line.
+ */
+
+static void
+cmdhelp(int mode, int which)
+{
+ if (which == -1)
+ fprintf(stderr, "usage:\n pw [user|group|lock|unlock] [add|del|mod|show|next] [help|switches/values]\n");
+ else if (mode == -1)
+ fprintf(stderr, "usage:\n pw %s [add|del|mod|show|next] [help|switches/values]\n", Which[which]);
+ else {
+
+ /*
+ * We need to give mode specific help
+ */
+ static const char *help[W_NUM][M_NUM] =
+ {
+ {
+ "usage: pw useradd [name] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ " Adding users:\n"
+ "\t-n name login name\n"
+ "\t-u uid user id\n"
+ "\t-c comment user name/comment\n"
+ "\t-d directory home directory\n"
+ "\t-e date account expiry date\n"
+ "\t-p date password expiry date\n"
+ "\t-g grp initial group\n"
+ "\t-G grp1,grp2 additional groups\n"
+ "\t-m [ -k dir ] create and set up home\n"
+ "\t-M mode home directory permissions\n"
+ "\t-s shell name of login shell\n"
+ "\t-o duplicate uid ok\n"
+ "\t-L class user class\n"
+ "\t-h fd read password on fd\n"
+ "\t-H fd read encrypted password on fd\n"
+ "\t-Y update NIS maps\n"
+ "\t-N no update\n"
+ " Setting defaults:\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-D set user defaults\n"
+ "\t-b dir default home root dir\n"
+ "\t-e period default expiry period\n"
+ "\t-p period default password change period\n"
+ "\t-g group default group\n"
+ "\t-G grp1,grp2 additional groups\n"
+ "\t-L class default user class\n"
+ "\t-k dir default home skeleton\n"
+ "\t-M mode home directory permissions\n"
+ "\t-u min,max set min,max uids\n"
+ "\t-i min,max set min,max gids\n"
+ "\t-w method set default password method\n"
+ "\t-s shell default shell\n"
+ "\t-y path set NIS passwd file path\n",
+ "usage: pw userdel [uid|name] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-n name login name\n"
+ "\t-u uid user id\n"
+ "\t-Y update NIS maps\n"
+ "\t-r remove home & contents\n",
+ "usage: pw usermod [uid|name] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ "\t-F force add if no user\n"
+ "\t-n name login name\n"
+ "\t-u uid user id\n"
+ "\t-c comment user name/comment\n"
+ "\t-d directory home directory\n"
+ "\t-e date account expiry date\n"
+ "\t-p date password expiry date\n"
+ "\t-g grp initial group\n"
+ "\t-G grp1,grp2 additional groups\n"
+ "\t-l name new login name\n"
+ "\t-L class user class\n"
+ "\t-m [ -k dir ] create and set up home\n"
+ "\t-M mode home directory permissions\n"
+ "\t-s shell name of login shell\n"
+ "\t-w method set new password using method\n"
+ "\t-h fd read password on fd\n"
+ "\t-H fd read encrypted password on fd\n"
+ "\t-Y update NIS maps\n"
+ "\t-N no update\n",
+ "usage: pw usershow [uid|name] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-n name login name\n"
+ "\t-u uid user id\n"
+ "\t-F force print\n"
+ "\t-P prettier format\n"
+ "\t-a print all users\n"
+ "\t-7 print in v7 format\n",
+ "usage: pw usernext [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n",
+ "usage pw: lock [switches]\n"
+ "\t-V etcdir alternate /etc locations\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n",
+ "usage pw: unlock [switches]\n"
+ "\t-V etcdir alternate /etc locations\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ },
+ {
+ "usage: pw groupadd [group|gid] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ "\t-n group group name\n"
+ "\t-g gid group id\n"
+ "\t-M usr1,usr2 add users as group members\n"
+ "\t-o duplicate gid ok\n"
+ "\t-Y update NIS maps\n"
+ "\t-N no update\n",
+ "usage: pw groupdel [group|gid] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-n name group name\n"
+ "\t-g gid group id\n"
+ "\t-Y update NIS maps\n",
+ "usage: pw groupmod [group|gid] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ "\t-F force add if not exists\n"
+ "\t-n name group name\n"
+ "\t-g gid group id\n"
+ "\t-M usr1,usr2 replaces users as group members\n"
+ "\t-m usr1,usr2 add users as group members\n"
+ "\t-d usr1,usr2 delete users as group members\n"
+ "\t-l name new group name\n"
+ "\t-Y update NIS maps\n"
+ "\t-N no update\n",
+ "usage: pw groupshow [group|gid] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-n name group name\n"
+ "\t-g gid group id\n"
+ "\t-F force print\n"
+ "\t-P prettier format\n"
+ "\t-a print all accounting groups\n",
+ "usage: pw groupnext [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ }
+ };
+
+ fprintf(stderr, "%s", help[which][mode]);
+ }
+ exit(EXIT_FAILURE);
+}
+
+struct carg *
+getarg(struct cargs * _args, int ch)
+{
+ struct carg *c = LIST_FIRST(_args);
+
+ while (c != NULL && c->ch != ch)
+ c = LIST_NEXT(c, list);
+ return c;
+}
+
+struct carg *
+addarg(struct cargs * _args, int ch, char *argstr)
+{
+ struct carg *ca = malloc(sizeof(struct carg));
+
+ if (ca == NULL)
+ errx(EX_OSERR, "out of memory");
+ ca->ch = ch;
+ ca->val = argstr;
+ LIST_INSERT_HEAD(_args, ca, list);
+ return ca;
+}
diff --git a/pw/pw.h b/pw/pw.h
new file mode 100644
index 0000000..1ff69a6
--- /dev/null
+++ b/pw/pw.h
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/queue.h>
+#include <sysexits.h>
+
+#include "psdate.h"
+#include "pwupd.h"
+
+enum _mode
+{
+ M_ADD,
+ M_DELETE,
+ M_UPDATE,
+ M_PRINT,
+ M_NEXT,
+ M_LOCK,
+ M_UNLOCK,
+ M_NUM
+};
+
+enum _which
+{
+ W_USER,
+ W_GROUP,
+ W_NUM
+};
+
+struct carg
+{
+ int ch;
+ char *val;
+ LIST_ENTRY(carg) list;
+};
+
+LIST_HEAD(cargs, carg);
+
+struct userconf
+{
+ int default_password; /* Default password for new users? */
+ int reuse_uids; /* Reuse uids? */
+ int reuse_gids; /* Reuse gids? */
+ char *nispasswd; /* Path to NIS version of the passwd file */
+ char *dotdir; /* Where to obtain skeleton files */
+ char *newmail; /* Mail to send to new accounts */
+ char *logfile; /* Where to log changes */
+ char *home; /* Where to create home directory */
+ mode_t homemode; /* Home directory permissions */
+ char *shelldir; /* Where shells are located */
+ char **shells; /* List of shells */
+ char *shell_default; /* Default shell */
+ char *default_group; /* Default group number */
+ char **groups; /* Default (additional) groups */
+ char *default_class; /* Default user class */
+ uid_t min_uid, max_uid; /* Allowed range of uids */
+ gid_t min_gid, max_gid; /* Allowed range of gids */
+ int expire_days; /* Days to expiry */
+ int password_days; /* Days to password expiry */
+ int numgroups; /* (internal) size of default_group array */
+};
+
+#define _DEF_DIRMODE (S_IRWXU | S_IRWXG | S_IRWXO)
+#define _PATH_PW_CONF "/etc/pw.conf"
+#define _UC_MAXLINE 1024
+#define _UC_MAXSHELLS 32
+
+struct userconf *read_userconfig(char const * file);
+int write_userconfig(char const * file);
+struct carg *addarg(struct cargs * _args, int ch, char *argstr);
+struct carg *getarg(struct cargs * _args, int ch);
+
+int pw_user(struct userconf * cnf, int mode, struct cargs * _args);
+int pw_group(struct userconf * cnf, int mode, struct cargs * _args);
+char *pw_checkname(u_char *name, int gecos);
+
+int addnispwent(const char *path, struct passwd *pwd);
+int delnispwent(const char *path, const char *login);
+int chgnispwent(const char *path, const char *login, struct passwd *pwd);
+
+int boolean_val(char const * str, int dflt);
+char const *boolean_str(int val);
+char *newstr(char const * p);
+
+void pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...) __printflike(4, 5);
+char *pw_pwcrypt(char *password);
+
+extern const char *Modes[];
+extern const char *Which[];
diff --git a/pw/pw_group.c b/pw/pw_group.c
new file mode 100644
index 0000000..f4f2116
--- /dev/null
+++ b/pw/pw_group.c
@@ -0,0 +1,424 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <termios.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <grp.h>
+#include <libutil.h>
+
+#include "pw.h"
+#include "bitmap.h"
+
+
+static struct passwd *lookup_pwent(const char *user);
+static void delete_members(char ***members, int *grmembers, int *i,
+ struct carg *arg, struct group *grp);
+static int print_group(struct group * grp, int pretty);
+static gid_t gr_gidpolicy(struct userconf * cnf, struct cargs * args);
+
+int
+pw_group(struct userconf * cnf, int mode, struct cargs * args)
+{
+ int rc;
+ struct carg *a_name = getarg(args, 'n');
+ struct carg *a_gid = getarg(args, 'g');
+ struct carg *arg;
+ struct group *grp = NULL;
+ int grmembers = 0;
+ char **members = NULL;
+
+ static struct group fakegroup =
+ {
+ "nogroup",
+ "*",
+ -1,
+ NULL
+ };
+
+ if (mode == M_LOCK || mode == M_UNLOCK)
+ errx(EX_USAGE, "'lock' command is not available for groups");
+
+ /*
+ * With M_NEXT, we only need to return the
+ * next gid to stdout
+ */
+ if (mode == M_NEXT) {
+ gid_t next = gr_gidpolicy(cnf, args);
+ if (getarg(args, 'q'))
+ return next;
+ printf("%ld\n", (long)next);
+ return EXIT_SUCCESS;
+ }
+
+ if (mode == M_PRINT && getarg(args, 'a')) {
+ int pretty = getarg(args, 'P') != NULL;
+
+ SETGRENT();
+ while ((grp = GETGRENT()) != NULL)
+ print_group(grp, pretty);
+ ENDGRENT();
+ return EXIT_SUCCESS;
+ }
+ if (a_gid == NULL) {
+ if (a_name == NULL)
+ errx(EX_DATAERR, "group name or id required");
+
+ if (mode != M_ADD && grp == NULL && isdigit((unsigned char)*a_name->val)) {
+ (a_gid = a_name)->ch = 'g';
+ a_name = NULL;
+ }
+ }
+ grp = (a_name != NULL) ? GETGRNAM(a_name->val) : GETGRGID((gid_t) atoi(a_gid->val));
+
+ if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) {
+ if (a_name == NULL && grp == NULL) /* Try harder */
+ grp = GETGRGID(atoi(a_gid->val));
+
+ if (grp == NULL) {
+ if (mode == M_PRINT && getarg(args, 'F')) {
+ char *fmems[1];
+ fmems[0] = NULL;
+ fakegroup.gr_name = a_name ? a_name->val : "nogroup";
+ fakegroup.gr_gid = a_gid ? (gid_t) atol(a_gid->val) : -1;
+ fakegroup.gr_mem = fmems;
+ return print_group(&fakegroup, getarg(args, 'P') != NULL);
+ }
+ errx(EX_DATAERR, "unknown group `%s'", a_name ? a_name->val : a_gid->val);
+ }
+ if (a_name == NULL) /* Needed later */
+ a_name = addarg(args, 'n', grp->gr_name);
+
+ /*
+ * Handle deletions now
+ */
+ if (mode == M_DELETE) {
+ gid_t gid = grp->gr_gid;
+
+ rc = delgrent(grp);
+ if (rc == -1)
+ err(EX_IOERR, "group '%s' not available (NIS?)", grp->gr_name);
+ else if (rc != 0) {
+ warn("group update");
+ return EX_IOERR;
+ }
+ pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) gid);
+ return EXIT_SUCCESS;
+ } else if (mode == M_PRINT)
+ return print_group(grp, getarg(args, 'P') != NULL);
+
+ if (a_gid)
+ grp->gr_gid = (gid_t) atoi(a_gid->val);
+
+ if ((arg = getarg(args, 'l')) != NULL)
+ grp->gr_name = pw_checkname((u_char *)arg->val, 0);
+ } else {
+ if (a_name == NULL) /* Required */
+ errx(EX_DATAERR, "group name required");
+ else if (grp != NULL) /* Exists */
+ errx(EX_DATAERR, "group name `%s' already exists", a_name->val);
+
+ extendarray(&members, &grmembers, 200);
+ members[0] = NULL;
+ grp = &fakegroup;
+ grp->gr_name = pw_checkname((u_char *)a_name->val, 0);
+ grp->gr_passwd = "*";
+ grp->gr_gid = gr_gidpolicy(cnf, args);
+ grp->gr_mem = members;
+ }
+
+ /*
+ * This allows us to set a group password Group passwords is an
+ * antique idea, rarely used and insecure (no secure database) Should
+ * be discouraged, but it is apparently still supported by some
+ * software.
+ */
+
+ if ((arg = getarg(args, 'h')) != NULL ||
+ (arg = getarg(args, 'H')) != NULL) {
+ if (strcmp(arg->val, "-") == 0)
+ grp->gr_passwd = "*"; /* No access */
+ else {
+ int fd = atoi(arg->val);
+ int precrypt = (arg->ch == 'H');
+ int b;
+ int istty = isatty(fd);
+ struct termios t;
+ char *p, line[256];
+
+ if (istty) {
+ if (tcgetattr(fd, &t) == -1)
+ istty = 0;
+ else {
+ struct termios n = t;
+
+ /* Disable echo */
+ n.c_lflag &= ~(ECHO);
+ tcsetattr(fd, TCSANOW, &n);
+ printf("%sassword for group %s:", (mode == M_UPDATE) ? "New p" : "P", grp->gr_name);
+ fflush(stdout);
+ }
+ }
+ b = read(fd, line, sizeof(line) - 1);
+ if (istty) { /* Restore state */
+ tcsetattr(fd, TCSANOW, &t);
+ fputc('\n', stdout);
+ fflush(stdout);
+ }
+ if (b < 0) {
+ warn("-h file descriptor");
+ return EX_OSERR;
+ }
+ line[b] = '\0';
+ if ((p = strpbrk(line, " \t\r\n")) != NULL)
+ *p = '\0';
+ if (!*line)
+ errx(EX_DATAERR, "empty password read on file descriptor %d", fd);
+ if (precrypt) {
+ if (strchr(line, ':') != NULL)
+ return EX_DATAERR;
+ grp->gr_passwd = line;
+ } else
+ grp->gr_passwd = pw_pwcrypt(line);
+ }
+ }
+
+ if (((arg = getarg(args, 'M')) != NULL ||
+ (arg = getarg(args, 'd')) != NULL ||
+ (arg = getarg(args, 'm')) != NULL) && arg->val) {
+ int i = 0;
+ char *p;
+ struct passwd *pwd;
+
+ /* Make sure this is not stay NULL with -M "" */
+ extendarray(&members, &grmembers, 200);
+ if (arg->ch == 'd')
+ delete_members(&members, &grmembers, &i, arg, grp);
+ else if (arg->ch == 'm') {
+ int k = 0;
+
+ while (grp->gr_mem[k] != NULL) {
+ if (extendarray(&members, &grmembers, i + 2) != -1)
+ members[i++] = grp->gr_mem[k];
+ k++;
+ }
+ }
+
+ if (arg->ch != 'd')
+ for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
+ int j;
+
+ /*
+ * Check for duplicates
+ */
+ pwd = lookup_pwent(p);
+ for (j = 0; j < i && strcmp(members[j], pwd->pw_name) != 0; j++)
+ ;
+ if (j == i && extendarray(&members, &grmembers, i + 2) != -1)
+ members[i++] = newstr(pwd->pw_name);
+ }
+ while (i < grmembers)
+ members[i++] = NULL;
+ grp->gr_mem = members;
+ }
+
+ if (getarg(args, 'N') != NULL)
+ return print_group(grp, getarg(args, 'P') != NULL);
+
+ if (mode == M_ADD && (rc = addgrent(grp)) != 0) {
+ if (rc == -1)
+ warnx("group '%s' already exists", grp->gr_name);
+ else
+ warn("group update");
+ return EX_IOERR;
+ } else if (mode == M_UPDATE && (rc = chggrent(a_name->val, grp)) != 0) {
+ if (rc == -1)
+ warnx("group '%s' not available (NIS?)", grp->gr_name);
+ else
+ warn("group update");
+ return EX_IOERR;
+ }
+ /* grp may have been invalidated */
+ if ((grp = GETGRNAM(a_name->val)) == NULL)
+ errx(EX_SOFTWARE, "group disappeared during update");
+
+ pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid);
+
+ if (members)
+ free(members);
+
+ return EXIT_SUCCESS;
+}
+
+
+/*
+ * Lookup a passwd entry using a name or UID.
+ */
+static struct passwd *
+lookup_pwent(const char *user)
+{
+ struct passwd *pwd;
+
+ if ((pwd = GETPWNAM(user)) == NULL &&
+ (!isdigit((unsigned char)*user) ||
+ (pwd = getpwuid((uid_t) atoi(user))) == NULL))
+ errx(EX_NOUSER, "user `%s' does not exist", user);
+
+ return (pwd);
+}
+
+
+/*
+ * Delete requested members from a group.
+ */
+static void
+delete_members(char ***members, int *grmembers, int *i, struct carg *arg,
+ struct group *grp)
+{
+ bool matchFound;
+ char *user;
+ char *valueCopy;
+ char *valuePtr;
+ int k;
+ struct passwd *pwd;
+
+ k = 0;
+ while (grp->gr_mem[k] != NULL) {
+ matchFound = false;
+ if ((valueCopy = strdup(arg->val)) == NULL)
+ errx(EX_UNAVAILABLE, "out of memory");
+ valuePtr = valueCopy;
+ while ((user = strsep(&valuePtr, ", \t")) != NULL) {
+ pwd = lookup_pwent(user);
+ if (strcmp(grp->gr_mem[k], pwd->pw_name) == 0) {
+ matchFound = true;
+ break;
+ }
+ }
+ free(valueCopy);
+
+ if (!matchFound &&
+ extendarray(members, grmembers, *i + 2) != -1)
+ (*members)[(*i)++] = grp->gr_mem[k];
+
+ k++;
+ }
+
+ return;
+}
+
+
+static gid_t
+gr_gidpolicy(struct userconf * cnf, struct cargs * args)
+{
+ struct group *grp;
+ gid_t gid = (gid_t) - 1;
+ struct carg *a_gid = getarg(args, 'g');
+
+ /*
+ * Check the given gid, if any
+ */
+ if (a_gid != NULL) {
+ gid = (gid_t) atol(a_gid->val);
+
+ if ((grp = GETGRGID(gid)) != NULL && getarg(args, 'o') == NULL)
+ errx(EX_DATAERR, "gid `%ld' has already been allocated", (long) grp->gr_gid);
+ } else {
+ struct bitmap bm;
+
+ /*
+ * We need to allocate the next available gid under one of
+ * two policies a) Grab the first unused gid b) Grab the
+ * highest possible unused gid
+ */
+ if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */
+ cnf->min_gid = 1000;
+ cnf->max_gid = 32000;
+ }
+ bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1);
+
+ /*
+ * Now, let's fill the bitmap from the password file
+ */
+ SETGRENT();
+ while ((grp = GETGRENT()) != NULL)
+ if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid &&
+ (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid)
+ bm_setbit(&bm, grp->gr_gid - cnf->min_gid);
+ ENDGRENT();
+
+ /*
+ * Then apply the policy, with fallback to reuse if necessary
+ */
+ if (cnf->reuse_gids)
+ gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
+ else {
+ gid = (gid_t) (bm_lastset(&bm) + 1);
+ if (!bm_isset(&bm, gid))
+ gid += cnf->min_gid;
+ else
+ gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
+ }
+
+ /*
+ * Another sanity check
+ */
+ if (gid < cnf->min_gid || gid > cnf->max_gid)
+ errx(EX_SOFTWARE, "unable to allocate a new gid - range fully used");
+ bm_dealloc(&bm);
+ }
+ return gid;
+}
+
+
+static int
+print_group(struct group * grp, int pretty)
+{
+ if (!pretty) {
+ char *buf = NULL;
+
+ buf = gr_make(grp);
+ fputs(buf, stdout);
+ free(buf);
+ } else {
+ int i;
+
+ printf("Group Name: %-15s #%lu\n"
+ " Members: ",
+ grp->gr_name, (long) grp->gr_gid);
+ for (i = 0; grp->gr_mem[i]; i++)
+ printf("%s%s", i ? "," : "", grp->gr_mem[i]);
+ fputs("\n\n", stdout);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/pw/pw_log.c b/pw/pw_log.c
new file mode 100644
index 0000000..f16274f
--- /dev/null
+++ b/pw/pw_log.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <fcntl.h>
+
+#include "pw.h"
+
+static FILE *logfile = NULL;
+
+void
+pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...)
+{
+ if (cnf->logfile && *cnf->logfile) {
+ if (logfile == NULL) { /* With umask==0 we need to control file access modes on create */
+ int fd = open(cnf->logfile, O_WRONLY | O_CREAT | O_APPEND, 0600);
+
+ if (fd != -1)
+ logfile = fdopen(fd, "a");
+ }
+ if (logfile != NULL) {
+ va_list argp;
+ int l;
+ time_t now = time(NULL);
+ struct tm *t = localtime(&now);
+ char nfmt[256];
+ const char *name;
+
+ if ((name = getenv("LOGNAME")) == NULL && (name = getenv("USER")) == NULL)
+ name = "unknown";
+ /* ISO 8601 International Standard Date format */
+ strftime(nfmt, sizeof nfmt, "%Y-%m-%d %T ", t);
+ l = strlen(nfmt);
+ sprintf(nfmt + strlen(nfmt), "[%s:%s%s] %s\n", name, Which[which], Modes[mode], fmt);
+ va_start(argp, fmt);
+ vfprintf(logfile, nfmt, argp);
+ va_end(argp);
+ fflush(logfile);
+ }
+ }
+}
diff --git a/pw/pw_nis.c b/pw/pw_nis.c
new file mode 100644
index 0000000..af5901a
--- /dev/null
+++ b/pw/pw_nis.c
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <err.h>
+#include <pwd.h>
+#include <libutil.h>
+
+#include "pw.h"
+
+static int
+pw_nisupdate(const char * path, struct passwd * pwd, char const * user)
+{
+ int pfd, tfd;
+ struct passwd *pw = NULL;
+ struct passwd *old_pw = NULL;
+
+ if (pwd != NULL)
+ pw = pw_dup(pwd);
+
+ if (user != NULL)
+ old_pw = GETPWNAM(user);
+
+ if (pw_init(NULL, path))
+ err(1,"pw_init()");
+ if ((pfd = pw_lock()) == -1) {
+ pw_fini();
+ err(1, "pw_lock()");
+ }
+ if ((tfd = pw_tmp(-1)) == -1) {
+ pw_fini();
+ err(1, "pw_tmp()");
+ }
+ if (pw_copy(pfd, tfd, pw, old_pw) == -1) {
+ pw_fini();
+ err(1, "pw_copy()");
+ }
+ if (rename(pw_tempname(), path) == -1)
+ err(1, "rename()");
+
+ free(pw);
+ pw_fini();
+
+ return (0);
+}
+
+int
+addnispwent(const char *path, struct passwd * pwd)
+{
+ return pw_nisupdate(path, pwd, NULL);
+}
+
+int
+chgnispwent(const char *path, char const * login, struct passwd * pwd)
+{
+ return pw_nisupdate(path, pwd, login);
+}
+
+int
+delnispwent(const char *path, const char *login)
+{
+ return pw_nisupdate(path, NULL, login);
+}
diff --git a/pw/pw_user.c b/pw/pw_user.c
index 1b72cbd..abf1c35 100644
--- a/pw/pw_user.c
+++ b/pw/pw_user.c
@@ -42,6 +42,9 @@ static const char rcsid[] =
#include <sys/resource.h>
#include <unistd.h>
#include <login_cap.h>
+#include <pwd.h>
+#include <grp.h>
+#include <libutil.h>
#include "pw.h"
#include "bitmap.h"
@@ -292,7 +295,6 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
if (mode == M_PRINT && getarg(args, 'a')) {
int pretty = getarg(args, 'P') != NULL;
int v7 = getarg(args, '7') != NULL;
-
SETPWENT();
while ((pwd = GETPWENT()) != NULL)
print_user(pwd, pretty, v7);
@@ -422,7 +424,24 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
/* non-fatal */
}
- editgroups(a_name->val, NULL);
+ grp = GETGRNAM(a_name->val);
+ if (*grp->gr_mem == NULL)
+ delgrent(GETGRNAM(a_name->val));
+ SETGRENT();
+ while ((grp = GETGRENT()) != NULL) {
+ int i;
+ char group[MAXLOGNAME];
+ for (i = 0; grp->gr_mem[i] != NULL; i++) {
+ if (!strcmp(grp->gr_mem[i], a_name->val)) {
+ while (grp->gr_mem[i] != NULL) {
+ grp->gr_mem[i] = grp->gr_mem[i+1];
+ }
+ strlcpy(group, grp->gr_name, MAXLOGNAME);
+ chggrent(group, grp);
+ }
+ }
+ }
+ ENDGRENT();
pw_log(cnf, mode, W_USER, "%s(%ld) account removed", a_name->val, (long) uid);
@@ -725,8 +744,29 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
* Ok, user is created or changed - now edit group file
*/
- if (mode == M_ADD || getarg(args, 'G') != NULL)
- editgroups(pwd->pw_name, cnf->groups);
+ if (mode == M_ADD || getarg(args, 'G') != NULL) {
+ int i, j;
+ for (i = 0; cnf->groups[i] != NULL; i++) {
+ grp = GETGRNAM(cnf->groups[i]);
+ for (j = 0; grp->gr_mem[j] != NULL; j++) {
+ if (!strcmp(grp->gr_mem[j], pwd->pw_name))
+ break;
+ }
+ if (grp->gr_mem[j] != NULL) /* user already member of group */
+ continue;
+
+ if (j == 0)
+ grp->gr_mem = NULL;
+
+ grp->gr_mem = reallocf(grp->gr_mem, sizeof(*grp->gr_mem) *
+ (j + 2));
+
+ grp->gr_mem[j] = pwd->pw_name;
+ grp->gr_mem[j+1] = NULL;
+ chggrent(cnf->groups[i], grp);
+ }
+ }
+
/* go get a current version of pwd */
pwd = GETPWNAM(a_name->val);
@@ -1090,10 +1130,14 @@ static int
print_user(struct passwd * pwd, int pretty, int v7)
{
if (!pretty) {
- char buf[_UC_MAXLINE];
+ char *buf;
+
+ if (!v7)
+ pwd->pw_passwd = (pwd->pw_passwd == NULL) ? "" : "*";
- fmtpwentry(buf, pwd, v7 ? PWF_PASSWD : PWF_STANDARD);
- fputs(buf, stdout);
+ buf = v7 ? pw_make_v7(pwd) : pw_make(pwd);
+ printf("%s\n", buf);
+ free(buf);
} else {
int j;
char *p;
diff --git a/pw/pw_vpw.c b/pw/pw_vpw.c
new file mode 100644
index 0000000..674b64f
--- /dev/null
+++ b/pw/pw_vpw.c
@@ -0,0 +1,302 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/param.h>
+
+#include "pwupd.h"
+
+static FILE * pwd_fp = NULL;
+
+void
+vendpwent(void)
+{
+ if (pwd_fp != NULL) {
+ fclose(pwd_fp);
+ pwd_fp = NULL;
+ }
+}
+
+void
+vsetpwent(void)
+{
+ vendpwent();
+}
+
+static struct passwd *
+vnextpwent(char const * nam, uid_t uid, int doclose)
+{
+ struct passwd * pw = NULL;
+ static char pwtmp[1024];
+
+ strlcpy(pwtmp, getpwpath(_MASTERPASSWD), sizeof(pwtmp));
+
+ if (pwd_fp != NULL || (pwd_fp = fopen(pwtmp, "r")) != NULL) {
+ int done = 0;
+
+ static struct passwd pwd;
+
+ while (!done && fgets(pwtmp, sizeof pwtmp, pwd_fp) != NULL)
+ {
+ int i, quickout = 0;
+ char * q;
+ char * p = strchr(pwtmp, '\n');
+
+ if (p == NULL) {
+ while (fgets(pwtmp, sizeof pwtmp, pwd_fp) != NULL && strchr(pwtmp, '\n')==NULL)
+ ; /* Skip long lines */
+ continue;
+ }
+
+ /* skip comments & empty lines */
+ if (*pwtmp =='\n' || *pwtmp == '#')
+ continue;
+
+ i = 0;
+ q = p = pwtmp;
+ bzero(&pwd, sizeof pwd);
+ while (!quickout && (p = strsep(&q, ":\n")) != NULL) {
+ switch (i++)
+ {
+ case 0: /* username */
+ pwd.pw_name = p;
+ if (nam) {
+ if (strcmp(nam, p) == 0)
+ done = 1;
+ else
+ quickout = 1;
+ }
+ break;
+ case 1: /* password */
+ pwd.pw_passwd = p;
+ break;
+ case 2: /* uid */
+ pwd.pw_uid = atoi(p);
+ if (uid != (uid_t)-1) {
+ if (uid == pwd.pw_uid)
+ done = 1;
+ else
+ quickout = 1;
+ }
+ break;
+ case 3: /* gid */
+ pwd.pw_gid = atoi(p);
+ break;
+ case 4: /* class */
+ if (nam == NULL && uid == (uid_t)-1)
+ done = 1;
+ pwd.pw_class = p;
+ break;
+ case 5: /* change */
+ pwd.pw_change = (time_t)atol(p);
+ break;
+ case 6: /* expire */
+ pwd.pw_expire = (time_t)atol(p);
+ break;
+ case 7: /* gecos */
+ pwd.pw_gecos = p;
+ break;
+ case 8: /* directory */
+ pwd.pw_dir = p;
+ break;
+ case 9: /* shell */
+ pwd.pw_shell = p;
+ break;
+ }
+ }
+ }
+ if (doclose)
+ vendpwent();
+ if (done && pwd.pw_name) {
+ pw = &pwd;
+
+ #define CKNULL(s) s = s ? s : ""
+ CKNULL(pwd.pw_passwd);
+ CKNULL(pwd.pw_class);
+ CKNULL(pwd.pw_gecos);
+ CKNULL(pwd.pw_dir);
+ CKNULL(pwd.pw_shell);
+ }
+ }
+ return pw;
+}
+
+struct passwd *
+vgetpwent(void)
+{
+ return vnextpwent(NULL, -1, 0);
+}
+
+struct passwd *
+vgetpwuid(uid_t uid)
+{
+ return vnextpwent(NULL, uid, 1);
+}
+
+struct passwd *
+vgetpwnam(const char * nam)
+{
+ return vnextpwent(nam, -1, 1);
+}
+
+
+static FILE * grp_fp = NULL;
+
+void
+vendgrent(void)
+{
+ if (grp_fp != NULL) {
+ fclose(grp_fp);
+ grp_fp = NULL;
+ }
+}
+
+RET_SETGRENT
+vsetgrent(void)
+{
+ vendgrent();
+#if defined(__FreeBSD__)
+ return 0;
+#endif
+}
+
+static struct group *
+vnextgrent(char const * nam, gid_t gid, int doclose)
+{
+ struct group * gr = NULL;
+
+ static char * grtmp = NULL;
+ static int grlen = 0;
+ static char ** mems = NULL;
+ static int memlen = 0;
+
+ extendline(&grtmp, &grlen, MAXPATHLEN);
+ strlcpy(grtmp, getgrpath(_GROUP), MAXPATHLEN);
+
+ if (grp_fp != NULL || (grp_fp = fopen(grtmp, "r")) != NULL) {
+ int done = 0;
+
+ static struct group grp;
+
+ while (!done && fgets(grtmp, grlen, grp_fp) != NULL)
+ {
+ int i, quickout = 0;
+ int mno = 0;
+ char * q, * p;
+ const char * sep = ":\n";
+
+ if ((p = strchr(grtmp, '\n')) == NULL) {
+ int l;
+ extendline(&grtmp, &grlen, grlen + PWBUFSZ);
+ l = strlen(grtmp);
+ if (fgets(grtmp + l, grlen - l, grp_fp) == NULL)
+ break; /* No newline terminator on last line */
+ }
+ /* Skip comments and empty lines */
+ if (*grtmp == '\n' || *grtmp == '#')
+ continue;
+ i = 0;
+ q = p = grtmp;
+ bzero(&grp, sizeof grp);
+ extendarray(&mems, &memlen, 200);
+ while (!quickout && (p = strsep(&q, sep)) != NULL) {
+ switch (i++)
+ {
+ case 0: /* groupname */
+ grp.gr_name = p;
+ if (nam) {
+ if (strcmp(nam, p) == 0)
+ done = 1;
+ else
+ quickout = 1;
+ }
+ break;
+ case 1: /* password */
+ grp.gr_passwd = p;
+ break;
+ case 2: /* gid */
+ grp.gr_gid = atoi(p);
+ if (gid != (gid_t)-1) {
+ if (gid == (gid_t)grp.gr_gid)
+ done = 1;
+ else
+ quickout = 1;
+ } else if (nam == NULL)
+ done = 1;
+ break;
+ case 3:
+ q = p;
+ sep = ",\n";
+ break;
+ default:
+ if (*p) {
+ extendarray(&mems, &memlen, mno + 2);
+ mems[mno++] = p;
+ }
+ break;
+ }
+ }
+ grp.gr_mem = mems;
+ mems[mno] = NULL;
+ }
+ if (doclose)
+ vendgrent();
+ if (done && grp.gr_name) {
+ gr = &grp;
+
+ CKNULL(grp.gr_passwd);
+ }
+ }
+ return gr;
+}
+
+struct group *
+vgetgrent(void)
+{
+ return vnextgrent(NULL, -1, 0);
+}
+
+
+struct group *
+vgetgrgid(gid_t gid)
+{
+ return vnextgrent(NULL, gid, 1);
+}
+
+struct group *
+vgetgrnam(const char * nam)
+{
+ return vnextgrent(nam, -1, 1);
+}
+
diff --git a/pw/pwupd.c b/pw/pwupd.c
index 1e20cc4..4ab0f01 100644
--- a/pw/pwupd.c
+++ b/pw/pwupd.c
@@ -34,7 +34,10 @@ static const char rcsid[] =
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
+#include <pwd.h>
+#include <libutil.h>
#include <errno.h>
+#include <err.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
@@ -71,7 +74,7 @@ getpwpath(char const * file)
return pathbuf;
}
-int
+static int
pwdb(char *arg,...)
{
int i = 0;
@@ -106,44 +109,11 @@ pwdb(char *arg,...)
return i;
}
-int
-fmtpwentry(char *buf, struct passwd * pwd, int type)
-{
- int l;
- char *pw;
-
- pw = (type == PWF_MASTER) ?
- ((pwd->pw_passwd == NULL) ? "" : pwd->pw_passwd) : "*";
-
- if (type == PWF_PASSWD)
- l = sprintf(buf, "%s:*:%ld:%ld:%s:%s:%s\n",
- pwd->pw_name, (long) pwd->pw_uid, (long) pwd->pw_gid,
- pwd->pw_gecos ? pwd->pw_gecos : "User &",
- pwd->pw_dir, pwd->pw_shell);
- else
- l = sprintf(buf, "%s:%s:%ld:%ld:%s:%lu:%lu:%s:%s:%s\n",
- pwd->pw_name, pw, (long) pwd->pw_uid, (long) pwd->pw_gid,
- pwd->pw_class ? pwd->pw_class : "",
- (unsigned long) pwd->pw_change,
- (unsigned long) pwd->pw_expire,
- pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
- return l;
-}
-
-
-int
-fmtpwent(char *buf, struct passwd * pwd)
-{
- return fmtpwentry(buf, pwd, PWF_STANDARD);
-}
-
static int
-pw_update(struct passwd * pwd, char const * user, int mode)
+pw_update(struct passwd * pwd, char const * user)
{
int rc = 0;
- ENDPWENT();
-
/*
* First, let's check the see if the database is alright
* Note: -C is only available in FreeBSD 2.2 and above
@@ -154,61 +124,57 @@ pw_update(struct passwd * pwd, char const * user, int mode)
#else
{ /* No -C */
#endif
- char pfx[PWBUFSZ];
- char pwbuf[PWBUFSZ];
- int l = snprintf(pfx, PWBUFSZ, "%s:", user);
-#ifdef HAVE_PWDB_U
- int isrename = pwd!=NULL && strcmp(user, pwd->pw_name);
-#endif
+ int pfd, tfd;
+ struct passwd *pw = NULL;
+ struct passwd *old_pw = NULL;
- /*
- * Update the passwd file first
- */
- if (pwd == NULL)
- *pwbuf = '\0';
- else
- fmtpwentry(pwbuf, pwd, PWF_PASSWD);
-
- if (l < 0)
- l = 0;
- rc = fileupdate(getpwpath(_PASSWD), 0644, pwbuf, pfx, l, mode);
- if (rc == 0) {
-
- /*
- * Then the master.passwd file
- */
- if (pwd != NULL)
- fmtpwentry(pwbuf, pwd, PWF_MASTER);
- rc = fileupdate(getpwpath(_MASTERPASSWD), 0600, pwbuf, pfx, l, mode);
- if (rc == 0) {
-#ifdef HAVE_PWDB_U
- if (mode == UPD_DELETE || isrename)
-#endif
- rc = pwdb(NULL);
-#ifdef HAVE_PWDB_U
- else
- rc = pwdb("-u", user, (char *)NULL);
-#endif
- }
+ if (pwd != NULL)
+ pw = pw_dup(pwd);
+
+ if (user != NULL)
+ old_pw = GETPWNAM(user);
+
+ if (pw_init(pwpath, NULL))
+ err(1, "pw_init()");
+ if ((pfd = pw_lock()) == -1) {
+ pw_fini();
+ err(1, "pw_lock()");
+ }
+ if ((tfd = pw_tmp(-1)) == -1) {
+ pw_fini();
+ err(1, "pw_tmp()");
+ }
+ if (pw_copy(pfd, tfd, pw, old_pw) == -1) {
+ pw_fini();
+ err(1, "pw_copy()");
}
+ if (pw_mkdb(user) == -1) {
+ pw_fini();
+ err(1, "pw_mkdb()");
+ }
+ free(pw);
+ pw_fini();
}
- return rc;
+ return 0;
}
int
addpwent(struct passwd * pwd)
{
- return pw_update(pwd, pwd->pw_name, UPD_CREATE);
+ return pw_update(pwd, NULL);
}
int
chgpwent(char const * login, struct passwd * pwd)
{
- return pw_update(pwd, login, UPD_REPLACE);
+ return pw_update(pwd, login);
}
int
delpwent(struct passwd * pwd)
{
- return pw_update(NULL, pwd->pw_name, UPD_DELETE);
+ char login[MAXLOGNAME];
+
+ strlcpy(login, pwd->pw_name, MAXLOGNAME);
+ return pw_update(NULL, login);
}
diff --git a/pw/pwupd.h b/pw/pwupd.h
new file mode 100644
index 0000000..200ffee
--- /dev/null
+++ b/pw/pwupd.h
@@ -0,0 +1,121 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. 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 DAVID L. NUGENT 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 DAVID L. NUGENT 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PWUPD_H_
+#define _PWUPD_H_
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <sys/cdefs.h>
+
+#if defined(__FreeBSD__)
+#define RET_SETGRENT int
+#else
+#define RET_SETGRENT void
+#endif
+
+struct pwf
+{
+ int _altdir;
+ void (*_setpwent)(void);
+ void (*_endpwent)(void);
+ struct passwd * (*_getpwent)(void);
+ struct passwd * (*_getpwuid)(uid_t uid);
+ struct passwd * (*_getpwnam)(const char * nam);
+ RET_SETGRENT (*_setgrent)(void);
+ void (*_endgrent)(void);
+ struct group * (*_getgrent)(void);
+ struct group * (*_getgrgid)(gid_t gid);
+ struct group * (*_getgrnam)(const char * nam);
+};
+
+extern struct pwf PWF;
+extern struct pwf VPWF;
+
+#define SETPWENT() PWF._setpwent()
+#define ENDPWENT() PWF._endpwent()
+#define GETPWENT() PWF._getpwent()
+#define GETPWUID(uid) PWF._getpwuid(uid)
+#define GETPWNAM(nam) PWF._getpwnam(nam)
+
+#define SETGRENT() PWF._setgrent()
+#define ENDGRENT() PWF._endgrent()
+#define GETGRENT() PWF._getgrent()
+#define GETGRGID(gid) PWF._getgrgid(gid)
+#define GETGRNAM(nam) PWF._getgrnam(nam)
+
+#define PWALTDIR() PWF._altdir
+#ifndef _PATH_PWD
+#define _PATH_PWD "/etc"
+#endif
+#ifndef _GROUP
+#define _GROUP "group"
+#endif
+#ifndef _MASTERPASSWD
+#define _MASTERPASSWD "master.passwd"
+#endif
+
+__BEGIN_DECLS
+int addpwent(struct passwd * pwd);
+int delpwent(struct passwd * pwd);
+int chgpwent(char const * login, struct passwd * pwd);
+
+int setpwdir(const char * dir);
+char * getpwpath(char const * file);
+
+int addgrent(struct group * grp);
+int delgrent(struct group * grp);
+int chggrent(char const * name, struct group * grp);
+int editgroups(char *name, char **groups);
+
+int setgrdir(const char * dir);
+char * getgrpath(const char *file);
+
+void vsetpwent(void);
+void vendpwent(void);
+struct passwd * vgetpwent(void);
+struct passwd * vgetpwuid(uid_t uid);
+struct passwd * vgetpwnam(const char * nam);
+
+struct group * vgetgrent(void);
+struct group * vgetgrgid(gid_t gid);
+struct group * vgetgrnam(const char * nam);
+RET_SETGRENT vsetgrent(void);
+void vendgrent(void);
+
+void copymkdir(char const * dir, char const * skel, mode_t mode, uid_t uid, gid_t gid);
+void rm_r(char const * dir, uid_t uid);
+int extendline(char **buf, int *buflen, int needed);
+int extendarray(char ***buf, int *buflen, int needed);
+__END_DECLS
+
+#define PWBUFSZ 1024
+
+#endif /* !_PWUPD_H */