static void edit_file(int is_shadow) { struct stat begin, end; pw_init(); pw_lock(); if (stat(tmp_file, &begin)) pw_error(tmp_file, 1, 1); pw_edit(0); if (stat(tmp_file, &end)) pw_error(tmp_file, 1, 1); if (begin.st_mtime == end.st_mtime) { (void)fprintf(stderr, _("%s: no changes made\n"), progname); pw_error((char *)NULL, 0, 0); } if (!is_shadow) chmod(tmp_file, 0644); #if 0 /* if shadow file, then mode is 0600 now */ else chmod(tmp_file, 0400); #endif pw_unlock(); }
static void pw_unlock(void) { char tmp[FILENAMELEN+4]; sprintf(tmp, "%s%s", orig_file, ".OLD"); unlink(tmp); link(orig_file, tmp); #ifdef HAVE_LIBSELINUX if (is_selinux_enabled()) { security_context_t passwd_context=NULL; int ret=0; if (getfilecon(orig_file,&passwd_context) < 0) { (void) fprintf(stderr,_("%s: Can't get context for %s"),progname,orig_file); pw_error(orig_file, 1, 1); } ret=setfilecon(tmp_file,passwd_context); freecon(passwd_context); if (ret!=0) { (void) fprintf(stderr,_("%s: Can't set context for %s"),progname,tmp_file); pw_error(tmp_file, 1, 1); } } #endif if (rename(tmp_file, orig_file) == -1) { int errsv = errno; fprintf(stderr, _("%s: can't unlock %s: %s (your changes are still in %s)\n"), progname, orig_file, strerror(errsv), tmp_file); exit(1); } unlink(tmp_file); }
main() { register int pfd, tfd; struct stat begin, end; pw_init(); pfd = pw_lock(); tfd = pw_tmp(); copyfile(pfd, tfd); (void)close(tfd); for (;;) { if (stat(tempname, &begin)) pw_error(tempname, 1, 1); pw_edit(0); if (stat(tempname, &end)) pw_error(tempname, 1, 1); if (begin.st_mtime == end.st_mtime) { (void)fprintf(stderr, "vipw: no changes made\n"); pw_error((char *)NULL, 0, 0); } if (pw_mkdb()) break; pw_prompt(); } exit(0); }
static char * getnewpasswd(struct passwd *pw, int min_pw_len) { int tries; char *p, *t; char buf[_PASSWORD_LEN+1], salt[_PASSWORD_LEN+1]; char option[LINE_MAX], *key, *opt; (void)printf("Changing local password for %s.\n", pw->pw_name); if (uid && pw->pw_passwd[0] && strcmp(crypt(getpass("Old password:"******"user %s (UID %lu) failed to change the " "local password of user %s: %m", pw->pw_name, (unsigned long)uid, pw->pw_name); pw_error(NULL, 1, 1); } for (buf[0] = '\0', tries = 0;;) { p = getpass("New password:"******"Password unchanged.\n"); pw_error(NULL, 0, 0); } if (min_pw_len > 0 && (int)strlen(p) < min_pw_len) { (void) printf("Password is too short.\n"); continue; } if (strlen(p) <= 5 && ++tries < 2) { (void)printf("Please enter a longer password.\n"); continue; } for (t = p; *t && islower((unsigned char)*t); ++t); if (!*t && ++tries < 2) { (void)printf("Please don't use an all-lower case " "password.\nUnusual capitalization, " "control characters or digits are " "suggested.\n"); continue; } (void)strlcpy(buf, p, sizeof(buf)); if (!strcmp(buf, getpass("Retype new password:"******"Mismatch; try again, EOF to quit.\n"); } pw_getpwconf(option, sizeof(option), pw, "localcipher"); opt = option; key = strsep(&opt, ","); if(pw_gensalt(salt, _PASSWORD_LEN, key, opt) == -1) { warn("Couldn't generate salt"); pw_error(NULL, 0, 0); } return(crypt(buf, salt)); }
static void edit_file(int is_shadow) { struct stat begin, end; int passwd_file, ch_ret; FILE *tmp_fd; pw_init(); /* acquire exclusive lock */ if (lckpwdf() < 0) err(EXIT_FAILURE, _("cannot get lock")); passwd_file = open(orig_file, O_RDONLY, 0); if (passwd_file < 0) err(EXIT_FAILURE, _("cannot open %s"), orig_file); tmp_fd = pw_tmpfile(passwd_file); if (fstat(fileno(tmp_fd), &begin)) pw_error(tmp_file, 1, 1); pw_edit(); if (fstat(fileno(tmp_fd), &end)) pw_error(tmp_file, 1, 1); /* Some editors, such as Vim with 'writebackup' mode enabled, * use "atomic save" in which the old file is deleted and a new * one with the same name created in its place. */ if (end.st_nlink == 0) { if (close_stream(tmp_fd) != 0) err(EXIT_FAILURE, _("write error")); tmp_fd = fopen(tmp_file, "r"); if (!tmp_file) err(EXIT_FAILURE, _("cannot open %s"), tmp_file); if (fstat(fileno(tmp_fd), &end)) pw_error(tmp_file, 1, 1); } if (begin.st_mtime == end.st_mtime) { warnx(_("no changes made")); pw_error((char *)NULL, 0, 0); } /* pw_tmpfile() will create the file with mode 600 */ if (!is_shadow) ch_ret = fchmod(fileno(tmp_fd), 0644); else ch_ret = fchmod(fileno(tmp_fd), 0400); if (ch_ret < 0) err(EXIT_FAILURE, "%s: %s", _("cannot chmod file"), orig_file); if (close_stream(tmp_fd) != 0) err(EXIT_FAILURE, _("write error")); pw_write(); close(passwd_file); ulckpwdf(); }
static void copyfile(int from, int to) { int nr, nw, off; char buf[8 * 1024]; while ((nr = read(from, buf, sizeof(buf))) > 0) for (off = 0; off < nr; nr -= nw, off += nw) if ((nw = write(to, buf + off, nr)) < 0) pw_error(tmp_file, 1, 1); if (nr < 0) pw_error(orig_file, 1, 1); }
int pw_mkdb(const char *username) { int pstat; pid_t pid; (void)fflush(stderr); if (!(pid = fork())) { if(!username) { warnx("rebuilding the database..."); execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", "-d", mppath, tempname, (char *)NULL); } else { warnx("updating the database..."); execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", "-d", mppath, "-u", username, tempname, (char *)NULL); } pw_error(_PATH_PWD_MKDB, 1, 1); } pid = waitpid(pid, &pstat, 0); if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) return (0); warnx("done"); return (1); }
struct passwd * ypgetpwnam(char *nam, int secure) { static struct passwd pwent; int reason, vallen; char *val; reason = yp_match(domain, secure ? "master.passwd.byname" : "passwd.byname", nam, strlen(nam), &val, &vallen); switch (reason) { case 0: break; default: return (NULL); } val[vallen] = '\0'; if (__yplin) free(__yplin); __yplin = (char *)malloc(vallen + 1); if (__yplin == NULL) pw_error(NULL, 1, 1); strlcpy(__yplin, val, vallen + 1); free(val); return (interpret(&pwent, __yplin, secure)); }
void pw_prompt(void) { int c, first; (void)printf("re-edit the password file? [y]: "); (void)fflush(stdout); first = c = getchar(); while (c != '\n' && c != EOF) c = getchar(); if (first == 'n') pw_error(NULL, 0, 0); }
static void pw_write(void) { char tmp[FILENAMELEN + 4]; sprintf(tmp, "%s%s", orig_file, ".OLD"); unlink(tmp); if (link(orig_file, tmp)) warn(_("%s: create a link to %s failed"), orig_file, tmp); #ifdef HAVE_LIBSELINUX if (is_selinux_enabled() > 0) { security_context_t passwd_context = NULL; int ret = 0; if (getfilecon(orig_file, &passwd_context) < 0) { warnx(_("Can't get context for %s"), orig_file); pw_error(orig_file, 1, 1); } ret = setfilecon(tmp_file, passwd_context); freecon(passwd_context); if (ret != 0) { warnx(_("Can't set context for %s"), tmp_file); pw_error(tmp_file, 1, 1); } } #endif if (rename(tmp_file, orig_file) == -1) { int errsv = errno; errx(EXIT_FAILURE, ("cannot write %s: %s (your changes are still in %s)"), orig_file, strerror(errsv), tmp_file); } unlink(tmp_file); free(tmp_file); }
void pw_edit(int notsetuid) { int pstat; char *p, *editor; if (!(editor = getenv("EDITOR"))) editor = _default_editor; if ((p = strrchr(editor, '/'))) ++p; else p = editor; if (!(editpid = fork())) { if (notsetuid) { (void)setgid(getgid()); (void)setuid(getuid()); } errno = 0; execlp(editor, p, tempname, (char *)NULL); _exit(errno); } for (;;) { editpid = waitpid(editpid, (int *)&pstat, WUNTRACED); errno = WEXITSTATUS(pstat); if (editpid == -1) pw_error(editor, 1, 1); else if (WIFSTOPPED(pstat)) raise(WSTOPSIG(pstat)); else if (WIFEXITED(pstat) && errno == 0) break; else pw_error(editor, 1, 1); } editpid = -1; }
int pw_mkdb() { int pstat; pid_t pid; warnx("rebuilding the database..."); (void)fflush(stderr); if (!(pid = vfork())) { execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", tempname, NULL); pw_error(_PATH_PWD_MKDB, 1, 1); } pid = waitpid(pid, &pstat, 0); if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) return (0); warnx("done"); return (1); }
static void pw_edit(int notsetuid) { int pstat; pid_t pid; char *p, *editor; if (!(editor = getenv("EDITOR"))) editor = strdup(_PATH_VI); /* [email protected] */ if ((p = strrchr(strtok(editor," \t"), '/')) != NULL) ++p; else p = editor; pid = fork(); if (pid < 0) { (void)fprintf(stderr, _("%s: Cannot fork\n"), progname); exit(1); } if (!pid) { if (notsetuid) { (void)setgid(getgid()); (void)setuid(getuid()); } execlp(editor, p, tmp_file, NULL); _exit(1); } for (;;) { pid = waitpid(pid, &pstat, WUNTRACED); if (WIFSTOPPED(pstat)) { /* the editor suspended, so suspend us as well */ kill(getpid(), SIGSTOP); kill(pid, SIGCONT); } else { break; } } if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) pw_error(editor, 1, 1); }
static void pw_edit(void) { int pstat; pid_t pid; char *p, *editor, *tk; editor = getenv("EDITOR"); editor = xstrdup(editor ? editor : _PATH_VI); tk = strtok(editor, " \t"); if (tk && (p = strrchr(tk, '/')) != NULL) ++p; else p = editor; pid = fork(); if (pid < 0) err(EXIT_FAILURE, _("fork failed")); if (!pid) { execlp(editor, p, tmp_file, NULL); /* Shouldn't get here */ _exit(EXIT_FAILURE); } for (;;) { pid = waitpid(pid, &pstat, WUNTRACED); if (WIFSTOPPED(pstat)) { /* the editor suspended, so suspend us as well */ kill(getpid(), SIGSTOP); kill(pid, SIGCONT); } else { break; } } if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) pw_error(editor, 1, 1); free(editor); }
int main(int argc, char **argv) { enum { NEWSH, LOADENTRY, EDITENTRY } op; struct passwd *pw, lpw, old_pw; int ch, dfd, pfd, tfd; #ifdef YP int yflag = 0; #endif char *arg, *username = NULL; #ifdef __GNUC__ pw = NULL; /* XXX gcc -Wuninitialized */ arg = NULL; #endif #ifdef YP use_yp = _yp_check(NULL); #endif op = EDITENTRY; while ((ch = getopt(argc, argv, "a:s:ly")) != -1) switch (ch) { case 'a': op = LOADENTRY; arg = optarg; break; case 's': op = NEWSH; arg = optarg; break; case 'l': use_yp = 0; break; case 'y': #ifdef YP if (!use_yp) errx(1, "YP not in use."); yflag = 1; #else errx(1, "YP support not compiled in."); #endif break; default: usage(); } argc -= optind; argv += optind; uid = getuid(); switch (argc) { case 0: /* nothing */ break; case 1: username = argv[0]; break; default: usage(); } #ifdef YP /* * We need to determine if we _really_ want to use YP. * If we defaulted to YP (i.e. were not given the -y flag), * and the master is not running rpc.yppasswdd, we check * to see if the user exists in the local passwd database. * If so, we use it, otherwise we error out. */ if (use_yp && yflag == 0) { if (check_yppasswdd()) { /* * We weren't able to contact rpc.yppasswdd. * Check to see if we're in the local * password database. If we are, use it. */ if (username != NULL) pw = getpwnam(username); else pw = getpwuid(uid); if (pw != NULL) use_yp = 0; else { warnx("master YP server not running yppasswd" " daemon."); errx(1, "Can't change password."); } } } #endif #ifdef YP if (use_yp) Pw_error = yppw_error; else #endif Pw_error = pw_error; #ifdef YP if (op == LOADENTRY && use_yp) errx(1, "cannot load entry using YP.\n" "\tUse the -l flag to load local."); #endif if (op == EDITENTRY || op == NEWSH) { if (username != NULL) { pw = getpwnam(username); if (pw == NULL) errx(1, "unknown user: %s", username); if (uid && uid != pw->pw_uid) baduser(); } else { pw = getpwuid(uid); if (pw == NULL) errx(1, "unknown user: uid %u", uid); } /* Make a copy for later verification */ old_pw = *pw; old_pw.pw_gecos = strdup(old_pw.pw_gecos); if (!old_pw.pw_gecos) { err(1, "strdup"); /*NOTREACHED*/ } } if (op == NEWSH) { /* protect p_shell -- it thinks NULL is /bin/sh */ if (!arg[0]) usage(); if (p_shell(arg, pw, NULL)) (*Pw_error)(NULL, 0, 1); } if (op == LOADENTRY) { if (uid) baduser(); pw = &lpw; if (!pw_scan(arg, pw, NULL)) exit(1); } /* Edit the user passwd information if requested. */ if (op == EDITENTRY) { struct stat sb; dfd = mkstemp(tempname); if (dfd < 0 || fcntl(dfd, F_SETFD, 1) < 0) (*Pw_error)(tempname, 1, 1); if (atexit(cleanup)) { cleanup(); errx(1, "couldn't register cleanup"); } if (stat(dirname(tempname), &sb) == -1) err(1, "couldn't stat `%s'", dirname(tempname)); if (!(sb.st_mode & S_ISTXT)) errx(1, "temporary directory `%s' is not sticky", dirname(tempname)); display(tempname, dfd, pw); edit(tempname, pw); } #ifdef YP if (use_yp) { if (pw_yp(pw, uid)) yppw_error(NULL, 0, 1); else exit(0); /* Will not exit from this if. */ } #endif /* YP */ /* * Get the passwd lock file and open the passwd file for * reading. */ pw_init(); tfd = pw_lock(0); if (tfd < 0) { if (errno != EEXIST) err(1, "%s", _PATH_MASTERPASSWD_LOCK); warnx("The passwd file is busy, waiting..."); tfd = pw_lock(10); if (tfd < 0) { if (errno != EEXIST) err(1, "%s", _PATH_MASTERPASSWD_LOCK); errx(1, "The passwd file is still busy, " "try again later."); } } if (fcntl(tfd, F_SETFD, 1) < 0) pw_error(_PATH_MASTERPASSWD_LOCK, 1, 1); pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0); if (pfd < 0 || fcntl(pfd, F_SETFD, 1) < 0) pw_error(_PATH_MASTERPASSWD, 1, 1); /* Copy the passwd file to the lock file, updating pw. */ pw_copy(pfd, tfd, pw, (op == LOADENTRY) ? NULL : &old_pw); close(pfd); close(tfd); /* Now finish the passwd file update. */ if (pw_mkdb(username, 0) < 0) pw_error(NULL, 0, 1); exit(0); }
int main(int argc, char *argv[]) { struct passwd *pw = NULL, *opw = NULL, lpw; int i, ch, pfd, tfd, dfd; char *tz, *arg = NULL; sigset_t fullset; #ifdef YP use_yp = _yp_check(NULL); #endif /* We need to use the system timezone for date conversions. */ if ((tz = getenv("TZ")) != NULL) { unsetenv("TZ"); tzset(); setenv("TZ", tz, 1); } op = EDITENTRY; while ((ch = getopt(argc, argv, "a:s:ly")) != -1) switch(ch) { case 'a': op = LOADENTRY; arg = optarg; break; case 's': op = NEWSH; arg = optarg; break; #ifdef YP case 'l': use_yp = 0; break; case 'y': if (!use_yp) { warnx("YP not in use."); usage(); } force_yp = 1; break; #endif case '?': default: usage(); } argc -= optind; argv += optind; #ifdef YP if (op == LOADENTRY && use_yp) errx(1, "cannot load using YP, use -l to load local."); #endif uid = getuid(); if (op == EDITENTRY || op == NEWSH) switch(argc) { case 0: pw = getpwuid(uid); #ifdef YP if (pw && !force_yp) use_yp = 0; else if (use_yp) pw = ypgetpwuid(uid); #endif /* YP */ if (!pw) errx(1, "unknown user: uid %u", uid); break; case 1: pw = getpwnam(*argv); #ifdef YP if (pw && !force_yp) use_yp = 0; else if (use_yp) pw = ypgetpwnam(*argv); #endif /* YP */ if (!pw) errx(1, "unknown user: %s", *argv); if (uid && uid != pw->pw_uid) baduser(); break; default: usage(); } if (op == LOADENTRY) { if (argc != 0) errx(1, "option -a does not accept user argument"); if (uid) baduser(); pw = &lpw; if (!pw_scan(arg, pw, NULL)) exit(1); opw = getpwnam(pw->pw_name); } if (opw == NULL && (opw = pw_dup(pw)) == NULL) err(1, NULL); /* Edit the user passwd information if requested. */ if (op == EDITENTRY) { char tempname[] = _PATH_VARTMP "pw.XXXXXXXXXX"; int edit_status; if ((pw = pw_dup(pw)) == NULL) pw_error(NULL, 1, 1); dfd = mkstemp(tempname); if (dfd == -1 || fcntl(dfd, F_SETFD, 1) == -1) pw_error(tempname, 1, 1); display(tempname, dfd, pw); edit_status = edit(tempname, pw); close(dfd); unlink(tempname); switch (edit_status) { case EDIT_OK: break; case EDIT_NOCHANGE: pw_error(NULL, 0, 0); break; case EDIT_ERROR: default: pw_error(tempname, 1, 1); break; } } if (op == NEWSH) { /* protect p_shell -- it thinks NULL is /bin/sh */ if (!arg[0]) usage(); if (p_shell(arg, pw, NULL)) pw_error(NULL, 0, 1); } /* Drop user's real uid and block all signals to avoid a DoS. */ setuid(0); sigfillset(&fullset); sigdelset(&fullset, SIGINT); sigprocmask(SIG_BLOCK, &fullset, NULL); /* Get the passwd lock file and open the passwd file for reading. */ pw_init(); for (i = 1; (tfd = pw_lock(0)) == -1; i++) { if (i == 4) (void)fputs("Attempting to lock password file, " "please wait or press ^C to abort", stderr); (void)signal(SIGINT, kbintr); if (i % 16 == 0) fputc('.', stderr); usleep(250000); (void)signal(SIGINT, SIG_IGN); } if (i >= 4) fputc('\n', stderr); pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0); if (pfd == -1 || fcntl(pfd, F_SETFD, 1) == -1) pw_error(_PATH_MASTERPASSWD, 1, 1); #ifdef YP if (use_yp) { if (pw_yp(pw, uid)) pw_error(NULL, 0, 1); else { pw_abort(); exit(0); } } else #endif /* YP */ { /* Copy the passwd file to the lock file, updating pw. */ pw_copy(pfd, tfd, pw, opw); /* If username changed we need to rebuild the entire db. */ arg = !strcmp(opw->pw_name, pw->pw_name) ? pw->pw_name : NULL; /* Now finish the passwd file update. */ if (pw_mkdb(arg, 0) == -1) pw_error(NULL, 0, 1); } exit(0); }
char * ypgetnewpasswd(struct passwd *pw, login_cap_t *lc, char **old_pass) { static char buf[_PASSWORD_LEN+1]; char salt[_PASSWORD_LEN]; sig_t saveint, savequit; int tries, pwd_tries; char *p; saveint = signal(SIGINT, kbintr); savequit = signal(SIGQUIT, kbintr); printf("Changing YP password for %s.\n", pw->pw_name); if (old_pass) { *old_pass = NULL; if (pw->pw_passwd[0]) { p = getpass("Old password:"******""; *old_pass = strdup(p); if (*old_pass == NULL) pw_error(NULL, 1, 1); } pwd_tries = pwd_gettries(lc); for (buf[0] = '\0', tries = 0;;) { p = getpass("New password:"******"s/key") == 0) { printf("That password collides with a system feature. " "Choose another.\n"); continue; } if ((tries++ < pwd_tries || pwd_tries == 0) && pwd_check(lc, p) == 0) continue; strlcpy(buf, p, sizeof buf); p = getpass("Retype new password:"******"Mismatch; try again, EOF to quit.\n"); } if (!pwd_gensalt(salt, _PASSWORD_LEN, lc, 'y')) { (void)printf("Couldn't generate salt.\n"); pw_error(NULL, 0, 0); } p = strdup(crypt(buf, salt)); if (p == NULL) pw_error(NULL, 1, 1); (void)signal(SIGINT, saveint); (void)signal(SIGQUIT, savequit); return (p); }
void pwlocal_process(const char *username, int argc, char **argv) { struct passwd *pw; struct passwd old_pw; time_t old_change; int pfd, tfd; int min_pw_len = 0; int pw_expiry = 0; int ch; #ifdef LOGIN_CAP login_cap_t *lc; #endif while ((ch = getopt(argc, argv, "l")) != -1) { switch (ch) { case 'l': /* * Aborb the -l that may have gotten us here. */ break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; switch (argc) { case 0: /* username already provided */ break; case 1: username = argv[0]; break; default: usage(); /* NOTREACHED */ } if (!(pw = getpwnam(username))) errx(1, "unknown user %s", username); uid = getuid(); if (uid && uid != pw->pw_uid) errx(1, "%s", strerror(EACCES)); /* Save the old pw information for comparing on pw_copy(). */ old_pw = *pw; /* * Get class restrictions for this user, then get the new password. */ #ifdef LOGIN_CAP if((lc = login_getclass(pw->pw_class)) != NULL) { min_pw_len = (int) login_getcapnum(lc, "minpasswordlen", 0, 0); pw_expiry = (int) login_getcaptime(lc, "passwordtime", 0, 0); login_close(lc); } #endif #if 0 printf("AAA: pw_expiry = %x\n", pw_expiry); #endif pw->pw_passwd = getnewpasswd(pw, min_pw_len); old_change = pw->pw_change; pw->pw_change = pw_expiry ? pw_expiry + time(NULL) : 0; /* * Now that the user has given us a new password, let us * change the database. */ pw_init(); tfd = pw_lock(0); if (tfd < 0) { warnx ("The passwd file is busy, waiting..."); tfd = pw_lock(10); if (tfd < 0) errx(1, "The passwd file is still busy, " "try again later."); } pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0); if (pfd < 0) pw_error(_PATH_MASTERPASSWD, 1, 1); pw_copy(pfd, tfd, pw, &old_pw); if (pw_mkdb(username, old_change == pw->pw_change) < 0) pw_error((char *)NULL, 0, 1); syslog(LOG_AUTH | LOG_INFO, "user %s (UID %lu) successfully changed " "the local password of user %s", uid ? username : "******", (unsigned long)uid, username); }