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