krb5 commit: Add support for UNIX domain sockets

ghudson at mit.edu ghudson at mit.edu
Wed Dec 11 17:02:59 EST 2024


https://github.com/krb5/krb5/commit/f3a59a8bae1c4db4a2b47cc1049236bc339a094f
commit f3a59a8bae1c4db4a2b47cc1049236bc339a094f
Author: Andreas Schneider <asn at samba.org>
Date:   Tue Jul 9 11:38:26 2024 +0200

    Add support for UNIX domain sockets
    
    Make the KDC and kadmind listen on UNIX domain sockets if any are
    listed in kdc_listen, kadmind_listen, or kpasswd_listen.  Send KDC and
    kpasswd requests on UNIX domain sockets if any are listed in the kdc
    and primary_kdc realm variables.
    
    [ghudson at mit.edu: combined several commits; simplified client side by
    treating UNIX domain socket entries like module-generated addresses;
    edited commit message]
    
    ticket: 9155

 doc/admin/conf_files/kdc_conf.rst  | 62 ++++++++++++++++++++------------------
 doc/admin/conf_files/krb5_conf.rst | 17 ++++++-----
 src/include/k5-int.h               |  1 +
 src/kadmin/server/ovsec_kadmd.c    |  3 ++
 src/kdc/main.c                     |  3 ++
 src/lib/krb5/os/locate_kdc.c       | 17 +++++++++++
 src/lib/krb5/os/os-proto.h         |  1 +
 src/lib/krb5/os/sendto_kdc.c       | 25 +++++++--------
 src/lib/krb5/os/trace.c            |  2 ++
 src/tests/t_changepw.py            | 22 +++++++++++++-
 src/tests/t_sendto_kdc.py          | 17 +++++++++++
 11 files changed, 119 insertions(+), 51 deletions(-)

diff --git a/doc/admin/conf_files/kdc_conf.rst b/doc/admin/conf_files/kdc_conf.rst
index ed07d83b0..f809f77dd 100644
--- a/doc/admin/conf_files/kdc_conf.rst
+++ b/doc/admin/conf_files/kdc_conf.rst
@@ -289,16 +289,16 @@ The following tags may be specified in a [realms] subsection:
 **kadmind_listen**
     (Whitespace- or comma-separated list.)  Specifies the kadmin RPC
     listening addresses and/or ports for the :ref:`kadmind(8)` daemon.
-    Each entry may be an interface address, a port number, or an
-    address and port number separated by a colon.  If the address
-    contains colons, enclose it in square brackets.  If no address is
-    specified, the wildcard address is used.  To disable listening for
-    kadmin RPC connections, set this relation to the empty string with
-    ``kadmind_listen = ""``.  If kadmind fails to bind to any of the
-    specified addresses, it will fail to start.  The default is to
-    bind to the wildcard address at the port specified in
-    **kadmind_port**, or the standard kadmin port (749).  New in
-    release 1.15.
+    Each entry may be an interface address, a port number, an address
+    and port number separated by a colon, or a UNIX domain socket
+    pathname.  If the address contains colons, enclose it in square
+    brackets.  If no address is specified, the wildcard address is
+    used.  To disable listening for kadmin RPC connections, set this
+    relation to the empty string with ``kadmind_listen = ""``.  If
+    kadmind fails to bind to any of the specified addresses, it will
+    fail to start.  The default is to bind to the wildcard address at
+    the port specified in **kadmind_port**, or the standard kadmin
+    port (749).  New in release 1.15.
 
 **kadmind_port**
     (Port number.)  Specifies the port on which the :ref:`kadmind(8)`
@@ -314,15 +314,16 @@ The following tags may be specified in a [realms] subsection:
 **kdc_listen**
     (Whitespace- or comma-separated list.)  Specifies the UDP
     listening addresses and/or ports for the :ref:`krb5kdc(8)` daemon.
-    Each entry may be an interface address, a port number, or an
-    address and port number separated by a colon.  If the address
-    contains colons, enclose it in square brackets.  If no address is
-    specified, the wildcard address is used.  If no port is specified,
-    the standard port (88) is used.  To disable listening on UDP, set
-    this relation to the empty string with ``kdc_listen = ""``.
-    If the KDC daemon fails to bind to any of the specified addresses,
-    it will fail to start.  The default is to bind to the wildcard
-    address on the standard port.  New in release 1.15.
+    Each entry may be an interface address, a port number, an address
+    and port number separated by a colon, or a UNIX domain socket
+    pathname.  If the address contains colons, enclose it in square
+    brackets.  If no address is specified, the wildcard address is
+    used.  If no port is specified, the standard port (88) is used.
+    To disable listening on UDP, set this relation to the empty string
+    with ``kdc_listen = ""``.  If the KDC daemon fails to bind to any
+    of the specified addresses, it will fail to start.  The default is
+    to bind to the wildcard address on the standard port.  New in
+    release 1.15.
 
 **kdc_ports**
     (Whitespace- or comma-separated list, deprecated.)  Prior to
