socket: prepare for proper selinux labelling of sockets
This commit is contained in:
parent
0009d2a633
commit
b15bdda870
|
@ -305,6 +305,7 @@ int socket_address_listen(
|
||||||
bool free_bind,
|
bool free_bind,
|
||||||
mode_t directory_mode,
|
mode_t directory_mode,
|
||||||
mode_t socket_mode,
|
mode_t socket_mode,
|
||||||
|
/* FIXME SELINUX: pass SELinux context object here */
|
||||||
int *ret) {
|
int *ret) {
|
||||||
|
|
||||||
int r, fd, one;
|
int r, fd, one;
|
||||||
|
@ -314,6 +315,9 @@ int socket_address_listen(
|
||||||
if ((r = socket_address_verify(a)) < 0)
|
if ((r = socket_address_verify(a)) < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
/* FIXME SELINUX: The socket() here should be done with the
|
||||||
|
* right SELinux context set */
|
||||||
|
|
||||||
if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0)
|
if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,7 @@ int socket_address_listen(
|
||||||
bool free_bind,
|
bool free_bind,
|
||||||
mode_t directory_mode,
|
mode_t directory_mode,
|
||||||
mode_t socket_mode,
|
mode_t socket_mode,
|
||||||
|
/* FIXME SELINUX: pass SELinux context object here */
|
||||||
int *ret);
|
int *ret);
|
||||||
|
|
||||||
bool socket_address_is(const SocketAddress *a, const char *s, int type);
|
bool socket_address_is(const SocketAddress *a, const char *s, int type);
|
||||||
|
|
188
src/socket.c
188
src/socket.c
|
@ -129,6 +129,42 @@ static void socket_done(Unit *u) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int socket_instantiate_service(Socket *s) {
|
||||||
|
char *prefix, *name;
|
||||||
|
int r;
|
||||||
|
Unit *u;
|
||||||
|
|
||||||
|
assert(s);
|
||||||
|
|
||||||
|
/* This fills in s->service if it isn't filled in yet. For
|
||||||
|
* Accept=yes sockets we create the next connection service
|
||||||
|
* here. For Accept=no this is mostly a NOP since the service
|
||||||
|
* is figured out at load time anyway. */
|
||||||
|
|
||||||
|
if (s->service)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
assert(s->accept);
|
||||||
|
|
||||||
|
if (!(prefix = unit_name_to_prefix(s->meta.id)))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = asprintf(&name, "%s@%u.service", prefix, s->n_accepted);
|
||||||
|
free(prefix);
|
||||||
|
|
||||||
|
if (r < 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = manager_load_unit(s->meta.manager, name, NULL, NULL, &u);
|
||||||
|
free(name);
|
||||||
|
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
s->service = SERVICE(u);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static bool have_non_accept_socket(Socket *s) {
|
static bool have_non_accept_socket(Socket *s) {
|
||||||
SocketPort *p;
|
SocketPort *p;
|
||||||
|
|
||||||
|
@ -460,8 +496,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
|
||||||
b = ntohl(remote.in.sin_addr.s_addr);
|
b = ntohl(remote.in.sin_addr.s_addr);
|
||||||
|
|
||||||
if (asprintf(&r,
|
if (asprintf(&r,
|
||||||
"%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
|
"%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
|
||||||
nr,
|
|
||||||
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
|
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
|
||||||
ntohs(local.in.sin_port),
|
ntohs(local.in.sin_port),
|
||||||
b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF,
|
b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF,
|
||||||
|
@ -483,8 +518,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
|
||||||
*b = remote.in6.sin6_addr.s6_addr+12;
|
*b = remote.in6.sin6_addr.s6_addr+12;
|
||||||
|
|
||||||
if (asprintf(&r,
|
if (asprintf(&r,
|
||||||
"%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
|
"%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
|
||||||
nr,
|
|
||||||
a[0], a[1], a[2], a[3],
|
a[0], a[1], a[2], a[3],
|
||||||
ntohs(local.in6.sin6_port),
|
ntohs(local.in6.sin6_port),
|
||||||
b[0], b[1], b[2], b[3],
|
b[0], b[1], b[2], b[3],
|
||||||
|
@ -494,8 +528,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
|
||||||
char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN];
|
char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
if (asprintf(&r,
|
if (asprintf(&r,
|
||||||
"%u-%s:%u-%s:%u",
|
"%s:%u-%s:%u",
|
||||||
nr,
|
|
||||||
inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)),
|
inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)),
|
||||||
ntohs(local.in6.sin6_port),
|
ntohs(local.in6.sin6_port),
|
||||||
inet_ntop(AF_INET6, &remote.in6.sin6_addr, b, sizeof(b)),
|
inet_ntop(AF_INET6, &remote.in6.sin6_addr, b, sizeof(b)),
|
||||||
|
@ -600,7 +633,7 @@ static void socket_apply_socket_options(Socket *s, int fd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void socket_apply_pipe_options(Socket *s, int fd) {
|
static void socket_apply_fifo_options(Socket *s, int fd) {
|
||||||
assert(s);
|
assert(s);
|
||||||
assert(fd >= 0);
|
assert(fd >= 0);
|
||||||
|
|
||||||
|
@ -609,12 +642,90 @@ static void socket_apply_pipe_options(Socket *s, int fd) {
|
||||||
log_warning("F_SETPIPE_SZ: %m");
|
log_warning("F_SETPIPE_SZ: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fifo_address_create(
|
||||||
|
const char *path,
|
||||||
|
mode_t directory_mode,
|
||||||
|
mode_t socket_mode,
|
||||||
|
/* FIXME SELINUX: pass SELinux context object here */
|
||||||
|
int *_fd) {
|
||||||
|
|
||||||
|
int fd = -1, r;
|
||||||
|
struct stat st;
|
||||||
|
mode_t old_mask;
|
||||||
|
|
||||||
|
assert(path);
|
||||||
|
assert(_fd);
|
||||||
|
|
||||||
|
mkdir_parents(path, directory_mode);
|
||||||
|
|
||||||
|
/* FIXME SELINUX: The mkfifo here should be done with
|
||||||
|
* the right SELinux context set */
|
||||||
|
|
||||||
|
/* Enforce the right access mode for the fifo */
|
||||||
|
old_mask = umask(~ socket_mode);
|
||||||
|
|
||||||
|
/* Include the original umask in our mask */
|
||||||
|
umask(~socket_mode | old_mask);
|
||||||
|
|
||||||
|
r = mkfifo(path, socket_mode);
|
||||||
|
umask(old_mask);
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
r = -errno;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) {
|
||||||
|
r = -errno;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fstat(fd, &st) < 0) {
|
||||||
|
r = -errno;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!S_ISFIFO(st.st_mode) ||
|
||||||
|
st.st_mode != (socket_mode & ~old_mask) ||
|
||||||
|
st.st_uid != getuid() ||
|
||||||
|
st.st_gid != getgid()) {
|
||||||
|
|
||||||
|
r = -EEXIST;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
*_fd = fd;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (fd >= 0)
|
||||||
|
close_nointr_nofail(fd);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
static int socket_open_fds(Socket *s) {
|
static int socket_open_fds(Socket *s) {
|
||||||
SocketPort *p;
|
SocketPort *p;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(s);
|
assert(s);
|
||||||
|
|
||||||
|
/* FIXME SELINUX: Somewhere here we must set the the SELinux
|
||||||
|
context for the created sockets and FIFOs. To figure out
|
||||||
|
the executable name for this, use
|
||||||
|
socket_instantiate_service() and then access the executable
|
||||||
|
path name via
|
||||||
|
s->service->exec_command[SERVICE_EXEC_START]->path. Example:
|
||||||
|
|
||||||
|
if ((r = socket_instantiate_service(s)) < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
log_debug("Socket unit %s will spawn service unit %s with executable path %s.",
|
||||||
|
s->meta.id,
|
||||||
|
s->service->meta.id,
|
||||||
|
s->service->exec_command[SERVICE_EXEC_START]->path);
|
||||||
|
*/
|
||||||
|
|
||||||
LIST_FOREACH(port, p, s->ports) {
|
LIST_FOREACH(port, p, s->ports) {
|
||||||
|
|
||||||
if (p->fd >= 0)
|
if (p->fd >= 0)
|
||||||
|
@ -630,41 +741,26 @@ static int socket_open_fds(Socket *s) {
|
||||||
s->free_bind,
|
s->free_bind,
|
||||||
s->directory_mode,
|
s->directory_mode,
|
||||||
s->socket_mode,
|
s->socket_mode,
|
||||||
|
/* FIXME SELINUX: Pass the SELinux context object here */
|
||||||
&p->fd)) < 0)
|
&p->fd)) < 0)
|
||||||
goto rollback;
|
goto rollback;
|
||||||
|
|
||||||
socket_apply_socket_options(s, p->fd);
|
socket_apply_socket_options(s, p->fd);
|
||||||
|
|
||||||
} else {
|
} else if (p->type == SOCKET_FIFO) {
|
||||||
struct stat st;
|
|
||||||
assert(p->type == SOCKET_FIFO);
|
|
||||||
|
|
||||||
mkdir_parents(p->path, s->directory_mode);
|
if ((r = fifo_address_create(
|
||||||
|
p->path,
|
||||||
if (mkfifo(p->path, s->socket_mode) < 0 && errno != EEXIST) {
|
s->directory_mode,
|
||||||
r = -errno;
|
s->socket_mode,
|
||||||
|
/* FIXME SELINUX: Pass the SELinux context object here */
|
||||||
|
&p->fd)) < 0)
|
||||||
goto rollback;
|
goto rollback;
|
||||||
}
|
|
||||||
|
|
||||||
if ((p->fd = open(p->path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) {
|
socket_apply_fifo_options(s, p->fd);
|
||||||
r = -errno;
|
|
||||||
goto rollback;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fstat(p->fd, &st) < 0) {
|
} else
|
||||||
r = -errno;
|
assert_not_reached("Unknown port type");
|
||||||
goto rollback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME verify user, access mode */
|
|
||||||
|
|
||||||
if (!S_ISFIFO(st.st_mode)) {
|
|
||||||
r = -EEXIST;
|
|
||||||
goto rollback;
|
|
||||||
}
|
|
||||||
|
|
||||||
socket_apply_pipe_options(s, p->fd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1059,8 +1155,8 @@ static void socket_enter_running(Socket *s, int cfd) {
|
||||||
|
|
||||||
socket_set_state(s, SOCKET_RUNNING);
|
socket_set_state(s, SOCKET_RUNNING);
|
||||||
} else {
|
} else {
|
||||||
Unit *u;
|
|
||||||
char *prefix, *instance = NULL, *name;
|
char *prefix, *instance = NULL, *name;
|
||||||
|
Service *service;
|
||||||
|
|
||||||
if (s->n_connections >= s->max_connections) {
|
if (s->n_connections >= s->max_connections) {
|
||||||
log_warning("Too many incoming connections (%u)", s->n_connections);
|
log_warning("Too many incoming connections (%u)", s->n_connections);
|
||||||
|
@ -1068,7 +1164,10 @@ static void socket_enter_running(Socket *s, int cfd) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((r = instance_from_socket(cfd, s->n_accepted++, &instance)) < 0)
|
if ((r = socket_instantiate_service(s)) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if ((r = instance_from_socket(cfd, s->n_accepted, &instance)) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (!(prefix = unit_name_to_prefix(s->meta.id))) {
|
if (!(prefix = unit_name_to_prefix(s->meta.id))) {
|
||||||
|
@ -1086,20 +1185,25 @@ static void socket_enter_running(Socket *s, int cfd) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = manager_load_unit(s->meta.manager, name, NULL, NULL, &u);
|
if ((r = unit_add_name(UNIT(s->service), name)) < 0) {
|
||||||
|
free(name);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
service = s->service;
|
||||||
|
s->service = NULL;
|
||||||
|
s->n_accepted ++;
|
||||||
|
|
||||||
|
unit_choose_id(UNIT(service), name);
|
||||||
free(name);
|
free(name);
|
||||||
|
|
||||||
if (r < 0)
|
if ((r = service_set_socket_fd(service, cfd, s)) < 0)
|
||||||
goto fail;
|
|
||||||
|
|
||||||
if ((r = service_set_socket_fd(SERVICE(u), cfd, s)) < 0)
|
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
cfd = -1;
|
cfd = -1;
|
||||||
|
|
||||||
s->n_connections ++;
|
s->n_connections ++;
|
||||||
|
|
||||||
if ((r = manager_add_job(u->meta.manager, JOB_START, u, JOB_REPLACE, true, &error, NULL)) < 0)
|
if ((r = manager_add_job(s->meta.manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL)) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,9 @@ struct Socket {
|
||||||
ExecCommand* exec_command[_SOCKET_EXEC_COMMAND_MAX];
|
ExecCommand* exec_command[_SOCKET_EXEC_COMMAND_MAX];
|
||||||
ExecContext exec_context;
|
ExecContext exec_context;
|
||||||
|
|
||||||
|
/* For Accept=no sockets refers to the one service we'll
|
||||||
|
activate. For Accept=yes sockets is either NULL, or filled
|
||||||
|
when the next service we spawn. */
|
||||||
Service *service;
|
Service *service;
|
||||||
|
|
||||||
SocketState state, deserialized_state;
|
SocketState state, deserialized_state;
|
||||||
|
|
Loading…
Reference in New Issue