1 /* $NetBSD: ssu_external.c,v 1.8 2025/01/26 16:25:25 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 /* 17 * This implements external update-policy rules. This allows permission 18 * to update a zone to be checked by consulting an external daemon (e.g., 19 * kerberos). 20 */ 21 22 #include <errno.h> 23 #include <inttypes.h> 24 #include <stdbool.h> 25 #include <sys/socket.h> 26 #include <sys/un.h> 27 #include <unistd.h> 28 29 #include <isc/magic.h> 30 #include <isc/mem.h> 31 #include <isc/netaddr.h> 32 #include <isc/result.h> 33 #include <isc/strerr.h> 34 #include <isc/string.h> 35 #include <isc/util.h> 36 37 #include <dns/fixedname.h> 38 #include <dns/log.h> 39 #include <dns/name.h> 40 #include <dns/rdatatype.h> 41 #include <dns/ssu.h> 42 43 #include <dst/dst.h> 44 45 static void 46 ssu_e_log(int level, const char *fmt, ...) { 47 va_list ap; 48 49 va_start(ap, fmt); 50 isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_SECURITY, DNS_LOGMODULE_ZONE, 51 ISC_LOG_DEBUG(level), fmt, ap); 52 va_end(ap); 53 } 54 55 /* 56 * Connect to a UNIX domain socket. 57 */ 58 static int 59 ux_socket_connect(const char *path) { 60 int fd = -1; 61 struct sockaddr_un addr; 62 63 REQUIRE(path != NULL); 64 65 if (strlen(path) > sizeof(addr.sun_path)) { 66 ssu_e_log(3, 67 "ssu_external: socket path '%s' " 68 "longer than system maximum %zu", 69 path, sizeof(addr.sun_path)); 70 return -1; 71 } 72 73 memset(&addr, 0, sizeof(addr)); 74 addr.sun_family = AF_UNIX; 75 strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); 76 77 fd = socket(AF_UNIX, SOCK_STREAM, 0); 78 if (fd == -1) { 79 char strbuf[ISC_STRERRORSIZE]; 80 strerror_r(errno, strbuf, sizeof(strbuf)); 81 ssu_e_log(3, "ssu_external: unable to create socket - %s", 82 strbuf); 83 return -1; 84 } 85 86 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 87 char strbuf[ISC_STRERRORSIZE]; 88 strerror_r(errno, strbuf, sizeof(strbuf)); 89 ssu_e_log(3, 90 "ssu_external: unable to connect to " 91 "socket '%s' - %s", 92 path, strbuf); 93 close(fd); 94 return -1; 95 } 96 return fd; 97 } 98 99 /* Change this version if you update the format of the request */ 100 #define SSU_EXTERNAL_VERSION 1 101 102 /* 103 * Perform an update-policy rule check against an external application 104 * over a socket. 105 * 106 * This currently only supports local: for unix domain datagram sockets. 107 * 108 * Note that by using a datagram socket and creating a new socket each 109 * time we avoid the need for locking and allow for parallel access to 110 * the authorization server. 111 */ 112 bool 113 dns_ssu_external_match(const dns_name_t *identity, const dns_name_t *signer, 114 const dns_name_t *name, const isc_netaddr_t *tcpaddr, 115 dns_rdatatype_t type, const dst_key_t *key, 116 isc_mem_t *mctx) { 117 char b_identity[DNS_NAME_FORMATSIZE]; 118 char b_signer[DNS_NAME_FORMATSIZE]; 119 char b_name[DNS_NAME_FORMATSIZE]; 120 char b_addr[ISC_NETADDR_FORMATSIZE]; 121 char b_type[DNS_RDATATYPE_FORMATSIZE]; 122 char b_key[DST_KEY_FORMATSIZE]; 123 isc_buffer_t *tkey_token = NULL; 124 int fd; 125 const char *sock_path; 126 unsigned int req_len; 127 isc_region_t token_region = { NULL, 0 }; 128 unsigned char *data; 129 isc_buffer_t buf; 130 uint32_t token_len = 0; 131 uint32_t reply; 132 ssize_t ret; 133 134 /* The identity contains local:/path/to/socket */ 135 dns_name_format(identity, b_identity, sizeof(b_identity)); 136 137 /* For now only local: is supported */ 138 if (strncmp(b_identity, "local:", 6) != 0) { 139 ssu_e_log(3, "ssu_external: invalid socket path '%s'", 140 b_identity); 141 return false; 142 } 143 sock_path = &b_identity[6]; 144 145 fd = ux_socket_connect(sock_path); 146 if (fd == -1) { 147 return false; 148 } 149 150 if (key != NULL) { 151 dst_key_format(key, b_key, sizeof(b_key)); 152 tkey_token = dst_key_tkeytoken(key); 153 } else { 154 b_key[0] = 0; 155 } 156 157 if (tkey_token != NULL) { 158 isc_buffer_region(tkey_token, &token_region); 159 token_len = token_region.length; 160 } 161 162 /* Format the request elements */ 163 if (signer != NULL) { 164 dns_name_format(signer, b_signer, sizeof(b_signer)); 165 } else { 166 b_signer[0] = 0; 167 } 168 169 dns_name_format(name, b_name, sizeof(b_name)); 170 171 if (tcpaddr != NULL) { 172 isc_netaddr_format(tcpaddr, b_addr, sizeof(b_addr)); 173 } else { 174 b_addr[0] = 0; 175 } 176 177 dns_rdatatype_format(type, b_type, sizeof(b_type)); 178 179 /* Work out how big the request will be */ 180 req_len = sizeof(uint32_t) + /* Format version */ 181 sizeof(uint32_t) + /* Length */ 182 strlen(b_signer) + 1 + /* Signer */ 183 strlen(b_name) + 1 + /* Name */ 184 strlen(b_addr) + 1 + /* Address */ 185 strlen(b_type) + 1 + /* Type */ 186 strlen(b_key) + 1 + /* Key */ 187 sizeof(uint32_t) + /* tkey_token length */ 188 token_len; /* tkey_token */ 189 190 /* format the buffer */ 191 data = isc_mem_allocate(mctx, req_len); 192 193 isc_buffer_init(&buf, data, req_len); 194 isc_buffer_putuint32(&buf, SSU_EXTERNAL_VERSION); 195 isc_buffer_putuint32(&buf, req_len); 196 197 /* Strings must be null-terminated */ 198 isc_buffer_putstr(&buf, b_signer); 199 isc_buffer_putuint8(&buf, 0); 200 isc_buffer_putstr(&buf, b_name); 201 isc_buffer_putuint8(&buf, 0); 202 isc_buffer_putstr(&buf, b_addr); 203 isc_buffer_putuint8(&buf, 0); 204 isc_buffer_putstr(&buf, b_type); 205 isc_buffer_putuint8(&buf, 0); 206 isc_buffer_putstr(&buf, b_key); 207 isc_buffer_putuint8(&buf, 0); 208 209 isc_buffer_putuint32(&buf, token_len); 210 if (tkey_token && token_len != 0) { 211 isc_buffer_putmem(&buf, token_region.base, token_len); 212 } 213 214 ENSURE(isc_buffer_availablelength(&buf) == 0); 215 216 /* Send the request */ 217 ret = write(fd, data, req_len); 218 isc_mem_free(mctx, data); 219 if (ret != (ssize_t)req_len) { 220 char strbuf[ISC_STRERRORSIZE]; 221 strerror_r(errno, strbuf, sizeof(strbuf)); 222 ssu_e_log(3, "ssu_external: unable to send request - %s", 223 strbuf); 224 close(fd); 225 return false; 226 } 227 228 /* Receive the reply */ 229 ret = read(fd, &reply, sizeof(uint32_t)); 230 if (ret != (ssize_t)sizeof(uint32_t)) { 231 char strbuf[ISC_STRERRORSIZE]; 232 strerror_r(errno, strbuf, sizeof(strbuf)); 233 ssu_e_log(3, "ssu_external: unable to receive reply - %s", 234 strbuf); 235 close(fd); 236 return false; 237 } 238 239 close(fd); 240 241 reply = ntohl(reply); 242 243 if (reply == 0) { 244 ssu_e_log(3, "ssu_external: denied external auth for '%s'", 245 b_name); 246 return false; 247 } else if (reply == 1) { 248 ssu_e_log(3, "ssu_external: allowed external auth for '%s'", 249 b_name); 250 return true; 251 } 252 253 ssu_e_log(3, "ssu_external: invalid reply 0x%08x", reply); 254 255 return false; 256 } 257