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