pw_user.c revision 20253
160812Sps/*- 260786Sps * Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>. 360786Sps * All rights reserved. 460786Sps * 560786Sps * Redistribution and use in source and binary forms, with or without 660786Sps * modification, are permitted provided that the following conditions 760786Sps * are met: 860786Sps * 1. Redistributions of source code must retain the above copyright 960786Sps * notice, this list of conditions and the following disclaimer as 1060786Sps * the first lines of this file unmodified. 1160786Sps * 2. Redistributions in binary form must reproduce the above copyright 1260786Sps * notice, this list of conditions and the following disclaimer in the 1360786Sps * documentation and/or other materials provided with the distribution. 1460786Sps * 3. All advertising materials mentioning features or use of this software 1560786Sps * must display the following acknowledgement: 1660786Sps * This product includes software developed by David L. Nugent. 1760786Sps * 4. The name of the author may not be used to endorse or promote products 1860786Sps * derived from this software without specific prior written permission. 1960786Sps * 2060786Sps * THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND 2160786Sps * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2260786Sps * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2360786Sps * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE 2460786Sps * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2560786Sps * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2660786Sps * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2760786Sps * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2860786Sps * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2960786Sps * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3060786Sps * SUCH DAMAGE. 3160812Sps * 3260786Sps * $Id$ 3360786Sps */ 3460786Sps 3560786Sps#include <unistd.h> 3660786Sps#include <fcntl.h> 3760786Sps#include <ctype.h> 3860786Sps#include <paths.h> 3960786Sps#include <sys/param.h> 4060786Sps#include <dirent.h> 4160786Sps#include <termios.h> 4260786Sps#include "pw.h" 4360786Sps#include "bitmap.h" 4460786Sps#include "pwupd.h" 4560786Sps 4660786Spsstatic int print_user(struct passwd * pwd, int pretty); 4760786Spsstatic uid_t pw_uidpolicy(struct userconf * cnf, struct cargs * args); 4860786Spsstatic uid_t pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer); 4960786Spsstatic time_t pw_pwdpolicy(struct userconf * cnf, struct cargs * args); 5060786Spsstatic time_t pw_exppolicy(struct userconf * cnf, struct cargs * args); 5160786Spsstatic char *pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user); 5260786Spsstatic char *pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell); 5360786Spsstatic char *pw_password(struct userconf * cnf, struct cargs * args, char const * user); 5460786Spsstatic char *pw_checkname(char *name, int gecos); 5560786Spsstatic char *shell_path(char const * path, char *shells[], char *sh); 5660786Spsstatic void rmat(uid_t uid); 5760786Sps 5860786Sps/*- 5960786Sps * -C config configuration file 6060786Sps * -q quiet operation 6160786Sps * -n name login name 6260786Sps * -u uid user id 6360786Sps * -c comment user name/comment 6460786Sps * -d directory home directory 6560786Sps * -e date account expiry date 6660786Sps * -p date password expiry date 6760786Sps * -g grp primary group 6860786Sps * -G grp1,grp2 additional groups 6960786Sps * -m [ -k dir ] create and set up home 7060786Sps * -s shell name of login shell 7160786Sps * -o duplicate uid ok 7260786Sps * -L class user class 7360786Sps * -l name new login name 7460786Sps * -h fd password filehandle 7560786Sps * -F force print or add 7660786Sps * Setting defaults: 7760786Sps * -D set user defaults 7860786Sps * -b dir default home root dir 7960786Sps * -e period default expiry period 8060786Sps * -p period default password change period 8160786Sps * -g group default group 8260786Sps * -G grp1,grp2.. default additional groups 8360786Sps * -L class default login class 8460786Sps * -k dir default home skeleton 8560786Sps * -s shell default shell 8660786Sps * -w method default password method 8760786Sps */ 8860786Sps 8960786Spsint 9060786Spspw_user(struct userconf * cnf, int mode, struct cargs * args) 9160786Sps{ 9260786Sps char *p = NULL; 9360786Sps struct carg *a_name; 9460786Sps struct carg *a_uid; 9560786Sps struct carg *arg; 9660786Sps struct passwd *pwd = NULL; 9760786Sps struct group *grp; 9860786Sps struct stat st; 9960786Sps char line[MAXPWLINE]; 10060786Sps 10160786Sps static struct passwd fakeuser = 10260786Sps { 10360786Sps NULL, 10460786Sps "*", 10560786Sps -1, 10660786Sps -1, 10760786Sps 0, 10860786Sps "", 10960786Sps "User &", 11060786Sps "/bin/sh", 11160786Sps 0, 11260786Sps 0 11360786Sps }; 11460786Sps 11560786Sps /* 11660786Sps * We can do all of the common legwork here 11760786Sps */ 11860786Sps 11960786Sps if ((arg = getarg(args, 'b')) != NULL) { 12060786Sps if (stat(cnf->home = arg->val, &st) == -1 || S_ISDIR(st.st_mode)) 12160786Sps cmderr(X_CMDERR, "root home `%s' is not a directory or does not exist\n", cnf->home); 12260786Sps } 12360786Sps if ((arg = getarg(args, 'e')) != NULL) 12460786Sps cnf->expire_days = atoi(arg->val); 12560786Sps 12660786Sps if ((arg = getarg(args, 'p')) != NULL && arg->val) 12760786Sps cnf->password_days = atoi(arg->val); 12860786Sps 12960786Sps if ((arg = getarg(args, 'g')) != NULL) { 13060786Sps p = arg->val; 13160786Sps if ((grp = getgrnam(p)) == NULL) { 13260786Sps if (!isdigit(*p) || (grp = getgrgid((gid_t) atoi(p))) == NULL) 13360786Sps cmderr(X_NOTFOUND, "group `%s' does not exist\n", p); 13460786Sps } 13560786Sps cnf->default_group = newstr(grp->gr_name); 13660786Sps } 13760786Sps if ((arg = getarg(args, 'L')) != NULL) 13860786Sps cnf->default_class = pw_checkname(arg->val, 0); 13960786Sps 14060786Sps if ((arg = getarg(args, 'G')) != NULL && arg->val) { 14160786Sps int i = 0; 14260786Sps 14360786Sps for (p = strtok(arg->val, ", \t"); i < _UC_MAXGROUPS && p != NULL; p = strtok(NULL, ", \t")) { 14460786Sps if ((grp = getgrnam(p)) == NULL) { 14560786Sps if (!isdigit(*p) || (grp = getgrgid((gid_t) atoi(p))) == NULL) 14660786Sps cmderr(X_NOTFOUND, "group `%s' does not exist\n", p); 14760812Sps } 14860812Sps cnf->groups[i++] = newstr(grp->gr_name); 14960812Sps } 15060812Sps while (i < _UC_MAXGROUPS) 15160812Sps cnf->groups[i++] = NULL; 15260812Sps } 15360786Sps if ((arg = getarg(args, 'k')) != NULL) { 15460786Sps if (stat(cnf->dotdir = arg->val, &st) == -1 || S_ISDIR(st.st_mode)) 15560786Sps cmderr(X_CMDERR, "skeleton `%s' is not a directory or does not exist\n", cnf->dotdir); 15660786Sps } 15760786Sps if ((arg = getarg(args, 's')) != NULL) 15860786Sps cnf->shell_default = arg->val; 15960786Sps 16060786Sps if (mode == M_ADD && getarg(args, 'D')) { 16160786Sps if (getarg(args, 'n') != NULL) 16260786Sps cmderr(X_CMDERR, "can't combine `-D' with `-n name'\n"); 16360786Sps if ((arg = getarg(args, 'u')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) { 16460786Sps if ((cnf->min_uid = (uid_t) atoi(p)) == 0) 16560786Sps cnf->min_uid = 1000; 16660786Sps if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_uid = (uid_t) atoi(p)) < cnf->min_uid) 16760786Sps cnf->max_uid = 32000; 16860786Sps } 16960786Sps if ((arg = getarg(args, 'i')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) { 17060786Sps if ((cnf->min_gid = (gid_t) atoi(p)) == 0) 17160786Sps cnf->min_gid = 1000; 17260786Sps if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_gid = (gid_t) atoi(p)) < cnf->min_gid) 17360786Sps cnf->max_gid = 32000; 17460786Sps } 17560786Sps if ((arg = getarg(args, 'w')) != NULL) 17660786Sps cnf->default_password = boolean_val(arg->val, cnf->default_password); 17760786Sps 17860786Sps arg = getarg(args, 'C'); 17960786Sps if (write_userconfig(arg ? arg->val : NULL)) 18060786Sps return X_ALLOK; 18160786Sps perror("config update"); 18260786Sps return X_UPDERROR; 18360786Sps } 18460786Sps if (mode == M_PRINT && getarg(args, 'a')) { 18560786Sps int pretty = getarg(args, 'p') != NULL; 18660786Sps 18760786Sps setpwent(); 18860786Sps while ((pwd = getpwent()) != NULL) 18960786Sps print_user(pwd, pretty); 19060786Sps endpwent(); 19160786Sps return X_ALLOK; 19260786Sps } 19360786Sps if ((a_name = getarg(args, 'n')) != NULL) 19460786Sps pwd = getpwnam(pw_checkname(a_name->val, 0)); 19560786Sps a_uid = getarg(args, 'u'); 19660786Sps 19760786Sps if (a_uid == NULL) { 19860786Sps if (a_name == NULL) 19960786Sps cmderr(X_CMDERR, "user name or id required\n"); 20060786Sps 20160786Sps /* 20260786Sps * Determine whether 'n' switch is name or uid - we don't 20360786Sps * really don't really care which we have, but we need to 20460786Sps * know. 20560786Sps */ 20660786Sps if (mode != M_ADD && pwd == NULL && isdigit(*a_name->val) && atoi(a_name->val) > 0) { /* Assume uid */ 20760786Sps (a_uid = a_name)->ch = 'u'; 20860786Sps a_name = NULL; 20960786Sps } 21060786Sps } 21160786Sps /* 21260786Sps * Update, delete & print require that the user exists 21360786Sps */ 21460786Sps if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) { 21560786Sps if (a_name == NULL && pwd == NULL) /* Try harder */ 21660786Sps pwd = getpwuid(atoi(a_uid->val)); 21760786Sps 21860786Sps if (pwd == NULL) { 21960786Sps if (mode == M_PRINT && getarg(args, 'F')) { 22060786Sps fakeuser.pw_name = a_name ? a_name->val : "nouser"; 22160786Sps fakeuser.pw_uid = a_uid ? (uid_t) atol(a_uid->val) : -1; 22260786Sps return print_user(&fakeuser, getarg(args, 'p') != NULL); 22360786Sps } 22460786Sps if (a_name == NULL) 22560786Sps cmderr(X_NOTFOUND, "no such uid `%s'\n", a_uid->val); 22660786Sps cmderr(X_NOTFOUND, "no such user `%s'\n", a_name->val); 22760786Sps } 22860786Sps if (a_name == NULL) /* May be needed later */ 22960786Sps a_name = addarg(args, 'n', newstr(pwd->pw_name)); 23060786Sps 23160786Sps /* 23260786Sps * Handle deletions now 23360786Sps */ 23460786Sps if (mode == M_DELETE) { 23560786Sps char file[MAXPATHLEN]; 23660816Sps char home[MAXPATHLEN]; 23760816Sps uid_t uid = pwd->pw_uid; 23860786Sps 23960786Sps if (strcmp(pwd->pw_name, "root") == 0) 24060786Sps cmderr(X_CMDERR, "cannot remove user 'root'\n"); 24160786Sps 24260786Sps /* 24360786Sps * Remove crontabs 24460786Sps */ 24560786Sps sprintf(file, "/var/cron/tabs/%s", pwd->pw_name); 24660786Sps if (access(file, F_OK) == 0) { 24760786Sps sprintf(file, "crontab -u %s -r", pwd->pw_name); 24860786Sps system(file); 24960786Sps } 25060786Sps /* 25160786Sps * Save these for later, since contents of pwd may be 25260786Sps * invalidated by deletion 25360786Sps */ 25460786Sps sprintf(file, "%s/%s", _PATH_MAILDIR, pwd->pw_name); 25560786Sps strncpy(home, pwd->pw_dir, sizeof home); 25660786Sps home[sizeof home - 1] = '\0'; 25760786Sps 25860786Sps if (!delpwent(pwd)) 25960786Sps cmderr(X_NOUPDATE, "Error updating passwd file: %s\n", strerror(errno)); 26060786Sps editgroups(a_name->val, NULL); 26160786Sps 26260786Sps pw_log(cnf, mode, W_USER, "%s(%ld) account removed", a_name->val, (long) uid); 26360786Sps 26460786Sps /* 26560786Sps * Remove mail file 26660786Sps */ 26760786Sps remove(file); 26860786Sps 26960786Sps /* 27060786Sps * Remove at jobs 27160786Sps */ 27260786Sps if (getpwuid(uid) == NULL) 27360786Sps rmat(uid); 27460786Sps 27560786Sps /* 27660786Sps * Remove home directory and contents 27760786Sps */ 27860786Sps if (getarg(args, 'r') != NULL && *home == '/' && getpwuid(uid) == NULL) { 27960786Sps if (stat(home, &st) != -1) { 28060786Sps rm_r(home, uid); 28160786Sps pw_log(cnf, mode, W_USER, "%s(%ld) home '%s' %sremoved", 28260786Sps a_name->val, (long) uid, home, 28360786Sps stat(home, &st) == -1 ? "" : "not completely "); 28460786Sps } 28560786Sps } 28660786Sps return X_ALLOK; 28760786Sps } else if (mode == M_PRINT) 28860786Sps return print_user(pwd, getarg(args, 'p') != NULL); 28960786Sps 29060786Sps /* 29160786Sps * The rest is edit code 29260786Sps */ 29360786Sps if ((arg = getarg(args, 'l')) != NULL) { 29460786Sps if (strcmp(pwd->pw_name, "root") == 0) 29560786Sps cmderr(X_CMDERR, "can't rename `root' account\n"); 29660786Sps pwd->pw_name = pw_checkname(arg->val, 0); 29760786Sps } 29860786Sps if ((arg = getarg(args, 'u')) != NULL && isdigit(*arg->val)) { 29960786Sps pwd->pw_uid = (uid_t) atol(arg->val); 30060786Sps if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0) 30160786Sps cmderr(X_CMDERR, "can't change uid of `root' account\n"); 30260786Sps if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0) 30360786Sps fprintf(stderr, "WARNING: account `%s' will have a uid of 0 (superuser access!)\n", pwd->pw_name); 30460786Sps } 30560786Sps if ((arg = getarg(args, 'g')) != NULL && pwd->pw_uid != 0) /* Already checked this */ 30660786Sps pwd->pw_gid = (gid_t) getgrnam(cnf->default_group)->gr_gid; 30760786Sps 30860786Sps if ((arg = getarg(args, 'p')) != NULL) { 30960786Sps if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) 31060786Sps pwd->pw_change = 0; 31160786Sps else { 31260786Sps time_t now = time(NULL); 31360786Sps time_t expire = parse_date(now, arg->val); 31460786Sps 31560786Sps if (now == expire) 31660786Sps cmderr(X_CMDERR, "Invalid password change date `%s'\n", arg->val); 31760786Sps pwd->pw_change = expire; 31860786Sps } 31960786Sps } 32060786Sps if ((arg = getarg(args, 'p')) != NULL) { 32160786Sps if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) 32260786Sps pwd->pw_expire = 0; 32360786Sps else { 32460786Sps time_t now = time(NULL); 32560786Sps time_t expire = parse_date(now, arg->val); 32660786Sps 32760786Sps if (now == expire) 32860786Sps cmderr(X_CMDERR, "Invalid password change date `%s'\n", arg->val); 32960786Sps pwd->pw_expire = expire; 33060786Sps } 33160786Sps } 33260786Sps if ((arg = getarg(args, 's')) != NULL) 33360786Sps pwd->pw_shell = shell_path(cnf->shelldir, cnf->shells, arg->val); 33460786Sps 33560786Sps if (getarg(args, 'L')) 33660786Sps pwd->pw_class = cnf->default_class; 33760786Sps 33860786Sps } else { 33960786Sps if (a_name == NULL) /* Required */ 34060786Sps cmderr(X_CMDERR, "login name required\n"); 34160786Sps else if ((pwd = getpwnam(a_name->val)) != NULL) /* Exists */ 34260786Sps cmderr(X_EXISTS, "login name `%s' already exists\n", a_name->val); 34360786Sps 34460786Sps /* 34560786Sps * Now, set up defaults for a new user 34660786Sps */ 34760786Sps pwd = &fakeuser; 34860786Sps pwd->pw_name = a_name->val; 34960786Sps pwd->pw_class = cnf->default_class ? cnf->default_class : ""; 35060786Sps pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name); 35160786Sps pwd->pw_uid = pw_uidpolicy(cnf, args); 35260786Sps pwd->pw_gid = pw_gidpolicy(cnf, args, pwd->pw_name, (gid_t) pwd->pw_uid); 35360786Sps pwd->pw_change = pw_pwdpolicy(cnf, args); 35460786Sps pwd->pw_expire = pw_exppolicy(cnf, args); 35560786Sps pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name); 35660786Sps pwd->pw_shell = pw_shellpolicy(cnf, args, NULL); 35760786Sps 35860786Sps if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0) 35960786Sps fprintf(stderr, "WARNING: new account `%s' has a uid of 0 (superuser access!)\n", pwd->pw_name); 36060786Sps } 36160786Sps 36260786Sps /* 36360786Sps * Shared add/edit code 36460786Sps */ 36560786Sps if ((arg = getarg(args, 'c')) != NULL) 36660786Sps pwd->pw_gecos = pw_checkname(arg->val, 1); 36760786Sps 36860786Sps if ((arg = getarg(args, 'h')) != NULL) { 36960786Sps if (strcmp(arg->val, "-") == 0) 37060786Sps pwd->pw_passwd = "*"; /* No access */ 37160786Sps else { 37260786Sps int fd = atoi(arg->val); 37360786Sps int b; 37460786Sps int istty = isatty(fd); 37560786Sps struct termios t; 37660786Sps 37760786Sps if (istty) { 37860786Sps if (tcgetattr(fd, &t) == -1) 37960786Sps istty = 0; 38060786Sps else { 38160786Sps struct termios n = t; 38260786Sps 38360786Sps /* Disable echo */ 38460786Sps n.c_lflag &= ~(ECHO); 38560786Sps tcsetattr(fd, TCSANOW, &n); 38660786Sps printf("%sassword for user %s:", (mode == M_UPDATE) ? "New p" : "P", pwd->pw_name); 38760786Sps fflush(stdout); 38860786Sps } 38960786Sps } 39060786Sps b = read(fd, line, sizeof(line) - 1); 39160786Sps if (istty) { /* Restore state */ 39260786Sps tcsetattr(fd, TCSANOW, &t); 39360786Sps fputc('\n', stdout); 39460786Sps fflush(stdout); 39560786Sps } 39660786Sps if (b < 0) { 39760786Sps perror("-h file descriptor"); 39860786Sps return X_CMDERR; 39960786Sps } 40060786Sps line[b] = '\0'; 40160786Sps if ((p = strpbrk(line, " \t\r\n")) != NULL) 40260786Sps *p = '\0'; 40360786Sps if (!*line) 40460786Sps cmderr(X_CMDERR, "empty password read on file descriptor %d\n", fd); 40560786Sps pwd->pw_passwd = pw_pwcrypt(line); 40660786Sps } 40760786Sps } 40860786Sps if ((mode == M_ADD && !addpwent(pwd)) || 40960786Sps (mode == M_UPDATE && !chgpwent(a_name->val, pwd))) { 41060786Sps perror("password update"); 41160786Sps return X_NOUPDATE; 41260786Sps } 41360786Sps /* 41460786Sps * Ok, user is created or changed - now edit group file 41560786Sps */ 41660786Sps 41760786Sps if (mode == M_ADD || getarg(args, 'G') != NULL) 41860786Sps editgroups(pwd->pw_name, cnf->groups); 419 420 /* pwd may have been invalidated */ 421 if ((pwd = getpwnam(a_name->val)) == NULL) 422 cmderr(X_NOTFOUND, "user '%s' disappeared during update\n", a_name->val); 423 424 grp = getgrgid(pwd->pw_gid); 425 pw_log(cnf, mode, W_USER, "%s(%ld):%s(%d):%s:%s:%s", 426 pwd->pw_name, (long) pwd->pw_uid, 427 grp ? grp->gr_name : "unknown", (long) (grp ? grp->gr_gid : -1), 428 pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell); 429 430 /* 431 * If adding, let's touch and chown the user's mail file. This is not 432 * strictly necessary under BSD with a 0755 maildir but it also 433 * doesn't hurt anything to create the empty mailfile 434 */ 435 if (mode == M_ADD) { 436 FILE *fp; 437 438 sprintf(line, "%s/%s", _PATH_MAILDIR, pwd->pw_name); 439 close(open(line, O_RDWR | O_CREAT, 0600)); /* Preserve contents & 440 * mtime */ 441 chown(line, pwd->pw_uid, pwd->pw_gid); 442 443 /* 444 * Send mail to the new user as well, if we are asked to 445 */ 446 if (cnf->newmail && *cnf->newmail && (fp = fopen(cnf->newmail, "r")) != NULL) { 447 FILE *pfp = popen(_PATH_SENDMAIL " -t", "w"); 448 449 if (pfp == NULL) 450 perror("sendmail"); 451 else { 452 fprintf(pfp, "From: root\n" "To: %s\n" "Subject: Welcome!\n\n", pwd->pw_name); 453 while (fgets(line, sizeof(line), fp) != NULL) { 454 /* Do substitutions? */ 455 fputs(line, pfp); 456 } 457 pclose(pfp); 458 pw_log(cnf, mode, W_USER, "%s(%ld) new user mail sent", 459 pwd->pw_name, (long) pwd->pw_uid); 460 } 461 fclose(fp); 462 } 463 } 464 /* 465 * Finally, let's create and populate the user's home directory. Note 466 * that this also `works' for editing users if -m is used, but 467 * existing files will *not* be overwritten. 468 */ 469 if (getarg(args, 'm') != NULL && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) { 470 copymkdir(pwd->pw_dir, cnf->dotdir, 0755, pwd->pw_uid, pwd->pw_gid); 471 pw_log(cnf, mode, W_USER, "%s(%ld) home %s made", 472 pwd->pw_name, (long) pwd->pw_uid, pwd->pw_dir); 473 } 474 return X_ALLOK; 475} 476 477 478static uid_t 479pw_uidpolicy(struct userconf * cnf, struct cargs * args) 480{ 481 struct passwd *pwd; 482 uid_t uid = (uid_t) - 1; 483 struct carg *a_uid = getarg(args, 'u'); 484 485 /* 486 * Check the given uid, if any 487 */ 488 if (a_uid != NULL) { 489 uid = (uid_t) atol(a_uid->val); 490 491 if ((pwd = getpwuid(uid)) != NULL && getarg(args, 'o') == NULL) 492 cmderr(X_EXISTS, "uid `%ld' has already been allocated\n", (long) pwd->pw_uid); 493 } else { 494 struct bitmap bm; 495 496 /* 497 * We need to allocate the next available uid under one of 498 * two policies a) Grab the first unused uid b) Grab the 499 * highest possible unused uid 500 */ 501 if (cnf->min_uid >= cnf->max_uid) { /* Sanity 502 * claus^H^H^H^Hheck */ 503 cnf->min_uid = 1000; 504 cnf->max_uid = 32000; 505 } 506 bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1); 507 508 /* 509 * Now, let's fill the bitmap from the password file 510 */ 511 setpwent(); 512 while ((pwd = getpwent()) != NULL) 513 if (pwd->pw_uid >= (int) cnf->min_uid && pwd->pw_uid <= (int) cnf->max_uid) 514 bm_setbit(&bm, pwd->pw_uid - cnf->min_uid); 515 endpwent(); 516 517 /* 518 * Then apply the policy, with fallback to reuse if necessary 519 */ 520 if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid) 521 uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid); 522 523 /* 524 * Another sanity check 525 */ 526 if (uid < cnf->min_uid || uid > cnf->max_uid) 527 cmderr(X_EXISTS, "unable to allocate a new uid - range fully used\n"); 528 bm_dealloc(&bm); 529 } 530 return uid; 531} 532 533 534static uid_t 535pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer) 536{ 537 struct group *grp; 538 gid_t gid = (uid_t) - 1; 539 struct carg *a_gid = getarg(args, 'g'); 540 541 /* 542 * If no arg given, see if default can help out 543 */ 544 if (a_gid == NULL && cnf->default_group && *cnf->default_group) 545 a_gid = addarg(args, 'g', cnf->default_group); 546 547 /* 548 * Check the given gid, if any 549 */ 550 if (a_gid != NULL) { 551 setgrent(); 552 if ((grp = getgrnam(a_gid->val)) == NULL) { 553 gid = (gid_t) atol(a_gid->val); 554 if ((gid == 0 && !isdigit(*a_gid->val)) || (grp = getgrgid(gid)) == NULL) 555 cmderr(X_NOTFOUND, "group `%s' is not defined\n", a_gid->val); 556 } 557 endgrent(); 558 gid = grp->gr_gid; 559 } else { 560 struct cargs grpargs; 561 char tmp[32]; 562 563 LIST_INIT(&grpargs); 564 addarg(&grpargs, 'n', nam); 565 566 /* 567 * We need to auto-create a group with the user's name. We 568 * can send all the appropriate output to our sister routine 569 * bit first see if we can create a group with gid==uid so we 570 * can keep the user and group ids in sync. We purposely do 571 * NOT check the gid range if we can force the sync. If the 572 * user's name dups an existing group, then the group add 573 * function will happily handle that case for us and exit. 574 */ 575 if (getgrgid(prefer) == NULL) { 576 sprintf(tmp, "%lu", (unsigned long) prefer); 577 addarg(&grpargs, 'g', tmp); 578 } 579 endgrent(); 580 pw_group(cnf, M_ADD, &grpargs); 581 a_gid = grpargs.lh_first; 582 while (a_gid != NULL) { 583 struct carg *t = a_gid->list.le_next; 584 585 LIST_REMOVE(a_gid, list); 586 a_gid = t; 587 } 588 if ((grp = getgrnam(nam)) != NULL) 589 gid = grp->gr_gid; 590 } 591 return gid; 592} 593 594 595static time_t 596pw_pwdpolicy(struct userconf * cnf, struct cargs * args) 597{ 598 time_t result = 0; 599 time_t now = time(NULL); 600 struct carg *arg = getarg(args, 'e'); 601 602 if (arg != NULL) { 603 if ((result = parse_date(now, arg->val)) == now) 604 cmderr(X_NOTFOUND, "invalid date/time `%s'\n", arg->val); 605 } else if (cnf->password_days > 0) 606 result = now + ((long) cnf->password_days * 86400L); 607 return result; 608} 609 610 611static time_t 612pw_exppolicy(struct userconf * cnf, struct cargs * args) 613{ 614 time_t result = 0; 615 time_t now = time(NULL); 616 struct carg *arg = getarg(args, 'e'); 617 618 if (arg != NULL) { 619 if ((result = parse_date(now, arg->val)) == now) 620 cmderr(X_NOTFOUND, "invalid date/time `%s'\n", arg->val); 621 } else if (cnf->expire_days > 0) 622 result = now + ((long) cnf->expire_days * 86400L); 623 return result; 624} 625 626 627static char * 628pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user) 629{ 630 struct carg *arg = getarg(args, 'd'); 631 632 if (arg) 633 return arg->val; 634 else { 635 static char home[128]; 636 637 if (cnf->home == NULL || *cnf->home == '\0') 638 cmderr(X_NOTFOUND, "no base home directory set\n"); 639 sprintf(home, "%s/%s", cnf->home, user); 640 return home; 641 } 642} 643 644static char * 645shell_path(char const * path, char *shells[], char *sh) 646{ 647 if (sh != NULL && (*sh == '/' || *sh == '\0')) 648 return sh; /* specified full path or forced none */ 649 else { 650 char *p; 651 char paths[_UC_MAXLINE]; 652 653 /* 654 * We need to search paths 655 */ 656 strncpy(paths, path, sizeof paths); 657 paths[sizeof paths - 1] = '\0'; 658 for (p = strtok(paths, ": \t\r\n"); p != NULL; p = strtok(NULL, ": \t\r\n")) { 659 int i; 660 static char shellpath[256]; 661 662 if (sh != NULL) { 663 sprintf(shellpath, "%s/%s", p, sh); 664 if (access(shellpath, X_OK) == 0) 665 return shellpath; 666 } else 667 for (i = 0; i < _UC_MAXSHELLS && shells[i] != NULL; i++) { 668 sprintf(shellpath, "%s/%s", p, shells[i]); 669 if (access(shellpath, X_OK) == 0) 670 return shellpath; 671 } 672 } 673 if (sh == NULL) 674 cmderr(X_CMDERR, "can't find shell `%s' in shell paths\n", sh); 675 cmderr(X_CMDERR, "no default shell available or defined\n"); 676 return NULL; 677 } 678} 679 680 681static char * 682pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell) 683{ 684 char *sh = newshell; 685 struct carg *arg = getarg(args, 's'); 686 687 if (newshell == NULL && arg != NULL) 688 sh = arg->val; 689 return shell_path(cnf->shelldir, cnf->shells, sh ? sh : cnf->shell_default); 690} 691 692static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ."; 693 694char * 695pw_pwcrypt(char *password) 696{ 697 int i; 698 char salt[12]; 699 700 static char buf[256]; 701 702 /* 703 * Calculate a salt value 704 */ 705 srandom((unsigned) (time(NULL) | getpid())); 706 for (i = 0; i < 8; i++) 707 salt[i] = chars[random() % 63]; 708 salt[i] = '\0'; 709 710 return strcpy(buf, crypt(password, salt)); 711} 712 713 714static char * 715pw_password(struct userconf * cnf, struct cargs * args, char const * user) 716{ 717 int i, l; 718 char pwbuf[32]; 719 720 switch (cnf->default_password) { 721 case -1: /* Random password */ 722 srandom((unsigned) (time(NULL) | getpid())); 723 l = (random() % 8 + 8); /* 8 - 16 chars */ 724 for (i = 0; i < l; i++) 725 pwbuf[i] = chars[random() % sizeof(chars)]; 726 pwbuf[i] = '\0'; 727 728 /* 729 * We give this information back to the user 730 */ 731 if (getarg(args, 'h') == NULL) { 732 if (isatty(0)) 733 printf("Password is: "); 734 printf("%s\n", pwbuf); 735 fflush(stdout); 736 } 737 break; 738 739 case -2: /* No password at all! */ 740 return ""; 741 742 case 0: /* No login - default */ 743 default: 744 return "*"; 745 746 case 1: /* user's name */ 747 strncpy(pwbuf, user, sizeof pwbuf); 748 pwbuf[sizeof pwbuf - 1] = '\0'; 749 break; 750 } 751 return pw_pwcrypt(pwbuf); 752} 753 754 755static int 756print_user(struct passwd * pwd, int pretty) 757{ 758 if (!pretty) { 759 char buf[_UC_MAXLINE]; 760 761 fmtpwent(buf, pwd); 762 fputs(buf, stdout); 763 } else { 764 char *p; 765 struct group *grp = getgrgid(pwd->pw_gid); 766 char uname[60] = "User &", office[60] = "[None]", 767 wphone[60] = "[None]", hphone[60] = "[None]"; 768 769 if ((p = strtok(pwd->pw_gecos, ",")) != NULL) { 770 strncpy(uname, p, sizeof uname); 771 uname[sizeof uname - 1] = '\0'; 772 if ((p = strtok(NULL, ",")) != NULL) { 773 strncpy(office, p, sizeof office); 774 office[sizeof office - 1] = '\0'; 775 if ((p = strtok(NULL, ",")) != NULL) { 776 strncpy(wphone, p, sizeof wphone); 777 wphone[sizeof wphone - 1] = '\0'; 778 if ((p = strtok(NULL, "")) != NULL) { 779 strncpy(hphone, p, sizeof hphone); 780 hphone[sizeof hphone - 1] = '\0'; 781 } 782 } 783 } 784 } 785 /* 786 * Handle '&' in gecos field 787 */ 788 if ((p = strchr(uname, '&')) != NULL) { 789 int l = strlen(pwd->pw_name); 790 int m = strlen(p); 791 792 memmove(p + l, p + 1, m); 793 memmove(p, pwd->pw_name, l); 794 *p = (char) toupper(*p); 795 } 796 printf("Login Name : %-10s #%-22ld Group : %-10s #%ld\n" 797 " Full Name : %s\n" 798 " Home : %-32.32s Class : %s\n" 799 " Shell : %-32.32s Office : %s\n" 800 "Work Phone : %-32.32s Home Phone : %s\n\n", 801 pwd->pw_name, (long) pwd->pw_uid, 802 grp ? grp->gr_name : "(invalid)", (long) pwd->pw_gid, 803 uname, pwd->pw_dir, pwd->pw_class, 804 pwd->pw_shell, office, wphone, hphone); 805 } 806 return X_ALLOK; 807} 808 809static char * 810pw_checkname(char *name, int gecos) 811{ 812 int l = 0; 813 char const *notch = gecos ? ":" : " :+-&#%$^()!@~*?<>=|\\/\""; 814 815 while (name[l]) { 816 if (strchr(notch, name[l]) != NULL || name[l] < ' ') 817 cmderr(X_CMDERR, "invalid character `%c' in field\n", name[l]); 818 ++l; 819 } 820 if (!gecos && l > 8) 821 cmderr(X_CMDERR, "name too long `%s'\n", name); 822 return name; 823} 824 825 826static void 827rmat(uid_t uid) 828{ 829 DIR *d = opendir("/var/at/jobs"); 830 831 if (d != NULL) { 832 struct dirent *e; 833 834 while ((e = readdir(d)) != NULL) { 835 struct stat st; 836 837 if (strncmp(e->d_name, ".lock", 5) != 0 && 838 stat(e->d_name, &st) == 0 && 839 !S_ISDIR(st.st_mode) && 840 st.st_uid == uid) { 841 char tmp[MAXPATHLEN]; 842 843 sprintf(tmp, "/usr/bin/atrm %s", e->d_name); 844 system(tmp); 845 } 846 } 847 closedir(d); 848 } 849} 850