krb5 commit: Add support for systemd socket activation

ghudson at mit.edu ghudson at mit.edu
Mon Feb 3 18:55:42 EST 2025


https://github.com/krb5/krb5/commit/11b9a8244f1b3f5226f315e998f4e892b262e46e
commit 11b9a8244f1b3f5226f315e998f4e892b262e46e
Author: Andreas Schneider <asn at samba.org>
Date:   Wed Nov 20 18:17:29 2024 +0100

    Add support for systemd socket activation
    
    If LISTEN_PID and LISTEN_FDS are set in the environment, expect
    listener sockets to be present at fds starting at 3.  If any match a
    configured listener address and port, use it instead of creating a new
    socket.
    
    [ghudson at mit.edu: combined two commits; changed sa_compare() to
    sa_equal(); restructured changes to fetch listener sockets into a list
    once and to match on socket type as well as address; added tests]
    
    ticket: 9157 (new)

 .gitignore                           |   1 +
 doc/admin/admin_commands/kadmind.rst |   8 ++
 doc/admin/admin_commands/krb5kdc.rst |   7 ++
 src/include/socket-utils.h           |  35 +++++++++
 src/kdc/Makefile.in                  |  10 ++-
 src/kdc/deps                         |  10 +++
 src/kdc/t_sockact.c                  | 121 +++++++++++++++++++++++++++++
 src/kdc/t_sockact.py                 |  43 +++++++++++
 src/lib/apputils/net-server.c        | 144 ++++++++++++++++++++++++++++++-----
 9 files changed, 358 insertions(+), 21 deletions(-)

diff --git a/.gitignore b/.gitignore
index a7a217a6f..ae6780c37 100644
--- a/.gitignore
+++ b/.gitignore
@@ -258,6 +258,7 @@ local.properties
 /src/kdc/rtest
 /src/kdc/t_ndr
 /src/kdc/t_replay
+/src/kdc/t_sockact
 
 /src/lib/k5sprt32.def
 
diff --git a/doc/admin/admin_commands/kadmind.rst b/doc/admin/admin_commands/kadmind.rst
index 7e1482635..bc66890de 100644
--- a/doc/admin/admin_commands/kadmind.rst
+++ b/doc/admin/admin_commands/kadmind.rst
@@ -121,6 +121,14 @@ ENVIRONMENT
 See :ref:`kerberos(7)` for a description of Kerberos environment
 variables.
 
+As of release 1.22, kadmind supports systemd socket activation via the
+LISTEN_PID and LISTEN_FDS environment variables.  Sockets provided by
+the caller must correspond to configured listener addresses (via the
+**kadmind_listen** or **kpasswd_listen** variables or equivalents) or
+they will be ignored.  Any configured listener addresses that do not
+correspond to caller-provided sockets will be ignored if socket
+activation is used.
+
 
 SEE ALSO
 --------
diff --git a/doc/admin/admin_commands/krb5kdc.rst b/doc/admin/admin_commands/krb5kdc.rst
index 631a0de84..97fbe5ed7 100644
--- a/doc/admin/admin_commands/krb5kdc.rst
+++ b/doc/admin/admin_commands/krb5kdc.rst
@@ -106,6 +106,13 @@ ENVIRONMENT
 See :ref:`kerberos(7)` for a description of Kerberos environment
 variables.
 
+As of release 1.22, krb5kdc supports systemd socket activation via the
+LISTEN_PID and LISTEN_FDS environment variables.  Sockets provided by
+the caller must correspond to configured listener addresses (via the
+**kdc_listen** variable or equivalent) or they will be ignored.  Any
+configured listener addresses that do not correspond to
+caller-provided sockets will be ignored if socket activation is used.
+
 
 SEE ALSO
 --------
diff --git a/src/include/socket-utils.h b/src/include/socket-utils.h
index 177662c87..02c10ec02 100644
--- a/src/include/socket-utils.h
+++ b/src/include/socket-utils.h
@@ -43,6 +43,8 @@
 #ifndef SOCKET_UTILS_H
 #define SOCKET_UTILS_H
 
