#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
#include <syslog.h>
#if !defined(_PATH_PWCHECKPID)
#ifdef _PATH_VARRUN
# define _PATH_PWCHECKPID (_PATH_VARRUN "pwcheck.pid")
#else
# define _PATH_PWCHECKPID (NULL)
#endif
#endif
void newclient(int);
int retry_write(int, const char *, unsigned int);
int
main()
{
char fnamebuf[MAXPATHLEN];
int s;
int c;
int count;
int rc;
struct sockaddr_un srvaddr;
struct sockaddr_un clientaddr;
int r;
int len;
mode_t oldumask;
char *pid_file = _PATH_PWCHECKPID;
FILE *fp = NULL;
pid_t pid;
openlog("pwcheck", LOG_NDELAY, LOG_AUTH);
count = 5;
while (count--) {
pid = fork();
if (pid > 0)
_exit(0);
if ((pid == -1) && (errno == EAGAIN)) {
syslog(LOG_WARNING, "master fork failed (sleeping): %m");
sleep(5);
continue;
}
}
if (pid == -1) {
rc = errno;
syslog(LOG_ERR, "FATAL: master fork failed: %m");
fprintf(stderr, "pwcheck: ");
errno = rc;
perror("fork");
exit(1);
}
if (setsid() == -1) {
rc = errno;
syslog(LOG_ERR, "FATAL: setsid: %m");
fprintf(stderr, "pwcheck: ");
errno = rc;
perror("setsid");
exit(1);
}
s = open("/dev/null", O_RDWR, 0);
if (s == -1) {
rc = errno;
syslog(LOG_ERR, "FATAL: /dev/null: %m");
fprintf(stderr, "pwcheck: ");
errno = rc;
perror("/dev/null");
exit(1);
}
dup2(s, fileno(stdin));
dup2(s, fileno(stdout));
dup2(s, fileno(stderr));
if (s > 2) {
close(s);
}
pid = getpid();
if (pid_file) {
fp = fopen(pid_file, "w");
}
if (fp) {
fprintf(fp, "%ld\n", (long)pid);
fclose(fp);
} else if (pid_file) {
syslog(LOG_WARNING, "%s: %m", pid_file);
}
s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s == -1) {
perror("socket");
exit(1);
}
strncpy(fnamebuf, PWCHECKDIR, sizeof(fnamebuf));
strncpy(fnamebuf + sizeof(PWCHECKDIR)-1, "/pwcheck",
sizeof(fnamebuf) - sizeof(PWCHECKDIR));
fnamebuf[MAXPATHLEN-1] = '\0';
(void) unlink(fnamebuf);
memset((char *)&srvaddr, 0, sizeof(srvaddr));
srvaddr.sun_family = AF_UNIX;
strncpy(srvaddr.sun_path, fnamebuf, sizeof(srvaddr.sun_path));
oldumask = umask((mode_t) 0);
r = bind(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
if (r == -1) {
syslog(LOG_ERR, "%.*s: %m",
sizeof(srvaddr.sun_path), srvaddr.sun_path);
exit(1);
}
umask(oldumask);
chmod(fnamebuf, (mode_t) 0777);
r = listen(s, 5);
if (r == -1) {
syslog(LOG_ERR, "listen: %m");
exit(1);
}
for (;;) {
len = sizeof(clientaddr);
c = accept(s, (struct sockaddr *)&clientaddr, &len);
if (c == -1 && errno != EINTR) {
syslog(LOG_WARNING, "accept: %m");
continue;
}
newclient(c);
}
}
void newclient(int c)
{
char request[1024];
int n;
unsigned int start;
char *reply;
extern char *pwcheck();
start = 0;
while (start < sizeof(request) - 1) {
n = read(c, request+start, sizeof(request) - 1 - start);
if (n < 1) {
reply = "Error reading request";
goto sendreply;
}
start += n;
if (request[start-1] == '\0' && strlen(request) < start) {
break;
}
}
if (start >= sizeof(request) - 1) {
reply = "Request too big";
}
else {
reply = pwcheck(request, request + strlen(request) + 1);
}
sendreply:
retry_write(c, reply, strlen(reply));
close(c);
}
int retry_write(int fd, const char *buf, unsigned int nbyte)
{
int n;
int written = 0;
if (nbyte == 0)
return 0;
for (;;) {
n = write(fd, buf, nbyte);
if (n == -1) {
if (errno == EINTR)
continue;
return -1;
}
written += n;
if ((unsigned int) n >= nbyte)
return written;
buf += n;
nbyte -= n;
}
}