1/*
2 * Copyright (C) 2004-2008, 2010, 2011  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000, 2001, 2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/*! \file */
19/*
20 * $Id: ssu.c,v 1.38 2011/01/06 23:47:00 tbox Exp $
21 * Principal Author: Brian Wellington
22 */
23
24#include <config.h>
25
26#include <isc/magic.h>
27#include <isc/mem.h>
28#include <isc/netaddr.h>
29#include <isc/result.h>
30#include <isc/string.h>
31#include <isc/util.h>
32
33#include <dns/dlz.h>
34#include <dns/fixedname.h>
35#include <dns/name.h>
36#include <dns/ssu.h>
37
38#include <dst/gssapi.h>
39#include <dst/dst.h>
40
41#define SSUTABLEMAGIC		ISC_MAGIC('S', 'S', 'U', 'T')
42#define VALID_SSUTABLE(table)	ISC_MAGIC_VALID(table, SSUTABLEMAGIC)
43
44#define SSURULEMAGIC		ISC_MAGIC('S', 'S', 'U', 'R')
45#define VALID_SSURULE(table)	ISC_MAGIC_VALID(table, SSURULEMAGIC)
46
47struct dns_ssurule {
48	unsigned int magic;
49	isc_boolean_t grant;	/*%< is this a grant or a deny? */
50	unsigned int matchtype;	/*%< which type of pattern match? */
51	dns_name_t *identity;	/*%< the identity to match */
52	dns_name_t *name;	/*%< the name being updated */
53	unsigned int ntypes;	/*%< number of data types covered */
54	dns_rdatatype_t *types;	/*%< the data types.  Can include ANY, */
55				/*%< defaults to all but SIG,SOA,NS if NULL */
56	ISC_LINK(dns_ssurule_t) link;
57};
58
59struct dns_ssutable {
60	unsigned int magic;
61	isc_mem_t *mctx;
62	unsigned int references;
63	isc_mutex_t lock;
64	dns_dlzdb_t *dlzdatabase;
65	ISC_LIST(dns_ssurule_t) rules;
66};
67
68isc_result_t
69dns_ssutable_create(isc_mem_t *mctx, dns_ssutable_t **tablep) {
70	isc_result_t result;
71	dns_ssutable_t *table;
72
73	REQUIRE(tablep != NULL && *tablep == NULL);
74	REQUIRE(mctx != NULL);
75
76	table = isc_mem_get(mctx, sizeof(dns_ssutable_t));
77	if (table == NULL)
78		return (ISC_R_NOMEMORY);
79	result = isc_mutex_init(&table->lock);
80	if (result != ISC_R_SUCCESS) {
81		isc_mem_put(mctx, table, sizeof(dns_ssutable_t));
82		return (result);
83	}
84	table->references = 1;
85	table->mctx = mctx;
86	ISC_LIST_INIT(table->rules);
87	table->magic = SSUTABLEMAGIC;
88	*tablep = table;
89	return (ISC_R_SUCCESS);
90}
91
92static inline void
93destroy(dns_ssutable_t *table) {
94	isc_mem_t *mctx;
95
96	REQUIRE(VALID_SSUTABLE(table));
97
98	mctx = table->mctx;
99	while (!ISC_LIST_EMPTY(table->rules)) {
100		dns_ssurule_t *rule = ISC_LIST_HEAD(table->rules);
101		if (rule->identity != NULL) {
102			dns_name_free(rule->identity, mctx);
103			isc_mem_put(mctx, rule->identity, sizeof(dns_name_t));
104		}
105		if (rule->name != NULL) {
106			dns_name_free(rule->name, mctx);
107			isc_mem_put(mctx, rule->name, sizeof(dns_name_t));
108		}
109		if (rule->types != NULL)
110			isc_mem_put(mctx, rule->types,
111				    rule->ntypes * sizeof(dns_rdatatype_t));
112		ISC_LIST_UNLINK(table->rules, rule, link);
113		rule->magic = 0;
114		isc_mem_put(mctx, rule, sizeof(dns_ssurule_t));
115	}
116	DESTROYLOCK(&table->lock);
117	table->magic = 0;
118	isc_mem_put(mctx, table, sizeof(dns_ssutable_t));
119}
120
121void
122dns_ssutable_attach(dns_ssutable_t *source, dns_ssutable_t **targetp) {
123	REQUIRE(VALID_SSUTABLE(source));
124	REQUIRE(targetp != NULL && *targetp == NULL);
125
126	LOCK(&source->lock);
127
128	INSIST(source->references > 0);
129	source->references++;
130	INSIST(source->references != 0);
131
132	UNLOCK(&source->lock);
133
134	*targetp = source;
135}
136
137void
138dns_ssutable_detach(dns_ssutable_t **tablep) {
139	dns_ssutable_t *table;
140	isc_boolean_t done = ISC_FALSE;
141
142	REQUIRE(tablep != NULL);
143	table = *tablep;
144	REQUIRE(VALID_SSUTABLE(table));
145
146	LOCK(&table->lock);
147
148	INSIST(table->references > 0);
149	if (--table->references == 0)
150		done = ISC_TRUE;
151	UNLOCK(&table->lock);
152
153	*tablep = NULL;
154
155	if (done)
156		destroy(table);
157}
158
159isc_result_t
160dns_ssutable_addrule(dns_ssutable_t *table, isc_boolean_t grant,
161		     dns_name_t *identity, unsigned int matchtype,
162		     dns_name_t *name, unsigned int ntypes,
163		     dns_rdatatype_t *types)
164{
165	dns_ssurule_t *rule;
166	isc_mem_t *mctx;
167	isc_result_t result;
168
169	REQUIRE(VALID_SSUTABLE(table));
170	REQUIRE(dns_name_isabsolute(identity));
171	REQUIRE(dns_name_isabsolute(name));
172	REQUIRE(matchtype <= DNS_SSUMATCHTYPE_MAX);
173	if (matchtype == DNS_SSUMATCHTYPE_WILDCARD)
174		REQUIRE(dns_name_iswildcard(name));
175	if (ntypes > 0)
176		REQUIRE(types != NULL);
177
178	mctx = table->mctx;
179	rule = isc_mem_get(mctx, sizeof(dns_ssurule_t));
180	if (rule == NULL)
181		return (ISC_R_NOMEMORY);
182
183	rule->identity = NULL;
184	rule->name = NULL;
185	rule->types = NULL;
186
187	rule->grant = grant;
188
189	rule->identity = isc_mem_get(mctx, sizeof(dns_name_t));
190	if (rule->identity == NULL) {
191		result = ISC_R_NOMEMORY;
192		goto failure;
193	}
194	dns_name_init(rule->identity, NULL);
195	result = dns_name_dup(identity, mctx, rule->identity);
196	if (result != ISC_R_SUCCESS)
197		goto failure;
198
199	rule->name = isc_mem_get(mctx, sizeof(dns_name_t));
200	if (rule->name == NULL) {
201		result = ISC_R_NOMEMORY;
202		goto failure;
203	}
204	dns_name_init(rule->name, NULL);
205	result = dns_name_dup(name, mctx, rule->name);
206	if (result != ISC_R_SUCCESS)
207		goto failure;
208
209	rule->matchtype = matchtype;
210
211	rule->ntypes = ntypes;
212	if (ntypes > 0) {
213		rule->types = isc_mem_get(mctx,
214					  ntypes * sizeof(dns_rdatatype_t));
215		if (rule->types == NULL) {
216			result = ISC_R_NOMEMORY;
217			goto failure;
218		}
219		memcpy(rule->types, types, ntypes * sizeof(dns_rdatatype_t));
220	} else
221		rule->types = NULL;
222
223	rule->magic = SSURULEMAGIC;
224	ISC_LIST_INITANDAPPEND(table->rules, rule, link);
225
226	return (ISC_R_SUCCESS);
227
228 failure:
229	if (rule->identity != NULL) {
230		if (dns_name_dynamic(rule->identity))
231			dns_name_free(rule->identity, mctx);
232		isc_mem_put(mctx, rule->identity, sizeof(dns_name_t));
233	}
234	if (rule->name != NULL) {
235		if (dns_name_dynamic(rule->name))
236			dns_name_free(rule->name, mctx);
237		isc_mem_put(mctx, rule->name, sizeof(dns_name_t));
238	}
239	if (rule->types != NULL)
240		isc_mem_put(mctx, rule->types,
241			    ntypes * sizeof(dns_rdatatype_t));
242	isc_mem_put(mctx, rule, sizeof(dns_ssurule_t));
243
244	return (result);
245}
246
247static inline isc_boolean_t
248isusertype(dns_rdatatype_t type) {
249	return (ISC_TF(type != dns_rdatatype_ns &&
250		       type != dns_rdatatype_soa &&
251		       type != dns_rdatatype_rrsig));
252}
253
254static void
255reverse_from_address(dns_name_t *tcpself, isc_netaddr_t *tcpaddr) {
256	char buf[16 * 4 + sizeof("IP6.ARPA.")];
257	isc_result_t result;
258	unsigned char *ap;
259	isc_buffer_t b;
260	unsigned long l;
261
262	switch (tcpaddr->family) {
263	case AF_INET:
264		l = ntohl(tcpaddr->type.in.s_addr);
265		result = isc_string_printf(buf, sizeof(buf),
266					   "%lu.%lu.%lu.%lu.IN-ADDR.ARPA.",
267					   (l >> 0) & 0xff, (l >> 8) & 0xff,
268					   (l >> 16) & 0xff, (l >> 24) & 0xff);
269		RUNTIME_CHECK(result == ISC_R_SUCCESS);
270		break;
271	case AF_INET6:
272		ap = tcpaddr->type.in6.s6_addr;
273		result = isc_string_printf(buf, sizeof(buf),
274					   "%x.%x.%x.%x.%x.%x.%x.%x."
275					   "%x.%x.%x.%x.%x.%x.%x.%x."
276					   "%x.%x.%x.%x.%x.%x.%x.%x."
277					   "%x.%x.%x.%x.%x.%x.%x.%x."
278					   "IP6.ARPA.",
279					   ap[15] & 0x0f, (ap[15] >> 4) & 0x0f,
280					   ap[14] & 0x0f, (ap[14] >> 4) & 0x0f,
281					   ap[13] & 0x0f, (ap[13] >> 4) & 0x0f,
282					   ap[12] & 0x0f, (ap[12] >> 4) & 0x0f,
283					   ap[11] & 0x0f, (ap[11] >> 4) & 0x0f,
284					   ap[10] & 0x0f, (ap[10] >> 4) & 0x0f,
285					   ap[9] & 0x0f, (ap[9] >> 4) & 0x0f,
286					   ap[8] & 0x0f, (ap[8] >> 4) & 0x0f,
287					   ap[7] & 0x0f, (ap[7] >> 4) & 0x0f,
288					   ap[6] & 0x0f, (ap[6] >> 4) & 0x0f,
289					   ap[5] & 0x0f, (ap[5] >> 4) & 0x0f,
290					   ap[4] & 0x0f, (ap[4] >> 4) & 0x0f,
291					   ap[3] & 0x0f, (ap[3] >> 4) & 0x0f,
292					   ap[2] & 0x0f, (ap[2] >> 4) & 0x0f,
293					   ap[1] & 0x0f, (ap[1] >> 4) & 0x0f,
294					   ap[0] & 0x0f, (ap[0] >> 4) & 0x0f);
295		RUNTIME_CHECK(result == ISC_R_SUCCESS);
296		break;
297	default:
298		INSIST(0);
299	}
300	isc_buffer_init(&b, buf, strlen(buf));
301	isc_buffer_add(&b, strlen(buf));
302	result = dns_name_fromtext(tcpself, &b, dns_rootname, 0, NULL);
303	RUNTIME_CHECK(result == ISC_R_SUCCESS);
304}
305
306static void
307stf_from_address(dns_name_t *stfself, isc_netaddr_t *tcpaddr) {
308	char buf[sizeof("X.X.X.X.Y.Y.Y.Y.2.0.0.2.IP6.ARPA.")];
309	isc_result_t result;
310	unsigned char *ap;
311	isc_buffer_t b;
312	unsigned long l;
313
314	switch(tcpaddr->family) {
315	case AF_INET:
316		l = ntohl(tcpaddr->type.in.s_addr);
317		result = isc_string_printf(buf, sizeof(buf),
318					   "%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx"
319					   "2.0.0.2.IP6.ARPA.",
320					   l & 0xf, (l >> 4) & 0xf,
321					   (l >> 8) & 0xf, (l >> 12) & 0xf,
322					   (l >> 16) & 0xf, (l >> 20) & 0xf,
323					   (l >> 24) & 0xf, (l >> 28) & 0xf);
324		RUNTIME_CHECK(result == ISC_R_SUCCESS);
325		break;
326	case AF_INET6:
327		ap = tcpaddr->type.in6.s6_addr;
328		result = isc_string_printf(buf, sizeof(buf),
329					   "%x.%x.%x.%x.%x.%x.%x.%x."
330					   "%x.%x.%x.%x.IP6.ARPA.",
331					   ap[5] & 0x0f, (ap[5] >> 4) & 0x0f,
332					   ap[4] & 0x0f, (ap[4] >> 4) & 0x0f,
333					   ap[3] & 0x0f, (ap[3] >> 4) & 0x0f,
334					   ap[2] & 0x0f, (ap[2] >> 4) & 0x0f,
335					   ap[1] & 0x0f, (ap[1] >> 4) & 0x0f,
336					   ap[0] & 0x0f, (ap[0] >> 4) & 0x0f);
337		RUNTIME_CHECK(result == ISC_R_SUCCESS);
338		break;
339	default:
340		INSIST(0);
341	}
342	isc_buffer_init(&b, buf, strlen(buf));
343	isc_buffer_add(&b, strlen(buf));
344	result = dns_name_fromtext(stfself, &b, dns_rootname, 0, NULL);
345	RUNTIME_CHECK(result == ISC_R_SUCCESS);
346}
347
348isc_boolean_t
349dns_ssutable_checkrules(dns_ssutable_t *table, dns_name_t *signer,
350			dns_name_t *name, isc_netaddr_t *tcpaddr,
351			dns_rdatatype_t type,
352			const dst_key_t *key)
353{
354	dns_ssurule_t *rule;
355	unsigned int i;
356	dns_fixedname_t fixed;
357	dns_name_t *wildcard;
358	dns_name_t *tcpself;
359	dns_name_t *stfself;
360	isc_result_t result;
361
362	REQUIRE(VALID_SSUTABLE(table));
363	REQUIRE(signer == NULL || dns_name_isabsolute(signer));
364	REQUIRE(dns_name_isabsolute(name));
365
366	if (signer == NULL && tcpaddr == NULL)
367		return (ISC_FALSE);
368
369	for (rule = ISC_LIST_HEAD(table->rules);
370	     rule != NULL;
371	     rule = ISC_LIST_NEXT(rule, link))
372	{
373		switch (rule->matchtype) {
374		case DNS_SSUMATCHTYPE_NAME:
375		case DNS_SSUMATCHTYPE_SUBDOMAIN:
376		case DNS_SSUMATCHTYPE_WILDCARD:
377		case DNS_SSUMATCHTYPE_SELF:
378		case DNS_SSUMATCHTYPE_SELFSUB:
379		case DNS_SSUMATCHTYPE_SELFWILD:
380			if (signer == NULL)
381				continue;
382			if (dns_name_iswildcard(rule->identity)) {
383				if (!dns_name_matcheswildcard(signer,
384							      rule->identity))
385					continue;
386			} else {
387				if (!dns_name_equal(signer, rule->identity))
388					continue;
389			}
390			break;
391		case DNS_SSUMATCHTYPE_SELFKRB5:
392		case DNS_SSUMATCHTYPE_SELFMS:
393		case DNS_SSUMATCHTYPE_SUBDOMAINKRB5:
394		case DNS_SSUMATCHTYPE_SUBDOMAINMS:
395			if (signer == NULL)
396				continue;
397			break;
398		case DNS_SSUMATCHTYPE_TCPSELF:
399		case DNS_SSUMATCHTYPE_6TO4SELF:
400			if (tcpaddr == NULL)
401				continue;
402			break;
403		}
404
405		switch (rule->matchtype) {
406		case DNS_SSUMATCHTYPE_NAME:
407			if (!dns_name_equal(name, rule->name))
408				continue;
409			break;
410		case DNS_SSUMATCHTYPE_SUBDOMAIN:
411			if (!dns_name_issubdomain(name, rule->name))
412				continue;
413			break;
414		case DNS_SSUMATCHTYPE_WILDCARD:
415			if (!dns_name_matcheswildcard(name, rule->name))
416				continue;
417			break;
418		case DNS_SSUMATCHTYPE_SELF:
419			if (!dns_name_equal(signer, name))
420				continue;
421			break;
422		case DNS_SSUMATCHTYPE_SELFSUB:
423			if (!dns_name_issubdomain(name, signer))
424				continue;
425			break;
426		case DNS_SSUMATCHTYPE_SELFWILD:
427			dns_fixedname_init(&fixed);
428			wildcard = dns_fixedname_name(&fixed);
429			result = dns_name_concatenate(dns_wildcardname, signer,
430						      wildcard, NULL);
431			if (result != ISC_R_SUCCESS)
432				continue;
433			if (!dns_name_matcheswildcard(name, wildcard))
434				continue;
435			break;
436		case DNS_SSUMATCHTYPE_SELFKRB5:
437			if (!dst_gssapi_identitymatchesrealmkrb5(signer, name,
438							       rule->identity))
439				continue;
440			break;
441		case DNS_SSUMATCHTYPE_SELFMS:
442			if (!dst_gssapi_identitymatchesrealmms(signer, name,
443							       rule->identity))
444				continue;
445			break;
446		case DNS_SSUMATCHTYPE_SUBDOMAINKRB5:
447			if (!dns_name_issubdomain(name, rule->name))
448				continue;
449			if (!dst_gssapi_identitymatchesrealmkrb5(signer, NULL,
450							       rule->identity))
451				continue;
452			break;
453		case DNS_SSUMATCHTYPE_SUBDOMAINMS:
454			if (!dns_name_issubdomain(name, rule->name))
455				continue;
456			if (!dst_gssapi_identitymatchesrealmms(signer, NULL,
457							       rule->identity))
458				continue;
459			break;
460		case DNS_SSUMATCHTYPE_TCPSELF:
461			dns_fixedname_init(&fixed);
462			tcpself = dns_fixedname_name(&fixed);
463			reverse_from_address(tcpself, tcpaddr);
464			if (dns_name_iswildcard(rule->identity)) {
465				if (!dns_name_matcheswildcard(tcpself,
466							      rule->identity))
467					continue;
468			} else {
469				if (!dns_name_equal(tcpself, rule->identity))
470					continue;
471			}
472			if (!dns_name_equal(tcpself, name))
473				continue;
474			break;
475		case DNS_SSUMATCHTYPE_6TO4SELF:
476			dns_fixedname_init(&fixed);
477			stfself = dns_fixedname_name(&fixed);
478			stf_from_address(stfself, tcpaddr);
479			if (dns_name_iswildcard(rule->identity)) {
480				if (!dns_name_matcheswildcard(stfself,
481							      rule->identity))
482					continue;
483			} else {
484				if (!dns_name_equal(stfself, rule->identity))
485					continue;
486			}
487			if (!dns_name_equal(stfself, name))
488				continue;
489			break;
490		case DNS_SSUMATCHTYPE_EXTERNAL:
491			if (!dns_ssu_external_match(rule->identity, signer,
492						    name, tcpaddr, type, key,
493						    table->mctx))
494				continue;
495			break;
496		case DNS_SSUMATCHTYPE_DLZ:
497			if (!dns_dlz_ssumatch(table->dlzdatabase, signer,
498					      name, tcpaddr, type, key))
499				continue;
500			break;
501		}
502
503		if (rule->ntypes == 0) {
504			/*
505			 * If this is a DLZ rule, then the DLZ ssu
506			 * checks will have already checked
507			 * the type.
508			 */
509			if (rule->matchtype != DNS_SSUMATCHTYPE_DLZ &&
510			    !isusertype(type))
511				continue;
512		} else {
513			for (i = 0; i < rule->ntypes; i++) {
514				if (rule->types[i] == dns_rdatatype_any ||
515				    rule->types[i] == type)
516					break;
517			}
518			if (i == rule->ntypes)
519				continue;
520		}
521		return (rule->grant);
522	}
523
524	return (ISC_FALSE);
525}
526
527isc_boolean_t
528dns_ssurule_isgrant(const dns_ssurule_t *rule) {
529	REQUIRE(VALID_SSURULE(rule));
530	return (rule->grant);
531}
532
533dns_name_t *
534dns_ssurule_identity(const dns_ssurule_t *rule) {
535	REQUIRE(VALID_SSURULE(rule));
536	return (rule->identity);
537}
538
539unsigned int
540dns_ssurule_matchtype(const dns_ssurule_t *rule) {
541	REQUIRE(VALID_SSURULE(rule));
542	return (rule->matchtype);
543}
544
545dns_name_t *
546dns_ssurule_name(const dns_ssurule_t *rule) {
547	REQUIRE(VALID_SSURULE(rule));
548	return (rule->name);
549}
550
551unsigned int
552dns_ssurule_types(const dns_ssurule_t *rule, dns_rdatatype_t **types) {
553	REQUIRE(VALID_SSURULE(rule));
554	REQUIRE(types != NULL && *types != NULL);
555	*types = rule->types;
556	return (rule->ntypes);
557}
558
559isc_result_t
560dns_ssutable_firstrule(const dns_ssutable_t *table, dns_ssurule_t **rule) {
561	REQUIRE(VALID_SSUTABLE(table));
562	REQUIRE(rule != NULL && *rule == NULL);
563	*rule = ISC_LIST_HEAD(table->rules);
564	return (*rule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE);
565}
566
567isc_result_t
568dns_ssutable_nextrule(dns_ssurule_t *rule, dns_ssurule_t **nextrule) {
569	REQUIRE(VALID_SSURULE(rule));
570	REQUIRE(nextrule != NULL && *nextrule == NULL);
571	*nextrule = ISC_LIST_NEXT(rule, link);
572	return (*nextrule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE);
573}
574
575/*
576 * Create a specialised SSU table that points at an external DLZ database
577 */
578isc_result_t
579dns_ssutable_createdlz(isc_mem_t *mctx, dns_ssutable_t **tablep,
580		       dns_dlzdb_t *dlzdatabase)
581{
582	isc_result_t result;
583	dns_ssurule_t *rule;
584	dns_ssutable_t *table = NULL;
585
586	REQUIRE(tablep != NULL && *tablep == NULL);
587
588	result = dns_ssutable_create(mctx, &table);
589	if (result != ISC_R_SUCCESS)
590		return (result);
591
592	table->dlzdatabase = dlzdatabase;
593
594	rule = isc_mem_get(table->mctx, sizeof(dns_ssurule_t));
595	if (rule == NULL) {
596		dns_ssutable_detach(&table);
597		return (ISC_R_NOMEMORY);
598	}
599
600	rule->identity = NULL;
601	rule->name = NULL;
602	rule->types = NULL;
603	rule->grant = ISC_TRUE;
604	rule->matchtype = DNS_SSUMATCHTYPE_DLZ;
605	rule->ntypes = 0;
606	rule->types = NULL;
607	rule->magic = SSURULEMAGIC;
608
609	ISC_LIST_INITANDAPPEND(table->rules, rule, link);
610	*tablep = table;
611	return (ISC_R_SUCCESS);
612}
613