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/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to the daemon, return FD
|
* Connect to the daemon, return FD
|
||||||
*/
|
*/
|
||||||
static int make_connection()
|
static int make_connection()
|
||||||
{
|
{
|
||||||
struct sockaddr_un addr;
|
union {
|
||||||
int fd;
|
struct sockaddr_un un;
|
||||||
|
struct sockaddr_in in;
|
||||||
|
struct sockaddr sa;
|
||||||
|
} addr;
|
||||||
|
int fd, len;
|
||||||
|
|
||||||
addr.sun_family = AF_UNIX;
|
if (getenv("TCP_PORT"))
|
||||||
strcpy(addr.sun_path, WHITELIST_SOCKET);
|
{
|
||||||
|
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)
|
if (fd < 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "opening socket failed: %s\n", strerror(errno));
|
fprintf(stderr, "opening socket failed: %s\n", strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (connect(fd, (struct sockaddr *)&addr,
|
if (connect(fd, &addr.sa, len) < 0)
|
||||||
offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path)) < 0)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "connecting to %s failed: %s\n",
|
fprintf(stderr, "connecting failed: %s\n", strerror(errno));
|
||||||
WHITELIST_SOCKET, strerror(errno));
|
|
||||||
close(fd);
|
close(fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return fd;
|
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
|
* Send a single message
|
||||||
*/
|
*/
|
||||||
static int send_msg(int type, char *id)
|
static int send_msg(int type, char *id)
|
||||||
{
|
{
|
||||||
whitelist_msg_t msg = {
|
whitelist_msg_t msg = {
|
||||||
.type = type,
|
.type = htonl(type),
|
||||||
};
|
};
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
@@ -66,7 +123,7 @@ static int send_msg(int type, char *id)
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
snprintf(msg.id, sizeof(msg.id), "%s", id);
|
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));
|
fprintf(stderr, "writing to socket failed: %s\n", strerror(errno));
|
||||||
close(fd);
|
close(fd);
|
||||||
@@ -74,9 +131,15 @@ static int send_msg(int type, char *id)
|
|||||||
}
|
}
|
||||||
if (type == WHITELIST_LIST)
|
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;
|
break;
|
||||||
}
|
}
|
||||||
@@ -94,7 +157,7 @@ static int send_msg(int type, char *id)
|
|||||||
static int send_batch(int type, char *file)
|
static int send_batch(int type, char *file)
|
||||||
{
|
{
|
||||||
whitelist_msg_t msg = {
|
whitelist_msg_t msg = {
|
||||||
.type = type,
|
.type = htonl(type),
|
||||||
};
|
};
|
||||||
FILE *f = stdin;
|
FILE *f = stdin;
|
||||||
int fd, len;
|
int fd, len;
|
||||||
@@ -125,7 +188,7 @@ static int send_batch(int type, char *file)
|
|||||||
{
|
{
|
||||||
msg.id[len-1] = '\0';
|
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));
|
fprintf(stderr, "writing to socket failed: %s\n", strerror(errno));
|
||||||
if (f != stdin)
|
if (f != stdin)
|
||||||
|
|||||||
@@ -23,8 +23,6 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <daemon.h>
|
#include <daemon.h>
|
||||||
#include <threading/thread.h>
|
|
||||||
#include <processing/jobs/callback_job.h>
|
|
||||||
|
|
||||||
#include "whitelist_msg.h"
|
#include "whitelist_msg.h"
|
||||||
|
|
||||||
@@ -46,65 +44,28 @@ struct private_whitelist_control_t {
|
|||||||
whitelist_listener_t *listener;
|
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
|
* Dispatch a received message
|
||||||
*/
|
*/
|
||||||
static void dispatch(private_whitelist_control_t *this,
|
static bool on_accept(private_whitelist_control_t *this, stream_t *stream)
|
||||||
int fd, whitelist_msg_t *msg)
|
|
||||||
{
|
{
|
||||||
identification_t *id, *current;
|
identification_t *id, *current;
|
||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
|
whitelist_msg_t msg;
|
||||||
|
|
||||||
msg->id[sizeof(msg->id)-1] = 0;
|
if (!stream->read_all(stream, &msg, sizeof(msg)))
|
||||||
id = identification_create_from_string(msg->id);
|
{
|
||||||
switch (msg->type)
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.id[sizeof(msg.id) - 1] = 0;
|
||||||
|
id = identification_create_from_string(msg.id);
|
||||||
|
switch (ntohl(msg.type))
|
||||||
{
|
{
|
||||||
case WHITELIST_ADD:
|
case WHITELIST_ADD:
|
||||||
this->listener->add(this->listener, id);
|
this->listener->add(this->listener, id);
|
||||||
@@ -118,8 +79,8 @@ static void dispatch(private_whitelist_control_t *this,
|
|||||||
{
|
{
|
||||||
if (current->matches(current, id))
|
if (current->matches(current, id))
|
||||||
{
|
{
|
||||||
snprintf(msg->id, sizeof(msg->id), "%Y", current);
|
snprintf(msg.id, sizeof(msg.id), "%Y", current);
|
||||||
if (send(fd, msg, sizeof(*msg), 0) != sizeof(*msg))
|
if (!stream->write_all(stream, &msg, sizeof(msg)))
|
||||||
{
|
{
|
||||||
DBG1(DBG_CFG, "listing whitelist failed");
|
DBG1(DBG_CFG, "listing whitelist failed");
|
||||||
break;
|
break;
|
||||||
@@ -127,9 +88,9 @@ static void dispatch(private_whitelist_control_t *this,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
msg->type = WHITELIST_END;
|
msg.type = htonl(WHITELIST_END);
|
||||||
memset(msg->id, 0, sizeof(msg->id));
|
memset(msg.id, 0, sizeof(msg.id));
|
||||||
send(fd, msg, sizeof(*msg), 0);
|
stream->write_all(stream, &msg, sizeof(msg));
|
||||||
break;
|
break;
|
||||||
case WHITELIST_FLUSH:
|
case WHITELIST_FLUSH:
|
||||||
this->listener->flush(this->listener, id);
|
this->listener->flush(this->listener, id);
|
||||||
@@ -145,58 +106,14 @@ static void dispatch(private_whitelist_control_t *this,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
id->destroy(id);
|
id->destroy(id);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return FALSE;
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(whitelist_control_t, destroy, void,
|
METHOD(whitelist_control_t, destroy, void,
|
||||||
private_whitelist_control_t *this)
|
private_whitelist_control_t *this)
|
||||||
{
|
{
|
||||||
close(this->socket);
|
this->service->destroy(this->service);
|
||||||
free(this);
|
free(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,6 +123,7 @@ METHOD(whitelist_control_t, destroy, void,
|
|||||||
whitelist_control_t *whitelist_control_create(whitelist_listener_t *listener)
|
whitelist_control_t *whitelist_control_create(whitelist_listener_t *listener)
|
||||||
{
|
{
|
||||||
private_whitelist_control_t *this;
|
private_whitelist_control_t *this;
|
||||||
|
char *uri;
|
||||||
|
|
||||||
INIT(this,
|
INIT(this,
|
||||||
.public = {
|
.public = {
|
||||||
@@ -214,15 +132,19 @@ whitelist_control_t *whitelist_control_create(whitelist_listener_t *listener)
|
|||||||
.listener = 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);
|
free(this);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
lib->processor->queue_job(lib->processor,
|
this->service->on_accept(this->service, (stream_service_cb_t)on_accept,
|
||||||
(job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this,
|
this, JOB_PRIO_CRITICAL, 0);
|
||||||
NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
|
|
||||||
|
|
||||||
return &this->public;
|
return &this->public;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,6 @@ struct whitelist_msg_t {
|
|||||||
int type;
|
int type;
|
||||||
/** null terminated identity */
|
/** null terminated identity */
|
||||||
char id[128];
|
char id[128];
|
||||||
};
|
} __attribute__((packed));
|
||||||
|
|
||||||
#endif /** WHITELIST_MSG_H_ @}*/
|
#endif /** WHITELIST_MSG_H_ @}*/
|
||||||
|
|||||||
Reference in New Issue
Block a user