#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include <userdefs.h>
#include <errno.h>
#include <project.h>
#include <unistd.h>
#include <user_attr.h>
#include <libcmdutils.h>
#include "users.h"
#include "messages.h"
#include "userdisp.h"
#include "funcs.h"
extern struct userdefs *getusrdef();
extern void dispusrdef();
static void cleanup();
extern int check_perm(), valid_expire();
extern int putusrdef(), valid_uid();
extern int call_passmgmt(), edit_group(), create_home();
extern int edit_project();
extern int **valid_lgroup();
extern projid_t **valid_lproject();
extern void update_def(struct userdefs *);
extern void import_def(struct userdefs *);
extern int get_default_zfs_flags();
static uid_t uid;
static char *logname;
static struct userdefs *usrdefs;
char *cmdname;
static char homedir[ PATH_MAX + 1 ];
static char gidstring[32];
static gid_t gid;
static char uidstring[32];
static char *uidstr = NULL;
static char *base_dir = NULL;
static char *group = NULL;
static char *grps = NULL;
static char *dir = NULL;
static char *shell = NULL;
static char *comment = NULL;
static char *skel_dir = NULL;
static long inact;
static char *inactstr = NULL;
static char inactstring[10];
static char *expirestr = NULL;
static char *projects = NULL;
static char *usertype = NULL;
typedef enum {
BASEDIR = 0,
SKELDIR,
SHELL
} path_opt_t;
static void valid_input(path_opt_t, const char *);
int
main(argc, argv)
int argc;
char *argv[];
{
int ch, ret, mflag = 0, oflag = 0, Dflag = 0;
int zflag = 0, Zflag = 0, **gidlist = NULL;
projid_t **projlist = NULL;
char *ptr;
struct group *g_ptr;
struct project p_ptr;
char mybuf[PROJECT_BUFSZ];
struct stat statbuf;
int warning;
int busy = 0;
char **nargv;
int argindex;
int zfs_flags = 0;
cmdname = argv[0];
if (geteuid() != 0) {
errmsg(M_PERM_DENIED);
exit(EX_NO_PERM);
}
opterr = 0;
usertype = getusertype(argv[0]);
change_key(USERATTR_TYPE_KW, usertype);
while ((ch = getopt(argc, argv,
"b:c:Dd:e:f:G:g:k:mzZop:s:u:A:P:R:K:")) != EOF)
switch (ch) {
case 'b':
base_dir = optarg;
break;
case 'c':
comment = optarg;
break;
case 'D':
Dflag++;
break;
case 'd':
dir = optarg;
break;
case 'e':
expirestr = optarg;
break;
case 'f':
inactstr = optarg;
break;
case 'G':
grps = optarg;
break;
case 'g':
group = optarg;
break;
case 'k':
skel_dir = optarg;
break;
case 'm':
mflag++;
break;
case 'o':
oflag++;
break;
case 'p':
projects = optarg;
break;
case 's':
shell = optarg;
break;
case 'u':
uidstr = optarg;
break;
case 'Z':
Zflag++;
break;
case 'z':
zflag++;
break;
case 'A':
change_key(USERATTR_AUTHS_KW, optarg);
break;
case 'P':
change_key(USERATTR_PROFILES_KW, optarg);
break;
case 'R':
if (is_role(usertype)) {
errmsg(M_ARUSAGE);
exit(EX_SYNTAX);
}
change_key(USERATTR_ROLES_KW, optarg);
break;
case 'K':
change_key(NULL, optarg);
break;
default:
case '?':
if (is_role(usertype))
errmsg(M_ARUSAGE);
else
errmsg(M_AUSAGE);
exit(EX_SYNTAX);
}
if (((!mflag) && (zflag || Zflag)) || (zflag && Zflag) ||
(mflag > 1 && (zflag || Zflag))) {
if (is_role(usertype))
errmsg(M_ARUSAGE);
else
errmsg(M_AUSAGE);
exit(EX_SYNTAX);
}
usrdefs = getusrdef(usertype);
if (Dflag) {
if (optind != argc) {
if (is_role(usertype))
errmsg(M_ARUSAGE);
else
errmsg(M_AUSAGE);
exit(EX_SYNTAX);
}
if (uidstr != NULL || oflag || grps != NULL ||
dir != NULL || mflag || comment != NULL) {
if (is_role(usertype))
errmsg(M_ARUSAGE);
else
errmsg(M_AUSAGE);
exit(EX_SYNTAX);
}
if (group != NULL) {
switch (valid_group(group, &g_ptr, &warning)) {
case INVALID:
errmsg(M_INVALID, group, "group id");
exit(EX_BADARG);
case TOOBIG:
errmsg(M_TOOBIG, "gid", group);
exit(EX_BADARG);
case RESERVED:
case UNIQUE:
errmsg(M_GRP_NOTUSED, group);
exit(EX_NAME_NOT_EXIST);
}
if (warning)
warningmsg(warning, group);
usrdefs->defgroup = g_ptr->gr_gid;
usrdefs->defgname = g_ptr->gr_name;
}
if (projects != NULL) {
switch (valid_project(projects, &p_ptr, mybuf,
sizeof (mybuf), &warning)) {
case INVALID:
errmsg(M_INVALID, projects, "project id");
exit(EX_BADARG);
case TOOBIG:
errmsg(M_TOOBIG, "projid", projects);
exit(EX_BADARG);
case UNIQUE:
errmsg(M_PROJ_NOTUSED, projects);
exit(EX_NAME_NOT_EXIST);
}
if (warning)
warningmsg(warning, projects);
usrdefs->defproj = p_ptr.pj_projid;
usrdefs->defprojname = p_ptr.pj_name;
}
if (base_dir != NULL) {
valid_input(BASEDIR, base_dir);
usrdefs->defparent = base_dir;
}
if (inactstr != NULL) {
inact = strtol(inactstr, &ptr, 10);
if (*ptr || inact < 0) {
errmsg(M_INVALID, inactstr,
"inactivity period");
exit(EX_BADARG);
}
usrdefs->definact = inact;
}
if (expirestr != NULL) {
if (*expirestr) {
if (valid_expire(expirestr, (time_t *)0)
== INVALID) {
errmsg(M_INVALID, expirestr,
"expiration date");
exit(EX_BADARG);
}
usrdefs->defexpire = expirestr;
} else
usrdefs->defexpire = "";
}
if (shell != NULL) {
valid_input(SHELL, shell);
usrdefs->defshell = shell;
}
if (skel_dir != NULL) {
valid_input(SKELDIR, skel_dir);
usrdefs->defskel = skel_dir;
}
update_def(usrdefs);
if (putusrdef(usrdefs, usertype) < 0) {
errmsg(M_UPDATE, "created");
exit(EX_UPDATE);
}
dispusrdef(stdout, (D_ALL & ~D_RID), usertype);
exit(EX_SUCCESS);
}
if (optind != argc - 1 || (skel_dir != NULL && !mflag)) {
if (is_role(usertype))
errmsg(M_ARUSAGE);
else
errmsg(M_AUSAGE);
exit(EX_SYNTAX);
}
logname = argv[optind];
switch (valid_login(logname, (struct passwd **)NULL, &warning)) {
case INVALID:
errmsg(M_INVALID, logname, "login name");
exit(EX_BADARG);
case NOTUNIQUE:
errmsg(M_USED, logname);
exit(EX_NAME_EXISTS);
case LONGNAME:
errmsg(M_TOO_LONG, logname);
exit(EX_BADARG);
}
if (warning)
warningmsg(warning, logname);
if (uidstr != NULL) {
errno = 0;
uid = (uid_t)strtol(uidstr, &ptr, (int)10);
if (*ptr || errno == ERANGE) {
errmsg(M_INVALID, uidstr, "user id");
exit(EX_BADARG);
}
switch (valid_uid(uid, NULL)) {
case NOTUNIQUE:
if (!oflag) {
errmsg(M_UID_USED, uid);
exit(EX_ID_EXISTS);
}
break;
case RESERVED:
errmsg(M_RESERVED, uid);
break;
case TOOBIG:
errmsg(M_TOOBIG, "uid", uid);
exit(EX_BADARG);
break;
}
} else {
if (findnextuid(DEFRID+1, MAXUID, &uid) != 0) {
errmsg(M_INVALID, "default id", "user id");
exit(EX_ID_EXISTS);
}
}
if (group != NULL) {
switch (valid_group(group, &g_ptr, &warning)) {
case INVALID:
errmsg(M_INVALID, group, "group id");
exit(EX_BADARG);
case TOOBIG:
errmsg(M_TOOBIG, "gid", group);
exit(EX_BADARG);
case RESERVED:
case UNIQUE:
errmsg(M_GRP_NOTUSED, group);
exit(EX_NAME_NOT_EXIST);
}
if (warning)
warningmsg(warning, group);
gid = g_ptr->gr_gid;
} else gid = usrdefs->defgroup;
if (grps != NULL) {
if (!*grps)
grps = (char *)0;
else if (!(gidlist = valid_lgroup(grps, gid)))
exit(EX_BADARG);
}
if (projects != NULL) {
if (! *projects)
projects = (char *)0;
else if (! (projlist = valid_lproject(projects)))
exit(EX_BADARG);
}
if (base_dir != NULL)
valid_input(BASEDIR, base_dir);
else
base_dir = usrdefs->defparent;
if (dir == NULL) {
(void) sprintf(homedir, "%s/%s", base_dir, logname);
} else if (REL_PATH(dir)) {
errmsg(M_RELPATH, dir);
exit(EX_BADARG);
} else
(void) strcpy(homedir, dir);
if (mflag) {
if (stat(homedir, &statbuf) == 0) {
mflag = 0;
if (check_perm(statbuf, uid, gid, S_IXOTH) != 0)
errmsg(M_NO_PERM, logname, homedir);
}
}
if (shell != NULL)
valid_input(SHELL, shell);
else
shell = usrdefs->defshell;
if (skel_dir != NULL)
valid_input(SKELDIR, skel_dir);
else
skel_dir = usrdefs->defskel;
if (inactstr != NULL) {
inact = strtol(inactstr, &ptr, 10);
if (*ptr || inact < 0) {
errmsg(M_INVALID, inactstr, "inactivity period");
exit(EX_BADARG);
}
} else inact = usrdefs->definact;
if (expirestr != NULL) {
if (*expirestr) {
if (valid_expire(expirestr, (time_t *)0) == INVALID) {
errmsg(M_INVALID, expirestr, "expiration date");
exit(EX_BADARG);
}
usrdefs->defexpire = expirestr;
} else
expirestr = (char *)0;
} else expirestr = usrdefs->defexpire;
import_def(usrdefs);
nargv = malloc((30 + nkeys * 2) * sizeof (char *));
argindex = 0;
nargv[argindex++] = PASSMGMT;
nargv[argindex++] = "-a";
if (comment != NULL) {
nargv[argindex++] = "-c";
nargv[argindex++] = comment;
}
nargv[argindex++] = "-h";
nargv[argindex++] = homedir;
nargv[argindex++] = "-g";
(void) sprintf(gidstring, "%u", gid);
nargv[argindex++] = gidstring;
nargv[argindex++] = "-s";
nargv[argindex++] = shell;
nargv[argindex++] = "-f";
(void) sprintf(inactstring, "%ld", inact);
nargv[argindex++] = inactstring;
if (expirestr != NULL) {
nargv[argindex++] = "-e";
nargv[argindex++] = expirestr;
}
nargv[argindex++] = "-u";
(void) sprintf(uidstring, "%u", uid);
nargv[argindex++] = uidstring;
if (oflag) nargv[argindex++] = "-o";
if (nkeys > 1)
addkey_args(nargv, &argindex);
nargv[argindex++] = logname;
nargv[argindex++] = NULL;
ret = PEX_FAILED;
while (busy < 3 && ret != PEX_SUCCESS) {
switch (ret = call_passmgmt(nargv)) {
case PEX_SUCCESS:
break;
case PEX_BUSY:
busy++;
break;
case PEX_HOSED_FILES:
errmsg(M_HOSED_FILES);
exit(EX_INCONSISTENT);
break;
case PEX_SYNTAX:
case PEX_BADARG:
if (is_role(usertype))
errmsg(M_ARUSAGE);
else
errmsg(M_AUSAGE);
exit(EX_SYNTAX);
break;
case PEX_BADUID:
if (uidstr != NULL) {
errmsg(M_UID_USED, uid);
exit(EX_ID_EXISTS);
} else {
if (findnextuid(DEFRID+1, MAXUID, &uid) != 0) {
errmsg(M_INVALID, "default id",
"user id");
exit(EX_ID_EXISTS);
}
(void) sprintf(uidstring, "%u", uid);
}
break;
case PEX_BADNAME:
errmsg(M_USED, logname);
exit(EX_NAME_EXISTS);
break;
default:
errmsg(M_UPDATE, "created");
exit(ret);
break;
}
}
if (busy == 3) {
errmsg(M_UPDATE, "created");
exit(ret);
}
if ((grps != NULL) && edit_group(logname, (char *)0, gidlist, 0)) {
errmsg(M_UPDATE, "created");
cleanup(logname);
exit(EX_UPDATE);
}
if ((projects != NULL) &&
edit_project(logname, (char *)NULL, projlist, 0)) {
errmsg(M_UPDATE, "created");
cleanup(logname);
exit(EX_UPDATE);
}
if (mflag) {
zfs_flags = get_default_zfs_flags();
if (zflag || mflag > 1)
zfs_flags |= MANAGE_ZFS;
else if (Zflag)
zfs_flags &= ~MANAGE_ZFS;
ret = create_home(homedir, skel_dir, uid, gid, zfs_flags);
}
if (ret != EX_SUCCESS) {
(void) edit_project(logname, (char *)NULL, (projid_t **)NULL,
0);
(void) edit_group(logname, (char *)0, (int **)0, 1);
cleanup(logname);
exit(EX_HOMEDIR);
}
return (ret);
}
static void
cleanup(logname)
char *logname;
{
char *nargv[4];
nargv[0] = PASSMGMT;
nargv[1] = "-d";
nargv[2] = logname;
nargv[3] = NULL;
switch (call_passmgmt(nargv)) {
case PEX_SUCCESS:
break;
case PEX_SYNTAX:
if (is_role(usertype))
errmsg(M_ARUSAGE);
else
errmsg(M_AUSAGE);
break;
case PEX_BADUID:
errmsg(M_UID_USED, uid);
break;
case PEX_BADNAME:
errmsg(M_USED, logname);
break;
default:
errmsg(M_UPDATE, "created");
break;
}
}
void
valid_input(path_opt_t opt, const char *input)
{
struct stat statbuf;
if (REL_PATH(input)) {
errmsg(M_RELPATH, input);
exit(EX_BADARG);
}
if (stat(input, &statbuf) == -1) {
errmsg(M_INVALID, input, "path");
exit(EX_BADARG);
}
if (opt == SHELL) {
if (!S_ISREG(statbuf.st_mode) ||
(statbuf.st_mode & 0555) != 0555) {
errmsg(M_INVALID, input, "shell");
exit(EX_BADARG);
}
} else {
if (!S_ISDIR(statbuf.st_mode)) {
errmsg(M_INVALID, input, "directory");
exit(EX_BADARG);
}
}
}