@@ -352,17 +353,18 @@ The following tags may be specified in a [realms] subsection:
     **kdc_tcp_listen** if that relation is not defined.
 
 **kpasswd_listen**
-    (Comma-separated list.)  Specifies the kpasswd listening addresses
-    and/or ports for the :ref:`kadmind(8)` daemon.  Each entry may be
-    an interface address, a port number, or an address and port number
-    separated by a colon.  If the address contains colons, enclose it
-    in square brackets.  If no address is specified, the wildcard
-    address is used.  To disable listening for kpasswd requests, set
-    this relation to the empty string with ``kpasswd_listen = ""``.
-    If kadmind fails to bind to any of the specified addresses, it
-    will fail to start.  The default is to bind to the wildcard
-    address at the port specified in **kpasswd_port**, or the standard
-    kpasswd port (464).  New in release 1.15.
+    (Comma-separated list.)  Specifies the kpasswd listening
+    addresses and/or ports for the :ref:`kadmind(8)` daemon.  Each
+    entry may be an interface address, a port number, an address and
+    port number separated by a colon, or a UNIX domain socket
+    pathname.  If the address contains colons, enclose it in square
+    brackets.  If no address is specified, the wildcard address is
+    used.  To disable listening for kpasswd requests, set this
+    relation to the empty string with ``kpasswd_listen = ""``.  If
+    kadmind fails to bind to any of the specified addresses, it will
+    fail to start.  The default is to bind to the wildcard address at
+    the port specified in **kpasswd_port**, or the standard kpasswd
+    port (464).  New in release 1.15.
 
 **kpasswd_port**
     (Port number.)  Specifies the port on which the :ref:`kadmind(8)`
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index 315c2ff42..e80e02eba 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
@@ -530,20 +530,21 @@ following tags may be specified in the realm's subsection:
     been set to ``FILE:/tmp/my_proxy.pem``.
 
 **kdc**
-    The name or address of a host running a KDC for that realm.  An
-    optional port number, separated from the hostname by a colon, may
-    be included.  If the name or address contains colons (for example,
-    if it is an IPv6 address), enclose it in square brackets to
+    The name or address of a host running a KDC for the realm, or a
+    UNIX domain socket path of a locally running KDC.  An optional
+    port number, separated from the hostname by a colon, may be
+    included.  If the name or address contains colons (for example, if
+    it is an IPv6 address), enclose it in square brackets to
     distinguish the colon from a port separator.  For your computer to
     be able to communicate with the KDC for each realm, this tag must
     be given a value in each realm subsection in the configuration
     file, or there must be DNS SRV records specifying the KDCs.
 
 **kpasswd_server**
-    Points to the server where all the password changes are performed.
-    If there is no such entry, DNS will be queried (unless forbidden
-    by **dns_lookup_kdc**).  Finally, port 464 on the **admin_server**
-    host will be tried.
+    The location of the password change server for the realm, using
+    the same syntax as **kdc**.  If there is no such entry, DNS will
+    be queried (unless forbidden by **dns_lookup_kdc**).  Finally,
+    port 464 on the **admin_server** host will be tried.
 
 **master_kdc**
     The name for **primary_kdc** prior to release 1.19.  Its value is
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index ec9e320e1..cfd2cc939 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -248,6 +248,7 @@ typedef unsigned char   u_char;
 #define KRB5_CONF_KDC_TCP_LISTEN               "kdc_tcp_listen"
 #define KRB5_CONF_KDC_TCP_LISTEN_BACKLOG       "kdc_tcp_listen_backlog"
 #define KRB5_CONF_KDC_TIMESYNC                 "kdc_timesync"
+#define KRB5_CONF_KDC_UNIXSOCK_LISTEN          "kdc_unixsock_listen"
 #define KRB5_CONF_KEY_STASH_FILE               "key_stash_file"
 #define KRB5_CONF_KPASSWD_LISTEN               "kpasswd_listen"
 #define KRB5_CONF_KPASSWD_PORT                 "kpasswd_port"
diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c
index 6e080e197..234d9e269 100644
--- a/src/kadmin/server/ovsec_kadmd.c
+++ b/src/kadmin/server/ovsec_kadmd.c
@@ -157,6 +157,9 @@ setup_loop(kadm5_config_params *params, int proponly, verto_ctx **ctx_out)
                                    KADM, KADMVERS, kadm_1);
         if (ret)
             return ret;
