1/* $OpenBSD: user.c,v 1.131 2023/05/18 18:29:28 millert Exp $ */ 2/* $NetBSD: user.c,v 1.69 2003/04/14 17:40:07 agc Exp $ */ 3 4/* 5 * Copyright (c) 1999 Alistair G. Crooks. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote 16 * products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/types.h> 33#include <sys/stat.h> 34#include <sys/wait.h> 35 36#include <ctype.h> 37#include <dirent.h> 38#include <err.h> 39#include <errno.h> 40#include <fcntl.h> 41#include <grp.h> 42#include <limits.h> 43#include <login_cap.h> 44#include <paths.h> 45#include <pwd.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <string.h> 49#include <syslog.h> 50#include <time.h> 51#include <unistd.h> 52#include <util.h> 53 54#include "usermgmt.h" 55 56 57/* this struct describes a uid range */ 58typedef struct range_t { 59 uid_t r_from; /* low uid */ 60 uid_t r_to; /* high uid */ 61} range_t; 62 63/* this struct encapsulates the user information */ 64typedef struct user_t { 65 int u_flags; /* see below */ 66 uid_t u_uid; /* uid of user */ 67 char *u_password; /* encrypted password */ 68 char *u_comment; /* comment field */ 69 char *u_home; /* home directory */ 70 char *u_primgrp; /* primary group */ 71 int u_groupc; /* # of secondary groups */ 72 const char *u_groupv[NGROUPS_MAX]; /* secondary groups */ 73 char *u_shell; /* user's shell */ 74 char *u_basedir; /* base directory for home */ 75 char *u_expire; /* when account will expire */ 76 char *u_inactive; /* when password will expire */ 77 char *u_skeldir; /* directory for startup files */ 78 char *u_class; /* login class */ 79 unsigned int u_rsize; /* size of range array */ 80 unsigned int u_rc; /* # of ranges */ 81 range_t *u_rv; /* the ranges */ 82 unsigned int u_defrc; /* # of ranges in defaults */ 83 int u_preserve; /* preserve uids on deletion */ 84} user_t; 85 86/* flags for which fields of the user_t replace the passwd entry */ 87enum { 88 F_COMMENT = 0x0001, 89 F_DUPUID = 0x0002, 90 F_EXPIRE = 0x0004, 91 F_GROUP = 0x0008, 92 F_HOMEDIR = 0x0010, 93 F_MKDIR = 0x0020, 94 F_INACTIVE = 0x0040, 95 F_PASSWORD = 0x0080, 96 F_SECGROUP = 0x0100, 97 F_SHELL = 0x0200, 98 F_UID = 0x0400, 99 F_USERNAME = 0x0800, 100 F_CLASS = 0x1000, 101 F_SETSECGROUP = 0x4000, 102 F_ACCTLOCK = 0x8000, 103 F_ACCTUNLOCK = 0x10000 104}; 105 106/* flags for runas() */ 107enum { 108 RUNAS_DUP_DEVNULL = 0x01, 109 RUNAS_IGNORE_EXITVAL = 0x02 110}; 111 112#define CONFFILE "/etc/usermgmt.conf" 113#define _PATH_NONEXISTENT "/nonexistent" 114 115#ifndef DEF_GROUP 116#define DEF_GROUP "=uid" 117#endif 118 119#ifndef DEF_BASEDIR 120#define DEF_BASEDIR "/home" 121#endif 122 123#ifndef DEF_SKELDIR 124#define DEF_SKELDIR "/etc/skel" 125#endif 126 127#ifndef DEF_SHELL 128#define DEF_SHELL _PATH_KSHELL 129#endif 130 131#ifndef DEF_COMMENT 132#define DEF_COMMENT "" 133#endif 134 135#ifndef DEF_LOWUID 136#define DEF_LOWUID 1000 137#endif 138 139#ifndef DEF_HIGHUID 140#define DEF_HIGHUID 60000 141#endif 142 143#ifndef DEF_INACTIVE 144#define DEF_INACTIVE 0 145#endif 146 147#ifndef DEF_EXPIRE 148#define DEF_EXPIRE NULL 149#endif 150 151#ifndef DEF_CLASS 152#define DEF_CLASS "" 153#endif 154 155#ifndef WAITSECS 156#define WAITSECS 10 157#endif 158 159#ifndef NOBODY_UID 160#define NOBODY_UID 32767 161#endif 162 163/* some useful constants */ 164enum { 165 MaxShellNameLen = 256, 166 MaxFileNameLen = PATH_MAX, 167 MaxUserNameLen = _PW_NAME_LEN, 168 MaxCommandLen = 2048, 169 PasswordLength = _PASSWORD_LEN, 170 LowGid = DEF_LOWUID, 171 HighGid = DEF_HIGHUID 172}; 173 174/* Full paths of programs used here */ 175#define CHMOD "/bin/chmod" 176#define CHOWN "/sbin/chown" 177#define MKDIR "/bin/mkdir" 178#define MV "/bin/mv" 179#define NOLOGIN "/sbin/nologin" 180#define CP "/bin/cp" 181#define RM "/bin/rm" 182 183#define UNSET_INACTIVE "Null (unset)" 184#define UNSET_EXPIRY "Null (unset)" 185 186static int adduser(char *, user_t *); 187static int append_group(char *, int, const char **); 188static int copydotfiles(char *, char *); 189static int creategid(char *, gid_t, const char *); 190static int getnextgid(uid_t *, uid_t, uid_t); 191static int getnextuid(int, uid_t *, uid_t, uid_t); 192static int is_local(char *, const char *); 193static int modify_gid(char *, char *); 194static int moduser(char *, char *, user_t *); 195static int removehomedir(const char *, uid_t, const char *); 196static int rm_user_from_groups(char *); 197static int save_range(user_t *, char *); 198static int scantime(time_t *, char *); 199static int setdefaults(user_t *); 200static int valid_class(char *); 201static int valid_group(char *); 202static int valid_login(char *); 203static size_t expand_len(const char *, const char *); 204static struct group *find_group_info(const char *); 205static struct passwd *find_user_info(const char *); 206static void checkeuid(void); 207static void strsave(char **, const char *); 208static void read_defaults(user_t *); 209 210static int verbose; 211 212/* free *cpp, then store a copy of `s' in it */ 213static void 214strsave(char **cpp, const char *s) 215{ 216 free(*cpp); 217 if ((*cpp = strdup(s)) == NULL) 218 err(1, NULL); 219} 220 221/* run the given command with optional arguments as the specified uid */ 222static int 223runas(const char *path, const char *const argv[], uid_t uid, int flags) 224{ 225 int argc, status, ret = 0; 226 char buf[MaxCommandLen]; 227 pid_t child; 228 229 strlcpy(buf, path, sizeof(buf)); 230 for (argc = 1; argv[argc] != NULL; argc++) { 231 strlcat(buf, " ", sizeof(buf)); 232 strlcat(buf, argv[argc], sizeof(buf)); 233 } 234 if (verbose) 235 printf("Command: %s\n", buf); 236 237 child = fork(); 238 switch (child) { 239 case -1: 240 err(EXIT_FAILURE, "fork"); 241 case 0: 242 if (flags & RUNAS_DUP_DEVNULL) { 243 /* Redirect output to /dev/null if possible. */ 244 int dev_null = open(_PATH_DEVNULL, O_RDWR); 245 if (dev_null != -1) { 246 dup2(dev_null, STDOUT_FILENO); 247 dup2(dev_null, STDERR_FILENO); 248 if (dev_null > STDERR_FILENO) 249 close(dev_null); 250 } else { 251 warn("%s", _PATH_DEVNULL); 252 } 253 } 254 if (uid != -1) { 255 if (setresuid(uid, uid, uid) == -1) 256 warn("setresuid(%u, %u, %u)", uid, uid, uid); 257 } 258 execv(path, (char **)argv); 259 warn("%s", buf); 260 _exit(EXIT_FAILURE); 261 default: 262 while (waitpid(child, &status, 0) == -1) { 263 if (errno != EINTR) 264 err(EXIT_FAILURE, "waitpid"); 265 } 266 if (WIFSIGNALED(status)) { 267 ret = WTERMSIG(status); 268 warnx("[Warning] `%s' killed by signal %d", buf, ret); 269 ret |= 128; 270 } else { 271 if (!(flags & RUNAS_IGNORE_EXITVAL)) 272 ret = WEXITSTATUS(status); 273 if (ret != 0) { 274 warnx("[Warning] `%s' failed with status %d", 275 buf, ret); 276 } 277 } 278 return ret; 279 } 280} 281 282/* run the given command with optional arguments */ 283static int 284run(const char *path, const char *const argv[]) 285{ 286 return runas(path, argv, -1, 0); 287} 288 289/* remove a users home directory, returning 1 for success (ie, no problems encountered) */ 290static int 291removehomedir(const char *user, uid_t uid, const char *dir) 292{ 293 const char *rm_argv[] = { "rm", "-rf", dir, NULL }; 294 struct stat st; 295 296 /* userid not root? */ 297 if (uid == 0) { 298 warnx("Not deleting home directory `%s'; userid is 0", dir); 299 return 0; 300 } 301 302 /* directory exists (and is a directory!) */ 303 if (stat(dir, &st) == -1) { 304 warnx("Home directory `%s' doesn't exist", dir); 305 return 0; 306 } 307 if (!S_ISDIR(st.st_mode)) { 308 warnx("Home directory `%s' is not a directory", dir); 309 return 0; 310 } 311 312 /* userid matches directory owner? */ 313 if (st.st_uid != uid) { 314 warnx("User `%s' doesn't own directory `%s', not removed", 315 user, dir); 316 return 0; 317 } 318 319 /* run "rm -rf dir 2>&1/dev/null" as user, not root */ 320 (void) runas(RM, rm_argv, uid, RUNAS_DUP_DEVNULL|RUNAS_IGNORE_EXITVAL); 321 if (rmdir(dir) == -1 && errno != ENOENT) { 322 warnx("Unable to remove all files in `%s'", dir); 323 return 0; 324 } 325 return 1; 326} 327 328/* 329 * check that the effective uid is 0 - called from funcs which will 330 * modify data and config files. 331 */ 332static void 333checkeuid(void) 334{ 335 if (geteuid() != 0) { 336 errx(EXIT_FAILURE, "Program must be run as root"); 337 } 338} 339 340/* copy any dot files into the user's home directory */ 341static int 342copydotfiles(char *skeldir, char *dst) 343{ 344 char src[MaxFileNameLen]; 345 struct dirent *dp; 346 DIR *dirp; 347 int len, n; 348 349 if (*skeldir == '\0') 350 return 0; 351 if ((dirp = opendir(skeldir)) == NULL) { 352 warn("can't open source . files dir `%s'", skeldir); 353 return 0; 354 } 355 for (n = 0; (dp = readdir(dirp)) != NULL && n == 0 ; ) { 356 if (strcmp(dp->d_name, ".") == 0 || 357 strcmp(dp->d_name, "..") == 0) { 358 continue; 359 } 360 n = 1; 361 } 362 (void) closedir(dirp); 363 if (n == 0) { 364 warnx("No \"dot\" initialisation files found"); 365 } else { 366 len = snprintf(src, sizeof(src), "%s/.", skeldir); 367 if (len < 0 || len >= sizeof(src)) { 368 warnx("skeleton directory `%s' too long", skeldir); 369 n = 0; 370 } else { 371 const char *cp_argv[] = { "cp", "-a", src, dst, NULL }; 372 if (verbose) 373 cp_argv[1] = "-av"; 374 run(CP, cp_argv); 375 } 376 } 377 return n; 378} 379 380/* returns 1 if the specified gid exists in the group file, else 0 */ 381static int 382gid_exists(gid_t gid) 383{ 384 return group_from_gid(gid, 1) != NULL; 385} 386 387/* return 1 if the specified group exists in the group file, else 0 */ 388static int 389group_exists(const char *group) 390{ 391 gid_t gid; 392 393 return gid_from_group(group, &gid) != -1; 394} 395 396/* create a group entry with gid `gid' */ 397static int 398creategid(char *group, gid_t gid, const char *name) 399{ 400 struct stat st; 401 FILE *from; 402 FILE *to; 403 char *buf; 404 char f[MaxFileNameLen]; 405 int fd, ret; 406 int wroteit = 0; 407 size_t len; 408 409 if (group_exists(group)) { 410 warnx("group `%s' already exists", group); 411 return 0; 412 } 413 if ((from = fopen(_PATH_GROUP, "r")) == NULL) { 414 warn("can't create gid for `%s': can't open `%s'", group, 415 _PATH_GROUP); 416 return 0; 417 } 418 if (flock(fileno(from), LOCK_EX | LOCK_NB) == -1) { 419 warn("can't lock `%s'", _PATH_GROUP); 420 } 421 (void) fstat(fileno(from), &st); 422 (void) snprintf(f, sizeof(f), "%s.XXXXXXXX", _PATH_GROUP); 423 if ((fd = mkstemp(f)) == -1) { 424 warn("can't create gid: mkstemp failed"); 425 fclose(from); 426 return 0; 427 } 428 if ((to = fdopen(fd, "w")) == NULL) { 429 warn("can't create gid: fdopen `%s' failed", f); 430 fclose(from); 431 close(fd); 432 unlink(f); 433 return 0; 434 } 435 while ((buf = fgetln(from, &len)) != NULL && len > 0) { 436 ret = 0; 437 if (buf[0] == '+' && wroteit == 0) { 438 ret = fprintf(to, "%s:*:%u:%s\n", group, gid, name); 439 wroteit = 1; 440 } 441 if (ret == -1 || 442 fprintf(to, "%*.*s", (int)len, (int)len, buf) != len) { 443 warn("can't create gid: short write to `%s'", f); 444 fclose(from); 445 fclose(to); 446 unlink(f); 447 return 0; 448 } 449 } 450 ret = 0; 451 if (wroteit == 0) 452 ret = fprintf(to, "%s:*:%u:%s\n", group, gid, name); 453 fclose(from); 454 if (fclose(to) == EOF || ret == -1) { 455 warn("can't create gid: short write to `%s'", f); 456 unlink(f); 457 return 0; 458 } 459 if (rename(f, _PATH_GROUP) == -1) { 460 warn("can't create gid: can't rename `%s' to `%s'", f, 461 _PATH_GROUP); 462 unlink(f); 463 return 0; 464 } 465 (void) chmod(_PATH_GROUP, st.st_mode & 0777); 466 syslog(LOG_INFO, "new group added: name=%s, gid=%u", group, gid); 467 return 1; 468} 469 470/* modify the group entry with name `group' to be newent */ 471static int 472modify_gid(char *group, char *newent) 473{ 474 struct stat st; 475 FILE *from; 476 FILE *to; 477 char buf[LINE_MAX]; 478 char f[MaxFileNameLen]; 479 char *colon; 480 int groupc; 481 int entc; 482 int fd; 483 int cc; 484 485 if ((from = fopen(_PATH_GROUP, "r")) == NULL) { 486 warn("can't modify gid for `%s': can't open `%s'", group, 487 _PATH_GROUP); 488 return 0; 489 } 490 if (flock(fileno(from), LOCK_EX | LOCK_NB) == -1) { 491 warn("can't lock `%s'", _PATH_GROUP); 492 } 493 (void) fstat(fileno(from), &st); 494 (void) snprintf(f, sizeof(f), "%s.XXXXXXXX", _PATH_GROUP); 495 if ((fd = mkstemp(f)) == -1) { 496 warn("can't modify gid: mkstemp failed"); 497 fclose(from); 498 return 0; 499 } 500 if ((to = fdopen(fd, "w")) == NULL) { 501 warn("can't modify gid: fdopen `%s' failed", f); 502 fclose(from); 503 close(fd); 504 unlink(f); 505 return 0; 506 } 507 groupc = strlen(group); 508 while (fgets(buf, sizeof(buf), from) != NULL) { 509 cc = strlen(buf); 510 if (cc > 0 && buf[cc - 1] != '\n' && !feof(from)) { 511 while (fgetc(from) != '\n' && !feof(from)) 512 cc++; 513 warnx("%s: line `%s' too long (%d bytes), skipping", 514 _PATH_GROUP, buf, cc); 515 continue; 516 } 517 if ((colon = strchr(buf, ':')) == NULL) { 518 /* 519 * The only valid entry with no column is the all-YP 520 * line. 521 */ 522 if (strcmp(buf, "+\n") != 0) { 523 warnx("badly formed entry `%.*s'", cc - 1, buf); 524 continue; 525 } 526 } else { 527 entc = (int)(colon - buf); 528 if (entc == groupc && strncmp(group, buf, entc) == 0) { 529 if (newent == NULL) { 530 continue; 531 } else { 532 cc = strlcpy(buf, newent, sizeof(buf)); 533 if (cc >= sizeof(buf)) { 534 warnx("group `%s' entry too long", 535 newent); 536 fclose(from); 537 fclose(to); 538 unlink(f); 539 return (0); 540 } 541 } 542 } 543 } 544 if (fwrite(buf, cc, 1, to) != 1) { 545 warn("can't modify gid: short write to `%s'", f); 546 fclose(from); 547 fclose(to); 548 unlink(f); 549 return 0; 550 } 551 } 552 fclose(from); 553 if (fclose(to) == EOF) { 554 warn("can't modify gid: short write to `%s'", f); 555 unlink(f); 556 return 0; 557 } 558 if (rename(f, _PATH_GROUP) == -1) { 559 warn("can't modify gid: can't rename `%s' to `%s'", f, _PATH_GROUP); 560 unlink(f); 561 return 0; 562 } 563 (void) chmod(_PATH_GROUP, st.st_mode & 0777); 564 if (newent == NULL) { 565 syslog(LOG_INFO, "group deleted: name=%s", group); 566 } else { 567 syslog(LOG_INFO, "group information modified: name=%s", group); 568 } 569 return 1; 570} 571 572/* modify the group entries for all `groups', by adding `user' */ 573static int 574append_group(char *user, int ngroups, const char **groups) 575{ 576 struct group *grp; 577 struct passwd *pwp; 578 struct stat st; 579 FILE *from; 580 FILE *to; 581 char buf[LINE_MAX]; 582 char f[MaxFileNameLen]; 583 char *colon; 584 const char *ugid = NULL; 585 int fd; 586 int cc; 587 int i; 588 int j; 589 590 if ((pwp = getpwnam(user))) { 591 if ((ugid = group_from_gid(pwp->pw_gid, 1)) == NULL) { 592 warnx("can't get primary group for user `%s'", user); 593 return 0; 594 } 595 } 596 597 for (i = 0 ; i < ngroups ; i++) { 598 if ((grp = getgrnam(groups[i])) == NULL) { 599 warnx("can't append group `%s' for user `%s'", 600 groups[i], user); 601 } else { 602 for (j = 0 ; grp->gr_mem[j] ; j++) { 603 if (strcmp(user, grp->gr_mem[j]) == 0) { 604 /* already in it */ 605 groups[i] = ""; 606 } 607 } 608 } 609 } 610 if ((from = fopen(_PATH_GROUP, "r")) == NULL) { 611 warn("can't append group for `%s': can't open `%s'", user, 612 _PATH_GROUP); 613 return 0; 614 } 615 if (flock(fileno(from), LOCK_EX | LOCK_NB) == -1) { 616 warn("can't lock `%s'", _PATH_GROUP); 617 } 618 (void) fstat(fileno(from), &st); 619 (void) snprintf(f, sizeof(f), "%s.XXXXXXXX", _PATH_GROUP); 620 if ((fd = mkstemp(f)) == -1) { 621 warn("can't append group: mkstemp failed"); 622 fclose(from); 623 return 0; 624 } 625 if ((to = fdopen(fd, "w")) == NULL) { 626 warn("can't append group: fdopen `%s' failed", f); 627 fclose(from); 628 close(fd); 629 unlink(f); 630 return 0; 631 } 632 while (fgets(buf, sizeof(buf), from) != NULL) { 633 cc = strlen(buf); 634 if (cc > 0 && buf[cc - 1] != '\n' && !feof(from)) { 635 while (fgetc(from) != '\n' && !feof(from)) 636 cc++; 637 warnx("%s: line `%s' too long (%d bytes), skipping", 638 _PATH_GROUP, buf, cc); 639 continue; 640 } 641 if ((colon = strchr(buf, ':')) == NULL) { 642 warnx("badly formed entry `%s'", buf); 643 continue; 644 } 645 for (i = 0 ; i < ngroups ; i++) { 646 j = (int)(colon - buf); 647 if (ugid) { 648 if (strcmp(ugid, groups[i]) == 0) { 649 /* user's primary group, no need to append */ 650 groups[i] = ""; 651 } 652 } 653 if (strncmp(groups[i], buf, j) == 0 && 654 groups[i][j] == '\0') { 655 while (isspace((unsigned char)buf[cc - 1])) 656 cc--; 657 buf[(j = cc)] = '\0'; 658 if (buf[strlen(buf) - 1] != ':') 659 strlcat(buf, ",", sizeof(buf)); 660 cc = strlcat(buf, user, sizeof(buf)) + 1; 661 if (cc >= sizeof(buf)) { 662 warnx("Warning: group `%s' would " 663 "become too long, not modifying", 664 groups[i]); 665 cc = j + 1; 666 } 667 buf[cc - 1] = '\n'; 668 buf[cc] = '\0'; 669 } 670 } 671 if (fwrite(buf, cc, 1, to) != 1) { 672 warn("can't append group: short write to `%s'", f); 673 fclose(from); 674 fclose(to); 675 unlink(f); 676 return 0; 677 } 678 } 679 fclose(from); 680 if (fclose(to) == EOF) { 681 warn("can't append group: short write to `%s'", f); 682 unlink(f); 683 return 0; 684 } 685 if (rename(f, _PATH_GROUP) == -1) { 686 warn("can't append group: can't rename `%s' to `%s'", f, _PATH_GROUP); 687 unlink(f); 688 return 0; 689 } 690 (void) chmod(_PATH_GROUP, st.st_mode & 0777); 691 return 1; 692} 693 694/* return 1 if `login' is a valid login name */ 695static int 696valid_login(char *login_name) 697{ 698 unsigned char *cp; 699 700 /* The first character cannot be a hyphen */ 701 if (*login_name == '-') 702 return 0; 703 704 for (cp = login_name ; *cp ; cp++) { 705 /* We allow '$' as the last character for samba */ 706 if (!isalnum((unsigned char)*cp) && *cp != '.' && 707 *cp != '_' && *cp != '-' && 708 !(*cp == '$' && *(cp + 1) == '\0')) { 709 return 0; 710 } 711 } 712 if ((char *)cp - login_name > MaxUserNameLen) 713 return 0; 714 return 1; 715} 716 717/* return 1 if `group' is a valid group name */ 718static int 719valid_group(char *group) 720{ 721 unsigned char *cp; 722 723 for (cp = group ; *cp ; cp++) { 724 if (!isalnum((unsigned char)*cp) && *cp != '.' && 725 *cp != '_' && *cp != '-') { 726 return 0; 727 } 728 } 729 if ((char *)cp - group > MaxUserNameLen) 730 return 0; 731 return 1; 732} 733 734/* return 1 if `class' exists */ 735static int 736valid_class(char *class) 737{ 738 login_cap_t *lc; 739 740 if ((lc = login_getclass(class)) != NULL) 741 login_close(lc); 742 return lc != NULL; 743} 744 745/* find the next gid in the range lo .. hi */ 746static int 747getnextgid(uid_t *gidp, uid_t lo, uid_t hi) 748{ 749 for (*gidp = lo ; *gidp < hi ; *gidp += 1) { 750 if (!gid_exists((gid_t)*gidp)) { 751 return 1; 752 } 753 } 754 return 0; 755} 756 757/* save a range of uids */ 758static int 759save_range(user_t *up, char *cp) 760{ 761 uid_t from; 762 uid_t to; 763 int i; 764 765 if (up->u_rc == up->u_rsize) { 766 up->u_rsize *= 2; 767 if ((up->u_rv = reallocarray(up->u_rv, up->u_rsize, 768 sizeof(range_t))) == NULL) { 769 warn(NULL); 770 return 0; 771 } 772 } 773 if (up->u_rv && sscanf(cp, "%u..%u", &from, &to) == 2) { 774 for (i = up->u_defrc ; i < up->u_rc ; i++) { 775 if (up->u_rv[i].r_from == from && up->u_rv[i].r_to == to) { 776 break; 777 } 778 } 779 if (i == up->u_rc) { 780 up->u_rv[up->u_rc].r_from = from; 781 up->u_rv[up->u_rc].r_to = to; 782 up->u_rc += 1; 783 } 784 } else { 785 warnx("Bad uid range `%s'", cp); 786 return 0; 787 } 788 return 1; 789} 790 791/* set the defaults in the defaults file */ 792static int 793setdefaults(user_t *up) 794{ 795 char template[MaxFileNameLen]; 796 FILE *fp; 797 int ret; 798 int fd; 799 int i; 800 801 (void) snprintf(template, sizeof(template), "%s.XXXXXXXX", CONFFILE); 802 if ((fd = mkstemp(template)) == -1) { 803 warnx("can't mkstemp `%s' for writing", CONFFILE); 804 return 0; 805 } 806 if ((fp = fdopen(fd, "w")) == NULL) { 807 warn("can't fdopen `%s' for writing", CONFFILE); 808 return 0; 809 } 810 ret = 1; 811 if (fprintf(fp, "group\t\t%s\n", up->u_primgrp) <= 0 || 812 fprintf(fp, "base_dir\t%s\n", up->u_basedir) <= 0 || 813 fprintf(fp, "skel_dir\t%s\n", up->u_skeldir) <= 0 || 814 fprintf(fp, "shell\t\t%s\n", up->u_shell) <= 0 || 815 fprintf(fp, "class\t\t%s\n", up->u_class) <= 0 || 816 fprintf(fp, "inactive\t%s\n", (up->u_inactive == NULL) ? UNSET_INACTIVE : up->u_inactive) <= 0 || 817 fprintf(fp, "expire\t\t%s\n", (up->u_expire == NULL) ? UNSET_EXPIRY : up->u_expire) <= 0 || 818 fprintf(fp, "preserve\t%s\n", (up->u_preserve == 0) ? "false" : "true") <= 0) { 819 warn("can't write to `%s'", CONFFILE); 820 ret = 0; 821 } 822 for (i = (up->u_defrc != up->u_rc) ? up->u_defrc : 0 ; i < up->u_rc ; i++) { 823 if (fprintf(fp, "range\t\t%u..%u\n", up->u_rv[i].r_from, up->u_rv[i].r_to) <= 0) { 824 warn("can't write to `%s'", CONFFILE); 825 ret = 0; 826 } 827 } 828 if (fclose(fp) == EOF) { 829 warn("can't write to `%s'", CONFFILE); 830 ret = 0; 831 } 832 if (ret) { 833 ret = ((rename(template, CONFFILE) == 0) && (chmod(CONFFILE, 0644) == 0)); 834 } 835 return ret; 836} 837 838/* read the defaults file */ 839static void 840read_defaults(user_t *up) 841{ 842 struct stat st; 843 size_t lineno; 844 size_t len; 845 FILE *fp; 846 unsigned char *cp; 847 unsigned char *s; 848 849 strsave(&up->u_primgrp, DEF_GROUP); 850 strsave(&up->u_basedir, DEF_BASEDIR); 851 strsave(&up->u_skeldir, DEF_SKELDIR); 852 strsave(&up->u_shell, DEF_SHELL); 853 strsave(&up->u_comment, DEF_COMMENT); 854 strsave(&up->u_class, DEF_CLASS); 855 up->u_rsize = 16; 856 up->u_defrc = 0; 857 if ((up->u_rv = calloc(up->u_rsize, sizeof(range_t))) == NULL) 858 err(1, NULL); 859 up->u_inactive = DEF_INACTIVE; 860 up->u_expire = DEF_EXPIRE; 861 if ((fp = fopen(CONFFILE, "r")) == NULL) { 862 if (stat(CONFFILE, &st) == -1 && !setdefaults(up)) { 863 warn("can't create `%s' defaults file", CONFFILE); 864 } 865 fp = fopen(CONFFILE, "r"); 866 } 867 if (fp != NULL) { 868 while ((s = fparseln(fp, &len, &lineno, NULL, 0)) != NULL) { 869 if (strncmp(s, "group", 5) == 0) { 870 for (cp = s + 5 ; isspace((unsigned char)*cp); cp++) { 871 } 872 strsave(&up->u_primgrp, cp); 873 } else if (strncmp(s, "base_dir", 8) == 0) { 874 for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { 875 } 876 strsave(&up->u_basedir, cp); 877 } else if (strncmp(s, "skel_dir", 8) == 0) { 878 for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { 879 } 880 strsave(&up->u_skeldir, cp); 881 } else if (strncmp(s, "shell", 5) == 0) { 882 for (cp = s + 5 ; isspace((unsigned char)*cp); cp++) { 883 } 884 strsave(&up->u_shell, cp); 885 } else if (strncmp(s, "password", 8) == 0) { 886 for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { 887 } 888 strsave(&up->u_password, cp); 889 } else if (strncmp(s, "class", 5) == 0) { 890 for (cp = s + 5 ; isspace((unsigned char)*cp); cp++) { 891 } 892 strsave(&up->u_class, cp); 893 } else if (strncmp(s, "inactive", 8) == 0) { 894 for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { 895 } 896 if (strcmp(cp, UNSET_INACTIVE) == 0) { 897 free(up->u_inactive); 898 up->u_inactive = NULL; 899 } else { 900 strsave(&up->u_inactive, cp); 901 } 902 } else if (strncmp(s, "range", 5) == 0) { 903 for (cp = s + 5 ; isspace((unsigned char)*cp); cp++) { 904 } 905 (void) save_range(up, cp); 906 } else if (strncmp(s, "preserve", 8) == 0) { 907 for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { 908 } 909 up->u_preserve = (strncmp(cp, "true", 4) == 0) ? 1 : 910 (strncmp(cp, "yes", 3) == 0) ? 1 : 911 strtonum(cp, INT_MIN, INT_MAX, NULL); 912 } else if (strncmp(s, "expire", 6) == 0) { 913 for (cp = s + 6 ; isspace((unsigned char)*cp); cp++) { 914 } 915 if (strcmp(cp, UNSET_EXPIRY) == 0) { 916 free(up->u_expire); 917 up->u_expire = NULL; 918 } else { 919 strsave(&up->u_expire, cp); 920 } 921 } 922 free(s); 923 } 924 fclose(fp); 925 } 926 if (up->u_rc == 0) { 927 up->u_rv[up->u_rc].r_from = DEF_LOWUID; 928 up->u_rv[up->u_rc].r_to = DEF_HIGHUID; 929 up->u_rc += 1; 930 } 931 up->u_defrc = up->u_rc; 932} 933 934/* return 1 if the specified uid exists in the passwd file, else 0 */ 935static int 936uid_exists(uid_t uid) 937{ 938 return user_from_uid(uid, 1) != NULL; 939} 940 941/* return 1 if the specified user exists in the passwd file, else 0 */ 942static int 943user_exists(const char *user) 944{ 945 uid_t uid; 946 947 return uid_from_user(user, &uid) != -1; 948} 949 950/* return the next valid unused uid */ 951static int 952getnextuid(int sync_uid_gid, uid_t *uid, uid_t low_uid, uid_t high_uid) 953{ 954 for (*uid = low_uid ; *uid <= high_uid ; (*uid)++) { 955 if (!uid_exists((uid_t)*uid) && *uid != NOBODY_UID) { 956 if (sync_uid_gid) { 957 if (!gid_exists((gid_t)*uid)) { 958 return 1; 959 } 960 } else { 961 return 1; 962 } 963 } 964 } 965 return 0; 966} 967 968/* look for a valid time, return 0 if it was specified but bad */ 969static int 970scantime(time_t *tp, char *s) 971{ 972 struct tm tm; 973 974 *tp = 0; 975 if (s != NULL) { 976 memset(&tm, 0, sizeof(tm)); 977 tm.tm_isdst = -1; 978 if (strptime(s, "%c", &tm) != NULL) { 979 *tp = mktime(&tm); 980 } else if (strptime(s, "%B %d %Y", &tm) != NULL) { 981 *tp = mktime(&tm); 982 } else if (isdigit((unsigned char) s[0]) != 0) { 983 *tp = (time_t)atoll(s); 984 } else { 985 return 0; 986 } 987 } 988 return 1; 989} 990 991/* compute the extra length '&' expansion consumes */ 992static size_t 993expand_len(const char *p, const char *username) 994{ 995 size_t alen; 996 size_t ulen; 997 998 ulen = strlen(username); 999 for (alen = 0; *p != '\0'; p++) 1000 if (*p == '&') 1001 alen += ulen - 1; 1002 return alen; 1003} 1004 1005/* see if we can find out the user struct */ 1006static struct passwd * 1007find_user_info(const char *name) 1008{ 1009 struct passwd *pwp; 1010 const char *errstr; 1011 uid_t uid; 1012 1013 if ((pwp = getpwnam(name)) == NULL) { 1014 uid = strtonum(name, -1, UID_MAX, &errstr); 1015 if (errstr == NULL) 1016 pwp = getpwuid(uid); 1017 } 1018 return pwp; 1019} 1020 1021/* see if we can find out the group struct */ 1022static struct group * 1023find_group_info(const char *name) 1024{ 1025 struct group *grp; 1026 const char *errstr; 1027 gid_t gid; 1028 1029 if ((grp = getgrnam(name)) == NULL) { 1030 gid = strtonum(name, -1, GID_MAX, &errstr); 1031 if (errstr == NULL) 1032 grp = getgrgid(gid); 1033 } 1034 return grp; 1035} 1036 1037/* add a user */ 1038static int 1039adduser(char *login_name, user_t *up) 1040{ 1041 struct group *grp; 1042 struct stat st; 1043 time_t expire; 1044 time_t inactive; 1045 char password[PasswordLength + 1]; 1046 char home[MaxFileNameLen]; 1047 char buf[LINE_MAX]; 1048 int sync_uid_gid; 1049 int masterfd; 1050 int ptmpfd; 1051 gid_t gid; 1052 int cc; 1053 int i, yp = 0; 1054 FILE *fp; 1055 1056 if (!valid_login(login_name)) { 1057 errx(EXIT_FAILURE, "`%s' is not a valid login name", login_name); 1058 } 1059 if (!valid_class(up->u_class)) { 1060 errx(EXIT_FAILURE, "No such login class `%s'", up->u_class); 1061 } 1062 if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) == -1) { 1063 err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD); 1064 } 1065 if (flock(masterfd, LOCK_EX | LOCK_NB) == -1) { 1066 err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD); 1067 } 1068 pw_init(); 1069 if ((ptmpfd = pw_lock(WAITSECS)) == -1) { 1070 int saved_errno = errno; 1071 close(masterfd); 1072 errc(EXIT_FAILURE, saved_errno, "can't obtain pw_lock"); 1073 } 1074 if ((fp = fdopen(masterfd, "r")) == NULL) { 1075 int saved_errno = errno; 1076 close(masterfd); 1077 close(ptmpfd); 1078 pw_abort(); 1079 errc(EXIT_FAILURE, saved_errno, 1080 "can't fdopen `%s' for reading", _PATH_MASTERPASSWD); 1081 } 1082 while (fgets(buf, sizeof(buf), fp) != NULL) { 1083 cc = strlen(buf); 1084 /* 1085 * Stop copying the file at the yp entry; we want to 1086 * put the new user before it, and preserve entries 1087 * after the yp entry. 1088 */ 1089 if (cc > 1 && buf[0] == '+' && buf[1] == ':') { 1090 yp = 1; 1091 break; 1092 } 1093 if (write(ptmpfd, buf, (size_t)(cc)) != cc) { 1094 int saved_errno = errno; 1095 fclose(fp); 1096 close(ptmpfd); 1097 pw_abort(); 1098 errc(EXIT_FAILURE, saved_errno, 1099 "short write to /etc/ptmp (not %d chars)", cc); 1100 } 1101 } 1102 if (ferror(fp)) { 1103 int saved_errno = errno; 1104 fclose(fp); 1105 close(ptmpfd); 1106 pw_abort(); 1107 errc(EXIT_FAILURE, saved_errno, "read error on %s", 1108 _PATH_MASTERPASSWD); 1109 } 1110 /* if no uid was specified, get next one in [low_uid..high_uid] range */ 1111 sync_uid_gid = (strcmp(up->u_primgrp, "=uid") == 0); 1112 if (up->u_uid == -1) { 1113 int got_id = 0; 1114 1115 /* 1116 * Look for a free UID in the command line ranges (if any). 1117 * These start after the ranges specified in the config file. 1118 */ 1119 for (i = up->u_defrc; got_id == 0 && i < up->u_rc ; i++) { 1120 got_id = getnextuid(sync_uid_gid, &up->u_uid, 1121 up->u_rv[i].r_from, up->u_rv[i].r_to); 1122 } 1123 /* 1124 * If there were no free UIDs in the command line ranges, 1125 * try the ranges from the config file (there will always 1126 * be at least one default). 1127 */ 1128 if (got_id == 0) { 1129 for (i = 0; got_id == 0 && i < up->u_defrc; i++) { 1130 got_id = getnextuid(sync_uid_gid, &up->u_uid, 1131 up->u_rv[i].r_from, up->u_rv[i].r_to); 1132 } 1133 } 1134 if (got_id == 0) { 1135 close(ptmpfd); 1136 pw_abort(); 1137 errx(EXIT_FAILURE, "can't get next uid for %u", up->u_uid); 1138 } 1139 } 1140 /* check uid isn't already allocated */ 1141 if (!(up->u_flags & F_DUPUID) && uid_exists((uid_t)up->u_uid)) { 1142 close(ptmpfd); 1143 pw_abort(); 1144 errx(EXIT_FAILURE, "uid %u is already in use", up->u_uid); 1145 } 1146 /* if -g=uid was specified, check gid is unused */ 1147 if (sync_uid_gid) { 1148 if (gid_exists((gid_t)up->u_uid)) { 1149 close(ptmpfd); 1150 pw_abort(); 1151 errx(EXIT_FAILURE, "gid %u is already in use", up->u_uid); 1152 } 1153 gid = up->u_uid; 1154 } else { 1155 if ((grp = find_group_info(up->u_primgrp)) == NULL) { 1156 close(ptmpfd); 1157 pw_abort(); 1158 errx(EXIT_FAILURE, "group %s not found", up->u_primgrp); 1159 } 1160 gid = grp->gr_gid; 1161 } 1162 /* check name isn't already in use */ 1163 if (!(up->u_flags & F_DUPUID) && user_exists(login_name)) { 1164 close(ptmpfd); 1165 pw_abort(); 1166 errx(EXIT_FAILURE, "already a `%s' user", login_name); 1167 } 1168 if (up->u_flags & F_HOMEDIR) { 1169 if (strlcpy(home, up->u_home, sizeof(home)) >= sizeof(home)) { 1170 close(ptmpfd); 1171 pw_abort(); 1172 errx(EXIT_FAILURE, "home directory `%s' too long", 1173 up->u_home); 1174 } 1175 } else { 1176 /* if home directory hasn't been given, make it up */ 1177 if (snprintf(home, sizeof(home), "%s/%s", up->u_basedir, 1178 login_name) >= sizeof(home)) { 1179 close(ptmpfd); 1180 pw_abort(); 1181 errx(EXIT_FAILURE, "home directory `%s/%s' too long", 1182 up->u_basedir, login_name); 1183 } 1184 } 1185 if (!scantime(&inactive, up->u_inactive)) { 1186 warnx("Warning: inactive time `%s' invalid, password expiry off", 1187 up->u_inactive); 1188 } 1189 if (!scantime(&expire, up->u_expire)) { 1190 warnx("Warning: expire time `%s' invalid, account expiry off", 1191 up->u_expire); 1192 } 1193 if (lstat(home, &st) == -1 && !(up->u_flags & F_MKDIR) && 1194 strcmp(home, _PATH_NONEXISTENT) != 0) { 1195 warnx("Warning: home directory `%s' doesn't exist, and -m was" 1196 " not specified", home); 1197 } 1198 (void) strlcpy(password, up->u_password ? up->u_password : "*", 1199 sizeof(password)); 1200 cc = snprintf(buf, sizeof(buf), "%s:%s:%u:%u:%s:%lld:%lld:%s:%s:%s\n", 1201 login_name, 1202 password, 1203 up->u_uid, 1204 gid, 1205 up->u_class, 1206 (long long) inactive, 1207 (long long) expire, 1208 up->u_comment, 1209 home, 1210 up->u_shell); 1211 if (cc >= sizeof(buf) || cc < 0 || 1212 cc + expand_len(up->u_comment, login_name) >= 1023) { 1213 close(ptmpfd); 1214 pw_abort(); 1215 errx(EXIT_FAILURE, "can't add `%s', line too long", buf); 1216 } 1217 if (write(ptmpfd, buf, (size_t) cc) != cc) { 1218 int saved_errno = errno; 1219 close(ptmpfd); 1220 pw_abort(); 1221 errc(EXIT_FAILURE, saved_errno, "can't add `%s'", buf); 1222 } 1223 if (yp) { 1224 /* put back the + line */ 1225 cc = snprintf(buf, sizeof(buf), "+:*::::::::\n"); 1226 if (cc < 0 || cc >= sizeof(buf)) { 1227 close(ptmpfd); 1228 pw_abort(); 1229 errx(EXIT_FAILURE, "can't add `%s', line too long", buf); 1230 } 1231 if (write(ptmpfd, buf, (size_t) cc) != cc) { 1232 int saved_errno = errno; 1233 close(ptmpfd); 1234 pw_abort(); 1235 errc(EXIT_FAILURE, saved_errno, "can't add `%s'", buf); 1236 } 1237 /* copy the entries following it, if any */ 1238 while (fgets(buf, sizeof(buf), fp) != NULL) { 1239 cc = strlen(buf); 1240 if (write(ptmpfd, buf, (size_t)(cc)) != cc) { 1241 int saved_errno = errno; 1242 fclose(fp); 1243 close(ptmpfd); 1244 pw_abort(); 1245 errc(EXIT_FAILURE, saved_errno, 1246 "short write to /etc/ptmp (not %d chars)", 1247 cc); 1248 } 1249 } 1250 if (ferror(fp)) { 1251 int saved_errno = errno; 1252 fclose(fp); 1253 close(ptmpfd); 1254 pw_abort(); 1255 errc(EXIT_FAILURE, saved_errno, "read error on %s", 1256 _PATH_MASTERPASSWD); 1257 } 1258 } 1259 if (up->u_flags & F_MKDIR) { 1260 if (lstat(home, &st) == 0) { 1261 close(ptmpfd); 1262 pw_abort(); 1263 errx(EXIT_FAILURE, "home directory `%s' already exists", 1264 home); 1265 } else { 1266 char idstr[64]; 1267 const char *mkdir_argv[] = 1268 { "mkdir", "-p", home, NULL }; 1269 const char *chown_argv[] = 1270 { "chown", "-RP", idstr, home, NULL }; 1271 const char *chmod_argv[] = 1272 { "chmod", "-R", "u+w", home, NULL }; 1273 1274 if (run(MKDIR, mkdir_argv) != 0) { 1275 int saved_errno = errno; 1276 close(ptmpfd); 1277 pw_abort(); 1278 errc(EXIT_FAILURE, saved_errno, 1279 "can't mkdir `%s'", home); 1280 } 1281 (void) copydotfiles(up->u_skeldir, home); 1282 (void) snprintf(idstr, sizeof(idstr), "%u:%u", 1283 up->u_uid, gid); 1284 (void) run(CHOWN, chown_argv); 1285 (void) run(CHMOD, chmod_argv); 1286 } 1287 } 1288 if (strcmp(up->u_primgrp, "=uid") == 0 && !group_exists(login_name) && 1289 !creategid(login_name, gid, "")) { 1290 close(ptmpfd); 1291 pw_abort(); 1292 errx(EXIT_FAILURE, "can't create gid %u for login name %s", 1293 gid, login_name); 1294 } 1295 if (up->u_groupc > 0 && !append_group(login_name, up->u_groupc, up->u_groupv)) { 1296 close(ptmpfd); 1297 pw_abort(); 1298 errx(EXIT_FAILURE, "can't append `%s' to new groups", login_name); 1299 } 1300 fclose(fp); 1301 close(ptmpfd); 1302 if (pw_mkdb(yp ? NULL : login_name, 0) == -1) { 1303 pw_abort(); 1304 err(EXIT_FAILURE, "pw_mkdb failed"); 1305 } 1306 syslog(LOG_INFO, "new user added: name=%s, uid=%u, gid=%u, home=%s, shell=%s", 1307 login_name, up->u_uid, gid, home, up->u_shell); 1308 return 1; 1309} 1310 1311/* remove a user from the groups file */ 1312static int 1313rm_user_from_groups(char *login_name) 1314{ 1315 struct stat st; 1316 size_t login_len; 1317 FILE *from; 1318 FILE *to; 1319 char buf[LINE_MAX]; 1320 char f[MaxFileNameLen]; 1321 char *cp, *ep; 1322 int fd; 1323 int cc; 1324 1325 login_len = strlen(login_name); 1326 if ((from = fopen(_PATH_GROUP, "r")) == NULL) { 1327 warn("can't remove gid for `%s': can't open `%s'", 1328 login_name, _PATH_GROUP); 1329 return 0; 1330 } 1331 if (flock(fileno(from), LOCK_EX | LOCK_NB) == -1) { 1332 warn("can't lock `%s'", _PATH_GROUP); 1333 } 1334 (void) fstat(fileno(from), &st); 1335 (void) snprintf(f, sizeof(f), "%s.XXXXXXXX", _PATH_GROUP); 1336 if ((fd = mkstemp(f)) == -1) { 1337 warn("can't remove gid for `%s': mkstemp failed", login_name); 1338 fclose(from); 1339 return 0; 1340 } 1341 if ((to = fdopen(fd, "w")) == NULL) { 1342 warn("can't remove gid for `%s': fdopen `%s' failed", 1343 login_name, f); 1344 fclose(from); 1345 close(fd); 1346 unlink(f); 1347 return 0; 1348 } 1349 while (fgets(buf, sizeof(buf), from) != NULL) { 1350 cc = strlen(buf); 1351 if (cc > 0 && buf[cc - 1] != '\n' && !feof(from)) { 1352 while (fgetc(from) != '\n' && !feof(from)) 1353 cc++; 1354 warnx("%s: line `%s' too long (%d bytes), skipping", 1355 _PATH_GROUP, buf, cc); 1356 continue; 1357 } 1358 1359 /* Break out the group list. */ 1360 for (cp = buf, cc = 0; *cp != '\0' && cc < 3; cp++) { 1361 if (*cp == ':') 1362 cc++; 1363 } 1364 if (cc != 3) { 1365 buf[strcspn(buf, "\n")] = '\0'; 1366 warnx("Malformed entry `%s'. Skipping", buf); 1367 continue; 1368 } 1369 while ((cp = strstr(cp, login_name)) != NULL) { 1370 if ((cp[-1] == ':' || cp[-1] == ',') && 1371 (cp[login_len] == ',' || cp[login_len] == '\n')) { 1372 ep = cp + login_len; 1373 if (cp[login_len] == ',') 1374 ep++; 1375 else if (cp[-1] == ',') 1376 cp--; 1377 memmove(cp, ep, strlen(ep) + 1); 1378 } else { 1379 if ((cp = strchr(cp, ',')) == NULL) 1380 break; 1381 cp++; 1382 } 1383 } 1384 if (fwrite(buf, strlen(buf), 1, to) != 1) { 1385 warn("can't remove gid for `%s': short write to `%s'", 1386 login_name, f); 1387 fclose(from); 1388 fclose(to); 1389 unlink(f); 1390 return 0; 1391 } 1392 } 1393 (void) fchmod(fileno(to), st.st_mode & 0777); 1394 fclose(from); 1395 if (fclose(to) == EOF) { 1396 warn("can't remove gid for `%s': short write to `%s'", 1397 login_name, f); 1398 unlink(f); 1399 return 0; 1400 } 1401 if (rename(f, _PATH_GROUP) == -1) { 1402 warn("can't remove gid for `%s': can't rename `%s' to `%s'", 1403 login_name, f, _PATH_GROUP); 1404 unlink(f); 1405 return 0; 1406 } 1407 return 1; 1408} 1409 1410/* check that the user or group is local, not from YP/NIS */ 1411static int 1412is_local(char *name, const char *file) 1413{ 1414 FILE *fp; 1415 char buf[LINE_MAX]; 1416 size_t len; 1417 int ret; 1418 int cc; 1419 1420 if ((fp = fopen(file, "r")) == NULL) { 1421 err(EXIT_FAILURE, "can't open `%s'", file); 1422 } 1423 len = strlen(name); 1424 for (ret = 0 ; fgets(buf, sizeof(buf), fp) != NULL ; ) { 1425 cc = strlen(buf); 1426 if (cc > 0 && buf[cc - 1] != '\n' && !feof(fp)) { 1427 while (fgetc(fp) != '\n' && !feof(fp)) 1428 cc++; 1429 warnx("%s: line `%s' too long (%d bytes), skipping", 1430 file, buf, cc); 1431 continue; 1432 } 1433 if (strncmp(buf, name, len) == 0 && buf[len] == ':') { 1434 ret = 1; 1435 break; 1436 } 1437 } 1438 fclose(fp); 1439 return ret; 1440} 1441 1442/* modify a user */ 1443static int 1444moduser(char *login_name, char *newlogin, user_t *up) 1445{ 1446 struct passwd *pwp = NULL; 1447 struct group *grp; 1448 const char *homedir; 1449 char buf[LINE_MAX]; 1450 char acctlock_str[] = "-"; 1451 char pwlock_str[] = "*"; 1452 char pw_len[PasswordLength + 1]; 1453 char shell_len[MaxShellNameLen]; 1454 char *shell_last_char; 1455 size_t colonc, loginc; 1456 size_t cc; 1457 size_t shell_buf; 1458 FILE *master; 1459 char newdir[MaxFileNameLen]; 1460 char *colon; 1461 char *pw_tmp = NULL; 1462 char *shell_tmp = NULL; 1463 int len; 1464 int locked = 0; 1465 int unlocked = 0; 1466 int masterfd; 1467 int ptmpfd; 1468 int rval; 1469 int i; 1470 1471 if (!valid_login(newlogin)) { 1472 errx(EXIT_FAILURE, "`%s' is not a valid login name", login_name); 1473 } 1474 if ((pwp = getpwnam_shadow(login_name)) == NULL) { 1475 errx(EXIT_FAILURE, "No such user `%s'", login_name); 1476 } 1477 if (up != NULL) { 1478 if ((*pwp->pw_passwd != '\0') && 1479 (up->u_flags & F_PASSWORD) == 0) { 1480 up->u_flags |= F_PASSWORD; 1481 strsave(&up->u_password, pwp->pw_passwd); 1482 explicit_bzero(pwp->pw_passwd, strlen(pwp->pw_passwd)); 1483 } 1484 } 1485 endpwent(); 1486 1487 if (pledge("stdio rpath wpath cpath fattr flock proc exec getpw id", 1488 NULL) == -1) 1489 err(1, "pledge"); 1490 1491 if (!is_local(login_name, _PATH_MASTERPASSWD)) { 1492 errx(EXIT_FAILURE, "User `%s' must be a local user", login_name); 1493 } 1494 if (up != NULL) { 1495 if ((up->u_flags & (F_ACCTLOCK | F_ACCTUNLOCK)) && (pwp->pw_uid < 1000)) 1496 errx(EXIT_FAILURE, "(un)locking is not supported for the `%s' account", pwp->pw_name); 1497 } 1498 /* keep dir name in case we need it for '-m' */ 1499 homedir = pwp->pw_dir; 1500 1501 /* get the last char of the shell in case we need it for '-U' or '-Z' */ 1502 shell_last_char = pwp->pw_shell+strlen(pwp->pw_shell) - 1; 1503 1504 if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) == -1) { 1505 err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD); 1506 } 1507 if (flock(masterfd, LOCK_EX | LOCK_NB) == -1) { 1508 err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD); 1509 } 1510 pw_init(); 1511 if ((ptmpfd = pw_lock(WAITSECS)) == -1) { 1512 int saved_errno = errno; 1513 close(masterfd); 1514 errc(EXIT_FAILURE, saved_errno, "can't obtain pw_lock"); 1515 } 1516 if ((master = fdopen(masterfd, "r")) == NULL) { 1517 int saved_errno = errno; 1518 close(masterfd); 1519 close(ptmpfd); 1520 pw_abort(); 1521 errc(EXIT_FAILURE, saved_errno, "can't fdopen fd for %s", 1522 _PATH_MASTERPASSWD); 1523 } 1524 if (up != NULL) { 1525 if (up->u_flags & F_USERNAME) { 1526 /* if changing name, check new name isn't already in use */ 1527 if (strcmp(login_name, newlogin) != 0 && 1528 user_exists(newlogin)) { 1529 close(ptmpfd); 1530 pw_abort(); 1531 errx(EXIT_FAILURE, "already a `%s' user", newlogin); 1532 } 1533 pwp->pw_name = newlogin; 1534 1535 /* 1536 * Provide a new directory name in case the 1537 * home directory is to be moved. 1538 */ 1539 if (up->u_flags & F_MKDIR) { 1540 (void) snprintf(newdir, sizeof(newdir), 1541 "%s/%s", up->u_basedir, newlogin); 1542 pwp->pw_dir = newdir; 1543 } 1544 } 1545 if (up->u_flags & F_PASSWORD) { 1546 if (up->u_password != NULL) 1547 pwp->pw_passwd = up->u_password; 1548 } 1549 if (up->u_flags & F_ACCTLOCK) { 1550 /* lock the account */ 1551 if (*shell_last_char != *acctlock_str) { 1552 shell_tmp = malloc(strlen(pwp->pw_shell) + sizeof(acctlock_str)); 1553 if (shell_tmp == NULL) { 1554 close(ptmpfd); 1555 pw_abort(); 1556 errx(EXIT_FAILURE, "account lock: cannot allocate memory"); 1557 } 1558 strlcpy(shell_tmp, pwp->pw_shell, sizeof(shell_len)); 1559 strlcat(shell_tmp, acctlock_str, sizeof(shell_len)); 1560 pwp->pw_shell = shell_tmp; 1561 } else { 1562 locked++; 1563 } 1564 /* lock the password */ 1565 if (strncmp(pwp->pw_passwd, pwlock_str, sizeof(pwlock_str)-1) != 0) { 1566 pw_tmp = malloc(strlen(pwp->pw_passwd) + sizeof(pwlock_str)); 1567 if (pw_tmp == NULL) { 1568 close(ptmpfd); 1569 pw_abort(); 1570 errx(EXIT_FAILURE, "password lock: cannot allocate memory"); 1571 } 1572 strlcpy(pw_tmp, pwlock_str, sizeof(pw_len)); 1573 strlcat(pw_tmp, pwp->pw_passwd, sizeof(pw_len)); 1574 pwp->pw_passwd = pw_tmp; 1575 } else { 1576 locked++; 1577 } 1578 1579 if (locked > 1) 1580 warnx("account `%s' is already locked", pwp->pw_name); 1581 } 1582 if (up->u_flags & F_ACCTUNLOCK) { 1583 /* unlock the password */ 1584 if (strcmp(pwp->pw_passwd, pwlock_str) != 0 && 1585 strcmp(pwp->pw_passwd, "*************") != 0) { 1586 if (strncmp(pwp->pw_passwd, pwlock_str, sizeof(pwlock_str)-1) == 0) { 1587 pwp->pw_passwd += sizeof(pwlock_str)-1; 1588 } else { 1589 unlocked++; 1590 } 1591 } else { 1592 warnx("account `%s' has no password: cannot fully unlock", pwp->pw_name); 1593 } 1594 /* unlock the account */ 1595 if (*shell_last_char == *acctlock_str) { 1596 shell_buf = strlen(pwp->pw_shell) + 2 - sizeof(acctlock_str); 1597 shell_tmp = malloc(shell_buf); 1598 if (shell_tmp == NULL) { 1599 close(ptmpfd); 1600 pw_abort(); 1601 errx(EXIT_FAILURE, "unlock: cannot allocate memory"); 1602 } 1603 strlcpy(shell_tmp, pwp->pw_shell, shell_buf); 1604 pwp->pw_shell = shell_tmp; 1605 } else { 1606 unlocked++; 1607 } 1608 1609 if (unlocked > 1) 1610 warnx("account `%s' is not locked", pwp->pw_name); 1611 } 1612 if (up->u_flags & F_UID) { 1613 /* check uid isn't already allocated */ 1614 if (!(up->u_flags & F_DUPUID) && 1615 uid_exists((uid_t)up->u_uid)) { 1616 close(ptmpfd); 1617 pw_abort(); 1618 errx(EXIT_FAILURE, "uid %u is already in use", up->u_uid); 1619 } 1620 pwp->pw_uid = up->u_uid; 1621 } 1622 if (up->u_flags & F_GROUP) { 1623 /* if -g=uid was specified, check gid is unused */ 1624 if (strcmp(up->u_primgrp, "=uid") == 0) { 1625 if (gid_exists((gid_t)pwp->pw_uid)) { 1626 close(ptmpfd); 1627 pw_abort(); 1628 errx(EXIT_FAILURE, "gid %u is already " 1629 "in use", pwp->pw_uid); 1630 } 1631 pwp->pw_gid = pwp->pw_uid; 1632 if (!creategid(newlogin, pwp->pw_gid, "")) { 1633 close(ptmpfd); 1634 pw_abort(); 1635 errx(EXIT_FAILURE, "could not create " 1636 "group %s with gid %u", newlogin, 1637 pwp->pw_gid); 1638 } 1639 } else { 1640 if ((grp = find_group_info(up->u_primgrp)) == NULL) { 1641 close(ptmpfd); 1642 pw_abort(); 1643 errx(EXIT_FAILURE, "group %s not found", 1644 up->u_primgrp); 1645 } 1646 pwp->pw_gid = grp->gr_gid; 1647 } 1648 } 1649 if (up->u_flags & F_INACTIVE) { 1650 if (!scantime(&pwp->pw_change, up->u_inactive)) { 1651 warnx("Warning: inactive time `%s' invalid, password expiry off", 1652 up->u_inactive); 1653 } 1654 } 1655 if (up->u_flags & F_EXPIRE) { 1656 if (!scantime(&pwp->pw_expire, up->u_expire)) { 1657 warnx("Warning: expire time `%s' invalid, account expiry off", 1658 up->u_expire); 1659 } 1660 } 1661 if (up->u_flags & F_COMMENT) 1662 pwp->pw_gecos = up->u_comment; 1663 if (up->u_flags & F_HOMEDIR) 1664 pwp->pw_dir = up->u_home; 1665 if (up->u_flags & F_SHELL) 1666 pwp->pw_shell = up->u_shell; 1667 if (up->u_flags & F_CLASS) { 1668 if (!valid_class(up->u_class)) { 1669 close(ptmpfd); 1670 pw_abort(); 1671 errx(EXIT_FAILURE, 1672 "No such login class `%s'", up->u_class); 1673 } 1674 pwp->pw_class = up->u_class; 1675 } 1676 } 1677 loginc = strlen(login_name); 1678 while (fgets(buf, sizeof(buf), master) != NULL) { 1679 if ((colon = strchr(buf, ':')) == NULL) { 1680 warnx("Malformed entry `%s'. Skipping", buf); 1681 continue; 1682 } 1683 colonc = (size_t)(colon - buf); 1684 if (strncmp(login_name, buf, loginc) == 0 && loginc == colonc) { 1685 if (up != NULL) { 1686 if ((len = snprintf(buf, sizeof(buf), 1687 "%s:%s:%u:%u:%s:%lld:%lld:%s:%s:%s\n", 1688 newlogin, 1689 pwp->pw_passwd, 1690 pwp->pw_uid, 1691 pwp->pw_gid, 1692 pwp->pw_class, 1693 (long long)pwp->pw_change, 1694 (long long)pwp->pw_expire, 1695 pwp->pw_gecos, 1696 pwp->pw_dir, 1697 pwp->pw_shell)) >= sizeof(buf) || len < 0 || 1698 len + expand_len(pwp->pw_gecos, newlogin) 1699 >= 1023) { 1700 close(ptmpfd); 1701 pw_abort(); 1702 errx(EXIT_FAILURE, "can't add `%s', " 1703 "line too long (%zu bytes)", buf, 1704 len + expand_len(pwp->pw_gecos, 1705 newlogin)); 1706 } 1707 if (write(ptmpfd, buf, len) != len) { 1708 int saved_errno = errno; 1709 close(ptmpfd); 1710 pw_abort(); 1711 errc(EXIT_FAILURE, saved_errno, 1712 "can't add `%s'", buf); 1713 } 1714 } 1715 } else { 1716 len = strlen(buf); 1717 if ((cc = write(ptmpfd, buf, len)) != len) { 1718 int saved_errno = errno; 1719 close(masterfd); 1720 close(ptmpfd); 1721 pw_abort(); 1722 errc(EXIT_FAILURE, saved_errno, 1723 "short write to /etc/ptmp (%lld not %lld chars)", 1724 (long long)cc, (long long)len); 1725 } 1726 } 1727 } 1728 if (up != NULL) { 1729 const char *mv_argv[] = { "mv", homedir, pwp->pw_dir, NULL }; 1730 if ((up->u_flags & F_MKDIR) && 1731 run(MV, mv_argv) != 0) { 1732 int saved_errno = errno; 1733 close(ptmpfd); 1734 pw_abort(); 1735 errc(EXIT_FAILURE, saved_errno, 1736 "can't move `%s' to `%s'", homedir, pwp->pw_dir); 1737 } 1738 if (up->u_flags & F_SETSECGROUP) { 1739 for (i = 0 ; i < up->u_groupc ; i++) { 1740 if (!group_exists(up->u_groupv[i])) { 1741 close(ptmpfd); 1742 pw_abort(); 1743 errx(EXIT_FAILURE, 1744 "aborting, group `%s' does not exist", 1745 up->u_groupv[i]); 1746 } 1747 } 1748 if (!rm_user_from_groups(newlogin)) { 1749 close(ptmpfd); 1750 pw_abort(); 1751 errx(EXIT_FAILURE, 1752 "can't reset groups for `%s'", newlogin); 1753 } 1754 } 1755 if (up->u_groupc > 0) { 1756 if (!append_group(newlogin, up->u_groupc, up->u_groupv)) { 1757 close(ptmpfd); 1758 pw_abort(); 1759 errx(EXIT_FAILURE, "can't append `%s' to new groups", 1760 newlogin); 1761 } 1762 } 1763 } 1764 fclose(master); 1765 close(ptmpfd); 1766 free(pw_tmp); 1767 free(shell_tmp); 1768 if (up != NULL && strcmp(login_name, newlogin) == 0) 1769 rval = pw_mkdb(login_name, 0); 1770 else 1771 rval = pw_mkdb(NULL, 0); 1772 if (rval == -1) { 1773 pw_abort(); 1774 err(EXIT_FAILURE, "pw_mkdb failed"); 1775 } 1776 if (up == NULL) { 1777 syslog(LOG_INFO, "user removed: name=%s", login_name); 1778 } else if (strcmp(login_name, newlogin) == 0) { 1779 syslog(LOG_INFO, "user information modified: name=%s, uid=%u, gid=%u, home=%s, shell=%s", 1780 login_name, pwp->pw_uid, pwp->pw_gid, pwp->pw_dir, pwp->pw_shell); 1781 } else { 1782 syslog(LOG_INFO, "user information modified: name=%s, new name=%s, uid=%u, gid=%u, home=%s, shell=%s", 1783 login_name, newlogin, pwp->pw_uid, pwp->pw_gid, pwp->pw_dir, pwp->pw_shell); 1784 } 1785 return 1; 1786} 1787 1788/* print out usage message, and then exit */ 1789void 1790usermgmt_usage(const char *prog) 1791{ 1792 if (strcmp(prog, "useradd") == 0) { 1793 fprintf(stderr, "usage: %s -D [-b base-directory] " 1794 "[-e expiry-time] [-f inactive-time]\n" 1795 " [-g gid | name | =uid] [-k skel-directory] " 1796 "[-L login-class]\n" 1797 " [-r low..high] [-s shell]\n", prog); 1798 fprintf(stderr, " %s [-mov] [-b base-directory] " 1799 "[-c comment] [-d home-directory]\n" 1800 " [-e expiry-time] [-f inactive-time]\n" 1801 " [-G secondary-group[,group,...]] " 1802 "[-g gid | name | =uid]\n" 1803 " [-k skel-directory] [-L login-class] " 1804 "[-p password] [-r low..high]\n" 1805 " [-s shell] [-u uid] user\n", prog); 1806 } else if (strcmp(prog, "usermod") == 0) { 1807 fprintf(stderr, "usage: %s [-moUvZ] " 1808 "[-c comment] [-d home-directory] [-e expiry-time]\n" 1809 " [-f inactive-time] " 1810 "[-G secondary-group[,group,...]]\n" 1811 " [-g gid | name | =uid] [-L login-class] " 1812 "[-l new-login]\n" 1813 " [-p password] " 1814 "[-S secondary-group[,group,...]]\n" 1815 " [-s shell] [-u uid] user\n", 1816 prog); 1817 } else if (strcmp(prog, "userdel") == 0) { 1818 fprintf(stderr, "usage: %s -D [-p preserve-value]\n", 1819 prog); 1820 fprintf(stderr, " %s [-rv] [-p preserve-value] " 1821 "user\n", prog); 1822 } else if (strcmp(prog, "userinfo") == 0) { 1823 fprintf(stderr, "usage: %s [-e] user\n", prog); 1824 } else if (strcmp(prog, "groupadd") == 0) { 1825 fprintf(stderr, "usage: %s [-ov] [-g gid] group\n", 1826 prog); 1827 } else if (strcmp(prog, "groupdel") == 0) { 1828 fprintf(stderr, "usage: %s [-v] group\n", prog); 1829 } else if (strcmp(prog, "groupmod") == 0) { 1830 fprintf(stderr, "usage: %s [-ov] [-g gid] [-n newname] " 1831 "group\n", prog); 1832 } else if (strcmp(prog, "user") == 0 || strcmp(prog, "group") == 0) { 1833 fprintf(stderr, "usage: %s [add | del | mod" 1834 " | info" 1835 "] ...\n", 1836 prog); 1837 } else if (strcmp(prog, "groupinfo") == 0) { 1838 fprintf(stderr, "usage: %s [-e] group\n", prog); 1839 } else { 1840 fprintf(stderr, "This program must be called as {user,group}{add,del,mod,info},\n%s is not an understood name.\n", prog); 1841 } 1842 exit(EXIT_FAILURE); 1843} 1844 1845int 1846useradd(int argc, char **argv) 1847{ 1848 user_t u; 1849 const char *errstr; 1850 int defaultfield; 1851 int bigD; 1852 int c; 1853 int i; 1854 1855 memset(&u, 0, sizeof(u)); 1856 read_defaults(&u); 1857 u.u_uid = -1; 1858 defaultfield = bigD = 0; 1859 while ((c = getopt(argc, argv, "DG:L:b:c:d:e:f:g:k:mop:r:s:u:v")) != -1) { 1860 switch(c) { 1861 case 'D': 1862 bigD = 1; 1863 break; 1864 case 'G': 1865 while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && 1866 u.u_groupc < NGROUPS_MAX - 2) { 1867 if (u.u_groupv[u.u_groupc][0] != 0) { 1868 u.u_groupc++; 1869 } 1870 } 1871 if (optarg != NULL) { 1872 warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX - 2); 1873 } 1874 break; 1875 case 'b': 1876 defaultfield = 1; 1877 strsave(&u.u_basedir, optarg); 1878 break; 1879 case 'c': 1880 strsave(&u.u_comment, optarg); 1881 break; 1882 case 'd': 1883 strsave(&u.u_home, optarg); 1884 u.u_flags |= F_HOMEDIR; 1885 break; 1886 case 'e': 1887 defaultfield = 1; 1888 strsave(&u.u_expire, optarg); 1889 break; 1890 case 'f': 1891 defaultfield = 1; 1892 strsave(&u.u_inactive, optarg); 1893 break; 1894 case 'g': 1895 defaultfield = 1; 1896 strsave(&u.u_primgrp, optarg); 1897 break; 1898 case 'k': 1899 defaultfield = 1; 1900 strsave(&u.u_skeldir, optarg); 1901 break; 1902 case 'L': 1903 defaultfield = 1; 1904 strsave(&u.u_class, optarg); 1905 break; 1906 case 'm': 1907 u.u_flags |= F_MKDIR; 1908 break; 1909 case 'o': 1910 u.u_flags |= F_DUPUID; 1911 break; 1912 case 'p': 1913 strsave(&u.u_password, optarg); 1914 explicit_bzero(optarg, strlen(optarg)); 1915 break; 1916 case 'r': 1917 defaultfield = 1; 1918 if (!save_range(&u, optarg)) 1919 exit(EXIT_FAILURE); 1920 break; 1921 case 's': 1922 defaultfield = 1; 1923 strsave(&u.u_shell, optarg); 1924 break; 1925 case 'u': 1926 u.u_uid = strtonum(optarg, -1, UID_MAX, &errstr); 1927 if (errstr != NULL) { 1928 errx(EXIT_FAILURE, "When using [-u uid], the uid must be numeric"); 1929 } 1930 break; 1931 case 'v': 1932 verbose = 1; 1933 break; 1934 default: 1935 usermgmt_usage("useradd"); 1936 } 1937 } 1938 1939 if (pledge("stdio rpath wpath cpath fattr flock proc exec getpw id", 1940 NULL) == -1) 1941 err(1, "pledge"); 1942 1943 if (bigD) { 1944 if (defaultfield) { 1945 checkeuid(); 1946 return setdefaults(&u) ? EXIT_SUCCESS : EXIT_FAILURE; 1947 } 1948 printf("group\t\t%s\n", u.u_primgrp); 1949 printf("base_dir\t%s\n", u.u_basedir); 1950 printf("skel_dir\t%s\n", u.u_skeldir); 1951 printf("shell\t\t%s\n", u.u_shell); 1952 printf("class\t\t%s\n", u.u_class); 1953 printf("inactive\t%s\n", (u.u_inactive == NULL) ? UNSET_INACTIVE : u.u_inactive); 1954 printf("expire\t\t%s\n", (u.u_expire == NULL) ? UNSET_EXPIRY : u.u_expire); 1955 for (i = 0 ; i < u.u_rc ; i++) { 1956 printf("range\t\t%u..%u\n", u.u_rv[i].r_from, u.u_rv[i].r_to); 1957 } 1958 return EXIT_SUCCESS; 1959 } 1960 argc -= optind; 1961 argv += optind; 1962 if (argc != 1) { 1963 usermgmt_usage("useradd"); 1964 } 1965 checkeuid(); 1966 openlog("useradd", LOG_PID, LOG_USER); 1967 return adduser(*argv, &u) ? EXIT_SUCCESS : EXIT_FAILURE; 1968} 1969 1970int 1971usermod(int argc, char **argv) 1972{ 1973 user_t u; 1974 char newuser[MaxUserNameLen + 1]; 1975 int c, have_new_user; 1976 const char *errstr; 1977 1978 memset(&u, 0, sizeof(u)); 1979 memset(newuser, 0, sizeof(newuser)); 1980 read_defaults(&u); 1981 free(u.u_primgrp); 1982 u.u_primgrp = NULL; 1983 have_new_user = 0; 1984 while ((c = getopt(argc, argv, "G:L:S:UZc:d:e:f:g:l:mop:s:u:v")) != -1) { 1985 switch(c) { 1986 case 'G': 1987 while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && 1988 u.u_groupc < NGROUPS_MAX - 2) { 1989 if (u.u_groupv[u.u_groupc][0] != 0) { 1990 u.u_groupc++; 1991 } 1992 } 1993 if (optarg != NULL) { 1994 warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX - 2); 1995 } 1996 u.u_flags |= F_SECGROUP; 1997 break; 1998 case 'S': 1999 while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && 2000 u.u_groupc < NGROUPS_MAX - 2) { 2001 if (u.u_groupv[u.u_groupc][0] != 0) { 2002 u.u_groupc++; 2003 } 2004 } 2005 if (optarg != NULL) { 2006 warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX - 2); 2007 } 2008 u.u_flags |= F_SETSECGROUP; 2009 break; 2010 case 'U': 2011 u.u_flags |= F_ACCTUNLOCK; 2012 break; 2013 case 'Z': 2014 u.u_flags |= F_ACCTLOCK; 2015 break; 2016 case 'c': 2017 strsave(&u.u_comment, optarg); 2018 u.u_flags |= F_COMMENT; 2019 break; 2020 case 'd': 2021 strsave(&u.u_home, optarg); 2022 u.u_flags |= F_HOMEDIR; 2023 break; 2024 case 'e': 2025 strsave(&u.u_expire, optarg); 2026 u.u_flags |= F_EXPIRE; 2027 break; 2028 case 'f': 2029 strsave(&u.u_inactive, optarg); 2030 u.u_flags |= F_INACTIVE; 2031 break; 2032 case 'g': 2033 strsave(&u.u_primgrp, optarg); 2034 u.u_flags |= F_GROUP; 2035 break; 2036 case 'l': 2037 if (strlcpy(newuser, optarg, sizeof(newuser)) >= 2038 sizeof(newuser)) 2039 errx(EXIT_FAILURE, "username `%s' too long", 2040 optarg); 2041 have_new_user = 1; 2042 u.u_flags |= F_USERNAME; 2043 break; 2044 case 'L': 2045 strsave(&u.u_class, optarg); 2046 u.u_flags |= F_CLASS; 2047 break; 2048 case 'm': 2049 u.u_flags |= F_MKDIR; 2050 break; 2051 case 'o': 2052 u.u_flags |= F_DUPUID; 2053 break; 2054 case 'p': 2055 strsave(&u.u_password, optarg); 2056 explicit_bzero(optarg, strlen(optarg)); 2057 u.u_flags |= F_PASSWORD; 2058 break; 2059 case 's': 2060 strsave(&u.u_shell, optarg); 2061 u.u_flags |= F_SHELL; 2062 break; 2063 case 'u': 2064 u.u_uid = strtonum(optarg, -1, UID_MAX, &errstr); 2065 u.u_flags |= F_UID; 2066 if (errstr != NULL) { 2067 errx(EXIT_FAILURE, "When using [-u uid], the uid must be numeric"); 2068 } 2069 break; 2070 case 'v': 2071 verbose = 1; 2072 break; 2073 default: 2074 usermgmt_usage("usermod"); 2075 } 2076 } 2077 2078 if ((u.u_flags & F_MKDIR) && !(u.u_flags & F_HOMEDIR) && 2079 !(u.u_flags & F_USERNAME)) { 2080 warnx("option 'm' useless without 'd' or 'l' -- ignored"); 2081 u.u_flags &= ~F_MKDIR; 2082 } 2083 if ((u.u_flags & F_SECGROUP) && (u.u_flags & F_SETSECGROUP)) 2084 errx(EXIT_FAILURE, "options 'G' and 'S' are mutually exclusive"); 2085 if ((u.u_flags & F_ACCTLOCK) && (u.u_flags & F_ACCTUNLOCK)) 2086 errx(EXIT_FAILURE, "options 'U' and 'Z' are mutually exclusive"); 2087 if ((u.u_flags & F_PASSWORD) && (u.u_flags & (F_ACCTLOCK | F_ACCTUNLOCK))) 2088 errx(EXIT_FAILURE, "options 'U' or 'Z' with 'p' are mutually exclusive"); 2089 argc -= optind; 2090 argv += optind; 2091 if (argc != 1) { 2092 usermgmt_usage("usermod"); 2093 } 2094 checkeuid(); 2095 openlog("usermod", LOG_PID, LOG_USER); 2096 return moduser(*argv, (have_new_user) ? newuser : *argv, &u) ? 2097 EXIT_SUCCESS : EXIT_FAILURE; 2098} 2099 2100int 2101userdel(int argc, char **argv) 2102{ 2103 struct passwd *pwp; 2104 user_t u; 2105 int defaultfield; 2106 int rmhome; 2107 int bigD; 2108 int c; 2109 2110 memset(&u, 0, sizeof(u)); 2111 read_defaults(&u); 2112 defaultfield = bigD = rmhome = 0; 2113 while ((c = getopt(argc, argv, "Dp:rv")) != -1) { 2114 switch(c) { 2115 case 'D': 2116 bigD = 1; 2117 break; 2118 case 'p': 2119 defaultfield = 1; 2120 u.u_preserve = (strcmp(optarg, "true") == 0) ? 1 : 2121 (strcmp(optarg, "yes") == 0) ? 1 : 2122 strtonum(optarg, INT_MIN, INT_MAX, NULL); 2123 break; 2124 case 'r': 2125 rmhome = 1; 2126 break; 2127 case 'v': 2128 verbose = 1; 2129 break; 2130 default: 2131 usermgmt_usage("userdel"); 2132 } 2133 } 2134 if (bigD) { 2135 if (defaultfield) { 2136 checkeuid(); 2137 return setdefaults(&u) ? EXIT_SUCCESS : EXIT_FAILURE; 2138 } 2139 printf("preserve\t%s\n", (u.u_preserve) ? "true" : "false"); 2140 return EXIT_SUCCESS; 2141 } 2142 argc -= optind; 2143 argv += optind; 2144 if (argc != 1) { 2145 usermgmt_usage("userdel"); 2146 } 2147 2148 if (pledge("stdio rpath wpath cpath fattr flock proc exec getpw id", 2149 NULL) == -1) 2150 err(1, "pledge"); 2151 2152 checkeuid(); 2153 if ((pwp = getpwnam(*argv)) == NULL) { 2154 warnx("No such user `%s'", *argv); 2155 return EXIT_FAILURE; 2156 } 2157 if (rmhome) 2158 (void)removehomedir(pwp->pw_name, pwp->pw_uid, pwp->pw_dir); 2159 if (u.u_preserve) { 2160 u.u_flags |= F_SHELL; 2161 strsave(&u.u_shell, NOLOGIN); 2162 strsave(&u.u_password, "*"); 2163 u.u_flags |= F_PASSWORD; 2164 openlog("userdel", LOG_PID, LOG_USER); 2165 return moduser(*argv, *argv, &u) ? EXIT_SUCCESS : EXIT_FAILURE; 2166 } 2167 if (!rm_user_from_groups(*argv)) { 2168 return 0; 2169 } 2170 openlog("userdel", LOG_PID, LOG_USER); 2171 return moduser(*argv, *argv, NULL) ? EXIT_SUCCESS : EXIT_FAILURE; 2172} 2173 2174/* add a group */ 2175int 2176groupadd(int argc, char **argv) 2177{ 2178 int dupgid; 2179 int gid; 2180 int c; 2181 const char *errstr; 2182 2183 gid = -1; 2184 dupgid = 0; 2185 while ((c = getopt(argc, argv, "g:ov")) != -1) { 2186 switch(c) { 2187 case 'g': 2188 gid = strtonum(optarg, -1, GID_MAX, &errstr); 2189 if (errstr != NULL) { 2190 errx(EXIT_FAILURE, "When using [-g gid], the gid must be numeric"); 2191 } 2192 break; 2193 case 'o': 2194 dupgid = 1; 2195 break; 2196 case 'v': 2197 verbose = 1; 2198 break; 2199 default: 2200 usermgmt_usage("groupadd"); 2201 } 2202 } 2203 argc -= optind; 2204 argv += optind; 2205 if (argc != 1) { 2206 usermgmt_usage("groupadd"); 2207 } 2208 2209 if (pledge("stdio rpath wpath cpath fattr flock getpw", NULL) == -1) 2210 err(1, "pledge"); 2211 2212 checkeuid(); 2213 if (!valid_group(*argv)) { 2214 errx(EXIT_FAILURE, "invalid group name `%s'", *argv); 2215 } 2216 if (gid < 0 && !getnextgid(&gid, LowGid, HighGid)) { 2217 errx(EXIT_FAILURE, "can't add group: can't get next gid"); 2218 } 2219 if (!dupgid && gid_exists((gid_t)gid)) { 2220 errx(EXIT_FAILURE, "can't add group: gid %d is a duplicate", gid); 2221 } 2222 openlog("groupadd", LOG_PID, LOG_USER); 2223 if (!creategid(*argv, gid, "")) { 2224 errx(EXIT_FAILURE, "can't add group: problems with %s file", 2225 _PATH_GROUP); 2226 } 2227 return EXIT_SUCCESS; 2228} 2229 2230/* remove a group */ 2231int 2232groupdel(int argc, char **argv) 2233{ 2234 int c; 2235 2236 while ((c = getopt(argc, argv, "v")) != -1) { 2237 switch(c) { 2238 case 'v': 2239 verbose = 1; 2240 break; 2241 default: 2242 usermgmt_usage("groupdel"); 2243 } 2244 } 2245 argc -= optind; 2246 argv += optind; 2247 if (argc != 1) { 2248 usermgmt_usage("groupdel"); 2249 } 2250 checkeuid(); 2251 openlog("groupdel", LOG_PID, LOG_USER); 2252 if (!group_exists(*argv)) { 2253 warnx("No such group: `%s'", *argv); 2254 return EXIT_FAILURE; 2255 } 2256 2257 if (pledge("stdio rpath wpath cpath fattr flock", NULL) == -1) 2258 err(1, "pledge"); 2259 2260 if (!modify_gid(*argv, NULL)) { 2261 err(EXIT_FAILURE, "can't change %s file", _PATH_GROUP); 2262 } 2263 return EXIT_SUCCESS; 2264} 2265 2266/* modify a group */ 2267int 2268groupmod(int argc, char **argv) 2269{ 2270 struct group *grp; 2271 const char *errstr; 2272 char buf[LINE_MAX]; 2273 char *newname; 2274 char **cpp; 2275 int dupgid; 2276 int gid; 2277 int cc; 2278 int c; 2279 2280 gid = -1; 2281 dupgid = 0; 2282 newname = NULL; 2283 while ((c = getopt(argc, argv, "g:n:ov")) != -1) { 2284 switch(c) { 2285 case 'g': 2286 gid = strtonum(optarg, -1, GID_MAX, &errstr); 2287 if (errstr != NULL) { 2288 errx(EXIT_FAILURE, "When using [-g gid], the gid must be numeric"); 2289 } 2290 break; 2291 case 'o': 2292 dupgid = 1; 2293 break; 2294 case 'n': 2295 strsave(&newname, optarg); 2296 break; 2297 case 'v': 2298 verbose = 1; 2299 break; 2300 default: 2301 usermgmt_usage("groupmod"); 2302 } 2303 } 2304 argc -= optind; 2305 argv += optind; 2306 if (argc != 1) { 2307 usermgmt_usage("groupmod"); 2308 } 2309 checkeuid(); 2310 if (gid < 0 && newname == NULL) { 2311 errx(EXIT_FAILURE, "Nothing to change"); 2312 } 2313 if (dupgid && gid < 0) { 2314 errx(EXIT_FAILURE, "Duplicate which gid?"); 2315 } 2316 if ((grp = getgrnam(*argv)) == NULL) { 2317 errx(EXIT_FAILURE, "can't find group `%s' to modify", *argv); 2318 } 2319 2320 if (pledge("stdio rpath wpath cpath fattr flock", NULL) == -1) 2321 err(1, "pledge"); 2322 2323 if (!is_local(*argv, _PATH_GROUP)) { 2324 errx(EXIT_FAILURE, "Group `%s' must be a local group", *argv); 2325 } 2326 if (newname != NULL && !valid_group(newname)) { 2327 errx(EXIT_FAILURE, "invalid group name `%s'", newname); 2328 } 2329 if ((cc = snprintf(buf, sizeof(buf), "%s:%s:%u:", 2330 (newname) ? newname : grp->gr_name, grp->gr_passwd, 2331 (gid < 0) ? grp->gr_gid : gid)) >= sizeof(buf) || cc < 0) 2332 errx(EXIT_FAILURE, "group `%s' entry too long", grp->gr_name); 2333 2334 for (cpp = grp->gr_mem ; *cpp ; cpp++) { 2335 cc = strlcat(buf, *cpp, sizeof(buf)) + 1; 2336 if (cc >= sizeof(buf)) 2337 errx(EXIT_FAILURE, "group `%s' entry too long", 2338 grp->gr_name); 2339 if (cpp[1] != NULL) { 2340 buf[cc - 1] = ','; 2341 buf[cc] = '\0'; 2342 } 2343 } 2344 cc = strlcat(buf, "\n", sizeof(buf)); 2345 if (cc >= sizeof(buf)) 2346 errx(EXIT_FAILURE, "group `%s' entry too long", grp->gr_name); 2347 2348 openlog("groupmod", LOG_PID, LOG_USER); 2349 if (!modify_gid(*argv, buf)) 2350 err(EXIT_FAILURE, "can't change %s file", _PATH_GROUP); 2351 return EXIT_SUCCESS; 2352} 2353 2354/* display user information */ 2355int 2356userinfo(int argc, char **argv) 2357{ 2358 struct passwd *pwp; 2359 struct group *grp; 2360 char **cpp; 2361 int exists; 2362 int i; 2363 2364 exists = 0; 2365 while ((i = getopt(argc, argv, "ev")) != -1) { 2366 switch(i) { 2367 case 'e': 2368 exists = 1; 2369 break; 2370 case 'v': 2371 verbose = 1; 2372 break; 2373 default: 2374 usermgmt_usage("userinfo"); 2375 } 2376 } 2377 argc -= optind; 2378 argv += optind; 2379 if (argc != 1) { 2380 usermgmt_usage("userinfo"); 2381 } 2382 2383 if (pledge("stdio getpw", NULL) == -1) 2384 err(1, "pledge"); 2385 2386 pwp = find_user_info(*argv); 2387 if (exists) { 2388 exit((pwp) ? EXIT_SUCCESS : EXIT_FAILURE); 2389 } 2390 if (pwp == NULL) { 2391 errx(EXIT_FAILURE, "can't find user `%s'", *argv); 2392 } 2393 printf("login\t%s\n", pwp->pw_name); 2394 printf("passwd\t%s\n", pwp->pw_passwd); 2395 printf("uid\t%u\n", pwp->pw_uid); 2396 if ((grp = getgrgid(pwp->pw_gid)) == NULL) 2397 printf("groups\t%u", pwp->pw_gid); 2398 else 2399 printf("groups\t%s", grp->gr_name); 2400 while ((grp = getgrent()) != NULL) { 2401 for (cpp = grp->gr_mem ; *cpp ; cpp++) { 2402 if (strcmp(*cpp, pwp->pw_name) == 0 && 2403 grp->gr_gid != pwp->pw_gid) 2404 printf(" %s", grp->gr_name); 2405 } 2406 } 2407 fputc('\n', stdout); 2408 printf("change\t%s", pwp->pw_change ? ctime(&pwp->pw_change) : "NEVER\n"); 2409 printf("class\t%s\n", pwp->pw_class); 2410 printf("gecos\t%s\n", pwp->pw_gecos); 2411 printf("dir\t%s\n", pwp->pw_dir); 2412 printf("shell\t%s\n", pwp->pw_shell); 2413 printf("expire\t%s", pwp->pw_expire ? ctime(&pwp->pw_expire) : "NEVER\n"); 2414 return EXIT_SUCCESS; 2415} 2416 2417/* display user information */ 2418int 2419groupinfo(int argc, char **argv) 2420{ 2421 struct group *grp; 2422 char **cpp; 2423 int exists; 2424 int i; 2425 2426 exists = 0; 2427 while ((i = getopt(argc, argv, "ev")) != -1) { 2428 switch(i) { 2429 case 'e': 2430 exists = 1; 2431 break; 2432 case 'v': 2433 verbose = 1; 2434 break; 2435 default: 2436 usermgmt_usage("groupinfo"); 2437 } 2438 } 2439 argc -= optind; 2440 argv += optind; 2441 if (argc != 1) { 2442 usermgmt_usage("groupinfo"); 2443 } 2444 2445 if (pledge("stdio getpw", NULL) == -1) 2446 err(1, "pledge"); 2447 2448 grp = find_group_info(*argv); 2449 if (exists) { 2450 exit((grp) ? EXIT_SUCCESS : EXIT_FAILURE); 2451 } 2452 if (grp == NULL) { 2453 errx(EXIT_FAILURE, "can't find group `%s'", *argv); 2454 } 2455 printf("name\t%s\n", grp->gr_name); 2456 printf("passwd\t%s\n", grp->gr_passwd); 2457 printf("gid\t%u\n", grp->gr_gid); 2458 printf("members\t"); 2459 for (cpp = grp->gr_mem ; *cpp ; cpp++) { 2460 printf("%s ", *cpp); 2461 } 2462 fputc('\n', stdout); 2463 return EXIT_SUCCESS; 2464} 2465