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