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