pw(8) -- a backend utility to manage the user and group databases.
sysinstall's new User&group menu will use it, hence it's a 2.2 candidate despite of providing new functionality. Submitted by: David L. Nugent, <davidn@blaze.net.au>
This commit is contained in:
parent
581f6ca842
commit
d6f907dc7a
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/cvs2svn/branches/DAVIDN/; revision=20253
18
usr.sbin/pw/Makefile
Normal file
18
usr.sbin/pw/Makefile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# $Id$
|
||||||
|
|
||||||
|
PROG= pw
|
||||||
|
SRCS= pw.c pw_conf.c pw_user.c pw_group.c pw_log.c \
|
||||||
|
grupd.c pwupd.c fileupd.c edgroup.c psdate.c \
|
||||||
|
bitmap.c cpdir.c rm_r.c
|
||||||
|
|
||||||
|
MAN5= pw.conf.5
|
||||||
|
MAN8= pw.8
|
||||||
|
|
||||||
|
CFLAGS+= -Wall
|
||||||
|
LDADD= -lcrypt
|
||||||
|
DPADD= ${LIBCRYPT}
|
||||||
|
|
||||||
|
BINOWN= root
|
||||||
|
BINMODE=0555
|
||||||
|
|
||||||
|
.include <bsd.prog.mk>
|
22
usr.sbin/pw/README
Normal file
22
usr.sbin/pw/README
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
pw is a command-line driven passwd/group editor utility that provides
|
||||||
|
an easy and safe means of modifying of any/all fields in the system
|
||||||
|
password files, and has an add, modify and delete mode for user and
|
||||||
|
group records. Command line options have been fashioned to be similar
|
||||||
|
to those used by the Sun/shadow commands: useradd, usermod, userdel,
|
||||||
|
groupadd, groupmod, groupdel, but combines all operations within the
|
||||||
|
single command `pw'.
|
||||||
|
|
||||||
|
User add mode also provides a means of easily setting system useradd
|
||||||
|
defaults (see pw.conf.5), so that adding a user is as easy as issuing
|
||||||
|
the command "pw useradd <loginid>". Creation of a unique primary
|
||||||
|
group for each user and automatic memberhip in secondary groups
|
||||||
|
is fully supported.
|
||||||
|
|
||||||
|
This program may be FreBSD specific, but should be trivial to port to
|
||||||
|
other bsd4.4 variants.
|
||||||
|
|
||||||
|
Author and maintainer: David L. Nugent, <davidn@blaze.net.au>
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
|
135
usr.sbin/pw/bitmap.c
Normal file
135
usr.sbin/pw/bitmap.c
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "bitmap.h"
|
||||||
|
|
||||||
|
struct bitmap
|
||||||
|
bm_alloc(int size)
|
||||||
|
{
|
||||||
|
struct bitmap bm;
|
||||||
|
int szmap = (size / 8) + !!(size % 8);
|
||||||
|
|
||||||
|
bm.size = size;
|
||||||
|
bm.map = malloc(szmap);
|
||||||
|
if (bm.map)
|
||||||
|
memset(bm.map, 0, szmap);
|
||||||
|
return bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bm_dealloc(struct bitmap * bm)
|
||||||
|
{
|
||||||
|
if (bm->map)
|
||||||
|
free(bm->map);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bm_getmask(int *pos, unsigned char *bmask)
|
||||||
|
{
|
||||||
|
*bmask = (unsigned char) (1 << (*pos % 8));
|
||||||
|
*pos /= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bm_setbit(struct bitmap * bm, int pos)
|
||||||
|
{
|
||||||
|
unsigned char bmask;
|
||||||
|
|
||||||
|
bm_getmask(&pos, &bmask);
|
||||||
|
bm->map[pos] |= bmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bm_clrbit(struct bitmap * bm, int pos)
|
||||||
|
{
|
||||||
|
unsigned char bmask;
|
||||||
|
|
||||||
|
bm_getmask(&pos, &bmask);
|
||||||
|
bm->map[pos] &= ~bmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
bm_isset(struct bitmap * bm, int pos)
|
||||||
|
{
|
||||||
|
unsigned char bmask;
|
||||||
|
|
||||||
|
bm_getmask(&pos, &bmask);
|
||||||
|
return !!(bm->map[pos] & bmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
bm_firstunset(struct bitmap * bm)
|
||||||
|
{
|
||||||
|
int szmap = (bm->size / 8) + !!(bm->size % 8);
|
||||||
|
int at = 0;
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
while (pos < szmap) {
|
||||||
|
unsigned char bmv = bm->map[pos++];
|
||||||
|
unsigned char bmask = 1;
|
||||||
|
|
||||||
|
while (bmask & 0xff) {
|
||||||
|
if ((bmv & bmask) == 0)
|
||||||
|
return at;
|
||||||
|
bmask <<= 1;
|
||||||
|
++at;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return at;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
bm_lastset(struct bitmap * bm)
|
||||||
|
{
|
||||||
|
int szmap = (bm->size / 8) + !!(bm->size % 8);
|
||||||
|
int at = 0;
|
||||||
|
int pos = 0;
|
||||||
|
int ofs = 0;
|
||||||
|
|
||||||
|
while (pos < szmap) {
|
||||||
|
unsigned char bmv = bm->map[pos++];
|
||||||
|
unsigned char bmask = 1;
|
||||||
|
|
||||||
|
while (bmask & 0xff) {
|
||||||
|
if ((bmv & bmask) != 0)
|
||||||
|
ofs = at;
|
||||||
|
bmask <<= 1;
|
||||||
|
++at;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ofs;
|
||||||
|
}
|
56
usr.sbin/pw/bitmap.h
Normal file
56
usr.sbin/pw/bitmap.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BITMAP_H_
|
||||||
|
#define _BITMAP_H_
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
|
||||||
|
struct bitmap
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
unsigned char *map;
|
||||||
|
};
|
||||||
|
|
||||||
|
__BEGIN_DECLS
|
||||||
|
struct bitmap bm_alloc __P((int size));
|
||||||
|
void bm_dealloc __P((struct bitmap * bm));
|
||||||
|
void bm_setbit __P((struct bitmap * bm, int pos));
|
||||||
|
void bm_clrbit __P((struct bitmap * bm, int pos));
|
||||||
|
int bm_isset __P((struct bitmap * bm, int pos));
|
||||||
|
int bm_firstunset __P((struct bitmap * bm));
|
||||||
|
int bm_lastset __P((struct bitmap * bm));
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
|
#endif /* !_BITMAP_H */
|
118
usr.sbin/pw/cpdir.c
Normal file
118
usr.sbin/pw/cpdir.c
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "pwupd.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
copymkdir(char const * dir, char const * skel, mode_t mode, uid_t uid, gid_t gid)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
char src[MAXPATHLEN];
|
||||||
|
char dst[MAXPATHLEN];
|
||||||
|
|
||||||
|
if (mkdir(dir, mode) != 0 && errno != EEXIST) {
|
||||||
|
sprintf(src, "mkdir(%s)", dir);
|
||||||
|
perror(src);
|
||||||
|
} else {
|
||||||
|
int infd, outfd;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
static char counter = 0;
|
||||||
|
static char *copybuf = NULL;
|
||||||
|
|
||||||
|
++counter;
|
||||||
|
chown(dir, uid, gid);
|
||||||
|
if (skel == NULL || *skel == '\0')
|
||||||
|
rc = 1;
|
||||||
|
else {
|
||||||
|
DIR *d = opendir(skel);
|
||||||
|
|
||||||
|
if (d != NULL) {
|
||||||
|
struct dirent *e;
|
||||||
|
|
||||||
|
while ((e = readdir(d)) != NULL) {
|
||||||
|
char *p = e->d_name;
|
||||||
|
|
||||||
|
sprintf(src, "%s/%s", skel, p);
|
||||||
|
if (stat(src, &st) == 0) {
|
||||||
|
if (strncmp(p, "dot.", 4) == 0) /* Conversion */
|
||||||
|
p += 3;
|
||||||
|
sprintf(dst, "%s/%s", dir, p);
|
||||||
|
if (S_ISDIR(st.st_mode)) { /* Recurse for this */
|
||||||
|
if (strcmp(e->d_name, ".") != 0 && strcmp(e->d_name, "..") != 0)
|
||||||
|
copymkdir(dst, src, (st.st_mode & 0777), uid, gid);
|
||||||
|
/*
|
||||||
|
* Note: don't propogate 'special' attributes
|
||||||
|
*/
|
||||||
|
} else if (S_ISREG(st.st_mode) && (outfd = open(dst, O_RDWR | O_CREAT | O_EXCL, st.st_mode)) != -1) {
|
||||||
|
if ((infd = open(src, O_RDONLY)) == -1) {
|
||||||
|
close(outfd);
|
||||||
|
remove(dst);
|
||||||
|
} else {
|
||||||
|
int b;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate our copy buffer if we need to
|
||||||
|
*/
|
||||||
|
if (copybuf == NULL)
|
||||||
|
copybuf = malloc(4096);
|
||||||
|
while ((b = read(infd, copybuf, 4096)) > 0)
|
||||||
|
write(outfd, copybuf, b);
|
||||||
|
close(infd);
|
||||||
|
close(outfd);
|
||||||
|
chown(dst, uid, gid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (--counter == 0 && copybuf != NULL) {
|
||||||
|
free(copybuf);
|
||||||
|
copybuf = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
206
usr.sbin/pw/edgroup.c
Normal file
206
usr.sbin/pw/edgroup.c
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "pwupd.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
isingroup(char const * name, char **mem)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAXGROUPS && mem[i] != NULL; i++)
|
||||||
|
if (strcmp(name, mem[i]) == 0)
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char groupfile[] = _PATH_GROUP;
|
||||||
|
static char grouptmp[] = _PATH_GROUP ".new";
|
||||||
|
|
||||||
|
int
|
||||||
|
editgroups(char *name, char **groups)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
int infd;
|
||||||
|
|
||||||
|
if ((infd = open(groupfile, O_RDWR | O_CREAT | O_EXLOCK, 0644)) != -1) {
|
||||||
|
FILE *infp;
|
||||||
|
|
||||||
|
if ((infp = fdopen(infd, "r+")) == NULL)
|
||||||
|
close(infd);
|
||||||
|
else {
|
||||||
|
int outfd;
|
||||||
|
|
||||||
|
if ((outfd = open(grouptmp, O_RDWR | O_CREAT | O_TRUNC | O_EXLOCK, 0644)) != -1) {
|
||||||
|
FILE *outfp;
|
||||||
|
|
||||||
|
if ((outfp = fdopen(outfd, "w+")) == NULL)
|
||||||
|
close(outfd);
|
||||||
|
else {
|
||||||
|
char line[MAXPWLINE];
|
||||||
|
char outl[MAXPWLINE];
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), infp) != NULL) {
|
||||||
|
char *p = strchr(line, '\n');
|
||||||
|
|
||||||
|
if (p == NULL) { /* Line too long */
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
fputs(line, outfp);
|
||||||
|
while ((ch = fgetc(infp)) != EOF) {
|
||||||
|
fputc(ch, outfp);
|
||||||
|
if (ch == '\n')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*line == '#')
|
||||||
|
strcpy(outl, line);
|
||||||
|
else if (*line == '\n')
|
||||||
|
*outl = '\0';
|
||||||
|
else {
|
||||||
|
int i,
|
||||||
|
mno = 0;
|
||||||
|
char *cp = line;
|
||||||
|
char const *sep = ":\n";
|
||||||
|
struct group grp;
|
||||||
|
char *mems[MAXGROUPS];
|
||||||
|
|
||||||
|
memset(&grp, 0, sizeof grp);
|
||||||
|
grp.gr_mem = mems;
|
||||||
|
for (i = 0; (p = strsep(&cp, sep)) != NULL; i++) {
|
||||||
|
switch (i) {
|
||||||
|
case 0: /* Group name */
|
||||||
|
grp.gr_name = p;
|
||||||
|
break;
|
||||||
|
case 1: /* Group password */
|
||||||
|
grp.gr_passwd = p;
|
||||||
|
break;
|
||||||
|
case 2: /* Group id */
|
||||||
|
grp.gr_gid = atoi(p);
|
||||||
|
break;
|
||||||
|
case 3: /* Member list */
|
||||||
|
cp = p;
|
||||||
|
sep = ",\n";
|
||||||
|
break;
|
||||||
|
default: /* Individual members */
|
||||||
|
if (mno < MAXGROUPS && *p)
|
||||||
|
mems[mno++] = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i < 2) /* Bail out -
|
||||||
|
* insufficient fields */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (i = mno; i < MAXGROUPS; i++)
|
||||||
|
mems[i] = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delete from group, or add to group?
|
||||||
|
*/
|
||||||
|
if (groups == NULL || isingroup(grp.gr_name, groups) == -1) { /* Delete */
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
while ((idx = isingroup(name, mems)) != -1) {
|
||||||
|
for (i = idx; i < (MAXGROUPS - 1); i++)
|
||||||
|
mems[i] = mems[i + 1];
|
||||||
|
mems[i] = NULL;
|
||||||
|
--mno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Special case - deleting user and group may be user's own
|
||||||
|
*/
|
||||||
|
if (groups == NULL && mems[0] == NULL && strcmp(name, grp.gr_name) == 0) { /* First, make _sure_ we
|
||||||
|
* don't have other
|
||||||
|
* members */
|
||||||
|
struct passwd *pwd;
|
||||||
|
|
||||||
|
setpwent();
|
||||||
|
while ((pwd = getpwent()) != NULL && pwd->pw_gid != grp.gr_gid);
|
||||||
|
endpwent();
|
||||||
|
if (pwd == NULL) /* No members at all */
|
||||||
|
continue; /* Drop the group */
|
||||||
|
}
|
||||||
|
} else if (isingroup(name, mems) == -1)
|
||||||
|
mems[mno++] = name;
|
||||||
|
fmtgrentry(outl, &grp, PWF_GROUP);
|
||||||
|
}
|
||||||
|
fputs(outl, outfp);
|
||||||
|
}
|
||||||
|
if (fflush(outfp) != EOF) {
|
||||||
|
rc = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy data back into the original file and truncate
|
||||||
|
*/
|
||||||
|
rewind(infp);
|
||||||
|
rewind(outfp);
|
||||||
|
while (fgets(line, sizeof(line), outfp) != NULL)
|
||||||
|
fputs(line, infp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a gross hack, but we may have corrupted the
|
||||||
|
* original file. Unfortunately, it will lose preservation
|
||||||
|
* of the inode.
|
||||||
|
*/
|
||||||
|
if (fflush(infp) == EOF || ferror(infp))
|
||||||
|
rc = rename(grouptmp, groupfile) == 0;
|
||||||
|
else
|
||||||
|
ftruncate(infd, ftell(infp));
|
||||||
|
}
|
||||||
|
fclose(outfp);
|
||||||
|
}
|
||||||
|
remove(grouptmp);
|
||||||
|
}
|
||||||
|
fclose(infp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
159
usr.sbin/pw/fileupd.c
Normal file
159
usr.sbin/pw/fileupd.c
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
||||||
|
fileupdate(char const * filename, mode_t fmode, char const * newline, char const * prefix, int pfxlen, int updmode)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (pfxlen <= 1)
|
||||||
|
errno = EINVAL;
|
||||||
|
else {
|
||||||
|
int infd = open(filename, O_RDWR | O_CREAT | O_EXLOCK, fmode);
|
||||||
|
|
||||||
|
if (infd != -1) {
|
||||||
|
FILE *infp = fdopen(infd, "r+");
|
||||||
|
|
||||||
|
if (infp == NULL)
|
||||||
|
close(infd);
|
||||||
|
else {
|
||||||
|
int outfd;
|
||||||
|
char file[MAXPATHLEN];
|
||||||
|
|
||||||
|
strcpy(file, filename);
|
||||||
|
strcat(file, ".new");
|
||||||
|
outfd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_EXLOCK, fmode);
|
||||||
|
if (outfd != -1) {
|
||||||
|
FILE *outfp = fdopen(outfd, "w+");
|
||||||
|
|
||||||
|
if (outfp == NULL)
|
||||||
|
close(outfd);
|
||||||
|
else {
|
||||||
|
int updated = UPD_CREATE;
|
||||||
|
char line[2048];
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), infp) != NULL) {
|
||||||
|
char *p = strchr(line, '\n');
|
||||||
|
|
||||||
|
if (p == NULL) { /* Line too long */
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
fputs(line, outfp);
|
||||||
|
while ((ch = fgetc(infp)) != EOF) {
|
||||||
|
fputc(ch, outfp);
|
||||||
|
if (ch == '\n')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*line != '#' && *line != '\n') {
|
||||||
|
if (!updated && strncmp(line, prefix, pfxlen) == 0) {
|
||||||
|
updated = updmode == UPD_REPLACE ? UPD_REPLACE : UPD_DELETE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only actually write changes if updating
|
||||||
|
*/
|
||||||
|
if (updmode == UPD_REPLACE)
|
||||||
|
strcpy(line, newline);
|
||||||
|
else if (updmode == UPD_DELETE)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fputs(line, outfp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, we need to decide what to do: If we are in
|
||||||
|
* update mode, and no record was updated, then error If
|
||||||
|
* we are in insert mode, and record already exists,
|
||||||
|
* then error
|
||||||
|
*/
|
||||||
|
if (updmode != updated)
|
||||||
|
errno = (updmode == UPD_CREATE) ? EEXIST : ENOENT;
|
||||||
|
else {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If adding a new record, append it to the end
|
||||||
|
*/
|
||||||
|
if (updmode == UPD_CREATE)
|
||||||
|
fputs(newline, outfp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flush the file and check for the result
|
||||||
|
*/
|
||||||
|
rc = fflush(outfp) != EOF;
|
||||||
|
if (rc) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy data back into the
|
||||||
|
* original file and truncate
|
||||||
|
*/
|
||||||
|
rewind(infp);
|
||||||
|
rewind(outfp);
|
||||||
|
while (fgets(line, sizeof(line), outfp) != NULL)
|
||||||
|
fputs(line, infp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a gross hack, but we may have
|
||||||
|
* corrupted the original file
|
||||||
|
* Unfortunately, it will lose the inode.
|
||||||
|
*/
|
||||||
|
if (fflush(infp) == EOF || ferror(infp))
|
||||||
|
rc = rename(file, filename) == 0;
|
||||||
|
else
|
||||||
|
ftruncate(infd, ftell(infp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(outfp);
|
||||||
|
}
|
||||||
|
remove(file);
|
||||||
|
}
|
||||||
|
fclose(infp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
111
usr.sbin/pw/grupd.c
Normal file
111
usr.sbin/pw/grupd.c
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "pwupd.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
fmtgrentry(char *buf, struct group * grp, int type)
|
||||||
|
{
|
||||||
|
int i, l;
|
||||||
|
|
||||||
|
if (type == PWF_STANDARD)
|
||||||
|
l = sprintf(buf, "%s:*:%ld:", grp->gr_name, (long) grp->gr_gid);
|
||||||
|
else
|
||||||
|
l = sprintf(buf, "%s:%s:%ld:", grp->gr_name, grp->gr_passwd, (long) grp->gr_gid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, list members
|
||||||
|
*/
|
||||||
|
for (i = 0; i < 200 && grp->gr_mem[i]; i++)
|
||||||
|
l += sprintf(buf + l, "%s%s", i ? "," : "", grp->gr_mem[i]);
|
||||||
|
buf[l++] = '\n';
|
||||||
|
buf[l] = '\0';
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
fmtgrent(char *buf, struct group * grp)
|
||||||
|
{
|
||||||
|
return fmtgrentry(buf, grp, PWF_STANDARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
gr_update(struct group * grp, char const * group, int mode)
|
||||||
|
{
|
||||||
|
int l;
|
||||||
|
char pfx[32];
|
||||||
|
char grbuf[MAXPWLINE];
|
||||||
|
|
||||||
|
endgrent();
|
||||||
|
l = sprintf(pfx, "%s:", group);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the group file
|
||||||
|
*/
|
||||||
|
if (grp == NULL)
|
||||||
|
*grbuf = '\0';
|
||||||
|
else
|
||||||
|
fmtgrentry(grbuf, grp, PWF_PASSWD);
|
||||||
|
return fileupdate(_PATH_GROUP, 0644, grbuf, pfx, l, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
addgrent(struct group * grp)
|
||||||
|
{
|
||||||
|
return gr_update(grp, grp->gr_name, UPD_CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
chggrent(char const * login, struct group * grp)
|
||||||
|
{
|
||||||
|
return gr_update(grp, login, UPD_REPLACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
delgrent(struct group * grp)
|
||||||
|
{
|
||||||
|
return gr_update(NULL, grp->gr_name, UPD_DELETE);
|
||||||
|
}
|
303
usr.sbin/pw/psdate.c
Normal file
303
usr.sbin/pw/psdate.c
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "psdate.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
a2i(char const ** str)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
char const *s = *str;
|
||||||
|
|
||||||
|
if (isdigit(*s)) {
|
||||||
|
i = atoi(s);
|
||||||
|
while (isdigit(*s))
|
||||||
|
++s;
|
||||||
|
*str = s;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
numerics(char const * str)
|
||||||
|
{
|
||||||
|
int rc = isdigit(*str);
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
while (isdigit(*str) || *str == 'x')
|
||||||
|
++str;
|
||||||
|
return rc && !*str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
aindex(char const * arr[], char const ** str, int len)
|
||||||
|
{
|
||||||
|
int l, i;
|
||||||
|
char mystr[32];
|
||||||
|
|
||||||
|
mystr[len] = '\0';
|
||||||
|
l = strlen(strncpy(mystr, *str, len));
|
||||||
|
for (i = 0; i < l; i++)
|
||||||
|
mystr[i] = (char) tolower(mystr[i]);
|
||||||
|
for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
|
||||||
|
if (arr[i] == NULL)
|
||||||
|
i = -1;
|
||||||
|
else { /* Skip past it */
|
||||||
|
while (**str && isalpha(**str))
|
||||||
|
++(*str);
|
||||||
|
/* And any following whitespace */
|
||||||
|
while (**str && (**str == ',' || isspace(**str)))
|
||||||
|
++(*str);
|
||||||
|
} /* Return index */
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
weekday(char const ** str)
|
||||||
|
{
|
||||||
|
static char const *days[] =
|
||||||
|
{"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
|
||||||
|
|
||||||
|
return aindex(days, str, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
month(char const ** str)
|
||||||
|
{
|
||||||
|
static char const *months[] =
|
||||||
|
{"jan", "feb", "mar", "apr", "may", "jun", "jul",
|
||||||
|
"aug", "sep", "oct", "nov", "dec", NULL};
|
||||||
|
|
||||||
|
return aindex(months, str, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
parse_time(char const * str, int *hour, int *min, int *sec)
|
||||||
|
{
|
||||||
|
*hour = a2i(&str);
|
||||||
|
if ((str = strchr(str, ':')) == NULL)
|
||||||
|
*min = *sec = 0;
|
||||||
|
else {
|
||||||
|
++str;
|
||||||
|
*min = a2i(&str);
|
||||||
|
*sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
parse_datesub(char const * str, int *day, int *mon, int *year)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
static char const nchrs[] = "0123456789 \t,/-.";
|
||||||
|
|
||||||
|
if ((i = month(&str)) != -1) {
|
||||||
|
*mon = i;
|
||||||
|
if ((i = a2i(&str)) != 0)
|
||||||
|
*day = i;
|
||||||
|
} else if ((i = a2i(&str)) != 0) {
|
||||||
|
*day = i;
|
||||||
|
while (*str && strchr(nchrs + 10, *str) != NULL)
|
||||||
|
++str;
|
||||||
|
if ((i = month(&str)) != -1)
|
||||||
|
*mon = i;
|
||||||
|
else if ((i = a2i(&str)) != 0)
|
||||||
|
*mon = i - 1;
|
||||||
|
} else
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (*str && strchr(nchrs + 10, *str) != NULL)
|
||||||
|
++str;
|
||||||
|
if (isdigit(*str)) {
|
||||||
|
*year = atoi(str);
|
||||||
|
if (*year > 1900)
|
||||||
|
*year -= 1900;
|
||||||
|
else if (*year < 32)
|
||||||
|
*year += 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* Parse time must be flexible, it handles the following formats:
|
||||||
|
* nnnnnnnnnnn UNIX timestamp (all numeric), 0 = now
|
||||||
|
* 0xnnnnnnnn UNIX timestamp in hexadecimal
|
||||||
|
* 0nnnnnnnnn UNIX timestamp in octal
|
||||||
|
* 0 Given time
|
||||||
|
* +nnnn[smhdwoy] Given time + nnnn hours, mins, days, weeks, months or years
|
||||||
|
* -nnnn[smhdwoy] Given time - nnnn hours, mins, days, weeks, months or years
|
||||||
|
* dd[ ./-]mmm[ ./-]yy Date }
|
||||||
|
* hh:mm:ss Time } May be combined
|
||||||
|
*/
|
||||||
|
|
||||||
|
time_t
|
||||||
|
parse_date(time_t dt, char const * str)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
int i;
|
||||||
|
long val;
|
||||||
|
struct tm *T;
|
||||||
|
|
||||||
|
if (dt == 0)
|
||||||
|
dt = time(NULL);
|
||||||
|
|
||||||
|
while (*str && isspace(*str))
|
||||||
|
++str;
|
||||||
|
|
||||||
|
if (numerics(str)) {
|
||||||
|
val = strtol(str, &p, 0);
|
||||||
|
dt = val ? val : dt;
|
||||||
|
} else if (*str == '+' || *str == '-') {
|
||||||
|
val = strtol(str, &p, 0);
|
||||||
|
switch (*p) {
|
||||||
|
case 'h':
|
||||||
|
case 'H': /* hours */
|
||||||
|
dt += (val * 3600L);
|
||||||
|
break;
|
||||||
|
case '\0':
|
||||||
|
case 'm':
|
||||||
|
case 'M': /* minutes */
|
||||||
|
dt += (val * 60L);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
case 'S': /* seconds */
|
||||||
|
dt += val;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
case 'D': /* days */
|
||||||
|
dt += (val * 86400L);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
case 'W': /* weeks */
|
||||||
|
dt += (val * 604800L);
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
case 'O': /* months */
|
||||||
|
T = localtime(&dt);
|
||||||
|
T->tm_mon += (int) val;
|
||||||
|
i = T->tm_mday;
|
||||||
|
goto fixday;
|
||||||
|
case 'y':
|
||||||
|
case 'Y': /* years */
|
||||||
|
T = localtime(&dt);
|
||||||
|
T->tm_year += (int) val;
|
||||||
|
i = T->tm_mday;
|
||||||
|
fixday:
|
||||||
|
dt = mktime(T);
|
||||||
|
T = localtime(&dt);
|
||||||
|
if (T->tm_mday != i) {
|
||||||
|
T->tm_mday = 1;
|
||||||
|
dt = mktime(T);
|
||||||
|
dt -= (time_t) 86400L;
|
||||||
|
}
|
||||||
|
default: /* unknown */
|
||||||
|
break; /* leave untouched */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
char *q, tmp[64];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip past any weekday prefix
|
||||||
|
*/
|
||||||
|
weekday(&str);
|
||||||
|
str = strncpy(tmp, str, sizeof tmp - 1);
|
||||||
|
tmp[sizeof tmp - 1] = '\0';
|
||||||
|
T = localtime(&dt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See if we can break off any timezone
|
||||||
|
*/
|
||||||
|
while ((q = strrchr(tmp, ' ')) != NULL) {
|
||||||
|
if (strchr("(+-", q[1]) != NULL)
|
||||||
|
*q = '\0';
|
||||||
|
else {
|
||||||
|
int j = 1;
|
||||||
|
|
||||||
|
while (q[j] && isupper(q[j]))
|
||||||
|
++j;
|
||||||
|
if (q[j] == '\0')
|
||||||
|
*q = '\0';
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See if there is a time hh:mm[:ss]
|
||||||
|
*/
|
||||||
|
if ((p = strchr(tmp, ':')) == NULL) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No time string involved
|
||||||
|
*/
|
||||||
|
T->tm_hour = T->tm_min = T->tm_sec = 0;
|
||||||
|
parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year);
|
||||||
|
} else {
|
||||||
|
char datestr[64], timestr[64];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Let's chip off the time string
|
||||||
|
*/
|
||||||
|
if ((q = strpbrk(p, " \t")) != NULL) { /* Time first? */
|
||||||
|
int l = q - str;
|
||||||
|
|
||||||
|
strncpy(timestr, str, l);
|
||||||
|
timestr[l] = '\0';
|
||||||
|
strncpy(datestr, q + 1, sizeof datestr);
|
||||||
|
datestr[sizeof datestr - 1] = '\0';
|
||||||
|
parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
|
||||||
|
parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
|
||||||
|
} else if ((q = strrchr(tmp, ' ')) != NULL) { /* Time last */
|
||||||
|
int l = q - tmp;
|
||||||
|
|
||||||
|
strncpy(timestr, q + 1, sizeof timestr);
|
||||||
|
timestr[sizeof timestr - 1] = '\0';
|
||||||
|
strncpy(datestr, tmp, l);
|
||||||
|
datestr[l] = '\0';
|
||||||
|
} else /* Bail out */
|
||||||
|
return dt;
|
||||||
|
parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
|
||||||
|
parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
|
||||||
|
}
|
||||||
|
dt = mktime(T);
|
||||||
|
}
|
||||||
|
return dt;
|
||||||
|
}
|
46
usr.sbin/pw/psdate.h
Normal file
46
usr.sbin/pw/psdate.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PSDATE_H_
|
||||||
|
#define _PSDATE_H_
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
|
||||||
|
__BEGIN_DECLS
|
||||||
|
time_t parse_date __P((time_t dt, char const * str));
|
||||||
|
void print_date __P((char *buf, time_t t, int dotime));
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
|
#endif /* !_PSDATE_H_ */
|
648
usr.sbin/pw/pw.8
Normal file
648
usr.sbin/pw/pw.8
Normal file
@ -0,0 +1,648 @@
|
|||||||
|
.\" Copyright (c) 1996
|
||||||
|
.\" David L. Nugent.
|
||||||
|
.\" Password Maintenance
|
||||||
|
.\"
|
||||||
|
.\" $Id: pw.8,v 1.3 1996/11/18 03:09:01 davidn Exp $
|
||||||
|
.\"
|
||||||
|
.Dd November 13, 1996
|
||||||
|
.Dt PW 8
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm pw
|
||||||
|
.Nd create, remove and modify system users and groups
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm pw
|
||||||
|
.Ar useradd
|
||||||
|
.Op name|uid
|
||||||
|
.Op Fl C Ar config
|
||||||
|
.Op Fl q
|
||||||
|
.Op Fl n Ar name
|
||||||
|
.Op Fl u Ar uid
|
||||||
|
.Op Fl c Ar comment
|
||||||
|
.Op Fl d Ar dir
|
||||||
|
.Op Fl e Ar date
|
||||||
|
.Op Fl p Ar date
|
||||||
|
.Op Fl g Ar group
|
||||||
|
.Op Fl G Ar grouplist
|
||||||
|
.Op Fl m
|
||||||
|
.Op Fl k Ar dir
|
||||||
|
.Op Fl s Ar shell
|
||||||
|
.Op Fl o
|
||||||
|
.Op Fl L Ar class
|
||||||
|
.Op Fl h Ar fd
|
||||||
|
.Nm pw
|
||||||
|
.Ar useradd
|
||||||
|
.Op name|uid
|
||||||
|
.Op Fl D
|
||||||
|
.Op Fl C Ar config
|
||||||
|
.Op Fl q
|
||||||
|
.Op Fl b Ar dir
|
||||||
|
.Op Fl e Ar days
|
||||||
|
.Op Fl p Ar days
|
||||||
|
.Op Fl g Ar group
|
||||||
|
.Op Fl G Ar grouplist
|
||||||
|
.Op Fl k Ar dir
|
||||||
|
.Op Fl u Ar min,max
|
||||||
|
.Op Fl i Ar min,max
|
||||||
|
.Op Fl w Ar method
|
||||||
|
.Op Fl s Ar shell
|
||||||
|
.Nm pw
|
||||||
|
.Ar userdel
|
||||||
|
.Op name|uid
|
||||||
|
.Op Fl n Ar name
|
||||||
|
.Op Fl u Ar uid
|
||||||
|
.Op Fl r
|
||||||
|
.Nm pw
|
||||||
|
.Ar usermod
|
||||||
|
.Op name|uid
|
||||||
|
.Op Fl C Ar config
|
||||||
|
.Op Fl q
|
||||||
|
.Op Fl n Ar name
|
||||||
|
.Op Fl u Ar uid
|
||||||
|
.Op Fl c Ar comment
|
||||||
|
.Op Fl d Ar dir
|
||||||
|
.Op Fl e Ar date
|
||||||
|
.Op Fl p Ar date
|
||||||
|
.Op Fl g Ar group
|
||||||
|
.Op Fl G Ar grouplist
|
||||||
|
.Op Fl l Ar name
|
||||||
|
.Op Fl m
|
||||||
|
.Op Fl k Ar dir
|
||||||
|
.Op Fl s Ar shell
|
||||||
|
.Op Fl L Ar class
|
||||||
|
.Op Fl h Ar fd
|
||||||
|
.Nm pw
|
||||||
|
.Ar usershow
|
||||||
|
.Op name|uid
|
||||||
|
.Op Fl n Ar name
|
||||||
|
.Op Fl u Ar uid
|
||||||
|
.Op Fl F
|
||||||
|
.Op Fl p
|
||||||
|
.Op Fl a
|
||||||
|
.Nm pw
|
||||||
|
.Ar groupadd
|
||||||
|
.Op group|gid
|
||||||
|
.Op Fl C Ar config
|
||||||
|
.Op Fl q
|
||||||
|
.Op Fl n Ar group
|
||||||
|
.Op Fl g Ar gid
|
||||||
|
.Op Fl o
|
||||||
|
.Op Fl h Ar fd
|
||||||
|
.Nm pw
|
||||||
|
.Ar groupdel
|
||||||
|
.Op Fl n Ar name
|
||||||
|
.Op Fl g Ar gid
|
||||||
|
.Nm pw
|
||||||
|
.Ar groupmod
|
||||||
|
.Op Fl C Ar config
|
||||||
|
.Op Fl q
|
||||||
|
.Op Fl F
|
||||||
|
.Op Fl n Ar name
|
||||||
|
.Op Fl g Ar gid
|
||||||
|
.Op Fl l Ar name
|
||||||
|
.Op Fl h Ar fd
|
||||||
|
.Nm pw
|
||||||
|
.Ar groupshow
|
||||||
|
.Op Fl n Ar name
|
||||||
|
.Op Fl g Ar gid
|
||||||
|
.Op Fl F
|
||||||
|
.Op Fl p
|
||||||
|
.Op Fl a
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm pw
|
||||||
|
is a command-line based editor for the system
|
||||||
|
.Em user
|
||||||
|
and
|
||||||
|
.Em group
|
||||||
|
files, allowing the superuser and easy to use and standardised way of adding,
|
||||||
|
modifying and removing users and groups.
|
||||||
|
Note that
|
||||||
|
.Nm pw
|
||||||
|
only operates on the local user and group files; NIS users and groups must be
|
||||||
|
maintained on the NIS server.
|
||||||
|
.Nm pw
|
||||||
|
handles updating the passwd, master.passwd, group and the secure and insecure
|
||||||
|
password database files, and must be run as root.
|
||||||
|
.Pp
|
||||||
|
The first one or two keywords provided on
|
||||||
|
.Xr pw 8 's
|
||||||
|
command line provide the context for the remainder of the arguments.
|
||||||
|
One of the keywords
|
||||||
|
.Ar user
|
||||||
|
and
|
||||||
|
.Ar group
|
||||||
|
may be combined or provided separately with
|
||||||
|
.Ar add ,
|
||||||
|
.Ar del ,
|
||||||
|
.Ar mod
|
||||||
|
or
|
||||||
|
.Ar show ,
|
||||||
|
and may be specified in either order (ie. showuser, usershow, show user and user show
|
||||||
|
are all considered to be the same thing).
|
||||||
|
This flexiblity is useful for interactive scripts which call
|
||||||
|
.Nm pw
|
||||||
|
for the actual user and group database manipulation.
|
||||||
|
Following these keywords, you may optionally specify the user or group name or numeric
|
||||||
|
id as an alternative to using the
|
||||||
|
.Fl n Ar name ,
|
||||||
|
.Fl u Ar uid ,
|
||||||
|
.Fl g Ar gid
|
||||||
|
switches.
|
||||||
|
.Pp
|
||||||
|
The following flags are common to most modes of operation:
|
||||||
|
.Pp
|
||||||
|
.Bl -tag -width "-C config"
|
||||||
|
.It Fl C Ar config
|
||||||
|
By default,
|
||||||
|
.Nm pw
|
||||||
|
reads the file
|
||||||
|
.Pa /etc/pw.conf
|
||||||
|
to obtain policy information on how new user accounts and groups are to be created,
|
||||||
|
and the
|
||||||
|
.Fl c
|
||||||
|
option overrides this to read a different file.
|
||||||
|
Most of the contents in the configuration file may be overridden via command line
|
||||||
|
options, but it may be more useful to set up standard information for addition of
|
||||||
|
new accounts in the configuration
|
||||||
|
file.
|
||||||
|
.It Fl q
|
||||||
|
Use of this option causes
|
||||||
|
.Nm pw
|
||||||
|
to suppress error messages, which may be useful in interactive environments where it
|
||||||
|
is preferable to interpret status codes returned by
|
||||||
|
.Nm pw
|
||||||
|
rather than messing up a carefully formatted display.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
.Sh USER OPTIONS
|
||||||
|
The following options apply to the
|
||||||
|
.Ar useradd ,
|
||||||
|
and
|
||||||
|
.Ar usermod ,
|
||||||
|
commands:
|
||||||
|
.Pp
|
||||||
|
.Bl -tag -width "-C config"
|
||||||
|
.It Fl n Ar name
|
||||||
|
Specifies the user/account name.
|
||||||
|
.It Fl u Ar uid
|
||||||
|
Specifies the user/account numeric id.
|
||||||
|
.Pp
|
||||||
|
Usually, you need only to provide one or the other of these options, as the account
|
||||||
|
name will imply the uid, and vice verca.
|
||||||
|
Also, you may provide either the account or userid immediately after the
|
||||||
|
.Ar useradd ,
|
||||||
|
.Ar userdel ,
|
||||||
|
.Ar usermod
|
||||||
|
or
|
||||||
|
.Ar usershow
|
||||||
|
keyword on the command line without the need to use
|
||||||
|
.Ql Fl n
|
||||||
|
or
|
||||||
|
.Ql Fl u .
|
||||||
|
There are times, however, were you need to provide both.
|
||||||
|
For example, when changing the uid of an existing user with
|
||||||
|
.Ar usermod ,
|
||||||
|
or overriding the default uid when creating a new account.
|
||||||
|
If you wish
|
||||||
|
.Nm pw
|
||||||
|
to automatically allocate the uid to a new user on
|
||||||
|
.Ar useradd ,
|
||||||
|
then you should
|
||||||
|
.Em not
|
||||||
|
use the
|
||||||
|
.Ql Fl u
|
||||||
|
switch.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Options available with both
|
||||||
|
.Ar useradd
|
||||||
|
and
|
||||||
|
.Ar usermod
|
||||||
|
are:
|
||||||
|
.Bl -tag -width "-G grouplist"
|
||||||
|
.It Fl c Ar comment
|
||||||
|
This field sets the contents of the passwd GECOS field, which normally contains up
|
||||||
|
to four comma-separated fields containing the user's full name, office or location,
|
||||||
|
work and home phone numbers.
|
||||||
|
These sub-fields are used by convention only, however, and are optional.
|
||||||
|
If this field is to contain spaces, you need to quote the comment itself with double
|
||||||
|
quotes
|
||||||
|
.Ql \&" .
|
||||||
|
Avoid using commas in this field as these are used as sub-field separators, and the
|
||||||
|
colon
|
||||||
|
.Ql \&:
|
||||||
|
character also cannot be used as this is the field separator in the passwd file.
|
||||||
|
.It Fl d Ar dir
|
||||||
|
This option sets the account's home directory.
|
||||||
|
Normally, you will only use this if the home directory is to be different from the
|
||||||
|
default (which is determined from pw.conf, which specifies the base home directory
|
||||||
|
- normally /home - with the account name as a subdirectory).
|
||||||
|
.It Fl e Ar date
|
||||||
|
Sets the account's expiration date.
|
||||||
|
Format of the date is either a UNIX time in decimal, or a date in
|
||||||
|
.Ql \& dd-mmm-yy[yy]
|
||||||
|
format, where dd is the day, mmm is the month, either in numeric or alphabetic format
|
||||||
|
('Jan', 'Feb' etc) and year is either a two or four digit year.
|
||||||
|
This option also accepts a relative date in the form
|
||||||
|
.Ql \&+n[mhdwoy]
|
||||||
|
where
|
||||||
|
.Ql \&n
|
||||||
|
is a decimal, octal (leading 0) or hexadecimal (leading 0x) digit followed by the
|
||||||
|
number of Minutes, Hours, Days, Weeks, mOnths or Years from the current date at
|
||||||
|
which the expiry date is to be set.
|
||||||
|
.It Fl p Ar date
|
||||||
|
Sets the account's password expiration date.
|
||||||
|
This field is identical to the account expiration date option, except that it
|
||||||
|
applies to forced password changes.
|
||||||
|
The same formats are accepted as with the account expiratino option.
|
||||||
|
.It Fl g Ar group
|
||||||
|
Sets the account's primary group to the given group.
|
||||||
|
.Ar group
|
||||||
|
may be either the group name or its corresponding group id number.
|
||||||
|
.It Fl G Ar grouplist
|
||||||
|
Sets the additional groups to which an account belongs.
|
||||||
|
.Ar grouplist
|
||||||
|
is a comma-separated list or group names or group ids.
|
||||||
|
When adding a user, the user's name is added to the group lists in
|
||||||
|
.Pa /etc/group ,
|
||||||
|
and when editing a user, the user's name is also added to the group lists, and
|
||||||
|
removed from any groups not specified in
|
||||||
|
.Ar grouplist .
|
||||||
|
Note: a user should not be added to their primary group in
|
||||||
|
.Pa /etc/group .
|
||||||
|
Also, group membership changes do not take effect immediately for current logins,
|
||||||
|
only logins subsequent to the change.
|
||||||
|
.It Fl m
|
||||||
|
This option instructs
|
||||||
|
.Nm pw
|
||||||
|
to attempt to create the user's home directory.
|
||||||
|
While primarily useful when adding a new account with
|
||||||
|
.Ar useradd ,
|
||||||
|
this may also be of use when moving an existing user's home directory elsewhere on
|
||||||
|
the filesystem.
|
||||||
|
The new home directory is populated with the contents of the
|
||||||
|
.Ar skeleton
|
||||||
|
directory, which typically contains a set of shell configuration files that the
|
||||||
|
user may personalise to taste.
|
||||||
|
When
|
||||||
|
.Ql Fl m
|
||||||
|
is used on an account with
|
||||||
|
.Ar usermod ,
|
||||||
|
any existing configuration files in the user's home directory are
|
||||||
|
.Em not
|
||||||
|
overwritten with the prototype files.
|
||||||
|
.Pp
|
||||||
|
When a user's home directory is created, it will be default be as a subdirectory of the
|
||||||
|
.Ar basehome
|
||||||
|
directory specified with the
|
||||||
|
.Ql Fl b Ar dir
|
||||||
|
option (see below), and will be named the same as the account.
|
||||||
|
This may be overridden with the
|
||||||
|
.Ql Fl d Ar dir
|
||||||
|
option on the command line, if desired.
|
||||||
|
.It Fl k Ar dir
|
||||||
|
Sets the
|
||||||
|
.Ar skeleton
|
||||||
|
subdirectory, from which the basic startup and configuration files are copied when
|
||||||
|
the user's home directory is created.
|
||||||
|
This option only has meaning when used with
|
||||||
|
.Ql Fl D
|
||||||
|
(see below) or
|
||||||
|
.Ql Fl m .
|
||||||
|
.It Fl s Ar shell
|
||||||
|
Sets or changes the user's login shell to
|
||||||
|
.Ar shell .
|
||||||
|
If the path to the shell program is omitted,
|
||||||
|
.Nm pw
|
||||||
|
searches the
|
||||||
|
.Ar shellpath
|
||||||
|
specified in
|
||||||
|
.Pa /etc/pw.conf
|
||||||
|
and fills it in as appropriate.
|
||||||
|
Note that unless you have a specific reason to do so, you should avoid
|
||||||
|
specifying the path - this will allow
|
||||||
|
.Nm pw
|
||||||
|
to validate that the program exists and is executable.
|
||||||
|
Specifying a full path (or supplying a blank "" shell) avoids this check
|
||||||
|
and allows for such entries as
|
||||||
|
.Ql \& /nonexistent
|
||||||
|
that should be set for accounts not intended for interactive login.
|
||||||
|
.It Fl L Ar class
|
||||||
|
Sets the
|
||||||
|
.Em class
|
||||||
|
field in the user's passwd record.
|
||||||
|
This field is not currently used, but will be in the future used to specify a
|
||||||
|
.Em termcap
|
||||||
|
entry like tag (see
|
||||||
|
.Xr passwd 5
|
||||||
|
for details).
|
||||||
|
.It Fl h Ar fd
|
||||||
|
This option provides a special interface by which interactive scripts can
|
||||||
|
set an account password using
|
||||||
|
.Nm pw .
|
||||||
|
Because the command line and environment are fundamental insecure mechanisms
|
||||||
|
by which programs can accept information,
|
||||||
|
.Nm pw
|
||||||
|
will only allow setting of account and group passwords via a file descriptor
|
||||||
|
(usually a pipe between an interactive script and the program).
|
||||||
|
.Ar sh ,
|
||||||
|
.Ar bash ,
|
||||||
|
.Ar ksh
|
||||||
|
and
|
||||||
|
.Ar perl
|
||||||
|
all posses mechanisms by which this can be done.
|
||||||
|
Alternatively,
|
||||||
|
.Nm pw
|
||||||
|
will prompt for the user's password if
|
||||||
|
.Ql Fl h Ar 0
|
||||||
|
is given, nominating
|
||||||
|
.Em stdin
|
||||||
|
as the file descriptor on which to read the password.
|
||||||
|
Note that this password will be read once and once only and is intended
|
||||||
|
for use by a script or similar rather than interactive use.
|
||||||
|
If you wish to have new password confirmation along the lines of
|
||||||
|
.Xr passwd 1 ,
|
||||||
|
this must be implemented as part of the interactive script that calls
|
||||||
|
.Nm pw .
|
||||||
|
.Pp
|
||||||
|
If a value of
|
||||||
|
.Ql \&-
|
||||||
|
is given as the argument
|
||||||
|
.Ar fd ,
|
||||||
|
then the password will be set to
|
||||||
|
.Ql \&* ,
|
||||||
|
rendering the account inaccessible via passworded login.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
It is possible to use
|
||||||
|
.Ar useradd
|
||||||
|
to create a new account that duplicates an existing user id.
|
||||||
|
While this is normally considered an error and will be rejected, the
|
||||||
|
.Ql Fl o
|
||||||
|
switch overrides the check for duplicates and allows the duplication of the user id.
|
||||||
|
This may be useful if you allow the same user to login under different contexts
|
||||||
|
(different group allocations, different home directory, different shell) while
|
||||||
|
providing basically the same permissions for access to the user's files in each
|
||||||
|
account.
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar useradd
|
||||||
|
command also has the ability to set new user and group defaults by using the
|
||||||
|
.Ql Fl D
|
||||||
|
switch.
|
||||||
|
Instead of adding a new user,
|
||||||
|
.Nm pw
|
||||||
|
writes a new set of defaults to its configuration file,
|
||||||
|
.Pa /etc/pw.conf .
|
||||||
|
When using the
|
||||||
|
.Ql Fl D
|
||||||
|
switch, you must not use either
|
||||||
|
.Ql Fl n Ar name
|
||||||
|
or
|
||||||
|
.Ql Fl u Ar uid
|
||||||
|
or an error will result.
|
||||||
|
Use of
|
||||||
|
.Ql Fl D
|
||||||
|
adds switches and changes the meaning of several command line switches in the
|
||||||
|
.Ar useradd
|
||||||
|
command.
|
||||||
|
These are:
|
||||||
|
.Bl -tag -width "-G grouplist"
|
||||||
|
.It Fl D
|
||||||
|
Set default values in
|
||||||
|
.Pa /etc/pw.conf
|
||||||
|
configuration file, or a different named configuration file if the
|
||||||
|
.Ql Fl C Ar config
|
||||||
|
switch is used.
|
||||||
|
.It Fl b Ar dir
|
||||||
|
Sets the root directory in which user home directories are created.
|
||||||
|
The default value for this is
|
||||||
|
.Ql \&/home ,
|
||||||
|
but it may be set elsewhere as desired.
|
||||||
|
.It Fl e Ar days
|
||||||
|
Sets the default account expiration period in days.
|
||||||
|
Unlike use without
|
||||||
|
.Ql Fl D ,
|
||||||
|
the argument must be numeric, which specifies the number of days after creation when
|
||||||
|
the account is to expire.
|
||||||
|
A value of 0 suppresses automatic calculation of the expiry date.
|
||||||
|
.It Fl p Ar days
|
||||||
|
Sets the default password expiration period in days.
|
||||||
|
.It Fl g Ar group
|
||||||
|
Sets the default group for new users.
|
||||||
|
If a blank group is specified using
|
||||||
|
.Ql Fl g Ar \&"" ,
|
||||||
|
then new users will be allocated their own private primary group (a new group created
|
||||||
|
with the same name as their login name).
|
||||||
|
If a group is supplied, either its name or uid may be given as an argument.
|
||||||
|
.It Fl G Ar grouplist
|
||||||
|
Sets the default groups in which new users are made members.
|
||||||
|
This is a separate set of groups from the primary group, and you should avoid
|
||||||
|
nominating the same group as both the primary and in extra groups.
|
||||||
|
In other words, these extra groups determine membership in groups
|
||||||
|
.Em other than
|
||||||
|
the primary group.
|
||||||
|
.Ar grouplist
|
||||||
|
is a comma-separated list of group names or ids, or a mixture of both, and are always
|
||||||
|
stored in
|
||||||
|
.Pa /etc/pw.conf
|
||||||
|
by their symbolic names.
|
||||||
|
.It Fl k Ar dir
|
||||||
|
Sets the default
|
||||||
|
.Em skeleton
|
||||||
|
directory, from which prototype shell and other initialisation files are copied when
|
||||||
|
.Nm pw
|
||||||
|
creates a user's home directory.
|
||||||
|
.It Fl u Ar min,max
|
||||||
|
.It Fl i Ar min,max
|
||||||
|
These switches set the minimum and maximum user and group ids allocated for new accounts
|
||||||
|
and groups created by
|
||||||
|
.Nm pw .
|
||||||
|
The default values for each is 1000 minimum and 32000 maximum.
|
||||||
|
.Ar min
|
||||||
|
and
|
||||||
|
.Ar max
|
||||||
|
are both numbers, where max must be greater than min, and both must be between 0
|
||||||
|
and 32767.
|
||||||
|
In general, user and group ids less than 100 are reserved for use by the system,
|
||||||
|
and numbers greater than 32000 may also be reserved for special purposes (used by
|
||||||
|
some system daemons).
|
||||||
|
.It Fl w Ar method
|
||||||
|
The
|
||||||
|
.Ql Fl w
|
||||||
|
switch sets the default method used to set passwords for newly created user accounts.
|
||||||
|
.Ar method
|
||||||
|
is one of:
|
||||||
|
.Pp
|
||||||
|
.Bl -tag -width random -offset indent -compact
|
||||||
|
.It no
|
||||||
|
disables login on newly created accounts
|
||||||
|
.It yes
|
||||||
|
forces the password to be the account name
|
||||||
|
.It none
|
||||||
|
forces a blank password
|
||||||
|
.It random
|
||||||
|
Generates a random password
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ql \&random
|
||||||
|
or
|
||||||
|
.Ql \&no
|
||||||
|
methods are the most secure; in the former case,
|
||||||
|
.Nm pw
|
||||||
|
generates a password and prints it to stdout, which is suitable where you issue
|
||||||
|
users with passwords to access their accounts rather than having the user nominate
|
||||||
|
their own (possibly poorly chosen) password.
|
||||||
|
The
|
||||||
|
.Ql \&no
|
||||||
|
method requires that the superuser use
|
||||||
|
.Xr passwd 1
|
||||||
|
to render the account accessible with a password.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar userdel
|
||||||
|
command has only three valid switches. The
|
||||||
|
.Ql Fl n Ar name
|
||||||
|
and
|
||||||
|
.Ql Fl u Ar uid
|
||||||
|
switches have already been covered above.
|
||||||
|
The additional switch is:
|
||||||
|
.Bl -tag -width flag
|
||||||
|
.It Fl r
|
||||||
|
This tells
|
||||||
|
.Nm pw
|
||||||
|
to remove the user's home directory and all of its contents.
|
||||||
|
.Nm pw
|
||||||
|
errs on the side of caution when removing files from the system.
|
||||||
|
Firstly, it will not do so if the uid of the account being removed is also used by
|
||||||
|
another account on the system, and the 'home' directory in the password file is
|
||||||
|
a valid path that commences with the character
|
||||||
|
.Ql \&/ .
|
||||||
|
Secondly, it will only remove files and directories that are actually owned by
|
||||||
|
the user, or symbolic links owned by anyone under the user's home directory.
|
||||||
|
Finally, after deleting all contents owned by the user only empty directories
|
||||||
|
will be removed.
|
||||||
|
If any additional cleanup work is required, this is left to the adminstrator.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Mail spool files and crontabs are always removed when an account is deleted as these
|
||||||
|
are unconditionally attached to the user name.
|
||||||
|
Jobs queued for processing by
|
||||||
|
.Ar at
|
||||||
|
are also removed if the user's uid is unique (not also used by another account on the
|
||||||
|
system).
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar usershow
|
||||||
|
command allows viewing of an account in one of two formats.
|
||||||
|
By default, the format is identical to the format used in
|
||||||
|
.Pa /etc/master.passwd
|
||||||
|
with the password field replaced with a
|
||||||
|
.Ql \&* .
|
||||||
|
Class, account and password expiration fields will be blank or zero zero unless the user
|
||||||
|
running
|
||||||
|
.Nm pw
|
||||||
|
has root priviledges, as the secure password file where these reside is not accessible
|
||||||
|
to non-root users.
|
||||||
|
If the
|
||||||
|
.Ql Fl p
|
||||||
|
switch is used, then
|
||||||
|
.Nm pw
|
||||||
|
outputs the account details in a more human readable form.
|
||||||
|
The
|
||||||
|
.Ql Fl a
|
||||||
|
switch lists all users currently on file.
|
||||||
|
.Pp
|
||||||
|
.Sh GROUP OPTIONS
|
||||||
|
The
|
||||||
|
.Ql Fl C Ar config
|
||||||
|
and
|
||||||
|
.Ql Fl q
|
||||||
|
options (explained at the start of the previous section) are available with the
|
||||||
|
.Ar groupadd
|
||||||
|
and
|
||||||
|
.Ar groupmod
|
||||||
|
commands.
|
||||||
|
Other common options to all group-related commands are:
|
||||||
|
.Bl -tag -width "-n name"
|
||||||
|
.It Fl n Ar name
|
||||||
|
Specifies the group name.
|
||||||
|
.It Fl g Ar gid
|
||||||
|
Specifies the group numeric id.
|
||||||
|
.Pp
|
||||||
|
As with the account name and id fields, yo uwill usually only need to supply one of
|
||||||
|
these, as the group name implies the uid and vice versa.
|
||||||
|
You will only need to use both when setting a specific group id against a new group
|
||||||
|
or when changing the uid of an existing group.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
.Ar groupadd
|
||||||
|
also has a
|
||||||
|
.Ql Fl o
|
||||||
|
option that allows allocation of an existing group id to new group.
|
||||||
|
The default action is to reject an attempt to add a group, and this option overrides
|
||||||
|
the check for duplicate group ids.
|
||||||
|
There is rarely any need to duplicate a group id.
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar groupmod
|
||||||
|
command adds one additonal switch:
|
||||||
|
.Pp
|
||||||
|
.Bl -tag -width "-l name"
|
||||||
|
.It Fl l Ar name
|
||||||
|
This option allows changing of an existing group name to
|
||||||
|
.Ql \&name .
|
||||||
|
The new name must not already exist, and any attempt to duplicate an existing group
|
||||||
|
name will be rejected.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Options for
|
||||||
|
.Ar groupshow
|
||||||
|
are the same as for
|
||||||
|
.Ar usershow ,
|
||||||
|
with the
|
||||||
|
.Ql Fl g Ar gid
|
||||||
|
replacing
|
||||||
|
.Ql Fl u Ar uid
|
||||||
|
to specify the group id.
|
||||||
|
.Pp
|
||||||
|
.Sh NOTES
|
||||||
|
For a summary of options available with each command, you can use
|
||||||
|
.Dl pw [command] help
|
||||||
|
For example,
|
||||||
|
.Dl pw useradd help
|
||||||
|
lists all available options for the useradd operation.
|
||||||
|
.Sh FILES
|
||||||
|
.Bl -tag -width /etc/master.passwd.new -compact
|
||||||
|
.It Pa /etc/master.passwd
|
||||||
|
The user database
|
||||||
|
.It Pa /etc/passwd
|
||||||
|
A Version 7 format password file
|
||||||
|
.It Pa /etc/group
|
||||||
|
The group database
|
||||||
|
.It Pa /etc/master.passwd.new
|
||||||
|
Temporary copy of the master password file
|
||||||
|
.It Pa /etc/passwd.new
|
||||||
|
Temporary copy of the Version 7 password file
|
||||||
|
.It Pa /etc/group.new
|
||||||
|
Temporary copy of the group file
|
||||||
|
.It Pa /etc/pw.conf
|
||||||
|
Pw default options file
|
||||||
|
.El
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr pw.conf 5 ,
|
||||||
|
.Xr passwd 1 ,
|
||||||
|
.Xr chpass 1 ,
|
||||||
|
.Xr passwd 5 ,
|
||||||
|
.Xr group 5 ,
|
||||||
|
.Xr pwd_mkdb 8 ,
|
||||||
|
.Xr vipw 5
|
||||||
|
.Sh HISTORY
|
||||||
|
.Nm pw
|
||||||
|
was written to mimick many of the options used in the Linux
|
||||||
|
.Em shadow
|
||||||
|
suite, but is modified for passwd and group fields specific to
|
||||||
|
the BSD 4.4 operating system.
|
||||||
|
|
321
usr.sbin/pw/pw.c
Normal file
321
usr.sbin/pw/pw.c
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pw.h"
|
||||||
|
|
||||||
|
static char *progname = "pw";
|
||||||
|
|
||||||
|
const char *Modes[] = {"add", "del", "mod", "show", NULL};
|
||||||
|
const char *Which[] = {"user", "group", NULL};
|
||||||
|
static const char *Combo1[] = {"useradd", "userdel", "usermod", "usershow",
|
||||||
|
"groupadd", "groupdel", "groupmod", "groupshow",
|
||||||
|
NULL};
|
||||||
|
static const char *Combo2[] = {"adduser", "deluser", "moduser", "showuser",
|
||||||
|
"addgroup", "delgroup", "modgroup", "showgroup",
|
||||||
|
NULL};
|
||||||
|
|
||||||
|
static struct cargs arglist;
|
||||||
|
|
||||||
|
static int getindex(const char *words[], const char *word);
|
||||||
|
static void cmdhelp(int mode, int which);
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int ch;
|
||||||
|
int mode = -1;
|
||||||
|
int which = -1;
|
||||||
|
struct userconf *cnf;
|
||||||
|
|
||||||
|
static const char *opts[W_NUM][M_NUM] =
|
||||||
|
{
|
||||||
|
/* user */ {"C:qn:u:c:d:e:p:g:G:mk:s:oL:i:w:h:Db", "C:qn:u:r", "C:qn:u:c:d:e:p:g:G:mk:s:L:h:F", "C:qn:u:Fpa"},
|
||||||
|
/* grp */ {"C:qn:g:h:p", "C:qn:g:", "C:qn:g:l:h:F", "C:qn:g:Fpa"}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int (*funcs[W_NUM]) (struct userconf * _cnf, int _mode, struct cargs * _args) =
|
||||||
|
{ /* Request handlers */
|
||||||
|
pw_user,
|
||||||
|
pw_group
|
||||||
|
};
|
||||||
|
|
||||||
|
umask(0); /* We wish to handle this manually */
|
||||||
|
progname = strrchr(argv[0], '/');
|
||||||
|
if (progname != NULL)
|
||||||
|
++progname;
|
||||||
|
else
|
||||||
|
progname = argv[0];
|
||||||
|
|
||||||
|
LIST_INIT(&arglist);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Break off the first couple of words to determine what exactly
|
||||||
|
* we're being asked to do
|
||||||
|
*/
|
||||||
|
while (argc > 1 && *argv[1] != '-') {
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
if ((tmp = getindex(Modes, argv[1])) != -1)
|
||||||
|
mode = tmp;
|
||||||
|
else if ((tmp = getindex(Which, argv[1])) != -1)
|
||||||
|
which = tmp;
|
||||||
|
else if ((tmp = getindex(Combo1, argv[1])) != -1 || (tmp = getindex(Combo2, argv[1])) != -1) {
|
||||||
|
which = tmp / M_NUM;
|
||||||
|
mode = tmp % M_NUM;
|
||||||
|
} else if (strcmp(argv[1], "help") == 0)
|
||||||
|
cmdhelp(mode, which);
|
||||||
|
else if (which != -1 && mode != -1 && arglist.lh_first == NULL)
|
||||||
|
addarg(&arglist, 'n', argv[1]);
|
||||||
|
else
|
||||||
|
cmderr(X_CMDERR, "Unknown keyword `%s'\n", argv[1]);
|
||||||
|
++argv;
|
||||||
|
--argc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bail out unless the user is specific!
|
||||||
|
*/
|
||||||
|
if (mode == -1 || which == -1)
|
||||||
|
cmdhelp(mode, which);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must be root to attempt an update
|
||||||
|
*/
|
||||||
|
if (getuid() != 0 && mode != M_PRINT)
|
||||||
|
cmderr(X_PERMERR, "you must be root to run this program\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We know which mode we're in and what we're about to do, so now
|
||||||
|
* let's dispatch the remaining command line args in a genric way.
|
||||||
|
*/
|
||||||
|
argv[0] = progname; /* Preserve this */
|
||||||
|
optarg = NULL;
|
||||||
|
|
||||||
|
while ((ch = getopt(argc, argv, opts[which][mode])) != -1) {
|
||||||
|
if (ch == '?')
|
||||||
|
cmderr(X_CMDERR, NULL);
|
||||||
|
else
|
||||||
|
addarg(&arglist, ch, optarg);
|
||||||
|
optarg = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We should immediately look for the -q 'quiet' switch so that we
|
||||||
|
* don't bother with extraneous errors
|
||||||
|
*/
|
||||||
|
if (getarg(&arglist, 'q') != NULL)
|
||||||
|
freopen("/dev/null", "w", stderr);
|
||||||
|
|
||||||
|
ch = X_CMDERR;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, let's do the common initialisation
|
||||||
|
*/
|
||||||
|
cnf = read_userconfig(getarg(&arglist, 'C') ? getarg(&arglist, 'C')->val : NULL);
|
||||||
|
|
||||||
|
if (funcs[which])
|
||||||
|
ch = funcs[which] (cnf, mode, &arglist);
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%s: %s[%s] not yet implemented.\n", progname, Which[which], Modes[mode]);
|
||||||
|
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
getindex(const char *words[], const char *word)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (words[i]) {
|
||||||
|
if (strcmp(words[i], word) == 0)
|
||||||
|
return i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is probably an overkill for a cmdline help system, but it reflects
|
||||||
|
* the complexity of the command line.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
banner(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: ", progname);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cmderr(int ec, char const * fmt,...)
|
||||||
|
{
|
||||||
|
if (fmt != NULL) {
|
||||||
|
va_list argp;
|
||||||
|
|
||||||
|
banner();
|
||||||
|
va_start(argp, fmt);
|
||||||
|
vfprintf(stderr, fmt, argp);
|
||||||
|
va_end(argp);
|
||||||
|
}
|
||||||
|
exit(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cmdhelp(int mode, int which)
|
||||||
|
{
|
||||||
|
banner();
|
||||||
|
if (which == -1)
|
||||||
|
fprintf(stderr, "usage: %s [user|group] [add|del|mod|show] [ help | switches/values ]\n", progname);
|
||||||
|
else if (mode == -1)
|
||||||
|
fprintf(stderr, "usage: %s %s [add|del|mod] [ help | switches/values ]\n", progname, Which[which]);
|
||||||
|
else {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to give mode specific help
|
||||||
|
*/
|
||||||
|
static const char *help[W_NUM][M_NUM] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"usage: %s useradd [name] [switches]\n"
|
||||||
|
"\t-C config configuration file\n"
|
||||||
|
"\t-q quiet operation\n"
|
||||||
|
" Adding users:\n"
|
||||||
|
"\t-n name login name\n"
|
||||||
|
"\t-u uid user id\n"
|
||||||
|
"\t-c comment user name/comment\n"
|
||||||
|
"\t-d directory home directory\n"
|
||||||
|
"\t-e date account expiry date\n"
|
||||||
|
"\t-p date password expiry date\n"
|
||||||
|
"\t-g grp initial group\n"
|
||||||
|
"\t-G grp1,grp2 additional groups\n"
|
||||||
|
"\t-m [ -k dir ] create and set up home\n"
|
||||||
|
"\t-s shell name of login shell\n"
|
||||||
|
"\t-o duplicate uid ok\n"
|
||||||
|
"\t-L class user class\n"
|
||||||
|
"\t-h fd read password on fd\n"
|
||||||
|
" Setting defaults:\n"
|
||||||
|
"\t-D set user defaults\n"
|
||||||
|
"\t-b dir default home root dir\n"
|
||||||
|
"\t-e period default expiry period\n"
|
||||||
|
"\t-p period default password change period\n"
|
||||||
|
"\t-g group default group\n"
|
||||||
|
"\t-G grp1,grp2 additional groups\n"
|
||||||
|
"\t-L class default user class\n"
|
||||||
|
"\t-k dir default home skeleton\n"
|
||||||
|
"\t-u min,max set min,max uids\n"
|
||||||
|
"\t-i min,max set min,max gids\n"
|
||||||
|
"\t-w method set default password method\n"
|
||||||
|
"\t-s shell default shell\n",
|
||||||
|
"usage: %s userdel [uid|name] [switches]\n"
|
||||||
|
"\t-n name login name\n"
|
||||||
|
"\t-u uid user id\n"
|
||||||
|
"\t-r remove home & contents\n",
|
||||||
|
"usage: %s usermod [uid|name] [switches]\n"
|
||||||
|
"\t-C config configuration file\n"
|
||||||
|
"\t-q quiet operation\n"
|
||||||
|
"\t-F force add if no user\n"
|
||||||
|
"\t-n name login name\n"
|
||||||
|
"\t-u uid user id\n"
|
||||||
|
"\t-c comment user name/comment\n"
|
||||||
|
"\t-d directory home directory\n"
|
||||||
|
"\t-e date account expiry date\n"
|
||||||
|
"\t-p date password expiry date\n"
|
||||||
|
"\t-g grp initial group\n"
|
||||||
|
"\t-G grp1,grp2 additional groups\n"
|
||||||
|
"\t-l name new login name\n"
|
||||||
|
"\t-L class user class\n"
|
||||||
|
"\t-m [ -k dir ] create and set up home\n"
|
||||||
|
"\t-s shell name of login shell\n"
|
||||||
|
"\t-h fd read password on fd\n",
|
||||||
|
"usage: %s usershow [uid|name] [switches]\n"
|
||||||
|
"\t-n name login name\n"
|
||||||
|
"\t-u uid user id\n"
|
||||||
|
"\t-F force print\n"
|
||||||
|
"\t-p prettier format\n"
|
||||||
|
"\t-a print all users\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"usage: %s groupadd [group|gid] [switches]\n"
|
||||||
|
"\t-C config configuration file\n"
|
||||||
|
"\t-q quiet operation\n"
|
||||||
|
"\t-n group group name\n"
|
||||||
|
"\t-g gid group id\n"
|
||||||
|
"\t-o duplicate gid ok\n",
|
||||||
|
"usage: %s groupdel [group|gid] [switches]\n"
|
||||||
|
"\t-n name group name\n"
|
||||||
|
"\t-g gid group id\n",
|
||||||
|
"usage: %s groupmod [group|gid] [switches]\n"
|
||||||
|
"\t-C config configuration file\n"
|
||||||
|
"\t-q quiet operation\n"
|
||||||
|
"\t-F force add if not exists\n"
|
||||||
|
"\t-n name group name\n"
|
||||||
|
"\t-g gid group id\n"
|
||||||
|
"\t-l name new group name\n",
|
||||||
|
"usage: %s groupshow [group|gid] [switches]\n"
|
||||||
|
"\t-n name group name\n"
|
||||||
|
"\t-g gid group id\n"
|
||||||
|
"\t-F force print\n"
|
||||||
|
"\t-p prettier format\n"
|
||||||
|
"\t-a print all accounting groups\n"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fprintf(stderr, help[which][mode], progname);
|
||||||
|
}
|
||||||
|
exit(X_CMDERR);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct carg *
|
||||||
|
getarg(struct cargs * _args, int ch)
|
||||||
|
{
|
||||||
|
struct carg *c = _args->lh_first;
|
||||||
|
|
||||||
|
while (c != NULL && c->ch != ch)
|
||||||
|
c = c->list.le_next;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct carg *
|
||||||
|
addarg(struct cargs * _args, int ch, char *argstr)
|
||||||
|
{
|
||||||
|
struct carg *ca = malloc(sizeof(struct carg));
|
||||||
|
|
||||||
|
if (ca == NULL)
|
||||||
|
cmderr(X_MEMERR, "Abort - out of memory\n");
|
||||||
|
ca->ch = ch;
|
||||||
|
ca->val = argstr;
|
||||||
|
LIST_INSERT_HEAD(_args, ca, list);
|
||||||
|
return ca;
|
||||||
|
}
|
266
usr.sbin/pw/pw.conf.5
Normal file
266
usr.sbin/pw/pw.conf.5
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
.\" Copyright (c) 1996
|
||||||
|
.\" David L. Nugent.
|
||||||
|
.\" Password/Group file maintenance suite
|
||||||
|
.\"
|
||||||
|
.\" $Id: pw.conf.5,v 1.2 1996/11/18 03:09:02 davidn Exp $
|
||||||
|
.\"
|
||||||
|
.Dd November 13, 1996
|
||||||
|
.Dt PW.CONF 5
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm pw.conf
|
||||||
|
.Nd format of the pw.conf configuration file
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
The file
|
||||||
|
.Aq Pa /etc/pw.conf
|
||||||
|
contains configuration data for the
|
||||||
|
.Xr pw 8
|
||||||
|
program.
|
||||||
|
The
|
||||||
|
.Xr pw 8
|
||||||
|
program is used for maintenance of the system password and group
|
||||||
|
files, allowing users and groups to be added, deleted and changed.
|
||||||
|
This file may be modified via the
|
||||||
|
.Xr pw 8
|
||||||
|
command using the
|
||||||
|
.Ql \&useradd
|
||||||
|
command and the
|
||||||
|
.Ql \&-D
|
||||||
|
option, or by editing it directly with a text editor.
|
||||||
|
.Pp
|
||||||
|
Each line in
|
||||||
|
.Aq Pa /etc/pw.conf
|
||||||
|
is treated either a comment or as configuration data;
|
||||||
|
blank lines and lines commencing with a
|
||||||
|
.Ql \&#
|
||||||
|
character are considered comments, and any remaining lines are
|
||||||
|
examined for a leading keyword, followed by corresponding data.
|
||||||
|
.Pp
|
||||||
|
Keywords recognised by
|
||||||
|
.Xr pw 8
|
||||||
|
are:
|
||||||
|
.Bl -tag -width password_days -offset indent -compact
|
||||||
|
.It defaultpasswd
|
||||||
|
affects passwords generated for new users
|
||||||
|
.It reuseuids
|
||||||
|
reuse gaps in uid sequences
|
||||||
|
.It reusegids
|
||||||
|
reuse gaps in gid sequences
|
||||||
|
.It skeleton
|
||||||
|
where to obtain default home contents
|
||||||
|
.It newmail
|
||||||
|
mail to send to new users
|
||||||
|
.It logfile
|
||||||
|
log user/group modifications to this file
|
||||||
|
.It home
|
||||||
|
root directory for home directories
|
||||||
|
.It shellpath
|
||||||
|
paths in which to locate shell programs
|
||||||
|
.It shells
|
||||||
|
list of valid shells (without path)
|
||||||
|
.It defaultshell
|
||||||
|
default shell (without path)
|
||||||
|
.It defaultgroup
|
||||||
|
default group
|
||||||
|
.It extragroups
|
||||||
|
add new users to this groups
|
||||||
|
.It loginclass
|
||||||
|
place new users in this login class
|
||||||
|
.It minuid
|
||||||
|
.It maxuid
|
||||||
|
range of valid default user ids
|
||||||
|
.It mingid
|
||||||
|
.It maxgid
|
||||||
|
range of valid default group ids
|
||||||
|
.It expire_days
|
||||||
|
days after which account expires
|
||||||
|
.It password_days
|
||||||
|
days after which password expires
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Valid values for
|
||||||
|
.Ar defaultpasswd
|
||||||
|
are
|
||||||
|
.Bl -tag -width password_days -offset indent -compact
|
||||||
|
.It no
|
||||||
|
disables login on newly created accounts
|
||||||
|
.It yes
|
||||||
|
forces the password to be the account name
|
||||||
|
.It none
|
||||||
|
forces a blank password
|
||||||
|
.It random
|
||||||
|
Generates a random password
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The second and third options are insecure and should be avoided if
|
||||||
|
possible on a publicly accessible system.
|
||||||
|
The first option requires that the superuser run
|
||||||
|
.Xr passwd 1
|
||||||
|
to set a password before the account may be used.
|
||||||
|
This may also be useful for creating administrative accounts.
|
||||||
|
The final option causes
|
||||||
|
.Xr pw 8
|
||||||
|
to respond by printing a randomly generated password on stdout.
|
||||||
|
This is the preferred and most secure option.
|
||||||
|
.Xr pw 8
|
||||||
|
also provides a method of setting a specific password for the new
|
||||||
|
user via a filehandle (command lines are not secure).
|
||||||
|
.Pp
|
||||||
|
Both
|
||||||
|
.Ar reuseuids
|
||||||
|
and
|
||||||
|
.Ar reusegids
|
||||||
|
determine the method by which new user and group id numbers are
|
||||||
|
generated.
|
||||||
|
A
|
||||||
|
.Ql \&yes
|
||||||
|
in this field will cause
|
||||||
|
.Xr pw 8
|
||||||
|
to search for the first unused user or group id within the allowed
|
||||||
|
range, whereas a
|
||||||
|
.Ql \&no
|
||||||
|
will ensure that no other existing user or group id within the range
|
||||||
|
is numerically lower than the new one generated, and therefore avoids
|
||||||
|
reusing gaps in the user or group id sequence that are caused by
|
||||||
|
previous user or group deletions.
|
||||||
|
Note that if the default group is not specified using the
|
||||||
|
.Ar defaultgroup
|
||||||
|
keyword,
|
||||||
|
.Xr pw 8
|
||||||
|
will create a new group for the user and attempt to keep the new
|
||||||
|
user's uid and gid the same.
|
||||||
|
If the new user's uid is currently in use as a group id, then the next
|
||||||
|
available group id is chosen instead.
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar skeleton
|
||||||
|
keyword nominates a directory from which the contents of a user's
|
||||||
|
new home directory is constructed.
|
||||||
|
This is
|
||||||
|
.Pa /usr/share/skel
|
||||||
|
by default.
|
||||||
|
.Xr pw 8 's
|
||||||
|
.Ql \&-m
|
||||||
|
option causes the user's home directory to be created and populated
|
||||||
|
using the files contained in the
|
||||||
|
.Ar skeleton
|
||||||
|
directory.
|
||||||
|
.Pp
|
||||||
|
To send an initial email to new users, the
|
||||||
|
.Ar newmail
|
||||||
|
keyword may be used to specify a path name to a file containing
|
||||||
|
the message body of the message to be sent.
|
||||||
|
To avoid sending mail when accounts are created, leave this entry
|
||||||
|
blank or specify
|
||||||
|
.Ql \&no .
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar logfile
|
||||||
|
option allows logging of password file modifications into the
|
||||||
|
nominated log file.
|
||||||
|
To avoid creating or adding to such a logfile, then leave this
|
||||||
|
field blank or specify
|
||||||
|
.Ql \&no .
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar home
|
||||||
|
keyword is mandatory.
|
||||||
|
This specifies the location of the directory in which all new user
|
||||||
|
home directories are created.
|
||||||
|
.Pp
|
||||||
|
.Ar shellpath
|
||||||
|
specifies a list of directories - separated by colons
|
||||||
|
.Ql \&:
|
||||||
|
- which contain the programs used by the login shells.
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar shells
|
||||||
|
keyword specifies a list of programs available for use as login
|
||||||
|
shells.
|
||||||
|
This list is a comma-separated list of shell names which should
|
||||||
|
not contain a path.
|
||||||
|
These shells must exist in one of the directories nominated by
|
||||||
|
.Ar shellpath .
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar defaultshell
|
||||||
|
keyword nominates which shell program to use for new users when
|
||||||
|
none is specified on the
|
||||||
|
.Xr pw 8
|
||||||
|
command line.
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar defaultgroup
|
||||||
|
keyword defines the primary group (the group id number in the
|
||||||
|
password file) used for new accounts.
|
||||||
|
If left blank, or the word
|
||||||
|
.Ql \&no
|
||||||
|
is used, then each new user will have a corresponding group of
|
||||||
|
their own created automatically.
|
||||||
|
This is the recommended procedure for new users as it best secures each
|
||||||
|
user's files against interference by other users of the system
|
||||||
|
irrespective of the
|
||||||
|
.Em umask .
|
||||||
|
normally used by the user.
|
||||||
|
.Pp
|
||||||
|
.Ar extragroups
|
||||||
|
provides an automatic means of placing new users into groups within
|
||||||
|
the
|
||||||
|
.Pa /etc/groups
|
||||||
|
file.
|
||||||
|
This is useful where all users share some resources, and is preferable
|
||||||
|
to placing users into the same primary group.
|
||||||
|
The effect of this keyword can be overridden using the
|
||||||
|
.Ql \&-G
|
||||||
|
option on
|
||||||
|
.Xr pw 8 's
|
||||||
|
command line.
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar minuid ,
|
||||||
|
.Ar maxuid ,
|
||||||
|
.Ar mingid ,
|
||||||
|
.Ar maxgid
|
||||||
|
keywords determines the allowed ranges of automatically allocated user
|
||||||
|
and group id numbers.
|
||||||
|
The default values for both user and group ids are 1000 and 32000 as
|
||||||
|
minimum and maximum respectively.
|
||||||
|
The user and group id's actually used when creating an account with
|
||||||
|
.Xr pw 8
|
||||||
|
may be overridden using the
|
||||||
|
.Ql \&-u
|
||||||
|
and
|
||||||
|
.Ql \&-g
|
||||||
|
command line options.
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ar expire_days
|
||||||
|
and
|
||||||
|
.Ar password_days
|
||||||
|
are used to automatically calculate the number of days from the date
|
||||||
|
on which an account is created when the account will expire or the
|
||||||
|
user will be forced to change the account's password.
|
||||||
|
A value of
|
||||||
|
.Ql \&0
|
||||||
|
in either field will disable the corresponding (account or password)
|
||||||
|
expiration date.
|
||||||
|
.Pp
|
||||||
|
.Sh LIMITS
|
||||||
|
The maximum line length of
|
||||||
|
.Pa /etc/acct/pw.conf
|
||||||
|
is 1024 characters. Longer lines will be skipped and treated
|
||||||
|
as comments.
|
||||||
|
.Sh FILES
|
||||||
|
.Bl -tag -width /etc/master.passwd -compact
|
||||||
|
.It Pa /etc/pw.conf
|
||||||
|
.It Pa /etc/passwd
|
||||||
|
.It Pa /etc/master.passwd
|
||||||
|
.It Pa /etc/group
|
||||||
|
.El
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr pw 8 ,
|
||||||
|
.Xr passwd 1 ,
|
||||||
|
.Xr passwd 5 ,
|
||||||
|
.Xr group 5
|
||||||
|
|
||||||
|
|
142
usr.sbin/pw/pw.h
Normal file
142
usr.sbin/pw/pw.h
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
|
||||||
|
#include "psdate.h"
|
||||||
|
|
||||||
|
enum _mode
|
||||||
|
{
|
||||||
|
M_ADD,
|
||||||
|
M_DELETE,
|
||||||
|
M_UPDATE,
|
||||||
|
M_PRINT,
|
||||||
|
M_NUM
|
||||||
|
};
|
||||||
|
|
||||||
|
enum _which
|
||||||
|
{
|
||||||
|
W_USER,
|
||||||
|
W_GROUP,
|
||||||
|
W_NUM
|
||||||
|
};
|
||||||
|
|
||||||
|
enum _excode
|
||||||
|
{
|
||||||
|
X_ALLOK,
|
||||||
|
X_CMDERR,
|
||||||
|
X_PERMERR,
|
||||||
|
X_MEMERR,
|
||||||
|
X_NOUPDATE,
|
||||||
|
X_NOTFOUND,
|
||||||
|
X_UPDERROR,
|
||||||
|
X_TOOMANY,
|
||||||
|
X_EXISTS,
|
||||||
|
X_DBERROR,
|
||||||
|
X_CONFIG
|
||||||
|
};
|
||||||
|
|
||||||
|
struct carg
|
||||||
|
{
|
||||||
|
int ch;
|
||||||
|
char *val;
|
||||||
|
LIST_ENTRY(carg) list;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern LIST_HEAD(cargs, carg) arglist;
|
||||||
|
|
||||||
|
struct userconf
|
||||||
|
{
|
||||||
|
int default_password; /* Default password for new users? */
|
||||||
|
int reuse_uids; /* Reuse uids? */
|
||||||
|
int reuse_gids; /* Reuse gids? */
|
||||||
|
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 */
|
||||||
|
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 */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define _PATH_PW_CONF "/etc/pw.conf"
|
||||||
|
#define _UC_MAXLINE 1024
|
||||||
|
#define _UC_MAXSHELLS 32
|
||||||
|
#define _UC_MAXGROUPS 200
|
||||||
|
|
||||||
|
struct userconf *read_userconfig(char const * file);
|
||||||
|
int write_userconfig(char const * file);
|
||||||
|
struct carg *addarg(struct cargs * _args, int ch, char *argstr);
|
||||||
|
struct carg *getarg(struct cargs * _args, int ch);
|
||||||
|
void cmderr(int ec, char const * fmt,...);
|
||||||
|
|
||||||
|
int pw_user(struct userconf * cnf, int mode, struct cargs * _args);
|
||||||
|
int pw_group(struct userconf * cnf, int mode, struct cargs * _args);
|
||||||
|
|
||||||
|
int addpwent(struct passwd * pwd);
|
||||||
|
int delpwent(struct passwd * pwd);
|
||||||
|
int chgpwent(char const * login, struct passwd * pwd);
|
||||||
|
int fmtpwent(char *buf, struct passwd * pwd);
|
||||||
|
|
||||||
|
int addgrent(struct group * grp);
|
||||||
|
int delgrent(struct group * grp);
|
||||||
|
int chggrent(char const * login, struct group * grp);
|
||||||
|
int fmtgrent(char *buf, struct group * grp);
|
||||||
|
|
||||||
|
int boolean_val(char const * str, int dflt);
|
||||||
|
char const *boolean_str(int val);
|
||||||
|
char *newstr(char const * p);
|
||||||
|
|
||||||
|
void pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...);
|
||||||
|
char *pw_pwcrypt(char *password);
|
||||||
|
|
||||||
|
extern const char *Modes[];
|
||||||
|
extern const char *Which[];
|
458
usr.sbin/pw/pw_conf.c
Normal file
458
usr.sbin/pw/pw_conf.c
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "pw.h"
|
||||||
|
|
||||||
|
#define debugging 0
|
||||||
|
|
||||||
|
enum {
|
||||||
|
_UC_NONE,
|
||||||
|
_UC_DEFAULTPWD,
|
||||||
|
_UC_REUSEUID,
|
||||||
|
_UC_REUSEGID,
|
||||||
|
_UC_DOTDIR,
|
||||||
|
_UC_NEWMAIL,
|
||||||
|
_UC_LOGFILE,
|
||||||
|
_UC_HOMEROOT,
|
||||||
|
_UC_SHELLPATH,
|
||||||
|
_UC_SHELLS,
|
||||||
|
_UC_DEFAULTSHELL,
|
||||||
|
_UC_DEFAULTGROUP,
|
||||||
|
_UC_EXTRAGROUPS,
|
||||||
|
_UC_DEFAULTCLASS,
|
||||||
|
_UC_MINUID,
|
||||||
|
_UC_MAXUID,
|
||||||
|
_UC_MINGID,
|
||||||
|
_UC_MAXGID,
|
||||||
|
_UC_EXPIRE,
|
||||||
|
_UC_PASSWORD,
|
||||||
|
_UC_FIELDS
|
||||||
|
};
|
||||||
|
|
||||||
|
static char bourne_shell[] = "sh";
|
||||||
|
|
||||||
|
static char *system_shells[_UC_MAXSHELLS] =
|
||||||
|
{
|
||||||
|
bourne_shell,
|
||||||
|
"csh"
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *default_groups[_UC_MAXGROUPS] =
|
||||||
|
{
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static char const *booltrue[] =
|
||||||
|
{
|
||||||
|
"yes", "true", "1", "on", NULL
|
||||||
|
};
|
||||||
|
static char const *boolfalse[] =
|
||||||
|
{
|
||||||
|
"no", "false", "0", "off", NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct userconf config =
|
||||||
|
{
|
||||||
|
0, /* Default password for new users? (nologin) */
|
||||||
|
0, /* Reuse uids? */
|
||||||
|
0, /* Reuse gids? */
|
||||||
|
"/usr/share/skel", /* Where to obtain skeleton files */
|
||||||
|
NULL, /* Mail to send to new accounts */
|
||||||
|
"/var/log/userlog", /* Where to log changes */
|
||||||
|
"/home", /* Where to create home directory */
|
||||||
|
"/bin", /* Where shells are located */
|
||||||
|
system_shells, /* List of shells (first is default) */
|
||||||
|
bourne_shell, /* Default shell */
|
||||||
|
NULL, /* Default group name */
|
||||||
|
default_groups, /* Default (additional) groups */
|
||||||
|
NULL, /* Default login class */
|
||||||
|
1000, 32000, /* Allowed range of uids */
|
||||||
|
1000, 32000, /* Allowed range of gids */
|
||||||
|
0, /* Days until account expires */
|
||||||
|
0 /* Days until password expires */
|
||||||
|
};
|
||||||
|
|
||||||
|
static char const *comments[_UC_FIELDS] =
|
||||||
|
{
|
||||||
|
"#\n# pw.conf - user/group configuration defaults\n#\n",
|
||||||
|
"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
|
||||||
|
"\n# Reuse gaps in uid sequence? (yes or no)\n",
|
||||||
|
"\n# Reuse gaps in gid sequence? (yes or no)\n",
|
||||||
|
"\n# Obtain default dotfiles from this directory\n",
|
||||||
|
"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
|
||||||
|
"\n# Log add/change/remove information in this file\n",
|
||||||
|
"\n# Root directory in which $HOME directory is created\n",
|
||||||
|
"\n# Colon separated list of directories containing valid shells\n",
|
||||||
|
"\n# Space separated list of available shells (without paths)\n",
|
||||||
|
"\n# Default shell (without path)\n",
|
||||||
|
"\n# Default group (leave blank for new group per user)\n",
|
||||||
|
"\n# Extra groups for new users\n",
|
||||||
|
"\n# Default login class for new users\n",
|
||||||
|
"\n# Range of valid default user ids\n",
|
||||||
|
NULL,
|
||||||
|
"\n# Range of valid default group ids\n",
|
||||||
|
NULL,
|
||||||
|
"\n# Days after which account expires (0=disabled)\n",
|
||||||
|
"\n# Days after which password expires (0=disabled)\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
static char const *kwds[] =
|
||||||
|
{
|
||||||
|
"",
|
||||||
|
"defaultpasswd",
|
||||||
|
"reuseuids",
|
||||||
|
"reusegids",
|
||||||
|
"skeleton",
|
||||||
|
"newmail",
|
||||||
|
"logfile",
|
||||||
|
"home",
|
||||||
|
"shellpath",
|
||||||
|
"shells",
|
||||||
|
"defaultshell",
|
||||||
|
"defaultgroup",
|
||||||
|
"extragroups",
|
||||||
|
"defaultclass",
|
||||||
|
"minuid",
|
||||||
|
"maxuid",
|
||||||
|
"mingid",
|
||||||
|
"maxgid",
|
||||||
|
"expire_days",
|
||||||
|
"password_days",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *
|
||||||
|
unquote(char const * str)
|
||||||
|
{
|
||||||
|
if (str && (*str == '"' || *str == '\'')) {
|
||||||
|
char *p = strchr(str + 1, *str);
|
||||||
|
|
||||||
|
if (p != NULL)
|
||||||
|
*p = '\0';
|
||||||
|
return (char *) (*++str ? str : NULL);
|
||||||
|
}
|
||||||
|
return (char *) str;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
boolean_val(char const * str, int dflt)
|
||||||
|
{
|
||||||
|
if ((str = unquote(str)) != NULL) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; booltrue[i]; i++)
|
||||||
|
if (strcmp(str, booltrue[i]) == 0)
|
||||||
|
return 1;
|
||||||
|
for (i = 0; boolfalse[i]; i++)
|
||||||
|
if (strcmp(str, boolfalse[i]) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Special cases for defaultpassword
|
||||||
|
*/
|
||||||
|
if (strcmp(str, "random") == 0)
|
||||||
|
return -1;
|
||||||
|
if (strcmp(str, "none") == 0)
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
return dflt;
|
||||||
|
}
|
||||||
|
|
||||||
|
char const *
|
||||||
|
boolean_str(int val)
|
||||||
|
{
|
||||||
|
if (val == -1)
|
||||||
|
return "random";
|
||||||
|
else if (val == -2)
|
||||||
|
return "none";
|
||||||
|
else
|
||||||
|
return val ? booltrue[0] : boolfalse[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
newstr(char const * p)
|
||||||
|
{
|
||||||
|
char *q = NULL;
|
||||||
|
|
||||||
|
if ((p = unquote(p)) != NULL) {
|
||||||
|
int l = strlen(p) + 1;
|
||||||
|
|
||||||
|
if ((q = malloc(l)) != NULL)
|
||||||
|
memcpy(q, p, l);
|
||||||
|
}
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct userconf *
|
||||||
|
read_userconfig(char const * file)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (file == NULL)
|
||||||
|
file = _PATH_PW_CONF;
|
||||||
|
if ((fp = fopen(file, "r")) != NULL) {
|
||||||
|
char buf[_UC_MAXLINE];
|
||||||
|
|
||||||
|
while (fgets(buf, sizeof buf, fp) != NULL) {
|
||||||
|
char *p = strchr(buf, '\n');
|
||||||
|
|
||||||
|
if (p == NULL) { /* Line too long */
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
while ((ch = fgetc(fp)) != '\n' && ch != EOF);
|
||||||
|
} else {
|
||||||
|
*p = '\0';
|
||||||
|
if (*buf && *buf != '\n' && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
|
||||||
|
static char const toks[] = " \t\r\n,=";
|
||||||
|
char *q = strtok(NULL, toks);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
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);
|
||||||
|
#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_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_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:
|
||||||
|
config.default_group = (q == NULL || !boolean_val(q, 1) || getgrnam(q) == NULL)
|
||||||
|
? NULL : newstr(q);
|
||||||
|
break;
|
||||||
|
case _UC_EXTRAGROUPS:
|
||||||
|
for (i = 0; i < _UC_MAXGROUPS && q != NULL; i++, q = strtok(NULL, toks))
|
||||||
|
default_groups[i] = newstr(q);
|
||||||
|
if (i > 0)
|
||||||
|
while (i < _UC_MAXGROUPS)
|
||||||
|
default_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
return &config;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
write_userconfig(char const * file)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
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;
|
||||||
|
char buf[_UC_MAXLINE];
|
||||||
|
|
||||||
|
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_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_SHELLPATH:
|
||||||
|
val = config.shelldir;
|
||||||
|
break;
|
||||||
|
case _UC_SHELLS:
|
||||||
|
for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++)
|
||||||
|
k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", system_shells[j]);
|
||||||
|
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:
|
||||||
|
for (j = k = 0; j < _UC_MAXGROUPS && default_groups[j] != NULL; j++)
|
||||||
|
k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", default_groups[j]);
|
||||||
|
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 (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 debugging
|
||||||
|
printf("WROTE: %s = %s\n", kwds[i], val);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fclose(fp) != EOF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
273
usr.sbin/pw/pw_group.c
Normal file
273
usr.sbin/pw/pw_group.c
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
#include "pw.h"
|
||||||
|
#include "bitmap.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int print_group(struct group * grp, int pretty);
|
||||||
|
static gid_t gr_gidpolicy(struct userconf * cnf, struct cargs * args);
|
||||||
|
|
||||||
|
int
|
||||||
|
pw_group(struct userconf * cnf, int mode, struct cargs * args)
|
||||||
|
{
|
||||||
|
struct carg *a_name = getarg(args, 'n');
|
||||||
|
struct carg *a_gid = getarg(args, 'g');
|
||||||
|
struct carg *arg;
|
||||||
|
struct group *grp = NULL;
|
||||||
|
char *members[_UC_MAXGROUPS];
|
||||||
|
|
||||||
|
static struct group fakegroup =
|
||||||
|
{
|
||||||
|
"nogroup",
|
||||||
|
"*",
|
||||||
|
-1,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
if (mode == M_PRINT && getarg(args, 'a')) {
|
||||||
|
int pretty = getarg(args, 'p') != NULL;
|
||||||
|
|
||||||
|
setgrent();
|
||||||
|
while ((grp = getgrent()) != NULL)
|
||||||
|
print_group(grp, pretty);
|
||||||
|
endgrent();
|
||||||
|
return X_ALLOK;
|
||||||
|
}
|
||||||
|
if (a_gid == NULL) {
|
||||||
|
if (a_name == NULL)
|
||||||
|
cmderr(X_CMDERR, "group name or id required\n");
|
||||||
|
|
||||||
|
if (mode != M_ADD && grp == NULL && isdigit(*a_name->val)) {
|
||||||
|
(a_gid = a_name)->ch = 'g';
|
||||||
|
a_name = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
grp = (a_name != NULL) ? getgrnam(a_name->val) : getgrgid((gid_t) atoi(a_gid->val));
|
||||||
|
|
||||||
|
if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) {
|
||||||
|
if (a_name == NULL && grp == NULL) /* Try harder */
|
||||||
|
grp = getgrgid(atoi(a_gid->val));
|
||||||
|
|
||||||
|
if (grp == NULL) {
|
||||||
|
if (mode == M_PRINT && getarg(args, 'F')) {
|
||||||
|
fakegroup.gr_name = a_name ? a_name->val : "nogroup";
|
||||||
|
fakegroup.gr_gid = a_gid ? (gid_t) atol(a_gid->val) : -1;
|
||||||
|
return print_group(&fakegroup, getarg(args, 'p') != NULL);
|
||||||
|
}
|
||||||
|
cmderr(X_CMDERR, "unknown group `%s'\n", a_name ? a_name->val : a_gid->val);
|
||||||
|
}
|
||||||
|
if (a_name == NULL) /* Needed later */
|
||||||
|
a_name = addarg(args, 'n', grp->gr_name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle deletions now
|
||||||
|
*/
|
||||||
|
if (mode == M_DELETE) {
|
||||||
|
gid_t gid = grp->gr_gid;
|
||||||
|
|
||||||
|
if (delgrent(grp) == -1)
|
||||||
|
cmderr(X_NOUPDATE, "Error updating group file: %s\n", strerror(errno));
|
||||||
|
pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) gid);
|
||||||
|
return X_ALLOK;
|
||||||
|
} else if (mode == M_PRINT)
|
||||||
|
return print_group(grp, getarg(args, 'p') != NULL);
|
||||||
|
|
||||||
|
if (a_gid)
|
||||||
|
grp->gr_gid = (gid_t) atoi(a_gid->val);
|
||||||
|
|
||||||
|
if ((arg = getarg(args, 'l')) != NULL)
|
||||||
|
grp->gr_name = arg->val;
|
||||||
|
} else {
|
||||||
|
if (a_name == NULL) /* Required */
|
||||||
|
cmderr(X_CMDERR, "group name required\n");
|
||||||
|
else if (grp != NULL) /* Exists */
|
||||||
|
cmderr(X_EXISTS, "group name `%s' already exists\n", a_name->val);
|
||||||
|
|
||||||
|
memset(members, 0, sizeof members);
|
||||||
|
grp = &fakegroup;
|
||||||
|
grp->gr_name = a_name->val;
|
||||||
|
grp->gr_passwd = "*";
|
||||||
|
grp->gr_gid = gr_gidpolicy(cnf, args);
|
||||||
|
grp->gr_mem = members;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This allows us to set a group password Group passwords is an
|
||||||
|
* antique idea, rarely used and insecure (no secure database) Should
|
||||||
|
* be discouraged, but it is apparently still supported by some
|
||||||
|
* software.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ((arg = getarg(args, 'h')) != NULL) {
|
||||||
|
if (strcmp(arg->val, "-") == 0)
|
||||||
|
grp->gr_passwd = "*"; /* No access */
|
||||||
|
else {
|
||||||
|
int fd = atoi(arg->val);
|
||||||
|
int b;
|
||||||
|
int istty = isatty(fd);
|
||||||
|
struct termios t;
|
||||||
|
char *p, line[256];
|
||||||
|
|
||||||
|
if (istty) {
|
||||||
|
if (tcgetattr(fd, &t) == -1)
|
||||||
|
istty = 0;
|
||||||
|
else {
|
||||||
|
struct termios n = t;
|
||||||
|
|
||||||
|
/* Disable echo */
|
||||||
|
n.c_lflag &= ~(ECHO);
|
||||||
|
tcsetattr(fd, TCSANOW, &n);
|
||||||
|
printf("%sassword for group %s:", (mode == M_UPDATE) ? "New p" : "P", grp->gr_name);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b = read(fd, line, sizeof(line) - 1);
|
||||||
|
if (istty) { /* Restore state */
|
||||||
|
tcsetattr(fd, TCSANOW, &t);
|
||||||
|
fputc('\n', stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
if (b < 0) {
|
||||||
|
perror("-h file descriptor");
|
||||||
|
return X_CMDERR;
|
||||||
|
}
|
||||||
|
line[b] = '\0';
|
||||||
|
if ((p = strpbrk(line, " \t\r\n")) != NULL)
|
||||||
|
*p = '\0';
|
||||||
|
if (!*line)
|
||||||
|
cmderr(X_CMDERR, "empty password read on file descriptor %d\n", fd);
|
||||||
|
grp->gr_passwd = pw_pwcrypt(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((mode == M_ADD && !addgrent(grp)) || (mode == M_UPDATE && !chggrent(a_name->val, grp))) {
|
||||||
|
perror("group update");
|
||||||
|
return X_NOUPDATE;
|
||||||
|
}
|
||||||
|
/* grp may have been invalidated */
|
||||||
|
if ((grp = getgrnam(a_name->val)) == NULL)
|
||||||
|
cmderr(X_NOTFOUND, "group disappeared during update\n");
|
||||||
|
|
||||||
|
pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid);
|
||||||
|
|
||||||
|
return X_ALLOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gid_t
|
||||||
|
gr_gidpolicy(struct userconf * cnf, struct cargs * args)
|
||||||
|
{
|
||||||
|
struct group *grp;
|
||||||
|
gid_t gid = (gid_t) - 1;
|
||||||
|
struct carg *a_gid = getarg(args, 'g');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check the given gid, if any
|
||||||
|
*/
|
||||||
|
if (a_gid != NULL) {
|
||||||
|
gid = (gid_t) atol(a_gid->val);
|
||||||
|
|
||||||
|
if ((grp = getgrgid(gid)) != NULL && getarg(args, 'o') == NULL)
|
||||||
|
cmderr(X_EXISTS, "gid `%ld' has already been allocated\n", (long) grp->gr_gid);
|
||||||
|
} else {
|
||||||
|
struct bitmap bm;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to allocate the next available gid under one of
|
||||||
|
* two policies a) Grab the first unused gid b) Grab the
|
||||||
|
* highest possible unused gid
|
||||||
|
*/
|
||||||
|
if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */
|
||||||
|
cnf->min_gid = 1000;
|
||||||
|
cnf->max_gid = 32000;
|
||||||
|
}
|
||||||
|
bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, let's fill the bitmap from the password file
|
||||||
|
*/
|
||||||
|
setgrent();
|
||||||
|
while ((grp = getgrent()) != NULL)
|
||||||
|
if (grp->gr_gid >= (int) cnf->min_gid && grp->gr_gid <= (int) cnf->max_gid)
|
||||||
|
bm_setbit(&bm, grp->gr_gid - cnf->min_gid);
|
||||||
|
endgrent();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Then apply the policy, with fallback to reuse if necessary
|
||||||
|
*/
|
||||||
|
if (cnf->reuse_gids)
|
||||||
|
gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
|
||||||
|
else {
|
||||||
|
gid = (gid_t) (bm_lastset(&bm) + 1);
|
||||||
|
if (!bm_isset(&bm, gid))
|
||||||
|
gid += cnf->min_gid;
|
||||||
|
else
|
||||||
|
gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Another sanity check
|
||||||
|
*/
|
||||||
|
if (gid < cnf->min_gid || gid > cnf->max_gid)
|
||||||
|
cmderr(X_EXISTS, "unable to allocate a new gid - range fully used\n");
|
||||||
|
bm_dealloc(&bm);
|
||||||
|
}
|
||||||
|
return gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
print_group(struct group * grp, int pretty)
|
||||||
|
{
|
||||||
|
if (!pretty) {
|
||||||
|
char buf[_UC_MAXLINE];
|
||||||
|
|
||||||
|
fmtgrent(buf, grp);
|
||||||
|
fputs(buf, stdout);
|
||||||
|
} else {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
printf("Group Name : %-10s #%lu\n"
|
||||||
|
" Members : ",
|
||||||
|
grp->gr_name, (long) grp->gr_gid);
|
||||||
|
for (i = 0; i < _UC_MAXGROUPS && grp->gr_mem[i]; i++)
|
||||||
|
printf("%s%s", i ? "," : "", grp->gr_mem[i]);
|
||||||
|
fputs("\n\n", stdout);
|
||||||
|
}
|
||||||
|
return X_ALLOK;
|
||||||
|
}
|
70
usr.sbin/pw/pw_log.c
Normal file
70
usr.sbin/pw/pw_log.c
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "pw.h"
|
||||||
|
|
||||||
|
static FILE *logfile = NULL;
|
||||||
|
|
||||||
|
void
|
||||||
|
pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...)
|
||||||
|
{
|
||||||
|
if (cnf->logfile && *cnf->logfile) {
|
||||||
|
if (logfile == NULL) { /* With umask==0 we need to control file access modes on create */
|
||||||
|
int fd = open(cnf->logfile, O_WRONLY | O_CREAT | O_APPEND, 0600);
|
||||||
|
|
||||||
|
if (fd != -1)
|
||||||
|
logfile = fdopen(fd, "a");
|
||||||
|
}
|
||||||
|
if (logfile != NULL) {
|
||||||
|
va_list argp;
|
||||||
|
int l;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
struct tm *t = localtime(&now);
|
||||||
|
char nfmt[256];
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
if ((name = getenv("LOGNAME")) == NULL && (name = getenv("USER")) == NULL)
|
||||||
|
name = "unknown";
|
||||||
|
strftime(nfmt, sizeof nfmt, "%d-%b-%Y %R ", t);
|
||||||
|
l = strlen(nfmt);
|
||||||
|
sprintf(nfmt + strlen(nfmt), "[%s:%s%s] %s\n", name, Which[which], Modes[mode], fmt);
|
||||||
|
va_start(argp, fmt);
|
||||||
|
vfprintf(logfile, nfmt, argp);
|
||||||
|
va_end(argp);
|
||||||
|
fflush(logfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
849
usr.sbin/pw/pw_user.c
Normal file
849
usr.sbin/pw/pw_user.c
Normal file
@ -0,0 +1,849 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <paths.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include "pw.h"
|
||||||
|
#include "bitmap.h"
|
||||||
|
#include "pwupd.h"
|
||||||
|
|
||||||
|
static int print_user(struct passwd * pwd, int pretty);
|
||||||
|
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 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);
|
||||||
|
static char *pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell);
|
||||||
|
static char *pw_password(struct userconf * cnf, struct cargs * args, char const * user);
|
||||||
|
static char *pw_checkname(char *name, int gecos);
|
||||||
|
static char *shell_path(char const * path, char *shells[], char *sh);
|
||||||
|
static void rmat(uid_t uid);
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* -C config configuration file
|
||||||
|
* -q quiet operation
|
||||||
|
* -n name login name
|
||||||
|
* -u uid user id
|
||||||
|
* -c comment user name/comment
|
||||||
|
* -d directory home directory
|
||||||
|
* -e date account expiry date
|
||||||
|
* -p date password expiry date
|
||||||
|
* -g grp primary group
|
||||||
|
* -G grp1,grp2 additional groups
|
||||||
|
* -m [ -k dir ] create and set up home
|
||||||
|
* -s shell name of login shell
|
||||||
|
* -o duplicate uid ok
|
||||||
|
* -L class user class
|
||||||
|
* -l name new login name
|
||||||
|
* -h fd password filehandle
|
||||||
|
* -F force print or add
|
||||||
|
* Setting defaults:
|
||||||
|
* -D set user defaults
|
||||||
|
* -b dir default home root dir
|
||||||
|
* -e period default expiry period
|
||||||
|
* -p period default password change period
|
||||||
|
* -g group default group
|
||||||
|
* -G grp1,grp2.. default additional groups
|
||||||
|
* -L class default login class
|
||||||
|
* -k dir default home skeleton
|
||||||
|
* -s shell default shell
|
||||||
|
* -w method default password method
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
pw_user(struct userconf * cnf, int mode, struct cargs * args)
|
||||||
|
{
|
||||||
|
char *p = NULL;
|
||||||
|
struct carg *a_name;
|
||||||
|
struct carg *a_uid;
|
||||||
|
struct carg *arg;
|
||||||
|
struct passwd *pwd = NULL;
|
||||||
|
struct group *grp;
|
||||||
|
struct stat st;
|
||||||
|
char line[MAXPWLINE];
|
||||||
|
|
||||||
|
static struct passwd fakeuser =
|
||||||
|
{
|
||||||
|
NULL,
|
||||||
|
"*",
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
"User &",
|
||||||
|
"/bin/sh",
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can do all of the common legwork here
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ((arg = getarg(args, 'b')) != NULL) {
|
||||||
|
if (stat(cnf->home = arg->val, &st) == -1 || S_ISDIR(st.st_mode))
|
||||||
|
cmderr(X_CMDERR, "root home `%s' is not a directory or does not exist\n", cnf->home);
|
||||||
|
}
|
||||||
|
if ((arg = getarg(args, 'e')) != NULL)
|
||||||
|
cnf->expire_days = atoi(arg->val);
|
||||||
|
|
||||||
|
if ((arg = getarg(args, 'p')) != NULL && arg->val)
|
||||||
|
cnf->password_days = atoi(arg->val);
|
||||||
|
|
||||||
|
if ((arg = getarg(args, 'g')) != NULL) {
|
||||||
|
p = arg->val;
|
||||||
|
if ((grp = getgrnam(p)) == NULL) {
|
||||||
|
if (!isdigit(*p) || (grp = getgrgid((gid_t) atoi(p))) == NULL)
|
||||||
|
cmderr(X_NOTFOUND, "group `%s' does not exist\n", p);
|
||||||
|
}
|
||||||
|
cnf->default_group = newstr(grp->gr_name);
|
||||||
|
}
|
||||||
|
if ((arg = getarg(args, 'L')) != NULL)
|
||||||
|
cnf->default_class = pw_checkname(arg->val, 0);
|
||||||
|
|
||||||
|
if ((arg = getarg(args, 'G')) != NULL && arg->val) {
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (p = strtok(arg->val, ", \t"); i < _UC_MAXGROUPS && p != NULL; p = strtok(NULL, ", \t")) {
|
||||||
|
if ((grp = getgrnam(p)) == NULL) {
|
||||||
|
if (!isdigit(*p) || (grp = getgrgid((gid_t) atoi(p))) == NULL)
|
||||||
|
cmderr(X_NOTFOUND, "group `%s' does not exist\n", p);
|
||||||
|
}
|
||||||
|
cnf->groups[i++] = newstr(grp->gr_name);
|
||||||
|
}
|
||||||
|
while (i < _UC_MAXGROUPS)
|
||||||
|
cnf->groups[i++] = NULL;
|
||||||
|
}
|
||||||
|
if ((arg = getarg(args, 'k')) != NULL) {
|
||||||
|
if (stat(cnf->dotdir = arg->val, &st) == -1 || S_ISDIR(st.st_mode))
|
||||||
|
cmderr(X_CMDERR, "skeleton `%s' is not a directory or does not exist\n", cnf->dotdir);
|
||||||
|
}
|
||||||
|
if ((arg = getarg(args, 's')) != NULL)
|
||||||
|
cnf->shell_default = arg->val;
|
||||||
|
|
||||||
|
if (mode == M_ADD && getarg(args, 'D')) {
|
||||||
|
if (getarg(args, 'n') != NULL)
|
||||||
|
cmderr(X_CMDERR, "can't combine `-D' with `-n name'\n");
|
||||||
|
if ((arg = getarg(args, 'u')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
|
||||||
|
if ((cnf->min_uid = (uid_t) atoi(p)) == 0)
|
||||||
|
cnf->min_uid = 1000;
|
||||||
|
if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_uid = (uid_t) atoi(p)) < cnf->min_uid)
|
||||||
|
cnf->max_uid = 32000;
|
||||||
|
}
|
||||||
|
if ((arg = getarg(args, 'i')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
|
||||||
|
if ((cnf->min_gid = (gid_t) atoi(p)) == 0)
|
||||||
|
cnf->min_gid = 1000;
|
||||||
|
if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_gid = (gid_t) atoi(p)) < cnf->min_gid)
|
||||||
|
cnf->max_gid = 32000;
|
||||||
|
}
|
||||||
|
if ((arg = getarg(args, 'w')) != NULL)
|
||||||
|
cnf->default_password = boolean_val(arg->val, cnf->default_password);
|
||||||
|
|
||||||
|
arg = getarg(args, 'C');
|
||||||
|
if (write_userconfig(arg ? arg->val : NULL))
|
||||||
|
return X_ALLOK;
|
||||||
|
perror("config update");
|
||||||
|
return X_UPDERROR;
|
||||||
|
}
|
||||||
|
if (mode == M_PRINT && getarg(args, 'a')) {
|
||||||
|
int pretty = getarg(args, 'p') != NULL;
|
||||||
|
|
||||||
|
setpwent();
|
||||||
|
while ((pwd = getpwent()) != NULL)
|
||||||
|
print_user(pwd, pretty);
|
||||||
|
endpwent();
|
||||||
|
return X_ALLOK;
|
||||||
|
}
|
||||||
|
if ((a_name = getarg(args, 'n')) != NULL)
|
||||||
|
pwd = getpwnam(pw_checkname(a_name->val, 0));
|
||||||
|
a_uid = getarg(args, 'u');
|
||||||
|
|
||||||
|
if (a_uid == NULL) {
|
||||||
|
if (a_name == NULL)
|
||||||
|
cmderr(X_CMDERR, "user name or id required\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 && isdigit(*a_name->val) && atoi(a_name->val) > 0) { /* Assume uid */
|
||||||
|
(a_uid = a_name)->ch = 'u';
|
||||||
|
a_name = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Update, delete & print require that the user exists
|
||||||
|
*/
|
||||||
|
if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) {
|
||||||
|
if (a_name == NULL && pwd == NULL) /* Try harder */
|
||||||
|
pwd = getpwuid(atoi(a_uid->val));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if (a_name == NULL)
|
||||||
|
cmderr(X_NOTFOUND, "no such uid `%s'\n", a_uid->val);
|
||||||
|
cmderr(X_NOTFOUND, "no such user `%s'\n", a_name->val);
|
||||||
|
}
|
||||||
|
if (a_name == NULL) /* May be needed later */
|
||||||
|
a_name = addarg(args, 'n', newstr(pwd->pw_name));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle deletions now
|
||||||
|
*/
|
||||||
|
if (mode == M_DELETE) {
|
||||||
|
char file[MAXPATHLEN];
|
||||||
|
char home[MAXPATHLEN];
|
||||||
|
uid_t uid = pwd->pw_uid;
|
||||||
|
|
||||||
|
if (strcmp(pwd->pw_name, "root") == 0)
|
||||||
|
cmderr(X_CMDERR, "cannot remove user 'root'\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove crontabs
|
||||||
|
*/
|
||||||
|
sprintf(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);
|
||||||
|
strncpy(home, pwd->pw_dir, sizeof home);
|
||||||
|
home[sizeof home - 1] = '\0';
|
||||||
|
|
||||||
|
if (!delpwent(pwd))
|
||||||
|
cmderr(X_NOUPDATE, "Error updating passwd file: %s\n", strerror(errno));
|
||||||
|
editgroups(a_name->val, NULL);
|
||||||
|
|
||||||
|
pw_log(cnf, mode, W_USER, "%s(%ld) account removed", a_name->val, (long) uid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 X_ALLOK;
|
||||||
|
} else if (mode == M_PRINT)
|
||||||
|
return print_user(pwd, getarg(args, 'p') != NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The rest is edit code
|
||||||
|
*/
|
||||||
|
if ((arg = getarg(args, 'l')) != NULL) {
|
||||||
|
if (strcmp(pwd->pw_name, "root") == 0)
|
||||||
|
cmderr(X_CMDERR, "can't rename `root' account\n");
|
||||||
|
pwd->pw_name = pw_checkname(arg->val, 0);
|
||||||
|
}
|
||||||
|
if ((arg = getarg(args, 'u')) != NULL && isdigit(*arg->val)) {
|
||||||
|
pwd->pw_uid = (uid_t) atol(arg->val);
|
||||||
|
if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0)
|
||||||
|
cmderr(X_CMDERR, "can't change uid of `root' account\n");
|
||||||
|
if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
|
||||||
|
fprintf(stderr, "WARNING: account `%s' will have a uid of 0 (superuser access!)\n", pwd->pw_name);
|
||||||
|
}
|
||||||
|
if ((arg = getarg(args, 'g')) != NULL && pwd->pw_uid != 0) /* Already checked this */
|
||||||
|
pwd->pw_gid = (gid_t) getgrnam(cnf->default_group)->gr_gid;
|
||||||
|
|
||||||
|
if ((arg = getarg(args, 'p')) != NULL) {
|
||||||
|
if (*arg->val == '\0' || strcmp(arg->val, "0") == 0)
|
||||||
|
pwd->pw_change = 0;
|
||||||
|
else {
|
||||||
|
time_t now = time(NULL);
|
||||||
|
time_t expire = parse_date(now, arg->val);
|
||||||
|
|
||||||
|
if (now == expire)
|
||||||
|
cmderr(X_CMDERR, "Invalid password change date `%s'\n", arg->val);
|
||||||
|
pwd->pw_change = expire;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((arg = getarg(args, 'p')) != NULL) {
|
||||||
|
if (*arg->val == '\0' || strcmp(arg->val, "0") == 0)
|
||||||
|
pwd->pw_expire = 0;
|
||||||
|
else {
|
||||||
|
time_t now = time(NULL);
|
||||||
|
time_t expire = parse_date(now, arg->val);
|
||||||
|
|
||||||
|
if (now == expire)
|
||||||
|
cmderr(X_CMDERR, "Invalid password change date `%s'\n", arg->val);
|
||||||
|
pwd->pw_expire = expire;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((arg = getarg(args, 's')) != NULL)
|
||||||
|
pwd->pw_shell = shell_path(cnf->shelldir, cnf->shells, arg->val);
|
||||||
|
|
||||||
|
if (getarg(args, 'L'))
|
||||||
|
pwd->pw_class = cnf->default_class;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (a_name == NULL) /* Required */
|
||||||
|
cmderr(X_CMDERR, "login name required\n");
|
||||||
|
else if ((pwd = getpwnam(a_name->val)) != NULL) /* Exists */
|
||||||
|
cmderr(X_EXISTS, "login name `%s' already exists\n", a_name->val);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, set up defaults for a new user
|
||||||
|
*/
|
||||||
|
pwd = &fakeuser;
|
||||||
|
pwd->pw_name = a_name->val;
|
||||||
|
pwd->pw_class = cnf->default_class ? cnf->default_class : "";
|
||||||
|
pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
|
||||||
|
pwd->pw_uid = pw_uidpolicy(cnf, args);
|
||||||
|
pwd->pw_gid = pw_gidpolicy(cnf, 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);
|
||||||
|
pwd->pw_shell = pw_shellpolicy(cnf, args, NULL);
|
||||||
|
|
||||||
|
if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
|
||||||
|
fprintf(stderr, "WARNING: new account `%s' has a uid of 0 (superuser access!)\n", pwd->pw_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shared add/edit code
|
||||||
|
*/
|
||||||
|
if ((arg = getarg(args, 'c')) != NULL)
|
||||||
|
pwd->pw_gecos = pw_checkname(arg->val, 1);
|
||||||
|
|
||||||
|
if ((arg = getarg(args, 'h')) != NULL) {
|
||||||
|
if (strcmp(arg->val, "-") == 0)
|
||||||
|
pwd->pw_passwd = "*"; /* No access */
|
||||||
|
else {
|
||||||
|
int fd = atoi(arg->val);
|
||||||
|
int b;
|
||||||
|
int istty = isatty(fd);
|
||||||
|
struct termios t;
|
||||||
|
|
||||||
|
if (istty) {
|
||||||
|
if (tcgetattr(fd, &t) == -1)
|
||||||
|
istty = 0;
|
||||||
|
else {
|
||||||
|
struct termios n = t;
|
||||||
|
|
||||||
|
/* Disable echo */
|
||||||
|
n.c_lflag &= ~(ECHO);
|
||||||
|
tcsetattr(fd, TCSANOW, &n);
|
||||||
|
printf("%sassword for user %s:", (mode == M_UPDATE) ? "New p" : "P", pwd->pw_name);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b = read(fd, line, sizeof(line) - 1);
|
||||||
|
if (istty) { /* Restore state */
|
||||||
|
tcsetattr(fd, TCSANOW, &t);
|
||||||
|
fputc('\n', stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
if (b < 0) {
|
||||||
|
perror("-h file descriptor");
|
||||||
|
return X_CMDERR;
|
||||||
|
}
|
||||||
|
line[b] = '\0';
|
||||||
|
if ((p = strpbrk(line, " \t\r\n")) != NULL)
|
||||||
|
*p = '\0';
|
||||||
|
if (!*line)
|
||||||
|
cmderr(X_CMDERR, "empty password read on file descriptor %d\n", fd);
|
||||||
|
pwd->pw_passwd = pw_pwcrypt(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((mode == M_ADD && !addpwent(pwd)) ||
|
||||||
|
(mode == M_UPDATE && !chgpwent(a_name->val, pwd))) {
|
||||||
|
perror("password update");
|
||||||
|
return X_NOUPDATE;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Ok, user is created or changed - now edit group file
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (mode == M_ADD || getarg(args, 'G') != NULL)
|
||||||
|
editgroups(pwd->pw_name, cnf->groups);
|
||||||
|
|
||||||
|
/* pwd may have been invalidated */
|
||||||
|
if ((pwd = getpwnam(a_name->val)) == NULL)
|
||||||
|
cmderr(X_NOTFOUND, "user '%s' disappeared during update\n", a_name->val);
|
||||||
|
|
||||||
|
grp = getgrgid(pwd->pw_gid);
|
||||||
|
pw_log(cnf, mode, W_USER, "%s(%ld):%s(%d):%s:%s:%s",
|
||||||
|
pwd->pw_name, (long) pwd->pw_uid,
|
||||||
|
grp ? grp->gr_name : "unknown", (long) (grp ? grp->gr_gid : -1),
|
||||||
|
pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If adding, let's touch and chown the user's mail file. This is not
|
||||||
|
* strictly necessary under BSD with a 0755 maildir but it also
|
||||||
|
* doesn't hurt anything to create the empty mailfile
|
||||||
|
*/
|
||||||
|
if (mode == M_ADD) {
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
sprintf(line, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
|
||||||
|
close(open(line, O_RDWR | O_CREAT, 0600)); /* Preserve contents &
|
||||||
|
* mtime */
|
||||||
|
chown(line, pwd->pw_uid, pwd->pw_gid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send mail to the new user as well, if we are asked to
|
||||||
|
*/
|
||||||
|
if (cnf->newmail && *cnf->newmail && (fp = fopen(cnf->newmail, "r")) != NULL) {
|
||||||
|
FILE *pfp = popen(_PATH_SENDMAIL " -t", "w");
|
||||||
|
|
||||||
|
if (pfp == NULL)
|
||||||
|
perror("sendmail");
|
||||||
|
else {
|
||||||
|
fprintf(pfp, "From: root\n" "To: %s\n" "Subject: Welcome!\n\n", pwd->pw_name);
|
||||||
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||||
|
/* Do substitutions? */
|
||||||
|
fputs(line, pfp);
|
||||||
|
}
|
||||||
|
pclose(pfp);
|
||||||
|
pw_log(cnf, mode, W_USER, "%s(%ld) new user mail sent",
|
||||||
|
pwd->pw_name, (long) pwd->pw_uid);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Finally, let's create and populate the user's home directory. Note
|
||||||
|
* that this also `works' for editing users if -m is used, but
|
||||||
|
* existing files will *not* be overwritten.
|
||||||
|
*/
|
||||||
|
if (getarg(args, 'm') != NULL && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
|
||||||
|
copymkdir(pwd->pw_dir, cnf->dotdir, 0755, 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);
|
||||||
|
}
|
||||||
|
return X_ALLOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uid_t
|
||||||
|
pw_uidpolicy(struct userconf * cnf, struct cargs * args)
|
||||||
|
{
|
||||||
|
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 ((pwd = getpwuid(uid)) != NULL && getarg(args, 'o') == NULL)
|
||||||
|
cmderr(X_EXISTS, "uid `%ld' has already been allocated\n", (long) pwd->pw_uid);
|
||||||
|
} else {
|
||||||
|
struct bitmap bm;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to allocate the next available uid under one of
|
||||||
|
* two policies a) Grab the first unused uid b) Grab the
|
||||||
|
* highest possible unused uid
|
||||||
|
*/
|
||||||
|
if (cnf->min_uid >= cnf->max_uid) { /* Sanity
|
||||||
|
* claus^H^H^H^Hheck */
|
||||||
|
cnf->min_uid = 1000;
|
||||||
|
cnf->max_uid = 32000;
|
||||||
|
}
|
||||||
|
bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, let's fill the bitmap from the password file
|
||||||
|
*/
|
||||||
|
setpwent();
|
||||||
|
while ((pwd = getpwent()) != NULL)
|
||||||
|
if (pwd->pw_uid >= (int) cnf->min_uid && pwd->pw_uid <= (int) cnf->max_uid)
|
||||||
|
bm_setbit(&bm, pwd->pw_uid - cnf->min_uid);
|
||||||
|
endpwent();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Then apply the policy, with fallback to reuse if necessary
|
||||||
|
*/
|
||||||
|
if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid)
|
||||||
|
uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Another sanity check
|
||||||
|
*/
|
||||||
|
if (uid < cnf->min_uid || uid > cnf->max_uid)
|
||||||
|
cmderr(X_EXISTS, "unable to allocate a new uid - range fully used\n");
|
||||||
|
bm_dealloc(&bm);
|
||||||
|
}
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uid_t
|
||||||
|
pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer)
|
||||||
|
{
|
||||||
|
struct group *grp;
|
||||||
|
gid_t gid = (uid_t) - 1;
|
||||||
|
struct carg *a_gid = getarg(args, 'g');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If no arg given, see if default can help out
|
||||||
|
*/
|
||||||
|
if (a_gid == NULL && cnf->default_group && *cnf->default_group)
|
||||||
|
a_gid = addarg(args, 'g', cnf->default_group);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check the given gid, if any
|
||||||
|
*/
|
||||||
|
if (a_gid != NULL) {
|
||||||
|
setgrent();
|
||||||
|
if ((grp = getgrnam(a_gid->val)) == NULL) {
|
||||||
|
gid = (gid_t) atol(a_gid->val);
|
||||||
|
if ((gid == 0 && !isdigit(*a_gid->val)) || (grp = getgrgid(gid)) == NULL)
|
||||||
|
cmderr(X_NOTFOUND, "group `%s' is not defined\n", a_gid->val);
|
||||||
|
}
|
||||||
|
endgrent();
|
||||||
|
gid = grp->gr_gid;
|
||||||
|
} else {
|
||||||
|
struct cargs grpargs;
|
||||||
|
char tmp[32];
|
||||||
|
|
||||||
|
LIST_INIT(&grpargs);
|
||||||
|
addarg(&grpargs, 'n', nam);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to auto-create a group with the user's name. We
|
||||||
|
* can send all the appropriate output to our sister routine
|
||||||
|
* bit first see if we can create a group with gid==uid so we
|
||||||
|
* can keep the user and group ids in sync. We purposely do
|
||||||
|
* NOT check the gid range if we can force the sync. If the
|
||||||
|
* user's name dups an existing group, then the group add
|
||||||
|
* function will happily handle that case for us and exit.
|
||||||
|
*/
|
||||||
|
if (getgrgid(prefer) == NULL) {
|
||||||
|
sprintf(tmp, "%lu", (unsigned long) prefer);
|
||||||
|
addarg(&grpargs, 'g', tmp);
|
||||||
|
}
|
||||||
|
endgrent();
|
||||||
|
pw_group(cnf, M_ADD, &grpargs);
|
||||||
|
a_gid = grpargs.lh_first;
|
||||||
|
while (a_gid != NULL) {
|
||||||
|
struct carg *t = a_gid->list.le_next;
|
||||||
|
|
||||||
|
LIST_REMOVE(a_gid, list);
|
||||||
|
a_gid = t;
|
||||||
|
}
|
||||||
|
if ((grp = getgrnam(nam)) != NULL)
|
||||||
|
gid = grp->gr_gid;
|
||||||
|
}
|
||||||
|
return gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static time_t
|
||||||
|
pw_pwdpolicy(struct userconf * cnf, struct cargs * args)
|
||||||
|
{
|
||||||
|
time_t result = 0;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
struct carg *arg = getarg(args, 'e');
|
||||||
|
|
||||||
|
if (arg != NULL) {
|
||||||
|
if ((result = parse_date(now, arg->val)) == now)
|
||||||
|
cmderr(X_NOTFOUND, "invalid date/time `%s'\n", arg->val);
|
||||||
|
} else if (cnf->password_days > 0)
|
||||||
|
result = now + ((long) cnf->password_days * 86400L);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static time_t
|
||||||
|
pw_exppolicy(struct userconf * cnf, struct cargs * args)
|
||||||
|
{
|
||||||
|
time_t result = 0;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
struct carg *arg = getarg(args, 'e');
|
||||||
|
|
||||||
|
if (arg != NULL) {
|
||||||
|
if ((result = parse_date(now, arg->val)) == now)
|
||||||
|
cmderr(X_NOTFOUND, "invalid date/time `%s'\n", arg->val);
|
||||||
|
} else if (cnf->expire_days > 0)
|
||||||
|
result = now + ((long) cnf->expire_days * 86400L);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user)
|
||||||
|
{
|
||||||
|
struct carg *arg = getarg(args, 'd');
|
||||||
|
|
||||||
|
if (arg)
|
||||||
|
return arg->val;
|
||||||
|
else {
|
||||||
|
static char home[128];
|
||||||
|
|
||||||
|
if (cnf->home == NULL || *cnf->home == '\0')
|
||||||
|
cmderr(X_NOTFOUND, "no base home directory set\n");
|
||||||
|
sprintf(home, "%s/%s", cnf->home, user);
|
||||||
|
return home;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
shell_path(char const * path, char *shells[], char *sh)
|
||||||
|
{
|
||||||
|
if (sh != NULL && (*sh == '/' || *sh == '\0'))
|
||||||
|
return sh; /* specified full path or forced none */
|
||||||
|
else {
|
||||||
|
char *p;
|
||||||
|
char paths[_UC_MAXLINE];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to search paths
|
||||||
|
*/
|
||||||
|
strncpy(paths, path, sizeof paths);
|
||||||
|
paths[sizeof paths - 1] = '\0';
|
||||||
|
for (p = strtok(paths, ": \t\r\n"); p != NULL; p = strtok(NULL, ": \t\r\n")) {
|
||||||
|
int i;
|
||||||
|
static char shellpath[256];
|
||||||
|
|
||||||
|
if (sh != NULL) {
|
||||||
|
sprintf(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]);
|
||||||
|
if (access(shellpath, X_OK) == 0)
|
||||||
|
return shellpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sh == NULL)
|
||||||
|
cmderr(X_CMDERR, "can't find shell `%s' in shell paths\n", sh);
|
||||||
|
cmderr(X_CMDERR, "no default shell available or defined\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell)
|
||||||
|
{
|
||||||
|
char *sh = newshell;
|
||||||
|
struct carg *arg = getarg(args, 's');
|
||||||
|
|
||||||
|
if (newshell == NULL && arg != NULL)
|
||||||
|
sh = arg->val;
|
||||||
|
return shell_path(cnf->shelldir, cnf->shells, sh ? sh : cnf->shell_default);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
|
||||||
|
|
||||||
|
char *
|
||||||
|
pw_pwcrypt(char *password)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char salt[12];
|
||||||
|
|
||||||
|
static char buf[256];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate a salt value
|
||||||
|
*/
|
||||||
|
srandom((unsigned) (time(NULL) | getpid()));
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
salt[i] = chars[random() % 63];
|
||||||
|
salt[i] = '\0';
|
||||||
|
|
||||||
|
return strcpy(buf, crypt(password, salt));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
pw_password(struct userconf * cnf, struct cargs * args, char const * user)
|
||||||
|
{
|
||||||
|
int i, l;
|
||||||
|
char pwbuf[32];
|
||||||
|
|
||||||
|
switch (cnf->default_password) {
|
||||||
|
case -1: /* Random password */
|
||||||
|
srandom((unsigned) (time(NULL) | getpid()));
|
||||||
|
l = (random() % 8 + 8); /* 8 - 16 chars */
|
||||||
|
for (i = 0; i < l; i++)
|
||||||
|
pwbuf[i] = chars[random() % sizeof(chars)];
|
||||||
|
pwbuf[i] = '\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We give this information back to the user
|
||||||
|
*/
|
||||||
|
if (getarg(args, 'h') == NULL) {
|
||||||
|
if (isatty(0))
|
||||||
|
printf("Password is: ");
|
||||||
|
printf("%s\n", pwbuf);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -2: /* No password at all! */
|
||||||
|
return "";
|
||||||
|
|
||||||
|
case 0: /* No login - default */
|
||||||
|
default:
|
||||||
|
return "*";
|
||||||
|
|
||||||
|
case 1: /* user's name */
|
||||||
|
strncpy(pwbuf, user, sizeof pwbuf);
|
||||||
|
pwbuf[sizeof pwbuf - 1] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return pw_pwcrypt(pwbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
print_user(struct passwd * pwd, int pretty)
|
||||||
|
{
|
||||||
|
if (!pretty) {
|
||||||
|
char buf[_UC_MAXLINE];
|
||||||
|
|
||||||
|
fmtpwent(buf, pwd);
|
||||||
|
fputs(buf, stdout);
|
||||||
|
} else {
|
||||||
|
char *p;
|
||||||
|
struct group *grp = getgrgid(pwd->pw_gid);
|
||||||
|
char uname[60] = "User &", office[60] = "[None]",
|
||||||
|
wphone[60] = "[None]", hphone[60] = "[None]";
|
||||||
|
|
||||||
|
if ((p = strtok(pwd->pw_gecos, ",")) != NULL) {
|
||||||
|
strncpy(uname, p, sizeof uname);
|
||||||
|
uname[sizeof uname - 1] = '\0';
|
||||||
|
if ((p = strtok(NULL, ",")) != NULL) {
|
||||||
|
strncpy(office, p, sizeof office);
|
||||||
|
office[sizeof office - 1] = '\0';
|
||||||
|
if ((p = strtok(NULL, ",")) != NULL) {
|
||||||
|
strncpy(wphone, p, sizeof wphone);
|
||||||
|
wphone[sizeof wphone - 1] = '\0';
|
||||||
|
if ((p = strtok(NULL, "")) != NULL) {
|
||||||
|
strncpy(hphone, p, sizeof hphone);
|
||||||
|
hphone[sizeof hphone - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Handle '&' in gecos field
|
||||||
|
*/
|
||||||
|
if ((p = strchr(uname, '&')) != NULL) {
|
||||||
|
int l = strlen(pwd->pw_name);
|
||||||
|
int m = strlen(p);
|
||||||
|
|
||||||
|
memmove(p + l, p + 1, m);
|
||||||
|
memmove(p, pwd->pw_name, l);
|
||||||
|
*p = (char) toupper(*p);
|
||||||
|
}
|
||||||
|
printf("Login Name : %-10s #%-22ld Group : %-10s #%ld\n"
|
||||||
|
" Full Name : %s\n"
|
||||||
|
" Home : %-32.32s Class : %s\n"
|
||||||
|
" Shell : %-32.32s Office : %s\n"
|
||||||
|
"Work Phone : %-32.32s Home Phone : %s\n\n",
|
||||||
|
pwd->pw_name, (long) pwd->pw_uid,
|
||||||
|
grp ? grp->gr_name : "(invalid)", (long) pwd->pw_gid,
|
||||||
|
uname, pwd->pw_dir, pwd->pw_class,
|
||||||
|
pwd->pw_shell, office, wphone, hphone);
|
||||||
|
}
|
||||||
|
return X_ALLOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
pw_checkname(char *name, int gecos)
|
||||||
|
{
|
||||||
|
int l = 0;
|
||||||
|
char const *notch = gecos ? ":" : " :+-&#%$^()!@~*?<>=|\\/\"";
|
||||||
|
|
||||||
|
while (name[l]) {
|
||||||
|
if (strchr(notch, name[l]) != NULL || name[l] < ' ')
|
||||||
|
cmderr(X_CMDERR, "invalid character `%c' in field\n", name[l]);
|
||||||
|
++l;
|
||||||
|
}
|
||||||
|
if (!gecos && l > 8)
|
||||||
|
cmderr(X_CMDERR, "name too long `%s'\n", name);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
rmat(uid_t uid)
|
||||||
|
{
|
||||||
|
DIR *d = opendir("/var/at/jobs");
|
||||||
|
|
||||||
|
if (d != NULL) {
|
||||||
|
struct dirent *e;
|
||||||
|
|
||||||
|
while ((e = readdir(d)) != NULL) {
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (strncmp(e->d_name, ".lock", 5) != 0 &&
|
||||||
|
stat(e->d_name, &st) == 0 &&
|
||||||
|
!S_ISDIR(st.st_mode) &&
|
||||||
|
st.st_uid == uid) {
|
||||||
|
char tmp[MAXPATHLEN];
|
||||||
|
|
||||||
|
sprintf(tmp, "/usr/bin/atrm %s", e->d_name);
|
||||||
|
system(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
}
|
||||||
|
}
|
160
usr.sbin/pw/pwupd.c
Normal file
160
usr.sbin/pw/pwupd.c
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "pwupd.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
pwdb(char *arg,...)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
pid_t pid;
|
||||||
|
va_list ap;
|
||||||
|
char *args[8];
|
||||||
|
|
||||||
|
args[i++] = _PATH_PWD_MKDB;
|
||||||
|
va_start(ap, arg);
|
||||||
|
while (i < 6 && arg != NULL) {
|
||||||
|
args[i++] = arg;
|
||||||
|
arg = va_arg(ap, char *);
|
||||||
|
}
|
||||||
|
args[i++] = _PATH_MASTERPASSWD;
|
||||||
|
args[i] = NULL;
|
||||||
|
|
||||||
|
if ((pid = fork()) == -1) /* Error (errno set) */
|
||||||
|
i = -1;
|
||||||
|
else if (pid == 0) { /* Child */
|
||||||
|
execv(args[0], args);
|
||||||
|
_exit(1);
|
||||||
|
} else { /* Parent */
|
||||||
|
waitpid(pid, &i, 0);
|
||||||
|
if ((i = WEXITSTATUS(i)) != 0)
|
||||||
|
errno = EIO; /* set SOMETHING */
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
fmtpwentry(char *buf, struct passwd * pwd, int type)
|
||||||
|
{
|
||||||
|
int l;
|
||||||
|
char *pw;
|
||||||
|
|
||||||
|
pw = (pwd->pw_passwd == NULL || !*pwd->pw_passwd) ? "" : (type == PWF_MASTER) ? pwd->pw_passwd : "*";
|
||||||
|
|
||||||
|
if (type == PWF_PASSWD)
|
||||||
|
l = sprintf(buf, "%s:*:%ld:%ld:%s:%s:%s\n",
|
||||||
|
pwd->pw_name, (long) pwd->pw_uid, (long) pwd->pw_gid,
|
||||||
|
pwd->pw_gecos ? pwd->pw_gecos : "User &",
|
||||||
|
pwd->pw_dir, pwd->pw_shell);
|
||||||
|
else
|
||||||
|
l = sprintf(buf, "%s:%s:%ld:%ld:%s:%lu:%lu:%s:%s:%s\n",
|
||||||
|
pwd->pw_name, pw, (long) pwd->pw_uid, (long) pwd->pw_gid,
|
||||||
|
pwd->pw_class ? pwd->pw_class : "",
|
||||||
|
(unsigned long) pwd->pw_change,
|
||||||
|
(unsigned long) pwd->pw_expire,
|
||||||
|
pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
fmtpwent(char *buf, struct passwd * pwd)
|
||||||
|
{
|
||||||
|
return fmtpwentry(buf, pwd, PWF_STANDARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
pw_update(struct passwd * pwd, char const * user, int mode)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
endpwent();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, let's check the see if the database is alright
|
||||||
|
*/
|
||||||
|
if (pwdb("-c", NULL) == 0) { /* Check only */
|
||||||
|
char pfx[32];
|
||||||
|
char pwbuf[MAXPWLINE];
|
||||||
|
int l = sprintf(pfx, "%s:", user);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the passwd file first
|
||||||
|
*/
|
||||||
|
if (pwd == NULL)
|
||||||
|
*pwbuf = '\0';
|
||||||
|
else
|
||||||
|
fmtpwentry(pwbuf, pwd, PWF_PASSWD);
|
||||||
|
if ((rc = fileupdate(_PATH_PASSWD, 0644, pwbuf, pfx, l, mode)) != 0) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Then the master.passwd file
|
||||||
|
*/
|
||||||
|
if (pwd != NULL)
|
||||||
|
fmtpwentry(pwbuf, pwd, PWF_MASTER);
|
||||||
|
if ((rc = fileupdate(_PATH_MASTERPASSWD, 0644, pwbuf, pfx, l, mode)) != 0)
|
||||||
|
rc = pwdb(NULL) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
addpwent(struct passwd * pwd)
|
||||||
|
{
|
||||||
|
return pw_update(pwd, pwd->pw_name, UPD_CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
chgpwent(char const * login, struct passwd * pwd)
|
||||||
|
{
|
||||||
|
return pw_update(pwd, login, UPD_REPLACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
delpwent(struct passwd * pwd)
|
||||||
|
{
|
||||||
|
return pw_update(NULL, pwd->pw_name, UPD_DELETE);
|
||||||
|
}
|
85
usr.sbin/pw/pwupd.h
Normal file
85
usr.sbin/pw/pwupd.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PWUPD_H_
|
||||||
|
#define _PWUPD_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
|
||||||
|
enum updtype
|
||||||
|
{
|
||||||
|
UPD_DELETE = -1,
|
||||||
|
UPD_CREATE = 0,
|
||||||
|
UPD_REPLACE = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
__BEGIN_DECLS
|
||||||
|
int fileupdate __P((char const * fname, mode_t fm, char const * nline, char const * pfx, int pfxlen, int updmode));
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
|
enum pwdfmttype
|
||||||
|
{
|
||||||
|
PWF_STANDARD, /* MASTER format but with '*' as password */
|
||||||
|
PWF_PASSWD, /* V7 format */
|
||||||
|
PWF_GROUP = PWF_PASSWD,
|
||||||
|
PWF_MASTER /* MASTER format with password */
|
||||||
|
};
|
||||||
|
|
||||||
|
__BEGIN_DECLS
|
||||||
|
int addpwent __P((struct passwd * pwd));
|
||||||
|
int delpwent __P((struct passwd * pwd));
|
||||||
|
int chgpwent __P((char const * login, struct passwd * pwd));
|
||||||
|
int fmtpwent __P((char *buf, struct passwd * pwd));
|
||||||
|
int fmtpwentry __P((char *buf, struct passwd * pwd, int type));
|
||||||
|
int addgrent __P((struct group * grp));
|
||||||
|
int delgrent __P((struct group * grp));
|
||||||
|
int chggrent __P((char const * name, struct group * grp));
|
||||||
|
int fmtgrent __P((char *buf, struct group * grp));
|
||||||
|
int fmtgrentry __P((char *buf, struct group * grp, int type));
|
||||||
|
int editgroups __P((char *name, char **groups));
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
|
#define MAXGROUPS 200
|
||||||
|
#define MAXPWLINE 1024
|
||||||
|
|
||||||
|
__BEGIN_DECLS
|
||||||
|
void copymkdir __P((char const * dir, char const * skel, mode_t mode, uid_t uid, gid_t gid));
|
||||||
|
void rm_r __P((char const * dir, uid_t uid));
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
|
#endif /* !_PWUPD_H */
|
77
usr.sbin/pw/rm_r.c
Normal file
77
usr.sbin/pw/rm_r.c
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer as
|
||||||
|
* the first lines of this file unmodified.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by David L. Nugent.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#include "pwupd.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
rm_r(char const * dir, uid_t uid)
|
||||||
|
{
|
||||||
|
DIR *d = opendir(dir);
|
||||||
|
|
||||||
|
if (d != NULL) {
|
||||||
|
struct dirent *e;
|
||||||
|
struct stat st;
|
||||||
|
char file[MAXPATHLEN];
|
||||||
|
|
||||||
|
while ((e = readdir(d)) != NULL) {
|
||||||
|
if (strcmp(e->d_name, ".") != 0 && strcmp(e->d_name, "..") != 0) {
|
||||||
|
sprintf(file, "%s/%s", dir, e->d_name);
|
||||||
|
if (lstat(file, &st) == 0) { /* Need symlinks, not
|
||||||
|
* linked file */
|
||||||
|
if (S_ISDIR(st.st_mode)) /* Directory - recurse */
|
||||||
|
rm_r(file, uid);
|
||||||
|
else {
|
||||||
|
if (S_ISLNK(st.st_mode) || st.st_uid == uid)
|
||||||
|
remove(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
if (lstat(dir, &st) == 0) {
|
||||||
|
if (S_ISLNK(st.st_mode))
|
||||||
|
remove(dir);
|
||||||
|
else if (st.st_uid == uid)
|
||||||
|
rmdir(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user