diff options
-rw-r--r-- | libutil/libutil.h | 2 | ||||
-rw-r--r-- | pw/Makefile | 2 | ||||
-rw-r--r-- | pw/grupd.c | 27 | ||||
-rw-r--r-- | pw/pw.8 | 21 | ||||
-rw-r--r-- | pw/pw.c | 179 | ||||
-rw-r--r-- | pw/pw.h | 30 | ||||
-rw-r--r-- | pw/pw_conf.c | 235 | ||||
-rw-r--r-- | pw/pw_group.c | 128 | ||||
-rw-r--r-- | pw/pw_user.c | 483 | ||||
-rw-r--r-- | pw/pwupd.c | 29 | ||||
-rw-r--r-- | pw/pwupd.h | 52 |
11 files changed, 629 insertions, 559 deletions
diff --git a/libutil/libutil.h b/libutil/libutil.h index b8b9836..b20ffa2 100644 --- a/libutil/libutil.h +++ b/libutil/libutil.h @@ -102,6 +102,8 @@ struct kinfo_file * kinfo_getfile(pid_t _pid, int *_cntp); struct kinfo_vmentry * kinfo_getvmmap(pid_t _pid, int *_cntp); +struct kinfo_vmobject * + kinfo_getvmobject(int *_cntp); struct kinfo_proc * kinfo_getallproc(int *_cntp); struct kinfo_proc * diff --git a/pw/Makefile b/pw/Makefile index 69953da..4bcf691 100644 --- a/pw/Makefile +++ b/pw/Makefile @@ -6,7 +6,7 @@ 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 +WARNS?= 3 LIBADD= crypt util sbuf @@ -39,28 +39,14 @@ static const char rcsid[] = #include "pwupd.h" -static char * grpath = _PATH_PWD; - -int -setgrdir(const char * dir) -{ - if (dir == NULL) - return -1; - else - grpath = strdup(dir); - if (grpath == NULL) - return -1; - - return 0; -} - char * getgrpath(const char * file) { static char pathbuf[MAXPATHLEN]; - snprintf(pathbuf, sizeof pathbuf, "%s/%s", grpath, file); - return pathbuf; + snprintf(pathbuf, sizeof pathbuf, "%s/%s", conf.etcpath, file); + + return (pathbuf); } static int @@ -76,7 +62,7 @@ gr_update(struct group * grp, char const * group) if (group != NULL) old_gr = GETGRNAM(group); - if (gr_init(grpath, NULL)) + if (gr_init(conf.etcpath, NULL)) err(1, "gr_init()"); if ((pfd = gr_lock()) == -1) { @@ -116,9 +102,6 @@ chggrent(char const * login, struct group * grp) int delgrent(struct group * grp) { - char group[MAXLOGNAME]; - - strlcpy(group, grp->gr_name, MAXLOGNAME); - return gr_update(NULL, group); + return (gr_update(NULL, grp->gr_name)); } @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 29, 2012 +.Dd June 3, 2015 .Dt PW 8 .Os .Sh NAME @@ -32,6 +32,7 @@ .Nd create, remove, modify & display system users and groups .Sh SYNOPSIS .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar useradd .Op name|uid @@ -57,6 +58,7 @@ .Op Fl P .Op Fl Y .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar useradd .Op name|uid @@ -76,6 +78,7 @@ .Op Fl s Ar shell .Op Fl y Ar path .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar userdel .Op name|uid @@ -84,6 +87,7 @@ .Op Fl r .Op Fl Y .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar usermod .Op name|uid @@ -109,6 +113,7 @@ .Op Fl P .Op Fl Y .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar usershow .Op name|uid @@ -119,11 +124,13 @@ .Op Fl 7 .Op Fl a .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar usernext .Op Fl C Ar config .Op Fl q .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar groupadd .Op group|gid @@ -138,6 +145,7 @@ .Op Fl P .Op Fl Y .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar groupdel .Op group|gid @@ -145,6 +153,7 @@ .Op Fl g Ar gid .Op Fl Y .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar groupmod .Op group|gid @@ -161,6 +170,7 @@ .Op Fl P .Op Fl Y .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar groupshow .Op group|gid @@ -170,17 +180,20 @@ .Op Fl P .Op Fl a .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar groupnext .Op Fl C Ar config .Op Fl q .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar lock .Op name|uid .Op Fl C Ar config .Op Fl q .Nm +.Op Fl R Ar rootdir .Op Fl V Ar etcdir .Ar unlock .Op name|uid @@ -246,6 +259,12 @@ options. .Pp The following flags are common to most or all modes of operation: .Bl -tag -width "-G grouplist" +.It Fl R Ar rootdir +Specifies an alternate root directory within which +.Nm +will operate. +Any paths specified will be relative to +.Va rootdir . .It Fl V Ar etcdir This flag sets an alternate location for the password, group and configuration files, and may be used to maintain a user/group database in an alternate location. @@ -33,6 +33,7 @@ static const char rcsid[] = #include <fcntl.h> #include <locale.h> #include <paths.h> +#include <stdbool.h> #include <sys/wait.h> #include "pw.h" @@ -56,7 +57,7 @@ static const char *Combo2[] = { struct pwf PWF = { - 0, + PWF_REGULAR, setpwent, endpwent, getpwent, @@ -71,7 +72,7 @@ struct pwf PWF = }; struct pwf VPWF = { - 1, + PWF_ALT, vsetpwent, vendpwent, vgetpwent, @@ -84,6 +85,8 @@ struct pwf VPWF = vgetgrnam, }; +struct pwconf conf; + static struct cargs arglist; static int getindex(const char *words[], const char *word); @@ -96,36 +99,45 @@ main(int argc, char *argv[]) int ch; int mode = -1; int which = -1; + long id = -1; char *config = NULL; - struct userconf *cnf; struct stat st; + const char *errstr; + char arg, *name; + bool relocated, nis; 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" + "R:V:C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y", + "R:V:C:qn:u:rY", + "R:V:C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:FNPY", + "R:V:C:qn:u:FPa7", + "R:V:C:q", + "R:V:C:q", + "R: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" + "R:V:C:qn:g:h:H:M:opNPY", + "R:V:C:qn:g:Y", + "R:V:C:qn:d:g:l:h:H:FM:m:NPY", + "R:V:C:qn:g:FPa", + "R:V:C:q" } }; - static int (*funcs[W_NUM]) (struct userconf * _cnf, int _mode, struct cargs * _args) = + static int (*funcs[W_NUM]) (int _mode, char *_name, long _id, + struct cargs * _args) = { /* Request handlers */ pw_user, pw_group }; + name = NULL; + relocated = nis = false; + memset(&conf, 0, sizeof(conf)); + strlcpy(conf.etcpath, _PATH_PWD, sizeof(conf.etcpath)); + LIST_INIT(&arglist); (void)setlocale(LC_ALL, ""); @@ -141,7 +153,12 @@ main(int argc, char *argv[]) /* * Special case, allow pw -V<dir> <operation> [args] for scripts etc. */ - if (argv[1][1] == 'V') { + arg = argv[1][1]; + if (arg == 'V' || arg == 'R') { + if (relocated) + errx(EXIT_FAILURE, "Both '-R' and '-V' " + "specified, only one accepted"); + relocated = true; optarg = &argv[1][2]; if (*optarg == '\0') { if (stat(argv[2], &st) != 0) @@ -155,7 +172,14 @@ main(int argc, char *argv[]) ++argv; --argc; } - addarg(&arglist, 'V', optarg); + memcpy(&PWF, &VPWF, sizeof PWF); + if (arg == 'R') { + strlcpy(conf.rootdir, optarg, + sizeof(conf.rootdir)); + PWF._altdir = PWF_ROOTDIR; + } + snprintf(conf.etcpath, sizeof(conf.etcpath), + "%s%s", optarg, arg == 'R' ? "/etc" : ""); } else break; } @@ -170,9 +194,15 @@ main(int argc, char *argv[]) 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 + else if (which != -1 && mode != -1) { + if (strspn(argv[1], "0123456789") == strlen(argv[1])) { + id = strtonum(argv[1], 0, LONG_MAX, &errstr); + if (errstr != NULL) + errx(EX_USAGE, "Bad id '%s': %s", + argv[1], errstr); + } else + name = argv[1]; + } else errx(EX_USAGE, "unknown keyword `%s'", argv[1]); ++argv; --argc; @@ -191,17 +221,82 @@ main(int argc, char *argv[]) optarg = NULL; while ((ch = getopt(argc, argv, opts[which][mode])) != -1) { - if (ch == '?') + switch (ch) { + case '?': errx(EX_USAGE, "unknown switch"); - else + break; + case '7': + conf.v7 = true; + break; + case 'C': + conf.config = optarg; + config = conf.config; + break; + case 'N': + conf.dryrun = true; + break; + case 'l': + if (strlen(optarg) >= MAXLOGNAME) + errx(EX_USAGE, "new name too long: %s", optarg); + conf.newname = optarg; + break; + case 'P': + conf.pretty = true; + break; + case 'Y': + nis = true; + break; + case 'g': + if (which == 0) { /* for user* */ + addarg(&arglist, 'g', optarg); + break; + } + if (strspn(optarg, "0123456789") != strlen(optarg)) + errx(EX_USAGE, "-g expects a number"); + id = strtonum(optarg, 0, LONG_MAX, &errstr); + if (errstr != NULL) + errx(EX_USAGE, "Bad id '%s': %s", optarg, + errstr); + break; + case 'u': + if (strspn(optarg, "0123456789,") != strlen(optarg)) + errx(EX_USAGE, "-u expects a number"); + if (strchr(optarg, ',') != NULL) { + addarg(&arglist, 'u', optarg); + break; + } + id = strtonum(optarg, 0, LONG_MAX, &errstr); + if (errstr != NULL) + errx(EX_USAGE, "Bad id '%s': %s", optarg, + errstr); + break; + case 'n': + if (strspn(optarg, "0123456789") != strlen(optarg)) { + name = optarg; + break; + } + id = strtonum(optarg, 0, LONG_MAX, &errstr); + if (errstr != NULL) + errx(EX_USAGE, "Bad id '%s': %s", optarg, + errstr); + break; + case 'o': + conf.checkduplicate = true; + break; + default: addarg(&arglist, ch, optarg); + break; + } optarg = NULL; } + if (name != NULL && strlen(name) >= MAXLOGNAME) + errx(EX_USAGE, "name too long: %s", name); + /* * Must be root to attempt an update */ - if (geteuid() != 0 && mode != M_PRINT && mode != M_NEXT && getarg(&arglist, 'N')==NULL) + if (geteuid() != 0 && mode != M_PRINT && mode != M_NEXT && !conf.dryrun) errx(EX_NOPERM, "you must be root to run this program"); /* @@ -215,33 +310,24 @@ main(int argc, char *argv[]) * 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); - } + if (config == NULL) { /* Only override config location if -C not specified */ + asprintf(&config, "%s/pw.conf", conf.etcpath); + if (config == NULL) + errx(EX_OSERR, "out of memory"); } /* * Now, let's do the common initialisation */ - cnf = read_userconfig(config); + conf.userconf = read_userconfig(config); - ch = funcs[which] (cnf, mode, &arglist); + ch = funcs[which] (mode, name, id, &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) { + if (ch == EXIT_SUCCESS && nis) { pid_t pid; fflush(NULL); @@ -259,7 +345,7 @@ main(int argc, char *argv[]) if ((i = WEXITSTATUS(i)) != 0) errx(ch, "make exited with status %d", i); else - pw_log(cnf, mode, which, "NIS maps updated"); + pw_log(conf.userconf, mode, which, "NIS maps updated"); } } return ch; @@ -302,6 +388,7 @@ cmdhelp(int mode, int which) { "usage: pw useradd [name] [switches]\n" "\t-V etcdir alternate /etc location\n" + "\t-R rootir alternate root directory\n" "\t-C config configuration file\n" "\t-q quiet operation\n" " Adding users:\n" @@ -324,6 +411,7 @@ cmdhelp(int mode, int which) "\t-N no update\n" " Setting defaults:\n" "\t-V etcdir alternate /etc location\n" + "\t-R rootir alternate root directory\n" "\t-D set user defaults\n" "\t-b dir default home root dir\n" "\t-e period default expiry period\n" @@ -340,12 +428,14 @@ cmdhelp(int mode, int which) "\t-y path set NIS passwd file path\n", "usage: pw userdel [uid|name] [switches]\n" "\t-V etcdir alternate /etc location\n" + "\t-R rootir alternate root directory\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-R rootir alternate root directory\n" "\t-C config configuration file\n" "\t-q quiet operation\n" "\t-F force add if no user\n" @@ -369,6 +459,7 @@ cmdhelp(int mode, int which) "\t-N no update\n", "usage: pw usershow [uid|name] [switches]\n" "\t-V etcdir alternate /etc location\n" + "\t-R rootir alternate root directory\n" "\t-n name login name\n" "\t-u uid user id\n" "\t-F force print\n" @@ -377,6 +468,7 @@ cmdhelp(int mode, int which) "\t-7 print in v7 format\n", "usage: pw usernext [switches]\n" "\t-V etcdir alternate /etc location\n" + "\t-R rootir alternate root directory\n" "\t-C config configuration file\n" "\t-q quiet operation\n", "usage pw: lock [switches]\n" @@ -391,6 +483,7 @@ cmdhelp(int mode, int which) { "usage: pw groupadd [group|gid] [switches]\n" "\t-V etcdir alternate /etc location\n" + "\t-R rootir alternate root directory\n" "\t-C config configuration file\n" "\t-q quiet operation\n" "\t-n group group name\n" @@ -401,11 +494,13 @@ cmdhelp(int mode, int which) "\t-N no update\n", "usage: pw groupdel [group|gid] [switches]\n" "\t-V etcdir alternate /etc location\n" + "\t-R rootir alternate root directory\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-R rootir alternate root directory\n" "\t-C config configuration file\n" "\t-q quiet operation\n" "\t-F force add if not exists\n" @@ -419,6 +514,7 @@ cmdhelp(int mode, int which) "\t-N no update\n", "usage: pw groupshow [group|gid] [switches]\n" "\t-V etcdir alternate /etc location\n" + "\t-R rootir alternate root directory\n" "\t-n name group name\n" "\t-g gid group id\n" "\t-F force print\n" @@ -426,6 +522,7 @@ cmdhelp(int mode, int which) "\t-a print all accounting groups\n", "usage: pw groupnext [switches]\n" "\t-V etcdir alternate /etc location\n" + "\t-R rootir alternate root directory\n" "\t-C config configuration file\n" "\t-q quiet operation\n" } @@ -72,30 +72,6 @@ struct carg 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 @@ -106,9 +82,9 @@ 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 pw_user(int mode, char *name, long id, struct cargs * _args); +int pw_group(int mode, char *name, long id, struct cargs * _args); +char *pw_checkname(char *name, int gecos); int addnispwent(const char *path, struct passwd *pwd); int delnispwent(const char *path, const char *login); diff --git a/pw/pw_conf.c b/pw/pw_conf.c index 99d3e8f..24c0650 100644 --- a/pw/pw_conf.c +++ b/pw/pw_conf.c @@ -241,126 +241,127 @@ read_userconfig(char const * file) if (file == NULL) file = _PATH_PW_CONF; - if ((fp = fopen(file, "r")) != NULL) { - while ((linelen = getline(&buf, &linecap, fp)) > 0) { - if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { - static char const toks[] = " \t\r\n,="; - char *q = strtok(NULL, toks); - int i = 0; - mode_t *modeset; - - while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) - ++i; + if ((fp = fopen(file, "r")) == NULL) + return (&config); + + while ((linelen = getline(&buf, &linecap, fp)) > 0) { + if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { + static char const toks[] = " \t\r\n,="; + char *q = strtok(NULL, toks); + int i = 0; + mode_t *modeset; + + while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) + ++i; #if debugging - if (i == _UC_FIELDS) - printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); - else - printf("Got kwd[%s]=%s\n", p, q); + if (i == _UC_FIELDS) + printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); + else + printf("Got kwd[%s]=%s\n", p, q); #endif - switch (i) { - case _UC_DEFAULTPWD: - config.default_password = boolean_val(q, 1); - break; - case _UC_REUSEUID: - config.reuse_uids = boolean_val(q, 0); - break; - case _UC_REUSEGID: - config.reuse_gids = boolean_val(q, 0); - break; - case _UC_NISPASSWD: - config.nispasswd = (q == NULL || !boolean_val(q, 1)) - ? NULL : newstr(q); - break; - case _UC_DOTDIR: - config.dotdir = (q == NULL || !boolean_val(q, 1)) - ? NULL : newstr(q); - break; + switch (i) { + case _UC_DEFAULTPWD: + config.default_password = boolean_val(q, 1); + break; + case _UC_REUSEUID: + config.reuse_uids = boolean_val(q, 0); + break; + case _UC_REUSEGID: + config.reuse_gids = boolean_val(q, 0); + break; + case _UC_NISPASSWD: + config.nispasswd = (q == NULL || !boolean_val(q, 1)) + ? NULL : newstr(q); + break; + case _UC_DOTDIR: + config.dotdir = (q == NULL || !boolean_val(q, 1)) + ? NULL : newstr(q); + break; case _UC_NEWMAIL: - config.newmail = (q == NULL || !boolean_val(q, 1)) - ? NULL : newstr(q); - break; - case _UC_LOGFILE: - config.logfile = (q == NULL || !boolean_val(q, 1)) - ? NULL : newstr(q); - break; - case _UC_HOMEROOT: - config.home = (q == NULL || !boolean_val(q, 1)) - ? "/home" : newstr(q); - break; - case _UC_HOMEMODE: - modeset = setmode(q); - config.homemode = (q == NULL || !boolean_val(q, 1)) - ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE); - free(modeset); - break; - case _UC_SHELLPATH: - config.shelldir = (q == NULL || !boolean_val(q, 1)) - ? "/bin" : newstr(q); - break; - case _UC_SHELLS: - for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) - system_shells[i] = newstr(q); - if (i > 0) - while (i < _UC_MAXSHELLS) - system_shells[i++] = NULL; - break; - case _UC_DEFAULTSHELL: - config.shell_default = (q == NULL || !boolean_val(q, 1)) - ? (char *) bourne_shell : newstr(q); - break; - case _UC_DEFAULTGROUP: - q = unquote(q); - config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL) - ? NULL : newstr(q); - break; - case _UC_EXTRAGROUPS: - for (i = 0; q != NULL; q = strtok(NULL, toks)) { - if (extendarray(&config.groups, &config.numgroups, i + 2) != -1) - config.groups[i++] = newstr(q); - } - if (i > 0) - while (i < config.numgroups) - config.groups[i++] = NULL; - break; - case _UC_DEFAULTCLASS: - config.default_class = (q == NULL || !boolean_val(q, 1)) - ? NULL : newstr(q); - break; - case _UC_MINUID: - if ((q = unquote(q)) != NULL && isdigit(*q)) - config.min_uid = (uid_t) atol(q); - break; - case _UC_MAXUID: - if ((q = unquote(q)) != NULL && isdigit(*q)) - config.max_uid = (uid_t) atol(q); - break; - case _UC_MINGID: - if ((q = unquote(q)) != NULL && isdigit(*q)) - config.min_gid = (gid_t) atol(q); - break; - case _UC_MAXGID: - if ((q = unquote(q)) != NULL && isdigit(*q)) - config.max_gid = (gid_t) atol(q); - break; - case _UC_EXPIRE: - if ((q = unquote(q)) != NULL && isdigit(*q)) - config.expire_days = atoi(q); - break; - case _UC_PASSWORD: - if ((q = unquote(q)) != NULL && isdigit(*q)) - config.password_days = atoi(q); - break; - case _UC_FIELDS: - case _UC_NONE: - break; + config.newmail = (q == NULL || !boolean_val(q, 1)) + ? NULL : newstr(q); + break; + case _UC_LOGFILE: + config.logfile = (q == NULL || !boolean_val(q, 1)) + ? NULL : newstr(q); + break; + case _UC_HOMEROOT: + config.home = (q == NULL || !boolean_val(q, 1)) + ? "/home" : newstr(q); + break; + case _UC_HOMEMODE: + modeset = setmode(q); + config.homemode = (q == NULL || !boolean_val(q, 1)) + ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE); + free(modeset); + break; + case _UC_SHELLPATH: + config.shelldir = (q == NULL || !boolean_val(q, 1)) + ? "/bin" : newstr(q); + break; + case _UC_SHELLS: + for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) + system_shells[i] = newstr(q); + if (i > 0) + while (i < _UC_MAXSHELLS) + system_shells[i++] = NULL; + break; + case _UC_DEFAULTSHELL: + config.shell_default = (q == NULL || !boolean_val(q, 1)) + ? (char *) bourne_shell : newstr(q); + break; + case _UC_DEFAULTGROUP: + q = unquote(q); + config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL) + ? NULL : newstr(q); + break; + case _UC_EXTRAGROUPS: + for (i = 0; q != NULL; q = strtok(NULL, toks)) { + if (extendarray(&config.groups, &config.numgroups, i + 2) != -1) + config.groups[i++] = newstr(q); } + if (i > 0) + while (i < config.numgroups) + config.groups[i++] = NULL; + break; + case _UC_DEFAULTCLASS: + config.default_class = (q == NULL || !boolean_val(q, 1)) + ? NULL : newstr(q); + break; + case _UC_MINUID: + if ((q = unquote(q)) != NULL && isdigit(*q)) + config.min_uid = (uid_t) atol(q); + break; + case _UC_MAXUID: + if ((q = unquote(q)) != NULL && isdigit(*q)) + config.max_uid = (uid_t) atol(q); + break; + case _UC_MINGID: + if ((q = unquote(q)) != NULL && isdigit(*q)) + config.min_gid = (gid_t) atol(q); + break; + case _UC_MAXGID: + if ((q = unquote(q)) != NULL && isdigit(*q)) + config.max_gid = (gid_t) atol(q); + break; + case _UC_EXPIRE: + if ((q = unquote(q)) != NULL && isdigit(*q)) + config.expire_days = atoi(q); + break; + case _UC_PASSWORD: + if ((q = unquote(q)) != NULL && isdigit(*q)) + config.password_days = atoi(q); + break; + case _UC_FIELDS: + case _UC_NONE: + break; } } - if (linecap > 0) - free(buf); - fclose(fp); } - return &config; + free(buf); + fclose(fp); + + return (&config); } @@ -452,19 +453,19 @@ write_userconfig(char const * file) config.default_class : ""); break; case _UC_MINUID: - sbuf_printf(buf, "%lu", (unsigned long) config.min_uid); + sbuf_printf(buf, "%u", config.min_uid); quote = 0; break; case _UC_MAXUID: - sbuf_printf(buf, "%lu", (unsigned long) config.max_uid); + sbuf_printf(buf, "%u", config.max_uid); quote = 0; break; case _UC_MINGID: - sbuf_printf(buf, "%lu", (unsigned long) config.min_gid); + sbuf_printf(buf, "%u", config.min_gid); quote = 0; break; case _UC_MAXGID: - sbuf_printf(buf, "%lu", (unsigned long) config.max_gid); + sbuf_printf(buf, "%u", config.max_gid); quote = 0; break; case _UC_EXPIRE: diff --git a/pw/pw_group.c b/pw/pw_group.c index b20ce88..b9cce0d 100644 --- a/pw/pw_group.c +++ b/pw/pw_group.c @@ -44,20 +44,18 @@ static const char rcsid[] = 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); +static int print_group(struct group * grp); +static gid_t gr_gidpolicy(struct userconf * cnf, long id); int -pw_group(struct userconf * cnf, int mode, struct cargs * args) +pw_group(int mode, char *name, long id, struct cargs * args) { int rc; - struct carg *a_newname = getarg(args, 'l'); - 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; + struct userconf *cnf = conf.userconf; static struct group fakegroup = { @@ -67,11 +65,6 @@ pw_group(struct userconf * cnf, int mode, struct cargs * args) NULL }; - if (a_gid != NULL) { - if (strspn(a_gid->val, "0123456789") != strlen(a_gid->val)) - errx(EX_USAGE, "-g expects a number"); - } - if (mode == M_LOCK || mode == M_UNLOCK) errx(EX_USAGE, "'lock' command is not available for groups"); @@ -80,86 +73,79 @@ pw_group(struct userconf * cnf, int mode, struct cargs * args) * next gid to stdout */ if (mode == M_NEXT) { - gid_t next = gr_gidpolicy(cnf, args); + gid_t next = gr_gidpolicy(cnf, id); if (getarg(args, 'q')) return next; - printf("%ld\n", (long)next); + printf("%u\n", 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); + print_group(grp); ENDGRENT(); return EXIT_SUCCESS; } - if (a_gid == NULL) { - if (a_name == NULL) - errx(EX_DATAERR, "group name or id required"); + if (id < 0 && 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)); + grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id); 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 (name == NULL && grp == NULL) /* Try harder */ + grp = GETGRGID(id); 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_name = name ? name : "nogroup"; + fakegroup.gr_gid = (gid_t) id; fakegroup.gr_mem = fmems; - return print_group(&fakegroup, getarg(args, 'P') != NULL); + return print_group(&fakegroup); } - errx(EX_DATAERR, "unknown group `%s'", a_name ? a_name->val : a_gid->val); + if (name == NULL) + errx(EX_DATAERR, "unknown group `%s'", name); + else + errx(EX_DATAERR, "unknown group `%ld'", id); } - if (a_name == NULL) /* Needed later */ - a_name = addarg(args, 'n', grp->gr_name); + if (name == NULL) /* Needed later */ + name = 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); + err(EX_IOERR, "group '%s' not available (NIS?)", + name); else if (rc != 0) { - warn("group update"); - return EX_IOERR; + err(EX_IOERR, "group update"); } - pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) gid); + pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", name, id); return EXIT_SUCCESS; } else if (mode == M_PRINT) - return print_group(grp, getarg(args, 'P') != NULL); + return print_group(grp); - if (a_gid) - grp->gr_gid = (gid_t) atoi(a_gid->val); + if (id > 0) + grp->gr_gid = (gid_t) id; - if (a_newname != NULL) - grp->gr_name = pw_checkname((u_char *)a_newname->val, 0); + if (conf.newname != NULL) + grp->gr_name = pw_checkname(conf.newname, 0); } else { - if (a_name == NULL) /* Required */ + if (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); + errx(EX_DATAERR, "group name `%s' already exists", name); extendarray(&members, &grmembers, 200); members[0] = NULL; grp = &fakegroup; - grp->gr_name = pw_checkname((u_char *)a_name->val, 0); + grp->gr_name = pw_checkname(name, 0); grp->gr_passwd = "*"; - grp->gr_gid = gr_gidpolicy(cnf, args); + grp->gr_gid = gr_gidpolicy(cnf, id); grp->gr_mem = members; } @@ -201,10 +187,8 @@ pw_group(struct userconf * cnf, int mode, struct cargs * args) fputc('\n', stdout); fflush(stdout); } - if (b < 0) { - warn("-h file descriptor"); - return EX_OSERR; - } + if (b < 0) + err(EX_OSERR, "-h file descriptor"); line[b] = '\0'; if ((p = strpbrk(line, " \t\r\n")) != NULL) *p = '\0'; @@ -260,29 +244,30 @@ pw_group(struct userconf * cnf, int mode, struct cargs * args) grp->gr_mem = members; } - if (getarg(args, 'N') != NULL) - return print_group(grp, getarg(args, 'P') != NULL); + if (conf.dryrun) + return print_group(grp); if (mode == M_ADD && (rc = addgrent(grp)) != 0) { if (rc == -1) - warnx("group '%s' already exists", grp->gr_name); + errx(EX_IOERR, "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) { + err(EX_IOERR, "group update"); + } else if (mode == M_UPDATE && (rc = chggrent(name, grp)) != 0) { if (rc == -1) - warnx("group '%s' not available (NIS?)", grp->gr_name); + errx(EX_IOERR, "group '%s' not available (NIS?)", + grp->gr_name); else - warn("group update"); - return EX_IOERR; + err(EX_IOERR, "group update"); } - arg = a_newname != NULL ? a_newname : a_name; + if (conf.newname != NULL) + name = conf.newname; /* grp may have been invalidated */ - if ((grp = GETGRNAM(arg->val)) == NULL) + if ((grp = GETGRNAM(name)) == NULL) errx(EX_SOFTWARE, "group disappeared during update"); - pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid); + pw_log(cnf, mode, W_GROUP, "%s(%u)", grp->gr_name, grp->gr_gid); free(members); @@ -351,20 +336,19 @@ delete_members(char ***members, int *grmembers, int *i, struct carg *arg, static gid_t -gr_gidpolicy(struct userconf * cnf, struct cargs * args) +gr_gidpolicy(struct userconf * cnf, long id) { 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 (id > 0) { + gid = (gid_t) id; - if ((grp = GETGRGID(gid)) != NULL && getarg(args, 'o') == NULL) - errx(EX_DATAERR, "gid `%ld' has already been allocated", (long) grp->gr_gid); + if ((grp = GETGRGID(gid)) != NULL && conf.checkduplicate) + errx(EX_DATAERR, "gid `%u' has already been allocated", grp->gr_gid); } else { struct bitmap bm; @@ -414,9 +398,9 @@ gr_gidpolicy(struct userconf * cnf, struct cargs * args) static int -print_group(struct group * grp, int pretty) +print_group(struct group * grp) { - if (!pretty) { + if (!conf.pretty) { char *buf = NULL; buf = gr_make(grp); diff --git a/pw/pw_user.c b/pw/pw_user.c index b058aab..c3b2751 100644 --- a/pw/pw_user.c +++ b/pw/pw_user.c @@ -51,9 +51,11 @@ static const char rcsid[] = static char locked_str[] = "*LOCKED*"; -static int print_user(struct passwd * pwd, int pretty, int v7); -static uid_t pw_uidpolicy(struct userconf * cnf, struct cargs * args); -static uid_t pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer); +static int delete_user(struct userconf *cnf, struct passwd *pwd, + char *name, int delete, int mode); +static int print_user(struct passwd * pwd); +static uid_t pw_uidpolicy(struct userconf * cnf, long id); +static uid_t pw_gidpolicy(struct cargs * args, char *nam, gid_t prefer); static time_t pw_pwdpolicy(struct userconf * cnf, struct cargs * args); static time_t pw_exppolicy(struct userconf * cnf, struct cargs * args); static char *pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user); @@ -63,6 +65,27 @@ static char *shell_path(char const * path, char *shells[], char *sh); static void rmat(uid_t uid); static void rmopie(char const * name); +static void +create_and_populate_homedir(int mode, struct passwd *pwd) +{ + char *homedir, *dotdir; + struct userconf *cnf = conf.userconf; + + homedir = dotdir = NULL; + + if (conf.rootdir[0] != '\0') { + asprintf(&homedir, "%s/%s", conf.rootdir, pwd->pw_dir); + if (homedir == NULL) + errx(EX_OSERR, "out of memory"); + asprintf(&dotdir, "%s/%s", conf.rootdir, cnf->dotdir); + } + + copymkdir(homedir ? homedir : pwd->pw_dir, dotdir ? dotdir: cnf->dotdir, + cnf->homemode, pwd->pw_uid, pwd->pw_gid); + pw_log(cnf, mode, W_USER, "%s(%u) home %s made", pwd->pw_name, + pwd->pw_uid, pwd->pw_dir); +} + /*- * -C config configuration file * -q quiet operation @@ -96,18 +119,18 @@ static void rmopie(char const * name); */ int -pw_user(struct userconf * cnf, int mode, struct cargs * args) +pw_user(int mode, char *name, long id, struct cargs * args) { int rc, edited = 0; char *p = NULL; char *passtmp; - struct carg *a_name; - struct carg *a_uid; struct carg *arg; struct passwd *pwd = NULL; struct group *grp; struct stat st; + struct userconf *cnf; char line[_PASSWORD_LEN+1]; + char path[MAXPATHLEN]; FILE *fp; char *dmode_c; void *set = NULL; @@ -129,6 +152,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) #endif }; + cnf = conf.userconf; /* * With M_NEXT, we only need to return the @@ -136,11 +160,11 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) */ if (mode == M_NEXT) { - uid_t next = pw_uidpolicy(cnf, args); + uid_t next = pw_uidpolicy(cnf, id); if (getarg(args, 'q')) return next; - printf("%ld:", (long)next); - pw_group(cnf, mode, args); + printf("%u:", next); + pw_group(mode, name, -1, args); return EXIT_SUCCESS; } @@ -240,7 +264,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) } } if ((arg = getarg(args, 'L')) != NULL) - cnf->default_class = pw_checkname((u_char *)arg->val, 0); + cnf->default_class = pw_checkname(arg->val, 0); if ((arg = getarg(args, 'G')) != NULL && arg->val) { int i = 0; @@ -268,7 +292,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) if ((arg = getarg(args, 'w')) != NULL) cnf->default_password = boolean_val(arg->val, cnf->default_password); if (mode == M_ADD && getarg(args, 'D')) { - if (getarg(args, 'n') != NULL) + if (name != NULL) errx(EX_DATAERR, "can't combine `-D' with `-n name'"); if ((arg = getarg(args, 'u')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) { if ((cnf->min_uid = (uid_t) atoi(p)) == 0) @@ -283,46 +307,24 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) cnf->max_gid = 32000; } - arg = getarg(args, 'C'); - if (write_userconfig(arg ? arg->val : NULL)) - return EXIT_SUCCESS; - warn("config update"); - return EX_IOERR; + if (write_userconfig(conf.config)) + return (EXIT_SUCCESS); + err(EX_IOERR, "config udpate"); } 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); + print_user(pwd); ENDPWENT(); return EXIT_SUCCESS; } - if ((a_name = getarg(args, 'n')) != NULL) - pwd = GETPWNAM(pw_checkname((u_char *)a_name->val, 0)); - a_uid = getarg(args, 'u'); + if (name != NULL) + pwd = GETPWNAM(pw_checkname(name, 0)); - if (a_uid == NULL) { - if (a_name == NULL) - errx(EX_DATAERR, "user name or id required"); - - /* - * Determine whether 'n' switch is name or uid - we don't - * really don't really care which we have, but we need to - * know. - */ - if (mode != M_ADD && pwd == NULL - && strspn(a_name->val, "0123456789") == strlen(a_name->val) - && *a_name->val) { - (a_uid = a_name)->ch = 'u'; - a_name = NULL; - } - } else { - if (strspn(a_uid->val, "0123456789") != strlen(a_uid->val)) - errx(EX_USAGE, "-u expects a number"); - } + if (id < 0 && name == NULL) + errx(EX_DATAERR, "user name or id required"); /* * Update, delete & print require that the user exists @@ -330,24 +332,22 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT || mode == M_LOCK || mode == M_UNLOCK) { - if (a_name == NULL && pwd == NULL) /* Try harder */ - pwd = GETPWUID(atoi(a_uid->val)); + if (name == NULL && pwd == NULL) /* Try harder */ + pwd = GETPWUID(id); if (pwd == NULL) { if (mode == M_PRINT && getarg(args, 'F')) { - fakeuser.pw_name = a_name ? a_name->val : "nouser"; - fakeuser.pw_uid = a_uid ? (uid_t) atol(a_uid->val) : -1; - return print_user(&fakeuser, - getarg(args, 'P') != NULL, - getarg(args, '7') != NULL); + fakeuser.pw_name = name ? name : "nouser"; + fakeuser.pw_uid = (uid_t) id; + return print_user(&fakeuser); } - if (a_name == NULL) - errx(EX_NOUSER, "no such uid `%s'", a_uid->val); - errx(EX_NOUSER, "no such user `%s'", a_name->val); + if (name == NULL) + errx(EX_NOUSER, "no such uid `%ld'", id); + errx(EX_NOUSER, "no such user `%s'", name); } - if (a_name == NULL) /* May be needed later */ - a_name = addarg(args, 'n', newstr(pwd->pw_name)); + if (name == NULL) + name = pwd->pw_name; /* * The M_LOCK and M_UNLOCK functions simply add or remove @@ -372,130 +372,24 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name); pwd->pw_passwd += sizeof(locked_str)-1; edited = 1; - } else if (mode == M_DELETE) { - /* - * Handle deletions now - */ - char file[MAXPATHLEN]; - char home[MAXPATHLEN]; - uid_t uid = pwd->pw_uid; - struct group *gr; - char grname[LOGNAMESIZE]; - - if (strcmp(pwd->pw_name, "root") == 0) - errx(EX_DATAERR, "cannot remove user 'root'"); - - if (!PWALTDIR()) { - /* - * Remove opie record from /etc/opiekeys - */ - - rmopie(pwd->pw_name); - - /* - * Remove crontabs - */ - snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name); - if (access(file, F_OK) == 0) { - snprintf(file, sizeof(file), "crontab -u %s -r", pwd->pw_name); - system(file); - } - } - /* - * Save these for later, since contents of pwd may be - * invalidated by deletion - */ - snprintf(file, sizeof(file), "%s/%s", _PATH_MAILDIR, pwd->pw_name); - strlcpy(home, pwd->pw_dir, sizeof(home)); - gr = GETGRGID(pwd->pw_gid); - if (gr != NULL) - strlcpy(grname, gr->gr_name, LOGNAMESIZE); - else - grname[0] = '\0'; - - rc = delpwent(pwd); - if (rc == -1) - err(EX_IOERR, "user '%s' does not exist", pwd->pw_name); - else if (rc != 0) { - warn("passwd update"); - return EX_IOERR; - } - - if (cnf->nispasswd && *cnf->nispasswd=='/') { - rc = delnispwent(cnf->nispasswd, a_name->val); - if (rc == -1) - warnx("WARNING: user '%s' does not exist in NIS passwd", pwd->pw_name); - else if (rc != 0) - warn("WARNING: NIS passwd update"); - /* non-fatal */ - } - - grp = GETGRNAM(a_name->val); - if (grp != NULL && - (grp->gr_mem == NULL || *grp->gr_mem == NULL) && - strcmp(a_name->val, grname) == 0) - delgrent(GETGRNAM(a_name->val)); - SETGRENT(); - while ((grp = GETGRENT()) != NULL) { - int i, j; - char group[MAXLOGNAME]; - if (grp->gr_mem != NULL) { - for (i = 0; grp->gr_mem[i] != NULL; i++) { - if (!strcmp(grp->gr_mem[i], a_name->val)) { - for (j = i; grp->gr_mem[j] != NULL; j++) - grp->gr_mem[j] = grp->gr_mem[j+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); - - if (!PWALTDIR()) { - /* - * Remove mail file - */ - remove(file); - - /* - * Remove at jobs - */ - if (getpwuid(uid) == NULL) - rmat(uid); - - /* - * Remove home directory and contents - */ - if (getarg(args, 'r') != NULL && *home == '/' && getpwuid(uid) == NULL) { - if (stat(home, &st) != -1) { - rm_r(home, uid); - pw_log(cnf, mode, W_USER, "%s(%ld) home '%s' %sremoved", - a_name->val, (long) uid, home, - stat(home, &st) == -1 ? "" : "not completely "); - } - } - } - return EXIT_SUCCESS; - } else if (mode == M_PRINT) - return print_user(pwd, - getarg(args, 'P') != NULL, - getarg(args, '7') != NULL); + } else if (mode == M_DELETE) + return (delete_user(cnf, pwd, name, + getarg(args, 'r') != NULL, mode)); + else if (mode == M_PRINT) + return print_user(pwd); /* * The rest is edit code */ - if ((arg = getarg(args, 'l')) != NULL) { + if (conf.newname != NULL) { if (strcmp(pwd->pw_name, "root") == 0) errx(EX_DATAERR, "can't rename `root' account"); - pwd->pw_name = pw_checkname((u_char *)arg->val, 0); + pwd->pw_name = pw_checkname(conf.newname, 0); edited = 1; } - if ((arg = getarg(args, 'u')) != NULL && isdigit((unsigned char)*arg->val)) { - pwd->pw_uid = (uid_t) atol(arg->val); + if (id > 0 && isdigit((unsigned char)*arg->val)) { + pwd->pw_uid = (uid_t)id; edited = 1; if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0) errx(EX_DATAERR, "can't change uid of `root' account"); @@ -596,19 +490,19 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) * Add code */ - if (a_name == NULL) /* Required */ + if (name == NULL) /* Required */ errx(EX_DATAERR, "login name required"); - else if ((pwd = GETPWNAM(a_name->val)) != NULL) /* Exists */ - errx(EX_DATAERR, "login name `%s' already exists", a_name->val); + else if ((pwd = GETPWNAM(name)) != NULL) /* Exists */ + errx(EX_DATAERR, "login name `%s' already exists", name); /* * Now, set up defaults for a new user */ pwd = &fakeuser; - pwd->pw_name = a_name->val; + pwd->pw_name = name; pwd->pw_class = cnf->default_class ? cnf->default_class : ""; - pwd->pw_uid = pw_uidpolicy(cnf, args); - pwd->pw_gid = pw_gidpolicy(cnf, args, pwd->pw_name, (gid_t) pwd->pw_uid); + pwd->pw_uid = pw_uidpolicy(cnf, id); + pwd->pw_gid = pw_gidpolicy(args, pwd->pw_name, (gid_t) pwd->pw_uid); pwd->pw_change = pw_pwdpolicy(cnf, args); pwd->pw_expire = pw_exppolicy(cnf, args); pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name); @@ -628,7 +522,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) * Shared add/edit code */ if ((arg = getarg(args, 'c')) != NULL) { - char *gecos = pw_checkname((u_char *)arg->val, 1); + char *gecos = pw_checkname(arg->val, 1); if (strcmp(pwd->pw_gecos, gecos) != 0) { pwd->pw_gecos = gecos; edited = 1; @@ -672,11 +566,9 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) fputc('\n', stdout); fflush(stdout); } - if (b < 0) { - warn("-%c file descriptor", precrypt ? 'H' : - 'h'); - return EX_IOERR; - } + if (b < 0) + err(EX_IOERR, "-%c file descriptor", + precrypt ? 'H' : 'h'); line[b] = '\0'; if ((p = strpbrk(line, "\r\n")) != NULL) *p = '\0'; @@ -701,21 +593,17 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) /* * Special case: -N only displays & exits */ - if (getarg(args, 'N') != NULL) - return print_user(pwd, - getarg(args, 'P') != NULL, - getarg(args, '7') != NULL); + if (conf.dryrun) + return print_user(pwd); if (mode == M_ADD) { edited = 1; /* Always */ rc = addpwent(pwd); - if (rc == -1) { - warnx("user '%s' already exists", pwd->pw_name); - return EX_IOERR; - } else if (rc != 0) { - warn("passwd file update"); - return EX_IOERR; - } + if (rc == -1) + errx(EX_IOERR, "user '%s' already exists", + pwd->pw_name); + else if (rc != 0) + err(EX_IOERR, "passwd file update"); if (cnf->nispasswd && *cnf->nispasswd=='/') { rc = addnispwent(cnf->nispasswd, pwd); if (rc == -1) @@ -726,16 +614,13 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) } } else if (mode == M_UPDATE || mode == M_LOCK || mode == M_UNLOCK) { if (edited) { /* Only updated this if required */ - rc = chgpwent(a_name->val, pwd); - if (rc == -1) { - warnx("user '%s' does not exist (NIS?)", pwd->pw_name); - return EX_IOERR; - } else if (rc != 0) { - warn("passwd file update"); - return EX_IOERR; - } + rc = chgpwent(name, pwd); + if (rc == -1) + errx(EX_IOERR, "user '%s' does not exist (NIS?)", pwd->pw_name); + else if (rc != 0) + err(EX_IOERR, "passwd file update"); if ( cnf->nispasswd && *cnf->nispasswd=='/') { - rc = chgnispwent(cnf->nispasswd, a_name->val, pwd); + rc = chgnispwent(cnf->nispasswd, name, pwd); if (rc == -1) warn("User '%s' not found in NIS passwd", pwd->pw_name); else @@ -787,21 +672,21 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) /* go get a current version of pwd */ - pwd = GETPWNAM(a_name->val); + pwd = GETPWNAM(name); if (pwd == NULL) { /* This will fail when we rename, so special case that */ - if (mode == M_UPDATE && (arg = getarg(args, 'l')) != NULL) { - a_name->val = arg->val; /* update new name */ - pwd = GETPWNAM(a_name->val); /* refetch renamed rec */ + if (mode == M_UPDATE && conf.newname != NULL) { + name = conf.newname; /* update new name */ + pwd = GETPWNAM(name); /* refetch renamed rec */ } } if (pwd == NULL) /* can't go on without this */ - errx(EX_NOUSER, "user '%s' disappeared during update", a_name->val); + errx(EX_NOUSER, "user '%s' disappeared during update", name); grp = GETGRGID(pwd->pw_gid); - pw_log(cnf, mode, W_USER, "%s(%ld):%s(%ld):%s:%s:%s", - pwd->pw_name, (long) pwd->pw_uid, - grp ? grp->gr_name : "unknown", (long) (grp ? grp->gr_gid : -1), + pw_log(cnf, mode, W_USER, "%s(%u):%s(%u):%s:%s:%s", + pwd->pw_name, pwd->pw_uid, + grp ? grp->gr_name : "unknown", (grp ? grp->gr_gid : (uid_t)-1), pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell); /* @@ -810,11 +695,13 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) * doesn't hurt anything to create the empty mailfile */ if (mode == M_ADD) { - if (!PWALTDIR()) { - snprintf(line, sizeof(line), "%s/%s", _PATH_MAILDIR, pwd->pw_name); - close(open(line, O_RDWR | O_CREAT, 0600)); /* Preserve contents & + if (PWALTDIR() != PWF_ALT) { + arg = getarg(args, 'R'); + snprintf(path, sizeof(path), "%s%s/%s", + arg ? arg->val : "", _PATH_MAILDIR, pwd->pw_name); + close(open(path, O_RDWR | O_CREAT, 0600)); /* Preserve contents & * mtime */ - chown(line, pwd->pw_uid, pwd->pw_gid); + chown(path, pwd->pw_uid, pwd->pw_gid); } } @@ -823,12 +710,9 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) * that this also `works' for editing users if -m is used, but * existing files will *not* be overwritten. */ - if (!PWALTDIR() && getarg(args, 'm') != NULL && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) { - copymkdir(pwd->pw_dir, cnf->dotdir, cnf->homemode, pwd->pw_uid, pwd->pw_gid); - pw_log(cnf, mode, W_USER, "%s(%ld) home %s made", - pwd->pw_name, (long) pwd->pw_uid, pwd->pw_dir); - } - + if (PWALTDIR() != PWF_ALT && getarg(args, 'm') != NULL && pwd->pw_dir && + *pwd->pw_dir == '/' && pwd->pw_dir[1]) + create_and_populate_homedir(mode, pwd); /* * Finally, send mail to the new user as well, if we are asked to @@ -845,8 +729,8 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) fputs(line, pfp); } pclose(pfp); - pw_log(cnf, mode, W_USER, "%s(%ld) new user mail sent", - pwd->pw_name, (long) pwd->pw_uid); + pw_log(cnf, mode, W_USER, "%s(%u) new user mail sent", + pwd->pw_name, pwd->pw_uid); } fclose(fp); } @@ -856,20 +740,19 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) static uid_t -pw_uidpolicy(struct userconf * cnf, struct cargs * args) +pw_uidpolicy(struct userconf * cnf, long id) { struct passwd *pwd; uid_t uid = (uid_t) - 1; - struct carg *a_uid = getarg(args, 'u'); /* * Check the given uid, if any */ - if (a_uid != NULL) { - uid = (uid_t) atol(a_uid->val); + if (id > 0) { + uid = (uid_t) id; - if ((pwd = GETPWUID(uid)) != NULL && getarg(args, 'o') == NULL) - errx(EX_DATAERR, "uid `%ld' has already been allocated", (long) pwd->pw_uid); + if ((pwd = GETPWUID(uid)) != NULL && conf.checkduplicate) + errx(EX_DATAERR, "uid `%u' has already been allocated", pwd->pw_uid); } else { struct bitmap bm; @@ -912,11 +795,12 @@ pw_uidpolicy(struct userconf * cnf, struct cargs * args) static uid_t -pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer) +pw_gidpolicy(struct cargs * args, char *nam, gid_t prefer) { struct group *grp; gid_t gid = (uid_t) - 1; struct carg *a_gid = getarg(args, 'g'); + struct userconf *cnf = conf.userconf; /* * If no arg given, see if default can help out @@ -943,7 +827,6 @@ pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer char tmp[32]; LIST_INIT(&grpargs); - addarg(&grpargs, 'n', nam); /* * We need to auto-create a group with the user's name. We @@ -958,15 +841,13 @@ pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer snprintf(tmp, sizeof(tmp), "%u", prefer); addarg(&grpargs, 'g', tmp); } - if (getarg(args, 'N')) - { - addarg(&grpargs, 'N', NULL); + if (conf.dryrun) { addarg(&grpargs, 'q', NULL); - gid = pw_group(cnf, M_NEXT, &grpargs); + gid = pw_group(M_NEXT, nam, -1, &grpargs); } else { - pw_group(cnf, M_ADD, &grpargs); + pw_group(M_ADD, nam, -1, &grpargs); if ((grp = GETGRNAM(nam)) != NULL) gid = grp->gr_gid; } @@ -1121,7 +1002,7 @@ pw_password(struct userconf * cnf, struct cargs * args, char const * user) * We give this information back to the user */ if (getarg(args, 'h') == NULL && getarg(args, 'H') == NULL && - getarg(args, 'N') == NULL) { + !conf.dryrun) { if (isatty(STDOUT_FILENO)) printf("Password for '%s' is: ", user); printf("%s\n", pwbuf); @@ -1143,17 +1024,124 @@ pw_password(struct userconf * cnf, struct cargs * args, char const * user) return pw_pwcrypt(pwbuf); } +static int +delete_user(struct userconf *cnf, struct passwd *pwd, char *name, + int delete, int mode) +{ + char file[MAXPATHLEN]; + char home[MAXPATHLEN]; + uid_t uid = pwd->pw_uid; + struct group *gr, *grp; + char grname[LOGNAMESIZE]; + int rc; + struct stat st; + + if (strcmp(pwd->pw_name, "root") == 0) + errx(EX_DATAERR, "cannot remove user 'root'"); + + if (!PWALTDIR()) { + /* + * Remove opie record from /etc/opiekeys + */ + + rmopie(pwd->pw_name); + + /* + * Remove crontabs + */ + snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name); + if (access(file, F_OK) == 0) { + snprintf(file, sizeof(file), "crontab -u %s -r", pwd->pw_name); + system(file); + } + } + /* + * Save these for later, since contents of pwd may be + * invalidated by deletion + */ + snprintf(file, sizeof(file), "%s/%s", _PATH_MAILDIR, pwd->pw_name); + strlcpy(home, pwd->pw_dir, sizeof(home)); + gr = GETGRGID(pwd->pw_gid); + if (gr != NULL) + strlcpy(grname, gr->gr_name, LOGNAMESIZE); + else + grname[0] = '\0'; + + rc = delpwent(pwd); + if (rc == -1) + err(EX_IOERR, "user '%s' does not exist", pwd->pw_name); + else if (rc != 0) + err(EX_IOERR, "passwd update"); + + if (cnf->nispasswd && *cnf->nispasswd=='/') { + rc = delnispwent(cnf->nispasswd, name); + if (rc == -1) + warnx("WARNING: user '%s' does not exist in NIS passwd", pwd->pw_name); + else if (rc != 0) + warn("WARNING: NIS passwd update"); + /* non-fatal */ + } + + grp = GETGRNAM(name); + if (grp != NULL && + (grp->gr_mem == NULL || *grp->gr_mem == NULL) && + strcmp(name, grname) == 0) + delgrent(GETGRNAM(name)); + SETGRENT(); + while ((grp = GETGRENT()) != NULL) { + int i, j; + char group[MAXLOGNAME]; + if (grp->gr_mem == NULL) + continue; + + for (i = 0; grp->gr_mem[i] != NULL; i++) { + if (strcmp(grp->gr_mem[i], name) != 0) + continue; + + for (j = i; grp->gr_mem[j] != NULL; j++) + grp->gr_mem[j] = grp->gr_mem[j+1]; + strlcpy(group, grp->gr_name, MAXLOGNAME); + chggrent(group, grp); + } + } + ENDGRENT(); + + pw_log(cnf, mode, W_USER, "%s(%u) account removed", name, uid); + + if (!PWALTDIR()) { + /* + * Remove mail file + */ + remove(file); + + /* + * Remove at jobs + */ + if (getpwuid(uid) == NULL) + rmat(uid); + + /* + * Remove home directory and contents + */ + if (delete && *home == '/' && getpwuid(uid) == NULL && + stat(home, &st) != -1) { + rm_r(home, uid); + pw_log(cnf, mode, W_USER, "%s(%u) home '%s' %sremoved", + name, uid, home, + stat(home, &st) == -1 ? "" : "not completely "); + } + } + + return (EXIT_SUCCESS); +} static int -print_user(struct passwd * pwd, int pretty, int v7) +print_user(struct passwd * pwd) { - if (!pretty) { + if (!conf.pretty) { char *buf; - if (!v7) - pwd->pw_passwd = (pwd->pw_passwd == NULL) ? "" : "*"; - - buf = v7 ? pw_make_v7(pwd) : pw_make(pwd); + buf = conf.v7 ? pw_make_v7(pwd) : pw_make(pwd); printf("%s\n", buf); free(buf); } else { @@ -1193,14 +1181,14 @@ print_user(struct passwd * pwd, int pretty, int v7) strftime(acexpire, sizeof acexpire, "%c", tptr); if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL) strftime(pwexpire, sizeof pwexpire, "%c", tptr); - printf("Login Name: %-15s #%-12ld Group: %-15s #%ld\n" + printf("Login Name: %-15s #%-12u Group: %-15s #%u\n" " Full Name: %s\n" " Home: %-26.26s Class: %s\n" " Shell: %-26.26s Office: %s\n" "Work Phone: %-26.26s Home Phone: %s\n" "Acc Expire: %-26.26s Pwd Expire: %s\n", - pwd->pw_name, (long) pwd->pw_uid, - grp ? grp->gr_name : "(invalid)", (long) pwd->pw_gid, + pwd->pw_name, pwd->pw_uid, + grp ? grp->gr_name : "(invalid)", pwd->pw_gid, uname, pwd->pw_dir, pwd->pw_class, pwd->pw_shell, office, wphone, hphone, acexpire, pwexpire); @@ -1227,11 +1215,11 @@ print_user(struct passwd * pwd, int pretty, int v7) return EXIT_SUCCESS; } -char * -pw_checkname(u_char *name, int gecos) +char * +pw_checkname(char *name, int gecos) { char showch[8]; - u_char const *badchars, *ch, *showtype; + const char *badchars, *ch, *showtype; int reject; ch = name; @@ -1282,7 +1270,8 @@ pw_checkname(u_char *name, int gecos) if (!gecos && (ch - name) > LOGNAMESIZE) errx(EX_DATAERR, "name too long `%s' (max is %d)", name, LOGNAMESIZE); - return (char *)name; + + return (name); } @@ -44,28 +44,12 @@ static const char rcsid[] = #include "pwupd.h" -static char pathpwd[] = _PATH_PWD; -static char * pwpath = pathpwd; - -int -setpwdir(const char * dir) -{ - if (dir == NULL) - return (-1); - else - pwpath = strdup(dir); - if (pwpath == NULL) - return (-1); - - return (0); -} - char * getpwpath(char const * file) { static char pathbuf[MAXPATHLEN]; - snprintf(pathbuf, sizeof pathbuf, "%s/%s", pwpath, file); + snprintf(pathbuf, sizeof pathbuf, "%s/%s", conf.etcpath, file); return (pathbuf); } @@ -80,9 +64,9 @@ pwdb_check(void) args[i++] = _PATH_PWD_MKDB; args[i++] = "-C"; - if (pwpath != pathpwd) { + if (strcmp(conf.etcpath, _PATH_PWD) != 0) { args[i++] = "-d"; - args[i++] = pwpath; + args[i++] = conf.etcpath; } args[i++] = getpwpath(_MASTERPASSWD); args[i] = NULL; @@ -117,7 +101,7 @@ pw_update(struct passwd * pwd, char const * user) if (user != NULL) old_pw = GETPWNAM(user); - if (pw_init(pwpath, NULL)) + if (pw_init(conf.etcpath, NULL)) err(1, "pw_init()"); if ((pfd = pw_lock()) == -1) { pw_fini(); @@ -162,9 +146,6 @@ chgpwent(char const * login, struct passwd * pwd) int delpwent(struct passwd * pwd) { - char login[MAXLOGNAME]; - - strlcpy(login, pwd->pw_name, MAXLOGNAME); - return (pw_update(NULL, login)); + return (pw_update(NULL, pwd->pw_name)); } @@ -29,11 +29,13 @@ #ifndef _PWUPD_H_ #define _PWUPD_H_ +#include <sys/cdefs.h> +#include <sys/param.h> #include <sys/types.h> + #include <pwd.h> #include <grp.h> - -#include <sys/cdefs.h> +#include <stdbool.h> #if defined(__FreeBSD__) #define RET_SETGRENT int @@ -41,8 +43,7 @@ #define RET_SETGRENT void #endif -struct pwf -{ +struct pwf { int _altdir; void (*_setpwent)(void); void (*_endpwent)(void); @@ -56,8 +57,44 @@ struct pwf struct group * (*_getgrnam)(const char * nam); }; +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 */ +}; + +struct pwconf { + char rootdir[MAXPATHLEN]; + char etcpath[MAXPATHLEN]; + char *newname; + char *config; + bool dryrun; + bool pretty; + bool v7; + bool checkduplicate; + struct userconf *userconf; +}; + extern struct pwf PWF; extern struct pwf VPWF; +extern struct pwconf conf; #define SETPWENT() PWF._setpwent() #define ENDPWENT() PWF._endpwent() @@ -71,6 +108,10 @@ extern struct pwf VPWF; #define GETGRGID(gid) PWF._getgrgid(gid) #define GETGRNAM(nam) PWF._getgrnam(nam) +#define PWF_REGULAR 0 +#define PWF_ALT 1 +#define PWF_ROOTDIR 2 + #define PWALTDIR() PWF._altdir #ifndef _PATH_PWD #define _PATH_PWD "/etc" @@ -87,15 +128,12 @@ 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); |