4f1b54dd92
When running as root, passwd attempts to compare the new password to the old password, without having grabbed the old passwd. This checks if the previous password hash was grabbed before comparing it against the new password hash.
288 lines
5.6 KiB
C
288 lines
5.6 KiB
C
/* See LICENSE file for copyright and license details. */
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/syscall.h>
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <pwd.h>
|
|
#include <shadow.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "config.h"
|
|
#include "passwd.h"
|
|
#include "text.h"
|
|
#include "util.h"
|
|
|
|
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
|
|
spw_write_file(FILE *fp, const struct spwd *spw, char *pwhash)
|
|
{
|
|
struct spwd *spwent;
|
|
int r = -1, w = 0;
|
|
FILE *tfp = NULL;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* generates a random base64-encoded salt string of length 16 */
|
|
static void
|
|
gensalt(char *s)
|
|
{
|
|
static const char b64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
uint8_t buf[12];
|
|
uint32_t n;
|
|
int i;
|
|
|
|
if (syscall(SYS_getrandom, buf, sizeof(buf), 0) < 0)
|
|
eprintf("getrandom:");
|
|
for (i = 0; i < 12; i += 3) {
|
|
n = buf[i] << 16 | buf[i+1] << 8 | buf[i+2];
|
|
*s++ = b64[n%64]; n /= 64;
|
|
*s++ = b64[n%64]; n /= 64;
|
|
*s++ = b64[n%64]; n /= 64;
|
|
*s++ = b64[n];
|
|
}
|
|
*s++ = '\0';
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
eprintf("usage: %s [username]\n", argv0);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
char *cryptpass1 = NULL, *cryptpass2 = NULL, *cryptpass3 = NULL;
|
|
char *inpass, *p, *prevhash = NULL, salt[sizeof(PW_CIPHER) + 16] = PW_CIPHER;
|
|
struct passwd *pw;
|
|
struct spwd *spw = NULL;
|
|
FILE *fp = NULL;
|
|
int r = -1, status = 1;
|
|
|
|
ARGBEGIN {
|
|
default:
|
|
usage();
|
|
} ARGEND;
|
|
|
|
pw_init();
|
|
umask(077);
|
|
|
|
errno = 0;
|
|
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");
|
|
}
|
|
|
|
/* is using shadow entry ? */
|
|
if (pw->pw_passwd[0] == 'x' && pw->pw_passwd[1] == '\0') {
|
|
errno = 0;
|
|
spw = getspnam(pw->pw_name);
|
|
if (!spw) {
|
|
if (errno)
|
|
eprintf("getspnam: %s:", pw->pw_name);
|
|
else
|
|
eprintf("who are you?\n");
|
|
}
|
|
}
|
|
|
|
/* Flush pending input */
|
|
ioctl(0, 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') {
|
|
goto newpass;
|
|
}
|
|
if (pw->pw_passwd[0] == 'x')
|
|
prevhash = spw->sp_pwdp;
|
|
else
|
|
prevhash = pw->pw_passwd;
|
|
}
|
|
|
|
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, prevhash);
|
|
if (!p)
|
|
eprintf("crypt:");
|
|
cryptpass1 = estrdup(p);
|
|
if (strcmp(cryptpass1, prevhash) != 0)
|
|
eprintf("incorrect password\n");
|
|
|
|
newpass:
|
|
inpass = getpass("Enter new password: ");
|
|
if (!inpass)
|
|
eprintf("getpass:");
|
|
if (inpass[0] == '\0')
|
|
eprintf("no password supplied\n");
|
|
|
|
if(prevhash) {
|
|
p = crypt(inpass, prevhash);
|
|
if (!p)
|
|
eprintf("crypt:");
|
|
if (cryptpass1 && strcmp(cryptpass1, p) == 0)
|
|
eprintf("password left unchanged\n");
|
|
}
|
|
gensalt(salt + strlen(salt));
|
|
p = crypt(inpass, salt);
|
|
if (!p)
|
|
eprintf("crypt:");
|
|
cryptpass2 = estrdup(p);
|
|
|
|
/* Flush pending input */
|
|
ioctl(0, TCFLSH, (void *)0);
|
|
|
|
inpass = getpass("Retype new password: ");
|
|
if (!inpass)
|
|
eprintf("getpass:");
|
|
if (inpass[0] == '\0')
|
|
eprintf("no password supplied\n");
|
|
p = crypt(inpass, salt);
|
|
if (!p)
|
|
eprintf("crypt:");
|
|
cryptpass3 = estrdup(p);
|
|
if (strcmp(cryptpass2, cryptpass3) != 0)
|
|
eprintf("passwords don't match\n");
|
|
|
|
fp = spw_get_file(pw->pw_name);
|
|
if (fp) {
|
|
r = spw_write_file(fp, spw, cryptpass3);
|
|
} else {
|
|
fp = fopen("/etc/passwd", "r+");
|
|
if (fp)
|
|
r = pw_write_file(fp, pw, cryptpass3);
|
|
else
|
|
weprintf("fopen:");
|
|
}
|
|
if (!r)
|
|
status = 0;
|
|
|
|
if (fp)
|
|
fclose(fp);
|
|
free(cryptpass3);
|
|
free(cryptpass2);
|
|
free(cryptpass1);
|
|
|
|
return status;
|
|
}
|