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