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