passwd: fix /etc/passwd support, ...

... rewrite parts to use libc functions (supported by musl and glibc).
This commit is contained in:
Hiltjo Posthuma 2014-07-13 19:55:46 +00:00 committed by sin
parent 10a539e744
commit 8fdc7d70bd
3 changed files with 134 additions and 216 deletions

237
passwd.c
View File

@ -25,29 +25,124 @@ usage(void)
eprintf("usage: %s [username]\n", argv0); eprintf("usage: %s [username]\n", argv0);
} }
static int static FILE *
gettempfile(char *template) spw_get_file(const char *user)
{ {
int fd; FILE *fp = NULL;
char file[PATH_MAX];
int r;
umask(077); r = snprintf(file, sizeof(file), "/etc/tcb/%s/shadow", user);
fd = mkostemp(template, O_RDWR); if (r < 0 || (size_t)r >= sizeof(file))
if (fd < 0) eprintf("snprintf:");
weprintf("mkstemp:"); fp = fopen(file, "r+");
return fd; 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;
} }
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
char *cryptpass1 = NULL, *cryptpass2 = NULL, *cryptpass3 = NULL; char *cryptpass1 = NULL, *cryptpass2 = NULL, *cryptpass3 = NULL;
char shadowfile[PATH_MAX], *inpass, *p, *pwd = NULL; char *inpass, *p, *salt = PW_CIPHER, *prevhash = NULL;
char template[] = "/tmp/pw.XXXXXX";
struct passwd *pw; struct passwd *pw;
struct spwd *spw = NULL, *spwent; struct spwd *spw = NULL;
uid_t uid; FILE *fp = NULL;
FILE *fp = NULL, *tfp = NULL; int r = -1, status = EXIT_FAILURE;
int ffd = -1, tfd = -1, r, status = EXIT_FAILURE;
ARGBEGIN { ARGBEGIN {
default: default:
@ -55,6 +150,7 @@ main(int argc, char *argv[])
} ARGEND; } ARGEND;
pw_init(); pw_init();
umask(077);
errno = 0; errno = 0;
if (argc == 0) if (argc == 0)
@ -69,7 +165,7 @@ main(int argc, char *argv[])
} }
/* is using shadow entry ? */ /* is using shadow entry ? */
if (pw->pw_passwd[0] == 'x') { if (pw->pw_passwd[0] == 'x' && pw->pw_passwd[1] == '\0') {
errno = 0; errno = 0;
spw = getspnam(pw->pw_name); spw = getspnam(pw->pw_name);
if (!spw) { if (!spw) {
@ -78,55 +174,46 @@ main(int argc, char *argv[])
else else
eprintf("who are you?\n"); eprintf("who are you?\n");
} }
pwd = spw->sp_pwdp;
} else {
pwd = pw->pw_passwd;
} }
uid = getuid(); /* Flush pending input */
if (uid == 0) { ioctl(STDIN_FILENO, TCFLSH, (void *)0);
if (pw->pw_passwd[0] == '!' ||
pw->pw_passwd[0] == 'x' || if (getuid() == 0) {
pw->pw_passwd[0] == '*' ||
pw->pw_passwd[0] == '\0')
pw->pw_passwd = PW_CIPHER;
goto newpass; goto newpass;
} else { } else {
if (pw->pw_passwd[0] == '!' || if (pw->pw_passwd[0] == '!' ||
pw->pw_passwd[0] == '*') pw->pw_passwd[0] == '*')
eprintf("denied\n"); eprintf("denied\n");
if (pw->pw_passwd[0] == '\0') { if (pw->pw_passwd[0] == '\0') {
pw->pw_passwd = PW_CIPHER;
goto newpass; goto newpass;
} }
if (pw->pw_passwd[0] == 'x')
prevhash = salt = spw->sp_pwdp;
else
prevhash = salt = pw->pw_passwd;
} }
/* Flush pending input */
ioctl(STDIN_FILENO, TCFLSH, (void *)0);
printf("Changing password for %s\n", pw->pw_name); printf("Changing password for %s\n", pw->pw_name);
inpass = getpass("Old password: "); inpass = getpass("Old password: ");
if (!inpass) if (!inpass)
eprintf("getpass:"); eprintf("getpass:");
if (inpass[0] == '\0') if (inpass[0] == '\0')
eprintf("no password supplied\n"); eprintf("no password supplied\n");
p = crypt(inpass, pwd); p = crypt(inpass, salt);
if (!p) if (!p)
eprintf("crypt:"); eprintf("crypt:");
cryptpass1 = estrdup(p); cryptpass1 = estrdup(p);
if (strcmp(cryptpass1, pwd) != 0) if (strcmp(cryptpass1, prevhash) != 0)
eprintf("incorrect password\n"); eprintf("incorrect password\n");
newpass: newpass:
/* Flush pending input */
ioctl(STDIN_FILENO, TCFLSH, (void *)0);
inpass = getpass("Enter new password: "); inpass = getpass("Enter new password: ");
if (!inpass) if (!inpass)
eprintf("getpass:"); eprintf("getpass:");
if (inpass[0] == '\0') if (inpass[0] == '\0')
eprintf("no password supplied\n"); eprintf("no password supplied\n");
p = crypt(inpass, pwd); p = crypt(inpass, salt);
if (!p) if (!p)
eprintf("crypt:"); eprintf("crypt:");
cryptpass2 = estrdup(p); cryptpass2 = estrdup(p);
@ -141,88 +228,28 @@ newpass:
eprintf("getpass:"); eprintf("getpass:");
if (inpass[0] == '\0') if (inpass[0] == '\0')
eprintf("no password supplied\n"); eprintf("no password supplied\n");
p = crypt(inpass, pwd); p = crypt(inpass, salt);
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");
r = snprintf(shadowfile, sizeof(shadowfile), "/etc/tcb/%s/shadow", pw->pw_name); fp = spw_get_file(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 (fp) {
if ((tfd = gettempfile(template)) == -1) r = spw_write_file(fp, spw, cryptpass3);
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);
if (fseek(fp, 0, SEEK_SET) == -1 || fseek(tfp, 0, SEEK_SET) == -1) {
weprintf("rewind:");
goto cleanup;
}
/* old shadow file with temporary file data. */
concat(tfp, template, fp, shadowfile);
ftruncate(tfd, ftell(tfp));
} else { } else {
/* write to /etc/passwd file. */ fp = fopen("/etc/passwd", "r+");
ffd = open("/etc/passwd", O_RDWR); if (fp)
if (ffd < 0) { r = pw_write_file(fp, pw, cryptpass3);
weprintf("open %s:", "/etc/passwd"); else
goto cleanup; weprintf("fopen:");
}
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;
} }
status = EXIT_SUCCESS; if (!r)
status = EXIT_SUCCESS;
cleanup:
if (fp) if (fp)
fclose(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);

