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