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