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