1224090Sdougb/* 2262706Serwin * Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC") 3224090Sdougb * 4224090Sdougb * Permission to use, copy, modify, and/or distribute this software for any 5224090Sdougb * purpose with or without fee is hereby granted, provided that the above 6224090Sdougb * copyright notice and this permission notice appear in all copies. 7224090Sdougb * 8224090Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9224090Sdougb * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10224090Sdougb * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11224090Sdougb * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12224090Sdougb * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13224090Sdougb * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14224090Sdougb * PERFORMANCE OF THIS SOFTWARE. 15224090Sdougb */ 16224090Sdougb 17234010Sdougb/* $Id$ */ 18224090Sdougb 19224090Sdougb/* 20224090Sdougb * This implements external update-policy rules. This allows permission 21224090Sdougb * to update a zone to be checked by consulting an external daemon (e.g., 22224090Sdougb * kerberos). 23224090Sdougb */ 24224090Sdougb 25224090Sdougb#include <config.h> 26224090Sdougb#include <errno.h> 27224090Sdougb#include <unistd.h> 28224090Sdougb 29224090Sdougb#ifdef ISC_PLATFORM_HAVESYSUNH 30224090Sdougb#include <sys/socket.h> 31224090Sdougb#include <sys/un.h> 32224090Sdougb#endif 33224090Sdougb 34224090Sdougb#include <isc/magic.h> 35224090Sdougb#include <isc/mem.h> 36224090Sdougb#include <isc/netaddr.h> 37224090Sdougb#include <isc/result.h> 38224090Sdougb#include <isc/string.h> 39224090Sdougb#include <isc/util.h> 40224090Sdougb#include <isc/strerror.h> 41224090Sdougb 42224090Sdougb#include <dns/fixedname.h> 43224090Sdougb#include <dns/name.h> 44224090Sdougb#include <dns/ssu.h> 45224090Sdougb#include <dns/log.h> 46224090Sdougb#include <dns/rdatatype.h> 47224090Sdougb 48224090Sdougb#include <dst/dst.h> 49224090Sdougb 50224090Sdougb 51224090Sdougbstatic void 52224090Sdougbssu_e_log(int level, const char *fmt, ...) { 53224090Sdougb va_list ap; 54224090Sdougb 55224090Sdougb va_start(ap, fmt); 56224090Sdougb isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_SECURITY, 57224090Sdougb DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(level), fmt, ap); 58224090Sdougb va_end(ap); 59224090Sdougb} 60224090Sdougb 61224090Sdougb 62224090Sdougb/* 63224090Sdougb * Connect to a UNIX domain socket. 64224090Sdougb */ 65224090Sdougbstatic int 66224090Sdougbux_socket_connect(const char *path) { 67224090Sdougb int fd = -1; 68224090Sdougb#ifdef ISC_PLATFORM_HAVESYSUNH 69224090Sdougb struct sockaddr_un addr; 70224090Sdougb 71224090Sdougb REQUIRE(path != NULL); 72224090Sdougb 73224090Sdougb if (strlen(path) > sizeof(addr.sun_path)) { 74224090Sdougb ssu_e_log(3, "ssu_external: socket path '%s' " 75224090Sdougb "longer than system maximum %u", 76224090Sdougb path, sizeof(addr.sun_path)); 77224090Sdougb return (-1); 78224090Sdougb } 79224090Sdougb 80224090Sdougb memset(&addr, 0, sizeof(addr)); 81224090Sdougb addr.sun_family = AF_UNIX; 82254402Serwin strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); 83224090Sdougb 84224090Sdougb fd = socket(AF_UNIX, SOCK_STREAM, 0); 85224090Sdougb if (fd == -1) { 86224090Sdougb char strbuf[ISC_STRERRORSIZE]; 87224090Sdougb isc__strerror(errno, strbuf, sizeof(strbuf)); 88224090Sdougb ssu_e_log(3, "ssu_external: unable to create socket - %s", 89224090Sdougb strbuf); 90224090Sdougb return (-1); 91224090Sdougb } 92224090Sdougb 93224090Sdougb if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 94224090Sdougb char strbuf[ISC_STRERRORSIZE]; 95224090Sdougb isc__strerror(errno, strbuf, sizeof(strbuf)); 96224090Sdougb ssu_e_log(3, "ssu_external: unable to connect to " 97224090Sdougb "socket '%s' - %s", 98224090Sdougb path, strbuf); 99224090Sdougb close(fd); 100224090Sdougb return (-1); 101224090Sdougb } 102224090Sdougb#endif 103224090Sdougb return (fd); 104224090Sdougb} 105224090Sdougb 106224090Sdougb/* Change this version if you update the format of the request */ 107224090Sdougb#define SSU_EXTERNAL_VERSION 1 108224090Sdougb 109224090Sdougb/* 110224090Sdougb * Perform an update-policy rule check against an external application 111224090Sdougb * over a socket. 112224090Sdougb * 113224090Sdougb * This currently only supports local: for unix domain datagram sockets. 114224090Sdougb * 115224090Sdougb * Note that by using a datagram socket and creating a new socket each 116224090Sdougb * time we avoid the need for locking and allow for parallel access to 117224090Sdougb * the authorization server. 118224090Sdougb */ 119224090Sdougbisc_boolean_t 120224090Sdougbdns_ssu_external_match(dns_name_t *identity, 121224090Sdougb dns_name_t *signer, dns_name_t *name, 122224090Sdougb isc_netaddr_t *tcpaddr, dns_rdatatype_t type, 123224090Sdougb const dst_key_t *key, isc_mem_t *mctx) 124224090Sdougb{ 125224090Sdougb char b_identity[DNS_NAME_FORMATSIZE]; 126224090Sdougb char b_signer[DNS_NAME_FORMATSIZE]; 127224090Sdougb char b_name[DNS_NAME_FORMATSIZE]; 128224090Sdougb char b_addr[ISC_NETADDR_FORMATSIZE]; 129224090Sdougb char b_type[DNS_RDATATYPE_FORMATSIZE]; 130224090Sdougb char b_key[DST_KEY_FORMATSIZE]; 131225361Sdougb isc_buffer_t *tkey_token = NULL; 132224090Sdougb int fd; 133224090Sdougb const char *sock_path; 134262706Serwin unsigned int req_len; 135224090Sdougb isc_region_t token_region; 136224090Sdougb unsigned char *data; 137224090Sdougb isc_buffer_t buf; 138224090Sdougb isc_uint32_t token_len = 0; 139224090Sdougb isc_uint32_t reply; 140224090Sdougb ssize_t ret; 141224090Sdougb 142224090Sdougb /* The identity contains local:/path/to/socket */ 143224090Sdougb dns_name_format(identity, b_identity, sizeof(b_identity)); 144224090Sdougb 145224090Sdougb /* For now only local: is supported */ 146224090Sdougb if (strncmp(b_identity, "local:", 6) != 0) { 147224090Sdougb ssu_e_log(3, "ssu_external: invalid socket path '%s'", 148224090Sdougb b_identity); 149224090Sdougb return (ISC_FALSE); 150224090Sdougb } 151224090Sdougb sock_path = &b_identity[6]; 152224090Sdougb 153224090Sdougb fd = ux_socket_connect(sock_path); 154224090Sdougb if (fd == -1) 155224090Sdougb return (ISC_FALSE); 156224090Sdougb 157225361Sdougb if (key != NULL) { 158225361Sdougb dst_key_format(key, b_key, sizeof(b_key)); 159225361Sdougb tkey_token = dst_key_tkeytoken(key); 160225361Sdougb } else 161225361Sdougb b_key[0] = 0; 162224090Sdougb 163225361Sdougb if (tkey_token != NULL) { 164225361Sdougb isc_buffer_region(tkey_token, &token_region); 165225361Sdougb token_len = token_region.length; 166225361Sdougb } 167225361Sdougb 168224090Sdougb /* Format the request elements */ 169225361Sdougb if (signer != NULL) 170224090Sdougb dns_name_format(signer, b_signer, sizeof(b_signer)); 171224090Sdougb else 172224090Sdougb b_signer[0] = 0; 173224090Sdougb 174224090Sdougb dns_name_format(name, b_name, sizeof(b_name)); 175224090Sdougb 176225361Sdougb if (tcpaddr != NULL) 177224090Sdougb isc_netaddr_format(tcpaddr, b_addr, sizeof(b_addr)); 178224090Sdougb else 179224090Sdougb b_addr[0] = 0; 180224090Sdougb 181224090Sdougb dns_rdatatype_format(type, b_type, sizeof(b_type)); 182224090Sdougb 183224090Sdougb /* Work out how big the request will be */ 184224090Sdougb req_len = sizeof(isc_uint32_t) + /* Format version */ 185224090Sdougb sizeof(isc_uint32_t) + /* Length */ 186224090Sdougb strlen(b_signer) + 1 + /* Signer */ 187224090Sdougb strlen(b_name) + 1 + /* Name */ 188224090Sdougb strlen(b_addr) + 1 + /* Address */ 189224090Sdougb strlen(b_type) + 1 + /* Type */ 190224090Sdougb strlen(b_key) + 1 + /* Key */ 191224090Sdougb sizeof(isc_uint32_t) + /* tkey_token length */ 192224090Sdougb token_len; /* tkey_token */ 193224090Sdougb 194224090Sdougb 195224090Sdougb /* format the buffer */ 196224090Sdougb data = isc_mem_allocate(mctx, req_len); 197224090Sdougb if (data == NULL) { 198224090Sdougb close(fd); 199224090Sdougb return (ISC_FALSE); 200224090Sdougb } 201224090Sdougb 202224090Sdougb isc_buffer_init(&buf, data, req_len); 203224090Sdougb isc_buffer_putuint32(&buf, SSU_EXTERNAL_VERSION); 204224090Sdougb isc_buffer_putuint32(&buf, req_len); 205224090Sdougb 206224090Sdougb /* Strings must be null-terminated */ 207224090Sdougb isc_buffer_putstr(&buf, b_signer); 208224090Sdougb isc_buffer_putuint8(&buf, 0); 209224090Sdougb isc_buffer_putstr(&buf, b_name); 210224090Sdougb isc_buffer_putuint8(&buf, 0); 211224090Sdougb isc_buffer_putstr(&buf, b_addr); 212224090Sdougb isc_buffer_putuint8(&buf, 0); 213224090Sdougb isc_buffer_putstr(&buf, b_type); 214224090Sdougb isc_buffer_putuint8(&buf, 0); 215224090Sdougb isc_buffer_putstr(&buf, b_key); 216224090Sdougb isc_buffer_putuint8(&buf, 0); 217224090Sdougb 218224090Sdougb isc_buffer_putuint32(&buf, token_len); 219224090Sdougb if (tkey_token && token_len != 0) 220224090Sdougb isc_buffer_putmem(&buf, token_region.base, token_len); 221224090Sdougb 222224090Sdougb ENSURE(isc_buffer_availablelength(&buf) == 0); 223224090Sdougb 224224090Sdougb /* Send the request */ 225224090Sdougb ret = write(fd, data, req_len); 226224090Sdougb isc_mem_free(mctx, data); 227224090Sdougb if (ret != (ssize_t) req_len) { 228224090Sdougb char strbuf[ISC_STRERRORSIZE]; 229224090Sdougb isc__strerror(errno, strbuf, sizeof(strbuf)); 230224090Sdougb ssu_e_log(3, "ssu_external: unable to send request - %s", 231224090Sdougb strbuf); 232224090Sdougb close(fd); 233224090Sdougb return (ISC_FALSE); 234224090Sdougb } 235224090Sdougb 236224090Sdougb /* Receive the reply */ 237224090Sdougb ret = read(fd, &reply, sizeof(isc_uint32_t)); 238224090Sdougb if (ret != (ssize_t) sizeof(isc_uint32_t)) { 239224090Sdougb char strbuf[ISC_STRERRORSIZE]; 240224090Sdougb isc__strerror(errno, strbuf, sizeof(strbuf)); 241224090Sdougb ssu_e_log(3, "ssu_external: unable to receive reply - %s", 242224090Sdougb strbuf); 243224090Sdougb close(fd); 244224090Sdougb return (ISC_FALSE); 245224090Sdougb } 246224090Sdougb 247224090Sdougb close(fd); 248224090Sdougb 249224090Sdougb reply = ntohl(reply); 250224090Sdougb 251224090Sdougb if (reply == 0) { 252224090Sdougb ssu_e_log(3, "ssu_external: denied external auth for '%s'", 253224090Sdougb b_name); 254224090Sdougb return (ISC_FALSE); 255224090Sdougb } else if (reply == 1) { 256224090Sdougb ssu_e_log(3, "ssu_external: allowed external auth for '%s'", 257224090Sdougb b_name); 258224090Sdougb return (ISC_TRUE); 259224090Sdougb } 260224090Sdougb 261224090Sdougb ssu_e_log(3, "ssu_external: invalid reply 0x%08x", reply); 262224090Sdougb 263224090Sdougb return (ISC_FALSE); 264224090Sdougb} 265