krb5 commit: Add underpinnings of UNIX domain socket support

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


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

    Add underpinnings of UNIX domain socket support
    
    Add sa2sun() and ss2sun() helpers to socket-utils.h.  Add UNIX domain
    socket support to sa_socklen() and print_addr().  Expand buffers for
    printing addresses to 128 bytes to accomodate the maximum UNIX domain
    socket path length.
    
    Add loop_add_unix_socket() to net-server.c, primarily using the
    existing TCP support (renamed to "stream").
    
    As there is no standard Kerberos address type for UNIX domain sockets,
    add basic directional address support.  Add a definition for
    ADDRTYPE_DIRECTIONAL in krb5.h.  Add private constant krb5_address
    objects to libkrb5 for initiator and acceptor directional addresses.
    Use directional addresses for the KRB-SAFE/KRB-PRIV source address in
    the kprop and password change protocols when the transport is not IPv4
    or IPv6.
    
    krb5_address objects are used for auditing purposes in the KDC audit
    and KDB pluggable interfaces.  Add a local-use address type
    ADDRTYPE_UNIXSOCK for use in these cases.  Add a flag to
    k5_sockaddr_to_address() to indicate whether this address type can be
    used.  Add UNIX domains socket conversion support to the test audit
    plugin module.
    
    [ghudson at mit.edu: combined several commits; used directional addresses
    for KRB-SAFE/KRB-PRIV; reduced duplication in net-server.c support;
    wrote commit message.  Also based on work by Alexander Bokovoy.]
    
    ticket: 9155

 doc/appdev/refs/macros/index.rst |   2 +
 src/include/k5-int.h             |  12 ++-
 src/include/krb5/krb5.hin        |   9 +++
 src/include/net-server.h         |   1 +
 src/include/port-sockets.h       |   1 +
 src/include/socket-utils.h       |  14 ++++
 src/kadmin/server/schpw.c        |   6 +-
 src/kdc/dispatch.c               |   2 +-
 src/kdc/kdc_audit.c              |   2 +-
 src/kdc/kdc_log.c                |   8 +-
 src/kdc/kdc_util.c               |   2 +-
 src/kprop/kprop.c                |   4 +-
 src/kprop/kpropd.c               |   4 +-
 src/lib/apputils/net-server.c    | 169 +++++++++++++++++++++++++++++----------
 src/lib/krb5/libkrb5.exports     |   2 +
 src/lib/krb5/os/addr.c           |  21 ++++-
 src/lib/krb5/os/changepw.c       |   6 ++
 src/plugins/audit/j_dict.h       |   1 +
 src/plugins/audit/kdc_j_encode.c |  11 +++
 19 files changed, 216 insertions(+), 61 deletions(-)

diff --git a/doc/appdev/refs/macros/index.rst b/doc/appdev/refs/macros/index.rst
index 645b59f02..c1bda5c6c 100644
--- a/doc/appdev/refs/macros/index.rst
+++ b/doc/appdev/refs/macros/index.rst
@@ -9,6 +9,7 @@ Public
 
    ADDRTYPE_ADDRPORT.rst
    ADDRTYPE_CHAOS.rst
+   ADDRTYPE_DIRECTIONAL.rst
    ADDRTYPE_DDP.rst
    ADDRTYPE_INET.rst
    ADDRTYPE_INET6.rst
@@ -17,6 +18,7 @@ Public
    ADDRTYPE_IS_LOCAL.rst
    ADDRTYPE_NETBIOS.rst
    ADDRTYPE_XNS.rst
+   ADDRTYPE_UNIXSOCK.rst
    AD_TYPE_EXTERNAL.rst
    AD_TYPE_FIELD_TYPE_MASK.rst
    AD_TYPE_REGISTERED.rst
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 12eb114a4..ec9e320e1 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -2409,13 +2409,19 @@ krb5_error_code
 k5_hmac_md5(const krb5_data *key, const krb5_crypto_iov *data, size_t num_data,
             krb5_data *output);
 
+/* Address objects for initiator and acceptor directional addresses. */
+extern const krb5_address k5_addr_directional_init;
+extern const krb5_address k5_addr_directional_accept;
+
 /*
  * Translate sa to a krb5_address, putting the result in *out with contents
- * aliased from *sa.  Return KRB5_PROG_ATYPE_NOSUPP if sa is not an IPv4 or
- * IPv6 address.
+ * aliased from *sa.  If local_use is true, translate UNIX domain socket names
+ * to ADDRTYPE_UNIXSOCK; otherwise do not handle them.  Return
+ * KRB5_PROG_ATYPE_NOSUPP if sa cannot be converted.
  */
 krb5_error_code