+#include <stdbool.h>
+
 /* Some useful stuff cross-platform for manipulating socket addresses.
    We assume at least ipv4 sockaddr_in support.  The sockaddr_storage
    stuff comes from the ipv6 socket api enhancements; socklen_t is
@@ -159,4 +161,37 @@ sa_socklen(const struct sockaddr *sa)
         abort();
 }
 
+/* Return true if a and b are the same address (and port if applicable). */
+static inline bool
+sa_equal(const struct sockaddr *a, const struct sockaddr *b)
+{
+    if (a == NULL || b == NULL || a->sa_family != b->sa_family)
+        return false;
+
+    if (a->sa_family == AF_INET) {
+        const struct sockaddr_in *x = sa2sin(a);
+        const struct sockaddr_in *y = sa2sin(b);
+
+        if (x->sin_port != y->sin_port)
+            return false;
+        return memcmp(&x->sin_addr, &y->sin_addr, sizeof(x->sin_addr)) == 0;
+    } else if (a->sa_family == AF_INET6) {
+        const struct sockaddr_in6 *x = sa2sin6(a);
+        const struct sockaddr_in6 *y = sa2sin6(b);
+
+        if (x->sin6_port != y->sin6_port)
+            return false;
+        return memcmp(&x->sin6_addr, &y->sin6_addr, sizeof(x->sin6_addr)) == 0;
+#ifndef _WIN32
+    } else if (a->sa_family == AF_UNIX) {
+        const struct sockaddr_un *x = sa2sun(a);
+        const struct sockaddr_un *y = sa2sun(b);
+
+        return strcmp(x->sun_path, y->sun_path) == 0;
+#endif
+    }
+
+    return false;
+}
+
 #endif /* SOCKET_UTILS_H */
diff --git a/src/kdc/Makefile.in b/src/kdc/Makefile.in
index 7199b3472..bf4d9b580 100644
--- a/src/kdc/Makefile.in
+++ b/src/kdc/Makefile.in
@@ -31,7 +31,8 @@ SRCS= \
 
 EXTRADEPSRCS= \
 	$(srcdir)/t_ndr.c \
-	$(srcdir)/t_replay.c
+	$(srcdir)/t_replay.c \
+	$(srcdir)/t_sockact.c
 
 OBJS= \
 	authind.o \
@@ -78,16 +79,21 @@ T_REPLAY_OBJS=t_replay.o
 t_replay: $(T_REPLAY_OBJS) replay.o $(KRB5_BASE_DEPLIBS)
 	$(CC_LINK) -o $@ $(T_REPLAY_OBJS) $(CMOCKA_LIBS) $(KRB5_BASE_LIBS)
 
+t_sockact: t_sockact.o $(KRB5_BASE_DEPLIBS)
+	$(CC_LINK) -o $@ t_sockact.o $(KRB5_BASE_LIBS)
+
 check-cmocka: t_replay
 	$(RUN_TEST) ./t_replay > /dev/null
 
-check-pytests:
+check-pytests: t_sockact
 	$(RUNPYTEST) $(srcdir)/t_workers.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_emptytgt.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_bigreply.py $(PYTESTFLAGS)
+	$(RUNPYTEST) $(srcdir)/t_sockact.py $(PYTESTFLAGS)
 
 install:
 	$(INSTALL_PROGRAM) krb5kdc ${DESTDIR}$(SERVER_BINDIR)/krb5kdc
 
 clean:
 	$(RM) krb5kdc rtest.o rtest t_replay.o t_replay t_ndr.o t_ndr
+	$(RM) t_sockact.o t_sockact
diff --git a/src/kdc/deps b/src/kdc/deps
index 2d54fa93d..86be4c475 100644
--- a/src/kdc/deps
+++ b/src/kdc/deps
@@ -427,3 +427,13 @@ $(OUTPRE)t_replay.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
   extern.h kdc_util.h realm_data.h replay.c reqstate.h \
   t_replay.c
