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