ubase

suckless linux base utils
git clone git://git.suckless.org/ubase
Log | Files | Refs | README | LICENSE

commit 8fdc7d70bd76b43ed760a4ab7e113a9720da9ca1
parent 10a539e7445ed00f3c6914bf17e3c9d84906c031
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sun, 13 Jul 2014 19:55:46 +0000

passwd: fix /etc/passwd support, ...

... rewrite parts to use libc functions (supported by musl and glibc).

Diffstat:
Mpasswd.c | 235++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mpasswd.h | 4+---
Mutil/passwd.c | 109+------------------------------------------------------------------------------
3 files changed, 133 insertions(+), 215 deletions(-)

diff --git a/passwd.c b/passwd.c @@ -25,29 +25,124 @@ usage(void) eprintf("usage: %s [username]\n", argv0); } +static FILE * +spw_get_file(const char *user) +{ + FILE *fp = NULL; + char file[PATH_MAX]; + int r; + + r = snprintf(file, sizeof(file), "/etc/tcb/%s/shadow", user); + if (r < 0 || (size_t)r >= sizeof(file)) + eprintf("snprintf:"); + fp = fopen(file, "r+"); + if (!fp) + fp = fopen("/etc/shadow", "r+"); + return fp; +} + static int -gettempfile(char *template) +spw_write_file(FILE *fp, const struct spwd *spw, char *pwhash) { - int fd; + struct spwd *spwent; + int r = -1, w = 0; + FILE *tfp = NULL; - umask(077); - fd = mkostemp(template, O_RDWR); - if (fd < 0) - weprintf("mkstemp:"); - return fd; + /* write to temporary file. */ + tfp = tmpfile(); + if (!tfp) { + weprintf("tmpfile:"); + goto cleanup; + } + while ((spwent = fgetspent(fp))) { + /* update entry on name match */ + if (strcmp(spwent->sp_namp, spw->sp_namp) == 0) { + spwent->sp_pwdp = pwhash; + w++; + } + errno = 0; + if (putspent(spwent, tfp) == -1) { + weprintf("putspent:"); + goto cleanup; + } + } + if (!w) { + weprintf("shadow: no matching entry to write to\n"); + goto cleanup; + } + fflush(tfp); + + if (fseek(fp, 0, SEEK_SET) == -1 || fseek(tfp, 0, SEEK_SET) == -1) { + weprintf("fseek:"); + goto cleanup; + } + + /* write temporary file to (tcb) shadow file */ + concat(tfp, "tmpfile", fp, "shadow"); + ftruncate(fileno(fp), ftell(tfp)); + + r = 0; /* success */ +cleanup: + if (tfp) + fclose(tfp); + return r; +} + +static +int pw_write_file(FILE *fp, const struct passwd *pw, char *pwhash) { + struct passwd *pwent; + int r = -1, w = 0; + FILE *tfp = NULL; + + /* write to temporary file. */ + tfp = tmpfile(); + if (!tfp) { + weprintf("tmpfile:"); + goto cleanup; + } + while ((pwent = fgetpwent(fp))) { + /* update entry on name match */ + if (strcmp(pwent->pw_name, pw->pw_name) == 0) { + pwent->pw_passwd = pwhash; + w++; + } + errno = 0; + if (putpwent(pwent, tfp) == -1) { + weprintf("putpwent:"); + goto cleanup; + } + } + if (!w) { + weprintf("passwd: no matching entry to write to\n"); + goto cleanup; + } + fflush(tfp); + + if (fseek(fp, 0, SEEK_SET) == -1 || fseek(tfp, 0, SEEK_SET) == -1) { + weprintf("fseek:"); + goto cleanup; + } + + /* write to passwd file. */ + concat(tfp, "tmpfile", fp, "passwd"); + ftruncate(fileno(fp), ftell(tfp)); + + r = 0; /* success */ +cleanup: + if (tfp) + fclose(tfp); + return r; } int main(int argc, char *argv[]) { char *cryptpass1 = NULL, *cryptpass2 = NULL, *cryptpass3 = NULL; - char shadowfile[PATH_MAX], *inpass, *p, *pwd = NULL; - char template[] = "/tmp/pw.XXXXXX"; + char *inpass, *p, *salt = PW_CIPHER, *prevhash = NULL; struct passwd *pw; - struct spwd *spw = NULL, *spwent; - uid_t uid; - FILE *fp = NULL, *tfp = NULL; - int ffd = -1, tfd = -1, r, status = EXIT_FAILURE; + struct spwd *spw = NULL; + FILE *fp = NULL; + int r = -1, status = EXIT_FAILURE; ARGBEGIN { default: @@ -55,6 +150,7 @@ main(int argc, char *argv[]) } ARGEND; pw_init(); + umask(077); errno = 0; if (argc == 0) @@ -69,7 +165,7 @@ main(int argc, char *argv[]) } /* is using shadow entry ? */ - if (pw->pw_passwd[0] == 'x') { + if (pw->pw_passwd[0] == 'x' && pw->pw_passwd[1] == '\0') { errno = 0; spw = getspnam(pw->pw_name); if (!spw) { @@ -78,55 +174,46 @@ main(int argc, char *argv[]) else eprintf("who are you?\n"); } - pwd = spw->sp_pwdp; - } else { - pwd = pw->pw_passwd; } - uid = getuid(); - if (uid == 0) { - if (pw->pw_passwd[0] == '!' || - pw->pw_passwd[0] == 'x' || - pw->pw_passwd[0] == '*' || - pw->pw_passwd[0] == '\0') - pw->pw_passwd = PW_CIPHER; + /* Flush pending input */ + ioctl(STDIN_FILENO, TCFLSH, (void *)0); + + if (getuid() == 0) { goto newpass; } else { if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') eprintf("denied\n"); if (pw->pw_passwd[0] == '\0') { - pw->pw_passwd = PW_CIPHER; goto newpass; } + if (pw->pw_passwd[0] == 'x') + prevhash = salt = spw->sp_pwdp; + else + prevhash = salt = pw->pw_passwd; } - /* Flush pending input */ - ioctl(STDIN_FILENO, TCFLSH, (void *)0); - printf("Changing password for %s\n", pw->pw_name); inpass = getpass("Old password: "); if (!inpass) eprintf("getpass:"); if (inpass[0] == '\0') eprintf("no password supplied\n"); - p = crypt(inpass, pwd); + p = crypt(inpass, salt); if (!p) eprintf("crypt:"); cryptpass1 = estrdup(p); - if (strcmp(cryptpass1, pwd) != 0) + if (strcmp(cryptpass1, prevhash) != 0) eprintf("incorrect password\n"); newpass: - /* Flush pending input */ - ioctl(STDIN_FILENO, TCFLSH, (void *)0); - inpass = getpass("Enter new password: "); if (!inpass) eprintf("getpass:"); if (inpass[0] == '\0') eprintf("no password supplied\n"); - p = crypt(inpass, pwd); + p = crypt(inpass, salt); if (!p) eprintf("crypt:"); cryptpass2 = estrdup(p); @@ -141,88 +228,28 @@ newpass: eprintf("getpass:"); if (inpass[0] == '\0') eprintf("no password supplied\n"); - p = crypt(inpass, pwd); + p = crypt(inpass, salt); if (!p) eprintf("crypt:"); cryptpass3 = estrdup(p); if (strcmp(cryptpass2, cryptpass3) != 0) eprintf("passwords don't match\n"); - r = snprintf(shadowfile, sizeof(shadowfile), "/etc/tcb/%s/shadow", pw->pw_name); - if (r < 0 || (size_t)r >= sizeof(shadowfile)) - eprintf("snprintf:"); - fp = fopen(shadowfile, "r+"); - if (!fp) { - strlcpy(shadowfile, "/etc/shadow", sizeof(shadowfile)); - fp = fopen(shadowfile, "r+"); - } + fp = spw_get_file(pw->pw_name); if (fp) { - if ((tfd = gettempfile(template)) == -1) - goto cleanup; - - /* write to (tcb) shadow file. */ - if (!(tfp = fdopen(tfd, "w+"))) { - weprintf("fdopen:"); - goto cleanup; - } - while ((spwent = fgetspent(fp))) { - /* update entry on name match */ - if (strcmp(spwent->sp_namp, spw->sp_namp) == 0) - spwent->sp_pwdp = cryptpass3; - errno = 0; - if (putspent(spwent, tfp) == -1) { - weprintf("putspent:"); - goto cleanup; - } - } - fflush(tfp); - - if (fseek(fp, 0, SEEK_SET) == -1 || fseek(tfp, 0, SEEK_SET) == -1) { - weprintf("rewind:"); - goto cleanup; - } - - /* old shadow file with temporary file data. */ - concat(tfp, template, fp, shadowfile); - ftruncate(tfd, ftell(tfp)); + r = spw_write_file(fp, spw, cryptpass3); } else { - /* write to /etc/passwd file. */ - ffd = open("/etc/passwd", O_RDWR); - if (ffd < 0) { - weprintf("open %s:", "/etc/passwd"); - goto cleanup; - } - pw->pw_passwd = cryptpass3; - - if ((tfd = gettempfile(template)) == -1) - goto cleanup; - - r = pw_copy(ffd, tfd, pw); - if (r < 0) - goto cleanup; - r = lseek(ffd, 0, SEEK_SET); - if (r < 0) - goto cleanup; - r = lseek(tfd, 0, SEEK_SET); - if (r < 0) - goto cleanup; - r = pw_copy(tfd, ffd, NULL); - if (r < 0) - goto cleanup; + fp = fopen("/etc/passwd", "r+"); + if (fp) + r = pw_write_file(fp, pw, cryptpass3); + else + weprintf("fopen:"); } - status = EXIT_SUCCESS; + if (!r) + status = EXIT_SUCCESS; -cleanup: if (fp) fclose(fp); - if (tfp) - fclose(tfp); - if (tfd != -1) { - close(tfd); - unlink(template); - } - if (ffd != -1) - close(ffd); free(cryptpass3); free(cryptpass2); free(cryptpass1); diff --git a/passwd.h b/passwd.h @@ -1,6 +1,4 @@ /* See LICENSE file for copyright and license details. */ /* passwd.c */ -int pw_check(struct passwd *, const char *); -int pw_copy(int, int, const struct passwd *); +int pw_check(const struct passwd *, const char *); int pw_init(void); -int pw_scan(char *, struct passwd *); diff --git a/util/passwd.c b/util/passwd.c @@ -17,7 +17,7 @@ /* Returns -1 on error, 0 for incorrect password * and 1 if all went OK */ int -pw_check(struct passwd *pw, const char *pass) +pw_check(const struct passwd *pw, const char *pass) { char *cryptpass, *p; struct spwd *spw; @@ -65,53 +65,6 @@ pw_check(struct passwd *pw, const char *pass) } int -pw_copy(int ffd, int tfd, const struct passwd *newpw) -{ - struct passwd pw; - char *buf = NULL, *p; - size_t size = 0; - FILE *from, *to; - - from = fdopen(ffd, "r"); - if (!from) { - weprintf("fdopen:"); - return -1; - } - to = fdopen(tfd, "w"); - if (!to) { - weprintf("fdopen:"); - return -1; - } - while (agetline(&buf, &size, from) != -1) { - p = strdup(buf); - if (!p) { - weprintf("strdup:"); - return -1; - } - if (newpw) { - if (pw_scan(p, &pw) < 0) - return -1; - if (strcmp(pw.pw_name, newpw->pw_name) == 0) { - fprintf(to, "%s:%s:%u:%u:%s:%s:%s\n", - newpw->pw_name, - newpw->pw_passwd, - newpw->pw_uid, - newpw->pw_gid, - newpw->pw_gecos, - newpw->pw_dir, - newpw->pw_shell); - continue; - } - } - fprintf(to, "%s", buf); - free(p); - } - fflush(to); - free(buf); - return 0; -} - -int pw_init(void) { struct rlimit rlim; @@ -122,63 +75,3 @@ pw_init(void) eprintf("setrlimit:"); return 0; } - -int -pw_scan(char *bp, struct passwd *pw) -{ - char *p; - - memset(pw, 0, sizeof(*pw)); - - /* login name */ - p = strsep(&bp, ":"); - if (!p || *p == '\0') - goto corrupt; - pw->pw_name = p; - - /* passwd */ - p = strsep(&bp, ":"); - if (!p) - goto corrupt; - pw->pw_passwd = p; - - /* uid */ - p = strsep(&bp, ":"); - if (!p) - goto corrupt; - pw->pw_uid = estrtol(p, 10); - - /* gid */ - p = strsep(&bp, ":"); - if (!p) - goto corrupt; - pw->pw_gid = estrtol(p, 10); - - /* user name or comment */ - p = strsep(&bp, ":"); - if (!p) - goto corrupt; - pw->pw_gecos = p; - - /* home directory */ - p = strsep(&bp, ":"); - if (!p) - goto corrupt; - pw->pw_dir = p; - - /* optional shell */ - p = strsep(&bp, ":"); - if (!p) - goto corrupt; - pw->pw_shell = p; - - /* look for redundant entries */ - p = strsep(&bp, ":"); - if (p) - goto corrupt; - - return 0; -corrupt: - weprintf("corrupted passwd entry\n"); - return -1; -}