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