pw_user.c revision 285430
1/*- 2 * Copyright (C) 1996 3 * David L. Nugent. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28#ifndef lint 29static const char rcsid[] = 30 "$FreeBSD: head/usr.sbin/pw/pw_user.c 285430 2015-07-12 20:29:51Z bapt $"; 31#endif /* not lint */ 32 33#include <ctype.h> 34#include <err.h> 35#include <fcntl.h> 36#include <sys/param.h> 37#include <dirent.h> 38#include <paths.h> 39#include <termios.h> 40#include <sys/types.h> 41#include <sys/time.h> 42#include <sys/resource.h> 43#include <login_cap.h> 44#include <pwd.h> 45#include <grp.h> 46#include <libutil.h> 47#include "pw.h" 48#include "bitmap.h" 49 50#define LOGNAMESIZE (MAXLOGNAME-1) 51 52static char locked_str[] = "*LOCKED*"; 53 54static int pw_userdel(char *name, long id); 55static int print_user(struct passwd * pwd); 56static uid_t pw_uidpolicy(struct userconf * cnf, long id); 57static uid_t pw_gidpolicy(struct cargs * args, char *nam, gid_t prefer); 58static time_t pw_pwdpolicy(struct userconf * cnf, struct cargs * args); 59static time_t pw_exppolicy(struct userconf * cnf, struct cargs * args); 60static char *pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user); 61static char *pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell); 62static char *pw_password(struct userconf * cnf, char const * user); 63static char *shell_path(char const * path, char *shells[], char *sh); 64static void rmat(uid_t uid); 65static void rmopie(char const * name); 66 67static void 68create_and_populate_homedir(struct passwd *pwd) 69{ 70 struct userconf *cnf = conf.userconf; 71 const char *skeldir; 72 int skelfd = -1; 73 74 skeldir = cnf->dotdir; 75 76 if (skeldir != NULL && *skeldir != '\0') { 77 skelfd = openat(conf.rootfd, cnf->dotdir, 78 O_DIRECTORY|O_CLOEXEC); 79 } 80 81 copymkdir(conf.rootfd, pwd->pw_dir, skelfd, cnf->homemode, pwd->pw_uid, 82 pwd->pw_gid, 0); 83 pw_log(cnf, M_ADD, W_USER, "%s(%u) home %s made", pwd->pw_name, 84 pwd->pw_uid, pwd->pw_dir); 85} 86 87static int 88set_passwd(struct passwd *pwd, bool update) 89{ 90 int b, istty; 91 struct termios t, n; 92 login_cap_t *lc; 93 char line[_PASSWORD_LEN+1]; 94 char *p; 95 96 if (conf.fd == '-') { 97 if (!pwd->pw_passwd || *pwd->pw_passwd != '*') { 98 pwd->pw_passwd = "*"; /* No access */ 99 return (1); 100 } 101 return (0); 102 } 103 104 if ((istty = isatty(conf.fd))) { 105 if (tcgetattr(conf.fd, &t) == -1) 106 istty = 0; 107 else { 108 n = t; 109 n.c_lflag &= ~(ECHO); 110 tcsetattr(conf.fd, TCSANOW, &n); 111 printf("%s%spassword for user %s:", 112 update ? "new " : "", 113 conf.precrypted ? "encrypted " : "", 114 pwd->pw_name); 115 fflush(stdout); 116 } 117 } 118 b = read(conf.fd, line, sizeof(line) - 1); 119 if (istty) { /* Restore state */ 120 tcsetattr(conf.fd, TCSANOW, &t); 121 fputc('\n', stdout); 122 fflush(stdout); 123 } 124 125 if (b < 0) 126 err(EX_IOERR, "-%c file descriptor", 127 conf.precrypted ? 'H' : 'h'); 128 line[b] = '\0'; 129 if ((p = strpbrk(line, "\r\n")) != NULL) 130 *p = '\0'; 131 if (!*line) 132 errx(EX_DATAERR, "empty password read on file descriptor %d", 133 conf.fd); 134 if (conf.precrypted) { 135 if (strchr(line, ':') != NULL) 136 errx(EX_DATAERR, "bad encrypted password"); 137 pwd->pw_passwd = line; 138 } else { 139 lc = login_getpwclass(pwd); 140 if (lc == NULL || 141 login_setcryptfmt(lc, "sha512", NULL) == NULL) 142 warn("setting crypt(3) format"); 143 login_close(lc); 144 pwd->pw_passwd = pw_pwcrypt(line); 145 } 146 return (1); 147} 148 149int 150pw_usernext(struct userconf *cnf, bool quiet) 151{ 152 uid_t next = pw_uidpolicy(cnf, -1); 153 154 if (quiet) 155 return (next); 156 157 printf("%u:", next); 158 pw_groupnext(cnf, quiet); 159 160 return (EXIT_SUCCESS); 161} 162 163static int 164pw_usershow(char *name, long id, struct passwd *fakeuser) 165{ 166 struct passwd *pwd = NULL; 167 168 if (id < 0 && name == NULL && !conf.all) 169 errx(EX_DATAERR, "username or id or '-a' required"); 170 171 if (conf.all) { 172 SETPWENT(); 173 while ((pwd = GETPWENT()) != NULL) 174 print_user(pwd); 175 ENDPWENT(); 176 return (EXIT_SUCCESS); 177 } 178 179 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id); 180 if (pwd == NULL) { 181 if (conf.force) { 182 pwd = fakeuser; 183 } else { 184 if (name == NULL) 185 errx(EX_NOUSER, "no such uid `%ld'", id); 186 errx(EX_NOUSER, "no such user `%s'", name); 187 } 188 } 189 190 return (print_user(pwd)); 191} 192 193static void 194perform_chgpwent(const char *name, struct passwd *pwd) 195{ 196 int rc; 197 198 rc = chgpwent(name, pwd); 199 if (rc == -1) 200 errx(EX_IOERR, "user '%s' does not exist (NIS?)", pwd->pw_name); 201 else if (rc != 0) 202 err(EX_IOERR, "passwd file update"); 203 204 if (conf.userconf->nispasswd && *conf.userconf->nispasswd == '/') { 205 rc = chgnispwent(conf.userconf->nispasswd, name, pwd); 206 if (rc == -1) 207 warn("User '%s' not found in NIS passwd", pwd->pw_name); 208 else 209 warn("NIS passwd update"); 210 /* NOTE: NIS-only update errors are not fatal */ 211 } 212} 213 214/* 215 * The M_LOCK and M_UNLOCK functions simply add or remove 216 * a "*LOCKED*" prefix from in front of the password to 217 * prevent it decoding correctly, and therefore prevents 218 * access. Of course, this only prevents access via 219 * password authentication (not ssh, kerberos or any 220 * other method that does not use the UNIX password) but 221 * that is a known limitation. 222 */ 223static int 224pw_userlock(char *name, long id, int mode) 225{ 226 struct passwd *pwd = NULL; 227 char *passtmp = NULL; 228 bool locked = false; 229 230 if (id < 0 && name == NULL) 231 errx(EX_DATAERR, "username or id required"); 232 233 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id); 234 if (pwd == NULL) { 235 if (name == NULL) 236 errx(EX_NOUSER, "no such uid `%ld'", id); 237 errx(EX_NOUSER, "no such user `%s'", name); 238 } 239 240 if (name == NULL) 241 name = pwd->pw_name; 242 243 if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str) -1) == 0) 244 locked = true; 245 if (mode == M_LOCK && locked) 246 errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name); 247 if (mode == M_UNLOCK && !locked) 248 errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name); 249 250 if (mode == M_LOCK) { 251 asprintf(&passtmp, "%s%s", locked_str, pwd->pw_passwd); 252 if (passtmp == NULL) /* disaster */ 253 errx(EX_UNAVAILABLE, "out of memory"); 254 pwd->pw_passwd = passtmp; 255 } else { 256 pwd->pw_passwd += sizeof(locked_str)-1; 257 } 258 259 perform_chgpwent(name, pwd); 260 free(passtmp); 261 262 return (EXIT_SUCCESS); 263} 264 265/*- 266 * -C config configuration file 267 * -q quiet operation 268 * -n name login name 269 * -u uid user id 270 * -c comment user name/comment 271 * -d directory home directory 272 * -e date account expiry date 273 * -p date password expiry date 274 * -g grp primary group 275 * -G grp1,grp2 additional groups 276 * -m [ -k dir ] create and set up home 277 * -s shell name of login shell 278 * -o duplicate uid ok 279 * -L class user class 280 * -l name new login name 281 * -h fd password filehandle 282 * -H fd encrypted password filehandle 283 * -F force print or add 284 * Setting defaults: 285 * -D set user defaults 286 * -b dir default home root dir 287 * -e period default expiry period 288 * -p period default password change period 289 * -g group default group 290 * -G grp1,grp2.. default additional groups 291 * -L class default login class 292 * -k dir default home skeleton 293 * -s shell default shell 294 * -w method default password method 295 */ 296 297int 298pw_user(int mode, char *name, long id, struct cargs * args) 299{ 300 int rc, edited = 0; 301 char *p = NULL; 302 struct carg *arg; 303 struct passwd *pwd = NULL; 304 struct group *grp; 305 struct stat st; 306 struct userconf *cnf; 307 char line[_PASSWORD_LEN+1]; 308 char path[MAXPATHLEN]; 309 FILE *fp; 310 char *dmode_c; 311 void *set = NULL; 312 313 static struct passwd fakeuser = 314 { 315 "nouser", 316 "*", 317 -1, 318 -1, 319 0, 320 "", 321 "User &", 322 "/nonexistent", 323 "/bin/sh", 324 0 325#if defined(__FreeBSD__) 326 ,0 327#endif 328 }; 329 330 cnf = conf.userconf; 331 332 if (mode == M_NEXT) 333 return (pw_usernext(cnf, conf.quiet)); 334 335 if (mode == M_PRINT) 336 return (pw_usershow(name, id, &fakeuser)); 337 338 if (mode == M_DELETE) 339 return (pw_userdel(name, id)); 340 341 if (mode == M_LOCK || mode == M_UNLOCK) 342 return (pw_userlock(name, id, mode)); 343 344 /* 345 * We can do all of the common legwork here 346 */ 347 348 if ((arg = getarg(args, 'b')) != NULL) { 349 cnf->home = arg->val; 350 } 351 352 if ((arg = getarg(args, 'M')) != NULL) { 353 dmode_c = arg->val; 354 if ((set = setmode(dmode_c)) == NULL) 355 errx(EX_DATAERR, "invalid directory creation mode '%s'", 356 dmode_c); 357 cnf->homemode = getmode(set, _DEF_DIRMODE); 358 free(set); 359 } 360 361 /* 362 * If we'll need to use it or we're updating it, 363 * then create the base home directory if necessary 364 */ 365 if (arg != NULL || getarg(args, 'm') != NULL) { 366 int l = strlen(cnf->home); 367 368 if (l > 1 && cnf->home[l-1] == '/') /* Shave off any trailing path delimiter */ 369 cnf->home[--l] = '\0'; 370 371 if (l < 2 || *cnf->home != '/') /* Check for absolute path name */ 372 errx(EX_DATAERR, "invalid base directory for home '%s'", cnf->home); 373 374 if (stat(cnf->home, &st) == -1) { 375 char dbuf[MAXPATHLEN]; 376 377 /* 378 * This is a kludge especially for Joerg :) 379 * If the home directory would be created in the root partition, then 380 * we really create it under /usr which is likely to have more space. 381 * But we create a symlink from cnf->home -> "/usr" -> cnf->home 382 */ 383 if (strchr(cnf->home+1, '/') == NULL) { 384 snprintf(dbuf, MAXPATHLEN, "/usr%s", cnf->home); 385 if (mkdir(dbuf, _DEF_DIRMODE) != -1 || errno == EEXIST) { 386 chown(dbuf, 0, 0); 387 /* 388 * Skip first "/" and create symlink: 389 * /home -> usr/home 390 */ 391 symlink(dbuf+1, cnf->home); 392 } 393 /* If this falls, fall back to old method */ 394 } 395 strlcpy(dbuf, cnf->home, sizeof(dbuf)); 396 p = dbuf; 397 if (stat(dbuf, &st) == -1) { 398 while ((p = strchr(p + 1, '/')) != NULL) { 399 *p = '\0'; 400 if (stat(dbuf, &st) == -1) { 401 if (mkdir(dbuf, _DEF_DIRMODE) == -1) 402 err(EX_OSFILE, "mkdir '%s'", dbuf); 403 chown(dbuf, 0, 0); 404 } else if (!S_ISDIR(st.st_mode)) 405 errx(EX_OSFILE, "'%s' (root home parent) is not a directory", dbuf); 406 *p = '/'; 407 } 408 } 409 if (stat(dbuf, &st) == -1) { 410 if (mkdir(dbuf, _DEF_DIRMODE) == -1) 411 err(EX_OSFILE, "mkdir '%s'", dbuf); 412 chown(dbuf, 0, 0); 413 } 414 } else if (!S_ISDIR(st.st_mode)) 415 errx(EX_OSFILE, "root home `%s' is not a directory", cnf->home); 416 } 417 418 if ((arg = getarg(args, 'e')) != NULL) 419 cnf->expire_days = atoi(arg->val); 420 421 if ((arg = getarg(args, 'y')) != NULL) 422 cnf->nispasswd = arg->val; 423 424 if ((arg = getarg(args, 'p')) != NULL && arg->val) 425 cnf->password_days = atoi(arg->val); 426 427 if ((arg = getarg(args, 'g')) != NULL) { 428 if (!*(p = arg->val)) /* Handle empty group list specially */ 429 cnf->default_group = ""; 430 else { 431 if ((grp = GETGRNAM(p)) == NULL) { 432 if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL) 433 errx(EX_NOUSER, "group `%s' does not exist", p); 434 } 435 cnf->default_group = newstr(grp->gr_name); 436 } 437 } 438 if ((arg = getarg(args, 'L')) != NULL) 439 cnf->default_class = pw_checkname(arg->val, 0); 440 441 if ((arg = getarg(args, 'G')) != NULL && arg->val) { 442 for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) { 443 if ((grp = GETGRNAM(p)) == NULL) { 444 if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL) 445 errx(EX_NOUSER, "group `%s' does not exist", p); 446 } 447 sl_add(cnf->groups, newstr(grp->gr_name)); 448 } 449 } 450 451 if ((arg = getarg(args, 'k')) != NULL) { 452 if (stat(cnf->dotdir = arg->val, &st) == -1 || !S_ISDIR(st.st_mode)) 453 errx(EX_OSFILE, "skeleton `%s' is not a directory or does not exist", cnf->dotdir); 454 } 455 456 if ((arg = getarg(args, 's')) != NULL) 457 cnf->shell_default = arg->val; 458 459 if ((arg = getarg(args, 'w')) != NULL) 460 cnf->default_password = boolean_val(arg->val, cnf->default_password); 461 if (mode == M_ADD && getarg(args, 'D')) { 462 if (name != NULL) 463 errx(EX_DATAERR, "can't combine `-D' with `-n name'"); 464 if ((arg = getarg(args, 'u')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) { 465 if ((cnf->min_uid = (uid_t) atoi(p)) == 0) 466 cnf->min_uid = 1000; 467 if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_uid = (uid_t) atoi(p)) < cnf->min_uid) 468 cnf->max_uid = 32000; 469 } 470 if ((arg = getarg(args, 'i')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) { 471 if ((cnf->min_gid = (gid_t) atoi(p)) == 0) 472 cnf->min_gid = 1000; 473 if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_gid = (gid_t) atoi(p)) < cnf->min_gid) 474 cnf->max_gid = 32000; 475 } 476 477 if (write_userconfig(conf.config)) 478 return (EXIT_SUCCESS); 479 err(EX_IOERR, "config udpate"); 480 } 481 482 if (name != NULL) 483 pwd = GETPWNAM(pw_checkname(name, 0)); 484 485 if (id < 0 && name == NULL) 486 errx(EX_DATAERR, "user name or id required"); 487 488 /* 489 * Update require that the user exists 490 */ 491 if (mode == M_UPDATE) { 492 493 if (name == NULL && pwd == NULL) /* Try harder */ 494 pwd = GETPWUID(id); 495 496 if (pwd == NULL) { 497 if (name == NULL) 498 errx(EX_NOUSER, "no such uid `%ld'", id); 499 errx(EX_NOUSER, "no such user `%s'", name); 500 } 501 502 if (name == NULL) 503 name = pwd->pw_name; 504 505 /* 506 * The rest is edit code 507 */ 508 if (conf.newname != NULL) { 509 if (strcmp(pwd->pw_name, "root") == 0) 510 errx(EX_DATAERR, "can't rename `root' account"); 511 pwd->pw_name = pw_checkname(conf.newname, 0); 512 edited = 1; 513 } 514 515 if (id > 0 && isdigit((unsigned char)*arg->val)) { 516 pwd->pw_uid = (uid_t)id; 517 edited = 1; 518 if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0) 519 errx(EX_DATAERR, "can't change uid of `root' account"); 520 if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0) 521 warnx("WARNING: account `%s' will have a uid of 0 (superuser access!)", pwd->pw_name); 522 } 523 524 if ((arg = getarg(args, 'g')) != NULL && pwd->pw_uid != 0) { /* Already checked this */ 525 gid_t newgid = (gid_t) GETGRNAM(cnf->default_group)->gr_gid; 526 if (newgid != pwd->pw_gid) { 527 edited = 1; 528 pwd->pw_gid = newgid; 529 } 530 } 531 532 if ((arg = getarg(args, 'p')) != NULL) { 533 if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) { 534 if (pwd->pw_change != 0) { 535 pwd->pw_change = 0; 536 edited = 1; 537 } 538 } 539 else { 540 time_t now = time(NULL); 541 time_t expire = parse_date(now, arg->val); 542 543 if (pwd->pw_change != expire) { 544 pwd->pw_change = expire; 545 edited = 1; 546 } 547 } 548 } 549 550 if ((arg = getarg(args, 'e')) != NULL) { 551 if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) { 552 if (pwd->pw_expire != 0) { 553 pwd->pw_expire = 0; 554 edited = 1; 555 } 556 } 557 else { 558 time_t now = time(NULL); 559 time_t expire = parse_date(now, arg->val); 560 561 if (pwd->pw_expire != expire) { 562 pwd->pw_expire = expire; 563 edited = 1; 564 } 565 } 566 } 567 568 if ((arg = getarg(args, 's')) != NULL) { 569 char *shell = shell_path(cnf->shelldir, cnf->shells, arg->val); 570 if (shell == NULL) 571 shell = ""; 572 if (strcmp(shell, pwd->pw_shell) != 0) { 573 pwd->pw_shell = shell; 574 edited = 1; 575 } 576 } 577 578 if (getarg(args, 'L')) { 579 if (cnf->default_class == NULL) 580 cnf->default_class = ""; 581 if (strcmp(pwd->pw_class, cnf->default_class) != 0) { 582 pwd->pw_class = cnf->default_class; 583 edited = 1; 584 } 585 } 586 587 if ((arg = getarg(args, 'd')) != NULL) { 588 if (strcmp(pwd->pw_dir, arg->val)) 589 edited = 1; 590 if (stat(pwd->pw_dir = arg->val, &st) == -1) { 591 if (getarg(args, 'm') == NULL && strcmp(pwd->pw_dir, "/nonexistent") != 0) 592 warnx("WARNING: home `%s' does not exist", pwd->pw_dir); 593 } else if (!S_ISDIR(st.st_mode)) 594 warnx("WARNING: home `%s' is not a directory", pwd->pw_dir); 595 } 596 597 if ((arg = getarg(args, 'w')) != NULL && conf.fd == -1) { 598 login_cap_t *lc; 599 600 lc = login_getpwclass(pwd); 601 if (lc == NULL || 602 login_setcryptfmt(lc, "sha512", NULL) == NULL) 603 warn("setting crypt(3) format"); 604 login_close(lc); 605 pwd->pw_passwd = pw_password(cnf, pwd->pw_name); 606 edited = 1; 607 } 608 609 } else { 610 login_cap_t *lc; 611 612 /* 613 * Add code 614 */ 615 616 if (name == NULL) /* Required */ 617 errx(EX_DATAERR, "login name required"); 618 else if ((pwd = GETPWNAM(name)) != NULL) /* Exists */ 619 errx(EX_DATAERR, "login name `%s' already exists", name); 620 621 /* 622 * Now, set up defaults for a new user 623 */ 624 pwd = &fakeuser; 625 pwd->pw_name = name; 626 pwd->pw_class = cnf->default_class ? cnf->default_class : ""; 627 pwd->pw_uid = pw_uidpolicy(cnf, id); 628 pwd->pw_gid = pw_gidpolicy(args, pwd->pw_name, (gid_t) pwd->pw_uid); 629 pwd->pw_change = pw_pwdpolicy(cnf, args); 630 pwd->pw_expire = pw_exppolicy(cnf, args); 631 pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name); 632 pwd->pw_shell = pw_shellpolicy(cnf, args, NULL); 633 lc = login_getpwclass(pwd); 634 if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) 635 warn("setting crypt(3) format"); 636 login_close(lc); 637 pwd->pw_passwd = pw_password(cnf, pwd->pw_name); 638 edited = 1; 639 640 if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0) 641 warnx("WARNING: new account `%s' has a uid of 0 (superuser access!)", pwd->pw_name); 642 } 643 644 /* 645 * Shared add/edit code 646 */ 647 if (conf.gecos != NULL) { 648 if (strcmp(pwd->pw_gecos, conf.gecos) != 0) { 649 pwd->pw_gecos = conf.gecos; 650 edited = 1; 651 } 652 } 653 654 if (conf.fd != -1) 655 edited = set_passwd(pwd, mode == M_UPDATE); 656 657 /* 658 * Special case: -N only displays & exits 659 */ 660 if (conf.dryrun) 661 return print_user(pwd); 662 663 if (mode == M_ADD) { 664 edited = 1; /* Always */ 665 rc = addpwent(pwd); 666 if (rc == -1) 667 errx(EX_IOERR, "user '%s' already exists", 668 pwd->pw_name); 669 else if (rc != 0) 670 err(EX_IOERR, "passwd file update"); 671 if (cnf->nispasswd && *cnf->nispasswd=='/') { 672 rc = addnispwent(cnf->nispasswd, pwd); 673 if (rc == -1) 674 warnx("User '%s' already exists in NIS passwd", pwd->pw_name); 675 else 676 warn("NIS passwd update"); 677 /* NOTE: we treat NIS-only update errors as non-fatal */ 678 } 679 } else if (mode == M_UPDATE && edited) /* Only updated this if required */ 680 perform_chgpwent(name, pwd); 681 682 /* 683 * Ok, user is created or changed - now edit group file 684 */ 685 686 if (mode == M_ADD || getarg(args, 'G') != NULL) { 687 int j; 688 size_t i; 689 /* First remove the user from all group */ 690 SETGRENT(); 691 while ((grp = GETGRENT()) != NULL) { 692 char group[MAXLOGNAME]; 693 if (grp->gr_mem == NULL) 694 continue; 695 for (i = 0; grp->gr_mem[i] != NULL; i++) { 696 if (strcmp(grp->gr_mem[i] , pwd->pw_name) != 0) 697 continue; 698 for (j = i; grp->gr_mem[j] != NULL ; j++) 699 grp->gr_mem[j] = grp->gr_mem[j+1]; 700 strlcpy(group, grp->gr_name, MAXLOGNAME); 701 chggrent(group, grp); 702 } 703 } 704 ENDGRENT(); 705 706 /* now add to group where needed */ 707 for (i = 0; i < cnf->groups->sl_cur; i++) { 708 grp = GETGRNAM(cnf->groups->sl_str[i]); 709 grp = gr_add(grp, pwd->pw_name); 710 /* 711 * grp can only be NULL in 2 cases: 712 * - the new member is already a member 713 * - a problem with memory occurs 714 * in both cases we want to skip now. 715 */ 716 if (grp == NULL) 717 continue; 718 chggrent(grp->gr_name, grp); 719 free(grp); 720 } 721 } 722 723 724 /* go get a current version of pwd */ 725 pwd = GETPWNAM(name); 726 if (pwd == NULL) { 727 /* This will fail when we rename, so special case that */ 728 if (mode == M_UPDATE && conf.newname != NULL) { 729 name = conf.newname; /* update new name */ 730 pwd = GETPWNAM(name); /* refetch renamed rec */ 731 } 732 } 733 if (pwd == NULL) /* can't go on without this */ 734 errx(EX_NOUSER, "user '%s' disappeared during update", name); 735 736 grp = GETGRGID(pwd->pw_gid); 737 pw_log(cnf, mode, W_USER, "%s(%u):%s(%u):%s:%s:%s", 738 pwd->pw_name, pwd->pw_uid, 739 grp ? grp->gr_name : "unknown", (grp ? grp->gr_gid : (uid_t)-1), 740 pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell); 741 742 /* 743 * If adding, let's touch and chown the user's mail file. This is not 744 * strictly necessary under BSD with a 0755 maildir but it also 745 * doesn't hurt anything to create the empty mailfile 746 */ 747 if (mode == M_ADD) { 748 if (PWALTDIR() != PWF_ALT) { 749 arg = getarg(args, 'R'); 750 snprintf(path, sizeof(path), "%s%s/%s", 751 arg ? arg->val : "", _PATH_MAILDIR, pwd->pw_name); 752 close(open(path, O_RDWR | O_CREAT, 0600)); /* Preserve contents & 753 * mtime */ 754 chown(path, pwd->pw_uid, pwd->pw_gid); 755 } 756 } 757 758 /* 759 * Let's create and populate the user's home directory. Note 760 * that this also `works' for editing users if -m is used, but 761 * existing files will *not* be overwritten. 762 */ 763 if (PWALTDIR() != PWF_ALT && getarg(args, 'm') != NULL && pwd->pw_dir && 764 *pwd->pw_dir == '/' && pwd->pw_dir[1]) 765 create_and_populate_homedir(pwd); 766 767 /* 768 * Finally, send mail to the new user as well, if we are asked to 769 */ 770 if (mode == M_ADD && !PWALTDIR() && cnf->newmail && *cnf->newmail && (fp = fopen(cnf->newmail, "r")) != NULL) { 771 FILE *pfp = popen(_PATH_SENDMAIL " -t", "w"); 772 773 if (pfp == NULL) 774 warn("sendmail"); 775 else { 776 fprintf(pfp, "From: root\n" "To: %s\n" "Subject: Welcome!\n\n", pwd->pw_name); 777 while (fgets(line, sizeof(line), fp) != NULL) { 778 /* Do substitutions? */ 779 fputs(line, pfp); 780 } 781 pclose(pfp); 782 pw_log(cnf, mode, W_USER, "%s(%u) new user mail sent", 783 pwd->pw_name, pwd->pw_uid); 784 } 785 fclose(fp); 786 } 787 788 return EXIT_SUCCESS; 789} 790 791 792static uid_t 793pw_uidpolicy(struct userconf * cnf, long id) 794{ 795 struct passwd *pwd; 796 uid_t uid = (uid_t) - 1; 797 798 /* 799 * Check the given uid, if any 800 */ 801 if (id > 0) { 802 uid = (uid_t) id; 803 804 if ((pwd = GETPWUID(uid)) != NULL && conf.checkduplicate) 805 errx(EX_DATAERR, "uid `%u' has already been allocated", pwd->pw_uid); 806 } else { 807 struct bitmap bm; 808 809 /* 810 * We need to allocate the next available uid under one of 811 * two policies a) Grab the first unused uid b) Grab the 812 * highest possible unused uid 813 */ 814 if (cnf->min_uid >= cnf->max_uid) { /* Sanity 815 * claus^H^H^H^Hheck */ 816 cnf->min_uid = 1000; 817 cnf->max_uid = 32000; 818 } 819 bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1); 820 821 /* 822 * Now, let's fill the bitmap from the password file 823 */ 824 SETPWENT(); 825 while ((pwd = GETPWENT()) != NULL) 826 if (pwd->pw_uid >= (uid_t) cnf->min_uid && pwd->pw_uid <= (uid_t) cnf->max_uid) 827 bm_setbit(&bm, pwd->pw_uid - cnf->min_uid); 828 ENDPWENT(); 829 830 /* 831 * Then apply the policy, with fallback to reuse if necessary 832 */ 833 if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid) 834 uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid); 835 836 /* 837 * Another sanity check 838 */ 839 if (uid < cnf->min_uid || uid > cnf->max_uid) 840 errx(EX_SOFTWARE, "unable to allocate a new uid - range fully used"); 841 bm_dealloc(&bm); 842 } 843 return uid; 844} 845 846 847static uid_t 848pw_gidpolicy(struct cargs * args, char *nam, gid_t prefer) 849{ 850 struct group *grp; 851 gid_t gid = (uid_t) - 1; 852 struct carg *a_gid = getarg(args, 'g'); 853 struct userconf *cnf = conf.userconf; 854 855 /* 856 * If no arg given, see if default can help out 857 */ 858 if (a_gid == NULL && cnf->default_group && *cnf->default_group) 859 a_gid = addarg(args, 'g', cnf->default_group); 860 861 /* 862 * Check the given gid, if any 863 */ 864 SETGRENT(); 865 if (a_gid != NULL) { 866 if ((grp = GETGRNAM(a_gid->val)) == NULL) { 867 gid = (gid_t) atol(a_gid->val); 868 if ((gid == 0 && !isdigit((unsigned char)*a_gid->val)) || (grp = GETGRGID(gid)) == NULL) 869 errx(EX_NOUSER, "group `%s' is not defined", a_gid->val); 870 } 871 gid = grp->gr_gid; 872 } else if ((grp = GETGRNAM(nam)) != NULL && 873 (grp->gr_mem == NULL || grp->gr_mem[0] == NULL)) { 874 gid = grp->gr_gid; /* Already created? Use it anyway... */ 875 } else { 876 gid_t grid = -1; 877 878 /* 879 * We need to auto-create a group with the user's name. We 880 * can send all the appropriate output to our sister routine 881 * bit first see if we can create a group with gid==uid so we 882 * can keep the user and group ids in sync. We purposely do 883 * NOT check the gid range if we can force the sync. If the 884 * user's name dups an existing group, then the group add 885 * function will happily handle that case for us and exit. 886 */ 887 if (GETGRGID(prefer) == NULL) 888 grid = prefer; 889 if (conf.dryrun) { 890 gid = pw_groupnext(cnf, true); 891 } else { 892 pw_group(M_ADD, nam, grid, NULL); 893 if ((grp = GETGRNAM(nam)) != NULL) 894 gid = grp->gr_gid; 895 } 896 } 897 ENDGRENT(); 898 return gid; 899} 900 901 902static time_t 903pw_pwdpolicy(struct userconf * cnf, struct cargs * args) 904{ 905 time_t result = 0; 906 time_t now = time(NULL); 907 struct carg *arg = getarg(args, 'p'); 908 909 if (arg != NULL) { 910 if ((result = parse_date(now, arg->val)) == now) 911 errx(EX_DATAERR, "invalid date/time `%s'", arg->val); 912 } else if (cnf->password_days > 0) 913 result = now + ((long) cnf->password_days * 86400L); 914 return result; 915} 916 917 918static time_t 919pw_exppolicy(struct userconf * cnf, struct cargs * args) 920{ 921 time_t result = 0; 922 time_t now = time(NULL); 923 struct carg *arg = getarg(args, 'e'); 924 925 if (arg != NULL) { 926 if ((result = parse_date(now, arg->val)) == now) 927 errx(EX_DATAERR, "invalid date/time `%s'", arg->val); 928 } else if (cnf->expire_days > 0) 929 result = now + ((long) cnf->expire_days * 86400L); 930 return result; 931} 932 933 934static char * 935pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user) 936{ 937 struct carg *arg = getarg(args, 'd'); 938 static char home[128]; 939 940 if (arg) 941 return (arg->val); 942 943 if (cnf->home == NULL || *cnf->home == '\0') 944 errx(EX_CONFIG, "no base home directory set"); 945 snprintf(home, sizeof(home), "%s/%s", cnf->home, user); 946 947 return (home); 948} 949 950static char * 951shell_path(char const * path, char *shells[], char *sh) 952{ 953 if (sh != NULL && (*sh == '/' || *sh == '\0')) 954 return sh; /* specified full path or forced none */ 955 else { 956 char *p; 957 char paths[_UC_MAXLINE]; 958 959 /* 960 * We need to search paths 961 */ 962 strlcpy(paths, path, sizeof(paths)); 963 for (p = strtok(paths, ": \t\r\n"); p != NULL; p = strtok(NULL, ": \t\r\n")) { 964 int i; 965 static char shellpath[256]; 966 967 if (sh != NULL) { 968 snprintf(shellpath, sizeof(shellpath), "%s/%s", p, sh); 969 if (access(shellpath, X_OK) == 0) 970 return shellpath; 971 } else 972 for (i = 0; i < _UC_MAXSHELLS && shells[i] != NULL; i++) { 973 snprintf(shellpath, sizeof(shellpath), "%s/%s", p, shells[i]); 974 if (access(shellpath, X_OK) == 0) 975 return shellpath; 976 } 977 } 978 if (sh == NULL) 979 errx(EX_OSFILE, "can't find shell `%s' in shell paths", sh); 980 errx(EX_CONFIG, "no default shell available or defined"); 981 return NULL; 982 } 983} 984 985 986static char * 987pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell) 988{ 989 char *sh = newshell; 990 struct carg *arg = getarg(args, 's'); 991 992 if (newshell == NULL && arg != NULL) 993 sh = arg->val; 994 return shell_path(cnf->shelldir, cnf->shells, sh ? sh : cnf->shell_default); 995} 996 997#define SALTSIZE 32 998 999static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./"; 1000 1001char * 1002pw_pwcrypt(char *password) 1003{ 1004 int i; 1005 char salt[SALTSIZE + 1]; 1006 char *cryptpw; 1007 1008 static char buf[256]; 1009 1010 /* 1011 * Calculate a salt value 1012 */ 1013 for (i = 0; i < SALTSIZE; i++) 1014 salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)]; 1015 salt[SALTSIZE] = '\0'; 1016 1017 cryptpw = crypt(password, salt); 1018 if (cryptpw == NULL) 1019 errx(EX_CONFIG, "crypt(3) failure"); 1020 return strcpy(buf, cryptpw); 1021} 1022 1023 1024static char * 1025pw_password(struct userconf * cnf, char const * user) 1026{ 1027 int i, l; 1028 char pwbuf[32]; 1029 1030 switch (cnf->default_password) { 1031 case -1: /* Random password */ 1032 l = (arc4random() % 8 + 8); /* 8 - 16 chars */ 1033 for (i = 0; i < l; i++) 1034 pwbuf[i] = chars[arc4random_uniform(sizeof(chars)-1)]; 1035 pwbuf[i] = '\0'; 1036 1037 /* 1038 * We give this information back to the user 1039 */ 1040 if (conf.fd == -1 && !conf.dryrun) { 1041 if (isatty(STDOUT_FILENO)) 1042 printf("Password for '%s' is: ", user); 1043 printf("%s\n", pwbuf); 1044 fflush(stdout); 1045 } 1046 break; 1047 1048 case -2: /* No password at all! */ 1049 return ""; 1050 1051 case 0: /* No login - default */ 1052 default: 1053 return "*"; 1054 1055 case 1: /* user's name */ 1056 strlcpy(pwbuf, user, sizeof(pwbuf)); 1057 break; 1058 } 1059 return pw_pwcrypt(pwbuf); 1060} 1061 1062static int 1063pw_userdel(char *name, long id) 1064{ 1065 struct passwd *pwd = NULL; 1066 char file[MAXPATHLEN]; 1067 char home[MAXPATHLEN]; 1068 uid_t uid; 1069 struct group *gr, *grp; 1070 char grname[LOGNAMESIZE]; 1071 int rc; 1072 struct stat st; 1073 1074 if (id < 0 && name == NULL) 1075 errx(EX_DATAERR, "username or id required"); 1076 1077 pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id); 1078 if (pwd == NULL) { 1079 if (name == NULL) 1080 errx(EX_NOUSER, "no such uid `%ld'", id); 1081 errx(EX_NOUSER, "no such user `%s'", name); 1082 } 1083 uid = pwd->pw_uid; 1084 if (name == NULL) 1085 name = pwd->pw_name; 1086 1087 if (strcmp(pwd->pw_name, "root") == 0) 1088 errx(EX_DATAERR, "cannot remove user 'root'"); 1089 1090 if (!PWALTDIR()) { 1091 /* 1092 * Remove opie record from /etc/opiekeys 1093 */ 1094 1095 rmopie(pwd->pw_name); 1096 1097 /* 1098 * Remove crontabs 1099 */ 1100 snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name); 1101 if (access(file, F_OK) == 0) { 1102 snprintf(file, sizeof(file), "crontab -u %s -r", pwd->pw_name); 1103 system(file); 1104 } 1105 } 1106 /* 1107 * Save these for later, since contents of pwd may be 1108 * invalidated by deletion 1109 */ 1110 snprintf(file, sizeof(file), "%s/%s", _PATH_MAILDIR, pwd->pw_name); 1111 strlcpy(home, pwd->pw_dir, sizeof(home)); 1112 gr = GETGRGID(pwd->pw_gid); 1113 if (gr != NULL) 1114 strlcpy(grname, gr->gr_name, LOGNAMESIZE); 1115 else 1116 grname[0] = '\0'; 1117 1118 rc = delpwent(pwd); 1119 if (rc == -1) 1120 err(EX_IOERR, "user '%s' does not exist", pwd->pw_name); 1121 else if (rc != 0) 1122 err(EX_IOERR, "passwd update"); 1123 1124 if (conf.userconf->nispasswd && *conf.userconf->nispasswd=='/') { 1125 rc = delnispwent(conf.userconf->nispasswd, name); 1126 if (rc == -1) 1127 warnx("WARNING: user '%s' does not exist in NIS passwd", 1128 pwd->pw_name); 1129 else if (rc != 0) 1130 warn("WARNING: NIS passwd update"); 1131 /* non-fatal */ 1132 } 1133 1134 grp = GETGRNAM(name); 1135 if (grp != NULL && 1136 (grp->gr_mem == NULL || *grp->gr_mem == NULL) && 1137 strcmp(name, grname) == 0) 1138 delgrent(GETGRNAM(name)); 1139 SETGRENT(); 1140 while ((grp = GETGRENT()) != NULL) { 1141 int i, j; 1142 char group[MAXLOGNAME]; 1143 if (grp->gr_mem == NULL) 1144 continue; 1145 1146 for (i = 0; grp->gr_mem[i] != NULL; i++) { 1147 if (strcmp(grp->gr_mem[i], name) != 0) 1148 continue; 1149 1150 for (j = i; grp->gr_mem[j] != NULL; j++) 1151 grp->gr_mem[j] = grp->gr_mem[j+1]; 1152 strlcpy(group, grp->gr_name, MAXLOGNAME); 1153 chggrent(group, grp); 1154 } 1155 } 1156 ENDGRENT(); 1157 1158 pw_log(conf.userconf, M_DELETE, W_USER, "%s(%u) account removed", name, 1159 uid); 1160 1161 if (!PWALTDIR()) { 1162 /* 1163 * Remove mail file 1164 */ 1165 remove(file); 1166 1167 /* 1168 * Remove at jobs 1169 */ 1170 if (getpwuid(uid) == NULL) 1171 rmat(uid); 1172 1173 /* 1174 * Remove home directory and contents 1175 */ 1176 if (conf.deletehome && *home == '/' && getpwuid(uid) == NULL && 1177 stat(home, &st) != -1) { 1178 rm_r(home, uid); 1179 pw_log(conf.userconf, M_DELETE, W_USER, "%s(%u) home '%s' %sremoved", 1180 name, uid, home, 1181 stat(home, &st) == -1 ? "" : "not completely "); 1182 } 1183 } 1184 1185 return (EXIT_SUCCESS); 1186} 1187 1188static int 1189print_user(struct passwd * pwd) 1190{ 1191 if (!conf.pretty) { 1192 char *buf; 1193 1194 buf = conf.v7 ? pw_make_v7(pwd) : pw_make(pwd); 1195 printf("%s\n", buf); 1196 free(buf); 1197 } else { 1198 int j; 1199 char *p; 1200 struct group *grp = GETGRGID(pwd->pw_gid); 1201 char uname[60] = "User &", office[60] = "[None]", 1202 wphone[60] = "[None]", hphone[60] = "[None]"; 1203 char acexpire[32] = "[None]", pwexpire[32] = "[None]"; 1204 struct tm * tptr; 1205 1206 if ((p = strtok(pwd->pw_gecos, ",")) != NULL) { 1207 strlcpy(uname, p, sizeof(uname)); 1208 if ((p = strtok(NULL, ",")) != NULL) { 1209 strlcpy(office, p, sizeof(office)); 1210 if ((p = strtok(NULL, ",")) != NULL) { 1211 strlcpy(wphone, p, sizeof(wphone)); 1212 if ((p = strtok(NULL, "")) != NULL) { 1213 strlcpy(hphone, p, 1214 sizeof(hphone)); 1215 } 1216 } 1217 } 1218 } 1219 /* 1220 * Handle '&' in gecos field 1221 */ 1222 if ((p = strchr(uname, '&')) != NULL) { 1223 int l = strlen(pwd->pw_name); 1224 int m = strlen(p); 1225 1226 memmove(p + l, p + 1, m); 1227 memmove(p, pwd->pw_name, l); 1228 *p = (char) toupper((unsigned char)*p); 1229 } 1230 if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL) 1231 strftime(acexpire, sizeof acexpire, "%c", tptr); 1232 if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL) 1233 strftime(pwexpire, sizeof pwexpire, "%c", tptr); 1234 printf("Login Name: %-15s #%-12u Group: %-15s #%u\n" 1235 " Full Name: %s\n" 1236 " Home: %-26.26s Class: %s\n" 1237 " Shell: %-26.26s Office: %s\n" 1238 "Work Phone: %-26.26s Home Phone: %s\n" 1239 "Acc Expire: %-26.26s Pwd Expire: %s\n", 1240 pwd->pw_name, pwd->pw_uid, 1241 grp ? grp->gr_name : "(invalid)", pwd->pw_gid, 1242 uname, pwd->pw_dir, pwd->pw_class, 1243 pwd->pw_shell, office, wphone, hphone, 1244 acexpire, pwexpire); 1245 SETGRENT(); 1246 j = 0; 1247 while ((grp=GETGRENT()) != NULL) 1248 { 1249 int i = 0; 1250 if (grp->gr_mem != NULL) { 1251 while (grp->gr_mem[i] != NULL) 1252 { 1253 if (strcmp(grp->gr_mem[i], pwd->pw_name)==0) 1254 { 1255 printf(j++ == 0 ? " Groups: %s" : ",%s", grp->gr_name); 1256 break; 1257 } 1258 ++i; 1259 } 1260 } 1261 } 1262 ENDGRENT(); 1263 printf("%s", j ? "\n" : ""); 1264 } 1265 return EXIT_SUCCESS; 1266} 1267 1268char * 1269pw_checkname(char *name, int gecos) 1270{ 1271 char showch[8]; 1272 const char *badchars, *ch, *showtype; 1273 int reject; 1274 1275 ch = name; 1276 reject = 0; 1277 if (gecos) { 1278 /* See if the name is valid as a gecos (comment) field. */ 1279 badchars = ":!@"; 1280 showtype = "gecos field"; 1281 } else { 1282 /* See if the name is valid as a userid or group. */ 1283 badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\""; 1284 showtype = "userid/group name"; 1285 /* Userids and groups can not have a leading '-'. */ 1286 if (*ch == '-') 1287 reject = 1; 1288 } 1289 if (!reject) { 1290 while (*ch) { 1291 if (strchr(badchars, *ch) != NULL || *ch < ' ' || 1292 *ch == 127) { 1293 reject = 1; 1294 break; 1295 } 1296 /* 8-bit characters are only allowed in GECOS fields */ 1297 if (!gecos && (*ch & 0x80)) { 1298 reject = 1; 1299 break; 1300 } 1301 ch++; 1302 } 1303 } 1304 /* 1305 * A `$' is allowed as the final character for userids and groups, 1306 * mainly for the benefit of samba. 1307 */ 1308 if (reject && !gecos) { 1309 if (*ch == '$' && *(ch + 1) == '\0') { 1310 reject = 0; 1311 ch++; 1312 } 1313 } 1314 if (reject) { 1315 snprintf(showch, sizeof(showch), (*ch >= ' ' && *ch < 127) 1316 ? "`%c'" : "0x%02x", *ch); 1317 errx(EX_DATAERR, "invalid character %s at position %td in %s", 1318 showch, (ch - name), showtype); 1319 } 1320 if (!gecos && (ch - name) > LOGNAMESIZE) 1321 errx(EX_DATAERR, "name too long `%s' (max is %d)", name, 1322 LOGNAMESIZE); 1323 1324 return (name); 1325} 1326 1327 1328static void 1329rmat(uid_t uid) 1330{ 1331 DIR *d = opendir("/var/at/jobs"); 1332 1333 if (d != NULL) { 1334 struct dirent *e; 1335 1336 while ((e = readdir(d)) != NULL) { 1337 struct stat st; 1338 1339 if (strncmp(e->d_name, ".lock", 5) != 0 && 1340 stat(e->d_name, &st) == 0 && 1341 !S_ISDIR(st.st_mode) && 1342 st.st_uid == uid) { 1343 char tmp[MAXPATHLEN]; 1344 1345 snprintf(tmp, sizeof(tmp), "/usr/bin/atrm %s", e->d_name); 1346 system(tmp); 1347 } 1348 } 1349 closedir(d); 1350 } 1351} 1352 1353static void 1354rmopie(char const * name) 1355{ 1356 static const char etcopie[] = "/etc/opiekeys"; 1357 FILE *fp = fopen(etcopie, "r+"); 1358 1359 if (fp != NULL) { 1360 char tmp[1024]; 1361 off_t atofs = 0; 1362 int length = strlen(name); 1363 1364 while (fgets(tmp, sizeof tmp, fp) != NULL) { 1365 if (strncmp(name, tmp, length) == 0 && tmp[length]==' ') { 1366 if (fseek(fp, atofs, SEEK_SET) == 0) { 1367 fwrite("#", 1, 1, fp); /* Comment username out */ 1368 } 1369 break; 1370 } 1371 atofs = ftell(fp); 1372 } 1373 /* 1374 * If we got an error of any sort, don't update! 1375 */ 1376 fclose(fp); 1377 } 1378} 1379 1380