+$(OUTPRE)t_sockact.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+  $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+  $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+  $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+  $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+  $(top_srcdir)/include/socket-utils.h t_sockact.c
diff --git a/src/kdc/t_sockact.c b/src/kdc/t_sockact.c
new file mode 100644
index 000000000..cb4a4bc7a
--- /dev/null
+++ b/src/kdc/t_sockact.c
@@ -0,0 +1,121 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kdc/t_sockact.c - socket activation test harness */
+/*
+ * Copyright (C) 2025 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Usage: t_sockact address... -- program args...
+ *
+ * This program simulates systemd socket activation by creating one or more
+ * listener sockets at the specified addresses, setting LISTEN_FDS and
+ * LISTEN_PID in the environment, and executing the specified command.  (The
+ * real systemd would not execute the program until there is input on one of
+ * the listener sockets, but we do not need to simulate that, and executing the
+ * command immediately allow easier integration with k5test.py.)
+ */
+
+#include "k5-int.h"
+#include "socket-utils.h"
+
+static int max_fd;
+
+static void
+create_socket(const struct sockaddr *addr)
+{
+    int fd, one = 1;
+
+    fd = socket(addr->sa_family, SOCK_STREAM, 0);
+    if (fd < 0)
+        abort();
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != 0)
+        abort();
+#ifdef SO_REUSEPORT
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != 0)
+        abort();
+#endif
+#if defined(IPV6_V6ONLY)
+    if (addr->sa_family == AF_INET6) {
+        if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) != 0)
+            abort();
+    }
+#endif
+    if (bind(fd, addr, sa_socklen(addr)) != 0)
+        abort();
+    if (listen(fd, 5) != 0)
+        abort();
+    max_fd = fd;
+}
+
+int
+main(int argc, char **argv)
+{
+    const char *addrstr;
+    struct sockaddr_storage ss = { 0 };
+    struct addrinfo hints = { 0 }, *ai_list = NULL, *ai = NULL;
+    char *host, nbuf[128];
+    int i, port;
+
+    for (i = 1; i < argc; i++) {
+        if (strcmp(argv[i], "--") == 0)
+            break;
+        addrstr = argv[i];
+        if (*addrstr == '/') {
+            ss.ss_family = AF_UNIX;
+            strlcpy(ss2sun(&ss)->sun_path, addrstr,
+                    sizeof(ss2sun(&ss)->sun_path));
+            create_socket(ss2sa(&ss));
+        } else {
+            if (k5_parse_host_string(addrstr, 0, &host, &port) != 0 || !port)
+                abort();
+            hints.ai_socktype = SOCK_STREAM;
+            hints.ai_family = AF_UNSPEC;
+            hints.ai_flags = AI_PASSIVE;
+#ifdef AI_NUMERICSERV
+            hints.ai_flags |= AI_NUMERICSERV;
+#endif
+            (void)snprintf(nbuf, sizeof(nbuf), "%d", port);
+            if (getaddrinfo(host, nbuf, &hints, &ai_list) != 0)
+                abort();
+            for (ai = ai_list; ai != NULL; ai = ai->ai_next)
+                create_socket(ai->ai_addr);
+            freeaddrinfo(ai_list);
+            free(host);
+        }
+    }
+    argv += i + 1;
+
+    (void)snprintf(nbuf, sizeof(nbuf), "%d", max_fd - 2);
+    setenv("LISTEN_FDS", nbuf, 1);
+    (void)snprintf(nbuf, sizeof(nbuf), "%lu", (unsigned long)getpid());
+    setenv("LISTEN_PID", nbuf, 1);
+    execv(argv[0], argv);
+    abort();
+    return 1;
+}
diff --git a/src/kdc/t_sockact.py b/src/kdc/t_sockact.py
new file mode 100644
index 000000000..2a4aeb9ed
--- /dev/null
+++ b/src/kdc/t_sockact.py
@@ -0,0 +1,43 @@
+from k5test import *
+
+if not which('systemd-socket-activate'):
+    skip_rest('socket activation tests', 'systemd-socket-activate not found')
+
+# Configure listeners for two UNIX domain sockets and two ports.
+kdc_conf = {'realms': {'$realm': {
+    'kdc_listen': '$testdir/sock1 $testdir/sock2',
+    'kdc_tcp_listen': '$port7 $port8'}}}
+realm = K5Realm(kdc_conf=kdc_conf, start_kdc=False)
+
+# Create socket activation fds for just one of the UNIX domain sockets
+# and one of the ports.
+realm.start_server(['./t_sockact', os.path.join(realm.testdir, 'sock1'),
+                    str(realm.portbase + 8), '--', krb5kdc, '-n'],
+                   'starting...')
+
+mark('UNIX socket 1')
+cconf1 = {'realms': {'$realm': {'kdc': '$testdir/sock1'}}}
+env1 = realm.special_env('sock1', False, krb5_conf=cconf1)
+realm.kinit(realm.user_princ, password('user'), env=env1)
+
+mark('port8')
+cconf2 = {'realms': {'$realm': {'kdc': '$hostname:$port8'}}}
+env2 = realm.special_env('sock1', False, krb5_conf=cconf2)
+realm.kinit(realm.user_princ, password('user'), env=env2)
+
+# Test that configured listener addresses are ignored if they don't
+# match caller-provided sockets.
+
+mark('UNIX socket 2')
+cconf3 = {'realms': {'$realm': {'kdc': '$testdir/sock2'}}}
+env3 = realm.special_env('sock2', False, krb5_conf=cconf3)
+realm.kinit(realm.user_princ, password('user'), env=env3, expected_code=1,
+            expected_msg='Cannot contact any KDC')
+
+mark('port7')
+cconf4 = {'realms': {'$realm': {'kdc': '$hostname:$port7'}}}
+env4 = realm.special_env('sock1', False, krb5_conf=cconf4)
+realm.kinit(realm.user_princ, password('user'), env=env3, expected_code=1,
+            expected_msg='Cannot contact any KDC')
+
+success('systemd socket activation tests')
diff --git a/src/lib/apputils/net-server.c b/src/lib/apputils/net-server.c
index 6fa8a97e0..c8c83606d 100644
--- a/src/lib/apputils/net-server.c
+++ b/src/lib/apputils/net-server.c
@@ -65,6 +65,19 @@
 
 #include "udppktinfo.h"
 
