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