1/*
2 * Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $Id$ */
18
19/*
20 * This implements external update-policy rules.  This allows permission
21 * to update a zone to be checked by consulting an external daemon (e.g.,
22 * kerberos).
23 */
24
25#include <config.h>
26#include <errno.h>
27#include <unistd.h>
28
29#ifdef ISC_PLATFORM_HAVESYSUNH
30#include <sys/socket.h>
31#include <sys/un.h>
32#endif
33
34#include <isc/magic.h>
35#include <isc/mem.h>
36#include <isc/netaddr.h>
37#include <isc/result.h>
38#include <isc/string.h>
39#include <isc/util.h>
40#include <isc/strerror.h>
41
42#include <dns/fixedname.h>
43#include <dns/name.h>
44#include <dns/ssu.h>
45#include <dns/log.h>
46#include <dns/rdatatype.h>
47
48#include <dst/dst.h>
49
50
51static void
52ssu_e_log(int level, const char *fmt, ...) {
53	va_list ap;
54
55	va_start(ap, fmt);
56	isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_SECURITY,
57		       DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(level), fmt, ap);
58	va_end(ap);
59}
60
61
62/*
63 * Connect to a UNIX domain socket.
64 */
65static int
66ux_socket_connect(const char *path) {
67	int fd = -1;
68#ifdef ISC_PLATFORM_HAVESYSUNH
69	struct sockaddr_un addr;
70
71	REQUIRE(path != NULL);
72
73	if (strlen(path) > sizeof(addr.sun_path)) {
74		ssu_e_log(3, "ssu_external: socket path '%s' "
75			     "longer than system maximum %u",
76			  path, sizeof(addr.sun_path));
77		return (-1);
78	}
79
80	memset(&addr, 0, sizeof(addr));
81	addr.sun_family = AF_UNIX;
82	strncpy(addr.sun_path, path, sizeof(addr.sun_path));
83
84	fd = socket(AF_UNIX, SOCK_STREAM, 0);
85	if (fd == -1) {
86		char strbuf[ISC_STRERRORSIZE];
87		isc__strerror(errno, strbuf, sizeof(strbuf));
88		ssu_e_log(3, "ssu_external: unable to create socket - %s",
89			  strbuf);
90		return (-1);
91	}
92
93	if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
94		char strbuf[ISC_STRERRORSIZE];
95		isc__strerror(errno, strbuf, sizeof(strbuf));
96		ssu_e_log(3, "ssu_external: unable to connect to "
97			     "socket '%s' - %s",
98			  path, strbuf);
99		close(fd);
100		return (-1);
101	}
102#endif
103	return (fd);
104}
105
106/* Change this version if you update the format of the request */
107#define SSU_EXTERNAL_VERSION 1
108
109/*
110 * Perform an update-policy rule check against an external application
111 * over a socket.
112 *
113 * This currently only supports local: for unix domain datagram sockets.
114 *
115 * Note that by using a datagram socket and creating a new socket each
116 * time we avoid the need for locking and allow for parallel access to
117 * the authorization server.
118 */
119isc_boolean_t
120dns_ssu_external_match(dns_name_t *identity,
121		       dns_name_t *signer, dns_name_t *name,
122		       isc_netaddr_t *tcpaddr, dns_rdatatype_t type,
123		       const dst_key_t *key, isc_mem_t *mctx)
124{
125	char b_identity[DNS_NAME_FORMATSIZE];
126	char b_signer[DNS_NAME_FORMATSIZE];
127	char b_name[DNS_NAME_FORMATSIZE];
128	char b_addr[ISC_NETADDR_FORMATSIZE];
129	char b_type[DNS_RDATATYPE_FORMATSIZE];
130	char b_key[DST_KEY_FORMATSIZE];
131	isc_buffer_t *tkey_token = NULL;
132	int fd;
133	const char *sock_path;
134	size_t req_len;
135	isc_region_t token_region;
136	unsigned char *data;
137	isc_buffer_t buf;
138	isc_uint32_t token_len = 0;
139	isc_uint32_t reply;
140	ssize_t ret;
141
142	/* The identity contains local:/path/to/socket */
143	dns_name_format(identity, b_identity, sizeof(b_identity));
144
145	/* For now only local: is supported */
146	if (strncmp(b_identity, "local:", 6) != 0) {
147		ssu_e_log(3, "ssu_external: invalid socket path '%s'",
148			  b_identity);
149		return (ISC_FALSE);
150	}
151	sock_path = &b_identity[6];
152
153	fd = ux_socket_connect(sock_path);
154	if (fd == -1)
155		return (ISC_FALSE);
156
157	if (key != NULL) {
158		dst_key_format(key, b_key, sizeof(b_key));
159		tkey_token = dst_key_tkeytoken(key);
160	} else
161		b_key[0] = 0;
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	dns_name_format(name, b_name, sizeof(b_name));
175
176	if (tcpaddr != NULL)
177		isc_netaddr_format(tcpaddr, b_addr, sizeof(b_addr));
178	else
179		b_addr[0] = 0;
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(isc_uint32_t)     + /* Format version */
185		  sizeof(isc_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(isc_uint32_t)     + /* tkey_token length */
192		  token_len;             /* tkey_token */
193
194
195	/* format the buffer */
196	data = isc_mem_allocate(mctx, req_len);
197	if (data == NULL) {
198		close(fd);
199		return (ISC_FALSE);
200	}
201
202	isc_buffer_init(&buf, data, req_len);
203	isc_buffer_putuint32(&buf, SSU_EXTERNAL_VERSION);
204	isc_buffer_putuint32(&buf, req_len);
205
206	/* Strings must be null-terminated */
207	isc_buffer_putstr(&buf, b_signer);
208	isc_buffer_putuint8(&buf, 0);
209	isc_buffer_putstr(&buf, b_name);
210	isc_buffer_putuint8(&buf, 0);
211	isc_buffer_putstr(&buf, b_addr);
212	isc_buffer_putuint8(&buf, 0);
213	isc_buffer_putstr(&buf, b_type);
214	isc_buffer_putuint8(&buf, 0);
215	isc_buffer_putstr(&buf, b_key);
216	isc_buffer_putuint8(&buf, 0);
217
218	isc_buffer_putuint32(&buf, token_len);
219	if (tkey_token && token_len != 0)
220		isc_buffer_putmem(&buf, token_region.base, token_len);
221
222	ENSURE(isc_buffer_availablelength(&buf) == 0);
223
224	/* Send the request */
225	ret = write(fd, data, req_len);
226	isc_mem_free(mctx, data);
227	if (ret != (ssize_t) req_len) {
228		char strbuf[ISC_STRERRORSIZE];
229		isc__strerror(errno, strbuf, sizeof(strbuf));
230		ssu_e_log(3, "ssu_external: unable to send request - %s",
231			  strbuf);
232		close(fd);
233		return (ISC_FALSE);
234	}
235
236	/* Receive the reply */
237	ret = read(fd, &reply, sizeof(isc_uint32_t));
238	if (ret != (ssize_t) sizeof(isc_uint32_t)) {
239		char strbuf[ISC_STRERRORSIZE];
240		isc__strerror(errno, strbuf, sizeof(strbuf));
241		ssu_e_log(3, "ssu_external: unable to receive reply - %s",
242			  strbuf);
243		close(fd);
244		return (ISC_FALSE);
245	}
246
247	close(fd);
248
249	reply = ntohl(reply);
250
251	if (reply == 0) {
252		ssu_e_log(3, "ssu_external: denied external auth for '%s'",
253			  b_name);
254		return (ISC_FALSE);
255	} else if (reply == 1) {
256		ssu_e_log(3, "ssu_external: allowed external auth for '%s'",
257			  b_name);
258		return (ISC_TRUE);
259	}
260
261	ssu_e_log(3, "ssu_external: invalid reply 0x%08x", reply);
262
263	return (ISC_FALSE);
264}
265