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