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