passwd: improvements

- add shadow support.
- allow passwd without argument, prompt which user password is changed.
This commit is contained in:
Hiltjo Posthuma 2014-07-10 17:51:51 +00:00 committed by sin
parent a27035c281
commit f48d545c77
6 changed files with 160 additions and 69 deletions

View File

@ -18,6 +18,7 @@ LIB = \
util/agetcwd.o \ util/agetcwd.o \
util/agetline.o \ util/agetline.o \
util/apathmax.o \ util/apathmax.o \
util/concat.o \
util/ealloc.o \ util/ealloc.o \
util/eprintf.o \ util/eprintf.o \
util/estrtol.o \ util/estrtol.o \

2
TODO
View File

@ -31,4 +31,4 @@ Misc
==== ====
* Decide what to do with 'bool vs int' debate. * Decide what to do with 'bool vs int' debate.
* Beautify passwd(1) + shadow support. * Beautify passwd(1).

View File

@ -2,12 +2,10 @@
.SH NAME .SH NAME
\fBpasswd\fR - Change a user's password \fBpasswd\fR - Change a user's password
.SH SYNOPSIS .SH SYNOPSIS
\fBpasswd\fR \fIusername\fR \fBpasswd\fR [\fIusername\fR]
.SH DESCRIPTION .SH DESCRIPTION
\fBpasswd\fR changes the user's password. The user is prompted \fBpasswd\fR changes the user's password. The user is prompted
for their current password. If the current password is correctly typed, for their current password. If the current password is correctly typed,
a new password is requested. The new password must be entered twice to 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 avoid typing errors. The superuser is not required to provide a user's
current password. current password.
.SH BUGS
Currently there's no shadow support.

199
passwd.c
View File

