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