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