diff options
author | Baptiste Daroussin <bapt@FreeBSD.org> | 2012-10-30 08:00:53 +0000 |
---|---|---|
committer | Baptiste Daroussin <bapt@FreeBSD.org> | 2012-10-30 08:00:53 +0000 |
commit | 32215e1fded83eb454a0ec2522dbd53877e5a384 (patch) | |
tree | 39860234c3deda7de976cccc795de42ae7b0aa77 /pw | |
parent | 955a095f02cda3bf4ac1e3bd760317addf54b19a (diff) | |
download | pw-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/Makefile | 14 | ||||
-rw-r--r-- | pw/fileupd.c | 68 | ||||
-rw-r--r-- | pw/grupd.c | 129 | ||||
-rw-r--r-- | pw/pw.8 | 8 | ||||
-rw-r--r-- | pw/pw.c | 452 | ||||
-rw-r--r-- | pw/pw.h | 124 | ||||
-rw-r--r-- | pw/pw_group.c | 424 | ||||
-rw-r--r-- | pw/pw_log.c | 68 | ||||
-rw-r--r-- | pw/pw_nis.c | 94 | ||||
-rw-r--r-- | pw/pw_user.c | 58 | ||||
-rw-r--r-- | pw/pw_vpw.c | 302 | ||||
-rw-r--r-- | pw/pwupd.c | 114 | ||||
-rw-r--r-- | pw/pwupd.h | 121 |
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); +} @@ -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 @@ -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; +} @@ -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); +} + @@ -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 */ |