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