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