hush/loginutils/passwd.c
Rob Landley d921b2ecc0 Remove bb_ prefixes from xfuncs.c (and a few other places), consolidate
things like xasprintf() into xfuncs.c, remove xprint_file_by_name() (it only
had one user), clean up lots of #includes...  General cleanup pass.  What I've
been doing for the last couple days.

And it conflicts!  I've removed httpd.c from this checkin due to somebody else
touching that file.  It builds for me.  I have to catch a bus.  (Now you know
why I'm looking forward to Mercurial.)
2006-08-03 15:41:12 +00:00

386 lines
8.5 KiB
C

/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
#include <syslog.h>
static char crypt_passwd[128];
static int create_backup(const char *backup, FILE * fp);
static int new_password(const struct passwd *pw, int amroot, int algo);
static void set_filesize_limit(int blocks);
static int get_algo(char *a)
{
int x = 1; /* standard: MD5 */
if (strcasecmp(a, "des") == 0)
x = 0;
return x;
}
static int update_passwd(const struct passwd *pw, const char *crypt_pw)
{
char filename[1024];
char buf[1025];
char buffer[80];
char username[32];
char *pw_rest;
int mask;
int continued;
FILE *fp;
FILE *out_fp;
struct stat sb;
struct flock lock;
#if ENABLE_FEATURE_SHADOWPASSWDS
if (access(bb_path_shadow_file, F_OK) == 0) {
snprintf(filename, sizeof filename, "%s", bb_path_shadow_file);
} else
#endif
{
snprintf(filename, sizeof filename, "%s", bb_path_passwd_file);
}
if (((fp = fopen(filename, "r+")) == 0) || (fstat(fileno(fp), &sb))) {
/* return 0; */
return 1;
}
/* Lock the password file before updating */
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
if (fcntl(fileno(fp), F_SETLK, &lock) < 0) {
fprintf(stderr, "%s: %s\n", filename, strerror(errno));
return 1;
}
lock.l_type = F_UNLCK;
snprintf(buf, sizeof buf, "%s-", filename);
if (create_backup(buf, fp)) {
fcntl(fileno(fp), F_SETLK, &lock);
fclose(fp);
return 1;
}
snprintf(buf, sizeof buf, "%s+", filename);
mask = umask(0777);
out_fp = fopen(buf, "w");
umask(mask);
if ((!out_fp) || (fchmod(fileno(out_fp), sb.st_mode & 0777))
|| (fchown(fileno(out_fp), sb.st_uid, sb.st_gid))) {
fcntl(fileno(fp), F_SETLK, &lock);
fclose(fp);
fclose(out_fp);
return 1;
}
continued = 0;
snprintf(username, sizeof username, "%s:", pw->pw_name);
rewind(fp);
while (!feof(fp)) {
fgets(buffer, sizeof buffer, fp);
if (!continued) { /* Check to see if we're updating this line. */
if (strncmp(username, buffer, strlen(username)) == 0) {
/* we have a match. */
pw_rest = strchr(buffer, ':');
*pw_rest++ = '\0';
pw_rest = strchr(pw_rest, ':');
fprintf(out_fp, "%s:%s%s", buffer, crypt_pw, pw_rest);
} else {
fputs(buffer, out_fp);
}
} else {
fputs(buffer, out_fp);
}
if (buffer[strlen(buffer) - 1] == '\n') {
continued = 0;
} else {
continued = 1;
}
memset(buffer, 0, sizeof buffer);
}
if (fflush(out_fp) || fsync(fileno(out_fp)) || fclose(out_fp)) {
unlink(buf);
fcntl(fileno(fp), F_SETLK, &lock);
fclose(fp);
return 1;
}
if (rename(buf, filename) < 0) {
fcntl(fileno(fp), F_SETLK, &lock);
fclose(fp);
return 1;
} else {
fcntl(fileno(fp), F_SETLK, &lock);
fclose(fp);
return 0;
}
}
int passwd_main(int argc, char **argv)
{
int amroot;
char *cp;
char *np;
char *name;
char *myname;
int flag;
int algo = 1; /* -a - password algorithm */
int lflg = 0; /* -l - lock account */
int uflg = 0; /* -u - unlock account */
int dflg = 0; /* -d - delete password */
const struct passwd *pw;
amroot = (getuid() == 0);
openlog("passwd", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
while ((flag = getopt(argc, argv, "a:dlu")) != EOF) {
switch (flag) {
case 'a':
algo = get_algo(optarg);
break;
case 'd':
dflg++;
break;
case 'l':
lflg++;
break;
case 'u':
uflg++;
break;
default:
bb_show_usage();
}
}
myname = (char *) xstrdup(bb_getpwuid(NULL, getuid(), -1));
/* exits on error */
if (optind < argc) {
name = argv[optind];
} else {
name = myname;
}
if ((lflg || uflg || dflg) && (optind >= argc || !amroot)) {
bb_show_usage();
}
pw = getpwnam(name);
if (!pw) {
bb_error_msg_and_die("Unknown user %s\n", name);
}
if (!amroot && pw->pw_uid != getuid()) {
syslog(LOG_WARNING, "can't change pwd for `%s'", name);
bb_error_msg_and_die("Permission denied.\n");
}
if (ENABLE_FEATURE_SHADOWPASSWDS) {
struct spwd *sp = getspnam(name);
if (!sp) bb_error_msg_and_die("Unknown user %s", name);
cp = sp->sp_pwdp;
} else cp = pw->pw_passwd;
np = name;
safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
if (!(dflg || lflg || uflg)) {
if (!amroot) {
if (cp[0] == '!') {
syslog(LOG_WARNING, "password locked for `%s'", np);
bb_error_msg_and_die( "The password for `%s' cannot be changed.\n", np);
}
}
printf("Changing password for %s\n", name);
if (new_password(pw, amroot, algo)) {
bb_error_msg_and_die( "The password for %s is unchanged.\n", name);
}
} else if (lflg) {
if (crypt_passwd[0] != '!') {
memmove(&crypt_passwd[1], crypt_passwd,
sizeof crypt_passwd - 1);
crypt_passwd[sizeof crypt_passwd - 1] = '\0';
crypt_passwd[0] = '!';
}
} else if (uflg) {
if (crypt_passwd[0] == '!') {
memmove(crypt_passwd, &crypt_passwd[1],
sizeof crypt_passwd - 1);
}
} else if (dflg) {
crypt_passwd[0] = '\0';
}
set_filesize_limit(30000);
signal(SIGHUP, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
umask(077);
xsetuid(0);
if (!update_passwd(pw, crypt_passwd)) {
syslog(LOG_INFO, "password for `%s' changed by user `%s'", name,
myname);
printf("Password changed.\n");
} else {
syslog(LOG_WARNING, "an error occurred updating the password file");
bb_error_msg_and_die("An error occurred updating the password file.\n");
}
if (ENABLE_FEATURE_CLEAN_UP) free(myname);
return (0);
}
static int create_backup(const char *backup, FILE * fp)
{
struct stat sb;
struct utimbuf ub;
FILE *bkfp;
int c, mask;
if (fstat(fileno(fp), &sb))
/* return -1; */
return 1;
mask = umask(077);
bkfp = fopen(backup, "w");
umask(mask);
if (!bkfp)
/* return -1; */
return 1;
/* TODO: faster copy, not one-char-at-a-time. --marekm */
rewind(fp);
while ((c = getc(fp)) != EOF) {
if (putc(c, bkfp) == EOF)
break;
}
if (c != EOF || fflush(bkfp)) {
fclose(bkfp);
/* return -1; */
return 1;
}
if (fclose(bkfp))
/* return -1; */
return 1;
ub.actime = sb.st_atime;
ub.modtime = sb.st_mtime;
utime(backup, &ub);
return 0;
}
static int i64c(int i)
{
if (i <= 0)
return ('.');
if (i == 1)
return ('/');
if (i >= 2 && i < 12)
return ('0' - 2 + i);
if (i >= 12 && i < 38)
return ('A' - 12 + i);
if (i >= 38 && i < 63)
return ('a' - 38 + i);
return ('z');
}
static char *crypt_make_salt(void)
{
time_t now;
static unsigned long x;
static char result[3];
time(&now);
x += now + getpid() + clock();
result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077);
result[1] = i64c(((x >> 12) ^ x) & 077);
result[2] = '\0';
return result;
}
static int new_password(const struct passwd *pw, int amroot, int algo)
{
char *clear;
char *cipher;
char *cp;
char salt[12]; /* "$N$XXXXXXXX" or "XX" */
char orig[200];
char pass[200];
if (!amroot && crypt_passwd[0]) {
if (!(clear = bb_askpass(0, "Old password:"))) {
/* return -1; */
return 1;
}
cipher = pw_encrypt(clear, crypt_passwd);
if (strcmp(cipher, crypt_passwd) != 0) {
syslog(LOG_WARNING, "incorrect password for `%s'",
pw->pw_name);
bb_do_delay(FAIL_DELAY);
fprintf(stderr, "Incorrect password.\n");
/* return -1; */
return 1;
}
safe_strncpy(orig, clear, sizeof(orig));
memset(clear, 0, strlen(clear));
memset(cipher, 0, strlen(cipher));
} else {
orig[0] = '\0';
}
if (! (cp=bb_askpass(0, "Enter the new password (minimum of 5, maximum of 8 characters)\n"
"Please use a combination of upper and lower case letters and numbers.\n"
"Enter new password: ")))
{
memset(orig, 0, sizeof orig);
/* return -1; */
return 1;
}
safe_strncpy(pass, cp, sizeof(pass));
memset(cp, 0, strlen(cp));
/* if (!obscure(orig, pass, pw)) { */
if (obscure(orig, pass, pw)) {
if (amroot) {
printf("\nWarning: weak password (continuing).\n");
} else {
/* return -1; */
return 1;
}
}
if (!(cp = bb_askpass(0, "Re-enter new password: "))) {
memset(orig, 0, sizeof orig);
/* return -1; */
return 1;
}
if (strcmp(cp, pass)) {
fprintf(stderr, "Passwords do not match.\n");
/* return -1; */
return 1;
}
memset(cp, 0, strlen(cp));
memset(orig, 0, sizeof(orig));
memset(salt, 0, sizeof(salt));
if (algo == 1) {
strcpy(salt, "$1$");
strcat(salt, crypt_make_salt());
strcat(salt, crypt_make_salt());
strcat(salt, crypt_make_salt());
}
strcat(salt, crypt_make_salt());
cp = pw_encrypt(pass, salt);
memset(pass, 0, sizeof pass);
safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
return 0;
}
static void set_filesize_limit(int blocks)
{
struct rlimit rlimit_fsize;
rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * blocks;
setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
}