
|
pw_user.c
/*-
* Copyright (C) 1996
* David L. Nugent. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD: src/usr.sbin/pw/pw_user.c,v 1.57.8.3 2007/05/04 17:36:37 le Exp $";
#endif /* not lint */
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
#include <sys/param.h>
#include <dirent.h>
#include <paths.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <utmp.h>
#if defined(USE_MD5RAND)
#include <md5.h>
#endif
#include "pw.h"
#include "bitmap.h"
#if (MAXLOGNAME-1) > UT_NAMESIZE
#define LOGNAMESIZE UT_NAMESIZE
#else
#define LOGNAMESIZE (MAXLOGNAME-1)
#endif
static char locked_str[] = "*LOCKED*";
static int print_user(struct passwd * pwd, int pretty, int v7);
static uid_t pw_uidpolicy(struct userconf * cnf, struct cargs * args);
static uid_t pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer);
static 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 *shell_path(char const * path, char *shells[], char *sh);
static void rmat(uid_t uid);
static void rmopie(char const * name);
/*-
* -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
* -H fd encrypted 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)
{
int rc, edited = 0;
char *p = NULL;
char *passtmp;
struct carg *a_name;
struct carg *a_uid;
struct carg *arg;
struct passwd *pwd = NULL;
struct group *grp;
struct stat st;
char line[_PASSWORD_LEN+1];
FILE *fp;
mode_t dmode;
char *dmode_c;
void *set = NULL;
static struct passwd fakeuser =
{
NULL,
"*",
-1,
-1,
0,
"",
"User &",
"/nonexistent",
"/bin/sh",
0
#if defined(__FreeBSD__)
,0
#endif
};
/*
* With M_NEXT, we only need to return the
* next uid to stdout
*/
if (mode == M_NEXT)
{
uid_t next = pw_uidpolicy(cnf, args);
if (getarg(args, 'q'))
return next;
printf("%ld:", (long)next);
pw_group(cnf, mode, args);
return EXIT_SUCCESS;
}
/*
* We can do all of the common legwork here
*/
if ((arg = getarg(args, 'b')) != NULL) {
cnf->home = arg->val;
}
if ((arg = getarg(args, 'M')) != NULL) {
dmode_c = arg->val;
if ((set = setmode(dmode_c)) == NULL)
errx(EX_DATAERR, "invalid directory creation mode '%s'",
dmode_c);
dmode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
free(set);
cnf->homemode = dmode;
}
/*
* If we'll need to use it or we're updating it,
* then create the base home directory if necessary
*/
if (arg != NULL || getarg(args, 'm') != NULL) {
int l = strlen(cnf->home);
if (l > 1 && cnf->home[l-1] == '/') /* Shave off any trailing path delimiter */
cnf->home[--l] = '\0';
if (l < 2 || *cnf->home != '/') /* Check for absolute path name */
errx(EX_DATAERR, "invalid base directory for home '%s'", cnf->home);
if (stat(cnf->home, &st) == -1) {
char dbuf[MAXPATHLEN];
/*
* This is a kludge especially for Joerg :)
* If the home directory would be created in the root partition, then
* we really create it under /usr which is likely to have more space.
* But we create a symlink from cnf->home -> "/usr" -> cnf->home
*/
if (strchr(cnf->home+1, '/') == NULL) {
strcpy(dbuf, "/usr");
strncat(dbuf, cnf->home, MAXPATHLEN-5);
if (mkdir(dbuf, cnf->homemode) != -1 || errno == EEXIST) {
chown(dbuf, 0, 0);
/*
* Skip first "/" and create symlink:
* /home -> usr/home
*/
symlink(dbuf+1, cnf->home);
}
/* If this falls, fall back to old method */
}
strlcpy(dbuf, cnf->home, sizeof(dbuf));
p = dbuf;
if (stat(dbuf, &st) == -1) {
while ((p = strchr(++p, '/')) != NULL) {
*p = '\0';
if (stat(dbuf, &st) == -1) {
if (mkdir(dbuf, cnf->homemode) == -1)
goto direrr;
chown(dbuf, 0, 0);
} else if (!S_ISDIR(st.st_mode))
errx(EX_OSFILE, "'%s' (root home parent) is not a directory", dbuf);
*p = '/';
}
}
if (stat(dbuf, &st) == -1) {
if (mkdir(dbuf, cnf->homemode) == -1) {
direrr: err(EX_OSFILE, "mkdir '%s'", dbuf);
}
chown(dbuf, 0, 0);
}
} else if (!S_ISDIR(st.st_mode))
errx(EX_OSFILE, "root home `%s' is not a directory", cnf->home);
}
if ((arg = getarg(args, 'e')) != NULL)
cnf->expire_days = atoi(arg->val);
if ((arg = getarg(args, 'y')) != NULL)
cnf->nispasswd = arg->val;
if ((arg = getarg(args, 'p')) != NULL && arg->val)
cnf->password_days = atoi(arg->val);
if ((arg = getarg(args, 'g')) != NULL) {
if (!*(p = arg->val)) /* Handle empty group list specially */
cnf->default_group = "";
else {
if ((grp = GETGRNAM(p)) == NULL) {
if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL)
errx(EX_NOUSER, "group `%s' does not exist", p);
}
cnf->default_group = newstr(grp->gr_name);
}
}
if ((arg = getarg(args, 'L')) != NULL)
cnf->default_class = pw_checkname((u_char *)arg->val, 0);
if ((arg = getarg(args, 'G')) != NULL && arg->val) {
int i = 0;
for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
if ((grp = GETGRNAM(p)) == NULL) {
if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL)
errx(EX_NOUSER, "group `%s' does not exist", p);
}
if (extendarray(&cnf->groups, &cnf->numgroups, i + 2) != -1)
cnf->groups[i++] = newstr(grp->gr_name);
}
while (i < cnf->numgroups)
cnf->groups[i++] = NULL;
}
if ((arg = getarg(args, 'k')) != NULL) {
if (stat(cnf->dotdir = arg->val, &st) == -1 || !S_ISDIR(st.st_mode))
errx(EX_OSFILE, "skeleton `%s' is not a directory or does not exist", cnf->dotdir);
}
if ((arg = getarg(args, 's')) != NULL)
cnf->shell_default = arg->val;
if ((arg = getarg(args, 'w')) != NULL)
cnf->default_password = boolean_val(arg->val, cnf->default_password);
if (mode == M_ADD && getarg(args, 'D')) {
if (getarg(args, 'n') != NULL)
errx(EX_DATAERR, "can't combine `-D' with `-n name'");
if ((arg = getarg(args, 'u')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
if ((cnf->min_uid = (uid_t) atoi(p)) == 0)
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;
}
arg = getarg(args, 'C');
if (write_userconfig(arg ? arg->val : NULL))
return EXIT_SUCCESS;
warn("config update");
return EX_IOERR;
}
if (mode == M_PRINT && getarg(args, 'a')) {
int pretty = getarg(args, 'P') != NULL;
int v7 = getarg(args, '7') != NULL;
SETPWENT();
while ((pwd = GETPWENT()) != NULL)
print_user(pwd, pretty, v7);
ENDPWENT();
return EXIT_SUCCESS;
}
if ((a_name = getarg(args, 'n')) != NULL)
pwd = GETPWNAM(pw_checkname((u_char *)a_name->val, 0));
a_uid = getarg(args, 'u');
if (a_uid == NULL) {
if (a_name == NULL)
errx(EX_DATAERR, "user name or id required");
/*
* Determine whether 'n' switch is name or uid - we don't
* really don't really care which we have, but we need to
* know.
*/
if (mode != M_ADD && pwd == NULL
&& strspn(a_name->val, "0123456789") == strlen(a_name->val)
&& atoi(a_name->val) > 0) { /* Assume uid */
(a_uid = |