1 /* $NetBSD: user.c,v 1.63 2002/10/01 02:50:51 itojun 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.63 2002/10/01 02:50:51 itojun 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 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 /* structure which defines a password type */ 880 typedef struct passwd_type_t { 881 const char *type; /* optional type descriptor */ 882 int desc_length; /* length of type descriptor */ 883 int length; /* length of password */ 884 const char *regex; /* regexp to output the password */ 885 int re_sub; /* subscript of regexp to use */ 886 } passwd_type_t; 887 888 static passwd_type_t passwd_types[] = { 889 { "$2a", 3, 54, "\\$[^$]+\\$[^$]+\\$(.*)", 1 }, /* Blowfish */ 890 { "$1", 2, 34, NULL, 0 }, /* MD5 */ 891 { "", 0, 13, NULL, 0 }, /* standard DES */ 892 { NULL, -1, -1, NULL, 0 } /* none - terminate search */ 893 }; 894 895 /* return non-zero if it's a valid password - check length for cipher type */ 896 static int 897 valid_password_length(char *newpasswd) 898 { 899 passwd_type_t *pwtp; 900 regmatch_t matchv[10]; 901 regex_t r; 902 903 for (pwtp = passwd_types ; pwtp->desc_length >= 0 ; pwtp++) { 904 if (strncmp(newpasswd, pwtp->type, pwtp->desc_length) == 0) { 905 if (pwtp->regex == NULL) { 906 return strlen(newpasswd) == pwtp->length; 907 } 908 (void) regcomp(&r, pwtp->regex, REG_EXTENDED); 909 if (regexec(&r, newpasswd, 10, matchv, 0) == 0) { 910 regfree(&r); 911 return (int)(matchv[pwtp->re_sub].rm_eo - matchv[pwtp->re_sub].rm_so + 1) == pwtp->length; 912 } 913 regfree(&r); 914 } 915 } 916 return 0; 917 } 918 919 /* add a user */ 920 static int 921 adduser(char *login_name, user_t *up) 922 { 923 struct group *grp; 924 struct stat st; 925 struct tm tm; 926 time_t expire; 927 time_t inactive; 928 char password[PasswordLength + 1]; 929 char home[MaxFileNameLen]; 930 char buf[MaxFileNameLen]; 931 int sync_uid_gid; 932 int masterfd; 933 int ptmpfd; 934 int gid; 935 int cc; 936 int i; 937 938 if (!valid_login(login_name)) { 939 errx(EXIT_FAILURE, "`%s' is not a valid login name", login_name); 940 } 941 if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) { 942 err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD); 943 } 944 if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) { 945 err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD); 946 } 947 pw_init(); 948 if ((ptmpfd = pw_lock(WAITSECS)) < 0) { 949 (void) close(masterfd); 950 err(EXIT_FAILURE, "can't obtain pw_lock"); 951 } 952 while ((cc = read(masterfd, buf, sizeof(buf))) > 0) { 953 if (write(ptmpfd, buf, (size_t)(cc)) != cc) { 954 (void) close(masterfd); 955 (void) close(ptmpfd); 956 pw_abort(); 957 err(EXIT_FAILURE, "short write to /etc/ptmp (not %d chars)", cc); 958 } 959 } 960 /* if no uid was specified, get next one in [low_uid..high_uid] range */ 961 sync_uid_gid = (strcmp(up->u_primgrp, "=uid") == 0); 962 if (up->u_uid == -1) { 963 int got_id = 0; 964 965 /* 966 * Look for a free UID in the command line ranges (if any). 967 * These start after the ranges specified in the config file. 968 */ 969 for (i = up->u_defrc; !got_id && i < up->u_rc ; i++) { 970 got_id = getnextuid(sync_uid_gid, &up->u_uid, 971 up->u_rv[i].r_from, up->u_rv[i].r_to); 972 } 973 /* 974 * If there were no free UIDs in the command line ranges, 975 * try the ranges from the config file (there will always 976 * be at least one default). 977 */ 978 for (i = 0; !got_id && i < up->u_defrc; i++) { 979 got_id = getnextuid(sync_uid_gid, &up->u_uid, 980 up->u_rv[i].r_from, up->u_rv[i].r_to); 981 } 982 if (!got_id) { 983 (void) close(ptmpfd); 984 pw_abort(); 985 errx(EXIT_FAILURE, "can't get next uid for %d", up->u_uid); 986 } 987 } 988 /* check uid isn't already allocated */ 989 if (!(up->u_flags & F_DUPUID) && getpwuid((uid_t)(up->u_uid)) != NULL) { 990 (void) close(ptmpfd); 991 pw_abort(); 992 errx(EXIT_FAILURE, "uid %d is already in use", up->u_uid); 993 } 994 /* if -g=uid was specified, check gid is unused */ 995 if (sync_uid_gid) { 996 if (getgrgid((gid_t)(up->u_uid)) != NULL) { 997 (void) close(ptmpfd); 998 pw_abort(); 999 errx(EXIT_FAILURE, "gid %d is already in use", up->u_uid); 1000 } 1001 gid = up->u_uid; 1002 } else if ((grp = getgrnam(up->u_primgrp)) != NULL) { 1003 gid = grp->gr_gid; 1004 } else if (is_number(up->u_primgrp) && 1005 (grp = getgrgid((gid_t)atoi(up->u_primgrp))) != NULL) { 1006 gid = grp->gr_gid; 1007 } else { 1008 (void) close(ptmpfd); 1009 pw_abort(); 1010 errx(EXIT_FAILURE, "group %s not found", up->u_primgrp); 1011 } 1012 /* check name isn't already in use */ 1013 if (!(up->u_flags & F_DUPUID) && getpwnam(login_name) != NULL) { 1014 (void) close(ptmpfd); 1015 pw_abort(); 1016 errx(EXIT_FAILURE, "already a `%s' user", login_name); 1017 } 1018 if (up->u_flags & F_HOMEDIR) { 1019 (void) strlcpy(home, up->u_home, sizeof(home)); 1020 } else { 1021 /* if home directory hasn't been given, make it up */ 1022 (void) snprintf(home, sizeof(home), "%s/%s", up->u_basedir, login_name); 1023 } 1024 inactive = 0; 1025 if (up->u_inactive != NULL) { 1026 (void) memset(&tm, 0, sizeof(tm)); 1027 if (strptime(up->u_inactive, "%c", &tm) != NULL) { 1028 inactive = mktime(&tm); 1029 } else if (strptime(up->u_inactive, "%B %d %Y", &tm) != NULL) { 1030 inactive = mktime(&tm); 1031 } else if (isdigit(up->u_inactive[0]) != NULL) { 1032 inactive = atoi(up->u_inactive); 1033 } else { 1034 warnx("Warning: inactive time `%s' invalid, account expiry off", 1035 up->u_inactive); 1036 } 1037 } 1038 expire = 0; 1039 if (up->u_expire != NULL) { 1040 (void) memset(&tm, 0, sizeof(tm)); 1041 if (strptime(up->u_expire, "%c", &tm) != NULL) { 1042 expire = mktime(&tm); 1043 } else if (strptime(up->u_expire, "%B %d %Y", &tm) != NULL) { 1044 expire = mktime(&tm); 1045 } else if (isdigit(up->u_expire[0]) != NULL) { 1046 expire = atoi(up->u_expire); 1047 } else { 1048 warnx("Warning: expire time `%s' invalid, password expiry off", 1049 up->u_expire); 1050 } 1051 } 1052 if (lstat(home, &st) < 0 && !(up->u_flags & F_MKDIR)) { 1053 warnx("Warning: home directory `%s' doesn't exist, and -m was not specified", 1054 home); 1055 } 1056 password[sizeof(password) - 1] = '\0'; 1057 if (up->u_password != NULL && valid_password_length(up->u_password)) { 1058 (void) strlcpy(password, up->u_password, sizeof(password)); 1059 } else { 1060 (void) memset(password, '\0', sizeof(password)); 1061 password[0] = '*'; 1062 if (up->u_password != NULL) { 1063 warnx("Password `%s' is invalid: setting it to `%s'", 1064 up->u_password, password); 1065 } 1066 } 1067 cc = snprintf(buf, sizeof(buf), "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", 1068 login_name, 1069 password, 1070 up->u_uid, 1071 gid, 1072 #ifdef EXTENSIONS 1073 up->u_class, 1074 #else 1075 "", 1076 #endif 1077 (long) inactive, 1078 (long) expire, 1079 up->u_comment, 1080 home, 1081 up->u_shell); 1082 if (write(ptmpfd, buf, (size_t) cc) != cc) { 1083 (void) close(ptmpfd); 1084 pw_abort(); 1085 err(EXIT_FAILURE, "can't add `%s'", buf); 1086 } 1087 if (up->u_flags & F_MKDIR) { 1088 if (lstat(home, &st) == 0) { 1089 (void) close(ptmpfd); 1090 pw_abort(); 1091 errx(EXIT_FAILURE, "home directory `%s' already exists", home); 1092 } else { 1093 if (asystem("%s -p %s", MKDIR, home) != 0) { 1094 (void) close(ptmpfd); 1095 pw_abort(); 1096 err(EXIT_FAILURE, "can't mkdir `%s'", home); 1097 } 1098 (void) copydotfiles(up->u_skeldir, up->u_uid, gid, home); 1099 } 1100 } 1101 if (strcmp(up->u_primgrp, "=uid") == 0 && 1102 getgrnam(login_name) == NULL && 1103 !creategid(login_name, gid, login_name)) { 1104 (void) close(ptmpfd); 1105 pw_abort(); 1106 errx(EXIT_FAILURE, "can't create gid %d for login name %s", gid, login_name); 1107 } 1108 if (up->u_groupc > 0 && !append_group(login_name, up->u_groupc, up->u_groupv)) { 1109 (void) close(ptmpfd); 1110 pw_abort(); 1111 errx(EXIT_FAILURE, "can't append `%s' to new groups", login_name); 1112 } 1113 (void) close(ptmpfd); 1114 #if PW_MKDB_ARGC == 2 1115 if (pw_mkdb(login_name, 0) < 0) { 1116 pw_abort(); 1117 err(EXIT_FAILURE, "pw_mkdb failed"); 1118 } 1119 #else 1120 if (pw_mkdb() < 0) { 1121 pw_abort(); 1122 err(EXIT_FAILURE, "pw_mkdb failed"); 1123 } 1124 #endif 1125 syslog(LOG_INFO, "new user added: name=%s, uid=%d, gid=%d, home=%s, shell=%s", 1126 login_name, up->u_uid, gid, home, up->u_shell); 1127 return 1; 1128 } 1129 1130 /* remove a user from the groups file */ 1131 static int 1132 rm_user_from_groups(char *login_name) 1133 { 1134 struct stat st; 1135 regmatch_t matchv[10]; 1136 regex_t r; 1137 FILE *from; 1138 FILE *to; 1139 char line[MaxEntryLen]; 1140 char buf[MaxEntryLen]; 1141 char f[MaxFileNameLen]; 1142 int fd; 1143 int cc; 1144 int sc; 1145 1146 (void) snprintf(line, sizeof(line), "(:|,)%s(,|\n|$)", login_name); 1147 if (regcomp(&r, line, REG_EXTENDED) != 0) { 1148 warn("can't compile regular expression `%s'", line); 1149 return 0; 1150 } 1151 if ((from = fopen(_PATH_GROUP, "r")) == NULL) { 1152 warn("can't remove gid for `%s': can't open `%s'", login_name, _PATH_GROUP); 1153 return 0; 1154 } 1155 if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { 1156 warn("can't lock `%s'", _PATH_GROUP); 1157 } 1158 (void) fstat(fileno(from), &st); 1159 (void) snprintf(f, sizeof(f), "%s.XXXXXX", _PATH_GROUP); 1160 if ((fd = mkstemp(f)) < 0) { 1161 (void) fclose(from); 1162 warn("can't create gid: mkstemp failed"); 1163 return 0; 1164 } 1165 if ((to = fdopen(fd, "w")) == NULL) { 1166 (void) fclose(from); 1167 (void) close(fd); 1168 (void) unlink(f); 1169 warn("can't create gid: fdopen `%s' failed", f); 1170 return 0; 1171 } 1172 while (fgets(buf, sizeof(buf), from) > 0) { 1173 cc = strlen(buf); 1174 if (regexec(&r, buf, 10, matchv, 0) == 0) { 1175 cc -= (int)(matchv[0].rm_eo); 1176 sc = (int) matchv[0].rm_so; 1177 if (cc > 0) { 1178 sc += 1; 1179 } 1180 if (fwrite(buf, sizeof(char), sc, to) != sc || 1181 fwrite(&buf[(int)matchv[0].rm_eo], sizeof(char), cc, to) != cc || 1182 (buf[(int)matchv[0].rm_eo - 1] != ',' && fwrite("\n", sizeof(char), 1, to) != 1)) { 1183 (void) fclose(from); 1184 (void) close(fd); 1185 (void) unlink(f); 1186 warn("can't create gid: short write to `%s'", f); 1187 return 0; 1188 } 1189 } else if (fwrite(buf, sizeof(char), (unsigned) cc, to) != cc) { 1190 (void) fclose(from); 1191 (void) close(fd); 1192 (void) unlink(f); 1193 warn("can't create gid: short write to `%s'", f); 1194 return 0; 1195 } 1196 } 1197 (void) fclose(from); 1198 (void) fclose(to); 1199 if (rename(f, _PATH_GROUP) < 0) { 1200 (void) unlink(f); 1201 warn("can't create gid: can't rename `%s' to `%s'", f, _PATH_GROUP); 1202 return 0; 1203 } 1204 (void) chmod(_PATH_GROUP, st.st_mode & 07777); 1205 return 1; 1206 } 1207 1208 /* check that the user or group is local, not from YP/NIS */ 1209 static int 1210 is_local(char *name, const char *file) 1211 { 1212 regmatch_t matchv[10]; 1213 regex_t r; 1214 FILE *fp; 1215 char buf[MaxEntryLen]; 1216 char re[MaxEntryLen]; 1217 int ret; 1218 1219 (void) snprintf(re, sizeof(re), "^%s:", name); 1220 if (regcomp(&r, re, REG_EXTENDED) != 0) { 1221 errx(EXIT_FAILURE, "can't compile regular expression `%s'", re); 1222 } 1223 if ((fp = fopen(file, "r")) == NULL) { 1224 err(EXIT_FAILURE, "can't open `%s'", file); 1225 } 1226 for (ret = 0 ; fgets(buf, sizeof(buf), fp) != NULL ; ) { 1227 if (regexec(&r, buf, 10, matchv, 0) == 0) { 1228 ret = 1; 1229 break; 1230 } 1231 } 1232 (void) fclose(fp); 1233 return ret; 1234 } 1235 1236 /* modify a user */ 1237 static int 1238 moduser(char *login_name, char *newlogin, user_t *up) 1239 { 1240 struct passwd *pwp; 1241 struct group *grp; 1242 const char *homedir; 1243 struct tm tm; 1244 size_t colonc; 1245 size_t loginc; 1246 size_t len; 1247 size_t cc; 1248 FILE *master; 1249 char newdir[MaxFileNameLen]; 1250 char *buf; 1251 char *colon; 1252 char *line; 1253 int masterfd; 1254 int ptmpfd; 1255 int error; 1256 1257 if (!valid_login(newlogin)) { 1258 errx(EXIT_FAILURE, "`%s' is not a valid login name", login_name); 1259 } 1260 if ((pwp = getpwnam(login_name)) == NULL) { 1261 errx(EXIT_FAILURE, "No such user `%s'", login_name); 1262 } 1263 if (!is_local(login_name, _PATH_MASTERPASSWD)) { 1264 errx(EXIT_FAILURE, "User `%s' must be a local user", login_name); 1265 } 1266 /* keep dir name in case we need it for '-m' */ 1267 homedir = pwp->pw_dir; 1268 1269 if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) { 1270 err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD); 1271 } 1272 if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) { 1273 err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD); 1274 } 1275 pw_init(); 1276 if ((ptmpfd = pw_lock(WAITSECS)) < 0) { 1277 (void) close(masterfd); 1278 err(EXIT_FAILURE, "can't obtain pw_lock"); 1279 } 1280 if ((master = fdopen(masterfd, "r")) == NULL) { 1281 (void) close(masterfd); 1282 (void) close(ptmpfd); 1283 pw_abort(); 1284 err(EXIT_FAILURE, "can't fdopen fd for %s", _PATH_MASTERPASSWD); 1285 } 1286 if (up != NULL) { 1287 if (up->u_flags & F_USERNAME) { 1288 /* if changing name, check new name isn't already in use */ 1289 if (strcmp(login_name, newlogin) != 0 && getpwnam(newlogin) != NULL) { 1290 (void) close(ptmpfd); 1291 pw_abort(); 1292 errx(EXIT_FAILURE, "already a `%s' user", newlogin); 1293 } 1294 pwp->pw_name = newlogin; 1295 1296 /* 1297 * Provide a new directory name in case the 1298 * home directory is to be moved. 1299 */ 1300 if (up->u_flags & F_MKDIR) { 1301 snprintf(newdir, sizeof(newdir), "%s/%s", up->u_basedir, newlogin); 1302 pwp->pw_dir = newdir; 1303 } 1304 } 1305 if (up->u_flags & F_PASSWORD) { 1306 if (up->u_password != NULL) { 1307 if (!valid_password_length(up->u_password)) { 1308 (void) close(ptmpfd); 1309 pw_abort(); 1310 errx(EXIT_FAILURE, "Invalid password: `%s'", 1311 up->u_password); 1312 } 1313 pwp->pw_passwd = up->u_password; 1314 } 1315 } 1316 if (up->u_flags & F_UID) { 1317 /* check uid isn't already allocated */ 1318 if (!(up->u_flags & F_DUPUID) && getpwuid((uid_t)(up->u_uid)) != NULL) { 1319 (void) close(ptmpfd); 1320 pw_abort(); 1321 errx(EXIT_FAILURE, "uid %d is already in use", up->u_uid); 1322 } 1323 pwp->pw_uid = up->u_uid; 1324 } 1325 if (up->u_flags & F_GROUP) { 1326 /* if -g=uid was specified, check gid is unused */ 1327 if (strcmp(up->u_primgrp, "=uid") == 0) { 1328 if (getgrgid((gid_t)(up->u_uid)) != NULL) { 1329 (void) close(ptmpfd); 1330 pw_abort(); 1331 errx(EXIT_FAILURE, "gid %d is already in use", up->u_uid); 1332 } 1333 pwp->pw_gid = up->u_uid; 1334 } else if ((grp = getgrnam(up->u_primgrp)) != NULL) { 1335 pwp->pw_gid = grp->gr_gid; 1336 } else if (is_number(up->u_primgrp) && 1337 (grp = getgrgid((gid_t)atoi(up->u_primgrp))) != NULL) { 1338 pwp->pw_gid = grp->gr_gid; 1339 } else { 1340 (void) close(ptmpfd); 1341 pw_abort(); 1342 errx(EXIT_FAILURE, "group %s not found", up->u_primgrp); 1343 } 1344 } 1345 if (up->u_flags & F_INACTIVE) { 1346 (void) memset(&tm, 0, sizeof(tm)); 1347 if (strptime(up->u_inactive, "%c", &tm) != NULL) { 1348 pwp->pw_change = mktime(&tm); 1349 } else if (strptime(up->u_inactive, "%B %d %Y", &tm) != NULL) { 1350 pwp->pw_change = mktime(&tm); 1351 } else if (isdigit(up->u_inactive[0]) != NULL) { 1352 pwp->pw_change = atoi(up->u_inactive); 1353 } else { 1354 warnx("Warning: inactive time `%s' invalid, password expiry off", 1355 up->u_inactive); 1356 } 1357 } 1358 if (up->u_flags & F_EXPIRE) { 1359 (void) memset(&tm, 0, sizeof(tm)); 1360 if (strptime(up->u_expire, "%c", &tm) != NULL) { 1361 pwp->pw_expire = mktime(&tm); 1362 } else if (strptime(up->u_expire, "%B %d %Y", &tm) != NULL) { 1363 pwp->pw_expire = mktime(&tm); 1364 } else if (isdigit(up->u_expire[0]) != NULL) { 1365 pwp->pw_expire = atoi(up->u_expire); 1366 } else { 1367 warnx("Warning: expire time `%s' invalid, password expiry off", 1368 up->u_expire); 1369 } 1370 } 1371 if (up->u_flags & F_COMMENT) 1372 pwp->pw_gecos = up->u_comment; 1373 if (up->u_flags & F_HOMEDIR) 1374 pwp->pw_dir = up->u_home; 1375 if (up->u_flags & F_SHELL) 1376 pwp->pw_shell = up->u_shell; 1377 #ifdef EXTENSIONS 1378 if (up->u_flags & F_CLASS) 1379 pwp->pw_class = up->u_class; 1380 #endif 1381 } 1382 loginc = strlen(login_name); 1383 while ((line = fgetln(master, &len)) != NULL) { 1384 if ((colon = strchr(line, ':')) == NULL) { 1385 warnx("Malformed entry `%s'. Skipping", line); 1386 continue; 1387 } 1388 colonc = (size_t)(colon - line); 1389 if (strncmp(login_name, line, loginc) == 0 && loginc == colonc) { 1390 if (up != NULL) { 1391 len = (int)asprintf(&buf, "%s:%s:%d:%d:" 1392 #ifdef EXTENSIONS 1393 "%s" 1394 #endif 1395 ":%ld:%ld:%s:%s:%s\n", 1396 newlogin, 1397 pwp->pw_passwd, 1398 pwp->pw_uid, 1399 pwp->pw_gid, 1400 #ifdef EXTENSIONS 1401 pwp->pw_class, 1402 #endif 1403 (long)pwp->pw_change, 1404 (long)pwp->pw_expire, 1405 pwp->pw_gecos, 1406 pwp->pw_dir, 1407 pwp->pw_shell); 1408 if (write(ptmpfd, buf, len) != len) { 1409 (void) close(ptmpfd); 1410 pw_abort(); 1411 err(EXIT_FAILURE, "can't add `%s'", buf); 1412 } 1413 (void) free(buf); 1414 } 1415 } else if ((cc = write(ptmpfd, line, len)) != len) { 1416 (void) close(masterfd); 1417 (void) close(ptmpfd); 1418 pw_abort(); 1419 err(EXIT_FAILURE, "short write to /etc/ptmp (%lld not %lld chars)", 1420 (long long)cc, 1421 (long long)len); 1422 } 1423 } 1424 if (up != NULL) { 1425 if ((up->u_flags & F_MKDIR) && 1426 asystem("%s %s %s", MV, homedir, pwp->pw_dir) != 0) { 1427 (void) close(ptmpfd); 1428 pw_abort(); 1429 err(EXIT_FAILURE, "can't move `%s' to `%s'", 1430 homedir, pwp->pw_dir); 1431 } 1432 if (up->u_groupc > 0 && 1433 !append_group(newlogin, up->u_groupc, up->u_groupv)) { 1434 (void) close(ptmpfd); 1435 pw_abort(); 1436 errx(EXIT_FAILURE, "can't append `%s' to new groups", 1437 newlogin); 1438 } 1439 } 1440 (void) close(ptmpfd); 1441 #if PW_MKDB_ARGC == 2 1442 if (up != NULL && strcmp(login_name, newlogin) == 0) { 1443 error = pw_mkdb(login_name, 0); 1444 } else { 1445 error = pw_mkdb(NULL, 0); 1446 } 1447 #else 1448 error = pw_mkdb(); 1449 #endif 1450 if (error < 0) { 1451 pw_abort(); 1452 err(EXIT_FAILURE, "pw_mkdb failed"); 1453 } 1454 if (up == NULL) { 1455 syslog(LOG_INFO, "user removed: name=%s", login_name); 1456 } else if (strcmp(login_name, newlogin) == 0) { 1457 syslog(LOG_INFO, "user information modified: name=%s, uid=%d, gid=%d, home=%s, shell=%s", 1458 login_name, pwp->pw_uid, pwp->pw_gid, pwp->pw_dir, pwp->pw_shell); 1459 } else { 1460 syslog(LOG_INFO, "user information modified: name=%s, new name=%s, uid=%d, gid=%d, home=%s, shell=%s", 1461 login_name, newlogin, pwp->pw_uid, pwp->pw_gid, pwp->pw_dir, pwp->pw_shell); 1462 } 1463 return 1; 1464 } 1465 1466 1467 #ifdef EXTENSIONS 1468 /* see if we can find out the user struct */ 1469 static struct passwd * 1470 find_user_info(char *name) 1471 { 1472 struct passwd *pwp; 1473 1474 if ((pwp = getpwnam(name)) != NULL) { 1475 return pwp; 1476 } 1477 if (is_number(name) && (pwp = getpwuid((uid_t)atoi(name))) != NULL) { 1478 return pwp; 1479 } 1480 return NULL; 1481 } 1482 #endif 1483 1484 #ifdef EXTENSIONS 1485 /* see if we can find out the group struct */ 1486 static struct group * 1487 find_group_info(char *name) 1488 { 1489 struct group *grp; 1490 1491 if ((grp = getgrnam(name)) != NULL) { 1492 return grp; 1493 } 1494 if (is_number(name) && (grp = getgrgid((gid_t)atoi(name))) != NULL) { 1495 return grp; 1496 } 1497 return NULL; 1498 } 1499 #endif 1500 1501 /* print out usage message, and then exit */ 1502 void 1503 usermgmt_usage(const char *prog) 1504 { 1505 if (strcmp(prog, "useradd") == 0) { 1506 (void) fprintf(stderr, "Usage: %s -D [-b basedir] [-e expiry] " 1507 "[-f inactive] [-g group]\n\t[-r lowuid..highuid] " 1508 "[-s shell] [-L class]\n", prog); 1509 (void) fprintf(stderr, "Usage: %s [-G group] [-b basedir] " 1510 "[-c comment] [-d homedir] [-e expiry]\n\t[-f inactive] " 1511 "[-g group] [-k skeletondir] [-m] [-o] [-p password]\n" 1512 "\t[-r lowuid..highuid] [-s shell]\n\t[-u uid] [-v] user\n", 1513 prog); 1514 } else if (strcmp(prog, "usermod") == 0) { 1515 (void) fprintf(stderr, "Usage: %s [-G group] [-c comment] " 1516 "[-d homedir] [-e expire] [-f inactive]\n\t[-g group] " 1517 "[-l newname] [-m] [-o] [-p password] [-s shell] [-u uid]\n" 1518 "\t[-L class] [-v] user\n", prog); 1519 } else if (strcmp(prog, "userdel") == 0) { 1520 (void) fprintf(stderr, "Usage: %s -D [-p preserve]\n", prog); 1521 (void) fprintf(stderr, 1522 "Usage: %s [-p preserve] [-r] [-v] user\n", prog); 1523 #ifdef EXTENSIONS 1524 } else if (strcmp(prog, "userinfo") == 0) { 1525 (void) fprintf(stderr, "Usage: %s [-e] [-v] user\n", prog); 1526 #endif 1527 } else if (strcmp(prog, "groupadd") == 0) { 1528 (void) fprintf(stderr, "Usage: %s [-g gid] [-o] [-v] group\n", 1529 prog); 1530 } else if (strcmp(prog, "groupdel") == 0) { 1531 (void) fprintf(stderr, "Usage: %s [-v] group\n", prog); 1532 } else if (strcmp(prog, "groupmod") == 0) { 1533 (void) fprintf(stderr, 1534 "Usage: %s [-g gid] [-o] [-n newname] [-v] group\n", prog); 1535 } else if (strcmp(prog, "user") == 0 || strcmp(prog, "group") == 0) { 1536 (void) fprintf(stderr, 1537 "Usage: %s ( add | del | mod | info ) ...\n", prog); 1538 #ifdef EXTENSIONS 1539 } else if (strcmp(prog, "groupinfo") == 0) { 1540 (void) fprintf(stderr, "Usage: %s [-e] [-v] group\n", prog); 1541 #endif 1542 } 1543 exit(EXIT_FAILURE); 1544 /* NOTREACHED */ 1545 } 1546 1547 #ifdef EXTENSIONS 1548 #define ADD_OPT_EXTENSIONS "p:r:vL:" 1549 #else 1550 #define ADD_OPT_EXTENSIONS 1551 #endif 1552 1553 int 1554 useradd(int argc, char **argv) 1555 { 1556 user_t u; 1557 int defaultfield; 1558 int bigD; 1559 int c; 1560 #ifdef EXTENSIONS 1561 int i; 1562 #endif 1563 1564 (void) memset(&u, 0, sizeof(u)); 1565 read_defaults(&u); 1566 u.u_uid = -1; 1567 defaultfield = bigD = 0; 1568 while ((c = getopt(argc, argv, "DG:b:c:d:e:f:g:k:mou:s:" ADD_OPT_EXTENSIONS)) != -1) { 1569 switch(c) { 1570 case 'D': 1571 bigD = 1; 1572 break; 1573 case 'G': 1574 while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && 1575 u.u_groupc < NGROUPS_MAX) { 1576 if (u.u_groupv[u.u_groupc][0] != 0) { 1577 u.u_groupc++; 1578 } 1579 } 1580 if (optarg != NULL) { 1581 warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX); 1582 } 1583 break; 1584 case 'b': 1585 defaultfield = 1; 1586 memsave(&u.u_basedir, optarg, strlen(optarg)); 1587 break; 1588 case 'c': 1589 memsave(&u.u_comment, optarg, strlen(optarg)); 1590 break; 1591 case 'd': 1592 memsave(&u.u_home, optarg, strlen(optarg)); 1593 u.u_flags |= F_HOMEDIR; 1594 break; 1595 case 'e': 1596 defaultfield = 1; 1597 memsave(&u.u_expire, optarg, strlen(optarg)); 1598 break; 1599 case 'f': 1600 defaultfield = 1; 1601 memsave(&u.u_inactive, optarg, strlen(optarg)); 1602 break; 1603 case 'g': 1604 defaultfield = 1; 1605 memsave(&u.u_primgrp, optarg, strlen(optarg)); 1606 break; 1607 case 'k': 1608 defaultfield = 1; 1609 memsave(&u.u_skeldir, optarg, strlen(optarg)); 1610 break; 1611 #ifdef EXTENSIONS 1612 case 'L': 1613 defaultfield = 1; 1614 memsave(&u.u_class, optarg, strlen(optarg)); 1615 break; 1616 #endif 1617 case 'm': 1618 u.u_flags |= F_MKDIR; 1619 break; 1620 case 'o': 1621 u.u_flags |= F_DUPUID; 1622 break; 1623 #ifdef EXTENSIONS 1624 case 'p': 1625 memsave(&u.u_password, optarg, strlen(optarg)); 1626 break; 1627 #endif 1628 #ifdef EXTENSIONS 1629 case 'r': 1630 defaultfield = 1; 1631 (void) save_range(&u, optarg); 1632 break; 1633 #endif 1634 case 's': 1635 defaultfield = 1; 1636 memsave(&u.u_shell, optarg, strlen(optarg)); 1637 break; 1638 case 'u': 1639 if (!is_number(optarg)) { 1640 errx(EXIT_FAILURE, "When using [-u uid], the uid must be numeric"); 1641 } 1642 u.u_uid = atoi(optarg); 1643 break; 1644 #ifdef EXTENSIONS 1645 case 'v': 1646 verbose = 1; 1647 break; 1648 #endif 1649 } 1650 } 1651 if (bigD) { 1652 if (defaultfield) { 1653 checkeuid(); 1654 return setdefaults(&u) ? EXIT_SUCCESS : EXIT_FAILURE; 1655 } 1656 (void) printf("group\t\t%s\n", u.u_primgrp); 1657 (void) printf("base_dir\t%s\n", u.u_basedir); 1658 (void) printf("skel_dir\t%s\n", u.u_skeldir); 1659 (void) printf("shell\t\t%s\n", u.u_shell); 1660 #ifdef EXTENSIONS 1661 (void) printf("class\t\t%s\n", u.u_class); 1662 #endif 1663 (void) printf("inactive\t%s\n", (u.u_inactive == NULL) ? UNSET_INACTIVE : u.u_inactive); 1664 (void) printf("expire\t\t%s\n", (u.u_expire == NULL) ? UNSET_EXPIRY : u.u_expire); 1665 #ifdef EXTENSIONS 1666 for (i = 0 ; i < u.u_rc ; i++) { 1667 (void) printf("range\t\t%d..%d\n", u.u_rv[i].r_from, u.u_rv[i].r_to); 1668 } 1669 #endif 1670 return EXIT_SUCCESS; 1671 } 1672 argc -= optind; 1673 argv += optind; 1674 if (argc != 1) { 1675 usermgmt_usage("useradd"); 1676 } 1677 checkeuid(); 1678 openlog("useradd", LOG_PID, LOG_USER); 1679 return adduser(*argv, &u) ? EXIT_SUCCESS : EXIT_FAILURE; 1680 } 1681 1682 #ifdef EXTENSIONS 1683 #define MOD_OPT_EXTENSIONS "p:vL:" 1684 #else 1685 #define MOD_OPT_EXTENSIONS 1686 #endif 1687 1688 int 1689 usermod(int argc, char **argv) 1690 { 1691 user_t u; 1692 char newuser[MaxUserNameLen + 1]; 1693 int c, have_new_user; 1694 1695 (void) memset(&u, 0, sizeof(u)); 1696 (void) memset(newuser, 0, sizeof(newuser)); 1697 read_defaults(&u); 1698 have_new_user = 0; 1699 while ((c = getopt(argc, argv, "G:c:d:e:f:g:l:mos:u:" MOD_OPT_EXTENSIONS)) != -1) { 1700 switch(c) { 1701 case 'G': 1702 while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && 1703 u.u_groupc < NGROUPS_MAX) { 1704 if (u.u_groupv[u.u_groupc][0] != 0) { 1705 u.u_groupc++; 1706 } 1707 } 1708 if (optarg != NULL) { 1709 warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX); 1710 } 1711 u.u_flags |= F_SECGROUP; 1712 break; 1713 case 'c': 1714 memsave(&u.u_comment, optarg, strlen(optarg)); 1715 u.u_flags |= F_COMMENT; 1716 break; 1717 case 'd': 1718 memsave(&u.u_home, optarg, strlen(optarg)); 1719 u.u_flags |= F_HOMEDIR; 1720 break; 1721 case 'e': 1722 memsave(&u.u_expire, optarg, strlen(optarg)); 1723 u.u_flags |= F_EXPIRE; 1724 break; 1725 case 'f': 1726 memsave(&u.u_inactive, optarg, strlen(optarg)); 1727 u.u_flags |= F_INACTIVE; 1728 break; 1729 case 'g': 1730 memsave(&u.u_primgrp, optarg, strlen(optarg)); 1731 u.u_flags |= F_GROUP; 1732 break; 1733 case 'l': 1734 (void) strlcpy(newuser, optarg, sizeof(newuser)); 1735 have_new_user = 1; 1736 u.u_flags |= F_USERNAME; 1737 break; 1738 #ifdef EXTENSIONS 1739 case 'L': 1740 memsave(&u.u_class, optarg, strlen(optarg)); 1741 u.u_flags |= F_CLASS; 1742 break; 1743 #endif 1744 case 'm': 1745 u.u_flags |= F_MKDIR; 1746 break; 1747 case 'o': 1748 u.u_flags |= F_DUPUID; 1749 break; 1750 #ifdef EXTENSIONS 1751 case 'p': 1752 memsave(&u.u_password, optarg, strlen(optarg)); 1753 u.u_flags |= F_PASSWORD; 1754 break; 1755 #endif 1756 case 's': 1757 memsave(&u.u_shell, optarg, strlen(optarg)); 1758 u.u_flags |= F_SHELL; 1759 break; 1760 case 'u': 1761 if (!is_number(optarg)) { 1762 errx(EXIT_FAILURE, "When using [-u uid], the uid must be numeric"); 1763 } 1764 u.u_uid = atoi(optarg); 1765 u.u_flags |= F_UID; 1766 break; 1767 #ifdef EXTENSIONS 1768 case 'v': 1769 verbose = 1; 1770 break; 1771 #endif 1772 } 1773 } 1774 if ((u.u_flags & F_MKDIR) && !(u.u_flags & F_HOMEDIR) && 1775 !(u.u_flags & F_USERNAME)) { 1776 warnx("option 'm' useless without 'd' or 'l' -- ignored"); 1777 u.u_flags &= ~F_MKDIR; 1778 } 1779 argc -= optind; 1780 argv += optind; 1781 if (argc != 1) { 1782 usermgmt_usage("usermod"); 1783 } 1784 checkeuid(); 1785 openlog("usermod", LOG_PID, LOG_USER); 1786 return moduser(*argv, (have_new_user) ? newuser : *argv, &u) ? EXIT_SUCCESS : EXIT_FAILURE; 1787 } 1788 1789 #ifdef EXTENSIONS 1790 #define DEL_OPT_EXTENSIONS "Dp:v" 1791 #else 1792 #define DEL_OPT_EXTENSIONS 1793 #endif 1794 1795 int 1796 userdel(int argc, char **argv) 1797 { 1798 struct passwd *pwp; 1799 user_t u; 1800 char password[PasswordLength + 1]; 1801 int defaultfield; 1802 int rmhome; 1803 int bigD; 1804 int c; 1805 1806 (void) memset(&u, 0, sizeof(u)); 1807 read_defaults(&u); 1808 defaultfield = bigD = rmhome = 0; 1809 while ((c = getopt(argc, argv, "r" DEL_OPT_EXTENSIONS)) != -1) { 1810 switch(c) { 1811 #ifdef EXTENSIONS 1812 case 'D': 1813 bigD = 1; 1814 break; 1815 #endif 1816 #ifdef EXTENSIONS 1817 case 'p': 1818 defaultfield = 1; 1819 u.u_preserve = (strcmp(optarg, "true") == 0) ? 1 : 1820 (strcmp(optarg, "yes") == 0) ? 1 : 1821 atoi(optarg); 1822 break; 1823 #endif 1824 case 'r': 1825 rmhome = 1; 1826 break; 1827 #ifdef EXTENSIONS 1828 case 'v': 1829 verbose = 1; 1830 break; 1831 #endif 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, '\0', sizeof(password)); 1860 password[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 } 1906 } 1907 argc -= optind; 1908 argv += optind; 1909 if (argc != 1) { 1910 usermgmt_usage("groupadd"); 1911 } 1912 checkeuid(); 1913 if (gid < 0 && !getnextgid(&gid, LowGid, HighGid)) { 1914 err(EXIT_FAILURE, "can't add group: can't get next gid"); 1915 } 1916 if (!dupgid && getgrgid((gid_t) gid) != NULL) { 1917 errx(EXIT_FAILURE, "can't add group: gid %d is a duplicate", gid); 1918 } 1919 if (!valid_group(*argv)) { 1920 warnx("warning - invalid group name `%s'", *argv); 1921 } 1922 openlog("groupadd", LOG_PID, LOG_USER); 1923 if (!creategid(*argv, gid, "")) { 1924 errx(EXIT_FAILURE, "can't add group: problems with %s file", _PATH_GROUP); 1925 } 1926 return EXIT_SUCCESS; 1927 } 1928 1929 #ifdef EXTENSIONS 1930 #define GROUP_DEL_OPT_EXTENSIONS "v" 1931 #else 1932 #define GROUP_DEL_OPT_EXTENSIONS 1933 #endif 1934 1935 /* remove a group */ 1936 int 1937 groupdel(int argc, char **argv) 1938 { 1939 int c; 1940 1941 while ((c = getopt(argc, argv, "" GROUP_DEL_OPT_EXTENSIONS)) != -1) { 1942 switch(c) { 1943 #ifdef EXTENSIONS 1944 case 'v': 1945 verbose = 1; 1946 break; 1947 #endif 1948 } 1949 } 1950 argc -= optind; 1951 argv += optind; 1952 if (argc != 1) { 1953 usermgmt_usage("groupdel"); 1954 } 1955 checkeuid(); 1956 openlog("groupdel", LOG_PID, LOG_USER); 1957 if (!modify_gid(*argv, NULL)) { 1958 err(EXIT_FAILURE, "can't change %s file", _PATH_GROUP); 1959 } 1960 return EXIT_SUCCESS; 1961 } 1962 1963 #ifdef EXTENSIONS 1964 #define GROUP_MOD_OPT_EXTENSIONS "v" 1965 #else 1966 #define GROUP_MOD_OPT_EXTENSIONS 1967 #endif 1968 1969 /* modify a group */ 1970 int 1971 groupmod(int argc, char **argv) 1972 { 1973 struct group *grp; 1974 char buf[MaxEntryLen]; 1975 char *newname; 1976 char **cpp; 1977 int dupgid; 1978 int gid; 1979 int cc; 1980 int c; 1981 1982 gid = -1; 1983 dupgid = 0; 1984 newname = NULL; 1985 while ((c = getopt(argc, argv, "g:on:" GROUP_MOD_OPT_EXTENSIONS)) != -1) { 1986 switch(c) { 1987 case 'g': 1988 if (!is_number(optarg)) { 1989 errx(EXIT_FAILURE, "When using [-g gid], the gid must be numeric"); 1990 } 1991 gid = atoi(optarg); 1992 break; 1993 case 'o': 1994 dupgid = 1; 1995 break; 1996 case 'n': 1997 memsave(&newname, optarg, strlen(optarg)); 1998 break; 1999 #ifdef EXTENSIONS 2000 case 'v': 2001 verbose = 1; 2002 break; 2003 #endif 2004 } 2005 } 2006 argc -= optind; 2007 argv += optind; 2008 if (argc != 1) { 2009 usermgmt_usage("groupmod"); 2010 } 2011 checkeuid(); 2012 if (gid < 0 && newname == NULL) { 2013 err(EXIT_FAILURE, "Nothing to change"); 2014 } 2015 if (dupgid && gid < 0) { 2016 err(EXIT_FAILURE, "Duplicate which gid?"); 2017 } 2018 if ((grp = getgrnam(*argv)) == NULL) { 2019 err(EXIT_FAILURE, "can't find group `%s' to modify", *argv); 2020 } 2021 if (!is_local(*argv, _PATH_GROUP)) { 2022 errx(EXIT_FAILURE, "Group `%s' must be a local group", *argv); 2023 } 2024 if (newname != NULL && !valid_group(newname)) { 2025 warn("warning - invalid group name `%s'", newname); 2026 } 2027 cc = snprintf(buf, sizeof(buf), "%s:%s:%d:", 2028 (newname) ? newname : grp->gr_name, 2029 grp->gr_passwd, 2030 (gid < 0) ? grp->gr_gid : gid); 2031 for (cpp = grp->gr_mem ; *cpp && cc < sizeof(buf) ; cpp++) { 2032 cc += snprintf(&buf[cc], sizeof(buf) - cc, "%s%s", *cpp, 2033 (cpp[1] == NULL) ? "" : ","); 2034 } 2035 cc += snprintf(&buf[cc], sizeof(buf) - cc, "\n"); 2036 openlog("groupmod", LOG_PID, LOG_USER); 2037 if (!modify_gid(*argv, buf)) { 2038 err(EXIT_FAILURE, "can't change %s file", _PATH_GROUP); 2039 } 2040 return EXIT_SUCCESS; 2041 } 2042 2043 #ifdef EXTENSIONS 2044 /* display user information */ 2045 int 2046 userinfo(int argc, char **argv) 2047 { 2048 struct passwd *pwp; 2049 struct group *grp; 2050 char buf[MaxEntryLen]; 2051 char **cpp; 2052 int exists; 2053 int cc; 2054 int i; 2055 2056 exists = 0; 2057 while ((i = getopt(argc, argv, "ev")) != -1) { 2058 switch(i) { 2059 case 'e': 2060 exists = 1; 2061 break; 2062 case 'v': 2063 verbose = 1; 2064 break; 2065 } 2066 } 2067 argc -= optind; 2068 argv += optind; 2069 if (argc != 1) { 2070 usermgmt_usage("userinfo"); 2071 } 2072 pwp = find_user_info(*argv); 2073 if (exists) { 2074 exit((pwp) ? EXIT_SUCCESS : EXIT_FAILURE); 2075 } 2076 if (pwp == NULL) { 2077 errx(EXIT_FAILURE, "can't find user `%s'", *argv); 2078 } 2079 (void) printf("login\t%s\n", pwp->pw_name); 2080 (void) printf("passwd\t%s\n", pwp->pw_passwd); 2081 (void) printf("uid\t%d\n", pwp->pw_uid); 2082 for (cc = 0 ; (grp = getgrent()) != NULL ; ) { 2083 for (cpp = grp->gr_mem ; *cpp ; cpp++) { 2084 if (strcmp(*cpp, *argv) == 0 && grp->gr_gid != pwp->pw_gid) { 2085 cc += snprintf(&buf[cc], sizeof(buf) - cc, "%s ", grp->gr_name); 2086 } 2087 } 2088 } 2089 if ((grp = getgrgid(pwp->pw_gid)) == NULL) { 2090 (void) printf("groups\t%d %s\n", pwp->pw_gid, buf); 2091 } else { 2092 (void) printf("groups\t%s %s\n", grp->gr_name, buf); 2093 } 2094 (void) printf("change\t%s", pwp->pw_change ? ctime(&pwp->pw_change) : "NEVER\n"); 2095 #ifdef EXTENSIONS 2096 (void) printf("class\t%s\n", pwp->pw_class); 2097 #endif 2098 (void) printf("gecos\t%s\n", pwp->pw_gecos); 2099 (void) printf("dir\t%s\n", pwp->pw_dir); 2100 (void) printf("shell\t%s\n", pwp->pw_shell); 2101 (void) printf("expire\t%s", pwp->pw_expire ? ctime(&pwp->pw_expire) : "NEVER\n"); 2102 return EXIT_SUCCESS; 2103 } 2104 #endif 2105 2106 #ifdef EXTENSIONS 2107 /* display user information */ 2108 int 2109 groupinfo(int argc, char **argv) 2110 { 2111 struct group *grp; 2112 char **cpp; 2113 int exists; 2114 int i; 2115 2116 exists = 0; 2117 while ((i = getopt(argc, argv, "ev")) != -1) { 2118 switch(i) { 2119 case 'e': 2120 exists = 1; 2121 break; 2122 case 'v': 2123 verbose = 1; 2124 break; 2125 } 2126 } 2127 argc -= optind; 2128 argv += optind; 2129 if (argc != 1) { 2130 usermgmt_usage("groupinfo"); 2131 } 2132 grp = find_group_info(*argv); 2133 if (exists) { 2134 exit((grp) ? EXIT_SUCCESS : EXIT_FAILURE); 2135 } 2136 if (grp == NULL) { 2137 errx(EXIT_FAILURE, "can't find group `%s'", *argv); 2138 } 2139 (void) printf("name\t%s\n", grp->gr_name); 2140 (void) printf("passwd\t%s\n", grp->gr_passwd); 2141 (void) printf("gid\t%d\n", grp->gr_gid); 2142 (void) printf("members\t"); 2143 for (cpp = grp->gr_mem ; *cpp ; cpp++) { 2144 (void) printf("%s ", *cpp); 2145 } 2146 (void) fputc('\n', stdout); 2147 return EXIT_SUCCESS; 2148 } 2149 #endif 2150