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