/*
* password.c
* implements safe password change non-interactive
*
* $Id: password.c,v 1.8 1999/05/29 13:17:22 eric Exp $
*/
#include <shadow.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include "password.h"
typedef enum {add, delete, modify} Action_t;
#define BUFFER_SIZE 2048
static int error_save; /* keep from clobbering errno */
long
_n_days_epoch() {
time_t curtime;
long days;
curtime = time(0);
days = (curtime)/86400;
return (days);
}
int
_file_copy(char *from, char *to, mode_t mode, mode_t final_mode) {
int fpr;
int fpw;
int n_bytes = 1;
char buffer[BUFFER_SIZE];
fpr = open(from, O_RDONLY);
if (fpr == -1) {
return(fpr);
}
fpw = open(to, O_WRONLY|O_CREAT|O_TRUNC, mode);
if (fpw == -1) {
error_save = errno;
close(fpr);
return(fpw);
}
while (n_bytes != 0) {
n_bytes = read(fpr, (void *)buffer, BUFFER_SIZE);
if (n_bytes < 0) {
error_save = errno;
close(fpr);
close(fpw);
return(n_bytes);
}
n_bytes = write(fpw, (void *)buffer, n_bytes);
if (n_bytes < 0) {
error_save = errno;
close(fpr);
close(fpw);
return(n_bytes);
}
}
close(fpr);
fchmod(fpw, final_mode);
close(fpw);
return 0;
}
int
_write_pass_shadow(Action_t action, char *login, char *pentry, char *sentry) {
int error;
int login_len;
char buffer[BUFFER_SIZE];
FILE *fp = 0;
FILE *fpr = 0;
login_len = strlen(login);
error = lckpwdf();
if (error == -1) {
syslog(LOG_ERR, "couldn't obtain passwd/shadow lock : %m");
errno = error_save;
return error;
}
error_save = 0;
/* back-up current shadow and passwd */
error = _file_copy("/etc/passwd", "/etc/opasswd_a", 0644, 0444);
if (error != 0) {
syslog(LOG_ERR, "couldn't backup passwd : %m");
errno = error_save;
goto DONE_WPS;
}
error = _file_copy("/etc/shadow", "/etc/oshadow_a", 0600, 0400);
if (error != 0) {
syslog(LOG_ERR, "couldn't backup shadow : %m");
errno = error_save;
goto DONE_WPS;
}
switch(action) {
case add: {
fp = fopen("/etc/passwd","a");
if (fp == (FILE *)0) {
error_save = errno;
syslog(LOG_ERR, "couldn't open /etc/passwd for appending : %m");
goto DONE_WPS;
}
error = fprintf(fp,"%s\n",pentry);
if (error < 0) {
error_save = errno;
syslog(LOG_ERR, "couldn't write to /etc/passwd : %m");
goto BACK_OUT;
}
fclose(fp);
fp = fopen("/etc/shadow","a");
if (fp == (FILE *)0) {
error_save = errno;
syslog(LOG_ERR, "couldn't open /etc/shadow for appending : %m");
goto BACK_OUT;
}
error = fprintf(fp,"%s\n",sentry);
if (error < 0) {
error_save = errno;
syslog(LOG_ERR, "couldn't write to /etc/shadow : %m");
goto BACK_OUT;
}
fclose(fp);
error = 0;
}; break;
case modify:
case delete: {
fpr = fopen("/etc/opasswd_a", "r");
if (fpr == (FILE *)0) {
error_save = errno;
syslog(LOG_ERR, "couldn't open /etc/opasswd_a for reading : %m");
goto DONE_WPS;
}
fp = fopen("/etc/passwd","w");
if (fp == (FILE *)0) {
error_save = errno;
syslog(LOG_ERR, "couldn't open /etc/passwd for writing : %m");
goto BACK_OUT;
}
while (!feof(fpr)) {
error = fscanf(fpr, "%[^\n]\n",buffer);
if (error != 1) {
error_save = errno;
syslog(LOG_ERR, "couldn't read /etc/opasswd_a : %m");
goto BACK_OUT;
}
if (!strncmp(login, buffer, login_len)) {
if (buffer[login_len] == ':') { /* match made in heaven :) */
if (action == modify) {
error = fprintf(fp,"%s\n",pentry);
if (error < 0) {
error_save = errno;
syslog(LOG_ERR, "couldn't write to /etc/passwd");
goto BACK_OUT;
}
} /* else remove which is no action */
} else { /* didn't match need to print what was there */
error = fprintf(fp,"%s\n",buffer);
if (error < 0) {
error_save = errno;
syslog(LOG_ERR, "couldn't write to /etc/passwd");
goto BACK_OUT;
}
}
} else {
error = fprintf(fp,"%s\n",buffer);
if (error < 0) {
error_save = errno;
syslog(LOG_ERR, "couldn't write to /etc/passwd");
goto BACK_OUT;
}
}
}
fclose(fp);
fclose(fpr);
fpr = fopen("/etc/oshadow_a", "r");
if (fpr == (FILE *)0) {
error_save = errno;
syslog(LOG_ERR, "couldn't open /etc/oshadow_a for reading : %m");
goto BACK_OUT;
}
fp = fopen("/etc/shadow","w");
if (fp == (FILE *)0) {
error_save = errno;
syslog(LOG_ERR, "couldn't open /etc/shadow for writing : %m");
goto BACK_OUT;
}
while (!feof(fpr)) {
error = fscanf(fpr, "%[^\n]\n",buffer);
if (error != 1) {
error_save = errno;
syslog(LOG_ERR, "couldn't read /etc/oshadow_a : %m");
goto BACK_OUT;
}
if (!strncmp(login, buffer, login_len)) {
if (buffer[login_len] == ':') { /* match made in heaven :) */
if (action == modify) {
error = fprintf(fp,"%s\n",sentry);
if (error < 0) {
error_save = errno;
syslog(LOG_ERR, "couldn't write to /etc/shadow");
goto BACK_OUT;
}
} /* else remove which is no action */
} else { /* not a match write it out */
error = fprintf(fp, "%s\n",buffer);
if (error < 0) {
error_save = errno;
syslog(LOG_ERR, "couldn't write to /etc/shadow");
goto BACK_OUT;
}
}
} else {
error = fprintf(fp,"%s\n",buffer);
if (error < 0) {
error_save = errno;
syslog(LOG_ERR, "couldn't write to /etc/shadow");
goto BACK_OUT;
}
}
}
}; break;
default: break;
};
error = 0;
DONE_WPS:
if (fp != (FILE *)0) {
fclose(fp);
}
if (fpr != (FILE *)0) {
fclose(fpr);
}
ulckpwdf();
errno = error_save;
return error;
BACK_OUT:
/* copy back passwd and shadow */
error = _file_copy("/etc/opasswd_a", "/etc/passwd", 0644, 0444);
if (error != 0) {
syslog(LOG_CRIT, "WHOOP! WHOOP! couldn't back out passwd : %m");
}
error = _file_copy("/etc/oshadow_a", "/etc/shadow", 0600, 0400);
if (error != 0) {
syslog(LOG_CRIT, "WHOOP! WHOOP! couldn't back out shadow : %m");
}
ulckpwdf();
errno = error_save;
return(-1);
}
int
_setpwinfo (struct passwd *p_entry, int create) {
char pentry[BUFFER_SIZE];
char sentry[BUFFER_SIZE];
struct passwd *pinfo;
Action_t action;
action = modify;
pinfo = getpwnam(p_entry->pw_name);
if (pinfo == (struct passwd *)0) {
if (create == 0) {
return 2;
} else {
action = add;
}
} else {
if (pinfo->pw_uid == 0) {
return 1;
}
}
if (p_entry->pw_uid == 0) {
return 1;
}
sprintf(pentry,"%s:%s:%d:%d:%s:%s:%s", p_entry->pw_name, "x",
p_entry->pw_uid, p_entry->pw_gid, p_entry->pw_gecos,
p_entry->pw_dir, p_entry->pw_shell);
sprintf(sentry,"%s:%s:%ld::::::", p_entry->pw_name, p_entry->pw_passwd,
_n_days_epoch());
return (_write_pass_shadow(action, p_entry->pw_name, pentry, sentry));
}
int
_rmpwnam (char *name) {
struct passwd *pinfo;
pinfo = getpwnam(name);
if (pinfo == (struct passwd *)0) {
return 2;
}
if (pinfo->pw_uid == 0) {
return 1;
}
return (_write_pass_shadow(delete, name, (char *)0, (char *)0));
}