-k5_sockaddr_to_address(const struct sockaddr *sa, krb5_address *out);
+k5_sockaddr_to_address(const struct sockaddr *sa, krb5_boolean local_use,
+                       krb5_address *out);
 
 /* Place a printable representation of sa (without port) into buf. */
 void
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index c6998adc5..b5d295f33 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -322,6 +322,7 @@ typedef struct _krb5_address {
 
 /* per Kerberos v5 protocol spec */
 #define ADDRTYPE_INET           0x0002
+#define ADDRTYPE_DIRECTIONAL    0x0003
 #define ADDRTYPE_CHAOS          0x0005
 #define ADDRTYPE_XNS            0x0006
 #define ADDRTYPE_ISO            0x0007
@@ -332,6 +333,14 @@ typedef struct _krb5_address {
 #define ADDRTYPE_ADDRPORT       0x0100
 #define ADDRTYPE_IPPORT         0x0101
 
+/*
+ * Negative values for the host address type are reserved for local use.  Do
+ * not use these types in Kerberos protocol elements.
+ */
+
+/* The contents field contains a UNIX domain socket path. */
+#define ADDRTYPE_UNIXSOCK       (0x8000 | 0x0001)
+
 /* macros to determine if a type is a local type */
 #define ADDRTYPE_IS_LOCAL(addrtype) (addrtype & 0x8000)
 
diff --git a/src/include/net-server.h b/src/include/net-server.h
index 14fc23a43..8ac3ff26f 100644
--- a/src/include/net-server.h
+++ b/src/include/net-server.h
@@ -59,6 +59,7 @@ krb5_error_code loop_add_rpc_service(int default_port, const char *addresses,
                                      u_long prognum, u_long versnum,
                                      void (*dispatchfn)(struct svc_req *,
                                                         SVCXPRT *));
+krb5_error_code loop_add_unix_socket(const char *socket_paths);
 
 krb5_error_code loop_setup_network(verto_ctx *ctx, void *handle,
                                    const char *progname,
diff --git a/src/include/port-sockets.h b/src/include/port-sockets.h
index 228e4cf05..459cd8667 100644
--- a/src/include/port-sockets.h
+++ b/src/include/port-sockets.h
@@ -157,6 +157,7 @@ typedef int socklen_t;
 
 #include <sys/types.h>
 #include <netinet/in.h>         /* For struct sockaddr_in and in_addr */
+#include <sys/un.h>             /* For struct sockaddr_un */
 #include <arpa/inet.h>          /* For inet_ntoa */
 #include <netdb.h>
 #include <string.h>             /* For memset */
diff --git a/src/include/socket-utils.h b/src/include/socket-utils.h
index d379b37e1..177662c87 100644
--- a/src/include/socket-utils.h
+++ b/src/include/socket-utils.h
@@ -90,6 +90,16 @@ static inline struct sockaddr_in6 *ss2sin6 (struct sockaddr_storage *ss)
 {
     return (struct sockaddr_in6 *) ss;
 }
+#ifndef _WIN32
+static inline const struct sockaddr_un *sa2sun(const struct sockaddr *sa)
+{
+    return (const struct sockaddr_un *)(void *)sa;
+}
+static inline struct sockaddr_un *ss2sun(struct sockaddr_storage *ss)
+{
+    return (struct sockaddr_un *)ss;
+}
+#endif
 
 /* Set the IPv4 or IPv6 port on sa to port.  Do nothing if sa is not an
  * Internet socket. */
@@ -141,6 +151,10 @@ sa_socklen(const struct sockaddr *sa)
         return sizeof(struct sockaddr_in6);
     else if (sa->sa_family == AF_INET)
         return sizeof(struct sockaddr_in);
+#ifndef _WIN32
+    else if (sa->sa_family == AF_UNIX)
+        return sizeof(struct sockaddr_un);
+#endif
     else
         abort();
 }
diff --git a/src/kadmin/server/schpw.c b/src/kadmin/server/schpw.c
index d366c050a..5b73a3d8d 100644
--- a/src/kadmin/server/schpw.c
+++ b/src/kadmin/server/schpw.c
@@ -40,7 +40,7 @@ process_chpw_request(krb5_context context, void *server_handle, char *realm,
     const char *errmsg = NULL;
     size_t clen;
     char *cdots;
-    char addrbuf[100];
+    char addrbuf[128];
 
     *rep = empty_data();
 
@@ -286,8 +286,8 @@ chpwfail:
     cipher = empty_data();
 
     if (ap_rep.length) {
-        if (k5_sockaddr_to_address(local_addr, &laddr) != 0)
-            abort();
+        if (k5_sockaddr_to_address(local_addr, FALSE, &laddr) != 0)
+            laddr = k5_addr_directional_accept;
         ret = krb5_auth_con_setaddrs(context, auth_context, &laddr, NULL);
         if (ret) {
             numresult = KRB5_KPASSWD_HARDERROR;
diff --git a/src/kdc/dispatch.c b/src/kdc/dispatch.c
index 13d17b6d3..46b1c8b44 100644
--- a/src/kdc/dispatch.c
+++ b/src/kdc/dispatch.c
@@ -116,7 +116,7 @@ dispatch(void *cb, const struct sockaddr *local_addr,
     if (kdc_check_lookaside(kdc_err_context, pkt, &response)) {
         /* a hit! */
         const char *name = 0;
-        char buf[46];
+        char buf[128];
 
         k5_print_addr(remote_addr, buf, sizeof(buf));
         if (name == 0)
diff --git a/src/kdc/kdc_audit.c b/src/kdc/kdc_audit.c
index a5e022489..9ce8263d2 100644
--- a/src/kdc/kdc_audit.c
+++ b/src/kdc/kdc_audit.c
@@ -188,7 +188,7 @@ kau_init_kdc_req(krb5_context context,
     if (state == NULL)
         return ret;
 
-    ret = k5_sockaddr_to_address(from, &addr);
+    ret = k5_sockaddr_to_address(from, TRUE, &addr);
     if (ret)
         addr = unknown_addr;
     ret = krb5_copy_addr(context, &addr, &state->cl_addr);
diff --git a/src/kdc/kdc_log.c b/src/kdc/kdc_log.c
index 6278d645a..0291362d4 100644
--- a/src/kdc/kdc_log.c
+++ b/src/kdc/kdc_log.c
@@ -63,7 +63,7 @@ log_as_req(krb5_context context,
            krb5_timestamp authtime,
            const char *status, krb5_error_code errcode, const char *emsg)
 {
-    char fromstring[70];
+    char fromstring[128];
     char *ktypestr = NULL;
     const char *cname2 = cname ? cname : "<unknown client>";
     const char *sname2 = sname ? sname : "<unknown server>";
@@ -88,8 +88,8 @@ log_as_req(krb5_context context,
                          ktypestr ? ktypestr : "", fromstring, status, cname2,
                          sname2, emsg ? ", " : "", emsg ? emsg : "");
     }
-    (void)k5_sockaddr_to_address(local_addr, &laddr);
-    (void)k5_sockaddr_to_address(remote_addr, &raddr);
+    (void)k5_sockaddr_to_address(local_addr, TRUE, &laddr);
+    (void)k5_sockaddr_to_address(remote_addr, TRUE, &raddr);
     krb5_db_audit_as_req(context, request, &laddr, &raddr, client, server,
                          authtime, errcode);
 
@@ -123,7 +123,7 @@ log_tgs_req(krb5_context ctx, const struct sockaddr *from,
             const char *status, krb5_error_code errcode, const char *emsg)
 {
     char *ktypestr = NULL, *rep_etypestr = NULL;
-    char fromstring[70];
+    char fromstring[128];
     char *cname = NULL, *sname = NULL, *altcname = NULL;
     char *logcname = NULL, *logsname = NULL, *logaltcname = NULL;
 
diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
index b7249c083..6f88afa2d 100644
--- a/src/kdc/kdc_util.c
+++ b/src/kdc/kdc_util.c
@@ -194,7 +194,7 @@ kdc_process_tgs_req(kdc_realm_t *realm, krb5_kdc_req *request,
 
     /* If from_addr isn't IPv4 or IPv6, fake up an address that won't be
      * matched if the ticket has an address list. */
-    retval = k5_sockaddr_to_address(from, &from_addr);
+    retval = k5_sockaddr_to_address(from, FALSE, &from_addr);
     if (retval)
         from_addr = nomatch_addr;
     retval = krb5_auth_con_setaddrs(context, auth_context, NULL, &from_addr);
diff --git a/src/kprop/kprop.c b/src/kprop/kprop.c
index cb5c5267a..5adf430ac 100644
--- a/src/kprop/kprop.c
+++ b/src/kprop/kprop.c
@@ -269,8 +269,8 @@ open_connection(krb5_context context, char *host, int *fd_out)
         com_err(progname, errno, _("while getting local socket address"));
         exit(1);
     }
-    if (k5_sockaddr_to_address(ss2sa(&my_sin), &addr) != 0)
-        abort();
+    if (k5_sockaddr_to_address(ss2sa(&my_sin), FALSE, &addr) != 0)
+        addr = k5_addr_directional_init;
     retval = krb5_copy_addr(context, &addr, &sender_addr);
     if (retval) {
         com_err(progname, retval, _("while converting local address"));
diff --git a/src/kprop/kpropd.c b/src/kprop/kpropd.c
index d6deb4738..4b3675264 100644
--- a/src/kprop/kpropd.c
+++ b/src/kprop/kpropd.c
@@ -1199,8 +1199,8 @@ kerberos_authenticate(krb5_context context, int fd, krb5_principal *clientp,
         exit(1);
     }
 
-    if (k5_sockaddr_to_address(ss2sa(my_sin), &addr) != 0)
-        abort();
+    if (k5_sockaddr_to_address(ss2sa(my_sin), FALSE, &addr) != 0)
+        addr = k5_addr_directional_accept;
     retval = krb5_copy_addr(context, &addr, &receiver_addr);
     if (retval) {
         com_err(progname, retval, _("while converting local address"));
diff --git a/src/lib/apputils/net-server.c b/src/lib/apputils/net-server.c
index 3713b5026..6fa8a97e0 100644
--- a/src/lib/apputils/net-server.c
+++ b/src/lib/apputils/net-server.c
@@ -39,6 +39,7 @@
 #include <sys/types.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 #ifdef HAVE_SYS_SOCKIO_H
 /* for SIOCGIFCONF, etc. */
 #include <sys/sockio.h>
@@ -67,8 +68,8 @@
 /* XXX */
 #define KDC5_NONET                               (-1779992062L)
 
-static int tcp_or_rpc_data_counter;
-static int max_tcp_or_rpc_data_connections = 45;
+static int stream_data_counter;
+static int max_stream_data_connections = 45;
 
 static int
 setreuseaddr(int sock, int value)
@@ -97,17 +98,29 @@ setv6only(int sock, int value)
 /* KDC data.  */
 
 enum conn_type {
-    CONN_UDP, CONN_TCP_LISTENER, CONN_TCP, CONN_RPC_LISTENER, CONN_RPC
+    CONN_UDP, CONN_TCP_LISTENER, CONN_TCP, CONN_RPC_LISTENER, CONN_RPC,
+    CONN_UNIXSOCK_LISTENER, CONN_UNIXSOCK
+};
+
+static const char *const conn_type_names[] = {
+    [CONN_UDP] = "UDP",
+    [CONN_TCP_LISTENER] = "TCP listener",
+    [CONN_TCP] = "TCP",
+    [CONN_RPC_LISTENER] = "RPC listener",
+    [CONN_RPC] = "RPC",
+    [CONN_UNIXSOCK_LISTENER] = "UNIX domain socket listener",
+    [CONN_UNIXSOCK] = "UNIX domain socket"
 };
 
 enum bind_type {
-    UDP, TCP, RPC
+    UDP, TCP, RPC, UNX
 };
 
 static const char *const bind_type_names[] = {
     [UDP] = "UDP",
     [TCP] = "TCP",
     [RPC] = "RPC",
+    [UNX] = "UNIXSOCK",
 };
 
 /* Per-connection info.  */
@@ -119,7 +132,7 @@ struct connection {
     /* Connection fields (TCP or RPC) */
     struct sockaddr_storage addr_s;
     socklen_t addrlen;
-    char addrbuf[56];
+    char addrbuf[128];
 
     /* Incoming data (TCP) */
     size_t bufsiz;
@@ -262,10 +275,10 @@ loop_setup_signals(verto_ctx *ctx, void *handle, void (*reset)(void *))
  *
  * Arguments:
  * - address
- *      A string for the address (or hostname).  Pass NULL to use the wildcard
- *      address.  The address is parsed with k5_parse_host_string().
+ *      An address string, hostname, or UNIX socket path.
+ *      Pass NULL to use the wildcard address for IP sockets.
  * - port
- *      What port the socket should be set to.
+ *      What port the socket should be set to (for IPv4 or IPv6).
  * - type
  *      bind_type for the socket.
  * - rpc_data
@@ -371,6 +384,19 @@ loop_add_addresses(const char *addresses, int default_port,
     /* Loop through each address in the string and add it to the loop. */
     addr = strtok_r(addresses_copy, ADDRESSES_DELIM, &saveptr);
     for (; addr != NULL; addr = strtok_r(NULL, ADDRESSES_DELIM, &saveptr)) {
+        if (type == UNX) {
+            /* Skip non-pathnames when binding UNIX domain sockets. */
+            if (*addr != '/')
+                continue;
+            ret = loop_add_address(addr, 0, type, rpc_data);
+            if (ret)
+                goto cleanup;
+            continue;
+        } else if (*addr == '/') {
+            /* Skip pathnames when not binding UNIX domain sockets. */
+            continue;
+        }
+
         /* Parse the host string. */
         ret = k5_parse_host_string(addr, default_port, &host, &port);
         if (ret)
@@ -416,6 +442,16 @@ loop_add_rpc_service(int default_port, const char *addresses, u_long prognum,
     return loop_add_addresses(addresses, default_port, RPC, &svc);
 }
 
+krb5_error_code
+loop_add_unix_socket(const char *socket_paths)
+{
+    /* There is no wildcard or default UNIX domain socket. */
+    if (socket_paths == NULL)
+        return 0;
+
+    return loop_add_addresses(socket_paths, 0, UNX, NULL);
+}
+
 #define USE_AF AF_INET
 #define USE_TYPE SOCK_DGRAM
 #define USE_PROTO 0
@@ -484,7 +520,8 @@ free_socket(verto_ctx *ctx, verto_ev *ev)
             }
             /* Fall through. */
         case CONN_TCP:
-            tcp_or_rpc_data_counter--;
+        case CONN_UNIXSOCK:
+            stream_data_counter--;
             break;
         default:
             break;
@@ -548,9 +585,9 @@ add_fd(int sock, enum conn_type conntype, verto_ev_flag flags, void *handle,
 }
 
 static void process_packet(verto_ctx *ctx, verto_ev *ev);
-static void accept_tcp_connection(verto_ctx *ctx, verto_ev *ev);
-static void process_tcp_connection_read(verto_ctx *ctx, verto_ev *ev);
-static void process_tcp_connection_write(verto_ctx *ctx, verto_ev *ev);
+static void accept_stream_connection(verto_ctx *ctx, verto_ev *ev);
+static void process_stream_connection_read(verto_ctx *ctx, verto_ev *ev);
+static void process_stream_connection_write(verto_ctx *ctx, verto_ev *ev);
 static void accept_rpc_connection(verto_ctx *ctx, verto_ev *ev);
 static void process_rpc_connection(verto_ctx *ctx, verto_ev *ev);
 
@@ -568,6 +605,8 @@ create_server_socket(struct sockaddr *addr, int type, const char *prog,
 
     *fd_out = -1;
 
+    if (addr->sa_family == AF_UNIX)
+        (void)unlink(sa2sun(addr)->sun_path);
     sock = socket(addr->sa_family, type, 0);
     if (sock == -1) {
         e = errno;
@@ -641,7 +680,8 @@ static const int bind_socktypes[] =
 {
     [UDP] = SOCK_DGRAM,
     [TCP] = SOCK_STREAM,
-    [RPC] = SOCK_STREAM
+    [RPC] = SOCK_STREAM,
+    [UNX] = SOCK_STREAM
 };
 
 /* An enum map containing conn_type (for struct connection) for each
@@ -650,7 +690,8 @@ static const enum conn_type bind_conn_types[] =
 {
     [UDP] = CONN_UDP,
     [TCP] = CONN_TCP_LISTENER,
-    [RPC] = CONN_RPC_LISTENER
+    [RPC] = CONN_RPC_LISTENER,
+    [UNX] = CONN_UNIXSOCK_LISTENER
 };
 
 /*
@@ -668,7 +709,7 @@ 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 tcp_listen_backlog, verto_callback vcb, enum conn_type ctype)
+             int listen_backlog, verto_callback vcb, enum conn_type ctype)
 {
     krb5_error_code ret;
     struct connection *conn;
@@ -687,16 +728,17 @@ setup_socket(struct bind_address *ba, struct sockaddr *sock_address,
     if (ret)
         goto cleanup;
 
-    /* Listen for backlogged connections on TCP sockets.  (For RPC sockets this
-     * will be done by svc_register().) */
-    if (ba->type == TCP && listen(sock, tcp_listen_backlog) != 0) {
+    /* 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 UDP and TCP listener sockets. */
+    /* Set non-blocking I/O for non-RPC listener sockets. */
     if (ba->type != RPC && setnbio(sock) != 0) {
         ret = errno;
         com_err(prog, errno,
@@ -780,18 +822,20 @@ cleanup:
  */
 static krb5_error_code
 setup_addresses(verto_ctx *ctx, void *handle, const char *prog,
-                int tcp_listen_backlog)
+                int listen_backlog)
 {
     /* An bind_type enum map for the verto callback functions. */
     static verto_callback *const verto_callbacks[] = {
         [UDP] = &process_packet,
-        [TCP] = &accept_tcp_connection,
-        [RPC] = &accept_rpc_connection
+        [TCP] = &accept_stream_connection,
+        [RPC] = &accept_rpc_connection,
+        [UNX] = &accept_stream_connection
     };
     krb5_error_code ret = 0;
     size_t i;
     int err, bound_any;
     struct bind_address addr;
+    struct sockaddr_un sun;
     struct addrinfo hints, *ai_list = NULL, *ai = NULL;
     verto_callback vcb;
     char addrbuf[128];
@@ -816,6 +860,28 @@ setup_addresses(verto_ctx *ctx, void *handle, const char *prog,
         addr = bind_addresses.data[i];
         hints.ai_socktype = bind_socktypes[addr.type];
 
+        if (addr.type == UNX) {
+            sun.sun_family = AF_UNIX;
+            if (strlcpy(sun.sun_path, addr.address, sizeof(sun.sun_path)) >=
+                sizeof(sun.sun_path)) {
+                ret = ENAMETOOLONG;
+                krb5_klog_syslog(LOG_ERR,
+                                 _("UNIX domain socket path too long: %s"),
+                                 addr.address);
+                goto cleanup;
+            }
+            ret = setup_socket(&addr, (struct sockaddr *)&sun, handle, prog,
+                               ctx, listen_backlog, verto_callbacks[addr.type],
+                               bind_conn_types[addr.type]);
+            if (ret) {
+                krb5_klog_syslog(LOG_ERR,
+                                 _("Failed setting up a UNIX socket (for %s)"),
+                                 addr.address);
+                goto cleanup;
+            }
+            continue;
+        }
+
         /* Call getaddrinfo, using a dummy port value. */
         err = getaddrinfo(addr.address, "0", &hints, &ai_list);
         if (err) {
@@ -846,7 +912,7 @@ setup_addresses(verto_ctx *ctx, void *handle, const char *prog,
             sa_setport(ai->ai_addr, addr.port);
 
             ret = setup_socket(&addr, ai->ai_addr, handle, prog, ctx,
-                               tcp_listen_backlog, verto_callbacks[addr.type],
+                               listen_backlog, verto_callbacks[addr.type],
                                bind_conn_types[addr.type]);
             if (ret) {
                 k5_print_addr(ai->ai_addr, addrbuf, sizeof(addrbuf));
@@ -876,7 +942,7 @@ cleanup:
 
 krb5_error_code
 loop_setup_network(verto_ctx *ctx, void *handle, const char *prog,
-                   int tcp_listen_backlog)
+                   int listen_backlog)
 {
     krb5_error_code ret;
     verto_ev *ev;
@@ -892,7 +958,7 @@ loop_setup_network(verto_ctx *ctx, void *handle, const char *prog,
     events.n = 0;
 
     krb5_klog_syslog(LOG_INFO, _("setting up network..."));
-    ret = setup_addresses(ctx, handle, prog, tcp_listen_backlog);
+    ret = setup_addresses(ctx, handle, prog, listen_backlog);
     if (ret) {
         com_err(prog, ret, _("Error setting up network"));
         exit(1);
@@ -1015,7 +1081,7 @@ process_packet(verto_ctx *ctx, verto_ev *ev)
 }
 
 static int
-kill_lru_tcp_or_rpc_connection(void *handle, verto_ev *newev)
+kill_lru_stream_connection(void *handle, verto_ev *newev)
 {
     struct connection *c = NULL, *oldest_c = NULL;
     verto_ev *ev, *oldest_ev = NULL;
@@ -1030,7 +1096,8 @@ kill_lru_tcp_or_rpc_connection(void *handle, verto_ev *newev)
         c = verto_get_private(ev);
         if (!c)
             continue;
-        if (c->type != CONN_TCP && c->type != CONN_RPC)
+        if (c->type != CONN_TCP && c->type != CONN_RPC &&
+            c->type != CONN_UNIXSOCK)
             continue;
         if (oldest_c == NULL
             || oldest_c->start_time > c->start_time) {
@@ -1040,7 +1107,7 @@ kill_lru_tcp_or_rpc_connection(void *handle, verto_ev *newev)
     }
     if (oldest_c != NULL) {
         krb5_klog_syslog(LOG_INFO, _("dropping %s fd %d from %s"),
-                         oldest_c->type == CONN_RPC ? "rpc" : "tcp",
+                         conn_type_names[oldest_c->type],
                          verto_get_fd(oldest_ev), oldest_c->addrbuf);
         if (oldest_c->type == CONN_RPC)
             oldest_c->rpc_force_close = 1;
@@ -1050,12 +1117,13 @@ kill_lru_tcp_or_rpc_connection(void *handle, verto_ev *newev)
 }
 
 static void
-accept_tcp_connection(verto_ctx *ctx, verto_ev *ev)
+accept_stream_connection(verto_ctx *ctx, verto_ev *ev)
 {
     int s;
     struct sockaddr_storage addr;
     socklen_t addrlen = sizeof(addr);
     struct connection *newconn, *conn;
+    enum conn_type ctype;
     verto_ev_flag flags;
     verto_ev *newev;
 
@@ -1070,16 +1138,31 @@ accept_tcp_connection(verto_ctx *ctx, verto_ev *ev)
         return;
     }
 #endif
-    setnbio(s), setnolinger(s), setkeepalive(s);
+    setnbio(s);
+    setnolinger(s);
+    if (addr.ss_family != AF_UNIX)
+        setkeepalive(s);
 
     flags = VERTO_EV_FLAG_IO_READ | VERTO_EV_FLAG_PERSIST;
-    if (add_fd(s, CONN_TCP, flags, conn->handle, conn->prog, ctx,
-               process_tcp_connection_read, &newev) != 0) {
+    ctype = (conn->type == CONN_TCP_LISTENER) ? CONN_TCP : CONN_UNIXSOCK;
+    if (add_fd(s, ctype, flags, conn->handle, conn->prog, ctx,
+               process_stream_connection_read, &newev) != 0) {
         close(s);
         return;
     }
     newconn = verto_get_private(newev);
 
+    if (addr.ss_family == AF_UNIX) {
+        /* accept() doesn't fill in sun_path as the client socket isn't bound.
+         * For logging purposes we will use the target address. */
+        addrlen = sizeof(addr);
+        if (getsockname(s, ss2sa(&addr), &addrlen) < 0) {
+            com_err(conn->prog, errno, _("Failed to get address for %d"), s);
+            close(s);
+            return;
+        }
+    }
+
     k5_print_addr_port(ss2sa(&addr), newconn->addrbuf,
                        sizeof(newconn->addrbuf));
     newconn->addr_s = addr;
@@ -1088,8 +1171,8 @@ accept_tcp_connection(verto_ctx *ctx, verto_ev *ev)
     newconn->buffer = malloc(newconn->bufsiz);
     newconn->start_time = time(0);
 
-    if (++tcp_or_rpc_data_counter > max_tcp_or_rpc_data_connections)
-        kill_lru_tcp_or_rpc_connection(conn->handle, newev);
+    if (++stream_data_counter > max_stream_data_connections)
+        kill_lru_stream_connection(conn->handle, newev);
 
     if (newconn->buffer == 0) {
         com_err(conn->prog, errno,
@@ -1112,7 +1195,7 @@ struct tcp_dispatch_state {
 };
 
 static void
-process_tcp_response(void *arg, krb5_error_code code, krb5_data *response)
+process_stream_response(void *arg, krb5_error_code code, krb5_data *response)
 {
     struct tcp_dispatch_state *state = arg;
     verto_ev *ev;
@@ -1132,14 +1215,14 @@ process_tcp_response(void *arg, krb5_error_code code, krb5_data *response)
     state->conn->sgnum = 2;
 
     ev = make_event(state->ctx, VERTO_EV_FLAG_IO_WRITE | VERTO_EV_FLAG_PERSIST,
-                    process_tcp_connection_write, state->sock, state->conn);
+                    process_stream_connection_write, state->sock, state->conn);
     if (ev) {
         free(state);
         return;
     }
 
 kill_tcp_connection:
-    tcp_or_rpc_data_counter--;
+    stream_data_counter--;
     free_connection(state->conn);
     close(state->sock);
     free(state);
@@ -1166,7 +1249,7 @@ prepare_for_dispatch(verto_ctx *ctx, verto_ev *ev)
 }
 
 static void
-process_tcp_connection_read(verto_ctx *ctx, verto_ev *ev)
+process_stream_connection_read(verto_ctx *ctx, verto_ev *ev)
 {
     struct tcp_dispatch_state *state = NULL;
     struct connection *conn = NULL;
@@ -1219,7 +1302,7 @@ process_tcp_connection_read(verto_ctx *ctx, verto_ev *ev)
                     krb5_free_data(get_context(conn->handle), response);
                     goto kill_tcp_connection;
                 }
-                process_tcp_response(state, 0, response);
+                process_stream_response(state, 0, response);
             }
         }
     } else {
@@ -1253,7 +1336,7 @@ process_tcp_connection_read(verto_ctx *ctx, verto_ev *ev)
         }
         dispatch(state->conn->handle, ss2sa(&state->local_saddr),
                  ss2sa(&conn->addr_s), &state->request, 1, ctx,
-                 process_tcp_response, state);
+                 process_stream_response, state);
     }
 
     return;
@@ -1263,7 +1346,7 @@ kill_tcp_connection:
 }
 
 static void
-process_tcp_connection_write(verto_ctx *ctx, verto_ev *ev)
+process_stream_connection_write(verto_ctx *ctx, verto_ev *ev)
 {
     struct connection *conn;
     SOCKET_WRITEV_TEMP tmp;
@@ -1377,8 +1460,8 @@ accept_rpc_connection(verto_ctx *ctx, verto_ev *ev)
         newconn->addrlen = addrlen;
         newconn->start_time = time(0);
 
-        if (++tcp_or_rpc_data_counter > max_tcp_or_rpc_data_connections)
-            kill_lru_tcp_or_rpc_connection(newconn->handle, newev);
+        if (++stream_data_counter > max_stream_data_connections)
+            kill_lru_stream_connection(newconn->handle, newev);
     }
 }
 
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index 63bc7a86c..1e7076d3c 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -119,6 +119,8 @@ initialize_prof_error_table
 k5_add_empty_pa_data
 k5_add_pa_data_element
 k5_add_pa_data_from_data
+k5_addr_directional_accept
+k5_addr_directional_init
 k5_alloc_pa_data
 k5_authind_decode
 k5_build_conf_principals
diff --git a/src/lib/krb5/os/addr.c b/src/lib/krb5/os/addr.c
index 1d8ae7501..e351ef800 100644
--- a/src/lib/krb5/os/addr.c
+++ b/src/lib/krb5/os/addr.c
@@ -33,8 +33,16 @@
 #include "k5-int.h"
 #include "socket-utils.h"
 
+const krb5_address k5_addr_directional_init = {
+    KV5M_ADDRESS, ADDRTYPE_DIRECTIONAL, 4, (uint8_t *)"\x00\x00\x00\x00"
+};
+const krb5_address k5_addr_directional_accept = {
+    KV5M_ADDRESS, ADDRTYPE_DIRECTIONAL, 4, (uint8_t *)"\x00\x00\x00\x01"
+};
+
 krb5_error_code
-k5_sockaddr_to_address(const struct sockaddr *sa, krb5_address *out)
+k5_sockaddr_to_address(const struct sockaddr *sa, krb5_boolean local_use,
+                       krb5_address *out)
 {
     if (sa->sa_family == AF_INET) {
         const struct sockaddr_in *sin = sa2sin(sa);
@@ -52,6 +60,13 @@ k5_sockaddr_to_address(const struct sockaddr *sa, krb5_address *out)
             out->length = sizeof(sin6->sin6_addr);
             out->contents = (uint8_t *)&sin6->sin6_addr;
         }
+#ifndef _WIN32
+    } else if (sa->sa_family == AF_UNIX && local_use) {
+        const struct sockaddr_un *sun = sa2sun(sa);
+        out->addrtype = ADDRTYPE_UNIXSOCK;
+        out->length = strlen(sun->sun_path);
+        out->contents = (uint8_t *)sun->sun_path;
+#endif
     } else {
         return KRB5_PROG_ATYPE_NOSUPP;
     }
@@ -66,6 +81,10 @@ k5_print_addr(const struct sockaddr *sa, char *buf, size_t len)
         if (getnameinfo(sa, sa_socklen(sa), buf, len, NULL, 0,
                         NI_NUMERICHOST | NI_NUMERICSERV) != 0)
             strlcpy(buf, "<unknown>", len);
+#ifndef _WIN32
+    } else if (sa->sa_family == AF_UNIX) {
+        strlcpy(buf, sa2sun(sa)->sun_path, len);
+#endif
     } else {
         strlcpy(buf, "<unknown>", len);
     }
diff --git a/src/lib/krb5/os/changepw.c b/src/lib/krb5/os/changepw.c
index bc132bc33..d32a12b63 100644
--- a/src/lib/krb5/os/changepw.c
+++ b/src/lib/krb5/os/changepw.c
@@ -143,6 +143,12 @@ kpasswd_sendto_msg_callback(SOCKET fd, void *data, krb5_data *message)
         local_kaddr.addrtype = ADDRTYPE_INET6;
         local_kaddr.length = sizeof(ss2sin6(&local_addr)->sin6_addr);
         local_kaddr.contents = (krb5_octet *) &ss2sin6(&local_addr)->sin6_addr;
+#ifndef _WIN32
+    } else if (local_addr.ss_family == AF_UNIX) {
+        /* There is no standard way to represent UNIX domain sockets.  Assume
+         * that the receiver will accept a directional address. */
+        local_kaddr = k5_addr_directional_init;
+#endif
     } else {
         code = krb5_os_localaddr(ctx->context, &addrs);
         if (code)
diff --git a/src/plugins/audit/j_dict.h b/src/plugins/audit/j_dict.h
index 65962e331..fd93ceb04 100644
--- a/src/plugins/audit/j_dict.h
+++ b/src/plugins/audit/j_dict.h
@@ -45,6 +45,7 @@
 #define AU_FROMADDR           "fromaddr"
 #define AU_TYPE               "type" /* used by fromaddr */
 #define AU_IP                 "ip" /* used by fromaddr */
+#define AU_PATH               "path" /* used by fromaddr */
 #define AU_SESS_ETYPE         "sess_etype"
 #define AU_SRV_ETYPE          "srv_etype"
 #define AU_REP_ETYPE          "rep_etype"
diff --git a/src/plugins/audit/kdc_j_encode.c b/src/plugins/audit/kdc_j_encode.c
index 0df258d76..31f308067 100755
--- a/src/plugins/audit/kdc_j_encode.c
+++ b/src/plugins/audit/kdc_j_encode.c
@@ -630,6 +630,17 @@ addr_to_obj(krb5_address *a, k5_json_object obj)
         ret = k5_json_object_set(obj, AU_IP, arr);
         if (ret)
             goto error;
+    } else if (a->addrtype == ADDRTYPE_UNIXSOCK) {
+        k5_json_string str = NULL;
+
+        ret = k5_json_string_create_len(a->contents, a->length, &str);
+        if (ret)
+            return ret;
+
+        ret = k5_json_object_set(obj, AU_PATH, str);
+        k5_json_release(str);
+        if (ret)
+            goto error;
     }
 
 error:


More information about the cvs-krb5 mailing list