1 /* $OpenBSD: user.c,v 1.100 2014/08/27 06:51:35 sebastia 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 NBLF "$2b" 880 #define BLF "$2a" 881 #define MD5 "$1" 882 #define DES "" 883 884 static passwd_type_t passwd_types[] = { 885 { NBLF, 3, 54 }, /* Blowfish bcrypt version 2b */ 886 { BLF, 3, 54 }, /* Blowfish */ 887 { MD5, 2, 34 }, /* MD5 */ 888 { DES, 0, DES_Len }, /* standard DES */ 889 { NULL, -1, -1 } /* none - terminate search */ 890 }; 891 892 /* return non-zero if it's a valid password - check length for cipher type */ 893 static int 894 valid_password_length(char *newpasswd) 895 { 896 passwd_type_t *pwtp; 897 898 for (pwtp = passwd_types ; pwtp->desc_length >= 0 ; pwtp++) { 899 if (strncmp(newpasswd, pwtp->type, pwtp->desc_length) == 0) { 900 char *p; 901 902 if (strcmp(pwtp->type, BLF) != 0 && 903 strcmp(pwtp->type, NBLF) != 0) { 904 return strlen(newpasswd) == pwtp->length; 905 } 906 /* Skip first three `$'. */ 907 if ((p = strchr(newpasswd, '$')) == NULL || 908 *(++p) == '$' || (p = strchr(p, '$')) == NULL || 909 *(++p) == '$' || (p = strchr(p, '$')) == NULL) 910 continue; 911 return (strlen(p) - 1); 912 } 913 } 914 return 0; 915 } 916 917 /* look for a valid time, return 0 if it was specified but bad */ 918 static int 919 scantime(time_t *tp, char *s) 920 { 921 struct tm tm; 922 923 *tp = 0; 924 if (s != NULL) { 925 (void) memset(&tm, 0, sizeof(tm)); 926 tm.tm_isdst = -1; 927 if (strptime(s, "%c", &tm) != NULL) { 928 *tp = mktime(&tm); 929 } else if (strptime(s, "%B %d %Y", &tm) != NULL) { 930 *tp = mktime(&tm); 931 } else if (isdigit((unsigned char) s[0]) != 0) { 932 *tp = (time_t)atoll(s); 933 } else { 934 return 0; 935 } 936 } 937 return 1; 938 } 939 940 /* compute the extra length '&' expansion consumes */ 941 static size_t 942 expand_len(const char *p, const char *username) 943 { 944 size_t alen; 945 size_t ulen; 946 947 ulen = strlen(username); 948 for (alen = 0; *p != '\0'; p++) 949 if (*p == '&') 950 alen += ulen - 1; 951 return alen; 952 } 953 954 /* add a user */ 955 static int 956 adduser(char *login_name, user_t *up) 957 { 958 struct group *grp; 959 struct stat st; 960 time_t expire; 961 time_t inactive; 962 char password[PasswordLength + 1]; 963 char home[MaxFileNameLen]; 964 char buf[LINE_MAX]; 965 int sync_uid_gid; 966 int masterfd; 967 int ptmpfd; 968 gid_t gid; 969 int cc; 970 int i, yp = 0; 971 FILE *fp; 972 973 if (!valid_login(login_name)) { 974 errx(EXIT_FAILURE, "`%s' is not a valid login name", login_name); 975 } 976 if (!valid_class(up->u_class)) { 977 errx(EXIT_FAILURE, "No such login class `%s'", up->u_class); 978 } 979 if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) { 980 err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD); 981 } 982 if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) { 983 err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD); 984 } 985 pw_init(); 986 if ((ptmpfd = pw_lock(WAITSECS)) < 0) { 987 int saved_errno = errno; 988 (void) close(masterfd); 989 errc(EXIT_FAILURE, saved_errno, "can't obtain pw_lock"); 990 } 991 if ((fp = fdopen(masterfd, "r")) == NULL) { 992 int saved_errno = errno; 993 (void) close(masterfd); 994 (void) close(ptmpfd); 995 pw_abort(); 996 errc(EXIT_FAILURE, saved_errno, 997 "can't fdopen `%s' for reading", _PATH_MASTERPASSWD); 998 } 999 while (fgets(buf, sizeof(buf), fp) != NULL) { 1000 cc = strlen(buf); 1001 /* 1002 * Stop copying the file at the yp entry; we want to 1003 * put the new user before it, and preserve entries 1004 * after the yp entry. 1005 */ 1006 if (cc > 1 && buf[0] == '+' && buf[1] == ':') { 1007 yp = 1; 1008 break; 1009 } 1010 if (write(ptmpfd, buf, (size_t)(cc)) != cc) { 1011 int saved_errno = errno; 1012 (void) fclose(fp); 1013 (void) close(ptmpfd); 1014 pw_abort(); 1015 errc(EXIT_FAILURE, saved_errno, 1016 "short write to /etc/ptmp (not %d chars)", cc); 1017 } 1018 } 1019 if (ferror(fp)) { 1020 int saved_errno = errno; 1021 (void) fclose(fp); 1022 (void) close(ptmpfd); 1023 pw_abort(); 1024 errc(EXIT_FAILURE, saved_errno, "read error on %s", 1025 _PATH_MASTERPASSWD); 1026 } 1027 /* if no uid was specified, get next one in [low_uid..high_uid] range */ 1028 sync_uid_gid = (strcmp(up->u_primgrp, "=uid") == 0); 1029 if (up->u_uid == UID_MAX) { 1030 int got_id = 0; 1031 1032 /* 1033 * Look for a free UID in the command line ranges (if any). 1034 * These start after the ranges specified in the config file. 1035 */ 1036 for (i = up->u_defrc; got_id == 0 && i < up->u_rc ; i++) { 1037 got_id = getnextuid(sync_uid_gid, &up->u_uid, 1038 up->u_rv[i].r_from, up->u_rv[i].r_to); 1039 } 1040 /* 1041 * If there were no free UIDs in the command line ranges, 1042 * try the ranges from the config file (there will always 1043 * be at least one default). 1044 */ 1045 if (got_id == 0) { 1046 for (i = 0; got_id == 0 && i < up->u_defrc; i++) { 1047 got_id = getnextuid(sync_uid_gid, &up->u_uid, 1048 up->u_rv[i].r_from, up->u_rv[i].r_to); 1049 } 1050 } 1051 if (got_id == 0) { 1052 (void) close(ptmpfd); 1053 pw_abort(); 1054 errx(EXIT_FAILURE, "can't get next uid for %u", up->u_uid); 1055 } 1056 } 1057 /* check uid isn't already allocated */ 1058 if (!(up->u_flags & F_DUPUID) && getpwuid((uid_t)(up->u_uid)) != NULL) { 1059 (void) close(ptmpfd); 1060 pw_abort(); 1061 errx(EXIT_FAILURE, "uid %u is already in use", up->u_uid); 1062 } 1063 /* if -g=uid was specified, check gid is unused */ 1064 if (sync_uid_gid) { 1065 if (getgrgid((gid_t)(up->u_uid)) != NULL) { 1066 (void) close(ptmpfd); 1067 pw_abort(); 1068 errx(EXIT_FAILURE, "gid %u is already in use", up->u_uid); 1069 } 1070 gid = up->u_uid; 1071 } else if ((grp = getgrnam(up->u_primgrp)) != NULL) { 1072 gid = grp->gr_gid; 1073 } else if (is_number(up->u_primgrp) && 1074 (grp = getgrgid((gid_t)atoi(up->u_primgrp))) != NULL) { 1075 gid = grp->gr_gid; 1076 } else { 1077 (void) close(ptmpfd); 1078 pw_abort(); 1079 errx(EXIT_FAILURE, "group %s not found", up->u_primgrp); 1080 } 1081 /* check name isn't already in use */ 1082 if (!(up->u_flags & F_DUPUID) && getpwnam(login_name) != NULL) { 1083 (void) close(ptmpfd); 1084 pw_abort(); 1085 errx(EXIT_FAILURE, "already a `%s' user", login_name); 1086 } 1087 if (up->u_flags & F_HOMEDIR) { 1088 if (strlcpy(home, up->u_home, sizeof(home)) >= sizeof(home)) { 1089 (void) close(ptmpfd); 1090 pw_abort(); 1091 errx(EXIT_FAILURE, "home directory `%s' too long", 1092 up->u_home); 1093 } 1094 } else { 1095 /* if home directory hasn't been given, make it up */ 1096 if (snprintf(home, sizeof(home), "%s/%s", up->u_basedir, 1097 login_name) >= sizeof(home)) { 1098 (void) close(ptmpfd); 1099 pw_abort(); 1100 errx(EXIT_FAILURE, "home directory `%s/%s' too long", 1101 up->u_basedir, login_name); 1102 } 1103 } 1104 if (!scantime(&inactive, up->u_inactive)) { 1105 warnx("Warning: inactive time `%s' invalid, password expiry off", 1106 up->u_inactive); 1107 } 1108 if (!scantime(&expire, up->u_expire)) { 1109 warnx("Warning: expire time `%s' invalid, account expiry off", 1110 up->u_expire); 1111 } 1112 if (lstat(home, &st) < 0 && !(up->u_flags & F_MKDIR) && 1113 strcmp(home, _PATH_NONEXISTENT) != 0) { 1114 warnx("Warning: home directory `%s' doesn't exist, and -m was" 1115 " not specified", home); 1116 } 1117 if (up->u_password != NULL && valid_password_length(up->u_password)) { 1118 (void) strlcpy(password, up->u_password, sizeof(password)); 1119 } else { 1120 (void) memset(password, '*', DES_Len); 1121 password[DES_Len] = 0; 1122 if (up->u_password != NULL) { 1123 warnx("Password `%s' is invalid: setting it to `%s'", 1124 up->u_password, password); 1125 } 1126 } 1127 cc = snprintf(buf, sizeof(buf), "%s:%s:%u:%u:%s:%ld:%ld:%s:%s:%s\n", 1128 login_name, 1129 password, 1130 up->u_uid, 1131 gid, 1132 up->u_class, 1133 (long) inactive, 1134 (long) expire, 1135 up->u_comment, 1136 home, 1137 up->u_shell); 1138 if (cc >= sizeof(buf) || cc < 0 || 1139 cc + expand_len(up->u_comment, login_name) >= 1023) { 1140 (void) close(ptmpfd); 1141 pw_abort(); 1142 errx(EXIT_FAILURE, "can't add `%s', line too long", buf); 1143 } 1144 if (write(ptmpfd, buf, (size_t) cc) != cc) { 1145 int saved_errno = errno; 1146 (void) close(ptmpfd); 1147 pw_abort(); 1148 errc(EXIT_FAILURE, saved_errno, "can't add `%s'", buf); 1149 } 1150 if (yp) { 1151 /* put back the + line */ 1152 cc = snprintf(buf, sizeof(buf), "+:*::::::::\n"); 1153 if (cc == -1 || cc >= sizeof(buf)) { 1154 (void) close(ptmpfd); 1155 pw_abort(); 1156 errx(EXIT_FAILURE, "can't add `%s', line too long", buf); 1157 } 1158 if (write(ptmpfd, buf, (size_t) cc) != cc) { 1159 int saved_errno = errno; 1160 (void) close(ptmpfd); 1161 pw_abort(); 1162 errc(EXIT_FAILURE, saved_errno, "can't add `%s'", buf); 1163 } 1164 /* copy the entries following it, if any */ 1165 while (fgets(buf, sizeof(buf), fp) != NULL) { 1166 cc = strlen(buf); 1167 if (write(ptmpfd, buf, (size_t)(cc)) != cc) { 1168 int saved_errno = errno; 1169 (void) fclose(fp); 1170 (void) close(ptmpfd); 1171 pw_abort(); 1172 errc(EXIT_FAILURE, saved_errno, 1173 "short write to /etc/ptmp (not %d chars)", 1174 cc); 1175 } 1176 } 1177 if (ferror(fp)) { 1178 int saved_errno = errno; 1179 (void) fclose(fp); 1180 (void) close(ptmpfd); 1181 pw_abort(); 1182 errc(EXIT_FAILURE, saved_errno, "read error on %s", 1183 _PATH_MASTERPASSWD); 1184 } 1185 } 1186 if (up->u_flags & F_MKDIR) { 1187 if (lstat(home, &st) == 0) { 1188 (void) close(ptmpfd); 1189 pw_abort(); 1190 errx(EXIT_FAILURE, "home directory `%s' already exists", 1191 home); 1192 } else { 1193 if (asystem("%s -p %s", MKDIR, home) != 0) { 1194 int saved_errno = errno; 1195 (void) close(ptmpfd); 1196 pw_abort(); 1197 errc(EXIT_FAILURE, saved_errno, 1198 "can't mkdir `%s'", home); 1199 } 1200 (void) copydotfiles(up->u_skeldir, up->u_uid, gid, home); 1201 (void) asystem("%s -R -P %u:%u %s", CHOWN, up->u_uid, 1202 gid, home); 1203 (void) asystem("%s -R u+w %s", CHMOD, home); 1204 } 1205 } 1206 if (strcmp(up->u_primgrp, "=uid") == 0 && 1207 getgrnam(login_name) == NULL && 1208 !creategid(login_name, gid, "")) { 1209 (void) close(ptmpfd); 1210 pw_abort(); 1211 errx(EXIT_FAILURE, "can't create gid %u for login name %s", 1212 gid, login_name); 1213 } 1214 if (up->u_groupc > 0 && !append_group(login_name, up->u_groupc, up->u_groupv)) { 1215 (void) close(ptmpfd); 1216 pw_abort(); 1217 errx(EXIT_FAILURE, "can't append `%s' to new groups", login_name); 1218 } 1219 (void) close(ptmpfd); 1220 if (pw_mkdb(yp ? NULL : login_name, 0) < 0) { 1221 pw_abort(); 1222 err(EXIT_FAILURE, "pw_mkdb failed"); 1223 } 1224 syslog(LOG_INFO, "new user added: name=%s, uid=%u, gid=%u, home=%s, shell=%s", 1225 login_name, up->u_uid, gid, home, up->u_shell); 1226 return 1; 1227 } 1228 1229 /* remove a user from the groups file */ 1230 static int 1231 rm_user_from_groups(char *login_name) 1232 { 1233 struct stat st; 1234 size_t login_len; 1235 FILE *from; 1236 FILE *to; 1237 char buf[LINE_MAX]; 1238 char f[MaxFileNameLen]; 1239 char *cp, *ep; 1240 int fd; 1241 int cc; 1242 1243 login_len = strlen(login_name); 1244 if ((from = fopen(_PATH_GROUP, "r")) == NULL) { 1245 warn("can't remove gid for `%s': can't open `%s'", 1246 login_name, _PATH_GROUP); 1247 return 0; 1248 } 1249 if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { 1250 warn("can't lock `%s'", _PATH_GROUP); 1251 } 1252 (void) fstat(fileno(from), &st); 1253 (void) snprintf(f, sizeof(f), "%s.XXXXXXXX", _PATH_GROUP); 1254 if ((fd = mkstemp(f)) < 0) { 1255 warn("can't remove gid for `%s': mkstemp failed", login_name); 1256 (void) fclose(from); 1257 return 0; 1258 } 1259 if ((to = fdopen(fd, "w")) == NULL) { 1260 warn("can't remove gid for `%s': fdopen `%s' failed", 1261 login_name, f); 1262 (void) fclose(from); 1263 (void) close(fd); 1264 (void) unlink(f); 1265 return 0; 1266 } 1267 while (fgets(buf, sizeof(buf), from) != NULL) { 1268 cc = strlen(buf); 1269 if (cc > 0 && buf[cc - 1] != '\n' && !feof(from)) { 1270 while (fgetc(from) != '\n' && !feof(from)) 1271 cc++; 1272 warnx("%s: line `%s' too long (%d bytes), skipping", 1273 _PATH_GROUP, buf, cc); 1274 continue; 1275 } 1276 1277 /* Break out the group list. */ 1278 for (cp = buf, cc = 0; *cp != '\0' && cc < 3; cp++) { 1279 if (*cp == ':') 1280 cc++; 1281 } 1282 if (cc != 3) { 1283 buf[strcspn(buf, "\n")] = '\0'; 1284 warnx("Malformed entry `%s'. Skipping", buf); 1285 continue; 1286 } 1287 while ((cp = strstr(cp, login_name)) != NULL) { 1288 if ((cp[-1] == ':' || cp[-1] == ',') && 1289 (cp[login_len] == ',' || cp[login_len] == '\n')) { 1290 ep = cp + login_len; 1291 if (cp[login_len] == ',') 1292 ep++; 1293 else if (cp[-1] == ',') 1294 cp--; 1295 memmove(cp, ep, strlen(ep) + 1); 1296 } else { 1297 if ((cp = strchr(cp, ',')) == NULL) 1298 break; 1299 cp++; 1300 } 1301 } 1302 if (fwrite(buf, strlen(buf), 1, to) != 1) { 1303 warn("can't remove gid for `%s': short write to `%s'", 1304 login_name, f); 1305 (void) fclose(from); 1306 (void) fclose(to); 1307 (void) unlink(f); 1308 return 0; 1309 } 1310 } 1311 (void) fchmod(fileno(to), st.st_mode & 07777); 1312 (void) fclose(from); 1313 if (fclose(to) == EOF) { 1314 warn("can't remove gid for `%s': short write to `%s'", 1315 login_name, f); 1316 (void) unlink(f); 1317 return 0; 1318 } 1319 if (rename(f, _PATH_GROUP) < 0) { 1320 warn("can't remove gid for `%s': can't rename `%s' to `%s'", 1321 login_name, f, _PATH_GROUP); 1322 (void) unlink(f); 1323 return 0; 1324 } 1325 return 1; 1326 } 1327 1328 /* check that the user or group is local, not from YP/NIS */ 1329 static int 1330 is_local(char *name, const char *file) 1331 { 1332 FILE *fp; 1333 char buf[LINE_MAX]; 1334 size_t len; 1335 int ret; 1336 int cc; 1337 1338 if ((fp = fopen(file, "r")) == NULL) { 1339 err(EXIT_FAILURE, "can't open `%s'", file); 1340 } 1341 len = strlen(name); 1342 for (ret = 0 ; fgets(buf, sizeof(buf), fp) != NULL ; ) { 1343 cc = strlen(buf); 1344 if (cc > 0 && buf[cc - 1] != '\n' && !feof(fp)) { 1345 while (fgetc(fp) != '\n' && !feof(fp)) 1346 cc++; 1347 warnx("%s: line `%s' too long (%d bytes), skipping", 1348 file, buf, cc); 1349 continue; 1350 } 1351 if (strncmp(buf, name, len) == 0 && buf[len] == ':') { 1352 ret = 1; 1353 break; 1354 } 1355 } 1356 (void) fclose(fp); 1357 return ret; 1358 } 1359 1360 /* modify a user */ 1361 static int 1362 moduser(char *login_name, char *newlogin, user_t *up) 1363 { 1364 struct passwd *pwp; 1365 struct group *grp; 1366 const char *homedir; 1367 char buf[LINE_MAX]; 1368 char acctlock_str[] = "-"; 1369 char pwlock_str[] = "*"; 1370 char pw_len[PasswordLength + 1]; 1371 char shell_len[MaxShellNameLen]; 1372 char *shell_last_char; 1373 size_t colonc, loginc; 1374 size_t cc; 1375 size_t shell_buf; 1376 FILE *master; 1377 char newdir[MaxFileNameLen]; 1378 char *colon; 1379 char *pw_tmp = NULL; 1380 char *shell_tmp = NULL; 1381 int len; 1382 int locked = 0; 1383 int unlocked = 0; 1384 int masterfd; 1385 int ptmpfd; 1386 int rval; 1387 int i; 1388 1389 if (!valid_login(newlogin)) { 1390 errx(EXIT_FAILURE, "`%s' is not a valid login name", login_name); 1391 } 1392 if ((pwp = getpwnam(login_name)) == NULL) { 1393 errx(EXIT_FAILURE, "No such user `%s'", login_name); 1394 } 1395 if (!is_local(login_name, _PATH_MASTERPASSWD)) { 1396 errx(EXIT_FAILURE, "User `%s' must be a local user", login_name); 1397 } 1398 if (up != NULL) { 1399 if ((up->u_flags & (F_ACCTLOCK | F_ACCTUNLOCK)) && (pwp->pw_uid < 1000)) 1400 errx(EXIT_FAILURE, "(un)locking is not supported for the `%s' account", pwp->pw_name); 1401 } 1402 /* keep dir name in case we need it for '-m' */ 1403 homedir = pwp->pw_dir; 1404 1405 /* get the last char of the shell in case we need it for '-U' or '-Z' */ 1406 shell_last_char = pwp->pw_shell+strlen(pwp->pw_shell) - 1; 1407 1408 if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) { 1409 err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD); 1410 } 1411 if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) { 1412 err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD); 1413 } 1414 pw_init(); 1415 if ((ptmpfd = pw_lock(WAITSECS)) < 0) { 1416 int saved_errno = errno; 1417 (void) close(masterfd); 1418 errc(EXIT_FAILURE, saved_errno, "can't obtain pw_lock"); 1419 } 1420 if ((master = fdopen(masterfd, "r")) == NULL) { 1421 int saved_errno = errno; 1422 (void) close(masterfd); 1423 (void) close(ptmpfd); 1424 pw_abort(); 1425 errc(EXIT_FAILURE, saved_errno, "can't fdopen fd for %s", 1426 _PATH_MASTERPASSWD); 1427 } 1428 if (up != NULL) { 1429 if (up->u_flags & F_USERNAME) { 1430 /* if changing name, check new name isn't already in use */ 1431 if (strcmp(login_name, newlogin) != 0 && getpwnam(newlogin) != NULL) { 1432 (void) close(ptmpfd); 1433 pw_abort(); 1434 errx(EXIT_FAILURE, "already a `%s' user", newlogin); 1435 } 1436 pwp->pw_name = newlogin; 1437 1438 /* 1439 * Provide a new directory name in case the 1440 * home directory is to be moved. 1441 */ 1442 if (up->u_flags & F_MKDIR) { 1443 (void) snprintf(newdir, sizeof(newdir), 1444 "%s/%s", up->u_basedir, newlogin); 1445 pwp->pw_dir = newdir; 1446 } 1447 } 1448 if (up->u_flags & F_PASSWORD) { 1449 if (up->u_password != NULL) { 1450 if (!valid_password_length(up->u_password)) { 1451 (void) close(ptmpfd); 1452 pw_abort(); 1453 errx(EXIT_FAILURE, "Invalid password: `%s'", 1454 up->u_password); 1455 } 1456 pwp->pw_passwd = up->u_password; 1457 } 1458 } 1459 if (up->u_flags & F_ACCTLOCK) { 1460 /* lock the account */ 1461 if (*shell_last_char != *acctlock_str) { 1462 shell_tmp = malloc(strlen(pwp->pw_shell) + sizeof(acctlock_str)); 1463 if (shell_tmp == NULL) { 1464 (void) close(ptmpfd); 1465 pw_abort(); 1466 errx(EXIT_FAILURE, "account lock: cannot allocate memory"); 1467 } 1468 strlcpy(shell_tmp, pwp->pw_shell, sizeof(shell_len)); 1469 strlcat(shell_tmp, acctlock_str, sizeof(shell_len)); 1470 pwp->pw_shell = shell_tmp; 1471 } else { 1472 locked++; 1473 } 1474 /* lock the password */ 1475 if (strncmp(pwp->pw_passwd, pwlock_str, sizeof(pwlock_str)-1) != 0) { 1476 pw_tmp = malloc(strlen(pwp->pw_passwd) + sizeof(pwlock_str)); 1477 if (pw_tmp == NULL) { 1478 (void) close(ptmpfd); 1479 pw_abort(); 1480 errx(EXIT_FAILURE, "password lock: cannot allocate memory"); 1481 } 1482 strlcpy(pw_tmp, pwlock_str, sizeof(pw_len)); 1483 strlcat(pw_tmp, pwp->pw_passwd, sizeof(pw_len)); 1484 pwp->pw_passwd = pw_tmp; 1485 } else { 1486 locked++; 1487 } 1488 1489 if (locked > 1) 1490 warnx("account `%s' is already locked", pwp->pw_name); 1491 } 1492 if (up->u_flags & F_ACCTUNLOCK) { 1493 /* unlock the password */ 1494 if (strcmp(pwp->pw_passwd, pwlock_str) != 0 && 1495 strcmp(pwp->pw_passwd, "*************") != 0) { 1496 if (strncmp(pwp->pw_passwd, pwlock_str, sizeof(pwlock_str)-1) == 0) { 1497 pwp->pw_passwd += sizeof(pwlock_str)-1; 1498 } else { 1499 unlocked++; 1500 } 1501 } else { 1502 warnx("account `%s' has no password: cannot fully unlock", pwp->pw_name); 1503 } 1504 /* unlock the account */ 1505 if (*shell_last_char == *acctlock_str) { 1506 shell_buf = strlen(pwp->pw_shell) + 2 - sizeof(acctlock_str); 1507 shell_tmp = malloc(shell_buf); 1508 if (shell_tmp == NULL) { 1509 (void) close(ptmpfd); 1510 pw_abort(); 1511 errx(EXIT_FAILURE, "unlock: cannot allocate memory"); 1512 } 1513 strlcpy(shell_tmp, pwp->pw_shell, shell_buf); 1514 pwp->pw_shell = shell_tmp; 1515 } else { 1516 unlocked++; 1517 } 1518 1519 if (unlocked > 1) 1520 warnx("account `%s' is not locked", pwp->pw_name); 1521 } 1522 if (up->u_flags & F_UID) { 1523 /* check uid isn't already allocated */ 1524 if (!(up->u_flags & F_DUPUID) && getpwuid((uid_t)(up->u_uid)) != NULL) { 1525 (void) close(ptmpfd); 1526 pw_abort(); 1527 errx(EXIT_FAILURE, "uid %u is already in use", up->u_uid); 1528 } 1529 pwp->pw_uid = up->u_uid; 1530 } 1531 if (up->u_flags & F_GROUP) { 1532 /* if -g=uid was specified, check gid is unused */ 1533 if (strcmp(up->u_primgrp, "=uid") == 0) { 1534 if (getgrgid((gid_t)(up->u_uid)) != NULL) { 1535 (void) close(ptmpfd); 1536 pw_abort(); 1537 errx(EXIT_FAILURE, "gid %u is already in use", up->u_uid); 1538 } 1539 pwp->pw_gid = up->u_uid; 1540 } else if ((grp = getgrnam(up->u_primgrp)) != NULL) { 1541 pwp->pw_gid = grp->gr_gid; 1542 } else if (is_number(up->u_primgrp) && 1543 (grp = getgrgid((gid_t)atoi(up->u_primgrp))) != NULL) { 1544 pwp->pw_gid = grp->gr_gid; 1545 } else { 1546 (void) close(ptmpfd); 1547 pw_abort(); 1548 errx(EXIT_FAILURE, "group %s not found", up->u_primgrp); 1549 } 1550 } 1551 if (up->u_flags & F_INACTIVE) { 1552 if (!scantime(&pwp->pw_change, up->u_inactive)) { 1553 warnx("Warning: inactive time `%s' invalid, password expiry off", 1554 up->u_inactive); 1555 } 1556 } 1557 if (up->u_flags & F_EXPIRE) { 1558 if (!scantime(&pwp->pw_expire, up->u_expire)) { 1559 warnx("Warning: expire time `%s' invalid, account expiry off", 1560 up->u_expire); 1561 } 1562 } 1563 if (up->u_flags & F_COMMENT) 1564 pwp->pw_gecos = up->u_comment; 1565 if (up->u_flags & F_HOMEDIR) 1566 pwp->pw_dir = up->u_home; 1567 if (up->u_flags & F_SHELL) 1568 pwp->pw_shell = up->u_shell; 1569 if (up->u_flags & F_CLASS) { 1570 if (!valid_class(up->u_class)) { 1571 (void) close(ptmpfd); 1572 pw_abort(); 1573 errx(EXIT_FAILURE, 1574 "No such login class `%s'", up->u_class); 1575 } 1576 pwp->pw_class = up->u_class; 1577 } 1578 } 1579 loginc = strlen(login_name); 1580 while (fgets(buf, sizeof(buf), master) != NULL) { 1581 if ((colon = strchr(buf, ':')) == NULL) { 1582 warnx("Malformed entry `%s'. Skipping", buf); 1583 continue; 1584 } 1585 colonc = (size_t)(colon - buf); 1586 if (strncmp(login_name, buf, loginc) == 0 && loginc == colonc) { 1587 if (up != NULL) { 1588 if ((len = snprintf(buf, sizeof(buf), 1589 "%s:%s:%u:%u:%s:%ld:%ld:%s:%s:%s\n", 1590 newlogin, 1591 pwp->pw_passwd, 1592 pwp->pw_uid, 1593 pwp->pw_gid, 1594 pwp->pw_class, 1595 (long)pwp->pw_change, 1596 (long)pwp->pw_expire, 1597 pwp->pw_gecos, 1598 pwp->pw_dir, 1599 pwp->pw_shell)) >= sizeof(buf) || len < 0 || 1600 len + expand_len(pwp->pw_gecos, newlogin) 1601 >= 1023) { 1602 (void) close(ptmpfd); 1603 pw_abort(); 1604 errx(EXIT_FAILURE, "can't add `%s', " 1605 "line too long (%zu bytes)", buf, 1606 len + expand_len(pwp->pw_gecos, 1607 newlogin)); 1608 } 1609 if (write(ptmpfd, buf, len) != len) { 1610 int saved_errno = errno; 1611 (void) close(ptmpfd); 1612 pw_abort(); 1613 errc(EXIT_FAILURE, saved_errno, 1614 "can't add `%s'", buf); 1615 } 1616 } 1617 } else { 1618 len = strlen(buf); 1619 if ((cc = write(ptmpfd, buf, len)) != len) { 1620 int saved_errno = errno; 1621 (void) close(masterfd); 1622 (void) close(ptmpfd); 1623 pw_abort(); 1624 errc(EXIT_FAILURE, saved_errno, 1625 "short write to /etc/ptmp (%lld not %lld chars)", 1626 (long long)cc, (long long)len); 1627 } 1628 } 1629 } 1630 if (up != NULL) { 1631 if ((up->u_flags & F_MKDIR) && 1632 asystem("%s %s %s", MV, homedir, pwp->pw_dir) != 0) { 1633 int saved_errno = errno; 1634 (void) close(ptmpfd); 1635 pw_abort(); 1636 errc(EXIT_FAILURE, saved_errno, 1637 "can't move `%s' to `%s'", homedir, pwp->pw_dir); 1638 } 1639 if (up->u_flags & F_SETSECGROUP) { 1640 for (i = 0 ; i < up->u_groupc ; i++) { 1641 if (getgrnam(up->u_groupv[i]) == NULL) { 1642 (void) close(ptmpfd); 1643 pw_abort(); 1644 errx(EXIT_FAILURE, "aborting, group `%s' does not exist", 1645 up->u_groupv[i]); 1646 } 1647 } 1648 if (!rm_user_from_groups(newlogin)) { 1649 (void) close(ptmpfd); 1650 pw_abort(); 1651 errx(EXIT_FAILURE, "can't reset groups for `%s'", newlogin); 1652 } 1653 } 1654 if (up->u_groupc > 0) { 1655 if (!append_group(newlogin, up->u_groupc, up->u_groupv)) { 1656 (void) close(ptmpfd); 1657 pw_abort(); 1658 errx(EXIT_FAILURE, "can't append `%s' to new groups", 1659 newlogin); 1660 } 1661 } 1662 } 1663 (void) close(ptmpfd); 1664 if (pw_tmp) 1665 FREE(pw_tmp); 1666 if (shell_tmp) 1667 FREE(shell_tmp); 1668 if (up != NULL && strcmp(login_name, newlogin) == 0) 1669 rval = pw_mkdb(login_name, 0); 1670 else 1671 rval = pw_mkdb(NULL, 0); 1672 if (rval == -1) { 1673 pw_abort(); 1674 err(EXIT_FAILURE, "pw_mkdb failed"); 1675 } 1676 if (up == NULL) { 1677 syslog(LOG_INFO, "user removed: name=%s", login_name); 1678 } else if (strcmp(login_name, newlogin) == 0) { 1679 syslog(LOG_INFO, "user information modified: name=%s, uid=%u, gid=%u, home=%s, shell=%s", 1680 login_name, pwp->pw_uid, pwp->pw_gid, pwp->pw_dir, pwp->pw_shell); 1681 } else { 1682 syslog(LOG_INFO, "user information modified: name=%s, new name=%s, uid=%u, gid=%u, home=%s, shell=%s", 1683 login_name, newlogin, pwp->pw_uid, pwp->pw_gid, pwp->pw_dir, pwp->pw_shell); 1684 } 1685 return 1; 1686 } 1687 1688 1689 /* see if we can find out the user struct */ 1690 static struct passwd * 1691 find_user_info(char *name) 1692 { 1693 struct passwd *pwp; 1694 1695 if ((pwp = getpwnam(name)) != NULL) { 1696 return pwp; 1697 } 1698 if (is_number(name) && (pwp = getpwuid((uid_t)atoi(name))) != NULL) { 1699 return pwp; 1700 } 1701 return NULL; 1702 } 1703 1704 /* see if we can find out the group struct */ 1705 static struct group * 1706 find_group_info(char *name) 1707 { 1708 struct group *grp; 1709 1710 if ((grp = getgrnam(name)) != NULL) { 1711 return grp; 1712 } 1713 if (is_number(name) && (grp = getgrgid((gid_t)atoi(name))) != NULL) { 1714 return grp; 1715 } 1716 return NULL; 1717 } 1718 1719 /* print out usage message, and then exit */ 1720 void 1721 usermgmt_usage(const char *prog) 1722 { 1723 if (strcmp(prog, "useradd") == 0) { 1724 (void) fprintf(stderr, "usage: %s -D [-b base-directory] " 1725 "[-e expiry-time] [-f inactive-time]\n" 1726 " [-g gid | name | =uid] [-k skel-directory] " 1727 "[-L login-class]\n" 1728 " [-r low..high] [-s shell]\n", prog); 1729 (void) fprintf(stderr, " %s [-mov] [-b base-directory] " 1730 "[-c comment] [-d home-directory]\n" 1731 " [-e expiry-time] [-f inactive-time]\n" 1732 " [-G secondary-group[,group,...]] " 1733 "[-g gid | name | =uid]\n" 1734 " [-k skel-directory] [-L login-class] " 1735 "[-p password] [-r low..high]\n" 1736 " [-s shell] [-u uid] user\n", prog); 1737 } else if (strcmp(prog, "usermod") == 0) { 1738 (void) fprintf(stderr, "usage: %s [-moUvZ] " 1739 "[-c comment] [-d home-directory] [-e expiry-time]\n" 1740 " [-f inactive-time] " 1741 "[-G secondary-group[,group,...]]\n" 1742 " [-g gid | name | =uid] [-L login-class] " 1743 "[-l new-login]\n" 1744 " [-p password] " 1745 "[-S secondary-group[,group,...]]\n" 1746 " [-s shell] [-u uid] user\n", 1747 prog); 1748 } else if (strcmp(prog, "userdel") == 0) { 1749 (void) fprintf(stderr, "usage: %s -D [-p preserve-value]\n", 1750 prog); 1751 (void) fprintf(stderr, " %s [-rv] [-p preserve-value] " 1752 "user\n", prog); 1753 } else if (strcmp(prog, "userinfo") == 0) { 1754 (void) fprintf(stderr, "usage: %s [-e] user\n", prog); 1755 } else if (strcmp(prog, "groupadd") == 0) { 1756 (void) fprintf(stderr, "usage: %s [-ov] [-g gid] group\n", 1757 prog); 1758 } else if (strcmp(prog, "groupdel") == 0) { 1759 (void) fprintf(stderr, "usage: %s [-v] group\n", prog); 1760 } else if (strcmp(prog, "groupmod") == 0) { 1761 (void) fprintf(stderr, "usage: %s [-ov] [-g gid] [-n newname] " 1762 "group\n", prog); 1763 } else if (strcmp(prog, "user") == 0 || strcmp(prog, "group") == 0) { 1764 (void) fprintf(stderr, "usage: %s [add | del | mod" 1765 " | info" 1766 "] ...\n", 1767 prog); 1768 } else if (strcmp(prog, "groupinfo") == 0) { 1769 (void) fprintf(stderr, "usage: %s [-e] group\n", prog); 1770 } else { 1771 (void) fprintf(stderr, "This program must be called as {user,group}{add,del,mod,info},\n%s is not an understood name.\n", prog); 1772 } 1773 exit(EXIT_FAILURE); 1774 /* NOTREACHED */ 1775 } 1776 1777 int 1778 useradd(int argc, char **argv) 1779 { 1780 user_t u; 1781 int defaultfield; 1782 int bigD; 1783 int c; 1784 int i; 1785 1786 (void) memset(&u, 0, sizeof(u)); 1787 read_defaults(&u); 1788 u.u_uid = UID_MAX; 1789 defaultfield = bigD = 0; 1790 while ((c = getopt(argc, argv, "DG:L:b:c:d:e:f:g:k:mop:r:s:u:v")) != -1) { 1791 switch(c) { 1792 case 'D': 1793 bigD = 1; 1794 break; 1795 case 'G': 1796 while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && 1797 u.u_groupc < NGROUPS_MAX - 2) { 1798 if (u.u_groupv[u.u_groupc][0] != 0) { 1799 u.u_groupc++; 1800 } 1801 } 1802 if (optarg != NULL) { 1803 warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX - 2); 1804 } 1805 break; 1806 case 'b': 1807 defaultfield = 1; 1808 memsave(&u.u_basedir, optarg, strlen(optarg)); 1809 break; 1810 case 'c': 1811 memsave(&u.u_comment, optarg, strlen(optarg)); 1812 break; 1813 case 'd': 1814 memsave(&u.u_home, optarg, strlen(optarg)); 1815 u.u_flags |= F_HOMEDIR; 1816 break; 1817 case 'e': 1818 defaultfield = 1; 1819 memsave(&u.u_expire, optarg, strlen(optarg)); 1820 break; 1821 case 'f': 1822 defaultfield = 1; 1823 memsave(&u.u_inactive, optarg, strlen(optarg)); 1824 break; 1825 case 'g': 1826 defaultfield = 1; 1827 memsave(&u.u_primgrp, optarg, strlen(optarg)); 1828 break; 1829 case 'k': 1830 defaultfield = 1; 1831 memsave(&u.u_skeldir, optarg, strlen(optarg)); 1832 break; 1833 case 'L': 1834 defaultfield = 1; 1835 memsave(&u.u_class, optarg, strlen(optarg)); 1836 break; 1837 case 'm': 1838 u.u_flags |= F_MKDIR; 1839 break; 1840 case 'o': 1841 u.u_flags |= F_DUPUID; 1842 break; 1843 case 'p': 1844 memsave(&u.u_password, optarg, strlen(optarg)); 1845 memset(optarg, 'X', strlen(optarg)); 1846 break; 1847 case 'r': 1848 defaultfield = 1; 1849 (void) save_range(&u, optarg); 1850 break; 1851 case 's': 1852 defaultfield = 1; 1853 memsave(&u.u_shell, optarg, strlen(optarg)); 1854 break; 1855 case 'u': 1856 if (!is_number(optarg)) { 1857 errx(EXIT_FAILURE, "When using [-u uid], the uid must be numeric"); 1858 } 1859 u.u_uid = atoi(optarg); 1860 break; 1861 case 'v': 1862 verbose = 1; 1863 break; 1864 default: 1865 usermgmt_usage("useradd"); 1866 /* NOTREACHED */ 1867 } 1868 } 1869 if (bigD) { 1870 if (defaultfield) { 1871 checkeuid(); 1872 return setdefaults(&u) ? EXIT_SUCCESS : EXIT_FAILURE; 1873 } 1874 (void) printf("group\t\t%s\n", u.u_primgrp); 1875 (void) printf("base_dir\t%s\n", u.u_basedir); 1876 (void) printf("skel_dir\t%s\n", u.u_skeldir); 1877 (void) printf("shell\t\t%s\n", u.u_shell); 1878 (void) printf("class\t\t%s\n", u.u_class); 1879 (void) printf("inactive\t%s\n", (u.u_inactive == NULL) ? UNSET_INACTIVE : u.u_inactive); 1880 (void) printf("expire\t\t%s\n", (u.u_expire == NULL) ? UNSET_EXPIRY : u.u_expire); 1881 for (i = 0 ; i < u.u_rc ; i++) { 1882 (void) printf("range\t\t%u..%u\n", u.u_rv[i].r_from, u.u_rv[i].r_to); 1883 } 1884 return EXIT_SUCCESS; 1885 } 1886 argc -= optind; 1887 argv += optind; 1888 if (argc != 1) { 1889 usermgmt_usage("useradd"); 1890 } 1891 checkeuid(); 1892 openlog("useradd", LOG_PID, LOG_USER); 1893 return adduser(*argv, &u) ? EXIT_SUCCESS : EXIT_FAILURE; 1894 } 1895 1896 int 1897 usermod(int argc, char **argv) 1898 { 1899 user_t u; 1900 char newuser[MaxUserNameLen + 1]; 1901 int c, have_new_user; 1902 1903 (void) memset(&u, 0, sizeof(u)); 1904 (void) memset(newuser, 0, sizeof(newuser)); 1905 read_defaults(&u); 1906 free(u.u_primgrp); 1907 u.u_primgrp = NULL; 1908 have_new_user = 0; 1909 while ((c = getopt(argc, argv, "G:L:S:UZc:d:e:f:g:l:mop:s:u:v")) != -1) { 1910 switch(c) { 1911 case 'G': 1912 while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && 1913 u.u_groupc < NGROUPS_MAX - 2) { 1914 if (u.u_groupv[u.u_groupc][0] != 0) { 1915 u.u_groupc++; 1916 } 1917 } 1918 if (optarg != NULL) { 1919 warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX - 2); 1920 } 1921 u.u_flags |= F_SECGROUP; 1922 break; 1923 case 'S': 1924 while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && 1925 u.u_groupc < NGROUPS_MAX - 2) { 1926 if (u.u_groupv[u.u_groupc][0] != 0) { 1927 u.u_groupc++; 1928 } 1929 } 1930 if (optarg != NULL) { 1931 warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX - 2); 1932 } 1933 u.u_flags |= F_SETSECGROUP; 1934 break; 1935 case 'U': 1936 u.u_flags |= F_ACCTUNLOCK; 1937 break; 1938 case 'Z': 1939 u.u_flags |= F_ACCTLOCK; 1940 break; 1941 case 'c': 1942 memsave(&u.u_comment, optarg, strlen(optarg)); 1943 u.u_flags |= F_COMMENT; 1944 break; 1945 case 'd': 1946 memsave(&u.u_home, optarg, strlen(optarg)); 1947 u.u_flags |= F_HOMEDIR; 1948 break; 1949 case 'e': 1950 memsave(&u.u_expire, optarg, strlen(optarg)); 1951 u.u_flags |= F_EXPIRE; 1952 break; 1953 case 'f': 1954 memsave(&u.u_inactive, optarg, strlen(optarg)); 1955 u.u_flags |= F_INACTIVE; 1956 break; 1957 case 'g': 1958 memsave(&u.u_primgrp, optarg, strlen(optarg)); 1959 u.u_flags |= F_GROUP; 1960 break; 1961 case 'l': 1962 if (strlcpy(newuser, optarg, sizeof(newuser)) >= 1963 sizeof(newuser)) 1964 errx(EXIT_FAILURE, "username `%s' too long", 1965 optarg); 1966 have_new_user = 1; 1967 u.u_flags |= F_USERNAME; 1968 break; 1969 case 'L': 1970 memsave(&u.u_class, optarg, strlen(optarg)); 1971 u.u_flags |= F_CLASS; 1972 break; 1973 case 'm': 1974 u.u_flags |= F_MKDIR; 1975 break; 1976 case 'o': 1977 u.u_flags |= F_DUPUID; 1978 break; 1979 case 'p': 1980 memsave(&u.u_password, optarg, strlen(optarg)); 1981 memset(optarg, 'X', strlen(optarg)); 1982 u.u_flags |= F_PASSWORD; 1983 break; 1984 case 's': 1985 memsave(&u.u_shell, optarg, strlen(optarg)); 1986 u.u_flags |= F_SHELL; 1987 break; 1988 case 'u': 1989 if (!is_number(optarg)) { 1990 errx(EXIT_FAILURE, "When using [-u uid], the uid must be numeric"); 1991 } 1992 u.u_uid = atoi(optarg); 1993 u.u_flags |= F_UID; 1994 break; 1995 case 'v': 1996 verbose = 1; 1997 break; 1998 default: 1999 usermgmt_usage("usermod"); 2000 /* NOTREACHED */ 2001 } 2002 } 2003 if ((u.u_flags & F_MKDIR) && !(u.u_flags & F_HOMEDIR) && 2004 !(u.u_flags & F_USERNAME)) { 2005 warnx("option 'm' useless without 'd' or 'l' -- ignored"); 2006 u.u_flags &= ~F_MKDIR; 2007 } 2008 if ((u.u_flags & F_SECGROUP) && (u.u_flags & F_SETSECGROUP)) 2009 errx(EXIT_FAILURE, "options 'G' and 'S' are mutually exclusive"); 2010 if ((u.u_flags & F_ACCTLOCK) && (u.u_flags & F_ACCTUNLOCK)) 2011 errx(EXIT_FAILURE, "options 'U' and 'Z' are mutually exclusive"); 2012 if ((u.u_flags & F_PASSWORD) && (u.u_flags & (F_ACCTLOCK | F_ACCTUNLOCK))) 2013 errx(EXIT_FAILURE, "options 'U' or 'Z' with 'p' are mutually exclusive"); 2014 argc -= optind; 2015 argv += optind; 2016 if (argc != 1) { 2017 usermgmt_usage("usermod"); 2018 } 2019 checkeuid(); 2020 openlog("usermod", LOG_PID, LOG_USER); 2021 return moduser(*argv, (have_new_user) ? newuser : *argv, &u) ? 2022 EXIT_SUCCESS : EXIT_FAILURE; 2023 } 2024 2025 int 2026 userdel(int argc, char **argv) 2027 { 2028 struct passwd *pwp; 2029 user_t u; 2030 char password[PasswordLength + 1]; 2031 int defaultfield; 2032 int rmhome; 2033 int bigD; 2034 int c; 2035 2036 (void) memset(&u, 0, sizeof(u)); 2037 read_defaults(&u); 2038 defaultfield = bigD = rmhome = 0; 2039 while ((c = getopt(argc, argv, "Dp:rv")) != -1) { 2040 switch(c) { 2041 case 'D': 2042 bigD = 1; 2043 break; 2044 case 'p': 2045 defaultfield = 1; 2046 u.u_preserve = (strcmp(optarg, "true") == 0) ? 1 : 2047 (strcmp(optarg, "yes") == 0) ? 1 : 2048 atoi(optarg); 2049 break; 2050 case 'r': 2051 rmhome = 1; 2052 break; 2053 case 'v': 2054 verbose = 1; 2055 break; 2056 default: 2057 usermgmt_usage("userdel"); 2058 /* NOTREACHED */ 2059 } 2060 } 2061 if (bigD) { 2062 if (defaultfield) { 2063 checkeuid(); 2064 return setdefaults(&u) ? EXIT_SUCCESS : EXIT_FAILURE; 2065 } 2066 (void) printf("preserve\t%s\n", (u.u_preserve) ? "true" : "false"); 2067 return EXIT_SUCCESS; 2068 } 2069 argc -= optind; 2070 argv += optind; 2071 if (argc != 1) { 2072 usermgmt_usage("userdel"); 2073 } 2074 checkeuid(); 2075 if ((pwp = getpwnam(*argv)) == NULL) { 2076 warnx("No such user `%s'", *argv); 2077 return EXIT_FAILURE; 2078 } 2079 if (rmhome) 2080 (void)removehomedir(pwp->pw_name, pwp->pw_uid, pwp->pw_dir); 2081 if (u.u_preserve) { 2082 u.u_flags |= F_SHELL; 2083 memsave(&u.u_shell, NOLOGIN, strlen(NOLOGIN)); 2084 (void) memset(password, '*', DES_Len); 2085 password[DES_Len] = 0; 2086 memsave(&u.u_password, password, strlen(password)); 2087 u.u_flags |= F_PASSWORD; 2088 openlog("userdel", LOG_PID, LOG_USER); 2089 return moduser(*argv, *argv, &u) ? EXIT_SUCCESS : EXIT_FAILURE; 2090 } 2091 if (!rm_user_from_groups(*argv)) { 2092 return 0; 2093 } 2094 openlog("userdel", LOG_PID, LOG_USER); 2095 return moduser(*argv, *argv, NULL) ? EXIT_SUCCESS : EXIT_FAILURE; 2096 } 2097 2098 /* add a group */ 2099 int 2100 groupadd(int argc, char **argv) 2101 { 2102 int dupgid; 2103 int gid; 2104 int c; 2105 2106 gid = GID_MAX; 2107 dupgid = 0; 2108 while ((c = getopt(argc, argv, "g:ov")) != -1) { 2109 switch(c) { 2110 case 'g': 2111 if (!is_number(optarg)) { 2112 errx(EXIT_FAILURE, "When using [-g gid], the gid must be numeric"); 2113 } 2114 gid = atoi(optarg); 2115 break; 2116 case 'o': 2117 dupgid = 1; 2118 break; 2119 case 'v': 2120 verbose = 1; 2121 break; 2122 default: 2123 usermgmt_usage("groupadd"); 2124 /* NOTREACHED */ 2125 } 2126 } 2127 argc -= optind; 2128 argv += optind; 2129 if (argc != 1) { 2130 usermgmt_usage("groupadd"); 2131 } 2132 checkeuid(); 2133 if (!valid_group(*argv)) { 2134 errx(EXIT_FAILURE, "invalid group name `%s'", *argv); 2135 } 2136 if (gid < 0 && !getnextgid(&gid, LowGid, HighGid)) { 2137 errx(EXIT_FAILURE, "can't add group: can't get next gid"); 2138 } 2139 if (!dupgid && getgrgid((gid_t) gid) != NULL) { 2140 errx(EXIT_FAILURE, "can't add group: gid %d is a duplicate", gid); 2141 } 2142 openlog("groupadd", LOG_PID, LOG_USER); 2143 if (!creategid(*argv, gid, "")) { 2144 errx(EXIT_FAILURE, "can't add group: problems with %s file", 2145 _PATH_GROUP); 2146 } 2147 return EXIT_SUCCESS; 2148 } 2149 2150 /* remove a group */ 2151 int 2152 groupdel(int argc, char **argv) 2153 { 2154 int c; 2155 2156 while ((c = getopt(argc, argv, "v")) != -1) { 2157 switch(c) { 2158 case 'v': 2159 verbose = 1; 2160 break; 2161 default: 2162 usermgmt_usage("groupdel"); 2163 /* NOTREACHED */ 2164 } 2165 } 2166 argc -= optind; 2167 argv += optind; 2168 if (argc != 1) { 2169 usermgmt_usage("groupdel"); 2170 } 2171 checkeuid(); 2172 openlog("groupdel", LOG_PID, LOG_USER); 2173 if (getgrnam(*argv) == NULL) { 2174 warnx("No such group: `%s'", *argv); 2175 return EXIT_FAILURE; 2176 } 2177 if (!modify_gid(*argv, NULL)) { 2178 err(EXIT_FAILURE, "can't change %s file", _PATH_GROUP); 2179 } 2180 return EXIT_SUCCESS; 2181 } 2182 2183 /* modify a group */ 2184 int 2185 groupmod(int argc, char **argv) 2186 { 2187 struct group *grp; 2188 char buf[LINE_MAX]; 2189 char *newname; 2190 char **cpp; 2191 int dupgid; 2192 int gid; 2193 int cc; 2194 int c; 2195 2196 gid = GID_MAX; 2197 dupgid = 0; 2198 newname = NULL; 2199 while ((c = getopt(argc, argv, "g:n:ov")) != -1) { 2200 switch(c) { 2201 case 'g': 2202 if (!is_number(optarg)) { 2203 errx(EXIT_FAILURE, "When using [-g gid], the gid must be numeric"); 2204 } 2205 gid = atoi(optarg); 2206 break; 2207 case 'o': 2208 dupgid = 1; 2209 break; 2210 case 'n': 2211 memsave(&newname, optarg, strlen(optarg)); 2212 break; 2213 case 'v': 2214 verbose = 1; 2215 break; 2216 default: 2217 usermgmt_usage("groupmod"); 2218 /* NOTREACHED */ 2219 } 2220 } 2221 argc -= optind; 2222 argv += optind; 2223 if (argc != 1) { 2224 usermgmt_usage("groupmod"); 2225 } 2226 checkeuid(); 2227 if (gid < 0 && newname == NULL) { 2228 errx(EXIT_FAILURE, "Nothing to change"); 2229 } 2230 if (dupgid && gid < 0) { 2231 errx(EXIT_FAILURE, "Duplicate which gid?"); 2232 } 2233 if ((grp = getgrnam(*argv)) == NULL) { 2234 errx(EXIT_FAILURE, "can't find group `%s' to modify", *argv); 2235 } 2236 if (!is_local(*argv, _PATH_GROUP)) { 2237 errx(EXIT_FAILURE, "Group `%s' must be a local group", *argv); 2238 } 2239 if (newname != NULL && !valid_group(newname)) { 2240 errx(EXIT_FAILURE, "invalid group name `%s'", newname); 2241 } 2242 if ((cc = snprintf(buf, sizeof(buf), "%s:%s:%u:", 2243 (newname) ? newname : grp->gr_name, grp->gr_passwd, 2244 (gid < 0) ? grp->gr_gid : gid)) >= sizeof(buf) || cc < 0) 2245 errx(EXIT_FAILURE, "group `%s' entry too long", grp->gr_name); 2246 2247 for (cpp = grp->gr_mem ; *cpp ; cpp++) { 2248 cc = strlcat(buf, *cpp, sizeof(buf)) + 1; 2249 if (cc >= sizeof(buf)) 2250 errx(EXIT_FAILURE, "group `%s' entry too long", 2251 grp->gr_name); 2252 if (cpp[1] != NULL) { 2253 buf[cc - 1] = ','; 2254 buf[cc] = '\0'; 2255 } 2256 } 2257 cc = strlcat(buf, "\n", sizeof(buf)); 2258 if (cc >= sizeof(buf)) 2259 errx(EXIT_FAILURE, "group `%s' entry too long", grp->gr_name); 2260 2261 openlog("groupmod", LOG_PID, LOG_USER); 2262 if (!modify_gid(*argv, buf)) 2263 err(EXIT_FAILURE, "can't change %s file", _PATH_GROUP); 2264 return EXIT_SUCCESS; 2265 } 2266 2267 /* display user information */ 2268 int 2269 userinfo(int argc, char **argv) 2270 { 2271 struct passwd *pwp; 2272 struct group *grp; 2273 char **cpp; 2274 int exists; 2275 int i; 2276 2277 exists = 0; 2278 while ((i = getopt(argc, argv, "ev")) != -1) { 2279 switch(i) { 2280 case 'e': 2281 exists = 1; 2282 break; 2283 case 'v': 2284 verbose = 1; 2285 break; 2286 default: 2287 usermgmt_usage("userinfo"); 2288 /* NOTREACHED */ 2289 } 2290 } 2291 argc -= optind; 2292 argv += optind; 2293 if (argc != 1) { 2294 usermgmt_usage("userinfo"); 2295 } 2296 pwp = find_user_info(*argv); 2297 if (exists) { 2298 exit((pwp) ? EXIT_SUCCESS : EXIT_FAILURE); 2299 } 2300 if (pwp == NULL) { 2301 errx(EXIT_FAILURE, "can't find user `%s'", *argv); 2302 } 2303 (void) printf("login\t%s\n", pwp->pw_name); 2304 (void) printf("passwd\t%s\n", pwp->pw_passwd); 2305 (void) printf("uid\t%u\n", pwp->pw_uid); 2306 if ((grp = getgrgid(pwp->pw_gid)) == NULL) 2307 (void) printf("groups\t%u", pwp->pw_gid); 2308 else 2309 (void) printf("groups\t%s", grp->gr_name); 2310 while ((grp = getgrent()) != NULL) { 2311 for (cpp = grp->gr_mem ; *cpp ; cpp++) { 2312 if (strcmp(*cpp, pwp->pw_name) == 0 && 2313 grp->gr_gid != pwp->pw_gid) 2314 (void) printf(" %s", grp->gr_name); 2315 } 2316 } 2317 (void) fputc('\n', stdout); 2318 (void) printf("change\t%s", pwp->pw_change ? ctime(&pwp->pw_change) : "NEVER\n"); 2319 (void) printf("class\t%s\n", pwp->pw_class); 2320 (void) printf("gecos\t%s\n", pwp->pw_gecos); 2321 (void) printf("dir\t%s\n", pwp->pw_dir); 2322 (void) printf("shell\t%s\n", pwp->pw_shell); 2323 (void) printf("expire\t%s", pwp->pw_expire ? ctime(&pwp->pw_expire) : "NEVER\n"); 2324 return EXIT_SUCCESS; 2325 } 2326 2327 /* display user information */ 2328 int 2329 groupinfo(int argc, char **argv) 2330 { 2331 struct group *grp; 2332 char **cpp; 2333 int exists; 2334 int i; 2335 2336 exists = 0; 2337 while ((i = getopt(argc, argv, "ev")) != -1) { 2338 switch(i) { 2339 case 'e': 2340 exists = 1; 2341 break; 2342 case 'v': 2343 verbose = 1; 2344 break; 2345 default: 2346 usermgmt_usage("groupinfo"); 2347 /* NOTREACHED */ 2348 } 2349 } 2350 argc -= optind; 2351 argv += optind; 2352 if (argc != 1) { 2353 usermgmt_usage("groupinfo"); 2354 } 2355 grp = find_group_info(*argv); 2356 if (exists) { 2357 exit((grp) ? EXIT_SUCCESS : EXIT_FAILURE); 2358 } 2359 if (grp == NULL) { 2360 errx(EXIT_FAILURE, "can't find group `%s'", *argv); 2361 } 2362 (void) printf("name\t%s\n", grp->gr_name); 2363 (void) printf("passwd\t%s\n", grp->gr_passwd); 2364 (void) printf("gid\t%u\n", grp->gr_gid); 2365 (void) printf("members\t"); 2366 for (cpp = grp->gr_mem ; *cpp ; cpp++) { 2367 (void) printf("%s ", *cpp); 2368 } 2369 (void) fputc('\n', stdout); 2370 return EXIT_SUCCESS; 2371 } 2372