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