@ -11,51 +11,82 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <limits.h>
#include <shadow.h>
#include "config.h" #include "config.h"
#include "passwd.h" #include "passwd.h"
#include "util.h" #include "util.h"
#include "text.h"
static void static void
usage(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 int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
char *pass;
char *cryptpass1 = NULL, *cryptpass2 = NULL, *cryptpass3 = NULL; char *cryptpass1 = NULL, *cryptpass2 = NULL, *cryptpass3 = NULL;
char *p; char shadowfile[PATH_MAX], *inpass, *p, *pwd = NULL;
char template[] = "/tmp/pw.XXXXXX"; char template[] = "/tmp/pw.XXXXXX";
uid_t uid;
struct passwd *pw; struct passwd *pw;
int ffd, tfd; struct spwd *spw = NULL, *spwent;
int r; uid_t uid;
FILE *fp = NULL, *tfp = NULL;
int ffd = -1, tfd = -1, r, status = EXIT_FAILURE;
ARGBEGIN { ARGBEGIN {
default: default:
usage(); usage();
} ARGEND; } ARGEND;
if (argc != 1)
usage();
pw_init(); pw_init();
errno = 0; errno = 0;
pw = getpwnam(argv[0]); if (argc == 0)
if (errno) pw = getpwuid(getuid());
eprintf("getpwnam: %s:", argv[0]); else
else if (!pw) pw = getpwnam(argv[0]);
eprintf("who are you?\n"); 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') /* is using shadow entry ? */
eprintf("no shadow support\n"); 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(); uid = getuid();
if (uid == 0) { if (uid == 0) {
if (pw->pw_passwd[0] == '!' || if (pw->pw_passwd[0] == '!' ||
pw->pw_passwd[0] == 'x' ||
pw->pw_passwd[0] == '*' || pw->pw_passwd[0] == '*' ||
pw->pw_passwd[0] == '\0') pw->pw_passwd[0] == '\0')
pw->pw_passwd = PW_CIPHER; pw->pw_passwd = PW_CIPHER;
@ -73,30 +104,29 @@ main(int argc, char *argv[])
/* Flush pending input */ /* Flush pending input */
ioctl(STDIN_FILENO, TCFLSH, (void *)0); ioctl(STDIN_FILENO, TCFLSH, (void *)0);
pass = getpass("Current password: "); printf("Changing password for %s\n", pw->pw_name);
putchar('\n'); inpass = getpass("Old password: ");
if (!pass) if (!inpass)
eprintf("getpass:"); eprintf("getpass:");
if (pass[0] == '\0') if (inpass[0] == '\0')
eprintf("no password supplied\n"); eprintf("no password supplied\n");
p = crypt(pass, pw->pw_passwd); p = crypt(inpass, pwd);
if (!p) if (!p)
eprintf("crypt:"); eprintf("crypt:");
cryptpass1 = estrdup(p); cryptpass1 = estrdup(p);
if (strcmp(cryptpass1, pw->pw_passwd) != 0) if (strcmp(cryptpass1, pwd) != 0)
eprintf("incorrect password\n"); eprintf("incorrect password\n");
newpass: newpass:
/* Flush pending input */ /* Flush pending input */
ioctl(STDIN_FILENO, TCFLSH, (void *)0); ioctl(STDIN_FILENO, TCFLSH, (void *)0);
pass = getpass("Enter new password: "); inpass = getpass("Enter new password: ");
putchar('\n'); if (!inpass)
if (!pass)
eprintf("getpass:"); eprintf("getpass:");
if (pass[0] == '\0') if (inpass[0] == '\0')
eprintf("no password supplied\n"); eprintf("no password supplied\n");
p = crypt(pass, pw->pw_passwd); p = crypt(inpass, pwd);
if (!p) if (!p)
eprintf("crypt:"); eprintf("crypt:");
cryptpass2 = estrdup(p); cryptpass2 = estrdup(p);
@ -106,58 +136,97 @@ newpass:
/* Flush pending input */ /* Flush pending input */
ioctl(STDIN_FILENO, TCFLSH, (void *)0); ioctl(STDIN_FILENO, TCFLSH, (void *)0);
pass = getpass("Retype new password: "); inpass = getpass("Retype new password: ");
putchar('\n'); if (!inpass)
if (!pass)
eprintf("getpass:"); eprintf("getpass:");
if (pass[0] == '\0') if (inpass[0] == '\0')
eprintf("no password supplied\n"); eprintf("no password supplied\n");
p = crypt(pass, pw->pw_passwd); p = crypt(inpass, pwd);
if (!p) if (!p)
eprintf("crypt:"); eprintf("crypt:");
cryptpass3 = estrdup(p); cryptpass3 = estrdup(p);
if (strcmp(cryptpass2, cryptpass3) != 0) if (strcmp(cryptpass2, cryptpass3) != 0)
eprintf("passwords don't match\n"); eprintf("passwords don't match\n");
pw->pw_passwd = cryptpass3; r = snprintf(shadowfile, sizeof(shadowfile), "/etc/tcb/%s/shadow", pw->pw_name);
if (r < 0 || (size_t)r >= sizeof(shadowfile))
ffd = open("/etc/passwd", O_RDWR); eprintf("snprintf:");
if (ffd < 0) fp = fopen(shadowfile, "r+");
eprintf("open %s:", "/etc/passwd"); if (!fp) {
strlcpy(shadowfile, "/etc/shadow", sizeof(shadowfile));
tfd = mkostemp(template, O_RDWR); fp = fopen(shadowfile, "r+");
if (tfd < 0)
eprintf("mkstemp:");
r = pw_copy(ffd, tfd, pw);
if (r < 0) {
unlink(template);
exit(EXIT_FAILURE);
} }
if (fp) {
if ((tfd = gettempfile(template)) == -1)
goto cleanup;
r = lseek(ffd, 0, SEEK_SET); /* write to (tcb) shadow file. */
if (r < 0) { if (!(tfp = fdopen(tfd, "w+"))) {
unlink(template); weprintf("fdopen:");
exit(EXIT_FAILURE); goto cleanup;
} }
r = lseek(tfd, 0, SEEK_SET); while ((spwent = fgetspent(fp))) {
if (r < 0) { /* update entry on name match */
unlink(template); if (strcmp(spwent->sp_namp, spw->sp_namp) == 0)
exit(EXIT_FAILURE); spwent->sp_pwdp = cryptpass3;
} errno = 0;
if (putspent(spwent, tfp) == -1) {
weprintf("putspent:");
goto cleanup;
}
}
fflush(tfp);
r = pw_copy(tfd, ffd, NULL); if (fseek(fp, 0, SEEK_SET) == -1 || fseek(tfp, 0, SEEK_SET) == -1) {
if (r < 0) { weprintf("rewind:");
unlink(template); goto cleanup;
exit(EXIT_FAILURE); }
}
close(tfd); /* old shadow file with temporary file data. */
close(ffd); concat(tfp, template, fp, shadowfile);
unlink(template); 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);
}
if (ffd != -1)
close(ffd);
free(cryptpass3); free(cryptpass3);
free(cryptpass2); free(cryptpass2);
free(cryptpass1); free(cryptpass1);
return EXIT_SUCCESS; return status;
} }

2
text.h
View File

@ -9,3 +9,5 @@ struct linebuf {
void getlines(FILE *, struct linebuf *); void getlines(FILE *, struct linebuf *);
ssize_t agetline(char **, size_t *, FILE *); ssize_t agetline(char **, size_t *, FILE *);
void concat(FILE *, const char *, FILE *, const char *);

21
util/concat.c Normal file
View File

@ -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);
}