1 /* $NetBSD: user.c,v 1.34 2000/12/07 17:44:03 wiz Exp $ */ 2 3 /* 4 * Copyright (c) 1999 Alistair G. Crooks. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Alistair G. Crooks. 17 * 4. The name of the author may not be used to endorse or promote 18 * products derived from this software without specific prior written 19 * permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 #include <sys/cdefs.h> 34 35 #ifndef lint 36 __COPYRIGHT("@(#) Copyright (c) 1999 \ 37 The NetBSD Foundation, Inc. All rights reserved."); 38 __RCSID("$NetBSD: user.c,v 1.34 2000/12/07 17:44:03 wiz Exp $"); 39 #endif 40 41 #include <sys/types.h> 42 #include <sys/param.h> 43 #include <sys/stat.h> 44 45 #include <ctype.h> 46 #include <dirent.h> 47 #include <err.h> 48 #include <fcntl.h> 49 #include <grp.h> 50 #include <paths.h> 51 #include <pwd.h> 52 #include <stdarg.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <time.h> 57 #include <unistd.h> 58 #include <util.h> 59 60 #include "defs.h" 61 #include "usermgmt.h" 62 63 64 /* this struct describes a uid range */ 65 typedef struct range_t { 66 int r_from; /* low uid */ 67 int r_to; /* high uid */ 68 } range_t; 69 70 /* this struct encapsulates the user information */ 71 typedef struct user_t { 72 int u_flags; /* see below */ 73 int u_uid; /* uid of user */ 74 char *u_password; /* encrypted password */ 75 char *u_comment; /* comment field */ 76 char *u_home; /* home directory */ 77 char *u_primgrp; /* primary group */ 78 int u_groupc; /* # of secondary groups */ 79 char *u_groupv[NGROUPS_MAX]; /* secondary groups */ 80 char *u_shell; /* user's shell */ 81 char *u_basedir; /* base directory for home */ 82 char *u_expire; /* when password will expire */ 83 int u_inactive; /* inactive */ 84 char *u_skeldir; /* directory for startup files */ 85 unsigned u_rsize; /* size of range array */ 86 unsigned u_rc; /* # of ranges */ 87 range_t *u_rv; /* the ranges */ 88 unsigned u_defrc; /* # of ranges in defaults */ 89 int u_preserve; /* preserve uids on deletion */ 90 } user_t; 91 92 /* flags for which fields of the user_t replace the passwd entry */ 93 enum { 94 F_COMMENT = 0x0001, 95 F_DUPUID = 0x0002, 96 F_EXPIRE = 0x0004, 97 F_GROUP = 0x0008, 98 F_HOMEDIR = 0x0010, 99 F_MKDIR = 0x0020, 100 F_INACTIVE = 0x0040, 101 F_PASSWORD = 0x0080, 102 F_SECGROUP = 0x0100, 103 F_SHELL = 0x0200, 104 F_UID = 0x0400, 105 F_USERNAME = 0x0800 106 }; 107 108 #define CONFFILE "/etc/usermgmt.conf" 109 110 #ifndef DEF_GROUP 111 #define DEF_GROUP "users" 112 #endif 113 114 #ifndef DEF_BASEDIR 115 #define DEF_BASEDIR "/home" 116 #endif 117 118 #ifndef DEF_SKELDIR 119 #define DEF_SKELDIR "/etc/skel" 120 #endif 121 122 #ifndef DEF_SHELL 123 #define DEF_SHELL _PATH_CSHELL 124 #endif 125 126 #ifndef DEF_COMMENT 127 #define DEF_COMMENT "" 128 #endif 129 130 #ifndef DEF_LOWUID 131 #define DEF_LOWUID 1000 132 #endif 133 134 #ifndef DEF_HIGHUID 135 #define DEF_HIGHUID 60000 136 #endif 137 138 #ifndef DEF_INACTIVE 139 #define DEF_INACTIVE 0 140 #endif 141 142 #ifndef DEF_EXPIRE 143 #define DEF_EXPIRE NULL 144 #endif 145 146 #ifndef WAITSECS 147 #define WAITSECS 10 148 #endif 149 150 #ifndef NOBODY_UID 151 #define NOBODY_UID 32767 152 #endif 153 154 /* some useful constants */ 155 enum { 156 MaxShellNameLen = 256, 157 MaxFileNameLen = MAXPATHLEN, 158 MaxUserNameLen = 32, 159 MaxFieldNameLen = 32, 160 MaxCommandLen = 2048, 161 MaxEntryLen = 2048, 162 PasswordLength = 13, 163 164 LowGid = DEF_LOWUID, 165 HighGid = DEF_HIGHUID 166 }; 167 168 /* Full paths of programs used here */ 169 #define CHMOD "/bin/chmod" 170 #define CHOWN "/usr/sbin/chown" 171 #define MKDIR "/bin/mkdir" 172 #define MV "/bin/mv" 173 #define NOLOGIN "/sbin/nologin" 174 #define PAX "/bin/pax" 175 #define RM "/bin/rm" 176 177 #define UNSET_EXPIRY "Null (unset)" 178 179 static int asystem(const char *fmt, ...) 180 __attribute__((__format__(__printf__, 1, 2))); 181 182 static int verbose; 183 184 /* if *cpp is non-null, free it, then assign `n' chars of `s' to it */ 185 static void 186 memsave(char **cpp, char *s, size_t n) 187 { 188 if (*cpp != NULL) { 189 FREE(*cpp); 190 } 191 NEWARRAY(char, *cpp, n + 1, exit(1)); 192 (void) memcpy(*cpp, s, n); 193 (*cpp)[n] = '\0'; 194 } 195 196 /* a replacement for system(3) */ 197 static int 198 asystem(const char *fmt, ...) 199 { 200 va_list vp; 201 char buf[MaxCommandLen]; 202 int ret; 203 204 va_start(vp, fmt); 205 (void) vsnprintf(buf, sizeof(buf), fmt, vp); 206 va_end(vp); 207 if (verbose) { 208 (void) printf("Command: %s\n", buf); 209 } 210 if ((ret = system(buf)) != 0) { 211 warnx("[Warning] can't system `%s'", buf); 212 } 213 return ret; 214 } 215 216 /* remove a users home directory, returning 1 for success (ie, no problems encountered) */ 217 static int 218 removehomedir(const char *user, int uid, const char *dir) 219 { 220 struct stat st; 221 222 /* userid not root? */ 223 if (uid == 0) { 224 warnx("Not deleting home directory `%s'; userid is 0", dir); 225 return 0; 226 } 227 228 /* directory exists (and is a directory!) */ 229 if (stat(dir, &st) < 0) { 230 warnx("Home directory `%s' doesn't exist", dir); 231 return 0; 232 } 233 if (!S_ISDIR(st.st_mode)) { 234 warnx("Home directory `%s' is not a directory", dir); 235 return 0; 236 } 237 238 /* userid matches directory owner? */ 239 if (st.st_uid != uid) { 240 warnx("User `%s' doesn't own directory `%s', not removed\n", user, dir); 241 return 0; 242 } 243 244 (void) seteuid(uid); 245 /* we add the "|| true" to keep asystem() quiet if there is a non-zero exit status. */ 246 (void) asystem("%s -rf %s > /dev/null 2>&1 || true", RM, dir); 247 (void) seteuid(0); 248 if (rmdir(dir) < 0) { 249 warnx("Unable to remove all files in `%s'\n", dir); 250 return 0; 251 } 252 return 1; 253 } 254 255 #define NetBSD_1_4_K 104110000 256 257 #if defined(__NetBSD_Version__) && (__NetBSD_Version__ < NetBSD_1_4_K) 258 /* bounds checking strncpy */ 259 static int 260 strlcpy(char *to, char *from, size_t tosize) 261 { 262 size_t n; 263 int fromsize; 264 265 fromsize = strlen(from); 266 n = MIN(tosize - 1, fromsize); 267 (void) memcpy(to, from, n); 268 to[n] = '\0'; 269 return fromsize; 270 } 271 #endif /* NetBSD < 1.4K */ 272 273 /* 274 * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com> 275 * All rights reserved. 276 * 277 * Redistribution and use in source and binary forms, with or without 278 * modification, are permitted provided that the following conditions 279 * are met: 280 * 1. Redistributions of source code must retain the above copyright 281 * notice, this list of conditions and the following disclaimer. 282 * 2. Redistributions in binary form must reproduce the above copyright 283 * notice, this list of conditions and the following disclaimer in the 284 * documentation and/or other materials provided with the distribution. 285 * 3. The name of the author may not be used to endorse or promote products 286 * derived from this software without specific prior written permission. 287 * 288 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 289 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 290 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 291 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 292 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 293 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 294 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 295 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 296 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 297 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 298 */ 299 300 /* 301 * research has shown that NetBSD 1.3H was the first version of -current 302 * with asprintf in libc. agc 303 */ 304 #define NetBSD_1_3_H 103080000 305 306 #if defined(__NetBSD_Version__) && (__NetBSD_Version__ < NetBSD_1_3_H) 307 308 int 309 asprintf(char **str, char const *fmt, ...) 310 { 311 int ret; 312 va_list ap; 313 FILE f; 314 unsigned char *_base; 315 316 va_start(ap, fmt); 317 f._flags = __SWR | __SSTR | __SALC; 318 f._bf._base = f._p = (unsigned char *)malloc(128); 319 if (f._bf._base == NULL) 320 goto err; 321 f._bf._size = f._w = 127; /* Leave room for the NUL */ 322 ret = vfprintf(&f, fmt, ap); 323 if (ret == -1) 324 goto err; 325 *f._p = '\0'; 326 va_end(ap); 327 _base = realloc(f._bf._base, (size_t)(ret + 1)); 328 if (_base == NULL) 329 goto err; 330 *str = (char *)_base; 331 return (ret); 332 333 err: 334 if (f._bf._base) 335 free(f._bf._base); 336 *str = NULL; 337 return (-1); 338 } 339 #endif /* NetBSD < 1.3H */ 340 341 /* return 1 if all of `s' is numeric */ 342 static int 343 is_number(char *s) 344 { 345 for ( ; *s ; s++) { 346 if (!isdigit(*s)) { 347 return 0; 348 } 349 } 350 return 1; 351 } 352 353 /* 354 * check that the effective uid is 0 - called from funcs which will 355 * modify data and config files. 356 */ 357 static void 358 checkeuid(void) 359 { 360 if (geteuid() != 0) { 361 errx(EXIT_FAILURE, "Program must be run as root"); 362 } 363 } 364 365 /* copy any dot files into the user's home directory */ 366 static int 367 copydotfiles(char *skeldir, int uid, int gid, char *dir) 368 { 369 struct dirent *dp; 370 DIR *dirp; 371 int n; 372 373 if ((dirp = opendir(skeldir)) == NULL) { 374 warn("can't open source . files dir `%s'", skeldir); 375 return 0; 376 } 377 for (n = 0; (dp = readdir(dirp)) != NULL && n == 0 ; ) { 378 if (strcmp(dp->d_name, ".") == 0 || 379 strcmp(dp->d_name, "..") == 0) { 380 continue; 381 } 382 n = 1; 383 } 384 (void) closedir(dirp); 385 if (n == 0) { 386 warnx("No \"dot\" initialisation files found"); 387 } else { 388 (void) asystem("cd %s; %s -rw -pe %s . %s", 389 skeldir, PAX, (verbose) ? "-v" : "", dir); 390 } 391 (void) asystem("%s -R -h %d:%d %s", CHOWN, uid, gid, dir); 392 (void) asystem("%s -R u+w %s", CHMOD, dir); 393 return n; 394 } 395 396 /* create a group entry with gid `gid' */ 397 static int 398 creategid(char *group, int gid, char *name) 399 { 400 struct stat st; 401 FILE *from; 402 FILE *to; 403 char buf[MaxEntryLen]; 404 char f[MaxFileNameLen]; 405 int fd; 406 int cc; 407 408 if (getgrnam(group) != NULL) { 409 warnx("group `%s' already exists", group); 410 return 0; 411 } 412 if ((from = fopen(_PATH_GROUP, "r")) == NULL) { 413 warn("can't create gid for %s: can't open %s", name, _PATH_GROUP); 414 return 0; 415 } 416 if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { 417 warn("can't lock `%s'", _PATH_GROUP); 418 } 419 (void) fstat(fileno(from), &st); 420 (void) snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP); 421 if ((fd = mkstemp(f)) < 0) { 422 (void) fclose(from); 423 warn("can't create gid: mkstemp failed"); 424 return 0; 425 } 426 if ((to = fdopen(fd, "w")) == NULL) { 427 (void) fclose(from); 428 (void) close(fd); 429 (void) unlink(f); 430 warn("can't create gid: fdopen `%s' failed", f); 431 return 0; 432 } 433 while ((cc = fread(buf, sizeof(char), sizeof(buf), from)) > 0) { 434 if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) { 435 (void) fclose(from); 436 (void) close(fd); 437 (void) unlink(f); 438 warn("can't create gid: short write to `%s'", f); 439 return 0; 440 } 441 } 442 (void) fprintf(to, "%s:*:%d:%s\n", group, gid, name); 443 (void) fclose(from); 444 (void) fclose(to); 445 if (rename(f, _PATH_GROUP) < 0) { 446 warn("can't create gid: can't rename `%s' to `%s'", f, _PATH_GROUP); 447 return 0; 448 } 449 (void) chmod(_PATH_GROUP, st.st_mode & 07777); 450 return 1; 451 } 452 453 /* modify the group entry with name `group' to be newent */ 454 static int 455 modify_gid(char *group, char *newent) 456 { 457 struct stat st; 458 FILE *from; 459 FILE *to; 460 char buf[MaxEntryLen]; 461 char f[MaxFileNameLen]; 462 char *colon; 463 int groupc; 464 int entc; 465 int fd; 466 int cc; 467 468 if ((from = fopen(_PATH_GROUP, "r")) == NULL) { 469 warn("can't create gid for %s: can't open %s", group, _PATH_GROUP); 470 return 0; 471 } 472 if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { 473 warn("can't lock `%s'", _PATH_GROUP); 474 } 475 (void) fstat(fileno(from), &st); 476 (void) snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP); 477 if ((fd = mkstemp(f)) < 0) { 478 (void) fclose(from); 479 warn("can't create gid: mkstemp failed"); 480 return 0; 481 } 482 if ((to = fdopen(fd, "w")) == NULL) { 483 (void) fclose(from); 484 (void) close(fd); 485 (void) unlink(f); 486 warn("can't create gid: fdopen `%s' failed", f); 487 return 0; 488 } 489 groupc = strlen(group); 490 while (fgets(buf, sizeof(buf), from) != NULL) { 491 cc = strlen(buf); 492 if ((colon = strchr(buf, ':')) == NULL) { 493 warn("badly formed entry `%s'", buf); 494 continue; 495 } 496 entc = (int)(colon - buf); 497 if (entc == groupc && strncmp(group, buf, (unsigned) entc) == 0) { 498 if (newent == NULL) { 499 continue; 500 } else { 501 cc = strlen(newent); 502 (void) strlcpy(buf, newent, sizeof(buf)); 503 } 504 } 505 if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) { 506 (void) fclose(from); 507 (void) close(fd); 508 (void) unlink(f); 509 warn("can't create gid: short write to `%s'", f); 510 return 0; 511 } 512 } 513 (void) fclose(from); 514 (void) fclose(to); 515 if (rename(f, _PATH_GROUP) < 0) { 516 warn("can't create gid: can't rename `%s' to `%s'", f, _PATH_GROUP); 517 return 0; 518 } 519 (void) chmod(_PATH_GROUP, st.st_mode & 07777); 520 return 1; 521 } 522 523 /* modify the group entries for all `groups', by adding `user' */ 524 static int 525 append_group(char *user, int ngroups, char **groups) 526 { 527 struct group *grp; 528 struct stat st; 529 FILE *from; 530 FILE *to; 531 char buf[MaxEntryLen]; 532 char f[MaxFileNameLen]; 533 char *colon; 534 int groupc; 535 int entc; 536 int fd; 537 int nc; 538 int cc; 539 int i; 540 int j; 541 542 for (i = 0 ; i < ngroups ; i++) { 543 if ((grp = getgrnam(groups[i])) == NULL) { 544 warnx("can't append group `%s' for user `%s'", groups[i], user); 545 } else { 546 for (j = 0 ; grp->gr_mem[j] ; j++) { 547 if (strcmp(user, grp->gr_mem[j]) == 0) { 548 /* already in it */ 549 groups[i] = ""; 550 } 551 } 552 } 553 } 554 if ((from = fopen(_PATH_GROUP, "r")) == NULL) { 555 warn("can't append group for %s: can't open %s", user, _PATH_GROUP); 556 return 0; 557 } 558 if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { 559 warn("can't lock `%s'", _PATH_GROUP); 560 } 561 (void) fstat(fileno(from), &st); 562 (void) snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP); 563 if ((fd = mkstemp(f)) < 0) { 564 (void) fclose(from); 565 warn("can't create gid: mkstemp failed"); 566 return 0; 567 } 568 if ((to = fdopen(fd, "w")) == NULL) { 569 (void) fclose(from); 570 (void) close(fd); 571 (void) unlink(f); 572 warn("can't create gid: fdopen `%s' failed", f); 573 return 0; 574 } 575 while (fgets(buf, sizeof(buf), from) != NULL) { 576 cc = strlen(buf); 577 if ((colon = strchr(buf, ':')) == NULL) { 578 warn("badly formed entry `%s'", buf); 579 continue; 580 } 581 entc = (int)(colon - buf); 582 for (i = 0 ; i < ngroups ; i++) { 583 if ((groupc = strlen(groups[i])) == 0) { 584 continue; 585 } 586 if (entc == groupc && strncmp(groups[i], buf, (unsigned) entc) == 0) { 587 if ((nc = snprintf(&buf[cc - 1], 588 sizeof(buf) - cc + 1, 589 "%s%s\n", 590 (buf[cc - 2] == ':') ? "" : ",", 591 user)) < 0) { 592 warnx("Warning: group `%s' entry too long", groups[i]); 593 } 594 cc += nc - 1; 595 } 596 } 597 if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) { 598 (void) fclose(from); 599 (void) close(fd); 600 (void) unlink(f); 601 warn("can't create gid: short write to `%s'", f); 602 return 0; 603 } 604 } 605 (void) fclose(from); 606 (void) fclose(to); 607 if (rename(f, _PATH_GROUP) < 0) { 608 warn("can't create gid: can't rename `%s' to `%s'", f, _PATH_GROUP); 609 return 0; 610 } 611 (void) chmod(_PATH_GROUP, st.st_mode & 07777); 612 return 1; 613 } 614 615 /* return 1 if `login' is a valid login name */ 616 static int 617 valid_login(char *login) 618 { 619 char *cp; 620 621 for (cp = login ; *cp ; cp++) { 622 if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-') { 623 return 0; 624 } 625 } 626 return 1; 627 } 628 629 /* return 1 if `group' is a valid group name */ 630 static int 631 valid_group(char *group) 632 { 633 char *cp; 634 635 for (cp = group ; *cp ; cp++) { 636 if (!isalnum(*cp)) { 637 return 0; 638 } 639 } 640 return 1; 641 } 642 643 /* find the next gid in the range lo .. hi */ 644 static int 645 getnextgid(int *gidp, int lo, int hi) 646 { 647 for (*gidp = lo ; *gidp < hi ; *gidp += 1) { 648 if (getgrgid((gid_t)*gidp) == NULL) { 649 return 1; 650 } 651 } 652 return 0; 653 } 654 655 #ifdef EXTENSIONS 656 /* save a range of uids */ 657 static int 658 save_range(user_t *up, char *cp) 659 { 660 int from; 661 int to; 662 int i; 663 664 if (up->u_rsize == 0) { 665 up->u_rsize = 32; 666 NEWARRAY(range_t, up->u_rv, up->u_rsize, return(0)); 667 } else if (up->u_rc == up->u_rsize) { 668 up->u_rsize *= 2; 669 RENEW(range_t, up->u_rv, up->u_rsize, return(0)); 670 } 671 if (up->u_rv && sscanf(cp, "%d..%d", &from, &to) == 2) { 672 for (i = 0 ; i < up->u_rc ; i++) { 673 if (up->u_rv[i].r_from == from && up->u_rv[i].r_to == to) { 674 break; 675 } 676 } 677 if (i == up->u_rc) { 678 up->u_rv[up->u_rc].r_from = from; 679 up->u_rv[up->u_rc].r_to = to; 680 up->u_rc += 1; 681 } 682 } else { 683 warnx("Bad range `%s'", cp); 684 return 0; 685 } 686 return 1; 687 } 688 #endif 689 690 /* set the defaults in the defaults file */ 691 static int 692 setdefaults(user_t *up) 693 { 694 char template[MaxFileNameLen]; 695 FILE *fp; 696 int ret; 697 int fd; 698 int i; 699 700 (void) snprintf(template, sizeof(template), "%s.XXXXXX", CONFFILE); 701 if ((fd = mkstemp(template)) < 0) { 702 warnx("can't mkstemp `%s' for writing", CONFFILE); 703 return 0; 704 } 705 if ((fp = fdopen(fd, "w")) == NULL) { 706 warn("can't fdopen `%s' for writing", CONFFILE); 707 return 0; 708 } 709 ret = 1; 710 if (fprintf(fp, "group\t\t%s\n", up->u_primgrp) <= 0 || 711 fprintf(fp, "base_dir\t%s\n", up->u_basedir) <= 0 || 712 fprintf(fp, "skel_dir\t%s\n", up->u_skeldir) <= 0 || 713 fprintf(fp, "shell\t\t%s\n", up->u_shell) <= 0 || 714 fprintf(fp, "inactive\t%d\n", up->u_inactive) <= 0 || 715 fprintf(fp, "expire\t\t%s\n", (up->u_expire == NULL) ? UNSET_EXPIRY : up->u_expire) <= 0 || 716 fprintf(fp, "preserve\t%s\n", (up->u_preserve == 0) ? "false" : "true") <= 0) { 717 warn("can't write to `%s'", CONFFILE); 718 ret = 0; 719 } 720 #ifdef EXTENSIONS 721 for (i = (up->u_defrc != up->u_rc) ? up->u_defrc : 0 ; i < up->u_rc ; i++) { 722 if (fprintf(fp, "range\t\t%d..%d\n", up->u_rv[i].r_from, up->u_rv[i].r_to) <= 0) { 723 warn("can't write to `%s'", CONFFILE); 724 ret = 0; 725 } 726 } 727 #endif 728 (void) fclose(fp); 729 if (ret) { 730 ret = ((rename(template, CONFFILE) == 0) && (chmod(CONFFILE, 0644) == 0)); 731 } 732 return ret; 733 } 734 735 /* read the defaults file */ 736 static void 737 read_defaults(user_t *up) 738 { 739 struct stat st; 740 size_t lineno; 741 size_t len; 742 FILE *fp; 743 char *cp; 744 char *s; 745 746 memsave(&up->u_primgrp, DEF_GROUP, strlen(DEF_GROUP)); 747 memsave(&up->u_basedir, DEF_BASEDIR, strlen(DEF_BASEDIR)); 748 memsave(&up->u_skeldir, DEF_SKELDIR, strlen(DEF_SKELDIR)); 749 memsave(&up->u_shell, DEF_SHELL, strlen(DEF_SHELL)); 750 memsave(&up->u_comment, DEF_COMMENT, strlen(DEF_COMMENT)); 751 up->u_rsize = 16; 752 NEWARRAY(range_t, up->u_rv, up->u_rsize, exit(1)); 753 up->u_inactive = DEF_INACTIVE; 754 up->u_expire = DEF_EXPIRE; 755 if ((fp = fopen(CONFFILE, "r")) == NULL) { 756 if (stat(CONFFILE, &st) < 0 && !setdefaults(up)) { 757 warn("can't create `%s' defaults file", CONFFILE); 758 } 759 fp = fopen(CONFFILE, "r"); 760 } 761 if (fp != NULL) { 762 while ((s = fparseln(fp, &len, &lineno, NULL, 0)) != NULL) { 763 if (strncmp(s, "group", 5) == 0) { 764 for (cp = s + 5 ; *cp && isspace(*cp) ; cp++) { 765 } 766 memsave(&up->u_primgrp, cp, strlen(cp)); 767 } else if (strncmp(s, "base_dir", 8) == 0) { 768 for (cp = s + 8 ; *cp && isspace(*cp) ; cp++) { 769 } 770 memsave(&up->u_basedir, cp, strlen(cp)); 771 } else if (strncmp(s, "skel_dir", 8) == 0) { 772 for (cp = s + 8 ; *cp && isspace(*cp) ; cp++) { 773 } 774 memsave(&up->u_skeldir, cp, strlen(cp)); 775 } else if (strncmp(s, "shell", 5) == 0) { 776 for (cp = s + 5 ; *cp && isspace(*cp) ; cp++) { 777 } 778 memsave(&up->u_shell, cp, strlen(cp)); 779 } else if (strncmp(s, "inactive", 8) == 0) { 780 for (cp = s + 8 ; *cp && isspace(*cp) ; cp++) { 781 } 782 up->u_inactive = atoi(cp); 783 #ifdef EXTENSIONS 784 } else if (strncmp(s, "range", 5) == 0) { 785 for (cp = s + 5 ; *cp && isspace(*cp) ; cp++) { 786 } 787 (void) save_range(up, cp); 788 #endif 789 #ifdef EXTENSIONS 790 } else if (strncmp(s, "preserve", 8) == 0) { 791 for (cp = s + 8 ; *cp && isspace(*cp) ; cp++) { 792 } 793 up->u_preserve = (strncmp(cp, "true", 4) == 0) ? 1 : 794 (strncmp(cp, "yes", 3) == 0) ? 1 : 795 atoi(cp); 796 #endif 797 } else if (strncmp(s, "expire", 6) == 0) { 798 for (cp = s + 6 ; *cp && isspace(*cp) ; cp++) { 799 } 800 if (strcmp(cp, UNSET_EXPIRY) == 0) { 801 if (up->u_expire) { 802 FREE(up->u_expire); 803 } 804 up->u_expire = NULL; 805 } else { 806 memsave(&up->u_expire, cp, strlen(cp)); 807 } 808 } 809 (void) free(s); 810 } 811 (void) fclose(fp); 812 } 813 if (up->u_rc == 0) { 814 up->u_rv[up->u_rc].r_from = DEF_LOWUID; 815 up->u_rv[up->u_rc].r_to = DEF_HIGHUID; 816 up->u_rc += 1; 817 } 818 up->u_defrc = up->u_rc; 819 } 820 821 /* return the next valid unused uid */ 822 static int 823 getnextuid(int sync_uid_gid, int *uid, int low_uid, int high_uid) 824 { 825 for (*uid = low_uid ; *uid <= high_uid ; (*uid)++) { 826 if (getpwuid((uid_t)(*uid)) == NULL && *uid != NOBODY_UID) { 827 if (sync_uid_gid) { 828 if (getgrgid((gid_t)(*uid)) == NULL) { 829 return 1; 830 } 831 } else { 832 return 1; 833 } 834 } 835 } 836 return 0; 837 } 838 839 /* add a user */ 840 static int 841 adduser(char *login, user_t *up) 842 { 843 struct group *grp; 844 struct stat st; 845 struct tm tm; 846 time_t expire; 847 char password[PasswordLength + 1]; 848 char home[MaxFileNameLen]; 849 char buf[MaxFileNameLen]; 850 int sync_uid_gid; 851 int masterfd; 852 int ptmpfd; 853 int gid; 854 int cc; 855 int i; 856 857 if (!valid_login(login)) { 858 errx(EXIT_FAILURE, "`%s' is not a valid login name", login); 859 } 860 if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) { 861 err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD); 862 } 863 if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) { 864 err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD); 865 } 866 pw_init(); 867 if ((ptmpfd = pw_lock(WAITSECS)) < 0) { 868 (void) close(masterfd); 869 err(EXIT_FAILURE, "can't obtain pw_lock"); 870 } 871 while ((cc = read(masterfd, buf, sizeof(buf))) > 0) { 872 if (write(ptmpfd, buf, (size_t)(cc)) != cc) { 873 (void) close(masterfd); 874 (void) close(ptmpfd); 875 (void) pw_abort(); 876 err(EXIT_FAILURE, "short write to /etc/ptmp (not %d chars)", cc); 877 } 878 } 879 /* if no uid was specified, get next one in [low_uid..high_uid] range */ 880 sync_uid_gid = (strcmp(up->u_primgrp, "=uid") == 0); 881 if (up->u_uid == -1) { 882 for (i = 0 ; i < up->u_rc ; i++) { 883 if (getnextuid(sync_uid_gid, &up->u_uid, up->u_rv[i].r_from, up->u_rv[i].r_to)) { 884 break; 885 } 886 } 887 if (i == up->u_rc) { 888 (void) close(ptmpfd); 889 (void) pw_abort(); 890 errx(EXIT_FAILURE, "can't get next uid for %d", up->u_uid); 891 } 892 } 893 /* check uid isn't already allocated */ 894 if (!(up->u_flags & F_DUPUID) && getpwuid((uid_t)(up->u_uid)) != NULL) { 895 (void) close(ptmpfd); 896 (void) pw_abort(); 897 errx(EXIT_FAILURE, "uid %d is already in use", up->u_uid); 898 } 899 /* if -g=uid was specified, check gid is unused */ 900 if (sync_uid_gid) { 901 if (getgrgid((gid_t)(up->u_uid)) != NULL) { 902 (void) close(ptmpfd); 903 (void) pw_abort(); 904 errx(EXIT_FAILURE, "gid %d is already in use", up->u_uid); 905 } 906 gid = up->u_uid; 907 } else if ((grp = getgrnam(up->u_primgrp)) != NULL) { 908 gid = grp->gr_gid; 909 } else if (is_number(up->u_primgrp) && 910 (grp = getgrgid((gid_t)atoi(up->u_primgrp))) != NULL) { 911 gid = grp->gr_gid; 912 } else { 913 (void) close(ptmpfd); 914 (void) pw_abort(); 915 errx(EXIT_FAILURE, "group %s not found", up->u_primgrp); 916 } 917 /* check name isn't already in use */ 918 if (!(up->u_flags & F_DUPUID) && getpwnam(login) != NULL) { 919 (void) close(ptmpfd); 920 (void) pw_abort(); 921 errx(EXIT_FAILURE, "already a `%s' user", login); 922 } 923 if (up->u_flags & F_HOMEDIR) { 924 (void) strlcpy(home, up->u_home, sizeof(home)); 925 } else { 926 /* if home directory hasn't been given, make it up */ 927 (void) snprintf(home, sizeof(home), "%s/%s", up->u_basedir, login); 928 } 929 expire = 0; 930 if (up->u_expire != NULL) { 931 (void) memset(&tm, 0, sizeof(tm)); 932 if (strptime(up->u_expire, "%c", &tm) == NULL) { 933 warnx("invalid time format `%s'", optarg); 934 } else { 935 expire = mktime(&tm); 936 } 937 } 938 if (lstat(home, &st) < 0 && !(up->u_flags & F_MKDIR)) { 939 warnx("Warning: home directory `%s' doesn't exist, and -m was not specified", 940 home); 941 } 942 password[PasswordLength] = '\0'; 943 if (up->u_password != NULL && 944 strlen(up->u_password) == PasswordLength) { 945 (void) memcpy(password, up->u_password, PasswordLength); 946 } else { 947 (void) memset(password, '*', PasswordLength); 948 if (up->u_password != NULL) { 949 warnx("Password `%s' is invalid: setting it to `%s'", 950 up->u_password, password); 951 } 952 } 953 cc = snprintf(buf, sizeof(buf), "%s:%s:%d:%d::%d:%ld:%s:%s:%s\n", 954 login, 955 password, 956 up->u_uid, 957 gid, 958 up->u_inactive, 959 (long) expire, 960 up->u_comment, 961 home, 962 up->u_shell); 963 if (write(ptmpfd, buf, (size_t) cc) != cc) { 964 (void) close(ptmpfd); 965 (void) pw_abort(); 966 err(EXIT_FAILURE, "can't add `%s'", buf); 967 } 968 if (up->u_flags & F_MKDIR) { 969 if (lstat(home, &st) == 0) { 970 (void) close(ptmpfd); 971 (void) pw_abort(); 972 errx(EXIT_FAILURE, "home directory `%s' already exists", home); 973 } else { 974 if (asystem("%s -p %s", MKDIR, home) != 0) { 975 (void) close(ptmpfd); 976 (void) pw_abort(); 977 err(EXIT_FAILURE, "can't mkdir `%s'", home); 978 } 979 (void) copydotfiles(up->u_skeldir, up->u_uid, gid, home); 980 } 981 } 982 if (strcmp(up->u_primgrp, "=uid") == 0 && 983 getgrnam(login) == NULL && 984 !creategid(login, gid, login)) { 985 (void) close(ptmpfd); 986 (void) pw_abort(); 987 err(EXIT_FAILURE, "can't create gid %d for login name %s", gid, login); 988 } 989 if (up->u_groupc > 0 && !append_group(login, up->u_groupc, up->u_groupv)) { 990 (void) close(ptmpfd); 991 (void) pw_abort(); 992 errx(EXIT_FAILURE, "can't append `%s' to new groups", login); 993 } 994 (void) close(ptmpfd); 995 if (pw_mkdb() < 0) { 996 err(EXIT_FAILURE, "pw_mkdb failed"); 997 } 998 return 1; 999 } 1000 1001 /* modify a user */ 1002 static int 1003 moduser(char *login, char *newlogin, user_t *up) 1004 { 1005 struct passwd *pwp, *newpwp; 1006 struct group *grp; 1007 struct tm tm; 1008 size_t colonc, len, loginc; 1009 FILE *master; 1010 char *buf, *colon, *line; 1011 int masterfd; 1012 int ptmpfd; 1013 1014 if (!valid_login(newlogin)) { 1015 errx(EXIT_FAILURE, "`%s' is not a valid login name", login); 1016 } 1017 if ((pwp = getpwnam(login)) == NULL) { 1018 errx(EXIT_FAILURE, "No such user `%s'", login); 1019 } 1020 newpwp = pwp; 1021 if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) { 1022 err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD); 1023 } 1024 if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) { 1025 err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD); 1026 } 1027 pw_init(); 1028 if ((ptmpfd = pw_lock(WAITSECS)) < 0) { 1029 (void) close(masterfd); 1030 err(EXIT_FAILURE, "can't obtain pw_lock"); 1031 } 1032 if ((master = fdopen(masterfd, "r")) == NULL) { 1033 (void) close(masterfd); 1034 (void) close(ptmpfd); 1035 (void) pw_abort(); 1036 err(EXIT_FAILURE, "can't fdopen fd for %s", _PATH_MASTERPASSWD); 1037 } 1038 if (up != NULL) { 1039 if (up->u_flags & F_USERNAME) { 1040 /* if changing name, check new name isn't already in use */ 1041 if (strcmp(login, newlogin) != 0 && getpwnam(newlogin) != NULL) { 1042 (void) close(ptmpfd); 1043 (void) pw_abort(); 1044 errx(EXIT_FAILURE, "already a `%s' user", newlogin); 1045 } 1046 newpwp->pw_name = newlogin; 1047 } 1048 if (up->u_flags & F_PASSWORD) { 1049 if (up->u_password != NULL && strlen(up->u_password) == PasswordLength) 1050 newpwp->pw_passwd = up->u_password; 1051 } 1052 if (up->u_flags & F_UID) { 1053 /* check uid isn't already allocated */ 1054 if (!(up->u_flags & F_DUPUID) && getpwuid((uid_t)(up->u_uid)) != NULL) { 1055 (void) close(ptmpfd); 1056 (void) pw_abort(); 1057 errx(EXIT_FAILURE, "uid %d is already in use", up->u_uid); 1058 } 1059 newpwp->pw_uid = up->u_uid; 1060 } 1061 if (up->u_flags & F_GROUP) { 1062 /* if -g=uid was specified, check gid is unused */ 1063 if (strcmp(up->u_primgrp, "=uid") == 0) { 1064 if (getgrgid((gid_t)(up->u_uid)) != NULL) { 1065 (void) close(ptmpfd); 1066 (void) pw_abort(); 1067 errx(EXIT_FAILURE, "gid %d is already in use", up->u_uid); 1068 } 1069 newpwp->pw_gid = up->u_uid; 1070 } else if ((grp = getgrnam(up->u_primgrp)) != NULL) { 1071 newpwp->pw_gid = grp->gr_gid; 1072 } else if (is_number(up->u_primgrp) && 1073 (grp = getgrgid((gid_t)atoi(up->u_primgrp))) != NULL) { 1074 newpwp->pw_gid = grp->gr_gid; 1075 } else { 1076 (void) close(ptmpfd); 1077 (void) pw_abort(); 1078 errx(EXIT_FAILURE, "group %s not found", up->u_primgrp); 1079 } 1080 } 1081 if (up->u_flags |= F_INACTIVE) 1082 newpwp->pw_change = up->u_inactive; 1083 if (up->u_flags & F_EXPIRE) { 1084 (void) memset(&tm, 0, sizeof(tm)); 1085 if (strptime(up->u_expire, "%c", &tm) == NULL) 1086 warnx("invalid time format `%s'", optarg); 1087 else 1088 newpwp->pw_expire = mktime(&tm); 1089 } 1090 if (up->u_flags & F_COMMENT) 1091 newpwp->pw_gecos = up->u_comment; 1092 if (up->u_flags & F_HOMEDIR) 1093 newpwp->pw_dir = up->u_home; 1094 if (up->u_flags & F_SHELL) 1095 newpwp->pw_shell = up->u_shell; 1096 } 1097 loginc = strlen(login); 1098 while ((line = fgetln(master, &len)) != NULL) { 1099 if ((colon = strchr(line, ':')) == NULL) { 1100 warnx("Malformed entry `%s'. Skipping", line); 1101 continue; 1102 } 1103 colonc = (size_t)(colon - line); 1104 if (strncmp(login, line, loginc) == 0 && loginc == colonc) { 1105 if (up != NULL) { 1106 len = (int)asprintf(&buf, "%s:%s:%d:%d::%ld:%ld:%s:%s:%s\n", 1107 newlogin, 1108 newpwp->pw_passwd, 1109 newpwp->pw_uid, 1110 newpwp->pw_gid, 1111 (long)newpwp->pw_change, 1112 (long)newpwp->pw_expire, 1113 newpwp->pw_gecos, 1114 newpwp->pw_dir, 1115 newpwp->pw_shell); 1116 if (write(ptmpfd, buf, len) != len) { 1117 (void) close(ptmpfd); 1118 (void) pw_abort(); 1119 err(EXIT_FAILURE, "can't add `%s'", buf); 1120 } 1121 } 1122 /* } else if (write(ptmpfd, line, len) != len) { */ 1123 } else if ((colonc = write(ptmpfd, line, len)) != len) { 1124 (void) close(masterfd); 1125 (void) close(ptmpfd); 1126 (void) pw_abort(); 1127 err(EXIT_FAILURE, "short write to /etc/ptmp (%lld not %lld chars)", 1128 (long long)colonc, 1129 (long long)len); 1130 } 1131 } 1132 if (up != NULL) { 1133 if ((up->u_flags & F_MKDIR) && 1134 asystem("%s %s %s", MV, pwp->pw_dir, newpwp->pw_dir) != 0) { 1135 (void) close(ptmpfd); 1136 (void) pw_abort(); 1137 err(EXIT_FAILURE, "can't move `%s' to `%s'", 1138 pwp->pw_dir, newpwp->pw_dir); 1139 } 1140 if (up->u_groupc > 0 && 1141 !append_group(newlogin, up->u_groupc, up->u_groupv)) { 1142 (void) close(ptmpfd); 1143 (void) pw_abort(); 1144 errx(EXIT_FAILURE, "can't append `%s' to new groups", 1145 newlogin); 1146 } 1147 } 1148 (void) close(ptmpfd); 1149 if (pw_mkdb() < 0) { 1150 err(EXIT_FAILURE, "pw_mkdb failed"); 1151 } 1152 return 1; 1153 } 1154 1155 1156 #ifdef EXTENSIONS 1157 /* see if we can find out the user struct */ 1158 static struct passwd * 1159 find_user_info(char *name) 1160 { 1161 struct passwd *pwp; 1162 1163 if ((pwp = getpwnam(name)) != NULL) { 1164 return pwp; 1165 } 1166 if (is_number(name) && (pwp = getpwuid((uid_t)atoi(name))) != NULL) { 1167 return pwp; 1168 } 1169 return NULL; 1170 } 1171 #endif 1172 1173 #ifdef EXTENSIONS 1174 /* see if we can find out the group struct */ 1175 static struct group * 1176 find_group_info(char *name) 1177 { 1178 struct group *grp; 1179 1180 if ((grp = getgrnam(name)) != NULL) { 1181 return grp; 1182 } 1183 if (is_number(name) && (grp = getgrgid((gid_t)atoi(name))) != NULL) { 1184 return grp; 1185 } 1186 return NULL; 1187 } 1188 #endif 1189 1190 /* print out usage message, and then exit */ 1191 void 1192 usermgmt_usage(char *prog) 1193 { 1194 if (strcmp(prog, "useradd") == 0) { 1195 (void) fprintf(stderr, "Usage: %s -D [-b basedir] [-e expiry] " 1196 "[-f inactive] [-g group]\n\t[-r lowuid..highuid] " 1197 "[-s shell]\n", prog); 1198 (void) fprintf(stderr, "Usage: %s [-G group] [-b basedir] " 1199 "[-c comment] [-d homedir] [-e expiry]\n\t[-f inactive] " 1200 "[-g group] [-k skeletondir] [-m] [-o] [-p password]\n" 1201 "\t[-r lowuid..highuid] [-s shell]\n\t[-u uid] [-v] user\n", 1202 prog); 1203 } else if (strcmp(prog, "usermod") == 0) { 1204 (void) fprintf(stderr, "Usage: %s [-G group] [-c comment] " 1205 "[-d homedir] [-e expire] [-f inactive]\n\t[-g group] " 1206 "[-l newname] [-m] [-o] [-p password] [-s shell] [-u uid]\n" 1207 "\t[-v] user\n", prog); 1208 } else if (strcmp(prog, "userdel") == 0) { 1209 (void) fprintf(stderr, "Usage: %s -D [-p preserve]\n", prog); 1210 (void) fprintf(stderr, 1211 "Usage: %s [-p preserve] [-r] [-v] user\n", prog); 1212 #ifdef EXTENSIONS 1213 } else if (strcmp(prog, "userinfo") == 0) { 1214 (void) fprintf(stderr, "Usage: %s [-e] [-v] user\n", prog); 1215 #endif 1216 } else if (strcmp(prog, "groupadd") == 0) { 1217 (void) fprintf(stderr, "Usage: %s [-g gid] [-o] [-v] group\n", 1218 prog); 1219 } else if (strcmp(prog, "groupdel") == 0) { 1220 (void) fprintf(stderr, "Usage: %s [-v] group\n", prog); 1221 } else if (strcmp(prog, "groupmod") == 0) { 1222 (void) fprintf(stderr, 1223 "Usage: %s [-g gid] [-o] [-n newname] [-v] group\n", prog); 1224 } else if (strcmp(prog, "user") == 0 || strcmp(prog, "group") == 0) { 1225 (void) fprintf(stderr, 1226 "Usage: %s ( add | del | mod | info ) ...\n", prog); 1227 #ifdef EXTENSIONS 1228 } else if (strcmp(prog, "groupinfo") == 0) { 1229 (void) fprintf(stderr, "Usage: %s [-e] [-v] group\n", prog); 1230 #endif 1231 } 1232 exit(EXIT_FAILURE); 1233 /* NOTREACHED */ 1234 } 1235 1236 #ifdef EXTENSIONS 1237 #define ADD_OPT_EXTENSIONS "p:r:v" 1238 #else 1239 #define ADD_OPT_EXTENSIONS 1240 #endif 1241 1242 int 1243 useradd(int argc, char **argv) 1244 { 1245 user_t u; 1246 int defaultfield; 1247 int bigD; 1248 int c; 1249 int i; 1250 1251 (void) memset(&u, 0, sizeof(u)); 1252 read_defaults(&u); 1253 u.u_uid = -1; 1254 defaultfield = bigD = 0; 1255 while ((c = getopt(argc, argv, "DG:b:c:d:e:f:g:k:mou:s:" ADD_OPT_EXTENSIONS)) != -1) { 1256 switch(c) { 1257 case 'D': 1258 bigD = 1; 1259 break; 1260 case 'G': 1261 while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && 1262 u.u_groupc < NGROUPS_MAX) { 1263 if (u.u_groupv[u.u_groupc][0] != 0) { 1264 u.u_groupc++; 1265 } 1266 } 1267 if (optarg != NULL) { 1268 warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX); 1269 } 1270 break; 1271 case 'b': 1272 defaultfield = 1; 1273 memsave(&u.u_basedir, optarg, strlen(optarg)); 1274 break; 1275 case 'c': 1276 memsave(&u.u_comment, optarg, strlen(optarg)); 1277 break; 1278 case 'd': 1279 memsave(&u.u_home, optarg, strlen(optarg)); 1280 u.u_flags |= F_HOMEDIR; 1281 break; 1282 case 'e': 1283 defaultfield = 1; 1284 memsave(&u.u_expire, optarg, strlen(optarg)); 1285 break; 1286 case 'f': 1287 defaultfield = 1; 1288 u.u_inactive = atoi(optarg); 1289 break; 1290 case 'g': 1291 defaultfield = 1; 1292 memsave(&u.u_primgrp, optarg, strlen(optarg)); 1293 break; 1294 case 'k': 1295 memsave(&u.u_skeldir, optarg, strlen(optarg)); 1296 break; 1297 case 'm': 1298 u.u_flags |= F_MKDIR; 1299 break; 1300 case 'o': 1301 u.u_flags |= F_DUPUID; 1302 break; 1303 #ifdef EXTENSIONS 1304 case 'p': 1305 memsave(&u.u_password, optarg, strlen(optarg)); 1306 break; 1307 #endif 1308 #ifdef EXTENSIONS 1309 case 'r': 1310 defaultfield = 1; 1311 (void) save_range(&u, optarg); 1312 break; 1313 #endif 1314 case 's': 1315 defaultfield = 1; 1316 memsave(&u.u_shell, optarg, strlen(optarg)); 1317 break; 1318 case 'u': 1319 if (!is_number(optarg)) { 1320 errx(EXIT_FAILURE, "When using [-u uid], the uid must be numeric"); 1321 } 1322 u.u_uid = atoi(optarg); 1323 break; 1324 #ifdef EXTENSIONS 1325 case 'v': 1326 verbose = 1; 1327 break; 1328 #endif 1329 } 1330 } 1331 if (bigD) { 1332 if (defaultfield) { 1333 checkeuid(); 1334 return setdefaults(&u) ? EXIT_SUCCESS : EXIT_FAILURE; 1335 } 1336 (void) printf("group\t\t%s\n", u.u_primgrp); 1337 (void) printf("base_dir\t%s\n", u.u_basedir); 1338 (void) printf("skel_dir\t%s\n", u.u_skeldir); 1339 (void) printf("shell\t\t%s\n", u.u_shell); 1340 (void) printf("inactive\t%d\n", u.u_inactive); 1341 (void) printf("expire\t\t%s\n", (u.u_expire == NULL) ? UNSET_EXPIRY : u.u_expire); 1342 #ifdef EXTENSIONS 1343 for (i = 0 ; i < u.u_rc ; i++) { 1344 (void) printf("range\t\t%d..%d\n", u.u_rv[i].r_from, u.u_rv[i].r_to); 1345 } 1346 #endif 1347 return EXIT_SUCCESS; 1348 } 1349 if (argc == optind) { 1350 usermgmt_usage("useradd"); 1351 } 1352 checkeuid(); 1353 return adduser(argv[optind], &u) ? EXIT_SUCCESS : EXIT_FAILURE; 1354 } 1355 1356 #ifdef EXTENSIONS 1357 #define MOD_OPT_EXTENSIONS "p:v" 1358 #else 1359 #define MOD_OPT_EXTENSIONS 1360 #endif 1361 1362 int 1363 usermod(int argc, char **argv) 1364 { 1365 user_t u; 1366 char newuser[MaxUserNameLen + 1]; 1367 int c, have_new_user; 1368 1369 (void) memset(&u, 0, sizeof(u)); 1370 (void) memset(newuser, 0, sizeof(newuser)); 1371 read_defaults(&u); 1372 have_new_user = 0; 1373 while ((c = getopt(argc, argv, "G:c:d:e:f:g:l:mos:u:" MOD_OPT_EXTENSIONS)) != -1) { 1374 switch(c) { 1375 case 'G': 1376 while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && 1377 u.u_groupc < NGROUPS_MAX) { 1378 if (u.u_groupv[u.u_groupc][0] != 0) { 1379 u.u_groupc++; 1380 } 1381 } 1382 if (optarg != NULL) { 1383 warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX); 1384 } 1385 u.u_flags |= F_SECGROUP; 1386 break; 1387 case 'c': 1388 memsave(&u.u_comment, optarg, strlen(optarg)); 1389 u.u_flags |= F_COMMENT; 1390 break; 1391 case 'd': 1392 memsave(&u.u_home, optarg, strlen(optarg)); 1393 u.u_flags |= F_HOMEDIR; 1394 break; 1395 case 'e': 1396 memsave(&u.u_expire, optarg, strlen(optarg)); 1397 u.u_flags |= F_EXPIRE; 1398 break; 1399 case 'f': 1400 u.u_inactive = atoi(optarg); 1401 u.u_flags |= F_INACTIVE; 1402 break; 1403 case 'g': 1404 memsave(&u.u_primgrp, optarg, strlen(optarg)); 1405 u.u_flags |= F_GROUP; 1406 break; 1407 case 'l': 1408 (void) strlcpy(newuser, optarg, sizeof(newuser)); 1409 have_new_user = 1; 1410 u.u_flags |= F_USERNAME; 1411 break; 1412 case 'm': 1413 u.u_flags |= F_MKDIR; 1414 break; 1415 case 'o': 1416 u.u_flags |= F_DUPUID; 1417 break; 1418 #ifdef EXTENSIONS 1419 case 'p': 1420 memsave(&u.u_password, optarg, strlen(optarg)); 1421 u.u_flags |= F_PASSWORD; 1422 break; 1423 #endif 1424 case 's': 1425 memsave(&u.u_shell, optarg, strlen(optarg)); 1426 u.u_flags |= F_SHELL; 1427 break; 1428 case 'u': 1429 if (!is_number(optarg)) { 1430 errx(EXIT_FAILURE, "When using [-u uid], the uid must be numeric"); 1431 } 1432 u.u_uid = atoi(optarg); 1433 u.u_flags |= F_UID; 1434 break; 1435 #ifdef EXTENSIONS 1436 case 'v': 1437 verbose = 1; 1438 break; 1439 #endif 1440 } 1441 } 1442 if (argc == optind) { 1443 usermgmt_usage("usermod"); 1444 } 1445 checkeuid(); 1446 return moduser(argv[optind], (have_new_user) ? newuser : argv[optind], &u) ? EXIT_SUCCESS : EXIT_FAILURE; 1447 } 1448 1449 #ifdef EXTENSIONS 1450 #define DEL_OPT_EXTENSIONS "Dp:v" 1451 #else 1452 #define DEL_OPT_EXTENSIONS 1453 #endif 1454 1455 int 1456 userdel(int argc, char **argv) 1457 { 1458 struct passwd *pwp; 1459 user_t u; 1460 char password[PasswordLength + 1]; 1461 int defaultfield; 1462 int rmhome; 1463 int bigD; 1464 int c; 1465 1466 (void) memset(&u, 0, sizeof(u)); 1467 read_defaults(&u); 1468 defaultfield = bigD = rmhome = 0; 1469 while ((c = getopt(argc, argv, "r" DEL_OPT_EXTENSIONS)) != -1) { 1470 switch(c) { 1471 #ifdef EXTENSIONS 1472 case 'D': 1473 bigD = 1; 1474 break; 1475 #endif 1476 #ifdef EXTENSIONS 1477 case 'p': 1478 defaultfield = 1; 1479 u.u_preserve = (strcmp(optarg, "true") == 0) ? 1 : 1480 (strcmp(optarg, "yes") == 0) ? 1 : 1481 atoi(optarg); 1482 break; 1483 #endif 1484 case 'r': 1485 rmhome = 1; 1486 break; 1487 #ifdef EXTENSIONS 1488 case 'v': 1489 verbose = 1; 1490 break; 1491 #endif 1492 } 1493 } 1494 #ifdef EXTENSIONS 1495 if (bigD) { 1496 if (defaultfield) { 1497 checkeuid(); 1498 return setdefaults(&u) ? EXIT_SUCCESS : EXIT_FAILURE; 1499 } 1500 (void) printf("preserve\t%s\n", (u.u_preserve) ? "true" : "false"); 1501 return EXIT_SUCCESS; 1502 } 1503 #endif 1504 if (argc == optind) { 1505 usermgmt_usage("userdel"); 1506 } 1507 checkeuid(); 1508 if ((pwp = getpwnam(argv[optind])) == NULL) { 1509 warnx("No such user `%s'", argv[optind]); 1510 return EXIT_FAILURE; 1511 } 1512 if (rmhome) 1513 (void)removehomedir(pwp->pw_name, pwp->pw_uid, pwp->pw_dir); 1514 if (u.u_preserve) { 1515 memsave(&u.u_shell, NOLOGIN, strlen(NOLOGIN)); 1516 (void) memset(password, '*', PasswordLength); 1517 password[PasswordLength] = '\0'; 1518 memsave(&u.u_password, password, PasswordLength); 1519 u.u_flags |= F_PASSWORD; 1520 return moduser(argv[optind], argv[optind], &u) ? EXIT_SUCCESS : EXIT_FAILURE; 1521 } 1522 return moduser(argv[optind], argv[optind], NULL) ? EXIT_SUCCESS : EXIT_FAILURE; 1523 } 1524 1525 #ifdef EXTENSIONS 1526 #define GROUP_ADD_OPT_EXTENSIONS "v" 1527 #else 1528 #define GROUP_ADD_OPT_EXTENSIONS 1529 #endif 1530 1531 /* add a group */ 1532 int 1533 groupadd(int argc, char **argv) 1534 { 1535 int dupgid; 1536 int gid; 1537 int c; 1538 1539 gid = -1; 1540 dupgid = 0; 1541 while ((c = getopt(argc, argv, "g:o" GROUP_ADD_OPT_EXTENSIONS)) != -1) { 1542 switch(c) { 1543 case 'g': 1544 if (!is_number(optarg)) { 1545 errx(EXIT_FAILURE, "When using [-g gid], the gid must be numeric"); 1546 } 1547 gid = atoi(optarg); 1548 break; 1549 case 'o': 1550 dupgid = 1; 1551 break; 1552 #ifdef EXTENSIONS 1553 case 'v': 1554 verbose = 1; 1555 break; 1556 #endif 1557 } 1558 } 1559 if (argc == optind) { 1560 usermgmt_usage("groupadd"); 1561 } 1562 checkeuid(); 1563 if (gid < 0 && !getnextgid(&gid, LowGid, HighGid)) { 1564 err(EXIT_FAILURE, "can't add group: can't get next gid"); 1565 } 1566 if (!dupgid && getgrgid((gid_t) gid) != NULL) { 1567 errx(EXIT_FAILURE, "can't add group: gid %d is a duplicate", gid); 1568 } 1569 if (!valid_group(argv[optind])) { 1570 warnx("warning - invalid group name `%s'", argv[optind]); 1571 } 1572 if (!creategid(argv[optind], gid, "")) { 1573 err(EXIT_FAILURE, "can't add group: problems with %s file", _PATH_GROUP); 1574 } 1575 return EXIT_SUCCESS; 1576 } 1577 1578 #ifdef EXTENSIONS 1579 #define GROUP_DEL_OPT_EXTENSIONS "v" 1580 #else 1581 #define GROUP_DEL_OPT_EXTENSIONS 1582 #endif 1583 1584 /* remove a group */ 1585 int 1586 groupdel(int argc, char **argv) 1587 { 1588 int c; 1589 1590 while ((c = getopt(argc, argv, "" GROUP_DEL_OPT_EXTENSIONS)) != -1) { 1591 switch(c) { 1592 #ifdef EXTENSIONS 1593 case 'v': 1594 verbose = 1; 1595 break; 1596 #endif 1597 } 1598 } 1599 if (argc == optind) { 1600 usermgmt_usage("groupdel"); 1601 } 1602 checkeuid(); 1603 if (!modify_gid(argv[optind], NULL)) { 1604 err(EXIT_FAILURE, "can't change %s file", _PATH_GROUP); 1605 } 1606 return EXIT_SUCCESS; 1607 } 1608 1609 #ifdef EXTENSIONS 1610 #define GROUP_MOD_OPT_EXTENSIONS "v" 1611 #else 1612 #define GROUP_MOD_OPT_EXTENSIONS 1613 #endif 1614 1615 /* modify a group */ 1616 int 1617 groupmod(int argc, char **argv) 1618 { 1619 struct group *grp; 1620 char buf[MaxEntryLen]; 1621 char *newname; 1622 char **cpp; 1623 int dupgid; 1624 int gid; 1625 int cc; 1626 int c; 1627 1628 gid = -1; 1629 dupgid = 0; 1630 newname = NULL; 1631 while ((c = getopt(argc, argv, "g:on:" GROUP_MOD_OPT_EXTENSIONS)) != -1) { 1632 switch(c) { 1633 case 'g': 1634 if (!is_number(optarg)) { 1635 errx(EXIT_FAILURE, "When using [-g gid], the gid must be numeric"); 1636 } 1637 gid = atoi(optarg); 1638 break; 1639 case 'o': 1640 dupgid = 1; 1641 break; 1642 case 'n': 1643 memsave(&newname, optarg, strlen(optarg)); 1644 break; 1645 #ifdef EXTENSIONS 1646 case 'v': 1647 verbose = 1; 1648 break; 1649 #endif 1650 } 1651 } 1652 if (argc == optind) { 1653 usermgmt_usage("groupmod"); 1654 } 1655 checkeuid(); 1656 if (gid < 0 && newname == NULL) { 1657 err(EXIT_FAILURE, "Nothing to change"); 1658 } 1659 if (dupgid && gid < 0) { 1660 err(EXIT_FAILURE, "Duplicate which gid?"); 1661 } 1662 if ((grp = getgrnam(argv[optind])) == NULL) { 1663 err(EXIT_FAILURE, "can't find group `%s' to modify", argv[optind]); 1664 } 1665 if (newname != NULL && !valid_group(newname)) { 1666 warn("warning - invalid group name `%s'", newname); 1667 } 1668 cc = snprintf(buf, sizeof(buf), "%s:%s:%d:", 1669 (newname) ? newname : grp->gr_name, 1670 grp->gr_passwd, 1671 (gid < 0) ? grp->gr_gid : gid); 1672 for (cpp = grp->gr_mem ; *cpp && cc < sizeof(buf) ; cpp++) { 1673 cc += snprintf(&buf[cc], sizeof(buf) - cc, "%s%s", *cpp, 1674 (cpp[1] == NULL) ? "" : ","); 1675 } 1676 if (!modify_gid(argv[optind], buf)) { 1677 err(EXIT_FAILURE, "can't change %s file", _PATH_GROUP); 1678 } 1679 return EXIT_SUCCESS; 1680 } 1681 1682 #ifdef EXTENSIONS 1683 /* display user information */ 1684 int 1685 userinfo(int argc, char **argv) 1686 { 1687 struct passwd *pwp; 1688 struct group *grp; 1689 char buf[MaxEntryLen]; 1690 char **cpp; 1691 int exists; 1692 int cc; 1693 int i; 1694 1695 exists = 0; 1696 while ((i = getopt(argc, argv, "ev")) != -1) { 1697 switch(i) { 1698 case 'e': 1699 exists = 1; 1700 break; 1701 case 'v': 1702 verbose = 1; 1703 break; 1704 } 1705 } 1706 if (argc == optind) { 1707 usermgmt_usage("userinfo"); 1708 } 1709 pwp = find_user_info(argv[optind]); 1710 if (exists) { 1711 exit((pwp) ? EXIT_SUCCESS : EXIT_FAILURE); 1712 } 1713 if (pwp == NULL) { 1714 errx(EXIT_FAILURE, "can't find user `%s'", argv[optind]); 1715 } 1716 (void) printf("login\t%s\n", pwp->pw_name); 1717 (void) printf("passwd\t%s\n", pwp->pw_passwd); 1718 (void) printf("uid\t%d\n", pwp->pw_uid); 1719 for (cc = 0 ; (grp = getgrent()) != NULL ; ) { 1720 for (cpp = grp->gr_mem ; *cpp ; cpp++) { 1721 if (strcmp(*cpp, argv[optind]) == 0 && grp->gr_gid != pwp->pw_gid) { 1722 cc += snprintf(&buf[cc], sizeof(buf) - cc, "%s ", grp->gr_name); 1723 } 1724 } 1725 } 1726 if ((grp = getgrgid(pwp->pw_gid)) == NULL) { 1727 (void) printf("groups\t%d %s\n", pwp->pw_gid, buf); 1728 } else { 1729 (void) printf("groups\t%s %s\n", grp->gr_name, buf); 1730 } 1731 (void) printf("change\t%s", pwp->pw_change ? ctime(&pwp->pw_change) : "NEVER\n"); 1732 (void) printf("class\t%s\n", pwp->pw_class); 1733 (void) printf("gecos\t%s\n", pwp->pw_gecos); 1734 (void) printf("dir\t%s\n", pwp->pw_dir); 1735 (void) printf("shell\t%s\n", pwp->pw_shell); 1736 (void) printf("expire\t%s", pwp->pw_expire ? ctime(&pwp->pw_expire) : "NEVER\n"); 1737 return EXIT_SUCCESS; 1738 } 1739 #endif 1740 1741 #ifdef EXTENSIONS 1742 /* display user information */ 1743 int 1744 groupinfo(int argc, char **argv) 1745 { 1746 struct group *grp; 1747 char **cpp; 1748 int exists; 1749 int i; 1750 1751 exists = 0; 1752 while ((i = getopt(argc, argv, "ev")) != -1) { 1753 switch(i) { 1754 case 'e': 1755 exists = 1; 1756 break; 1757 case 'v': 1758 verbose = 1; 1759 break; 1760 } 1761 } 1762 if (argc == optind) { 1763 usermgmt_usage("groupinfo"); 1764 } 1765 grp = find_group_info(argv[optind]); 1766 if (exists) { 1767 exit((grp) ? EXIT_SUCCESS : EXIT_FAILURE); 1768 } 1769 if (grp == NULL) { 1770 errx(EXIT_FAILURE, "can't find group `%s'", argv[optind]); 1771 } 1772 (void) printf("name\t%s\n", grp->gr_name); 1773 (void) printf("passwd\t%s\n", grp->gr_passwd); 1774 (void) printf("gid\t%d\n", grp->gr_gid); 1775 (void) printf("members\t"); 1776 for (cpp = grp->gr_mem ; *cpp ; cpp++) { 1777 (void) printf("%s ", *cpp); 1778 } 1779 (void) fputc('\n', stdout); 1780 return EXIT_SUCCESS; 1781 } 1782 #endif 1783