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