#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <errno.h>
#include <fcntl.h>
#include <sysexits.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "stringops.h"
#define TEMP_FILE_TEMPLATE "/var/run/.pwtmpXXXXXX"
#define LOCK_FILE "/var/run/.passwd.lock"
#define _PASSWD_FILE "/etc/master.passwd"
#define _COMPAT_FILE "/etc/passwd"
#define _PASSWD_FIELDS 10
#define BUFSIZE 8192
extern void getpasswd(char *, int, int, int, int, char *, char **, char**, char **);
char *
getline(FILE *fp)
{
static char s[BUFSIZE];
int len;
s[0] = '\0';
fgets(s, BUFSIZE, fp);
if (s == NULL || s[0] == '\0') return NULL;
if (s[0] == '#') return s;
len = strlen(s) - 1;
s[len] = '\0';
return s;
}
struct passwd *
parse_user(char *line)
{
static struct passwd pw = {0};
char **tokens;
int i, len;
if (pw.pw_name != NULL) free(pw.pw_name);
pw.pw_name = NULL;
if (pw.pw_passwd != NULL) free(pw.pw_passwd);
pw.pw_passwd = NULL;
if (pw.pw_gecos != NULL) free(pw.pw_gecos);
pw.pw_gecos = NULL;
if (pw.pw_dir != NULL) free(pw.pw_dir);
pw.pw_dir = NULL;
if (pw.pw_shell != NULL) free(pw.pw_shell);
pw.pw_shell = NULL;
if (pw.pw_class != NULL) free(pw.pw_class);
pw.pw_class = NULL;
if (line == NULL) return (struct passwd *)NULL;
tokens = explode(line, ':');
len = listLength(tokens);
if (len != _PASSWD_FIELDS)
{
freeList(tokens);
return (struct passwd *)NULL;
}
i = 0;
pw.pw_name = tokens[i++];
pw.pw_passwd = tokens[i++];
pw.pw_uid = atoi(tokens[i]);
free(tokens[i++]);
pw.pw_gid = atoi(tokens[i]);
free(tokens[i++]);
pw.pw_class = tokens[i++];
pw.pw_change = atoi(tokens[i]);
free(tokens[i++]);
pw.pw_expire = atoi(tokens[i]);
free(tokens[i++]);
pw.pw_gecos = tokens[i++];
pw.pw_dir = tokens[i++];
pw.pw_shell = tokens[i++];
return &pw;
}
struct passwd *
find_user(char *uname, FILE *fp)
{
char *line;
struct passwd *pw;
rewind(fp);
while (NULL != (line = getline(fp)))
{
if (line[0] == '#') continue;
pw = parse_user(line);
if (pw == (struct passwd *)NULL) continue;
if (!strcmp(uname, pw->pw_name)) return pw;
}
pw = parse_user(NULL);
return (struct passwd *)NULL;
}
void
rewrite_file(char *pwname, FILE *fp, struct passwd *newpw, char *locn)
{
char *line;
struct passwd *pw;
FILE *tfp, *cfp;
int fd;
char fname[256];
sprintf(fname, "%s.%.5d", TEMP_FILE_TEMPLATE, getpid());
fd = mkstemps(fname, 6);
if (fd == -1)
{
fprintf(stderr, "can't create temporary file \"%s\": ", fname);
perror("");
exit(1);
}
if (fchmod(fd, (S_IRUSR | S_IWUSR)) != 0)
{
close(fd);
unlink(fname);
fprintf(stderr, "can't set permissions for temporary file \"%s\": ", fname);
perror("");
exit(1);
}
tfp = fdopen(fd, "w+");
if (tfp == NULL)
{
close(fd);
unlink(fname);
fprintf(stderr, "can't write temporary file \"%s\": ", fname);
perror("");
exit(1);
}
cfp = NULL;
if (!strcmp(pwname, _PASSWD_FILE))
{
cfp = fopen(_COMPAT_FILE, "w");
if (cfp == NULL)
{
fprintf(stderr, "warning: can't write compatability file \"%s\": ",
_COMPAT_FILE);
perror("");
}
}
if (cfp != NULL)
{
fprintf(cfp, "#\n");
fprintf(cfp, "# 4.3BSD-compatable User Database\n");
fprintf(cfp, "#\n");
fprintf(cfp, "# Note that this file is not consulted for login.\n");
fprintf(cfp, "# It only exisits for compatability with 4.3BSD utilities.\n");
fprintf(cfp, "#\n");
fprintf(cfp, "# This file is automatically re-written by various system utilities.\n");
fprintf(cfp, "# Do not edit this file. Changes will be lost.\n");
fprintf(cfp, "#\n");
}
rewind(fp);
while (NULL != (line = getline(fp)))
{
if (line[0] == '#')
{
fprintf(tfp, "%s", line);
continue;
}
pw = parse_user(line);
if (pw == (struct passwd *)NULL)
{
fprintf(stderr, "warning: bad format for entry: \"%s\"\n", line);
fprintf(tfp, "%s\n", line);
if (cfp != NULL) fprintf(cfp, "%s\n", line);
continue;
}
if (strcmp(newpw->pw_name, pw->pw_name))
{
fprintf(tfp, "%s\n", line);
if (cfp != NULL) fprintf(cfp, "%s\n", line);
continue;
}
fprintf(tfp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
newpw->pw_name, newpw->pw_passwd, newpw->pw_uid, newpw->pw_gid,
newpw->pw_class, newpw->pw_change, newpw->pw_expire,
newpw->pw_gecos, newpw->pw_dir, newpw->pw_shell);
if (cfp != NULL)
{
fprintf(cfp, "%s:",newpw->pw_name);
if ((newpw->pw_passwd == NULL) || (newpw->pw_passwd[0] == '\0'))
fprintf(cfp, ":");
else
fprintf(cfp, "*:");
fprintf(cfp, "%d:%d:%s:%s:%s\n",
newpw->pw_uid, newpw->pw_gid, newpw->pw_gecos,
newpw->pw_dir, newpw->pw_shell);
}
}
if (cfp != NULL) fclose(cfp);
fclose(fp);
rewind(tfp);
if (locn != NULL) {
if (seteuid(getuid()) != 0) {
fprintf(stderr, "Unable to set privileges.");
perror("seteuid");
exit(1);
}
}
fp = fopen(pwname, "w");
if (fp == NULL)
{
fprintf(stderr, "ERROR: can't update \"%s\"\n", pwname);
fprintf(stderr, "new passwd file is \"%s\"\n", fname);
perror("open");
exit(1);
}
while (NULL != (line = getline(tfp)))
{
fprintf(fp, "%s", line);
if (line[0] != '#') fprintf(fp, "\n");
}
fclose(fp);
fclose(tfp);
unlink(fname);
}
int
_file_passwd_main(char *uname, char *locn)
{
char *ne, *oc, *nc;
FILE *fp;
char *fname;
struct passwd *pw;
struct passwd newpw;
struct stat sb;
int uid;
uid_t euid;
fname = _PASSWD_FILE;
if (locn != NULL) fname = locn;
umask((S_IRWXG | S_IRWXO));
if ( lstat(fname, &sb) != 0 )
{
fprintf(stderr, "The file does not exist.\n");
exit(1);
}
euid = geteuid();
if (locn != NULL) {
if (seteuid(getuid()) != 0) {
fprintf(stderr, "Permission denied.\n");
exit(1);
}
}
fp = fopen(fname, "a+");
if (locn != NULL) {
seteuid(euid);
}
if (fp == NULL)
{
fprintf(stderr, "can't write to file \"%s\": ", fname);
perror("");
exit(1);
}
if (fchmod(fileno(fp), (S_IRUSR | S_IWUSR)) != 0)
{
fclose(fp);
fprintf(stderr, "can't set permissions for file \"%s\": ", fname);
perror("");
exit(1);
}
pw = find_user(uname, fp);
if (pw == (struct passwd *)NULL)
{
fprintf(stderr, "user %s not found in file %s\n", uname, fname);
exit(1);
}
uid = getuid();
if ((uid != 0) && (uid != pw->pw_uid))
{
fprintf(stderr, "Permission denied\n");
exit(1);
}
getpasswd(uname, (uid == 0), 5, 0, 0, pw->pw_passwd, &ne, &oc, &nc);
newpw.pw_name = copyString(pw->pw_name);
newpw.pw_passwd = copyString(ne);
newpw.pw_uid = pw->pw_uid;
newpw.pw_gid = pw->pw_gid;
newpw.pw_class = copyString(pw->pw_class);
newpw.pw_change = pw->pw_change;
newpw.pw_expire = pw->pw_expire;
newpw.pw_gecos = copyString(pw->pw_gecos);
newpw.pw_dir = copyString(pw->pw_dir);
newpw.pw_shell = copyString(pw->pw_shell);
rewrite_file(fname, fp, &newpw, locn);
pw = parse_user(NULL);
free(newpw.pw_name);
free(newpw.pw_passwd);
free(newpw.pw_gecos);
free(newpw.pw_dir);
free(newpw.pw_shell);
free(newpw.pw_class);
fclose(fp);
return 0;
}
void sighandler(int inSignal)
{
unlink(LOCK_FILE);
exit(1);
}
int
file_passwd(char *uname, char *locn)
{
pid_t pid;
int retVal = 0;
int waitResult = 0;
int retries = 0;
struct stat sb;
FILE *lockFile;
struct sigaction action = {{0}};
struct rlimit rlim;
rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
(void)setrlimit(RLIMIT_CPU, &rlim);
(void)setrlimit(RLIMIT_FSIZE, &rlim);
(void)setrlimit(RLIMIT_STACK, &rlim);
(void)setrlimit(RLIMIT_DATA, &rlim);
(void)setrlimit(RLIMIT_RSS, &rlim);
(void)setrlimit(RLIMIT_NOFILE, &rlim);
sigfillset( &action.sa_mask );
action.sa_flags = SA_RESTART;
action.sa_handler = sighandler;
sigaction(SIGHUP, &action, NULL);
sigaction(SIGINT, &action, NULL); sigaction(SIGQUIT, &action, NULL);
sigaction(SIGABRT, &action, NULL);
sigaction(SIGPIPE, &action, NULL);
sigaction(SIGALRM, &action, NULL);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGSTOP, &action, NULL);
sigaction(SIGTSTP, &action, NULL);
for (retries = 0; retries < 5; retries++)
{
retVal = lstat(LOCK_FILE, &sb);
if (retVal != 0)
break;
usleep(100000);
}
if (retVal == 0)
{
fprintf(stderr, "another passwd process is running.\n");
exit(EX_TEMPFAIL);
}
umask((S_IRWXG | S_IRWXO));
lockFile = fopen(LOCK_FILE, "w");
if (lockFile == NULL)
{
fprintf(stderr, "can't create lock file.\n");
exit(EX_CANTCREAT);
}
fprintf(lockFile, "%d\n", getpid());
fclose(lockFile);
pid = fork();
if (pid == -1)
{
fprintf(stderr, "can't fork\n");
exit(EX_OSERR);
}
if (pid == 0)
{
retVal = _file_passwd_main(uname, locn);
exit(retVal);
}
waitResult = waitpid(pid, &retVal, 0);
retVal = (waitResult == 0) ? WEXITSTATUS(retVal) : 1;
unlink(LOCK_FILE);
return retVal;
}