kpasswd: add support for systemd socket activation
Adds support for systemd's fd-passing socket activation, letting systemd listen for connections before starting the service. This might enable quicker startup on systems with dependent services, since they will now only need to wait for the socket, which holds connections until kdc is finished starting up. It also enables use of the `PrivateNetwork` directive, sandboxing kpasswd into it's own network namespace.
This commit is contained in:
+87
-34
@@ -706,45 +706,98 @@ doit(krb5_keytab keytab, int port)
|
||||
fd_set real_fdset;
|
||||
struct sockaddr_storage __ss;
|
||||
struct sockaddr *sa = (struct sockaddr *)&__ss;
|
||||
int sd_activated = 0;
|
||||
|
||||
if (explicit_addresses.len) {
|
||||
addrs = explicit_addresses;
|
||||
} else {
|
||||
ret = krb5_get_all_server_addrs(context, &addrs);
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "krb5_get_all_server_addrs");
|
||||
}
|
||||
n = addrs.len;
|
||||
#ifdef HAVE_SYSTEMD
|
||||
{
|
||||
char **names = NULL;
|
||||
int sd_n, j;
|
||||
|
||||
sockets = malloc(n * sizeof(*sockets));
|
||||
if (sockets == NULL)
|
||||
krb5_errx(context, 1, "out of memory");
|
||||
maxfd = -1;
|
||||
FD_ZERO(&real_fdset);
|
||||
for (i = 0; i < n; ++i) {
|
||||
krb5_socklen_t sa_size = sizeof(__ss);
|
||||
sd_n = sd_listen_fds_with_names(0, &names);
|
||||
if (sd_n < 0)
|
||||
krb5_err(context, 1, -sd_n, "sd_listen_fds_with_names");
|
||||
if (sd_n > 0) {
|
||||
sd_activated = 1;
|
||||
sockets = malloc(sd_n * sizeof(*sockets));
|
||||
if (sockets == NULL)
|
||||
krb5_errx(context, 1, "out of memory");
|
||||
memset(&addrs, 0, sizeof(addrs));
|
||||
addrs.val = malloc(sd_n * sizeof(*addrs.val));
|
||||
if (addrs.val == NULL)
|
||||
krb5_errx(context, 1, "out of memory");
|
||||
n = 0;
|
||||
maxfd = -1;
|
||||
FD_ZERO(&real_fdset);
|
||||
for (j = 0; j < sd_n; j++) {
|
||||
int fd = SD_LISTEN_FDS_START + j;
|
||||
krb5_socklen_t sa_size = sizeof(__ss);
|
||||
|
||||
krb5_addr2sockaddr(context, &addrs.val[i], sa, &sa_size, port);
|
||||
|
||||
sockets[i] = socket(__ss.ss_family, SOCK_DGRAM, 0);
|
||||
if (sockets[i] < 0)
|
||||
krb5_err(context, 1, errno, "socket");
|
||||
if (bind(sockets[i], sa, sa_size) < 0) {
|
||||
char str[128];
|
||||
size_t len;
|
||||
int save_errno = errno;
|
||||
|
||||
ret = krb5_print_address(&addrs.val[i], str, sizeof(str), &len);
|
||||
if (ret)
|
||||
strlcpy(str, "unknown address", sizeof(str));
|
||||
krb5_warn(context, save_errno, "bind(%s)", str);
|
||||
continue;
|
||||
if (names == NULL || names[j] == NULL ||
|
||||
strncmp(names[j], "kpasswdd", 8) != 0)
|
||||
continue;
|
||||
if (getsockname(fd, sa, &sa_size) < 0) {
|
||||
krb5_warn(context, errno,
|
||||
"getsockname on socket-activated fd %d", fd);
|
||||
continue;
|
||||
}
|
||||
if (krb5_sockaddr2address(context, sa, &addrs.val[n]) != 0)
|
||||
continue;
|
||||
sockets[n] = fd;
|
||||
maxfd = max(maxfd, fd);
|
||||
if (maxfd >= FD_SETSIZE)
|
||||
krb5_errx(context, 1, "fd too large");
|
||||
FD_SET(fd, &real_fdset);
|
||||
n++;
|
||||
}
|
||||
addrs.len = n;
|
||||
}
|
||||
maxfd = max(maxfd, sockets[i]);
|
||||
if (maxfd >= FD_SETSIZE)
|
||||
krb5_errx(context, 1, "fd too large");
|
||||
FD_SET(sockets[i], &real_fdset);
|
||||
for (j = 0; names != NULL && names[j] != NULL; j++)
|
||||
free(names[j]);
|
||||
free(names);
|
||||
}
|
||||
#endif /* HAVE_SYSTEMD */
|
||||
|
||||
if (!sd_activated) {
|
||||
if (explicit_addresses.len) {
|
||||
addrs = explicit_addresses;
|
||||
} else {
|
||||
ret = krb5_get_all_server_addrs(context, &addrs);
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "krb5_get_all_server_addrs");
|
||||
}
|
||||
n = addrs.len;
|
||||
|
||||
sockets = malloc(n * sizeof(*sockets));
|
||||
if (sockets == NULL)
|
||||
krb5_errx(context, 1, "out of memory");
|
||||
maxfd = -1;
|
||||
FD_ZERO(&real_fdset);
|
||||
for (i = 0; i < n; ++i) {
|
||||
krb5_socklen_t sa_size = sizeof(__ss);
|
||||
|
||||
krb5_addr2sockaddr(context, &addrs.val[i], sa, &sa_size, port);
|
||||
|
||||
sockets[i] = socket(__ss.ss_family, SOCK_DGRAM, 0);
|
||||
if (sockets[i] < 0)
|
||||
krb5_err(context, 1, errno, "socket");
|
||||
if (bind(sockets[i], sa, sa_size) < 0) {
|
||||
char str[128];
|
||||
size_t len;
|
||||
int save_errno = errno;
|
||||
|
||||
ret = krb5_print_address(&addrs.val[i], str, sizeof(str), &len);
|
||||
if (ret)
|
||||
strlcpy(str, "unknown address", sizeof(str));
|
||||
krb5_warn(context, save_errno, "bind(%s)", str);
|
||||
continue;
|
||||
}
|
||||
maxfd = max(maxfd, sockets[i]);
|
||||
if (maxfd >= FD_SETSIZE)
|
||||
krb5_errx(context, 1, "fd too large");
|
||||
FD_SET(sockets[i], &real_fdset);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxfd == -1)
|
||||
krb5_errx(context, 1, "No sockets!");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user