/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "users.h" #include "messages.h" #include "userdisp.h" #include "funcs.h" /* * useradd [-u uid [-o] | -g group | -G group [[, group]...] | -d dir [-m] * | -s shell | -c comment | -k skel_dir] ] * [ -A authorization [, authorization ...]] * [ -P profile [, profile ...]] * [ -K key=value ] * [ -R role [, role ...]] [-p project [, project ...]] login * useradd -D [ -g group ] [ -b base_dir | -f inactive | -e expire ] * [ -A authorization [, authorization ...]] * [ -P profile [, profile ...]] [ -K key=value ] * [ -R role [, role ...]] [-p project [, project ...]] login * * This command adds new user logins to the system. Arguments are: * * uid - an integer * group - an existing group's integer ID or char string name * dir - home directory * shell - a program to be used as a shell * comment - any text string * skel_dir - a skeleton directory * base_dir - a directory * login - a string of printable chars except colon(:) * authorization - One or more comma separated authorizations defined * in auth_attr(4). * profile - One or more comma separated execution profiles defined * in prof_attr(4) * role - One or more comma-separated role names defined in user_attr(4) * project - One or more comma-separated project names or numbers * */ extern struct userdefs *getusrdef(); extern void dispusrdef(); static void cleanup(); extern uid_t findnextuid(void); 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 *); static uid_t uid; /* new uid */ static char *logname; /* login name to add */ static struct userdefs *usrdefs; /* defaults for useradd */ char *cmdname; static char homedir[ PATH_MAX + 1 ]; /* home directory */ static char gidstring[32]; /* group id string representation */ static gid_t gid; /* gid of new login */ static char uidstring[32]; /* user id string representation */ static char *uidstr = NULL; /* uid from command line */ static char *base_dir = NULL; /* base_dir from command line */ static char *group = NULL; /* group from command line */ static char *grps = NULL; /* multi groups from command line */ static char *dir = NULL; /* home dir from command line */ static char *shell = NULL; /* shell from command line */ static char *comment = NULL; /* comment from command line */ static char *skel_dir = NULL; /* skel dir from command line */ static long inact; /* inactive days */ static char *inactstr = NULL; /* inactive from command line */ static char inactstring[10]; /* inactivity string representation */ static char *expirestr = NULL; /* expiration date from command line */ static char *projects = NULL; /* project id's from command line */ static char *usertype = NULL; /* type of user, either role or normal */ int main(argc, argv) int argc; char *argv[]; { int ch, ret, mflag = 0, oflag = 0, Dflag = 0, **gidlist; projid_t **projlist; char *ptr; /* loc in a str, may be set by strtol */ struct group *g_ptr; struct project p_ptr; char mybuf[PROJECT_BUFSZ]; struct stat statbuf; /* status buffer for stat */ int warning; int busy = 0; char **nargv; /* arguments for execvp of passmgmt */ int argindex; /* argument index into nargv */ cmdname = argv[0]; if (geteuid() != 0) { errmsg(M_PERM_DENIED); exit(EX_NO_PERM); } opterr = 0; /* no print errors from getopt */ usertype = getusertype(argv[0]); change_key(USERATTR_TYPE_KW, usertype); while ((ch = getopt(argc, argv, "b:c:Dd:e:f:G:g:k:mop: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 '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); } /* get defaults for adding new users */ usrdefs = getusrdef(usertype); if (Dflag) { /* DISPLAY mode */ /* check syntax */ if (optind != argc) { if (is_role(usertype)) errmsg(M_ARUSAGE); else errmsg(M_AUSAGE); exit(EX_SYNTAX); } if (uidstr || oflag || grps || dir || mflag || shell || comment || skel_dir) { if (is_role(usertype)) errmsg(M_ARUSAGE); else errmsg(M_AUSAGE); exit(EX_SYNTAX); } /* Group must be an existing group */ if (group) { switch (valid_group(group, &g_ptr, &warning)) { case INVALID: errmsg(M_INVALID, group, "group id"); exit(EX_BADARG); /*NOTREACHED*/ case TOOBIG: errmsg(M_TOOBIG, "gid", group); exit(EX_BADARG); /*NOTREACHED*/ 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; } /* project must be an existing project */ if (projects) { switch (valid_project(projects, &p_ptr, mybuf, sizeof (mybuf), &warning)) { case INVALID: errmsg(M_INVALID, projects, "project id"); exit(EX_BADARG); /*NOTREACHED*/ case TOOBIG: errmsg(M_TOOBIG, "projid", projects); exit(EX_BADARG); /*NOTREACHED*/ 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; } /* base_dir must be an existing directory */ if (base_dir) { if (REL_PATH(base_dir)) { errmsg(M_RELPATH, base_dir); exit(EX_BADARG); } if (stat(base_dir, &statbuf) < 0 && (statbuf.st_mode & S_IFMT) != S_IFDIR) { errmsg(M_INVALID, base_dir, "base directory"); exit(EX_BADARG); } usrdefs->defparent = base_dir; } /* inactivity period is an integer */ if (inactstr) { /* convert inactstr to integer */ inact = strtol(inactstr, &ptr, 10); if (*ptr || inact < 0) { errmsg(M_INVALID, inactstr, "inactivity period"); exit(EX_BADARG); } usrdefs->definact = inact; } /* expiration string is a date, newer than today */ if (expirestr) { if (*expirestr) { if (valid_expire(expirestr, (time_t *)0) == INVALID) { errmsg(M_INVALID, expirestr, "expiration date"); exit(EX_BADARG); } usrdefs->defexpire = expirestr; } else /* Unset the expiration date */ usrdefs->defexpire = ""; } update_def(usrdefs); /* change defaults for useradd */ if (putusrdef(usrdefs, usertype) < 0) { errmsg(M_UPDATE, "created"); exit(EX_UPDATE); } /* Now, display */ dispusrdef(stdout, (D_ALL & ~D_RID), usertype); exit(EX_SUCCESS); } /* ADD mode */ /* check syntax */ if (optind != argc - 1 || base_dir || (skel_dir && !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); /*NOTREACHED*/ case NOTUNIQUE: errmsg(M_USED, logname); exit(EX_NAME_EXISTS); /*NOTREACHED*/ } if (warning) warningmsg(warning, logname); if (uidstr) { /* convert uidstr to integer */ 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) { /* override not specified */ 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 ((uid = findnextuid()) < 0) { errmsg(M_INVALID, "default id", "user id"); exit(EX_ID_EXISTS); } } if (group) { switch (valid_group(group, &g_ptr, &warning)) { case INVALID: errmsg(M_INVALID, group, "group id"); exit(EX_BADARG); /*NOTREACHED*/ case TOOBIG: errmsg(M_TOOBIG, "gid", group); exit(EX_BADARG); /*NOTREACHED*/ case RESERVED: case UNIQUE: errmsg(M_GRP_NOTUSED, group); exit(EX_NAME_NOT_EXIST); /*NOTREACHED*/ } if (warning) warningmsg(warning, group); gid = g_ptr->gr_gid; } else gid = usrdefs->defgroup; if (grps) { if (!*grps) /* ignore -G "" */ grps = (char *)0; else if (!(gidlist = valid_lgroup(grps, gid))) exit(EX_BADARG); } if (projects) { if (! *projects) projects = (char *)0; else if (! (projlist = valid_lproject(projects))) exit(EX_BADARG); } if (!dir) { /* set homedir to home directory made from base_dir */ (void) sprintf(homedir, "%s/%s", usrdefs->defparent, logname); } else if (REL_PATH(dir)) { errmsg(M_RELPATH, dir); exit(EX_BADARG); } else (void) strcpy(homedir, dir); if (mflag) { /* Does home dir. already exist? */ if (stat(homedir, &statbuf) == 0) { /* directory exists - don't try to create */ mflag = 0; if (check_perm(statbuf, uid, gid, S_IXOTH) != 0) errmsg(M_NO_PERM, logname, homedir); } } if (shell) { if (REL_PATH(shell)) { errmsg(M_RELPATH, shell); exit(EX_BADARG); } /* check that shell is an executable file */ if (stat(shell, &statbuf) < 0 || (statbuf.st_mode & S_IFMT) != S_IFREG || (statbuf.st_mode & 0555) != 0555) { errmsg(M_INVALID, shell, "shell"); exit(EX_BADARG); } } else shell = usrdefs->defshell; if (skel_dir) { if (REL_PATH(skel_dir)) { errmsg(M_RELPATH, skel_dir); exit(EX_BADARG); } if (stat(skel_dir, &statbuf) < 0 && (statbuf.st_mode & S_IFMT) != S_IFDIR) { errmsg(M_INVALID, skel_dir, "directory"); exit(EX_BADARG); } } else skel_dir = usrdefs->defskel; if (inactstr) { /* convert inactstr to integer */ inact = strtol(inactstr, &ptr, 10); if (*ptr || inact < 0) { errmsg(M_INVALID, inactstr, "inactivity period"); exit(EX_BADARG); } } else inact = usrdefs->definact; /* expiration string is a date, newer than today */ if (expirestr) { if (*expirestr) { if (valid_expire(expirestr, (time_t *)0) == INVALID) { errmsg(M_INVALID, expirestr, "expiration date"); exit(EX_BADARG); } usrdefs->defexpire = expirestr; } else /* Unset the expiration date */ expirestr = (char *)0; } else expirestr = usrdefs->defexpire; import_def(usrdefs); /* must now call passmgmt */ /* set up arguments to passmgmt in nargv array */ nargv = malloc((30 + nkeys * 2) * sizeof (char *)); argindex = 0; nargv[argindex++] = "passmgmt"; nargv[argindex++] = "-a"; /* add */ if (comment) { /* comment */ nargv[argindex++] = "-c"; nargv[argindex++] = comment; } /* flags for home directory */ nargv[argindex++] = "-h"; nargv[argindex++] = homedir; /* set gid flag */ nargv[argindex++] = "-g"; (void) sprintf(gidstring, "%ld", gid); nargv[argindex++] = gidstring; /* shell */ nargv[argindex++] = "-s"; nargv[argindex++] = shell; /* set inactive */ nargv[argindex++] = "-f"; (void) sprintf(inactstring, "%ld", inact); nargv[argindex++] = inactstring; /* set expiration date */ if (expirestr) { nargv[argindex++] = "-e"; nargv[argindex++] = expirestr; } /* set uid flag */ nargv[argindex++] = "-u"; (void) sprintf(uidstring, "%ld", uid); nargv[argindex++] = uidstring; if (oflag) nargv[argindex++] = "-o"; if (nkeys > 1) addkey_args(nargv, &argindex); /* finally - login name */ nargv[argindex++] = logname; /* set the last to null */ nargv[argindex++] = NULL; /* now call passmgmt */ ret = PEX_FAILED; /* * If call_passmgmt fails for any reason other than PEX_BADUID, exit * is invoked with an appropriate error message. If PEX_BADUID is * returned, then if the user specified the ID, exit is invoked * with an appropriate error message. Otherwise we try to pick a * different ID and try again. If we run out of IDs, i.e. no more * users can be created, then -1 is returned and we terminate via exit. * If PEX_BUSY is returned we increment a count, since we will stop * trying if PEX_BUSY reaches 3. For PEX_SUCCESS we immediately * terminate the loop. */ 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: /* should NEVER occur that passmgmt usage is wrong */ if (is_role(usertype)) errmsg(M_ARUSAGE); else errmsg(M_AUSAGE); exit(EX_SYNTAX); break; case PEX_BADUID: /* * The uid has been taken. If it was specified by a * user, then we must fail. Otherwise, keep trying * to get a good uid until we run out of IDs. */ if (uidstr != NULL) { errmsg(M_UID_USED, uid); exit(EX_ID_EXISTS); } else { if ((uid = findnextuid()) < 0) { errmsg(M_INVALID, "default id", "user id"); exit(EX_ID_EXISTS); } (void) sprintf(uidstring, "%ld", uid); } break; case PEX_BADNAME: /* invalid loname */ 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); } /* add group entry */ if (grps && edit_group(logname, (char *)0, gidlist, 0)) { errmsg(M_UPDATE, "created"); cleanup(logname); exit(EX_UPDATE); } /* update project database */ if (projects && edit_project(logname, (char *)NULL, projlist, 0)) { errmsg(M_UPDATE, "created"); cleanup(logname); exit(EX_UPDATE); } /* create home directory */ if (mflag && (create_home(homedir, skel_dir, uid, gid) != EX_SUCCESS)) { (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: /* should NEVER occur that passmgmt usage is wrong */ if (is_role(usertype)) errmsg(M_ARUSAGE); else errmsg(M_AUSAGE); break; case PEX_BADUID: /* uid is used - shouldn't happen but print message anyway */ errmsg(M_UID_USED, uid); break; case PEX_BADNAME: /* invalid loname */ errmsg(M_USED, logname); break; default: errmsg(M_UPDATE, "created"); break; } }