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