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