#include "c2s.h"
#ifdef STORAGE_PIPE
#include <sys/wait.h>
typedef struct moddata_st {
char *exec;
pid_t child;
int in, out;
} *moddata_t;
static int _ar_pipe_write(authreg_t ar, int fd, char *msgfmt, ...)
{
va_list args;
char buf[1024];
int ret;
va_start(args, msgfmt);
vsnprintf(buf, 1024, msgfmt, args);
va_end(args);
log_debug(ZONE, "writing to pipe: %s", buf);
ret = write(fd, buf, strlen(buf));
if(ret < 0)
log_write(ar->c2s->log, LOG_ERR, "pipe: write to pipe failed: %s", strerror(errno));
return ret;
}
static int _ar_pipe_read(authreg_t ar, int fd, char *buf, int buflen)
{
int ret;
char *c;
ret = read(fd, buf, buflen);
if(ret == 0)
log_write(ar->c2s->log, LOG_ERR, "pipe: got EOF from pipe");
if(ret < 0)
log_write(ar->c2s->log, LOG_ERR, "pipe: read from pipe failed: %s", strerror(errno));
if(ret <= 0)
return ret;
buf[ret] = '\0';
c = strchr(buf, '\n');
if(c != NULL)
*c = '\0';
log_debug(ZONE, "read from pipe: %s", buf);
return ret;
}
static int _ar_pipe_user_exists(authreg_t ar, char *username, char *realm)
{
moddata_t data = (moddata_t) ar->private;
char buf[1024];
if(_ar_pipe_write(ar, data->out, "USER-EXISTS %s %s\n", username, realm) < 0)
return 0;
if(_ar_pipe_read(ar, data->in, buf, 1023) <= 0)
return 0;
if(buf[0] != 'O' || buf[1] != 'K')
return 0;
return 1;
}
static int _ar_pipe_get_password(authreg_t ar, char *username, char *realm, char password[257])
{
moddata_t data = (moddata_t) ar->private;
char buf[1024];
if(_ar_pipe_write(ar, data->out, "GET-PASSWORD %s %s\n", username, realm) < 0)
return 1;
if(_ar_pipe_read(ar, data->in, buf, 1023) <= 0)
return 1;
if(buf[0] != 'O' || buf[1] != 'K')
return 1;
if(buf[2] != ' ' || buf[3] == '\0')
{
log_debug(ZONE, "malformed response from pipe");
return 1;
}
if(ap_base64decode_len(&buf[3], -1) >= 256) {
log_debug(ZONE, "decoded password longer than buffer");
return 1;
}
ap_base64decode(password, &buf[3], -1);
log_debug(ZONE, "got password: %s", password);
return 0;
}
static int _ar_pipe_check_password(authreg_t ar, char *username, char *realm, char password[257])
{
moddata_t data = (moddata_t) ar->private;
char buf[1024];
int plen;
plen = strlen(password);
if(ap_base64encode_len(plen) >= 1023) {
log_debug(ZONE, "unable to encode password");
return 1;
}
ap_base64encode(buf, password, plen);
if(_ar_pipe_write(ar, data->out, "CHECK-PASSWORD %s %s %s\n", username, buf, realm) < 0)
return 1;
if(_ar_pipe_read(ar, data->in, buf, 1023) <= 0)
return 1;
if(buf[0] != 'O' || buf[1] != 'K')
return 1;
return 0;
}
static int _ar_pipe_set_password(authreg_t ar, char *username, char *realm, char password[257])
{
moddata_t data = (moddata_t) ar->private;
char buf[1024];
int plen;
plen = strlen(password);
if(ap_base64encode_len(plen) >= 1023) {
log_debug(ZONE, "unable to encode password");
return 1;
}
ap_base64encode(buf, password, plen);
if(_ar_pipe_write(ar, data->out, "SET-PASSWORD %s %s %s\n", username, buf, realm) < 0)
return 1;
if(_ar_pipe_read(ar, data->in, buf, 1023) <= 0)
return 1;
if(buf[0] != 'O' || buf[1] != 'K')
return 1;
return 0;
}
static int _ar_pipe_get_zerok(authreg_t ar, char *username, char *realm, char hash[41], char token[11], int *sequence)
{
moddata_t data = (moddata_t) ar->private;
char buf[1024], *tok, *c;
int i = 0;
if(_ar_pipe_write(ar, data->out, "GET-ZEROK %s %s\n", username, realm) < 0)
return 1;
if(_ar_pipe_read(ar, data->in, buf, 1023) <= 0)
return 1;
if(buf[0] != 'O' || buf[1] != 'K')
return 1;
if(buf[2] != ' ' || !((buf[3] >= '0' && buf[3] <= '9') || (buf[3] >= 'a' && buf[3] <= 'f')))
{
log_debug(ZONE, "malformed response from pipe");
return 1;
}
c = &buf[3];
while(c != NULL && i < 3)
{
tok = c;
c = strchr(c, ' ');
if(c != NULL)
{
*c = '\0';
c++;
}
switch(i)
{
case 0:
snprintf(hash, 41, "%s", tok);
break;
case 1:
snprintf(token, 11, "%s", tok);
break;
case 2:
*sequence = atoi(tok);
break;
}
i++;
}
if(i < 3)
{
log_debug(ZONE, "malformed response from pipe");
return 1;
}
log_debug(ZONE, "got zerok: hash=%s, token=%s, sequence=%d", hash, token, sequence);
return 0;
}
static int _ar_pipe_set_zerok(authreg_t ar, char *username, char *realm, char hash[41], char token[11], int sequence)
{
moddata_t data = (moddata_t) ar->private;
char buf[1024];
if(_ar_pipe_write(ar, data->out, "SET-ZEROK %s %s %s %d %s\n", username, hash, token, sequence, realm) < 0)
return 1;
if(_ar_pipe_read(ar, data->in, buf, 1023) <= 0)
return 1;
if(buf[0] != 'O' || buf[1] != 'K')
return 1;
return 0;
}
static int _ar_pipe_create_user(authreg_t ar, char *username, char *realm)
{
moddata_t data = (moddata_t) ar->private;
char buf[1024];
if(_ar_pipe_write(ar, data->out, "CREATE-USER %s %s\n", username, realm) < 0)
return 1;
if(_ar_pipe_read(ar, data->in, buf, 1023) <= 0)
return 1;
if(buf[0] != 'O' || buf[1] != 'K')
return 1;
return 0;
}
static int _ar_pipe_delete_user(authreg_t ar, char *username, char *realm)
{
moddata_t data = (moddata_t) ar->private;
char buf[1024];
if(_ar_pipe_write(ar, data->out, "DELETE-USER %s %s\n", username, realm) < 0)
return 1;
if(_ar_pipe_read(ar, data->in, buf, 1023) <= 0)
return 1;
if(buf[0] != 'O' || buf[1] != 'K')
return 1;
return 0;
}
static void _ar_pipe_free(authreg_t ar)
{
moddata_t data = (moddata_t) ar->private;
if(_ar_pipe_write(ar, data->out, "FREE\n") < 0)
return;
close(data->in);
close(data->out);
free(data);
return;
}
static void _ar_pipe_signal(int signum)
{
wait(NULL);
}
int ar_pipe_init(authreg_t ar)
{
moddata_t data;
int to[2], from[2], ret;
char buf[1024], *tok, *c;
data = (moddata_t) malloc(sizeof(struct moddata_st));
memset(data, 0, sizeof(struct moddata_st));
data->exec = config_get_one(ar->c2s->config, "authreg.pipe.exec", 0);
if(data->exec == NULL)
{
log_write(ar->c2s->log, LOG_ERR, "pipe: no executable specified in config file");
free(data);
return 1;
}
if(pipe(to) < 0)
{
log_write(ar->c2s->log, LOG_ERR, "pipe: failed to create pipe: %s", strerror(errno));
free(data);
return 1;
}
if(pipe(from) < 0)
{
log_write(ar->c2s->log, LOG_ERR, "pipe: failed to create pipe: %s", strerror(errno));
close(to[0]);
close(to[1]);
free(data);
return 1;
}
signal(SIGCHLD, _ar_pipe_signal);
log_debug(ZONE, "attempting to fork");
data->child = fork();
if(data->child < 0)
{
log_write(ar->c2s->log, LOG_ERR, "pipe: failed to fork: %s", strerror(errno));
close(to[0]);
close(to[1]);
close(from[0]);
close(from[1]);
free(data);
return 1;
}
if(data->child == 0)
{
log_debug(ZONE, "executing %s", data->exec);
close(STDIN_FILENO);
close(STDOUT_FILENO);
dup2(to[0], STDIN_FILENO);
dup2(from[1], STDOUT_FILENO);
close(to[0]);
close(to[1]);
close(from[0]);
close(from[1]);
execl(data->exec, data->exec, NULL);
log_write(ar->c2s->log, LOG_ERR, "pipe: failed to execute %s: %s", data->exec, strerror(errno));
free(data);
exit(1);
}
log_write(ar->c2s->log, LOG_NOTICE, "pipe authenticator %s running (pid %d)", data->exec, data->child);
close(to[0]);
close(from[1]);
data->in = from[0];
data->out = to[1];
ret = _ar_pipe_read(ar, data->in, buf, 1023);
if(ret <= 0)
{
close(data->in);
close(data->out);
free(data);
return 1;
}
c = buf;
while(c != NULL)
{
tok = c;
c = strchr(c, ' ');
if(c != NULL)
{
*c = '\0';
c++;
}
if(tok == buf)
{
if(strcmp(tok, "OK") == 0)
continue;
log_write(ar->c2s->log, LOG_ERR, "pipe: pipe authenticator failed to initialise");
kill(data->child, SIGTERM);
close(data->in);
close(data->out);
free(data);
return 1;
}
log_debug(ZONE, "module feature: %s", tok);
if(strcmp(tok, "USER-EXISTS") == 0)
ar->user_exists = _ar_pipe_user_exists;
else if(strcmp(tok, "GET-PASSWORD") == 0)
ar->get_password = _ar_pipe_get_password;
else if(strcmp(tok, "CHECK-PASSWORD") == 0)
ar->check_password = _ar_pipe_check_password;
else if(strcmp(tok, "SET-PASSWORD") == 0)
ar->set_password = _ar_pipe_set_password;
else if(strcmp(tok, "GET-ZEROK") == 0)
ar->get_zerok = _ar_pipe_get_zerok;
else if(strcmp(tok, "SET-ZEROK") == 0)
ar->set_zerok = _ar_pipe_set_zerok;
else if(strcmp(tok, "CREATE-USER") == 0)
ar->create_user = _ar_pipe_create_user;
else if(strcmp(tok, "DELETE-USER") == 0)
ar->delete_user = _ar_pipe_delete_user;
else if(strcmp(tok, "FREE") == 0)
ar->free = _ar_pipe_free;
}
ar->private = (void *) data;
return 0;
}
#endif