xref: /netbsd-src/external/mpl/bind/dist/lib/dns/ssu_external.c (revision 2718af68c3efc72c9769069b5c7f9ed36f6b9def)
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