mirror of git://git.suckless.org/sbase
passwd: improvements
- add shadow support. - allow passwd without argument, prompt which user password is changed.
This commit is contained in:
parent
a27035c281
commit
f48d545c77
1
Makefile
1
Makefile
|
@ -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
2
TODO
|
@ -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).
|
||||||
|
|
4
passwd.1
4
passwd.1
|
@ -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.
|
|
||||||
|
|
181
passwd.c
181
passwd.c
|
@ -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;
|
||||||
|
if (argc == 0)
|
||||||
|
pw = getpwuid(getuid());
|
||||||
|
else
|
||||||
pw = getpwnam(argv[0]);
|
pw = getpwnam(argv[0]);
|
||||||
|
if (!pw) {
|
||||||
if (errno)
|
if (errno)
|
||||||
eprintf("getpwnam: %s:", argv[0]);
|
eprintf("getpwnam: %s:", argv[0]);
|
||||||
else if (!pw)
|
else
|
||||||
eprintf("who are you?\n");
|
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");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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 {
|
||||||
|
/* 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;
|
pw->pw_passwd = cryptpass3;
|
||||||
|
|
||||||
ffd = open("/etc/passwd", O_RDWR);
|
if ((tfd = gettempfile(template)) == -1)
|
||||||
if (ffd < 0)
|
goto cleanup;
|
||||||
eprintf("open %s:", "/etc/passwd");
|
|
||||||
|
|
||||||
tfd = mkostemp(template, O_RDWR);
|
|
||||||
if (tfd < 0)
|
|
||||||
eprintf("mkstemp:");
|
|
||||||
|
|
||||||
r = pw_copy(ffd, tfd, pw);
|
r = pw_copy(ffd, tfd, pw);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
unlink(template);
|
goto cleanup;
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = lseek(ffd, 0, SEEK_SET);
|
r = lseek(ffd, 0, SEEK_SET);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
unlink(template);
|
goto cleanup;
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
r = lseek(tfd, 0, SEEK_SET);
|
r = lseek(tfd, 0, SEEK_SET);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
unlink(template);
|
goto cleanup;
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = pw_copy(tfd, ffd, NULL);
|
r = pw_copy(tfd, ffd, NULL);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
unlink(template);
|
goto cleanup;
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(tfd);
|
|
||||||
close(ffd);
|
close(ffd);
|
||||||
|
}
|
||||||
|
status = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (fp)
|
||||||
|
fclose(fp);
|
||||||
|
if (tfp)
|
||||||
|
fclose(tfp);
|
||||||
|
if (tfd != -1) {
|
||||||
|
close(tfd);
|
||||||
unlink(template);
|
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
2
text.h
|
@ -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 *);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue