1/*
2 * Copyright (C) 2004-2008, 2010, 2011, 2013, 2014  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 = NULL;
86	isc_mem_attach(mctx, &table->mctx);
87	ISC_LIST_INIT(table->rules);
88	table->magic = SSUTABLEMAGIC;
89	*tablep = table;
90	return (ISC_R_SUCCESS);
91}
92
93static inline void
94destroy(dns_ssutable_t *table) {
95	isc_mem_t *mctx;
96
97	REQUIRE(VALID_SSUTABLE(table));
98
99	mctx = table->mctx;
100	while (!ISC_LIST_EMPTY(table->rules)) {
101		dns_ssurule_t *rule = ISC_LIST_HEAD(table->rules);
102		if (rule->identity != NULL) {
103			dns_name_free(rule->identity, mctx);
104			isc_mem_put(mctx, rule->identity, sizeof(dns_name_t));
105		}
106		if (rule->name != NULL) {
107			dns_name_free(rule->name, mctx);
108			isc_mem_put(mctx, rule->name, sizeof(dns_name_t));
109		}
110		if (rule->types != NULL)
111			isc_mem_put(mctx, rule->types,
112				    rule->ntypes * sizeof(dns_rdatatype_t));
113		ISC_LIST_UNLINK(table->rules, rule, link);
114		rule->magic = 0;
115		isc_mem_put(mctx, rule, sizeof(dns_ssurule_t));
116	}
117	DESTROYLOCK(&table->lock);
118	table->magic = 0;
119	isc_mem_putanddetach(&table->mctx, table, sizeof(dns_ssutable_t));
120}
121
122void
123dns_ssutable_attach(dns_ssutable_t *source, dns_ssutable_t **targetp) {
124	REQUIRE(VALID_SSUTABLE(source));
125	REQUIRE(targetp != NULL && *targetp == NULL);
126
127	LOCK(&source->lock);
128
129	INSIST(source->references > 0);
130	source->references++;
131	INSIST(source->references != 0);
132
133	UNLOCK(&source->lock);
134
135	*targetp = source;
136}
137
138void
139dns_ssutable_detach(dns_ssutable_t **tablep) {
140	dns_ssutable_t *table;
141	isc_boolean_t done = ISC_FALSE;
142
143	REQUIRE(tablep != NULL);
144	table = *tablep;
145	REQUIRE(VALID_SSUTABLE(table));
146
147	LOCK(&table->lock);
148
149	INSIST(table->references > 0);
150	if (--table->references == 0)
151		done = ISC_TRUE;
152	UNLOCK(&table->lock);
153
154	*tablep = NULL;
155
156	if (done)
157		destroy(table);
158}
159
160isc_result_t
161dns_ssutable_addrule(dns_ssutable_t *table, isc_boolean_t grant,
162		     dns_name_t *identity, unsigned int matchtype,
163		     dns_name_t *name, unsigned int ntypes,
164		     dns_rdatatype_t *types)
165{
166	dns_ssurule_t *rule;
167	isc_mem_t *mctx;
168	isc_result_t result;
169
170	REQUIRE(VALID_SSUTABLE(table));
171	REQUIRE(dns_name_isabsolute(identity));
172	REQUIRE(dns_name_isabsolute(name));
173	REQUIRE(matchtype <= DNS_SSUMATCHTYPE_MAX);
174	if (matchtype == DNS_SSUMATCHTYPE_WILDCARD)
175		REQUIRE(dns_name_iswildcard(name));
176	if (ntypes > 0)
177		REQUIRE(types != NULL);
178
179	mctx = table->mctx;
180	rule = isc_mem_get(mctx, sizeof(dns_ssurule_t));
181	if (rule == NULL)
182		return (ISC_R_NOMEMORY);
183
184	rule->identity = NULL;
185	rule->name = NULL;
186	rule->types = NULL;
187
188	rule->grant = grant;
189
190	rule->identity = isc_mem_get(mctx, sizeof(dns_name_t));
191	if (rule->identity == NULL) {
192		result = ISC_R_NOMEMORY;
193		goto failure;
194	}
195	dns_name_init(rule->identity, NULL);
196	result = dns_name_dup(identity, mctx, rule->identity);
197	if (result != ISC_R_SUCCESS)
198		goto failure;
199
200	rule->name = isc_mem_get(mctx, sizeof(dns_name_t));
201	if (rule->name == NULL) {
202		result = ISC_R_NOMEMORY;
203		goto failure;
204	}
205	dns_name_init(rule->name, NULL);
206	result = dns_name_dup(name, mctx, rule->name);
207	if (result != ISC_R_SUCCESS)
208		goto failure;
209
210	rule->matchtype = matchtype;
211
212	rule->ntypes = ntypes;
213	if (ntypes > 0) {
214		rule->types = isc_mem_get(mctx,
215					  ntypes * sizeof(dns_rdatatype_t));
216		if (rule->types == NULL) {
217			result = ISC_R_NOMEMORY;
218			goto failure;
219		}
220		memmove(rule->types, types, ntypes * sizeof(dns_rdatatype_t));
221	} else
222		rule->types = NULL;
223
224	rule->magic = SSURULEMAGIC;
225	ISC_LIST_INITANDAPPEND(table->rules, rule, link);
226
227	return (ISC_R_SUCCESS);
228
229 failure:
230	if (rule->identity != NULL) {
231		if (dns_name_dynamic(rule->identity))
232			dns_name_free(rule->identity, mctx);
233		isc_mem_put(mctx, rule->identity, sizeof(dns_name_t));
234	}
235	if (rule->name != NULL) {
236		if (dns_name_dynamic(rule->name))
237			dns_name_free(rule->name, mctx);
238		isc_mem_put(mctx, rule->name, sizeof(dns_name_t));
239	}
240	if (rule->types != NULL)
241		isc_mem_put(mctx, rule->types,
242			    ntypes * sizeof(dns_rdatatype_t));
243	isc_mem_put(mctx, rule, sizeof(dns_ssurule_t));
244
245	return (result);
246}
247
248static inline isc_boolean_t
249isusertype(dns_rdatatype_t type) {
250	return (ISC_TF(type != dns_rdatatype_ns &&
251		       type != dns_rdatatype_soa &&
252		       type != dns_rdatatype_rrsig));
253}
254
255static void
256reverse_from_address(dns_name_t *tcpself, isc_netaddr_t *tcpaddr) {
257	char buf[16 * 4 + sizeof("IP6.ARPA.")];
258	isc_result_t result;
259	unsigned char *ap;
260	isc_buffer_t b;
261	unsigned long l;
262
263	switch (tcpaddr->family) {
264	case AF_INET:
265		l = ntohl(tcpaddr->type.in.s_addr);
266		result = isc_string_printf(buf, sizeof(buf),
267					   "%lu.%lu.%lu.%lu.IN-ADDR.ARPA.",
268					   (l >> 0) & 0xff, (l >> 8) & 0xff,
269					   (l >> 16) & 0xff, (l >> 24) & 0xff);
270		RUNTIME_CHECK(result == ISC_R_SUCCESS);
271		break;
272	case AF_INET6:
273		ap = tcpaddr->type.in6.s6_addr;
274		result = isc_string_printf(buf, sizeof(buf),
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					   "%x.%x.%x.%x.%x.%x.%x.%x."
279					   "IP6.ARPA.",
280					   ap[15] & 0x0f, (ap[15] >> 4) & 0x0f,
281					   ap[14] & 0x0f, (ap[14] >> 4) & 0x0f,
282					   ap[13] & 0x0f, (ap[13] >> 4) & 0x0f,
283					   ap[12] & 0x0f, (ap[12] >> 4) & 0x0f,
284					   ap[11] & 0x0f, (ap[11] >> 4) & 0x0f,
285					   ap[10] & 0x0f, (ap[10] >> 4) & 0x0f,
286					   ap[9] & 0x0f, (ap[9] >> 4) & 0x0f,
287					   ap[8] & 0x0f, (ap[8] >> 4) & 0x0f,
288					   ap[7] & 0x0f, (ap[7] >> 4) & 0x0f,
289					   ap[6] & 0x0f, (ap[6] >> 4) & 0x0f,
290					   ap[5] & 0x0f, (ap[5] >> 4) & 0x0f,
291					   ap[4] & 0x0f, (ap[4] >> 4) & 0x0f,
292					   ap[3] & 0x0f, (ap[3] >> 4) & 0x0f,
293					   ap[2] & 0x0f, (ap[2] >> 4) & 0x0f,
294					   ap[1] & 0x0f, (ap[1] >> 4) & 0x0f,
295					   ap[0] & 0x0f, (ap[0] >> 4) & 0x0f);
296		RUNTIME_CHECK(result == ISC_R_SUCCESS);
297		break;
298	default:
299		INSIST(0);
300	}
301	isc_buffer_init(&b, buf, strlen(buf));
302	isc_buffer_add(&b, strlen(buf));
303	result = dns_name_fromtext(tcpself, &b, dns_rootname, 0, NULL);
304	RUNTIME_CHECK(result == ISC_R_SUCCESS);
305}
306
307static void
308stf_from_address(dns_name_t *stfself, isc_netaddr_t *tcpaddr) {
309	char buf[sizeof("X.X.X.X.Y.Y.Y.Y.2.0.0.2.IP6.ARPA.")];
310	isc_result_t result;
311	unsigned char *ap;
312	isc_buffer_t b;
313	unsigned long l;
314
315	switch(tcpaddr->family) {
316	case AF_INET:
317		l = ntohl(tcpaddr->type.in.s_addr);
318		result = isc_string_printf(buf, sizeof(buf),
319					   "%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx"
320					   "2.0.0.2.IP6.ARPA.",
321					   l & 0xf, (l >> 4) & 0xf,
322					   (l >> 8) & 0xf, (l >> 12) & 0xf,
323					   (l >> 16) & 0xf, (l >> 20) & 0xf,
324					   (l >> 24) & 0xf, (l >> 28) & 0xf);
325		RUNTIME_CHECK(result == ISC_R_SUCCESS);
326		break;
327	case AF_INET6:
328		ap = tcpaddr->type.in6.s6_addr;
329		result = isc_string_printf(buf, sizeof(buf),
330					   "%x.%x.%x.%x.%x.%x.%x.%x."
331					   "%x.%x.%x.%x.IP6.ARPA.",
332					   ap[5] & 0x0f, (ap[5] >> 4) & 0x0f,
333					   ap[4] & 0x0f, (ap[4] >> 4) & 0x0f,
334					   ap[3] & 0x0f, (ap[3] >> 4) & 0x0f,
335					   ap[2] & 0x0f, (ap[2] >> 4) & 0x0f,
336					   ap[1] & 0x0f, (ap[1] >> 4) & 0x0f,
337					   ap[0] & 0x0f, (ap[0] >> 4) & 0x0f);
338		RUNTIME_CHECK(result == ISC_R_SUCCESS);
339		break;
340	default:
341		INSIST(0);
342	}
343	isc_buffer_init(&b, buf, strlen(buf));
344	isc_buffer_add(&b, strlen(buf));
345	result = dns_name_fromtext(stfself, &b, dns_rootname, 0, NULL);
346	RUNTIME_CHECK(result == ISC_R_SUCCESS);
347}
348
349isc_boolean_t
350dns_ssutable_checkrules(dns_ssutable_t *table, dns_name_t *signer,
351			dns_name_t *name, isc_netaddr_t *tcpaddr,
352			dns_rdatatype_t type,
353			const dst_key_t *key)
354{
355	dns_ssurule_t *rule;
356	unsigned int i;
357	dns_fixedname_t fixed;
358	dns_name_t *wildcard;
359	dns_name_t *tcpself;
360	dns_name_t *stfself;
361	isc_result_t result;
362
363	REQUIRE(VALID_SSUTABLE(table));
364	REQUIRE(signer == NULL || dns_name_isabsolute(signer));
365	REQUIRE(dns_name_isabsolute(name));
366
367	if (signer == NULL && tcpaddr == NULL)
368		return (ISC_FALSE);
369
370	for (rule = ISC_LIST_HEAD(table->rules);
371	     rule != NULL;
372	     rule = ISC_LIST_NEXT(rule, link))
373	{
374		switch (rule->matchtype) {
375		case DNS_SSUMATCHTYPE_NAME:
376		case DNS_SSUMATCHTYPE_SUBDOMAIN:
377		case DNS_SSUMATCHTYPE_WILDCARD:
378		case DNS_SSUMATCHTYPE_SELF:
379		case DNS_SSUMATCHTYPE_SELFSUB:
380		case DNS_SSUMATCHTYPE_SELFWILD:
381			if (signer == NULL)
382				continue;
383			if (dns_name_iswildcard(rule->identity)) {
384				if (!dns_name_matcheswildcard(signer,
385							      rule->identity))
386					continue;
387			} else {
388				if (!dns_name_equal(signer, rule->identity))
389					continue;
390			}
391			break;
392		case DNS_SSUMATCHTYPE_SELFKRB5:
393		case DNS_SSUMATCHTYPE_SELFMS:
394		case DNS_SSUMATCHTYPE_SUBDOMAINKRB5:
395		case DNS_SSUMATCHTYPE_SUBDOMAINMS:
396			if (signer == NULL)
397				continue;
398			break;
399		case DNS_SSUMATCHTYPE_TCPSELF:
400		case DNS_SSUMATCHTYPE_6TO4SELF:
401			if (tcpaddr == NULL)
402				continue;
403			break;
404		}
405
406		switch (rule->matchtype) {
407		case DNS_SSUMATCHTYPE_NAME:
408			if (!dns_name_equal(name, rule->name))
409				continue;
410			break;
411		case DNS_SSUMATCHTYPE_SUBDOMAIN:
412			if (!dns_name_issubdomain(name, rule->name))
413				continue;
414			break;
415		case DNS_SSUMATCHTYPE_WILDCARD:
416			if (!dns_name_matcheswildcard(name, rule->name))
417				continue;
418			break;
419		case DNS_SSUMATCHTYPE_SELF:
420			if (!dns_name_equal(signer, name))
421				continue;
422			break;
423		case DNS_SSUMATCHTYPE_SELFSUB:
424			if (!dns_name_issubdomain(name, signer))
425				continue;
426			break;
427		case DNS_SSUMATCHTYPE_SELFWILD:
428			dns_fixedname_init(&fixed);
429			wildcard = dns_fixedname_name(&fixed);
430			result = dns_name_concatenate(dns_wildcardname, signer,
431						      wildcard, NULL);
432			if (result != ISC_R_SUCCESS)
433				continue;
434			if (!dns_name_matcheswildcard(name, wildcard))
435				continue;
436			break;
437		case DNS_SSUMATCHTYPE_SELFKRB5:
438			if (!dst_gssapi_identitymatchesrealmkrb5(signer, name,
439							       rule->identity))
440				continue;
441			break;
442		case DNS_SSUMATCHTYPE_SELFMS:
443			if (!dst_gssapi_identitymatchesrealmms(signer, name,
444							       rule->identity))
445				continue;
446			break;
447		case DNS_SSUMATCHTYPE_SUBDOMAINKRB5:
448			if (!dns_name_issubdomain(name, rule->name))
449				continue;
450			if (!dst_gssapi_identitymatchesrealmkrb5(signer, NULL,
451							       rule->identity))
452				continue;
453			break;
454		case DNS_SSUMATCHTYPE_SUBDOMAINMS:
455			if (!dns_name_issubdomain(name, rule->name))
456				continue;
457			if (!dst_gssapi_identitymatchesrealmms(signer, NULL,
458							       rule->identity))
459				continue;
460			break;
461		case DNS_SSUMATCHTYPE_TCPSELF:
462			dns_fixedname_init(&fixed);
463			tcpself = dns_fixedname_name(&fixed);
464			reverse_from_address(tcpself, tcpaddr);
465			if (dns_name_iswildcard(rule->identity)) {
466				if (!dns_name_matcheswildcard(tcpself,
467							      rule->identity))
468					continue;
469			} else {
470				if (!dns_name_equal(tcpself, rule->identity))
471					continue;
472			}
473			if (!dns_name_equal(tcpself, name))
474				continue;
475			break;
476		case DNS_SSUMATCHTYPE_6TO4SELF:
477			dns_fixedname_init(&fixed);
478			stfself = dns_fixedname_name(&fixed);
479			stf_from_address(stfself, tcpaddr);
480			if (dns_name_iswildcard(rule->identity)) {
481				if (!dns_name_matcheswildcard(stfself,
482							      rule->identity))
483					continue;
484			} else {
485				if (!dns_name_equal(stfself, rule->identity))
486					continue;
487			}
488			if (!dns_name_equal(stfself, name))
489				continue;
490			break;
491		case DNS_SSUMATCHTYPE_EXTERNAL:
492			if (!dns_ssu_external_match(rule->identity, signer,
493						    name, tcpaddr, type, key,
494						    table->mctx))
495				continue;
496			break;
497		case DNS_SSUMATCHTYPE_DLZ:
498			if (!dns_dlz_ssumatch(table->dlzdatabase, signer,
499					      name, tcpaddr, type, key))
500				continue;
501			break;
502		}
503
504		if (rule->ntypes == 0) {
505			/*
506			 * If this is a DLZ rule, then the DLZ ssu
507			 * checks will have already checked
508			 * the type.
509			 */
510			if (rule->matchtype != DNS_SSUMATCHTYPE_DLZ &&
511			    !isusertype(type))
512				continue;
513		} else {
514			for (i = 0; i < rule->ntypes; i++) {
515				if (rule->types[i] == dns_rdatatype_any ||
516				    rule->types[i] == type)
517					break;
518			}
519			if (i == rule->ntypes)
520				continue;
521		}
522		return (rule->grant);
523	}
524
525	return (ISC_FALSE);
526}
527
528isc_boolean_t
529dns_ssurule_isgrant(const dns_ssurule_t *rule) {
530	REQUIRE(VALID_SSURULE(rule));
531	return (rule->grant);
532}
533
534dns_name_t *
535dns_ssurule_identity(const dns_ssurule_t *rule) {
536	REQUIRE(VALID_SSURULE(rule));
537	return (rule->identity);
538}
539
540unsigned int
541dns_ssurule_matchtype(const dns_ssurule_t *rule) {
542	REQUIRE(VALID_SSURULE(rule));
543	return (rule->matchtype);
544}
545
546dns_name_t *
547dns_ssurule_name(const dns_ssurule_t *rule) {
548	REQUIRE(VALID_SSURULE(rule));
549	return (rule->name);
550}
551
552unsigned int
553dns_ssurule_types(const dns_ssurule_t *rule, dns_rdatatype_t **types) {
554	REQUIRE(VALID_SSURULE(rule));
555	REQUIRE(types != NULL && *types != NULL);
556	*types = rule->types;
557	return (rule->ntypes);
558}
559
560isc_result_t
561dns_ssutable_firstrule(const dns_ssutable_t *table, dns_ssurule_t **rule) {
562	REQUIRE(VALID_SSUTABLE(table));
563	REQUIRE(rule != NULL && *rule == NULL);
564	*rule = ISC_LIST_HEAD(table->rules);
565	return (*rule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE);
566}
567
568isc_result_t
569dns_ssutable_nextrule(dns_ssurule_t *rule, dns_ssurule_t **nextrule) {
570	REQUIRE(VALID_SSURULE(rule));
571	REQUIRE(nextrule != NULL && *nextrule == NULL);
572	*nextrule = ISC_LIST_NEXT(rule, link);
573	return (*nextrule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE);
574}
575
576/*
577 * Create a specialised SSU table that points at an external DLZ database
578 */
579isc_result_t
580dns_ssutable_createdlz(isc_mem_t *mctx, dns_ssutable_t **tablep,
581		       dns_dlzdb_t *dlzdatabase)
582{
583	isc_result_t result;
584	dns_ssurule_t *rule;
585	dns_ssutable_t *table = NULL;
586
587	REQUIRE(tablep != NULL && *tablep == NULL);
588
589	result = dns_ssutable_create(mctx, &table);
590	if (result != ISC_R_SUCCESS)
591		return (result);
592
593	table->dlzdatabase = dlzdatabase;
594
595	rule = isc_mem_get(table->mctx, sizeof(dns_ssurule_t));
596	if (rule == NULL) {
597		dns_ssutable_detach(&table);
598		return (ISC_R_NOMEMORY);
599	}
600
601	rule->identity = NULL;
602	rule->name = NULL;
603	rule->types = NULL;
604	rule->grant = ISC_TRUE;
605	rule->matchtype = DNS_SSUMATCHTYPE_DLZ;
606	rule->ntypes = 0;
607	rule->types = NULL;
608	rule->magic = SSURULEMAGIC;
609
610	ISC_LIST_INITANDAPPEND(table->rules, rule, link);
611	*tablep = table;
612	return (ISC_R_SUCCESS);
613}
614