Coverage Report

Created: 2026-07-16 07:05

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