ubase

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

commit f48d545c77c013aa6da92b011a053699cb45622c
parent a27035c281a8ef1e7420e8c4dd6afa2045692061
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Thu Jul 10 17:51:51 +0000

passwd: improvements

- add shadow support.
- allow passwd without argument, prompt which user password is changed.

Diffstat:
Makefile | 1+
TODO | 2+-
passwd.1 | 4+---
passwd.c | 197+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
text.h | 2++
util/concat.c | 21+++++++++++++++++++++
6 files changed, 159 insertions(+), 68 deletions(-)
diff --git a/Makefile b/Makefile @@ -18,6 +18,7 @@ LIB = \ util/agetcwd.o \ util/agetline.o \ util/apathmax.o \ + util/concat.o \ util/ealloc.o \ util/eprintf.o \ util/estrtol.o \ diff --git a/TODO b/TODO @@ -31,4 +31,4 @@ Misc ==== * Decide what to do with 'bool vs int' debate. - * Beautify passwd(1) + shadow support. + * Beautify passwd(1). diff --git a/passwd.1 b/passwd.1 @@ -2,12 +2,10 @@ .SH NAME \fBpasswd\fR - Change a user's password .SH SYNOPSIS -\fBpasswd\fR \fIusername\fR +\fBpasswd\fR [\fIusername\fR] .SH DESCRIPTION \fBpasswd\fR changes the user's password. The user is prompted for their current password. If the current password is correctly typed, a new password is requested. The new password must be entered twice to avoid typing errors. The superuser is not required to provide a user's current password. -.SH BUGS -Currently there's no shadow support. diff --git a/passwd.c b/passwd.c @@ -11,51 +11,82 @@ #include <string.h> #include <unistd.h> +#include <limits.h> +#include <shadow.h> + #include "config.h" #include "passwd.h" #include "util.h" +#include "text.h" static void usage(void) { - eprintf("usage: %s username\n", argv0); + eprintf("usage: %s [username]\n", argv0); +} + +static int +gettempfile(char *template) +{ + int fd; + + umask(077); + fd = mkostemp(template, O_RDWR); + if (fd < 0) + weprintf("mkstemp:"); + return fd; } int main(int argc, char *argv[]) { - char *pass; char *cryptpass1 = NULL, *cryptpass2 = NULL, *cryptpass3 = NULL; - char *p; + char shadowfile[PATH_MAX], *inpass, *p, *pwd = NULL; char template[] = "/tmp/pw.XXXXXX"; - uid_t uid; struct passwd *pw; - int ffd, tfd; - int r; + struct spwd *spw = NULL, *spwent; + uid_t uid; + FILE *fp = NULL, *tfp = NULL; + int ffd = -1, tfd = -1, r, status = EXIT_FAILURE; ARGBEGIN { default: usage(); } ARGEND; - if (argc != 1) - usage(); - pw_init(); errno = 0; - pw = getpwnam(argv[0]); - if (errno) - eprintf("getpwnam: %s:", argv[0]); - else if (!pw) - eprintf("who are you?\n"); + if (argc == 0) + pw = getpwuid(getuid()); + else + pw = getpwnam(argv[0]); + if (!pw) { + if (errno) + eprintf("getpwnam: %s:", argv[0]); + else + eprintf("who are you?\n"); + } - if (pw->pw_passwd[0] == 'x' && pw->pw_passwd[1] == '\0') - eprintf("no shadow support\n"); + /* is using shadow entry ? */ + if (pw->pw_passwd[0] == 'x') { + errno = 0; + spw = getspnam(pw->pw_name); + if (!spw) { + if (errno) + eprintf("getspnam: %s:", pw->pw_name); + 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; @@ -73,30 +104,29 @@ main(int argc, char *argv[]) /* Flush pending input */ ioctl(STDIN_FILENO, TCFLSH, (void *)0); - pass = getpass("Current password: "); - putchar('\n'); - if (!pass) + printf("Changing password for %s\n", pw->pw_name); + inpass = getpass("Old password: "); + if (!inpass) eprintf("getpass:"); - if (pass[0] == '\0') + if (inpass[0] == '\0') eprintf("no password supplied\n"); - p = crypt(pass, pw->pw_passwd); + p = crypt(inpass, pwd); if (!p) eprintf("crypt:"); cryptpass1 = estrdup(p); - if (strcmp(cryptpass1, pw->pw_passwd) != 0) + if (strcmp(cryptpass1, pwd) != 0) eprintf("incorrect password\n"); newpass: /* Flush pending input */ ioctl(STDIN_FILENO, TCFLSH, (void *)0); - pass = getpass("Enter new password: "); - putchar('\n'); - if (!pass) + inpass = getpass("Enter new password: "); + if (!inpass) eprintf("getpass:"); - if (pass[0] == '\0') + if (inpass[0] == '\0') eprintf("no password supplied\n"); - p = crypt(pass, pw->pw_passwd); + p = crypt(inpass, pwd); if (!p) eprintf("crypt:"); cryptpass2 = estrdup(p); @@ -106,58 +136,97 @@ newpass: /* Flush pending input */ ioctl(STDIN_FILENO, TCFLSH, (void *)0); - pass = getpass("Retype new password: "); - putchar('\n'); - if (!pass) + inpass = getpass("Retype new password: "); + if (!inpass) eprintf("getpass:"); - if (pass[0] == '\0') + if (inpass[0] == '\0') eprintf("no password supplied\n"); - p = crypt(pass, pw->pw_passwd); + p = crypt(inpass, pwd); if (!p) eprintf("crypt:"); cryptpass3 = estrdup(p); if (strcmp(cryptpass2, cryptpass3) != 0) eprintf("passwords don't match\n"); - pw->pw_passwd = cryptpass3; - - ffd = open("/etc/passwd", O_RDWR); - if (ffd < 0) - eprintf("open %s:", "/etc/passwd"); - - tfd = mkostemp(template, O_RDWR); - if (tfd < 0) - eprintf("mkstemp:"); - - r = pw_copy(ffd, tfd, pw); - if (r < 0) { - unlink(template); - exit(EXIT_FAILURE); + 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+"); } + 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); - r = lseek(ffd, 0, SEEK_SET); - if (r < 0) { - unlink(template); - exit(EXIT_FAILURE); - } - r = lseek(tfd, 0, SEEK_SET); - if (r < 0) { - unlink(template); - exit(EXIT_FAILURE); - } + if (fseek(fp, 0, SEEK_SET) == -1 || fseek(tfp, 0, SEEK_SET) == -1) { + weprintf("rewind:"); + goto cleanup; + } - r = pw_copy(tfd, ffd, NULL); - if (r < 0) { + /* old shadow file with temporary file data. */ + concat(tfp, template, fp, shadowfile); + ftruncate(tfd, ftell(tfp)); + } 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; + close(ffd); + } + status = EXIT_SUCCESS; + +cleanup: + if (fp) + fclose(fp); + if (tfp) + fclose(tfp); + if (tfd != -1) { + close(tfd); unlink(template); - exit(EXIT_FAILURE); } - - close(tfd); - close(ffd); - unlink(template); + if (ffd != -1) + close(ffd); free(cryptpass3); free(cryptpass2); free(cryptpass1); - return EXIT_SUCCESS; + return status; } diff --git a/text.h b/text.h @@ -9,3 +9,5 @@ struct linebuf { void getlines(FILE *, struct linebuf *); ssize_t agetline(char **, size_t *, FILE *); + +void concat(FILE *, const char *, FILE *, const char *); diff --git a/util/concat.c b/util/concat.c @@ -0,0 +1,21 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> + +#include "../text.h" +#include "../util.h" + +void +concat(FILE *fp1, const char *s1, FILE *fp2, const char *s2) +{ + char buf[BUFSIZ]; + size_t n; + + while ((n = fread(buf, 1, sizeof buf, fp1)) > 0) { + if (fwrite(buf, 1, n, fp2) != n) + eprintf("%s: write error:", s2); + if (feof(fp1)) + break; + } + if (ferror(fp1)) + eprintf("%s: read error:", s1); +}