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