+        ret = loop_add_unix_socket(params->kpasswd_listen);
+        if (ret)
+            return ret;
     }
 #ifndef DISABLE_IPROP
     if (params->iprop_enabled) {
diff --git a/src/kdc/main.c b/src/kdc/main.c
index bef8b3ee2..439565cd5 100644
--- a/src/kdc/main.c
+++ b/src/kdc/main.c
@@ -967,6 +967,9 @@ int main(int argc, char **argv)
     for (i = 0; i < shandle.kdc_numrealms; i++) {
         realm = shandle.kdc_realmlist[i];
         retval = loop_add_udp_address(KRB5_DEFAULT_PORT, realm->realm_listen);
+        if (retval)
+            goto net_init_error;
+        retval = loop_add_unix_socket(realm->realm_listen);
         if (retval)
             goto net_init_error;
         retval = loop_add_tcp_address(KRB5_DEFAULT_PORT,
diff --git a/src/lib/krb5/os/locate_kdc.c b/src/lib/krb5/os/locate_kdc.c
index b5e84ebc5..d1df04a63 100644
--- a/src/lib/krb5/os/locate_kdc.c
+++ b/src/lib/krb5/os/locate_kdc.c
@@ -295,6 +295,23 @@ locate_srv_conf_1(krb5_context context, const krb5_data *realm,
         hostspec = hostlist[i];
         Tprintf("entry %d is '%s'\n", i, hostspec);
 
+#ifndef _WIN32
+        if (hostspec[0] == '/') {
+            struct sockaddr_un sun = { 0 };
+
+            sun.sun_family = AF_UNIX;
+            if (strlcpy(sun.sun_path, hostspec, sizeof(sun.sun_path)) >=
+                sizeof(sun.sun_path)) {
+                code = ENAMETOOLONG;
+                goto cleanup;
+            }
+            code = add_addr_to_list(serverlist, UNIXSOCK, AF_UNIX, sizeof(sun),
+                                    (struct sockaddr *)&sun);
+            if (code)
+                goto cleanup;
+            continue;
+        }
+#endif
         parse_uri_if_https(hostspec, &this_transport, &hostspec, &uri_path);
 
         default_port = (this_transport == HTTPS) ? 443 : udpport;
diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
index a21558d23..a60c777a2 100644
--- a/src/lib/krb5/os/os-proto.h
+++ b/src/lib/krb5/os/os-proto.h
@@ -43,6 +43,7 @@ typedef enum {
     TCP,
     UDP,
     HTTPS,
+    UNIXSOCK,
 } k5_transport;
 
 typedef enum {
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index 4b2906139..9b51e95ae 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -385,6 +385,7 @@ socktype_for_transport(k5_transport transport)
         return SOCK_DGRAM;
     case TCP:
     case HTTPS:
+    case UNIXSOCK:
         return SOCK_STREAM;
     default:
         return 0;
@@ -668,7 +669,7 @@ set_transport_message(struct conn_state *state, const krb5_data *realm,
     if (message == NULL || message->length == 0)
         return 0;
 
-    if (state->addr.transport == TCP) {
+    if (state->addr.transport == TCP || state->addr.transport == UNIXSOCK) {
         store_32_be(message->length, out->msg_len_buf);
         SG_SET(&out->sgbuf[0], out->msg_len_buf, 4);
         SG_SET(&out->sgbuf[1], message->data, message->length);
@@ -713,7 +714,7 @@ add_connection(struct conn_state **conns, k5_transport transport,
     state->fd = INVALID_SOCKET;
     state->server_index = server_index;
     SG_SET(&state->out.sgbuf[1], NULL, 0);
-    if (transport == TCP) {
+    if (transport == TCP || transport == UNIXSOCK) {
         state->service_connect = service_tcp_connect;
         state->service_write = service_tcp_write;
         state->service_read = service_tcp_read;
@@ -822,16 +823,6 @@ resolve_server(krb5_context context, const krb5_data *realm,
         return 0;
 
     transport = (strategy == UDP_FIRST || strategy == ONLY_UDP) ? UDP : TCP;
-    if (entry->hostname == NULL) {
-        /* Added by a module, so transport is either TCP or UDP. */
-        ai.ai_socktype = socktype_for_transport(entry->transport);
-        ai.ai_family = entry->family;
-        ai.ai_addrlen = entry->addrlen;
-        ai.ai_addr = (struct sockaddr *)&entry->addr;
-        defer = (entry->transport != transport);
-        return add_connection(conns, entry->transport, defer, &ai, ind, realm,
-                              NULL, NULL, entry->uri_path, udpbufp);
-    }
 
     /* If the entry has a specified transport, use it, but possibly defer the
      * addresses we add based on the strategy. */
@@ -841,6 +832,16 @@ resolve_server(krb5_context context, const krb5_data *realm,
             (entry->transport == UDP && strategy == UDP_LAST);
     }
 
+    if (entry->hostname == NULL) {
+        /* The entry contains an address; skip name resolution. */
+        ai.ai_socktype = socktype_for_transport(entry->transport);
+        ai.ai_family = entry->family;
+        ai.ai_addrlen = entry->addrlen;
+        ai.ai_addr = ss2sa(&entry->addr);
+        return add_connection(conns, entry->transport, defer, &ai, ind, realm,
+                              NULL, NULL, entry->uri_path, udpbufp);
+    }
+
     memset(&hint, 0, sizeof(hint));
     hint.ai_family = entry->family;
     hint.ai_socktype = socktype_for_transport(transport);
diff --git a/src/lib/krb5/os/trace.c b/src/lib/krb5/os/trace.c
index 89e796f69..89699f7df 100644
--- a/src/lib/krb5/os/trace.c
+++ b/src/lib/krb5/os/trace.c
@@ -252,6 +252,8 @@ trace_format(krb5_context context, const char *fmt, va_list ap)
                 k5_buf_add(&buf, "stream");
             else if (ra->transport == HTTPS)
                 k5_buf_add(&buf, "https");
+            else if (ra->transport == UNIXSOCK)
+                k5_buf_add(&buf, "UNIX domain socket");
             else
                 k5_buf_add_fmt(&buf, "transport%d", ra->transport);
 
diff --git a/src/tests/t_changepw.py b/src/tests/t_changepw.py
index bf8e3a9eb..7e8a66397 100755
--- a/src/tests/t_changepw.py
+++ b/src/tests/t_changepw.py
@@ -1,6 +1,12 @@
 from k5test import *
 
-realm = K5Realm(create_host=False, get_creds=False, start_kadmind=True)
+# Also listen on a UNIX domain sockets for kpasswd.
+unix_conf = {'realms': {'$realm': {
+    'kdc_listen': '$port0, $testdir/krb5.sock',
+    'kadmind_listen': '$port1, $testdir/kadmin.sock',
+    'kpasswd_listen': '$port2, $testdir/kpasswd.sock'}}}
+realm = K5Realm(create_host=False,get_creds=False, kdc_conf=unix_conf)
+realm.start_kadmind()
 realm.prep_kadmin()
 
 # Mark a principal as expired and change its password through kinit.
@@ -47,4 +53,18 @@ realm.kinit('testprinc', 'pw4')
 realm.run([kdestroy])
 realm.run([kadminl, 'delprinc', 'testprinc'])
 
+mark('password change over UNIX domain socket')
+
+unix_cli_conf = {'realms': {'$realm': {
+    'kdc': '$testdir/krb5.sock',
+    'admin_server': '$testdir/kadmin.sock',
+    'kpasswd_server': '$testdir/kpasswd.sock'}}}
+unix_cli = realm.special_env('unix_cli', False, krb5_conf=unix_cli_conf)
+
+realm.run([kadminl, 'addprinc', '-pw', 'pw1', 'testprinc'])
+msgs = ('Sending TCP request to UNIX domain socket',)
+realm.run([kpasswd, 'testprinc'], input='pw1\npw2\npw2\n', env=unix_cli,
+          expected_trace=msgs)
+realm.run([kadminl, 'delprinc', 'testprinc'])
+
 success('Password change tests')
diff --git a/src/tests/t_sendto_kdc.py b/src/tests/t_sendto_kdc.py
index 8a3f8e62d..d27467b6b 100644
--- a/src/tests/t_sendto_kdc.py
+++ b/src/tests/t_sendto_kdc.py
@@ -25,4 +25,21 @@ realm.kinit(realm.user_princ, 'new', env=fallback, expected_trace=msgs)
 
 stop_daemon(replica_kdc)
 
+mark('UNIX domain socket')
+
+conf_unix = {'realms': {'$realm': {'kdc_listen': '$testdir/krb5.sock',
+                                   'kdc_tcp_listen': ''}}}
+unix = realm.special_env('unix', True, kdc_conf=conf_unix)
+realm.run([kdb5_util, 'load', dumpfile], env=unix)
+realm.stop_kdc()
+realm.start_kdc(env=unix)
+
+conf_unix_cli = {'realms': {'$realm': {'kdc': '$testdir/krb5.sock'}}}
+unix_cli = realm.special_env('unix_cli', False, krb5_conf=conf_unix_cli)
+
+# Do a kinit and check if we send the packet via a UNIX domain socket.
+msgs = ('Sending TCP request to UNIX domain socket',)
+realm.kinit(realm.user_princ, password('user'), env=unix_cli,
+            expected_trace=msgs)
+
 success('sendto_kdc')


More information about the cvs-krb5 mailing list