View File

@ -1,6 +1,4 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
/* passwd.c */ /* passwd.c */
int pw_check(struct passwd *, const char *); int pw_check(const struct passwd *, const char *);
int pw_copy(int, int, const struct passwd *);
int pw_init(void); int pw_init(void);
int pw_scan(char *, struct passwd *);

View File

@ -17,7 +17,7 @@
/* Returns -1 on error, 0 for incorrect password /* Returns -1 on error, 0 for incorrect password
* and 1 if all went OK */ * and 1 if all went OK */
int int
pw_check(struct passwd *pw, const char *pass) pw_check(const struct passwd *pw, const char *pass)
{ {
char *cryptpass, *p; char *cryptpass, *p;
struct spwd *spw; struct spwd *spw;
@ -64,53 +64,6 @@ pw_check(struct passwd *pw, const char *pass)
return 1; return 1;
} }
int
pw_copy(int ffd, int tfd, const struct passwd *newpw)
{
struct passwd pw;
char *buf = NULL, *p;
size_t size = 0;
FILE *from, *to;
from = fdopen(ffd, "r");
if (!from) {
weprintf("fdopen:");
return -1;
}
to = fdopen(tfd, "w");
if (!to) {
weprintf("fdopen:");
return -1;
}
while (agetline(&buf, &size, from) != -1) {
p = strdup(buf);
if (!p) {
weprintf("strdup:");
return -1;
}
if (newpw) {
if (pw_scan(p, &pw) < 0)
return -1;
if (strcmp(pw.pw_name, newpw->pw_name) == 0) {
fprintf(to, "%s:%s:%u:%u:%s:%s:%s\n",
newpw->pw_name,
newpw->pw_passwd,
newpw->pw_uid,
newpw->pw_gid,
newpw->pw_gecos,
newpw->pw_dir,
newpw->pw_shell);
continue;
}
}
fprintf(to, "%s", buf);
free(p);
}
fflush(to);
free(buf);
return 0;
}
int int
pw_init(void) pw_init(void)
{ {
@ -122,63 +75,3 @@ pw_init(void)
eprintf("setrlimit:"); eprintf("setrlimit:");
return 0; return 0;
} }
int
pw_scan(char *bp, struct passwd *pw)
{
char *p;
memset(pw, 0, sizeof(*pw));
/* login name */
p = strsep(&bp, ":");
if (!p || *p == '\0')
goto corrupt;
pw->pw_name = p;
/* passwd */
p = strsep(&bp, ":");
if (!p)
goto corrupt;
pw->pw_passwd = p;
/* uid */
p = strsep(&bp, ":");
if (!p)
goto corrupt;
pw->pw_uid = estrtol(p, 10);
/* gid */
p = strsep(&bp, ":");
if (!p)
goto corrupt;
pw->pw_gid = estrtol(p, 10);
/* user name or comment */
p = strsep(&bp, ":");
if (!p)
goto corrupt;
pw->pw_gecos = p;
/* home directory */
p = strsep(&bp, ":");
if (!p)
goto corrupt;
pw->pw_dir = p;
/* optional shell */
p = strsep(&bp, ":");
if (!p)
goto corrupt;
pw->pw_shell = p;
/* look for redundant entries */
p = strsep(&bp, ":");
if (p)
goto corrupt;
return 0;
corrupt:
weprintf("corrupted passwd entry\n");
return -1;
}