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:
2026-07-22 04:42:36 +09:00
parent 049748c182
commit f6ec5e5f9a
+87 -34
View File
@@ -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!");