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