pw_user.c revision 285433
11844Swollman/*- 229141Speter * Copyright (C) 1996 31844Swollman * David L. Nugent. All rights reserved. 41638Srgrimes * 51638Srgrimes * Redistribution and use in source and binary forms, with or without 61638Srgrimes * modification, are permitted provided that the following conditions 71638Srgrimes * are met: 81638Srgrimes * 1. Redistributions of source code must retain the above copyright 929129Speter * notice, this list of conditions and the following disclaimer. 1029129Speter * 2. Redistributions in binary form must reproduce the above copyright 1129129Speter * notice, this list of conditions and the following disclaimer in the 121844Swollman * documentation and/or other materials provided with the distribution. 131844Swollman * 1428945Speter * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 151844Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161844Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1728945Speter * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 181844Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1929141Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2029141Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2129141Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2229141Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231844Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241844Swollman * SUCH DAMAGE. 2526051Sasami * 261844Swollman */ 272353Sbde 282827Sjkh#ifndef lint 292827Sjkhstatic const char rcsid[] = 302827Sjkh "$FreeBSD: head/usr.sbin/pw/pw_user.c 285433 2015-07-12 21:43:57Z bapt $"; 312827Sjkh#endif /* not lint */ 322827Sjkh 331638Srgrimes#include <ctype.h> 342827Sjkh#include <err.h> 351638Srgrimes#include <fcntl.h> 3618529Sbde#include <sys/param.h> 3718529Sbde#include <dirent.h> 381638Srgrimes#include <paths.h> 391638Srgrimes#include <termios.h> 401638Srgrimes#include <sys/types.h> 411844Swollman#include <sys/time.h> 421638Srgrimes#include <sys/resource.h> 431844Swollman#include <login_cap.h> 441638Srgrimes#include <pwd.h> 451638Srgrimes#include <grp.h> 461844Swollman#include <libutil.h> 4715959Sphk#include "pw.h" 481638Srgrimes#include "bitmap.h" 491638Srgrimes 5024761Sjdp#define LOGNAMESIZE (MAXLOGNAME-1) 5115959Sphk 521638Srgrimesstatic char locked_str[] = "*LOCKED*"; 531844Swollman 541844Swollmanstatic int pw_userdel(char *name, long id); 5515959Sphkstatic int print_user(struct passwd * pwd); 561844Swollmanstatic uid_t pw_uidpolicy(struct userconf * cnf, long id); 5716097Sjfieberstatic uid_t pw_gidpolicy(struct cargs * args, char *nam, gid_t prefer); 581844Swollmanstatic time_t pw_pwdpolicy(struct userconf * cnf, struct cargs * args); 5915959Sphkstatic time_t pw_exppolicy(struct userconf * cnf, struct cargs * args); 601844Swollmanstatic char *pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user); 6116097Sjfieberstatic char *pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell); 6224761Sjdpstatic char *pw_password(struct userconf * cnf, char const * user); 6315959Sphkstatic char *shell_path(char const * path, char *shells[], char *sh); 641844Swollmanstatic void rmat(uid_t uid); 6516097Sjfieberstatic void rmopie(char const * name); 661844Swollman 6715959Sphkstatic void 681844Swollmancreate_and_populate_homedir(struct passwd *pwd) 691844Swollman{ 701844Swollman struct userconf *cnf = conf.userconf; 7115959Sphk const char *skeldir; 721844Swollman int skelfd = -1; 731844Swollman 7424761Sjdp skeldir = cnf->dotdir; 7515959Sphk 761844Swollman if (skeldir != NULL && *skeldir != '\0') { 771844Swollman skelfd = openat(conf.rootfd, cnf->dotdir, 781844Swollman O_DIRECTORY|O_CLOEXEC); 7915959Sphk } 801844Swollman 811638Srgrimes copymkdir(conf.rootfd, pwd->pw_dir, skelfd, cnf->homemode, pwd->pw_uid, 8226715Sasami pwd->pw_gid, 0); 8317510Speter pw_log(cnf, M_ADD, W_USER, "%s(%u) home %s made", pwd->pw_name, 8415959Sphk pwd->pw_uid, pwd->pw_dir); 851638Srgrimes} 861638Srgrimes 8726715Sasamistatic int 8817510Speterset_passwd(struct passwd *pwd, bool update) 8915959Sphk{ 901638Srgrimes int b, istty; 911844Swollman struct termios t, n; 9226715Sasami login_cap_t *lc; 9317510Speter char line[_PASSWORD_LEN+1]; 9415959Sphk char *p; 951638Srgrimes 961844Swollman if (conf.fd == '-') { 9726715Sasami if (!pwd->pw_passwd || *pwd->pw_passwd != '*') { 9825104Sbde pwd->pw_passwd = "*"; /* No access */ 991844Swollman return (1); 1001844Swollman } 10126715Sasami return (0); 10225104Sbde } 1031844Swollman 1041844Swollman if ((istty = isatty(conf.fd))) { 10526715Sasami if (tcgetattr(conf.fd, &t) == -1) 10615959Sphk istty = 0; 1071844Swollman else { 10825104Sbde n = t; 10925104Sbde n.c_lflag &= ~(ECHO); 11025104Sbde tcsetattr(conf.fd, TCSANOW, &n); 11125104Sbde printf("%s%spassword for user %s:", 1121844Swollman update ? "new " : "", 11324761Sjdp conf.precrypted ? "encrypted " : "", 11415959Sphk pwd->pw_name); 1151844Swollman fflush(stdout); 1162870Swollman } 1172868Swollman } 1181638Srgrimes b = read(conf.fd, line, sizeof(line) - 1); 1191638Srgrimes if (istty) { /* Restore state */ 1201638Srgrimes tcsetattr(conf.fd, TCSANOW, &t); 1211638Srgrimes fputc('\n', stdout); 1222870Swollman fflush(stdout); 1231638Srgrimes } 1241844Swollman 12528945Speter if (b < 0) 1261844Swollman err(EX_IOERR, "-%c file descriptor", 1271844Swollman conf.precrypted ? 'H' : 'h'); 1281638Srgrimes line[b] = '\0'; 12928945Speter if ((p = strpbrk(line, "\r\n")) != NULL) 13028945Speter *p = '\0'; 13128945Speter if (!*line) 13228945Speter errx(EX_DATAERR, "empty password read on file descriptor %d", 13328945Speter conf.fd); 1341844Swollman if (conf.precrypted) { 1351844Swollman if (strchr(line, ':') != NULL) 1361844Swollman errx(EX_DATAERR, "bad encrypted password"); 1371844Swollman pwd->pw_passwd = line; 1381638Srgrimes } else { 1391844Swollman lc = login_getpwclass(pwd); 1401844Swollman if (lc == NULL || 1411844Swollman login_setcryptfmt(lc, "sha512", NULL) == NULL) 1421638Srgrimes warn("setting crypt(3) format"); 14318340Sswallace login_close(lc); 1441844Swollman pwd->pw_passwd = pw_pwcrypt(line); 1451844Swollman } 1461844Swollman return (1); 1471638Srgrimes} 1482353Sbde 1491638Srgrimesint 15017400Sjkhpw_usernext(struct userconf *cnf, bool quiet) 1511844Swollman{ 1521638Srgrimes uid_t next = pw_uidpolicy(cnf, -1); 1533859Sbde 1541638Srgrimes if (quiet) 1551638Srgrimes return (next); 1562353Sbde 1571638Srgrimes printf("%u:", next); 15817400Sjkh pw_groupnext(cnf, quiet); 1591844Swollman 1603859Sbde return (EXIT_SUCCESS); 1611638Srgrimes} 1621844Swollman 16325468Sjdpstatic int 1641844Swollmanpw_usershow(char *name, long id, struct passwd *fakeuser) 1651844Swollman{ 1663859Sbde struct passwd *pwd = NULL; 1671844Swollman 16828945Speter if (id < 0 && name == NULL && !conf.all) 16928945Speter errx(EX_DATAERR, "username or id or '-a' required"); 1701844Swollman 1712353Sbde if (conf.all) { 1721844Swollman SETPWENT(); 17326073Sdfr while ((pwd = GETPWENT()) != NULL) 1741844Swollman print_user(pwd); 17517400Sjkh ENDPWENT(); 17628945Speter return (EXIT_SUCCESS); 17728945Speter } 17828945Speter 17928945Speter pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id); 18028945Speter if (pwd == NULL) { 18129141Speter if (conf.force) { 18228945Speter pwd = fakeuser; 18328945Speter } else { 1841844Swollman if (name == NULL) 1851844Swollman errx(EX_NOUSER, "no such uid `%ld'", id); 1862353Sbde errx(EX_NOUSER, "no such user `%s'", name); 1871844Swollman } 1885253Sache } 1891844Swollman 1903859Sbde return (print_user(pwd)); 1911844Swollman} 1921638Srgrimes 1931638Srgrimesstatic void 1941638Srgrimesperform_chgpwent(const char *name, struct passwd *pwd) 1951638Srgrimes{ 19616663Sjkh int rc; 1971844Swollman 1981844Swollman rc = chgpwent(name, pwd); 1991844Swollman if (rc == -1) 2001844Swollman errx(EX_IOERR, "user '%s' does not exist (NIS?)", pwd->pw_name); 2011844Swollman else if (rc != 0) 20216826Sphk err(EX_IOERR, "passwd file update"); 20316437Sphk 2041638Srgrimes if (conf.userconf->nispasswd && *conf.userconf->nispasswd == '/') { 20516437Sphk rc = chgnispwent(conf.userconf->nispasswd, name, pwd); 2061638Srgrimes if (rc == -1) 2071844Swollman warn("User '%s' not found in NIS passwd", pwd->pw_name); 20824750Sbde else 20924750Sbde warn("NIS passwd update"); 21024750Sbde /* NOTE: NIS-only update errors are not fatal */ 21124750Sbde } 21224750Sbde} 2131638Srgrimes 21427910Sasami/* 21524750Sbde * The M_LOCK and M_UNLOCK functions simply add or remove 21628945Speter * a "*LOCKED*" prefix from in front of the password to 21724750Sbde * prevent it decoding correctly, and therefore prevents 21825468Sjdp * access. Of course, this only prevents access via 21925468Sjdp * password authentication (not ssh, kerberos or any 22028945Speter * other method that does not use the UNIX password) but 22128945Speter * that is a known limitation. 22228945Speter */ 22328945Speterstatic int 22427910Sasamipw_userlock(char *name, long id, int mode) 22528945Speter{ 22628945Speter struct passwd *pwd = NULL; 2271638Srgrimes char *passtmp = NULL; 2281638Srgrimes bool locked = false; 2291638Srgrimes 2301638Srgrimes if (id < 0 && name == NULL) 2311638Srgrimes errx(EX_DATAERR, "username or id required"); 2321638Srgrimes 2332298Swollman pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id); 2342298Swollman if (pwd == NULL) { 2352298Swollman if (name == NULL) 2362298Swollman errx(EX_NOUSER, "no such uid `%ld'", id); 2371638Srgrimes errx(EX_NOUSER, "no such user `%s'", name); 2382298Swollman } 2391996Swollman 2401996Swollman if (name == NULL) 2411638Srgrimes name = pwd->pw_name; 2421844Swollman 2431996Swollman if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str) -1) == 0) 2441638Srgrimes locked = true; 2452298Swollman if (mode == M_LOCK && locked) 2461844Swollman errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name); 24728945Speter if (mode == M_UNLOCK && !locked) 2481844Swollman errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name); 2491844Swollman 2502298Swollman if (mode == M_LOCK) { 2512298Swollman asprintf(&passtmp, "%s%s", locked_str, pwd->pw_passwd); 2524450Sbde if (passtmp == NULL) /* disaster */ 2531844Swollman errx(EX_UNAVAILABLE, "out of memory"); 25428945Speter pwd->pw_passwd = passtmp; 25528945Speter } else { 25628945Speter pwd->pw_passwd += sizeof(locked_str)-1; 25728945Speter } 25828945Speter 25928945Speter perform_chgpwent(name, pwd); 26028945Speter free(passtmp); 26128945Speter 26228945Speter return (EXIT_SUCCESS); 26328945Speter} 2641844Swollman 2651844Swollman/*- 2661996Swollman * -C config configuration file 2671844Swollman * -q quiet operation 2681844Swollman * -n name login name 2691638Srgrimes * -u uid user id 2701638Srgrimes * -c comment user name/comment 2711638Srgrimes * -d directory home directory 2721638Srgrimes * -e date account expiry date 2731638Srgrimes * -p date password expiry date 2741638Srgrimes * -g grp primary group 2751638Srgrimes * -G grp1,grp2 additional groups 2762353Sbde * -m [ -k dir ] create and set up home 2771638Srgrimes * -s shell name of login shell 2786032Sjkh * -o duplicate uid ok 2791638Srgrimes * -L class user class 2801638Srgrimes * -l name new login name 2811638Srgrimes * -h fd password filehandle 28216663Sjkh * -H fd encrypted password filehandle 2831844Swollman * -F force print or add 2841844Swollman * Setting defaults: 2851844Swollman * -D set user defaults 2861638Srgrimes * -b dir default home root dir 2871638Srgrimes * -e period default expiry period 2881638Srgrimes * -p period default password change period 2891638Srgrimes * -g group default group 2905585Sjkh * -G grp1,grp2.. default additional groups 2914442Sphk * -L class default login class 29216663Sjkh * -k dir default home skeleton 29326760Sjkh * -s shell default shell 29426760Sjkh * -w method default password method 29526760Sjkh */ 2964442Sphk 2974442Sphkint 2981638Srgrimespw_user(int mode, char *name, long id, struct cargs * args) 2991638Srgrimes{ 3001638Srgrimes int rc, edited = 0; 3011638Srgrimes char *p = NULL; 30224861Sjkh struct carg *arg; 30324861Sjkh struct passwd *pwd = NULL; 30424861Sjkh struct group *grp; 30524861Sjkh struct stat st; 3061638Srgrimes struct userconf *cnf; 30716663Sjkh char line[_PASSWORD_LEN+1]; 30824861Sjkh char path[MAXPATHLEN]; 30924861Sjkh FILE *fp; 31024861Sjkh char *dmode_c; 3111638Srgrimes void *set = NULL; 31224861Sjkh 3131638Srgrimes static struct passwd fakeuser = 3141844Swollman { 3151638Srgrimes "nouser", 3161844Swollman "*", 3171844Swollman -1, 31811136Swollman -1, 3191844Swollman 0, 3201844Swollman "", 3211844Swollman "User &", 32216663Sjkh "/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 snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, 750 pwd->pw_name); 751 close(openat(conf.rootfd, path +1, O_RDWR | O_CREAT, 752 0600)); /* Preserve contents & mtime */ 753 fchownat(conf.rootfd, path + 1, pwd->pw_uid, 754 pwd->pw_gid, AT_SYMLINK_NOFOLLOW); 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 /* Remove opie record from /etc/opiekeys */ 1091 1092 if (PWALTDIR() != PWF_ALT) 1093 rmopie(pwd->pw_name); 1094 1095 if (!PWALTDIR()) { 1096 /* Remove crontabs */ 1097 snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name); 1098 if (access(file, F_OK) == 0) { 1099 snprintf(file, sizeof(file), "crontab -u %s -r", pwd->pw_name); 1100 system(file); 1101 } 1102 } 1103 /* 1104 * Save these for later, since contents of pwd may be 1105 * invalidated by deletion 1106 */ 1107 snprintf(file, sizeof(file), "%s/%s", _PATH_MAILDIR, pwd->pw_name); 1108 strlcpy(home, pwd->pw_dir, sizeof(home)); 1109 gr = GETGRGID(pwd->pw_gid); 1110 if (gr != NULL) 1111 strlcpy(grname, gr->gr_name, LOGNAMESIZE); 1112 else 1113 grname[0] = '\0'; 1114 1115 rc = delpwent(pwd); 1116 if (rc == -1) 1117 err(EX_IOERR, "user '%s' does not exist", pwd->pw_name); 1118 else if (rc != 0) 1119 err(EX_IOERR, "passwd update"); 1120 1121 if (conf.userconf->nispasswd && *conf.userconf->nispasswd=='/') { 1122 rc = delnispwent(conf.userconf->nispasswd, name); 1123 if (rc == -1) 1124 warnx("WARNING: user '%s' does not exist in NIS passwd", 1125 pwd->pw_name); 1126 else if (rc != 0) 1127 warn("WARNING: NIS passwd update"); 1128 /* non-fatal */ 1129 } 1130 1131 grp = GETGRNAM(name); 1132 if (grp != NULL && 1133 (grp->gr_mem == NULL || *grp->gr_mem == NULL) && 1134 strcmp(name, grname) == 0) 1135 delgrent(GETGRNAM(name)); 1136 SETGRENT(); 1137 while ((grp = GETGRENT()) != NULL) { 1138 int i, j; 1139 char group[MAXLOGNAME]; 1140 if (grp->gr_mem == NULL) 1141 continue; 1142 1143 for (i = 0; grp->gr_mem[i] != NULL; i++) { 1144 if (strcmp(grp->gr_mem[i], name) != 0) 1145 continue; 1146 1147 for (j = i; grp->gr_mem[j] != NULL; j++) 1148 grp->gr_mem[j] = grp->gr_mem[j+1]; 1149 strlcpy(group, grp->gr_name, MAXLOGNAME); 1150 chggrent(group, grp); 1151 } 1152 } 1153 ENDGRENT(); 1154 1155 pw_log(conf.userconf, M_DELETE, W_USER, "%s(%u) account removed", name, 1156 uid); 1157 1158 /* Remove mail file */ 1159 if (PWALTDIR() != PWF_ALT) 1160 unlinkat(conf.rootfd, file + 1, 0); 1161 1162 /* Remove at jobs */ 1163 if (!PWALTDIR() && getpwuid(uid) == NULL) 1164 rmat(uid); 1165 1166 /* Remove home directory and contents */ 1167 if (PWALTDIR() != PWF_ALT && conf.deletehome && *home == '/' && 1168 getpwuid(uid) == NULL && 1169 fstatat(conf.rootfd, home + 1, &st, 0) != -1) { 1170 rm_r(conf.rootfd, home, uid); 1171 pw_log(conf.userconf, M_DELETE, W_USER, "%s(%u) home '%s' %s" 1172 "removed", name, uid, home, 1173 fstatat(conf.rootfd, home + 1, &st, 0) == -1 ? "" : "not " 1174 "completely "); 1175 } 1176 1177 return (EXIT_SUCCESS); 1178} 1179 1180static int 1181print_user(struct passwd * pwd) 1182{ 1183 if (!conf.pretty) { 1184 char *buf; 1185 1186 buf = conf.v7 ? pw_make_v7(pwd) : pw_make(pwd); 1187 printf("%s\n", buf); 1188 free(buf); 1189 } else { 1190 int j; 1191 char *p; 1192 struct group *grp = GETGRGID(pwd->pw_gid); 1193 char uname[60] = "User &", office[60] = "[None]", 1194 wphone[60] = "[None]", hphone[60] = "[None]"; 1195 char acexpire[32] = "[None]", pwexpire[32] = "[None]"; 1196 struct tm * tptr; 1197 1198 if ((p = strtok(pwd->pw_gecos, ",")) != NULL) { 1199 strlcpy(uname, p, sizeof(uname)); 1200 if ((p = strtok(NULL, ",")) != NULL) { 1201 strlcpy(office, p, sizeof(office)); 1202 if ((p = strtok(NULL, ",")) != NULL) { 1203 strlcpy(wphone, p, sizeof(wphone)); 1204 if ((p = strtok(NULL, "")) != NULL) { 1205 strlcpy(hphone, p, 1206 sizeof(hphone)); 1207 } 1208 } 1209 } 1210 } 1211 /* 1212 * Handle '&' in gecos field 1213 */ 1214 if ((p = strchr(uname, '&')) != NULL) { 1215 int l = strlen(pwd->pw_name); 1216 int m = strlen(p); 1217 1218 memmove(p + l, p + 1, m); 1219 memmove(p, pwd->pw_name, l); 1220 *p = (char) toupper((unsigned char)*p); 1221 } 1222 if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL) 1223 strftime(acexpire, sizeof acexpire, "%c", tptr); 1224 if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL) 1225 strftime(pwexpire, sizeof pwexpire, "%c", tptr); 1226 printf("Login Name: %-15s #%-12u Group: %-15s #%u\n" 1227 " Full Name: %s\n" 1228 " Home: %-26.26s Class: %s\n" 1229 " Shell: %-26.26s Office: %s\n" 1230 "Work Phone: %-26.26s Home Phone: %s\n" 1231 "Acc Expire: %-26.26s Pwd Expire: %s\n", 1232 pwd->pw_name, pwd->pw_uid, 1233 grp ? grp->gr_name : "(invalid)", pwd->pw_gid, 1234 uname, pwd->pw_dir, pwd->pw_class, 1235 pwd->pw_shell, office, wphone, hphone, 1236 acexpire, pwexpire); 1237 SETGRENT(); 1238 j = 0; 1239 while ((grp=GETGRENT()) != NULL) 1240 { 1241 int i = 0; 1242 if (grp->gr_mem != NULL) { 1243 while (grp->gr_mem[i] != NULL) 1244 { 1245 if (strcmp(grp->gr_mem[i], pwd->pw_name)==0) 1246 { 1247 printf(j++ == 0 ? " Groups: %s" : ",%s", grp->gr_name); 1248 break; 1249 } 1250 ++i; 1251 } 1252 } 1253 } 1254 ENDGRENT(); 1255 printf("%s", j ? "\n" : ""); 1256 } 1257 return EXIT_SUCCESS; 1258} 1259 1260char * 1261pw_checkname(char *name, int gecos) 1262{ 1263 char showch[8]; 1264 const char *badchars, *ch, *showtype; 1265 int reject; 1266 1267 ch = name; 1268 reject = 0; 1269 if (gecos) { 1270 /* See if the name is valid as a gecos (comment) field. */ 1271 badchars = ":!@"; 1272 showtype = "gecos field"; 1273 } else { 1274 /* See if the name is valid as a userid or group. */ 1275 badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\""; 1276 showtype = "userid/group name"; 1277 /* Userids and groups can not have a leading '-'. */ 1278 if (*ch == '-') 1279 reject = 1; 1280 } 1281 if (!reject) { 1282 while (*ch) { 1283 if (strchr(badchars, *ch) != NULL || *ch < ' ' || 1284 *ch == 127) { 1285 reject = 1; 1286 break; 1287 } 1288 /* 8-bit characters are only allowed in GECOS fields */ 1289 if (!gecos && (*ch & 0x80)) { 1290 reject = 1; 1291 break; 1292 } 1293 ch++; 1294 } 1295 } 1296 /* 1297 * A `$' is allowed as the final character for userids and groups, 1298 * mainly for the benefit of samba. 1299 */ 1300 if (reject && !gecos) { 1301 if (*ch == '$' && *(ch + 1) == '\0') { 1302 reject = 0; 1303 ch++; 1304 } 1305 } 1306 if (reject) { 1307 snprintf(showch, sizeof(showch), (*ch >= ' ' && *ch < 127) 1308 ? "`%c'" : "0x%02x", *ch); 1309 errx(EX_DATAERR, "invalid character %s at position %td in %s", 1310 showch, (ch - name), showtype); 1311 } 1312 if (!gecos && (ch - name) > LOGNAMESIZE) 1313 errx(EX_DATAERR, "name too long `%s' (max is %d)", name, 1314 LOGNAMESIZE); 1315 1316 return (name); 1317} 1318 1319 1320static void 1321rmat(uid_t uid) 1322{ 1323 DIR *d = opendir("/var/at/jobs"); 1324 1325 if (d != NULL) { 1326 struct dirent *e; 1327 1328 while ((e = readdir(d)) != NULL) { 1329 struct stat st; 1330 1331 if (strncmp(e->d_name, ".lock", 5) != 0 && 1332 stat(e->d_name, &st) == 0 && 1333 !S_ISDIR(st.st_mode) && 1334 st.st_uid == uid) { 1335 char tmp[MAXPATHLEN]; 1336 1337 snprintf(tmp, sizeof(tmp), "/usr/bin/atrm %s", e->d_name); 1338 system(tmp); 1339 } 1340 } 1341 closedir(d); 1342 } 1343} 1344 1345static void 1346rmopie(char const * name) 1347{ 1348 char tmp[1014]; 1349 FILE *fp; 1350 int fd; 1351 size_t len; 1352 off_t atofs = 0; 1353 1354 if ((fd = openat(conf.rootfd, "etc/opiekeys", O_RDWR)) == -1) 1355 return; 1356 1357 fp = fdopen(fd, "r+"); 1358 len = strlen(name); 1359 1360 while (fgets(tmp, sizeof(tmp), fp) != NULL) { 1361 if (strncmp(name, tmp, len) == 0 && tmp[len]==' ') { 1362 /* Comment username out */ 1363 if (fseek(fp, atofs, SEEK_SET) == 0) 1364 fwrite("#", 1, 1, fp); 1365 break; 1366 } 1367 atofs = ftell(fp); 1368 } 1369 /* 1370 * If we got an error of any sort, don't update! 1371 */ 1372 fclose(fp); 1373} 1374