#include "saslauthd-main.h"
#ifdef USE_UNIX_IPC
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include "globals.h"
#include "utils.h"
static void do_request(int);
static void send_no(int, char *);
static int rel_accept_lock();
static int get_accept_lock();
static int sock_fd;
static int accept_fd;
static struct sockaddr_un server;
static struct sockaddr_un client;
static SALEN_TYPE len;
static char *sock_file;
static char *accept_file;
void ipc_init() {
int rc;
size_t sock_file_len;
if (num_procs == 0)
flags &= ~USE_ACCEPT_LOCK;
if (flags & USE_ACCEPT_LOCK) {
size_t accept_file_len;
accept_file_len = strlen(run_path) + sizeof(ACCEPT_LOCK_FILE) + 1;
if ((accept_file = malloc(accept_file_len)) == NULL) {
logger(L_ERR, L_FUNC, "could not allocate memory");
exit(1);
}
strlcpy(accept_file, run_path, accept_file_len);
strlcat(accept_file, ACCEPT_LOCK_FILE, accept_file_len);
if ((accept_fd = open(accept_file, O_RDWR|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR)) == -1) {
rc = errno;
logger(L_ERR, L_FUNC, "could not open accept lock file: %s", accept_file);
logger(L_ERR, L_FUNC, "open: %s", strerror(rc));
exit(1);
}
if (flags & VERBOSE)
logger(L_DEBUG, L_FUNC, "using accept lock file: %s", accept_file);
}
detach_tty();
sock_file_len = strlen(run_path) + sizeof(SOCKET_FILE) + 1;
if ((sock_file = malloc(sock_file_len)) == NULL) {
logger(L_ERR, L_FUNC, "could not allocate memory");
exit(1);
}
strlcpy(sock_file, run_path, sock_file_len);
strlcat(sock_file, SOCKET_FILE, sock_file_len);
unlink(sock_file);
memset(&server, 0, sizeof(server));
strlcpy(server.sun_path, sock_file, sizeof(server.sun_path));
server.sun_family = AF_UNIX;
if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
rc = errno;
logger(L_ERR, L_FUNC, "could not create socket");
logger(L_ERR, L_FUNC, "socket: %s", strerror(rc));
exit(1);
}
umask(0);
if (bind(sock_fd, (struct sockaddr *)&server, sizeof(server)) == -1) {
rc = errno;
logger(L_ERR, L_FUNC, "could not bind to socket: %s", sock_file);
logger(L_ERR, L_FUNC, "bind: %s", strerror(rc));
exit(1);
}
if (chmod(sock_file, S_IRWXU|S_IRWXG|S_IRWXO) == -1) {
rc = errno;
logger(L_ERR, L_FUNC, "could not chmod socket: %s", sock_file);
logger(L_ERR, L_FUNC, "chmod: %s", strerror(rc));
exit(1);
}
fchmod(sock_fd, S_IRWXU|S_IRWXG|S_IRWXO);
umask(077);
if (listen(sock_fd, SOCKET_BACKLOG) == -1) {
rc = errno;
logger(L_ERR, L_FUNC, "could not listen on socket: %s", sock_file);
logger(L_ERR, L_FUNC, "listen: %s", strerror(rc));
exit(1);
}
logger(L_INFO, L_FUNC, "listening on socket: %s", sock_file);
if (num_procs != 0)
flags |= USE_PROCESS_MODEL;
return;
}
void ipc_loop() {
int rc;
int conn_fd;
while(1) {
len = sizeof(client);
if (get_accept_lock() != 0) {
sleep(5);
continue;
}
conn_fd = accept(sock_fd, (struct sockaddr *)&client, &len);
rc = errno;
rel_accept_lock();
if (conn_fd == -1) {
if (rc != EINTR) {
logger(L_ERR, L_FUNC, "socket accept failure");
logger(L_ERR, L_FUNC, "accept: %s", strerror(rc));
sleep(5);
}
continue;
}
if (num_procs == 0) {
if(flags & DETACH_TTY) {
if (have_baby() > 0) {
close(conn_fd);
continue;
}
close(sock_fd);
}
do_request(conn_fd);
close(conn_fd);
if(flags & DETACH_TTY) {
exit(0);
} else {
continue;
}
}
do_request(conn_fd);
close(conn_fd);
}
return;
}
void ipc_cleanup() {
struct flock lock_st;
if (flags & USE_ACCEPT_LOCK) {
lock_st.l_type = F_UNLCK;
lock_st.l_start = 0;
lock_st.l_whence = SEEK_SET;
lock_st.l_len = 1;
fcntl(accept_fd, F_SETLK, &lock_st);
close(accept_fd);
unlink(accept_file);
if (flags & VERBOSE)
logger(L_DEBUG, L_FUNC, "accept lock file removed: %s", accept_file);
}
close(sock_fd);
unlink(sock_file);
if (flags & VERBOSE)
logger(L_DEBUG, L_FUNC, "socket removed: %s", sock_file);
}
void do_request(int conn_fd) {
unsigned short count;
unsigned short ncount;
char *response;
char login[MAX_REQ_LEN + 1];
char password[MAX_REQ_LEN + 1];
char service[MAX_REQ_LEN + 1];
char realm[MAX_REQ_LEN + 1];
if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
return;
count = ntohs(count);
if (count > MAX_REQ_LEN) {
logger(L_ERR, L_FUNC, "login exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
send_no(conn_fd, "");
return;
}
if (rx_rec(conn_fd, (void *)login, (size_t)count) != (ssize_t)count)
return;
login[count] = '\0';
if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
return;
count = ntohs(count);
if (count > MAX_REQ_LEN) {
logger(L_ERR, L_FUNC, "password exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
send_no(conn_fd, "");
return;
}
if (rx_rec(conn_fd, (void *)password, (size_t)count) != (ssize_t)count)
return;
password[count] = '\0';
if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
return;
count = ntohs(count);
if (count > MAX_REQ_LEN) {
logger(L_ERR, L_FUNC, "service exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
send_no(conn_fd, "");
return;
}
if (rx_rec(conn_fd, (void *)service, (size_t)count) != (ssize_t)count)
return;
service[count] = '\0';
if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count))
return;
count = ntohs(count);
if (count > MAX_REQ_LEN) {
logger(L_ERR, L_FUNC, "realm exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
send_no(conn_fd, "");
return;
}
if (rx_rec(conn_fd, (void *)realm, (size_t)count) != (ssize_t)count)
return;
realm[count] = '\0';
if (*login == '\0') {
logger(L_ERR, L_FUNC, "NULL login received");
send_no(conn_fd, "NULL login received");
return;
}
if (*password == '\0') {
logger(L_ERR, L_FUNC, "NULL password received");
send_no(conn_fd, "NULL password received");
return;
}
response = do_auth(login, password, service, realm);
memset(password, 0, strlen(password));
if (response == NULL) {
send_no(conn_fd, "NULL response from mechanism");
return;
}
count = strlen(response);
ncount = htons(count);
if (tx_rec(conn_fd, (void *)&ncount, (size_t)sizeof(ncount)) != (ssize_t)sizeof(ncount)) {
free(response);
return;
}
if (tx_rec(conn_fd, (void *)response, (size_t)count) != (ssize_t)sizeof(count)) {
free(response);
return;
}
if (flags & VERBOSE)
logger(L_DEBUG, L_FUNC, "response: %s", response);
free(response);
return;
}
void send_no(int conn_fd, char *mesg) {
char buff[1024];
unsigned short count;
unsigned short ncount;
buff[0] = 'N';
buff[1] = 'O';
buff[2] = ' ';
strncpy(buff + 3, mesg, sizeof(buff) - 1 - 3);
buff[1023] = '\0';
count = strlen(buff);
ncount = htons(count);
if (tx_rec(conn_fd, (void *)&ncount, (size_t)sizeof(ncount)) != (ssize_t)sizeof(ncount))
return;
if (tx_rec(conn_fd, (void *)buff, (size_t)count) != (ssize_t)sizeof(count))
return;
if (flags & VERBOSE)
logger(L_DEBUG, L_FUNC, "response: %s", buff);
return;
}
int get_accept_lock() {
struct flock lock_st;
int rc;
if (!(flags & USE_ACCEPT_LOCK))
return 0;
lock_st.l_type = F_WRLCK;
lock_st.l_start = 0;
lock_st.l_whence = SEEK_SET;
lock_st.l_len = 1;
errno = 0;
do {
rc = fcntl(accept_fd, F_SETLKW, &lock_st);
} while (rc != 0 && errno == EINTR);
if (rc != 0) {
rc = errno;
logger(L_ERR, L_FUNC, "could not acquire accept lock");
logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc));
return -1;
}
if (flags & VERBOSE)
logger(L_DEBUG, L_FUNC, "acquired accept lock");
return 0;
}
int rel_accept_lock() {
struct flock lock_st;
int rc;
if (!(flags & USE_ACCEPT_LOCK))
return 0;
lock_st.l_type = F_UNLCK;
lock_st.l_start = 0;
lock_st.l_whence = SEEK_SET;
lock_st.l_len = 1;
errno = 0;
do {
rc = fcntl(accept_fd, F_SETLKW, &lock_st);
} while (rc != 0 && errno == EINTR);
if (rc != 0) {
rc = errno;
logger(L_ERR, L_FUNC, "could not release accept lock");
logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc));
return -1;
}
if (flags & VERBOSE)
logger(L_DEBUG, L_FUNC, "released accept lock");
return 0;
}
#endif