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