summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authorbapt <bapt@FreeBSD.org>2015-07-03 14:22:44 +0000
committerbapt <bapt@FreeBSD.org>2015-07-03 14:22:44 +0000
commit9420a4ea0e57a8af4cb3a021b5529153fcb64251 (patch)
treed53116e1f843494918313336ade226a34d28f4f4 /usr.sbin
parentaf3b3e839600760eccc5a9e28b4d86d1ee729b03 (diff)
downloadFreeBSD-src-9420a4ea0e57a8af4cb3a021b5529153fcb64251.zip
FreeBSD-src-9420a4ea0e57a8af4cb3a021b5529153fcb64251.tar.gz
MFC: r274011,r274022,r274453,r274542,r274632,r274727,r275653,r275656,r275657,
r275658,r275829,r277652,r277764,r278475,r278767,r278819,r278902,r279256, r282681,r282683,r282685,r282686,r282687,r282697,r282698,r282699,r282700, r282709,r282712,r282713,r282716,r282718,r282719,r282720,r282721,r283809, r283810,r283811,r283814,r283815,r283816,r283818,r283841,r283842,r283843, r283961,r283962,r284110,r284111,r284112,r284113,r284114,r284117,r284118, r284119,r284120,r284121,r284122,r284123,r284124,r284126,r284128,r284129, r284130,r284133,r284135,r284137,r284139,r284140,r284148,r284149,r284392 Lots of cleanup in the pw(8) code Add pw -R <rootdir> Add lots of regression tests More accurate error messages Approved by: re (kib) Sponsored by: gandi.net
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/pw/Makefile6
-rw-r--r--usr.sbin/pw/fileupd.c21
-rw-r--r--usr.sbin/pw/grupd.c31
-rw-r--r--usr.sbin/pw/pw.823
-rw-r--r--usr.sbin/pw/pw.c187
-rw-r--r--usr.sbin/pw/pw.h30
-rw-r--r--usr.sbin/pw/pw_conf.c493
-rw-r--r--usr.sbin/pw/pw_group.c128
-rw-r--r--usr.sbin/pw/pw_nis.c3
-rw-r--r--usr.sbin/pw/pw_user.c514
-rw-r--r--usr.sbin/pw/pwupd.c126
-rw-r--r--usr.sbin/pw/pwupd.h54
-rw-r--r--usr.sbin/pw/tests/Makefile28
-rwxr-xr-xusr.sbin/pw/tests/helper_functions.shin3
-rw-r--r--usr.sbin/pw/tests/pw-modified.conf62
-rw-r--r--usr.sbin/pw/tests/pw.conf62
-rwxr-xr-xusr.sbin/pw/tests/pw_config.sh26
-rwxr-xr-xusr.sbin/pw/tests/pw_delete.sh47
-rwxr-xr-xusr.sbin/pw/tests/pw_etcdir.sh18
-rwxr-xr-xusr.sbin/pw/tests/pw_groupdel.sh24
-rwxr-xr-xusr.sbin/pw/tests/pw_groupmod.sh91
-rwxr-xr-xusr.sbin/pw/tests/pw_lock.sh22
-rwxr-xr-xusr.sbin/pw/tests/pw_modify.sh80
-rwxr-xr-xusr.sbin/pw/tests/pw_useradd.sh196
-rwxr-xr-xusr.sbin/pw/tests/pw_userdel.sh37
-rwxr-xr-xusr.sbin/pw/tests/pw_usermod.sh133
-rwxr-xr-xusr.sbin/pw/tests/pw_usernext.sh45
27 files changed, 1571 insertions, 919 deletions
diff --git a/usr.sbin/pw/Makefile b/usr.sbin/pw/Makefile
index 487ebb1..70b05ac 100644
--- a/usr.sbin/pw/Makefile
+++ b/usr.sbin/pw/Makefile
@@ -8,10 +8,10 @@ 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
-DPADD= ${LIBCRYPT} ${LIBUTIL}
-LDADD= -lcrypt -lutil
+DPADD= ${LIBCRYPT} ${LIBUTIL} ${LIBSBUF}
+LDADD= -lcrypt -lutil -lsbuf
.if ${MK_TESTS} != "no"
SUBDIR+= tests
diff --git a/usr.sbin/pw/fileupd.c b/usr.sbin/pw/fileupd.c
index 7df4bb1..dc32712 100644
--- a/usr.sbin/pw/fileupd.c
+++ b/usr.sbin/pw/fileupd.c
@@ -29,32 +29,11 @@ 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) {
diff --git a/usr.sbin/pw/grupd.c b/usr.sbin/pw/grupd.c
index 3f78e95..d52a345 100644
--- a/usr.sbin/pw/grupd.c
+++ b/usr.sbin/pw/grupd.c
@@ -35,36 +35,18 @@ static const char rcsid[] =
#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
- 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
@@ -80,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) {
@@ -120,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));
}
diff --git a/usr.sbin/pw/pw.8 b/usr.sbin/pw/pw.8
index 076f2eb..c29a8a9 100644
--- a/usr.sbin/pw/pw.8
+++ b/usr.sbin/pw/pw.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd October 29, 2012
+.Dd June 14, 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.
@@ -259,7 +278,7 @@ flag may be used to override this behaviour.
As an exception to the general rule where options must follow the operation
type, the
.Fl V
-flag may be used on the command line before the operation keyword.
+flag must be used on the command line before the operation keyword.
.It Fl C Ar config
By default,
.Nm
diff --git a/usr.sbin/pw/pw.c b/usr.sbin/pw/pw.c
index b0ac728..30fb55b 100644
--- a/usr.sbin/pw/pw.c
+++ b/usr.sbin/pw/pw.c
@@ -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,35 +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, "");
@@ -140,14 +153,33 @@ 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)
+ errx(EX_OSFILE, \
+ "no such directory `%s'",
+ argv[2]);
+ if (!S_ISDIR(st.st_mode))
+ errx(EX_OSFILE, "`%s' not a "
+ "directory", argv[2]);
optarg = argv[2];
++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;
}
@@ -162,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;
@@ -183,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");
/*
@@ -207,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);
@@ -251,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;
@@ -294,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"
@@ -316,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"
@@ -332,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"
@@ -361,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"
@@ -369,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"
@@ -383,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"
@@ -393,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"
@@ -411,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"
@@ -418,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"
}
diff --git a/usr.sbin/pw/pw.h b/usr.sbin/pw/pw.h
index a1ed0c4..6239004 100644
--- a/usr.sbin/pw/pw.h
+++ b/usr.sbin/pw/pw.h
@@ -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/usr.sbin/pw/pw_conf.c b/usr.sbin/pw/pw_conf.c
index 1289b3e..24c0650 100644
--- a/usr.sbin/pw/pw_conf.c
+++ b/usr.sbin/pw/pw_conf.c
@@ -29,9 +29,12 @@ static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
+#include <sys/types.h>
+#include <sys/sbuf.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
+#include <err.h>
#include "pw.h"
@@ -209,19 +212,16 @@ boolean_str(int val)
char *
newstr(char const * p)
{
- char *q = NULL;
+ char *q;
- if ((p = unquote(p)) != NULL) {
- int l = strlen(p) + 1;
+ if ((p = unquote(p)) == NULL)
+ return (NULL);
- if ((q = malloc(l)) != NULL)
- memcpy(q, p, l);
- }
- return q;
-}
-
-#define LNBUFSZ 1024
+ if ((q = strdup(p)) == NULL)
+ err(1, "strdup()");
+ return (q);
+}
struct userconf *
read_userconfig(char const * file)
@@ -234,131 +234,134 @@ read_userconfig(char const * file)
buf = NULL;
linecap = 0;
- extendarray(&config.groups, &config.numgroups, 200);
- memset(config.groups, 0, config.numgroups * sizeof(char *));
+ config.numgroups = 200;
+ config.groups = calloc(config.numgroups, sizeof(char *));
+ if (config.groups == NULL)
+ err(1, "calloc()");
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;
+ if ((fp = fopen(file, "r")) == NULL)
+ return (&config);
- while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
- ++i;
+ 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);
}
@@ -366,138 +369,132 @@ int
write_userconfig(char const * file)
{
int fd;
+ int i, j;
+ struct sbuf *buf;
+ FILE *fp;
if (file == NULL)
file = _PATH_PW_CONF;
- if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
- FILE *fp;
-
- if ((fp = fdopen(fd, "w")) == NULL)
- close(fd);
- else {
- int i, j, k;
- int len = LNBUFSZ;
- char *buf = malloc(len);
-
- for (i = _UC_NONE; i < _UC_FIELDS; i++) {
- int quote = 1;
- char const *val = buf;
-
- *buf = '\0';
- switch (i) {
- case _UC_DEFAULTPWD:
- val = boolean_str(config.default_password);
- break;
- case _UC_REUSEUID:
- val = boolean_str(config.reuse_uids);
- break;
- case _UC_REUSEGID:
- val = boolean_str(config.reuse_gids);
- break;
- case _UC_NISPASSWD:
- val = config.nispasswd ? config.nispasswd : "";
- quote = 0;
- break;
- case _UC_DOTDIR:
- val = config.dotdir ? config.dotdir : boolean_str(0);
- break;
- case _UC_NEWMAIL:
- val = config.newmail ? config.newmail : boolean_str(0);
- break;
- case _UC_LOGFILE:
- val = config.logfile ? config.logfile : boolean_str(0);
- break;
- case _UC_HOMEROOT:
- val = config.home;
- break;
- case _UC_HOMEMODE:
- sprintf(buf, "%04o", config.homemode);
- quote = 0;
- break;
- case _UC_SHELLPATH:
- val = config.shelldir;
- break;
- case _UC_SHELLS:
- for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
- char lbuf[64];
- int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
- if (l < 0)
- l = 0;
- if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
- strcpy(buf + k, lbuf);
- k += l;
- }
- }
- quote = 0;
- break;
- case _UC_DEFAULTSHELL:
- val = config.shell_default ? config.shell_default : bourne_shell;
- break;
- case _UC_DEFAULTGROUP:
- val = config.default_group ? config.default_group : "";
- break;
- case _UC_EXTRAGROUPS:
- extendarray(&config.groups, &config.numgroups, 200);
- for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
- char lbuf[64];
- int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
- if (l < 0)
- l = 0;
- if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
- strcpy(buf + k, lbuf);
- k += l;
- }
- }
- quote = 0;
- break;
- case _UC_DEFAULTCLASS:
- val = config.default_class ? config.default_class : "";
- break;
- case _UC_MINUID:
- sprintf(buf, "%lu", (unsigned long) config.min_uid);
- quote = 0;
- break;
- case _UC_MAXUID:
- sprintf(buf, "%lu", (unsigned long) config.max_uid);
- quote = 0;
- break;
- case _UC_MINGID:
- sprintf(buf, "%lu", (unsigned long) config.min_gid);
- quote = 0;
- break;
- case _UC_MAXGID:
- sprintf(buf, "%lu", (unsigned long) config.max_gid);
- quote = 0;
- break;
- case _UC_EXPIRE:
- sprintf(buf, "%d", config.expire_days);
- quote = 0;
- break;
- case _UC_PASSWORD:
- sprintf(buf, "%d", config.password_days);
- quote = 0;
- break;
- case _UC_NONE:
- break;
- }
+ if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
+ return (0);
+
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ close(fd);
+ return (0);
+ }
+
+ buf = sbuf_new_auto();
+ for (i = _UC_NONE; i < _UC_FIELDS; i++) {
+ int quote = 1;
+
+ sbuf_clear(buf);
+ switch (i) {
+ case _UC_DEFAULTPWD:
+ sbuf_cat(buf, boolean_str(config.default_password));
+ break;
+ case _UC_REUSEUID:
+ sbuf_cat(buf, boolean_str(config.reuse_uids));
+ break;
+ case _UC_REUSEGID:
+ sbuf_cat(buf, boolean_str(config.reuse_gids));
+ break;
+ case _UC_NISPASSWD:
+ sbuf_cat(buf, config.nispasswd ? config.nispasswd :
+ "");
+ quote = 0;
+ break;
+ case _UC_DOTDIR:
+ sbuf_cat(buf, config.dotdir ? config.dotdir :
+ boolean_str(0));
+ break;
+ case _UC_NEWMAIL:
+ sbuf_cat(buf, config.newmail ? config.newmail :
+ boolean_str(0));
+ break;
+ case _UC_LOGFILE:
+ sbuf_cat(buf, config.logfile ? config.logfile :
+ boolean_str(0));
+ break;
+ case _UC_HOMEROOT:
+ sbuf_cat(buf, config.home);
+ break;
+ case _UC_HOMEMODE:
+ sbuf_printf(buf, "%04o", config.homemode);
+ quote = 0;
+ break;
+ case _UC_SHELLPATH:
+ sbuf_cat(buf, config.shelldir);
+ break;
+ case _UC_SHELLS:
+ for (j = 0; j < _UC_MAXSHELLS &&
+ system_shells[j] != NULL; j++)
+ sbuf_printf(buf, "%s\"%s\"", j ?
+ "," : "", system_shells[j]);
+ quote = 0;
+ break;
+ case _UC_DEFAULTSHELL:
+ sbuf_cat(buf, config.shell_default ?
+ config.shell_default : bourne_shell);
+ break;
+ case _UC_DEFAULTGROUP:
+ sbuf_cat(buf, config.default_group ?
+ config.default_group : "");
+ break;
+ case _UC_EXTRAGROUPS:
+ for (j = 0; j < config.numgroups &&
+ config.groups[j] != NULL; j++)
+ sbuf_printf(buf, "%s\"%s\"", j ?
+ "," : "", config.groups[j]);
+ quote = 0;
+ break;
+ case _UC_DEFAULTCLASS:
+ sbuf_cat(buf, config.default_class ?
+ config.default_class : "");
+ break;
+ case _UC_MINUID:
+ sbuf_printf(buf, "%u", config.min_uid);
+ quote = 0;
+ break;
+ case _UC_MAXUID:
+ sbuf_printf(buf, "%u", config.max_uid);
+ quote = 0;
+ break;
+ case _UC_MINGID:
+ sbuf_printf(buf, "%u", config.min_gid);
+ quote = 0;
+ break;
+ case _UC_MAXGID:
+ sbuf_printf(buf, "%u", config.max_gid);
+ quote = 0;
+ break;
+ case _UC_EXPIRE:
+ sbuf_printf(buf, "%d", config.expire_days);
+ quote = 0;
+ break;
+ case _UC_PASSWORD:
+ sbuf_printf(buf, "%d", config.password_days);
+ quote = 0;
+ break;
+ case _UC_NONE:
+ break;
+ }
+ sbuf_finish(buf);
- if (comments[i])
- fputs(comments[i], fp);
+ if (comments[i])
+ fputs(comments[i], fp);
- if (*kwds[i]) {
- if (quote)
- fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
- else
- fprintf(fp, "%s = %s\n", kwds[i], val);
+ if (*kwds[i]) {
+ if (quote)
+ fprintf(fp, "%s = \"%s\"\n", kwds[i],
+ sbuf_data(buf));
+ else
+ fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
#if debugging
- printf("WROTE: %s = %s\n", kwds[i], val);
+ printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
#endif
- }
- }
- free(buf);
- return fclose(fp) != EOF;
}
}
- return 0;
+ sbuf_delete(buf);
+ return (fclose(fp) != EOF);
}
diff --git a/usr.sbin/pw/pw_group.c b/usr.sbin/pw/pw_group.c
index b20ce88..b9cce0d 100644
--- a/usr.sbin/pw/pw_group.c
+++ b/usr.sbin/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/usr.sbin/pw/pw_nis.c b/usr.sbin/pw/pw_nis.c
index 918fc30..c786cc7 100644
--- a/usr.sbin/pw/pw_nis.c
+++ b/usr.sbin/pw/pw_nis.c
@@ -29,9 +29,6 @@ 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>
diff --git a/usr.sbin/pw/pw_user.c b/usr.sbin/pw/pw_user.c
index 483148a..c3b2751 100644
--- a/usr.sbin/pw/pw_user.c
+++ b/usr.sbin/pw/pw_user.c
@@ -40,7 +40,6 @@ static const char rcsid[] =
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
-#include <unistd.h>
#include <login_cap.h>
#include <pwd.h>
#include <grp.h>
@@ -52,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);
@@ -64,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
@@ -97,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;
@@ -130,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
@@ -137,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;
}
@@ -185,8 +208,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
* But we create a symlink from cnf->home -> "/usr" -> cnf->home
*/
if (strchr(cnf->home+1, '/') == NULL) {
- strcpy(dbuf, "/usr");
- strncat(dbuf, cnf->home, MAXPATHLEN-5);
+ snprintf(dbuf, MAXPATHLEN, "/usr%s", cnf->home);
if (mkdir(dbuf, _DEF_DIRMODE) != -1 || errno == EEXIST) {
chown(dbuf, 0, 0);
/*
@@ -242,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;
@@ -270,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)
@@ -285,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
@@ -332,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
@@ -364,11 +362,9 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
if (mode == M_LOCK) {
if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) == 0)
errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name);
- passtmp = malloc(strlen(pwd->pw_passwd) + sizeof(locked_str));
+ asprintf(&passtmp, "%s%s", locked_str, pwd->pw_passwd);
if (passtmp == NULL) /* disaster */
errx(EX_UNAVAILABLE, "out of memory");
- strcpy(passtmp, locked_str);
- strcat(passtmp, pwd->pw_passwd);
pwd->pw_passwd = passtmp;
edited = 1;
} else if (mode == M_UNLOCK) {
@@ -376,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) {
- sprintf(file, "crontab -u %s -r", pwd->pw_name);
- system(file);
- }
- }
- /*
- * Save these for later, since contents of pwd may be
- * invalidated by deletion
- */
- sprintf(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");
@@ -600,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);
@@ -632,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;
@@ -676,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';
@@ -705,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)
@@ -730,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
@@ -791,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);
/*
@@ -814,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()) {
- sprintf(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);
}
}
@@ -827,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
@@ -849,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);
}
@@ -860,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;
@@ -916,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
@@ -947,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
@@ -959,18 +838,16 @@ pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer
* function will happily handle that case for us and exit.
*/
if (GETGRGID(prefer) == NULL) {
- sprintf(tmp, "%lu", (unsigned long) 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;
}
@@ -1022,17 +899,16 @@ static char *
pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user)
{
struct carg *arg = getarg(args, 'd');
+ static char home[128];
if (arg)
- return arg->val;
- else {
- static char home[128];
+ return (arg->val);
- if (cnf->home == NULL || *cnf->home == '\0')
- errx(EX_CONFIG, "no base home directory set");
- sprintf(home, "%s/%s", cnf->home, user);
- return home;
- }
+ if (cnf->home == NULL || *cnf->home == '\0')
+ errx(EX_CONFIG, "no base home directory set");
+ snprintf(home, sizeof(home), "%s/%s", cnf->home, user);
+
+ return (home);
}
static char *
@@ -1053,12 +929,12 @@ shell_path(char const * path, char *shells[], char *sh)
static char shellpath[256];
if (sh != NULL) {
- sprintf(shellpath, "%s/%s", p, sh);
+ snprintf(shellpath, sizeof(shellpath), "%s/%s", p, sh);
if (access(shellpath, X_OK) == 0)
return shellpath;
} else
for (i = 0; i < _UC_MAXSHELLS && shells[i] != NULL; i++) {
- sprintf(shellpath, "%s/%s", p, shells[i]);
+ snprintf(shellpath, sizeof(shellpath), "%s/%s", p, shells[i]);
if (access(shellpath, X_OK) == 0)
return shellpath;
}
@@ -1126,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);
@@ -1148,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 {
@@ -1198,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);
@@ -1232,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;
@@ -1287,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);
}
@@ -1308,7 +1292,7 @@ rmat(uid_t uid)
st.st_uid == uid) {
char tmp[MAXPATHLEN];
- sprintf(tmp, "/usr/bin/atrm %s", e->d_name);
+ snprintf(tmp, sizeof(tmp), "/usr/bin/atrm %s", e->d_name);
system(tmp);
}
}
diff --git a/usr.sbin/pw/pwupd.c b/usr.sbin/pw/pwupd.c
index c2a9a53..f9e1959 100644
--- a/usr.sbin/pw/pwupd.c
+++ b/usr.sbin/pw/pwupd.c
@@ -33,7 +33,6 @@ static const char rcsid[] =
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <stdarg.h>
#include <pwd.h>
#include <libutil.h>
#include <errno.h>
@@ -45,47 +44,29 @@ 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);
- return pathbuf;
+ snprintf(pathbuf, sizeof pathbuf, "%s/%s", conf.etcpath, file);
+
+ return (pathbuf);
}
static int
-pwdb(char *arg,...)
+pwdb_check(void)
{
int i = 0;
pid_t pid;
- va_list ap;
char *args[10];
args[i++] = _PATH_PWD_MKDB;
- va_start(ap, arg);
- while (i < 6 && arg != NULL) {
- args[i++] = arg;
- arg = va_arg(ap, char *);
- }
- if (pwpath != pathpwd) {
+ args[i++] = "-C";
+
+ if (strcmp(conf.etcpath, _PATH_PWD) != 0) {
args[i++] = "-d";
- args[i++] = pwpath;
+ args[i++] = conf.etcpath;
}
args[i++] = getpwpath(_MASTERPASSWD);
args[i] = NULL;
@@ -100,72 +81,71 @@ pwdb(char *arg,...)
if (WEXITSTATUS(i))
i = EIO;
}
- va_end(ap);
- return i;
+
+ return (i);
}
static int
pw_update(struct passwd * pwd, char const * user)
{
- int rc = 0;
-
- rc = pwdb("-C", (char *)NULL); /* Check only */
- if (rc == 0) {
- 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(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()");
- }
- /*
- * in case of deletion of a user, the whole database
- * needs to be regenerated
- */
- if (pw_mkdb(pw != NULL ? pw->pw_name : NULL) == -1) {
- pw_fini();
- err(1, "pw_mkdb()");
- }
- free(pw);
+ struct passwd *pw = NULL;
+ struct passwd *old_pw = NULL;
+ int rc, pfd, tfd;
+
+ if ((rc = pwdb_check()) != 0)
+ return (rc);
+
+ if (pwd != NULL)
+ pw = pw_dup(pwd);
+
+ if (user != NULL)
+ old_pw = GETPWNAM(user);
+
+ if (pw_init(conf.etcpath, 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()");
}
- return 0;
+ if (pw_copy(pfd, tfd, pw, old_pw) == -1) {
+ pw_fini();
+ err(1, "pw_copy()");
+ }
+ /*
+ * in case of deletion of a user, the whole database
+ * needs to be regenerated
+ */
+ if (pw_mkdb(pw != NULL ? pw->pw_name : NULL) == -1) {
+ pw_fini();
+ err(1, "pw_mkdb()");
+ }
+ free(pw);
+ pw_fini();
+
+ return (0);
}
int
addpwent(struct passwd * pwd)
{
- return pw_update(pwd, NULL);
+
+ return (pw_update(pwd, NULL));
}
int
chgpwent(char const * login, struct passwd * pwd)
{
- return pw_update(pwd, login);
+
+ return (pw_update(pwd, login));
}
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));
}
diff --git a/usr.sbin/pw/pwupd.h b/usr.sbin/pw/pwupd.h
index 4fbc35a..8f46e7d 100644
--- a/usr.sbin/pw/pwupd.h
+++ b/usr.sbin/pw/pwupd.h
@@ -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,14 +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 setgrdir(const char * dir);
char * getgrpath(const char *file);
void vsetpwent(void);
@@ -111,10 +150,7 @@ 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 */
diff --git a/usr.sbin/pw/tests/Makefile b/usr.sbin/pw/tests/Makefile
index 3003c8f..193b8ab 100644
--- a/usr.sbin/pw/tests/Makefile
+++ b/usr.sbin/pw/tests/Makefile
@@ -1,10 +1,34 @@
# $FreeBSD$
+TESTSRC= ${.CURDIR}/../../../contrib/netbsd-tests/usr.sbin/useradd
+.PATH: ${TESTSRC}
+
TESTSDIR= ${TESTSBASE}/usr.sbin/pw
-ATF_TESTS_SH= pw_delete pw_modify
+ATF_TESTS_SH= pw_etcdir \
+ pw_lock \
+ pw_config \
+ pw_groupdel \
+ pw_groupmod \
+ pw_useradd \
+ pw_userdel \
+ pw_usermod \
+ pw_usernext
+
+.for tp in ${ATF_TESTS_SH}
+TEST_METADATA.${tp}+= required_user="root"
+.endfor
-FILES= group helper_functions.shin master.passwd
+FILES= group helper_functions.shin master.passwd pw.conf \
+ pw-modified.conf
FILESDIR= ${TESTSDIR}
+ATF_TESTS_SH+= pw_test
+# - user{add,del} does not exist on FreeBSD; use pw user{add,del} instead
+# - The command passes on FreeBSD
+ATF_TESTS_SH_SED_pw_test= -e 's/useradd /pw useradd /'
+ATF_TESTS_SH_SED_pw_test+= -e 's/userdel /pw userdel /'
+ATF_TESTS_SH_SED_pw_test+= -e '/atf_expect_fail "PR bin\/39546"/d'
+ATF_TESTS_SH_SRC_pw_test= t_useradd.sh
+
.include <bsd.test.mk>
diff --git a/usr.sbin/pw/tests/helper_functions.shin b/usr.sbin/pw/tests/helper_functions.shin
index f87b1e7..3680dfe 100755
--- a/usr.sbin/pw/tests/helper_functions.shin
+++ b/usr.sbin/pw/tests/helper_functions.shin
@@ -1,5 +1,8 @@
# $FreeBSD$
+# The pw command
+PW="pw -V ${HOME}"
+
# Workdir to run tests in
TESTDIR=$(atf_get_srcdir)
diff --git a/usr.sbin/pw/tests/pw-modified.conf b/usr.sbin/pw/tests/pw-modified.conf
new file mode 100644
index 0000000..84f44e7
--- /dev/null
+++ b/usr.sbin/pw/tests/pw-modified.conf
@@ -0,0 +1,62 @@
+#
+# pw.conf - user/group configuration defaults
+#
+
+# Password for new users? no=nologin yes=loginid none=blank random=random
+defaultpasswd = "no"
+
+# Reuse gaps in uid sequence? (yes or no)
+reuseuids = "no"
+
+# Reuse gaps in gid sequence? (yes or no)
+reusegids = "no"
+
+# Path to the NIS passwd file (blank or 'no' for none)
+nispasswd =
+
+# Obtain default dotfiles from this directory
+skeleton = "/usr/share/skel"
+
+# Mail this file to new user (/etc/newuser.msg or no)
+newmail = "no"
+
+# Log add/change/remove information in this file
+logfile = "/var/log/userlog"
+
+# Root directory in which $HOME directory is created
+home = "/home"
+
+# Mode for the new $HOME directory, will be modified by umask
+homemode = 0777
+
+# Colon separated list of directories containing valid shells
+shellpath = "/bin"
+
+# Comma separated list of available shells (without paths)
+shells = "sh","csh","tcsh"
+
+# Default shell (without path)
+defaultshell = "sh"
+
+# Default group (leave blank for new group per user)
+defaultgroup = ""
+
+# Extra groups for new users
+extragroups =
+
+# Default login class for new users
+defaultclass = ""
+
+# Range of valid default user ids
+minuid = 2000
+maxuid = 5000
+
+# Range of valid default group ids
+mingid = 2100
+maxgid = 6000
+
+# Days after which account expires (0=disabled)
+expire_days = 0
+
+# Days after which password expires (0=disabled)
+password_days = 0
diff --git a/usr.sbin/pw/tests/pw.conf b/usr.sbin/pw/tests/pw.conf
new file mode 100644
index 0000000..4e493f6
--- /dev/null
+++ b/usr.sbin/pw/tests/pw.conf
@@ -0,0 +1,62 @@
+#
+# pw.conf - user/group configuration defaults
+#
+
+# Password for new users? no=nologin yes=loginid none=blank random=random
+defaultpasswd = "no"
+
+# Reuse gaps in uid sequence? (yes or no)
+reuseuids = "no"
+
+# Reuse gaps in gid sequence? (yes or no)
+reusegids = "no"
+
+# Path to the NIS passwd file (blank or 'no' for none)
+nispasswd =
+
+# Obtain default dotfiles from this directory
+skeleton = "/usr/share/skel"
+
+# Mail this file to new user (/etc/newuser.msg or no)
+newmail = "no"
+
+# Log add/change/remove information in this file
+logfile = "/var/log/userlog"
+
+# Root directory in which $HOME directory is created
+home = "/home"
+
+# Mode for the new $HOME directory, will be modified by umask
+homemode = 0777
+
+# Colon separated list of directories containing valid shells
+shellpath = "/bin"
+
+# Comma separated list of available shells (without paths)
+shells = "sh","csh","tcsh"
+
+# Default shell (without path)
+defaultshell = "sh"
+
+# Default group (leave blank for new group per user)
+defaultgroup = ""
+
+# Extra groups for new users
+extragroups =
+
+# Default login class for new users
+defaultclass = ""
+
+# Range of valid default user ids
+minuid = 1000
+maxuid = 32000
+
+# Range of valid default group ids
+mingid = 1000
+maxgid = 32000
+
+# Days after which account expires (0=disabled)
+expire_days = 0
+
+# Days after which password expires (0=disabled)
+password_days = 0
diff --git a/usr.sbin/pw/tests/pw_config.sh b/usr.sbin/pw/tests/pw_config.sh
new file mode 100755
index 0000000..fb6489a
--- /dev/null
+++ b/usr.sbin/pw/tests/pw_config.sh
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+# Import helper functions
+. $(atf_get_srcdir)/helper_functions.shin
+
+atf_test_case generate_config
+generate_config_body() {
+ atf_check -s exit:0 \
+ ${PW} useradd -D -C ${HOME}/foo.conf
+ atf_check -o file:$(atf_get_srcdir)/pw.conf \
+ cat ${HOME}/foo.conf
+}
+
+atf_test_case modify_config_uid_gid_boundaries
+modify_config_uid_gid_boundaries_body() {
+ atf_check -s exit:0 \
+ ${PW} useradd -D -C ${HOME}/foo.conf \
+ -u 2000,5000 -i 2100,6000
+ atf_check -o file:$(atf_get_srcdir)/pw-modified.conf \
+ cat ${HOME}/foo.conf
+}
+
+atf_init_test_cases() {
+ atf_add_test_case generate_config
+ atf_add_test_case modify_config_uid_gid_boundaries
+}
diff --git a/usr.sbin/pw/tests/pw_delete.sh b/usr.sbin/pw/tests/pw_delete.sh
deleted file mode 100755
index 0e97939..0000000
--- a/usr.sbin/pw/tests/pw_delete.sh
+++ /dev/null
@@ -1,47 +0,0 @@
-# $FreeBSD$
-
-# Import helper functions
-. $(atf_get_srcdir)/helper_functions.shin
-
-# Test that a user can be deleted when another user is part of this
-# user's default group and does not go into an infinate loop.
-# PR: 191427
-atf_test_case rmuser_seperate_group cleanup
-rmuser_seperate_group_head() {
- atf_set "timeout" "30"
-}
-rmuser_seperate_group_body() {
- populate_etc_skel
- pw -V ${HOME} useradd test || atf_fail "Creating test user"
- pw -V ${HOME} groupmod test -M 'test,root' || \
- atf_fail "Modifying the group"
- pw -V ${HOME} userdel test || atf_fail "delete the user"
-}
-
-atf_test_case group_do_not_delete_wheel_if_group_unkown
-group_do_not_delete_wheel_if_group_unkown_head() {
- atf_set "descr" "Make sure we do not consider as gid 0 an unknown group"
-}
-
-group_do_not_delete_wheel_if_group_unkown_body() {
- populate_etc_skel
- atf_check -s exit:0 -o inline:"wheel:*:0:root\n" -x pw -V ${HOME} groupshow wheel
- atf_check -e inline:"pw: -g expects a number\n" -s exit:64 -x pw -V ${HOME} groupdel -g I_do_not_exist
- atf_check -s exit:0 -o inline:"wheel:*:0:root\n" -x pw -V ${HOME} groupshow wheel
-}
-
-atf_test_case user_do_not_try_to_delete_root_if_user_unkown
-user_do_not_try_to_delete_root_if_user_unkown_head() {
- atf_set "descr" "Make sure not to try to remove root if deleteing an unknown user"
-}
-
-user_do_not_try_to_delete_root_if_user_unkown_body() {
- populate_etc_skel
- atf_check -e inline:"pw: -u expects a number\n" -s exit:64 -x pw -V ${HOME} userdel -u plop
-}
-
-atf_init_test_cases() {
- atf_add_test_case rmuser_seperate_group
- atf_add_test_case group_do_not_delete_wheel_if_group_unkown
- atf_add_test_case user_do_not_try_to_delete_root_if_user_unkown
-}
diff --git a/usr.sbin/pw/tests/pw_etcdir.sh b/usr.sbin/pw/tests/pw_etcdir.sh
new file mode 100755
index 0000000..b237789
--- /dev/null
+++ b/usr.sbin/pw/tests/pw_etcdir.sh
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+# When the '-V directory' option is provided, the directory must exist
+atf_test_case etcdir_must_exist
+etcdir_must_exist_head() {
+ atf_set "descr" "When the '-V directory' option is provided, the directory must exist"
+}
+
+etcdir_must_exist_body() {
+ local fakedir="/this_directory_does_not_exist"
+ atf_check -e inline:"pw: no such directory \`$fakedir'\n" \
+ -s exit:72 -x pw -V ${fakedir} usershow root
+}
+
+atf_init_test_cases() {
+ atf_add_test_case etcdir_must_exist
+}
+
diff --git a/usr.sbin/pw/tests/pw_groupdel.sh b/usr.sbin/pw/tests/pw_groupdel.sh
new file mode 100755
index 0000000..75b063a
--- /dev/null
+++ b/usr.sbin/pw/tests/pw_groupdel.sh
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+# Import helper functions
+. $(atf_get_srcdir)/helper_functions.shin
+
+
+# Test to make sure we do not accidentially delete wheel when trying to delete
+# an unknown group
+atf_test_case group_do_not_delete_wheel_if_group_unknown
+group_do_not_delete_wheel_if_group_unknown_head() {
+ atf_set "descr" "Make sure we do not consider gid 0 an unknown group"
+}
+group_do_not_delete_wheel_if_group_unknown_body() {
+ populate_etc_skel
+ atf_check -s exit:0 -o inline:"wheel:*:0:root\n" -x ${PW} groupshow wheel
+ atf_check -e inline:"pw: -g expects a number\n" -s exit:64 -x \
+ ${PW} groupdel -g I_do_not_exist
+ atf_check -s exit:0 -o inline:"wheel:*:0:root\n" -x ${PW} groupshow wheel
+}
+
+
+atf_init_test_cases() {
+ atf_add_test_case group_do_not_delete_wheel_if_group_unknown
+}
diff --git a/usr.sbin/pw/tests/pw_groupmod.sh b/usr.sbin/pw/tests/pw_groupmod.sh
new file mode 100755
index 0000000..9ea8a6d
--- /dev/null
+++ b/usr.sbin/pw/tests/pw_groupmod.sh
@@ -0,0 +1,91 @@
+# $FreeBSD$
+
+# Import helper functions
+. $(atf_get_srcdir)/helper_functions.shin
+
+
+# Test adding & removing a user from a group
+atf_test_case groupmod_user
+groupmod_user_body() {
+ populate_etc_skel
+ atf_check -s exit:0 ${PW} addgroup test
+ atf_check -s exit:0 ${PW} groupmod test -m root
+ atf_check -s exit:0 -o match:"^test:\*:1001:root$" \
+ grep "^test:\*:.*:root$" $HOME/group
+ atf_check -s exit:0 ${PW} groupmod test -d root
+ atf_check -s exit:0 -o match:"^test:\*:1001:$" \
+ grep "^test:\*:.*:$" $HOME/group
+}
+
+
+# Test adding and removing a user that does not exist
+atf_test_case groupmod_invalid_user
+groupmod_invalid_user_body() {
+ populate_etc_skel
+ atf_check -s exit:0 ${PW} addgroup test
+ atf_check -s exit:67 -e match:"does not exist" ${PW} groupmod test -m foo
+ atf_check -s exit:0 ${PW} groupmod test -d foo
+}
+
+atf_test_case groupmod_bug_193704
+groupmod_bug_193704_head() {
+ atf_set "descr" "Regression test for the #193704 bug"
+}
+groupmod_bug_193704_body() {
+ populate_etc_skel
+ atf_check -s exit:0 -x ${PW} groupadd test
+ atf_check -s exit:0 -x ${PW} groupmod test -l newgroupname
+ atf_check -s exit:65 -e match:"^pw: unknown group" -x ${PW} groupshow test
+}
+
+atf_test_case usermod_bug_185666
+usermod_bug_185666_head() {
+ atf_set "descr" "Regression test for the #185666 bug"
+}
+
+usermod_bug_185666_body() {
+ populate_etc_skel
+ atf_check -s exit:0 -x ${PW} useradd testuser
+ atf_check -s exit:0 -x ${PW} groupadd testgroup
+ atf_check -s exit:0 -x ${PW} groupadd testgroup2
+ atf_check -s exit:0 -x ${PW} usermod testuser -G testgroup
+ atf_check -o inline:"testuser:*:1001:\n" -x ${PW} groupshow testuser
+ atf_check -o inline:"testgroup:*:1002:testuser\n" -x ${PW} groupshow testgroup
+ atf_check -o inline:"testgroup2:*:1003:\n" -x ${PW} groupshow testgroup2
+ atf_check -s exit:0 -x ${PW} usermod testuser -G testgroup2
+ atf_check -o inline:"testuser:*:1001:\n" -x ${PW} groupshow testuser
+ atf_check -o inline:"testgroup:*:1002:\n" -x ${PW} groupshow testgroup
+ atf_check -o inline:"testgroup2:*:1003:testuser\n" -x ${PW} groupshow testgroup2
+}
+
+atf_test_case do_not_duplicate_group_on_gid_change
+do_not_duplicate_group_on_gid_change_head() {
+ atf_set "descr" "Do not duplicate group on gid change"
+}
+
+do_not_duplicate_group_on_gid_change_body() {
+ populate_etc_skel
+ atf_check -s exit:0 -x ${PW} groupadd testgroup
+ atf_check -s exit:0 -x ${PW} groupmod testgroup -g 12345
+ # use grep to see if the entry has not be duplicated
+ atf_check -o inline:"testgroup:*:12345:\n" -s exit:0 -x grep "^testgroup" ${HOME}/group
+}
+
+atf_test_case groupmod_rename
+groupmod_rename_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 ${PW} groupadd foo
+ atf_check -s exit:0 ${PW} groupmod foo -l bar
+ atf_check -s exit:0 -o match:"^bar:.*" \
+ grep "^bar:.*" ${HOME}/group
+}
+
+atf_init_test_cases() {
+ atf_add_test_case groupmod_user
+ atf_add_test_case groupmod_invalid_user
+ atf_add_test_case groupmod_bug_193704
+ atf_add_test_case usermod_bug_185666
+ atf_add_test_case do_not_duplicate_group_on_gid_change
+ atf_add_test_case groupmod_rename
+}
diff --git a/usr.sbin/pw/tests/pw_lock.sh b/usr.sbin/pw/tests/pw_lock.sh
new file mode 100755
index 0000000..9f14e24
--- /dev/null
+++ b/usr.sbin/pw/tests/pw_lock.sh
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+# Import helper functions
+. $(atf_get_srcdir)/helper_functions.shin
+
+# Test locking and unlocking a user account
+atf_test_case user_locking cleanup
+user_locking_body() {
+ populate_etc_skel
+ ${PW} useradd test || atf_fail "Creating test user"
+ ${PW} lock test || atf_fail "Locking the user"
+ atf_check -s exit:0 -o match:"^test:\*LOCKED\*\*:1001:" \
+ grep "^test:\*LOCKED\*\*:1001:" $HOME/master.passwd
+ ${PW} unlock test || atf_fail "Locking the user"
+ atf_check -s exit:0 -o match:"^test:\*:1001:" \
+ grep "^test:\*:1001:" $HOME/master.passwd
+}
+
+
+atf_init_test_cases() {
+ atf_add_test_case user_locking
+}
diff --git a/usr.sbin/pw/tests/pw_modify.sh b/usr.sbin/pw/tests/pw_modify.sh
deleted file mode 100755
index b81f105..0000000
--- a/usr.sbin/pw/tests/pw_modify.sh
+++ /dev/null
@@ -1,80 +0,0 @@
-# $FreeBSD$
-
-# Import helper functions
-. $(atf_get_srcdir)/helper_functions.shin
-
-
-# Test adding & removing a user from a group
-atf_test_case groupmod_user
-groupmod_user_body() {
- populate_etc_skel
- atf_check -s exit:0 pw -V ${HOME} addgroup test
- atf_check -s exit:0 pw -V ${HOME} groupmod test -m root
- atf_check -s exit:0 -o match:"^test:\*:1001:root$" \
- grep "^test:\*:.*:root$" $HOME/group
- atf_check -s exit:0 pw -V ${HOME} groupmod test -d root
- atf_check -s exit:0 -o match:"^test:\*:1001:$" \
- grep "^test:\*:.*:$" $HOME/group
-}
-
-
-# Test adding and removing a user that does not exist
-atf_test_case groupmod_invalid_user
-groupmod_invalid_user_body() {
- populate_etc_skel
- atf_check -s exit:0 pw -V ${HOME} addgroup test
- atf_check -s exit:67 -e match:"does not exist" pw -V ${HOME} groupmod test -m foo
- atf_check -s exit:0 pw -V ${HOME} groupmod test -d foo
-}
-
-atf_test_case groupmod_bug_193704
-groupmod_bug_193704_head() {
- atf_set "descr" "Regression test for the #193704 bug"
-}
-groupmod_bug_193704_body() {
- populate_etc_skel
- atf_check -s exit:0 -x pw -V ${HOME} groupadd test
- atf_check -s exit:0 -x pw -V ${HOME} groupmod test -l newgroupname
- atf_check -s exit:65 -e match:"^pw: unknown group" -x pw -V ${HOME} groupshow test
-}
-
-atf_test_case usermod_bug_185666
-usermod_bug_185666_head() {
- atf_set "descr" "Regression test for the #185666 bug"
-}
-
-usermod_bug_185666_body() {
- populate_etc_skel
- atf_check -s exit:0 -x pw -V ${HOME} useradd testuser
- atf_check -s exit:0 -x pw -V ${HOME} groupadd testgroup
- atf_check -s exit:0 -x pw -V ${HOME} groupadd testgroup2
- atf_check -s exit:0 -x pw -V ${HOME} usermod testuser -G testgroup
- atf_check -o inline:"testuser:*:1001:\n" -x pw -V${HOME} groupshow testuser
- atf_check -o inline:"testgroup:*:1002:testuser\n" -x pw -V ${HOME} groupshow testgroup
- atf_check -o inline:"testgroup2:*:1003:\n" -x pw -V${HOME} groupshow testgroup2
- atf_check -s exit:0 -x pw -V ${HOME} usermod testuser -G testgroup2
- atf_check -o inline:"testuser:*:1001:\n" -x pw -V ${HOME} groupshow testuser
- atf_check -o inline:"testgroup:*:1002:\n" -x pw -V ${HOME} groupshow testgroup
- atf_check -o inline:"testgroup2:*:1003:testuser\n" -x pw -V ${HOME} groupshow testgroup2
-}
-
-atf_test_case do_not_duplicate_group_on_gid_change
-do_not_duplicate_group_on_gid_change_head() {
- atf_set "descr" "Do not duplicate group on gid change"
-}
-
-do_not_duplicate_group_on_gid_change_body() {
- populate_etc_skel
- atf_check -s exit:0 -x pw -V ${HOME} groupadd testgroup
- atf_check -s exit:0 -x pw -V ${HOME} groupmod testgroup -g 12345
- # use grep to see if the entry has not be duplicated
- atf_check -o inline:"testgroup:*:12345:\n" -s exit:0 -x grep "^testgroup" ${HOME}/group
-}
-
-atf_init_test_cases() {
- atf_add_test_case groupmod_user
- atf_add_test_case groupmod_invalid_user
- atf_add_test_case groupmod_bug_193704
- atf_add_test_case usermod_bug_185666
- atf_add_test_case do_not_duplicate_group_on_gid_change
-}
diff --git a/usr.sbin/pw/tests/pw_useradd.sh b/usr.sbin/pw/tests/pw_useradd.sh
new file mode 100755
index 0000000..27a8624
--- /dev/null
+++ b/usr.sbin/pw/tests/pw_useradd.sh
@@ -0,0 +1,196 @@
+# $FreeBSD$
+
+# Import helper functions
+. $(atf_get_srcdir)/helper_functions.shin
+
+# Test add user
+atf_test_case user_add
+user_add_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 ${PW} useradd test
+ atf_check -s exit:0 -o match:"^test:.*" \
+ grep "^test:.*" $HOME/master.passwd
+}
+
+# Test add user with option -N
+atf_test_case user_add_noupdate
+user_add_noupdate_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 -o match:"^test:.*" ${PW} useradd test -N
+ atf_check -s exit:1 -o empty grep "^test:.*" $HOME/master.passwd
+}
+
+# Test add user with comments
+atf_test_case user_add_comments
+user_add_comments_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 ${PW} useradd test -c "Test User,work,123,456"
+ atf_check -s exit:0 -o match:"^test:.*:Test User,work,123,456:" \
+ grep "^test:.*:Test User,work,123,456:" $HOME/master.passwd
+}
+
+# Test add user with comments and option -N
+atf_test_case user_add_comments_noupdate
+user_add_comments_noupdate_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 -o match:"^test:.*:Test User,work,123,456:" \
+ ${PW} useradd test -c "Test User,work,123,456" -N
+ atf_check -s exit:1 -o empty grep "^test:.*" $HOME/master.passwd
+}
+
+# Test add user with invalid comments
+atf_test_case user_add_comments_invalid
+user_add_comments_invalid_body() {
+ populate_etc_skel
+
+ atf_check -s exit:65 -e match:"invalid character" \
+ ${PW} useradd test -c "Test User,work,123:456,456"
+ atf_check -s exit:1 -o empty \
+ grep "^test:.*:Test User,work,123:456,456:" $HOME/master.passwd
+}
+
+# Test add user with invalid comments and option -N
+atf_test_case user_add_comments_invalid_noupdate
+user_add_comments_invalid_noupdate_body() {
+ populate_etc_skel
+
+ atf_check -s exit:65 -e match:"invalid character" \
+ ${PW} useradd test -c "Test User,work,123:456,456" -N
+ atf_check -s exit:1 -o empty grep "^test:.*" $HOME/master.passwd
+}
+
+# Test add user with alternate homedir
+atf_test_case user_add_homedir
+user_add_homedir_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 ${PW} useradd test -d /foo/bar
+ atf_check -s exit:0 -o match:"^test:\*:.*::0:0:User &:/foo/bar:.*" \
+ ${PW} usershow test
+}
+
+# Test add user with account expiration as an epoch date
+atf_test_case user_add_account_expiration_epoch
+user_add_account_expiration_epoch_body() {
+ populate_etc_skel
+
+ DATE=`date -j -v+1d "+%s"`
+ atf_check -s exit:0 ${PW} useradd test -e ${DATE}
+ atf_check -s exit:0 -o match:"^test:\*:.*::0:${DATE}:.*" \
+ ${PW} usershow test
+}
+
+# Test add user with account expiration as a DD-MM-YYYY date
+atf_test_case user_add_account_expiration_date_numeric
+user_add_account_expiration_date_numeric_body() {
+ populate_etc_skel
+
+ DATE=`date -j -v+1d "+%d-%m-%Y"`
+ EPOCH=`date -j -f "%d-%m-%Y %H:%M:%S" "${DATE} 00:00:00" "+%s"`
+ atf_check -s exit:0 ${PW} useradd test -e ${DATE}
+ atf_check -s exit:0 -o match:"^test:\*:.*::0:${EPOCH}:User &:.*" \
+ ${PW} usershow test
+}
+
+# Test add user with account expiration as a DD-MM-YYYY date
+atf_test_case user_add_account_expiration_date_month
+user_add_account_expiration_date_month_body() {
+ populate_etc_skel
+
+ DATE=`date -j -v+1d "+%d-%b-%Y"`
+ EPOCH=`date -j -f "%d-%b-%Y %H:%M:%S" "${DATE} 00:00:00" "+%s"`
+ atf_check -s exit:0 ${PW} useradd test -e ${DATE}
+ atf_check -s exit:0 -o match:"^test:\*:.*::0:${EPOCH}:User &:.*" \
+ ${PW} usershow test
+}
+
+# Test add user with account expiration as a relative date
+atf_test_case user_add_account_expiration_date_relative
+user_add_account_expiration_date_relative_body() {
+ populate_etc_skel
+
+ EPOCH=`date -j -v+13m "+%s"`
+ BUF=`expr $EPOCH + 5`
+ atf_check -s exit:0 ${PW} useradd test -e +13o
+ TIME=`${PW} usershow test | awk -F ':' '{print $7}'`
+ [ ! -z $TIME -a $TIME -ge $EPOCH -a $TIME -lt $BUF ] || \
+ atf_fail "Expiration time($TIME) was not within $EPOCH - $BUF seconds."
+}
+
+# Test add user with password expiration as an epoch date
+atf_test_case user_add_password_expiration_epoch
+user_add_password_expiration_epoch_body() {
+ populate_etc_skel
+
+ DATE=`date -j -v+1d "+%s"`
+ atf_check -s exit:0 ${PW} useradd test -p ${DATE}
+ atf_check -s exit:0 -o match:"^test:\*:.*::${DATE}:0:.*" \
+ ${PW} usershow test
+}
+
+# Test add user with password expiration as a DD-MM-YYYY date
+atf_test_case user_add_password_expiration_date_numeric
+user_add_password_expiration_date_numeric_body() {
+ populate_etc_skel
+
+ DATE=`date -j -v+1d "+%d-%m-%Y"`
+ EPOCH=`date -j -f "%d-%m-%Y %H:%M:%S" "${DATE} 00:00:00" "+%s"`
+ atf_check -s exit:0 ${PW} useradd test -p ${DATE}
+ atf_check -s exit:0 -o match:"^test:\*:.*::${EPOCH}:0:User &:.*" \
+ ${PW} usershow test
+}
+
+# Test add user with password expiration as a DD-MMM-YYYY date
+atf_test_case user_add_password_expiration_date_month
+user_add_password_expiration_date_month_body() {
+ populate_etc_skel
+
+ DATE=`date -j -v+1d "+%d-%b-%Y"`
+ EPOCH=`date -j -f "%d-%b-%Y %H:%M:%S" "${DATE} 00:00:00" "+%s"`
+ atf_check -s exit:0 ${PW} useradd test -p ${DATE}
+ atf_check -s exit:0 -o match:"^test:\*:.*::${EPOCH}:0:User &:.*" \
+ ${PW} usershow test
+}
+
+# Test add user with password expiration as a relative date
+atf_test_case user_add_password_expiration_date_relative
+user_add_password_expiration_date_relative_body() {
+ populate_etc_skel
+
+ EPOCH=`date -j -v+13m "+%s"`
+ BUF=`expr $EPOCH + 5`
+ atf_check -s exit:0 ${PW} useradd test -p +13o
+ TIME=`${PW} usershow test | awk -F ':' '{print $6}'`
+ [ ! -z $TIME -a $TIME -ge $EPOCH -a $TIME -lt $BUF ] || \
+ atf_fail "Expiration time($TIME) was not within $EPOCH - $BUF seconds."
+}
+
+atf_test_case user_add_name_too_long
+user_add_name_too_long_body() {
+ populate_etc_skel
+ atf_check -e match:"too long" -s exit:64 \
+ ${PW} useradd name_very_vert_very_very_very_long
+}
+
+atf_init_test_cases() {
+ atf_add_test_case user_add
+ atf_add_test_case user_add_noupdate
+ atf_add_test_case user_add_comments
+ atf_add_test_case user_add_comments_noupdate
+ atf_add_test_case user_add_comments_invalid
+ atf_add_test_case user_add_comments_invalid_noupdate
+ atf_add_test_case user_add_homedir
+ atf_add_test_case user_add_account_expiration_epoch
+ atf_add_test_case user_add_account_expiration_date_numeric
+ atf_add_test_case user_add_account_expiration_date_month
+ atf_add_test_case user_add_account_expiration_date_relative
+ atf_add_test_case user_add_password_expiration_epoch
+ atf_add_test_case user_add_password_expiration_date_numeric
+ atf_add_test_case user_add_password_expiration_date_month
+ atf_add_test_case user_add_password_expiration_date_relative
+ atf_add_test_case user_add_name_too_long
+}
diff --git a/usr.sbin/pw/tests/pw_userdel.sh b/usr.sbin/pw/tests/pw_userdel.sh
new file mode 100755
index 0000000..71a7033
--- /dev/null
+++ b/usr.sbin/pw/tests/pw_userdel.sh
@@ -0,0 +1,37 @@
+# $FreeBSD$
+
+# Import helper functions
+. $(atf_get_srcdir)/helper_functions.shin
+
+
+# Test that a user can be deleted when another user is part of this
+# user's default group and does not go into an infinate loop.
+# PR: 191427
+atf_test_case rmuser_seperate_group cleanup
+rmuser_seperate_group_head() {
+ atf_set "timeout" "30"
+}
+rmuser_seperate_group_body() {
+ populate_etc_skel
+ ${PW} useradd test || atf_fail "Creating test user"
+ ${PW} groupmod test -M 'test,root' || \
+ atf_fail "Modifying the group"
+ ${PW} userdel test || atf_fail "Delete the test user"
+}
+
+
+atf_test_case user_do_not_try_to_delete_root_if_user_unknown
+user_do_not_try_to_delete_root_if_user_unknown_head() {
+ atf_set "descr" \
+ "Make sure not to try to remove root if deleting an unknown user"
+}
+user_do_not_try_to_delete_root_if_user_unknown_body() {
+ populate_etc_skel
+ atf_check -e inline:"pw: -u expects a number\n" -s exit:64 -x \
+ ${PW} userdel -u plop
+}
+
+atf_init_test_cases() {
+ atf_add_test_case rmuser_seperate_group
+ atf_add_test_case user_do_not_try_to_delete_root_if_user_unknown
+}
diff --git a/usr.sbin/pw/tests/pw_usermod.sh b/usr.sbin/pw/tests/pw_usermod.sh
new file mode 100755
index 0000000..dbc6481
--- /dev/null
+++ b/usr.sbin/pw/tests/pw_usermod.sh
@@ -0,0 +1,133 @@
+# $FreeBSD$
+
+# Import helper functions
+. $(atf_get_srcdir)/helper_functions.shin
+
+# Test modifying a user
+atf_test_case user_mod
+user_mod_body() {
+ populate_etc_skel
+
+ atf_check -s exit:67 -e match:"no such user" ${PW} usermod test
+ atf_check -s exit:0 ${PW} useradd test
+ atf_check -s exit:0 ${PW} usermod test
+ atf_check -s exit:0 -o match:"^test:.*" \
+ grep "^test:.*" $HOME/master.passwd
+}
+
+# Test modifying a user with option -N
+atf_test_case user_mod_noupdate
+user_mod_noupdate_body() {
+ populate_etc_skel
+
+ atf_check -s exit:67 -e match:"no such user" ${PW} usermod test -N
+ atf_check -s exit:0 ${PW} useradd test
+ atf_check -s exit:0 -o match:"^test:.*" ${PW} usermod test -N
+ atf_check -s exit:0 -o match:"^test:.*" \
+ grep "^test:.*" $HOME/master.passwd
+}
+
+# Test modifying a user with comments
+atf_test_case user_mod_comments
+user_mod_comments_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 ${PW} useradd test -c "Test User,home,123,456"
+ atf_check -s exit:0 ${PW} usermod test -c "Test User,work,123,456"
+ atf_check -s exit:0 -o match:"^test:.*:Test User,work,123,456:" \
+ grep "^test:.*:Test User,work,123,456:" $HOME/master.passwd
+}
+
+# Test modifying a user with comments with option -N
+atf_test_case user_mod_comments_noupdate
+user_mod_comments_noupdate_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 ${PW} useradd test -c "Test User,home,123,456"
+ atf_check -s exit:0 -o match:"^test:.*:Test User,work,123,456:" \
+ ${PW} usermod test -c "Test User,work,123,456" -N
+ atf_check -s exit:0 -o match:"^test:.*:Test User,home,123,456:" \
+ grep "^test:.*:Test User,home,123,456:" $HOME/master.passwd
+}
+
+# Test modifying a user with invalid comments
+atf_test_case user_mod_comments_invalid
+user_mod_comments_invalid_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 ${PW} useradd test
+ atf_check -s exit:65 -e match:"invalid character" \
+ ${PW} usermod test -c "Test User,work,123:456,456"
+ atf_check -s exit:1 -o empty \
+ grep "^test:.*:Test User,work,123:456,456:" $HOME/master.passwd
+ atf_check -s exit:0 -o match:"^test:\*" \
+ grep "^test:\*" $HOME/master.passwd
+}
+
+# Test modifying a user with invalid comments with option -N
+atf_test_case user_mod_comments_invalid_noupdate
+user_mod_comments_invalid_noupdate_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 ${PW} useradd test
+ atf_check -s exit:65 -e match:"invalid character" \
+ ${PW} usermod test -c "Test User,work,123:456,456" -N
+ atf_check -s exit:1 -o empty \
+ grep "^test:.*:Test User,work,123:456,456:" $HOME/master.passwd
+ atf_check -s exit:0 -o match:"^test:\*" \
+ grep "^test:\*" $HOME/master.passwd
+}
+
+# Test modifying a user name with -l
+atf_test_case user_mod_name
+user_mod_name_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 ${PW} useradd foo
+ atf_check -s exit:0 ${PW} usermod foo -l "bar"
+ atf_check -s exit:0 -o match:"^bar:.*" \
+ grep "^bar:.*" $HOME/master.passwd
+}
+
+# Test modifying a user name with -l with option -N
+atf_test_case user_mod_name_noupdate
+user_mod_name_noupdate_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 ${PW} useradd foo
+ atf_check -s exit:0 -o match:"^bar:.*" ${PW} usermod foo -l "bar" -N
+ atf_check -s exit:0 -o match:"^foo:.*" \
+ grep "^foo:.*" $HOME/master.passwd
+}
+
+atf_test_case user_mod_rename
+user_mod_rename_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 ${PW} useradd foo
+ atf_check -s exit:0 ${PW} usermod foo -l bar
+ atf_check -s exit:0 -o match:"^bar:.*" \
+ grep "^bar:.*" ${HOME}/master.passwd
+}
+
+atf_test_case user_mod_rename_too_long
+user_mod_rename_too_long_body() {
+ populate_etc_skel
+
+ atf_check -s exit:0 ${PW} useradd foo
+ atf_check -s exit:64 -e match:"too long" ${PW} usermod foo \
+ -l name_very_very_very_very_very_long
+}
+
+atf_init_test_cases() {
+ atf_add_test_case user_mod
+ atf_add_test_case user_mod_noupdate
+ atf_add_test_case user_mod_comments
+ atf_add_test_case user_mod_comments_noupdate
+ atf_add_test_case user_mod_comments_invalid
+ atf_add_test_case user_mod_comments_invalid_noupdate
+ atf_add_test_case user_mod_rename
+ atf_add_test_case user_mod_name_noupdate
+ atf_add_test_case user_mod_rename
+ atf_add_test_case user_mod_rename_too_long
+}
diff --git a/usr.sbin/pw/tests/pw_usernext.sh b/usr.sbin/pw/tests/pw_usernext.sh
new file mode 100755
index 0000000..89f938e
--- /dev/null
+++ b/usr.sbin/pw/tests/pw_usernext.sh
@@ -0,0 +1,45 @@
+# $FreeBSD$
+
+# Import helper functions
+. $(atf_get_srcdir)/helper_functions.shin
+
+# Test usernext after adding a random number of new users.
+atf_test_case usernext
+usernext_body() {
+ populate_etc_skel
+
+ CURRENT=`${PW} usernext | sed -e 's/:.*//'`
+ RANDOM=`jot -r 1 1 150`
+ MAX=`expr ${CURRENT} + ${RANDOM}`
+ while [ "${CURRENT}" -lt "${MAX}" ]
+ do
+ atf_check -s exit:0 ${PW} useradd test${CURRENT}
+ CURRENT=`expr ${CURRENT} + 1`
+ done
+ atf_check -s exit:0 -o match:"${CURRENT}:${CURRENT}" \
+ ${PW} usernext
+}
+
+# Test usernext when multiple users are added to the same group so
+# that group id doesn't increment at the same pace as new users.
+atf_test_case usernext_assigned_group
+usernext_assigned_group_body() {
+ populate_etc_skel
+
+ CURRENT=`${PW} usernext | sed -e 's/:.*//'`
+ CURRENTGID=`${PW} groupnext`
+ RANDOM=`jot -r 1 1 150`
+ MAX=`expr ${CURRENT} + ${RANDOM}`
+ while [ "${CURRENT}" -lt "${MAX}" ]
+ do
+ atf_check -s exit:0 ${PW} useradd -n test${CURRENT} -g 0
+ CURRENT=`expr ${CURRENT} + 1`
+ done
+ atf_check -s exit:0 -o match:"${CURRENT}:${CURRENTGID}" \
+ ${PW} usernext
+}
+
+atf_init_test_cases() {
+ atf_add_test_case usernext
+ atf_add_test_case usernext_assigned_group
+}
OpenPOWER on IntegriCloud