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