whitelist: use a stream service to accept client connections
Use SOCK_STREAM, as we don't have SOCK_SEQPACKET on TCP. To have network transparency, the message now uses network byte order.
This commit is contained in:
@@ -18,45 +18,102 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
/**
|
||||
* Connect to the daemon, return FD
|
||||
*/
|
||||
static int make_connection()
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int fd;
|
||||
union {
|
||||
struct sockaddr_un un;
|
||||
struct sockaddr_in in;
|
||||
struct sockaddr sa;
|
||||
} addr;
|
||||
int fd, len;
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, WHITELIST_SOCKET);
|
||||
if (getenv("TCP_PORT"))
|
||||
{
|
||||
addr.in.sin_family = AF_INET;
|
||||
addr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
addr.in.sin_port = htons(atoi(getenv("TCP_PORT")));
|
||||
len = sizeof(addr.in);
|
||||
}
|
||||
else
|
||||
{
|
||||
addr.un.sun_family = AF_UNIX;
|
||||
strcpy(addr.un.sun_path, WHITELIST_SOCKET);
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||
len = offsetof(struct sockaddr_un, sun_path) + strlen(addr.un.sun_path);
|
||||
}
|
||||
fd = socket(addr.sa.sa_family, SOCK_STREAM, 0);
|
||||
if (fd < 0)
|
||||
{
|
||||
fprintf(stderr, "opening socket failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (connect(fd, (struct sockaddr *)&addr,
|
||||
offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path)) < 0)
|
||||
if (connect(fd, &addr.sa, len) < 0)
|
||||
{
|
||||
fprintf(stderr, "connecting to %s failed: %s\n",
|
||||
WHITELIST_SOCKET, strerror(errno));
|
||||
fprintf(stderr, "connecting failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int read_all(int fd, void *buf, size_t len)
|
||||
{
|
||||
ssize_t ret, done = 0;
|
||||
|
||||
while (done < len)
|
||||
{
|
||||
ret = read(fd, buf, len - done);
|
||||
if (ret == -1 && errno == EINTR)
|
||||
{ /* interrupted, try again */
|
||||
continue;
|
||||
}
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
done += ret;
|
||||
buf += ret;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int write_all(int fd, void *buf, size_t len)
|
||||
{
|
||||
ssize_t ret, done = 0;
|
||||
|
||||
while (done < len)
|
||||
{
|
||||
ret = write(fd, buf, len - done);
|
||||
if (ret == -1 && errno == EINTR)
|
||||
{ /* interrupted, try again */
|
||||
continue;
|
||||
}
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
done += ret;
|
||||
buf += ret;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a single message
|
||||
*/
|
||||
static int send_msg(int type, char *id)
|
||||
{
|
||||
whitelist_msg_t msg = {
|
||||
.type = type,
|
||||
.type = htonl(type),
|
||||
};
|
||||
int fd;
|
||||
|
||||
@@ -66,7 +123,7 @@ static int send_msg(int type, char *id)
|
||||
return 2;
|
||||
}
|
||||
snprintf(msg.id, sizeof(msg.id), "%s", id);
|
||||
if (send(fd, &msg, sizeof(msg), 0) != sizeof(msg))
|
||||
if (write_all(fd, &msg, sizeof(msg)) != sizeof(msg))
|
||||
{
|
||||
fprintf(stderr, "writing to socket failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
@@ -74,9 +131,15 @@ static int send_msg(int type, char *id)
|
||||
}
|
||||
if (type == WHITELIST_LIST)
|
||||
{
|
||||
while (recv(fd, &msg, sizeof(msg), 0) == sizeof(msg))
|
||||
while (1)
|
||||
{
|
||||
if (msg.type != WHITELIST_LIST)
|
||||
if (read_all(fd, &msg, sizeof(msg)) != sizeof(msg))
|
||||
{
|
||||
fprintf(stderr, "reading failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
return 2;
|
||||
}
|
||||
if (ntohl(msg.type) != WHITELIST_LIST)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -94,7 +157,7 @@ static int send_msg(int type, char *id)
|
||||
static int send_batch(int type, char *file)
|
||||
{
|
||||
whitelist_msg_t msg = {
|
||||
.type = type,
|
||||
.type = htonl(type),
|
||||
};
|
||||
FILE *f = stdin;
|
||||
int fd, len;
|
||||
@@ -125,7 +188,7 @@ static int send_batch(int type, char *file)
|
||||
{
|
||||
msg.id[len-1] = '\0';
|
||||
}
|
||||
if (send(fd, &msg, sizeof(msg), 0) != sizeof(msg))
|
||||
if (write_all(fd, &msg, sizeof(msg)) != sizeof(msg))
|
||||
{
|
||||
fprintf(stderr, "writing to socket failed: %s\n", strerror(errno));
|
||||
if (f != stdin)
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include <daemon.h>
|
||||
#include <threading/thread.h>
|
||||
#include <processing/jobs/callback_job.h>
|
||||
|
||||
#include "whitelist_msg.h"
|
||||
|
||||
@@ -46,65 +44,28 @@ struct private_whitelist_control_t {
|
||||
whitelist_listener_t *listener;
|
||||
|
||||
/**
|
||||
* Whitelist unix socket file descriptor
|
||||
* Whitelist stream service
|
||||
*/
|
||||
int socket;
|
||||
stream_service_t *service;
|
||||
};
|
||||
|
||||
/**
|
||||
* Open whitelist unix socket
|
||||
*/
|
||||
static bool open_socket(private_whitelist_control_t *this)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
mode_t old;
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, WHITELIST_SOCKET);
|
||||
|
||||
this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||
if (this->socket == -1)
|
||||
{
|
||||
DBG1(DBG_CFG, "creating whitelist socket failed");
|
||||
return FALSE;
|
||||
}
|
||||
unlink(addr.sun_path);
|
||||
old = umask(~(S_IRWXU | S_IRWXG));
|
||||
if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
DBG1(DBG_CFG, "binding whitelist socket failed: %s", strerror(errno));
|
||||
close(this->socket);
|
||||
return FALSE;
|
||||
}
|
||||
umask(old);
|
||||
if (chown(addr.sun_path, lib->caps->get_uid(lib->caps),
|
||||
lib->caps->get_gid(lib->caps)) != 0)
|
||||
{
|
||||
DBG1(DBG_CFG, "changing whitelist socket permissions failed: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
if (listen(this->socket, 10) < 0)
|
||||
{
|
||||
DBG1(DBG_CFG, "listening on whitelist socket failed: %s", strerror(errno));
|
||||
close(this->socket);
|
||||
unlink(addr.sun_path);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a received message
|
||||
*/
|
||||
static void dispatch(private_whitelist_control_t *this,
|
||||
int fd, whitelist_msg_t *msg)
|
||||
static bool on_accept(private_whitelist_control_t *this, stream_t *stream)
|
||||
{
|
||||
identification_t *id, *current;
|
||||
enumerator_t *enumerator;
|
||||
whitelist_msg_t msg;
|
||||
|
||||
msg->id[sizeof(msg->id)-1] = 0;
|
||||
id = identification_create_from_string(msg->id);
|
||||
switch (msg->type)
|
||||
if (!stream->read_all(stream, &msg, sizeof(msg)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
msg.id[sizeof(msg.id) - 1] = 0;
|
||||
id = identification_create_from_string(msg.id);
|
||||
switch (ntohl(msg.type))
|
||||
{
|
||||
case WHITELIST_ADD:
|
||||
this->listener->add(this->listener, id);
|
||||
@@ -118,8 +79,8 @@ static void dispatch(private_whitelist_control_t *this,
|
||||
{
|
||||
if (current->matches(current, id))
|
||||
{
|
||||
snprintf(msg->id, sizeof(msg->id), "%Y", current);
|
||||
if (send(fd, msg, sizeof(*msg), 0) != sizeof(*msg))
|
||||
snprintf(msg.id, sizeof(msg.id), "%Y", current);
|
||||
if (!stream->write_all(stream, &msg, sizeof(msg)))
|
||||
{
|
||||
DBG1(DBG_CFG, "listing whitelist failed");
|
||||
break;
|
||||
@@ -127,9 +88,9 @@ static void dispatch(private_whitelist_control_t *this,
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
msg->type = WHITELIST_END;
|
||||
memset(msg->id, 0, sizeof(msg->id));
|
||||
send(fd, msg, sizeof(*msg), 0);
|
||||
msg.type = htonl(WHITELIST_END);
|
||||
memset(msg.id, 0, sizeof(msg.id));
|
||||
stream->write_all(stream, &msg, sizeof(msg));
|
||||
break;
|
||||
case WHITELIST_FLUSH:
|
||||
this->listener->flush(this->listener, id);
|
||||
@@ -145,58 +106,14 @@ static void dispatch(private_whitelist_control_t *this,
|
||||
break;
|
||||
}
|
||||
id->destroy(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept whitelist control connections, dispatch
|
||||
*/
|
||||
static job_requeue_t receive(private_whitelist_control_t *this)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int fd, len = sizeof(addr);
|
||||
whitelist_msg_t msg;
|
||||
bool oldstate;
|
||||
|
||||
oldstate = thread_cancelability(TRUE);
|
||||
fd = accept(this->socket, (struct sockaddr*)&addr, &len);
|
||||
thread_cancelability(oldstate);
|
||||
|
||||
if (fd != -1)
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
oldstate = thread_cancelability(TRUE);
|
||||
len = recv(fd, &msg, sizeof(msg), 0);
|
||||
thread_cancelability(oldstate);
|
||||
|
||||
if (len == sizeof(msg))
|
||||
{
|
||||
dispatch(this, fd, &msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (len != 0)
|
||||
{
|
||||
DBG1(DBG_CFG, "receiving whitelist msg failed: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_CFG, "accepting whitelist connection failed: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
return JOB_REQUEUE_FAIR;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(whitelist_control_t, destroy, void,
|
||||
private_whitelist_control_t *this)
|
||||
{
|
||||
close(this->socket);
|
||||
this->service->destroy(this->service);
|
||||
free(this);
|
||||
}
|
||||
|
||||
@@ -206,6 +123,7 @@ METHOD(whitelist_control_t, destroy, void,
|
||||
whitelist_control_t *whitelist_control_create(whitelist_listener_t *listener)
|
||||
{
|
||||
private_whitelist_control_t *this;
|
||||
char *uri;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
@@ -214,15 +132,19 @@ whitelist_control_t *whitelist_control_create(whitelist_listener_t *listener)
|
||||
.listener = listener,
|
||||
);
|
||||
|
||||
if (!open_socket(this))
|
||||
uri = lib->settings->get_str(lib->settings,
|
||||
"%s.plugins.whitelist.socket", "unix://" WHITELIST_SOCKET,
|
||||
charon->name);
|
||||
this->service = lib->streams->create_service(lib->streams, uri, 10);
|
||||
if (!this->service)
|
||||
{
|
||||
DBG1(DBG_CFG, "creating whitelist socket failed");
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lib->processor->queue_job(lib->processor,
|
||||
(job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this,
|
||||
NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
|
||||
this->service->on_accept(this->service, (stream_service_cb_t)on_accept,
|
||||
this, JOB_PRIO_CRITICAL, 0);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,6 @@ struct whitelist_msg_t {
|
||||
int type;
|
||||
/** null terminated identity */
|
||||
char id[128];
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /** WHITELIST_MSG_H_ @}*/
|
||||
|
||||
Reference in New Issue
Block a user