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