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