1 /* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2
3 This program is free software: you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation, either version 3 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17 #include "knot/updates/acl.h"
18 #include "contrib/wire_ctx.h"
19
match_type(uint16_t type,conf_val_t * types)20 static bool match_type(uint16_t type, conf_val_t *types)
21 {
22 if (types == NULL) {
23 return true;
24 }
25
26 conf_val_reset(types);
27 while (types->code == KNOT_EOK) {
28 if (type == knot_wire_read_u64(types->data)) {
29 return true;
30 }
31 conf_val_next(types);
32 }
33
34 return false;
35 }
36
match_name(const knot_dname_t * rr_owner,const knot_dname_t * name,acl_update_owner_match_t match)37 static bool match_name(const knot_dname_t *rr_owner, const knot_dname_t *name,
38 acl_update_owner_match_t match)
39 {
40 if (name == NULL) {
41 return true;
42 }
43
44 int ret = knot_dname_in_bailiwick(rr_owner, name);
45 switch (match) {
46 case ACL_UPDATE_MATCH_SUBEQ:
47 return (ret >= 0);
48 case ACL_UPDATE_MATCH_EQ:
49 return (ret == 0);
50 case ACL_UPDATE_MATCH_SUB:
51 return (ret > 0);
52 default:
53 return false;
54 }
55 }
56
match_names(const knot_dname_t * rr_owner,const knot_dname_t * zone_name,conf_val_t * names,acl_update_owner_match_t match)57 static bool match_names(const knot_dname_t *rr_owner, const knot_dname_t *zone_name,
58 conf_val_t *names, acl_update_owner_match_t match)
59 {
60 if (names == NULL) {
61 return true;
62 }
63
64 conf_val_reset(names);
65 while (names->code == KNOT_EOK) {
66 knot_dname_storage_t full_name;
67 size_t len;
68 const uint8_t *name = conf_data(names, &len);
69 if (name[len - 1] != '\0') {
70 // Append zone name if non-FQDN.
71 wire_ctx_t ctx = wire_ctx_init(full_name, sizeof(full_name));
72 wire_ctx_write(&ctx, name, len);
73 wire_ctx_write(&ctx, zone_name, knot_dname_size(zone_name));
74 if (ctx.error != KNOT_EOK) {
75 return false;
76 }
77 name = full_name;
78 }
79 if (match_name(rr_owner, name, match)) {
80 return true;
81 }
82 conf_val_next(names);
83 }
84
85 return false;
86 }
87
update_match(conf_t * conf,conf_val_t * acl,knot_dname_t * key_name,const knot_dname_t * zone_name,knot_pkt_t * query)88 static bool update_match(conf_t *conf, conf_val_t *acl, knot_dname_t *key_name,
89 const knot_dname_t *zone_name, knot_pkt_t *query)
90 {
91 if (query == NULL) {
92 return true;
93 }
94
95 conf_val_t val_types = conf_id_get(conf, C_ACL, C_UPDATE_TYPE, acl);
96 conf_val_t *types = (conf_val_count(&val_types) > 0) ? &val_types : NULL;
97
98 conf_val_t val = conf_id_get(conf, C_ACL, C_UPDATE_OWNER, acl);
99 acl_update_owner_t owner = conf_opt(&val);
100
101 /* Return if no specific requirements configured. */
102 if (types == NULL && owner == ACL_UPDATE_OWNER_NONE) {
103 return true;
104 }
105
106 acl_update_owner_match_t match = ACL_UPDATE_MATCH_SUBEQ;
107 if (owner != ACL_UPDATE_OWNER_NONE) {
108 val = conf_id_get(conf, C_ACL, C_UPDATE_OWNER_MATCH, acl);
109 match = conf_opt(&val);
110 }
111
112 conf_val_t *names = NULL;
113 conf_val_t val_names;
114 if (owner == ACL_UPDATE_OWNER_NAME) {
115 val_names = conf_id_get(conf, C_ACL, C_UPDATE_OWNER_NAME, acl);
116 if (conf_val_count(&val_names) > 0) {
117 names = &val_names;
118 }
119 }
120
121 /* Updated RRs are contained in the Authority section of the query
122 * (RFC 2136 Section 2.2)
123 */
124 uint16_t pos = query->sections[KNOT_AUTHORITY].pos;
125 uint16_t count = query->sections[KNOT_AUTHORITY].count;
126
127 for (int i = pos; i < pos + count; i++) {
128 knot_rrset_t *rr = &query->rr[i];
129 if (!match_type(rr->type, types)) {
130 return false;
131 }
132
133 switch (owner) {
134 case ACL_UPDATE_OWNER_NAME:
135 if (!match_names(rr->owner, zone_name, names, match)) {
136 return false;
137 }
138 break;
139 case ACL_UPDATE_OWNER_KEY:
140 if (!match_name(rr->owner, key_name, match)) {
141 return false;
142 }
143 break;
144 case ACL_UPDATE_OWNER_ZONE:
145 if (!match_name(rr->owner, zone_name, match)) {
146 return false;
147 }
148 break;
149 default:
150 break;
151 }
152 }
153
154 return true;
155 }
156
check_addr_key(conf_t * conf,conf_val_t * addr_val,conf_val_t * key_val,bool remote,const struct sockaddr_storage * addr,const knot_tsig_key_t * tsig,bool deny)157 static bool check_addr_key(conf_t *conf, conf_val_t *addr_val, conf_val_t *key_val,
158 bool remote, const struct sockaddr_storage *addr,
159 const knot_tsig_key_t *tsig, bool deny)
160 {
161 /* Check if the address matches the acl address list or remote addresses. */
162 if (addr_val->code != KNOT_ENOENT) {
163 if (remote) {
164 if (!conf_addr_match(addr_val, addr)) {
165 return false;
166 }
167 } else {
168 if (!conf_addr_range_match(addr_val, addr)) {
169 return false;
170 }
171 }
172 }
173
174 /* Check if the key matches the acl key list or remote key. */
175 while (key_val->code == KNOT_EOK) {
176 /* No key provided, but required. */
177 if (tsig->name == NULL) {
178 goto next_key;
179 }
180
181 /* Compare key names (both in lower-case). */
182 const knot_dname_t *key_name = conf_dname(key_val);
183 if (!knot_dname_is_equal(key_name, tsig->name)) {
184 goto next_key;
185 }
186
187 /* Compare key algorithms. */
188 conf_val_t alg_val = conf_id_get(conf, C_KEY, C_ALG, key_val);
189 if (conf_opt(&alg_val) != tsig->algorithm) {
190 goto next_key;
191 }
192
193 break;
194 next_key:
195 if (remote) {
196 assert(!(key_val->item->flags & YP_FMULTI));
197 key_val->code = KNOT_EOF;
198 break;
199 } else {
200 assert(key_val->item->flags & YP_FMULTI);
201 conf_val_next(key_val);
202 }
203 }
204 switch (key_val->code) {
205 case KNOT_EOK:
206 // Key match.
207 break;
208 case KNOT_ENOENT:
209 // Empty list without key provided or denied.
210 if (tsig->name == NULL || deny) {
211 break;
212 }
213 // FALLTHROUGH
214 default:
215 return false;
216 }
217
218 return true;
219 }
220
acl_allowed(conf_t * conf,conf_val_t * acl,acl_action_t action,const struct sockaddr_storage * addr,knot_tsig_key_t * tsig,const knot_dname_t * zone_name,knot_pkt_t * query)221 bool acl_allowed(conf_t *conf, conf_val_t *acl, acl_action_t action,
222 const struct sockaddr_storage *addr, knot_tsig_key_t *tsig,
223 const knot_dname_t *zone_name, knot_pkt_t *query)
224 {
225 if (acl == NULL || addr == NULL || tsig == NULL) {
226 return false;
227 }
228
229 while (acl->code == KNOT_EOK) {
230 conf_val_t rmt_val = conf_id_get(conf, C_ACL, C_RMT, acl);
231 bool remote = (rmt_val.code == KNOT_EOK);
232 conf_val_t deny_val = conf_id_get(conf, C_ACL, C_DENY, acl);
233 bool deny = conf_bool(&deny_val);
234
235 /* Check if a remote matches given address and key. */
236 conf_val_t addr_val, key_val;
237 while (rmt_val.code == KNOT_EOK) {
238 addr_val = conf_id_get(conf, C_RMT, C_ADDR, &rmt_val);
239 key_val = conf_id_get(conf, C_RMT, C_KEY, &rmt_val);
240 if (check_addr_key(conf, &addr_val, &key_val, remote, addr, tsig, deny)) {
241 break;
242 }
243 conf_val_next(&rmt_val);
244 }
245 if (rmt_val.code == KNOT_EOF) {
246 goto next_acl;
247 }
248 /* Or check if acl address/key matches given address and key. */
249 if (!remote) {
250 addr_val = conf_id_get(conf, C_ACL, C_ADDR, acl);
251 key_val = conf_id_get(conf, C_ACL, C_KEY, acl);
252 if (!check_addr_key(conf, &addr_val, &key_val, remote, addr, tsig, deny)) {
253 goto next_acl;
254 }
255 }
256
257 /* Check if the action is allowed. */
258 if (action != ACL_ACTION_NONE) {
259 conf_val_t val = conf_id_get(conf, C_ACL, C_ACTION, acl);
260 while (val.code == KNOT_EOK) {
261 if (conf_opt(&val) != action) {
262 conf_val_next(&val);
263 continue;
264 }
265
266 break;
267 }
268 switch (val.code) {
269 case KNOT_EOK: /* Check for action match. */
270 break;
271 case KNOT_ENOENT: /* Empty action list allowed with deny only. */
272 return false;
273 default: /* No match. */
274 goto next_acl;
275 }
276 }
277
278 /* If the action is update, check for update rule match. */
279 if (action == ACL_ACTION_UPDATE &&
280 !update_match(conf, acl, tsig->name, zone_name, query)) {
281 goto next_acl;
282 }
283
284 /* Check if denied. */
285 if (deny) {
286 return false;
287 }
288
289 /* Fill the output with tsig secret if provided. */
290 if (tsig->name != NULL) {
291 conf_val_t val = conf_id_get(conf, C_KEY, C_SECRET, &key_val);
292 tsig->secret.data = (uint8_t *)conf_bin(&val, &tsig->secret.size);
293 }
294
295 return true;
296 next_acl:
297 conf_val_next(acl);
298 }
299
300 return false;
301 }
302