+/* List of systemd socket activation addresses and socket types. */
+struct sockact_list {
+    size_t nsockets;
+    struct {
+        struct sockaddr_storage addr;
+        int type;
+    } *fds;
+};
+
+/* When systemd socket activation is used, caller-provided sockets begin at
+ * file descriptor 3. */
+const int SOCKACT_START = 3;
+
 /* XXX */
 #define KDC5_NONET                               (-1779992062L)
 
@@ -694,6 +707,82 @@ static const enum conn_type bind_conn_types[] =
     [UNX] = CONN_UNIXSOCK_LISTENER
 };
 
+/* If any systemd socket activation fds are indicated by the environment, set
+ * them close-on-exec and put their addresses and socket types into *list. */
+static void
+init_sockact_list(struct sockact_list *list)
+{
+    const char *v;
+    char *end;
+    long lpid;
+    int fd;
+    size_t nfds, i;
+    socklen_t slen;
+
+    list->nsockets = 0;
+    list->fds = NULL;
+
+    /* Check if LISTEN_FDS is meant for this process. */
+    v = getenv("LISTEN_PID");
+    if (v == NULL)
+        return;
+    lpid = strtol(v, &end, 10);
+    if (end == NULL || end == v || *end != '\0' || lpid != getpid())
+        return;
+
+    /* Get the number of activated sockets. */
+    v = getenv("LISTEN_FDS");
+    if (v == NULL)
+        return;
+    nfds = strtoul(v, &end, 10);
+    if (end == NULL || end == v || *end != '\0')
+        return;
+    if (nfds == 0 || nfds > (size_t)INT_MAX - SOCKACT_START)
+        return;
+
+    list->fds = calloc(nfds, sizeof(*list->fds));
+    if (list->fds == NULL)
+        return;
+
+    for (i = 0; i < nfds; i++) {
+        fd = i + SOCKACT_START;
+        set_cloexec_fd(fd);
+        slen = sizeof(list->fds[i].addr);
+        (void)getsockname(fd, ss2sa(&list->fds[i].addr), &slen);
+        slen = sizeof(list->fds[i].type);
+        (void)getsockopt(fd, SOL_SOCKET, SO_TYPE, &list->fds[i].type, &slen);
+    }
+
+    list->nsockets = nfds;
+}
+
+/* Release any storage used by *list. */
+static void
+fini_sockact_list(struct sockact_list *list)
+{
+    free(list->fds);
+    list->fds = NULL;
+    list->nsockets = 0;
+}
+
+/* If sa matches an address in *list, return the associated file descriptor and
+ * clear the address from *list.  Otherwise return -1. */
+static int
+find_sockact(struct sockact_list *list, const struct sockaddr *sa, int type)
+{
+    size_t i;
+
+    for (i = 0; i < list->nsockets; i++) {
+        if (list->fds[i].type == type &&
+            sa_equal(ss2sa(&list->fds[i].addr), sa)) {
+            list->fds[i].type = -1;
+            memset(&list->fds[i].addr, 0, sizeof(list->fds[i].addr));
+            return i + SOCKACT_START;
+        }
+    }
+    return -1;
+}
+
 /*
  * Set up a listening socket.
  *
@@ -708,8 +797,9 @@ static const enum conn_type bind_conn_types[] =
  */
 static krb5_error_code
 setup_socket(struct bind_address *ba, struct sockaddr *sock_address,
-             void *handle, const char *prog, verto_ctx *ctx,
-             int listen_backlog, verto_callback vcb, enum conn_type ctype)
+             struct sockact_list *sockacts, void *handle, const char *prog,
+             verto_ctx *ctx, int listen_backlog, verto_callback vcb,
+             enum conn_type ctype)
 {
     krb5_error_code ret;
     struct connection *conn;
@@ -722,20 +812,31 @@ setup_socket(struct bind_address *ba, struct sockaddr *sock_address,
     krb5_klog_syslog(LOG_DEBUG, _("Setting up %s socket for address %s"),
                      bind_type_names[ba->type], addrbuf);
 
-    /* Create the socket. */
-    ret = create_server_socket(sock_address, bind_socktypes[ba->type], prog,
-                               &sock);
-    if (ret)
-        goto cleanup;
+    if (sockacts->nsockets > 0) {
+        /* Look for a systemd socket activation fd matching sock_address. */
+        sock = find_sockact(sockacts, sock_address, bind_socktypes[ba->type]);
+        if (sock == -1) {
+            /* Ignore configured addresses that don't match any caller-provided
+             * sockets. */
+            ret = 0;
+            goto cleanup;
+        }
+    } else {
+        /* We're not using socket activation; create the socket. */
+        ret = create_server_socket(sock_address, bind_socktypes[ba->type],
+                                   prog, &sock);
+        if (ret)
+            goto cleanup;
 
-    /* Listen for backlogged connections on stream sockets.  (For RPC sockets
-     * this will be done by svc_register().) */
-    if ((ba->type == TCP || ba->type == UNX) &&
-        listen(sock, listen_backlog) != 0) {
-        ret = errno;
-        com_err(prog, errno, _("Cannot listen on %s server socket on %s"),
-                bind_type_names[ba->type], addrbuf);
-        goto cleanup;
+        /* Listen for backlogged connections on stream sockets.  (For RPC
+         * sockets this will be done by svc_register().) */
+        if ((ba->type == TCP || ba->type == UNX) &&
+            listen(sock, listen_backlog) != 0) {
+            ret = errno;
+            com_err(prog, errno, _("Cannot listen on %s server socket on %s"),
+                    bind_type_names[ba->type], addrbuf);
+            goto cleanup;
+        }
     }
 
     /* Set non-blocking I/O for non-RPC listener sockets. */
@@ -837,6 +938,7 @@ setup_addresses(verto_ctx *ctx, void *handle, const char *prog,
     struct bind_address addr;
     struct sockaddr_un sun;
     struct addrinfo hints, *ai_list = NULL, *ai = NULL;
+    struct sockact_list sockacts = { 0 };
     verto_callback vcb;
     char addrbuf[128];
 
@@ -855,6 +957,8 @@ setup_addresses(verto_ctx *ctx, void *handle, const char *prog,
     hints.ai_flags |= AI_NUMERICSERV;
 #endif
 
+    init_sockact_list(&sockacts);
+
     /* Add all the requested addresses. */
     for (i = 0; i < bind_addresses.n; i++) {
         addr = bind_addresses.data[i];
@@ -870,8 +974,9 @@ setup_addresses(verto_ctx *ctx, void *handle, const char *prog,
                                  addr.address);
                 goto cleanup;
             }
-            ret = setup_socket(&addr, (struct sockaddr *)&sun, handle, prog,
-                               ctx, listen_backlog, verto_callbacks[addr.type],
+            ret = setup_socket(&addr, (struct sockaddr *)&sun, &sockacts,
+                               handle, prog, ctx, listen_backlog,
+                               verto_callbacks[addr.type],
                                bind_conn_types[addr.type]);
             if (ret) {
                 krb5_klog_syslog(LOG_ERR,
@@ -911,8 +1016,8 @@ setup_addresses(verto_ctx *ctx, void *handle, const char *prog,
             /* Set the real port number. */
             sa_setport(ai->ai_addr, addr.port);
 
-            ret = setup_socket(&addr, ai->ai_addr, handle, prog, ctx,
-                               listen_backlog, verto_callbacks[addr.type],
+            ret = setup_socket(&addr, ai->ai_addr, &sockacts, handle, prog,
+                               ctx, listen_backlog, verto_callbacks[addr.type],
                                bind_conn_types[addr.type]);
             if (ret) {
                 k5_print_addr(ai->ai_addr, addrbuf, sizeof(addrbuf));
@@ -937,6 +1042,7 @@ setup_addresses(verto_ctx *ctx, void *handle, const char *prog,
 cleanup:
     if (ai_list != NULL)
         freeaddrinfo(ai_list);
+    fini_sockact_list(&sockacts);
     return ret;
 }
 


More information about the cvs-krb5 mailing list