commit f48d545c77c013aa6da92b011a053699cb45622c
parent a27035c281a8ef1e7420e8c4dd6afa2045692061
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Thu, 10 Jul 2014 17:51:51 +0000
passwd: improvements
- add shadow support.
- allow passwd without argument, prompt which user password is changed.
Diffstat:
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);
+}