1
0
mirror of git://git.suckless.org/ubase synced 2024-12-18 12:44:57 +00:00
ubase/passwd.c
2016-04-24 17:47:28 +01:00

258 lines
5.0 KiB
C

/* See LICENSE file for copyright and license details. */
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <shadow.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;
}
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, *salt = PW_CIPHER, *prevhash = NULL;
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 = salt = spw->sp_pwdp;
else
prevhash = salt = 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, salt);
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");
p = crypt(inpass, salt);
if (!p)
eprintf("crypt:");
cryptpass2 = estrdup(p);
if (cryptpass1 && strcmp(cryptpass1, cryptpass2) == 0)
eprintf("password left unchanged\n");
/* 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;
}