Browse code

fixes SER-321 patch reflecting comments applied

Change to DNS subsystem: if search list used (not recomended) and name
was resolved using the search list, the link between the short name (query)
and the long name (answer) is stored in DNS cache as fake CNAME record.

New config script setting available (dns_search_full_match) which controls,
whether SER will check if the extension of the name is present in search list
(defualt) or blindly accepted (faster, but little bit risky for spoofed
DNS replies)

Michal Matyska authored on 12/10/2007 22:25:40
Showing 7 changed files
... ...
@@ -103,6 +103,8 @@ core:
103 103
              - devel: new PROC_INIT rank, init_child(PROC_INIT) called first
104 104
              - futex support on linux (better behaviour when waiting on 
105 105
                long held locks, almost no performance impact otherwise)
106
+             - when dns search list was used for resolution, store the "link"
107
+               between the short name and long name in cache as CNAME record
106 108
 
107 109
 new config variables:
108 110
   pmtu_discovery = 0 | 1 (default 0) - set DF bit in outbound IP if enabled
... ...
@@ -117,6 +119,9 @@ new config variables:
117 117
     dns_udp_pref=1). To completely ignore NAPTR records for a specific 
118 118
     protocol, set the corresponding protocol preference to -1 (or any other 
119 119
     negative number).  (see doc/dns.txt for more info)
120
+  dns_search_full_match = yes | no (default yes) - when name was resolved using
121
+    dns search list, check the domain added in the answer matches with one from
122
+    the search list (small performance hit, but more safe)
120 123
   mlock_pages = yes |no (default no) - locks all ser pages into memory making 
121 124
     it unswappable (in general one doesn't want his sip proxy swapped out :-))
122 125
   shm_force_alloc = yes | no (default no) - tries to pre-fault all the 
... ...
@@ -71,6 +71,7 @@
71 71
  *  2007-06-18  added DNS_{UDP,TCP,TLS}_PREF (andrei)
72 72
  *  2007-09-10  introduced phone2tel option which allows NOT to consider
73 73
  *              user=phone URIs as TEL URIs (jiri)
74
+ *  2007-10-10  added DNS_SEARCH_FMATCH (mma)
74 75
 */
75 76
 
76 77
 
... ...
@@ -244,6 +245,7 @@ DNS_RETR_TIME	dns_retr_time
244 244
 DNS_RETR_NO		dns_retr_no
245 245
 DNS_SERVERS_NO	dns_servers_no
246 246
 DNS_USE_SEARCH	dns_use_search_list
247
+DNS_SEARCH_FMATCH	dns_search_full_match
247 248
 /* dns cache */
248 249
 DNS_USE_CACHE	use_dns_cache
249 250
 DNS_USE_FAILOVER	use_dns_failover
... ...
@@ -485,6 +487,8 @@ EAT_ABLE	[\ \t\b\r]
485 485
 								return DNS_SERVERS_NO; }
486 486
 <INITIAL>{DNS_USE_SEARCH}	{ count(); yylval.strval=yytext;
487 487
 								return DNS_USE_SEARCH; }
488
+<INITIAL>{DNS_SEARCH_FMATCH}	{ count(); yylval.strval=yytext;
489
+								return DNS_SEARCH_FMATCH; }
488 490
 <INITIAL>{DNS_USE_CACHE}	{ count(); yylval.strval=yytext;
489 491
 								return DNS_USE_CACHE; }
490 492
 <INITIAL>{DNS_USE_FAILOVER}	{ count(); yylval.strval=yytext;
... ...
@@ -84,6 +84,7 @@
84 84
  * 2007-06-16  added DDNS_SRV_LB, DNS_TRY_NAPTR (andrei)
85 85
  * 2007-09-10  introduced phone2tel option which allows NOT to consider
86 86
  *             user=phone URIs as TEL URIs (jiri)
87
+ * 2007-10-10  added DNS_SEARCH_FMATCH (mma)
87 88
 */
88 89
 
89 90
 %{
... ...
@@ -284,6 +285,7 @@ static struct socket_id* mk_listen_id(char*, int, int);
284 284
 %token DNS_RETR_NO
285 285
 %token DNS_SERVERS_NO
286 286
 %token DNS_USE_SEARCH
287
+%token DNS_SEARCH_FMATCH
287 288
 %token DNS_USE_CACHE
288 289
 %token DNS_USE_FAILOVER
289 290
 %token DNS_CACHE_FLAGS
... ...
@@ -607,6 +609,8 @@ assign_stm:
607 607
 	| DNS_SERVERS_NO error { yyerror("number expected"); }
608 608
 	| DNS_USE_SEARCH EQUAL NUMBER   { dns_search_list=$3; }
609 609
 	| DNS_USE_SEARCH error { yyerror("boolean value expected"); }
610
+	| DNS_SEARCH_FMATCH EQUAL NUMBER   { dns_search_fmatch=$3; }
611
+	| DNS_SEARCH_FMATCH error { yyerror("boolean value expected"); }
610 612
 	| DNS_USE_CACHE EQUAL NUMBER   { IF_DNS_CACHE(use_dns_cache=$3); }
611 613
 	| DNS_USE_CACHE error { yyerror("boolean value expected"); }
612 614
 	| DNS_USE_FAILOVER EQUAL NUMBER   { IF_DNS_FAILOVER(use_dns_failover=$3);}
... ...
@@ -97,14 +97,15 @@ DNS Resolver Options
97 97
       (according to RFC2915). If the values are positive but different, ser
98 98
       will select the NAPTR record whose protocol it prefers the most
99 99
       (the protocol with the highest dns_<proto>_pref number). If there are 
100
-       several NAPTR records with the same preferred protocol, ser will select        among them based on their order and preference (see RFC2915).
100
+      several NAPTR records with the same preferred protocol, ser will select
101
+      among them based on their order and preference (see RFC2915).
101 102
       To completely disable selecting a specific protocol, use  a negative
102
-       number. For example dns_tcp_pref=-1 will completely disable selection
103
-       of tcp NAPTR records, even if this will result in the NAPTR lookup
104
-       failure.
105
-       Default: dns_udp_pref=3, dns_tcp_pref=2 and dns_tls_pref=1
106
-       (prefer udp, but if no udp NAPTR record found or no SRV-resolvable 
107
-        udp NAPTR record found use tcp records and if this fails too use tls)
103
+      number. For example dns_tcp_pref=-1 will completely disable selection
104
+      of tcp NAPTR records, even if this will result in the NAPTR lookup
105
+      failure.
106
+      Default: dns_udp_pref=3, dns_tcp_pref=2 and dns_tls_pref=1
107
+      (prefer udp, but if no udp NAPTR record found or no SRV-resolvable 
108
+      udp NAPTR record found use tcp records and if this fails too use tls)
108 109
 
109 110
    dns_tcp_pref = number  (see dns_udp_pref above)
110 111
 
... ...
@@ -128,6 +129,14 @@ DNS Resolver Options
128 128
       fact search "" (so even if the search list is empty/missing there will
129 129
       still be 2 dns queries, eg. foo+'.' and foo+""+'.')
130 130
 
131
+   dns_search_full_match = yes/no - controls the check of the name part
132
+      which is found in the answer expanding the searched name before
133
+      the answer is treated as correct and "link" (fake CNAME record)
134
+      between the short name (query) and long name (answer) is created
135
+      which is then stored in dns_cache and reused for next queries.
136
+      If set to no - no additional check is done.
137
+      If set to yes - the additional part is checked against the search list.
138
+
131 139
  The maximum time a dns request can take (before failing) is:
132 140
  (dns_retr_time*dns_retr_no)*(search_list_domains) If dns_try_ipv6 is yes,
133 141
  mutliply it again by 2.
... ...
@@ -196,6 +196,7 @@ extern int dns_retr_time;
196 196
 extern int dns_retr_no;
197 197
 extern int dns_servers_no;
198 198
 extern int dns_search_list;
199
+extern int dns_search_fmatch;
199 200
 #ifdef USE_DNS_CACHE
200 201
 extern int use_dns_cache; /* 1 if the cache is enabled, 0 otherwise */
201 202
 extern int use_dns_failover; /* 1 if failover is enabled, 0 otherwise */
... ...
@@ -36,6 +36,11 @@
36 36
  *  2006-08-18  get_record can append also the additional records to the
37 37
  *               returned list (andrei)
38 38
  *  2007-06-15  naptr support (andrei)
39
+ *  2007-10-10  short name resolution using search list supported (mma)
40
+ *              set dns_use_search_list=1 (default on)
41
+ *              new option dns_search_full_match (default on) controls
42
+ *              whether rest of the name is matched against search list
43
+ *              or blindly accepted (better performance but exploitable)
39 44
  */ 
40 45
 
41 46
 
... ...
@@ -84,7 +89,7 @@ int dns_retr_time=-1;
84 84
 int dns_retr_no=-1;
85 85
 int dns_servers_no=-1;
86 86
 int dns_search_list=-1;
87
-
87
+int dns_search_fmatch=-1;
88 88
 
89 89
 #ifdef USE_NAPTR
90 90
 void init_naptr_proto_prefs()
... ...
@@ -412,7 +417,20 @@ void free_rdata_list(struct rdata* head)
412 412
 	}
413 413
 }
414 414
 
415
-
415
+#ifdef HAVE_RESOLV_RES
416
+/* checks whether supplied name exists in the resolver search list
417
+ * returns 1 if found
418
+ *         0 if not found
419
+ */
420
+int match_search_list(const res_state res, char* name) {
421
+	int i;
422
+	for (i=0; (i<MAXDNSRCH) && (res->dnsrch[i]); i++) {
423
+		if (strcasecmp(name, res->dnsrch[i])==0) 
424
+			return 1;
425
+	}
426
+	return 0;
427
+}
428
+#endif
416 429
 
417 430
 /* gets the DNS records for name:type
418 431
  * returns a dyn. alloc'ed struct rdata linked list with the parsed responses
... ...
@@ -437,7 +455,19 @@ struct rdata* get_record(char* name, int type, int flags)
437 437
 	struct rdata* rd;
438 438
 	struct srv_rdata* srv_rd;
439 439
 	struct srv_rdata* crt_srv;
440
+	int search_list_used;
441
+	int name_len;
442
+	struct rdata* fullname_rd;
440 443
 	
444
+	if (dns_search_list==0) {
445
+		search_list_used=0;
446
+		name_len=0;
447
+	} else {
448
+		search_list_used=1;
449
+		name_len=strlen(name);
450
+	}
451
+	fullname_rd=0;
452
+
441 453
 	size=res_search(name, C_IN, type, buff.buff, sizeof(buff));
442 454
 	if (size<0) {
443 455
 		DBG("get_record: lookup(%s, %d) failed\n", name, type);
... ...
@@ -527,6 +557,29 @@ again:
527 527
 		memcpy(rd->name, rec_name, rec_name_len);
528 528
 		rd->name[rec_name_len]=0;
529 529
 		rd->name_len=rec_name_len;
530
+		/* check if full name matches */
531
+		if ((search_list_used==1)&&(fullname_rd==0)&&
532
+				(rec_name_len>=name_len)&&
533
+				(strncasecmp(rec_name, name, name_len)==0)) {
534
+			/* now we have record which's name is the same (up-to the name_len
535
+			 * with the searched one
536
+			 * if the length is the same - we found full match, no fake cname
537
+			 *   needed, just clear the flag
538
+			 * if the length of the name differs - it has matched using search list
539
+			 *   remember the rd, so we can create fake CNAME record when all answers
540
+			 *   are used and no better match found
541
+			 */
542
+			if (rec_name_len==name_len)
543
+				search_list_used=0;
544
+			/* this is safe.... here was rec_name_len > name_len */
545
+			else if (rec_name[name_len]=='.') {
546
+#ifdef HAVE_RESOLV_RES
547
+				if ((dns_search_fmatch==0) ||
548
+						(match_search_list(&_res, rec_name+name_len+1)!=0))
549
+#endif
550
+					fullname_rd=rd;
551
+			}
552
+		}
530 553
 		switch(rtype){
531 554
 			case T_SRV:
532 555
 				srv_rd= dns_srv_parser(buff.buff, end, p);
... ...
@@ -609,7 +662,36 @@ again:
609 609
 #endif
610 610
 		goto again; /* add also the additional records */
611 611
 	}
612
-			
612
+
613
+    /* if the name was expanded using DNS search list
614
+	 * create fake CNAME record to convert the short name
615
+	 * (queried) to long name (answered)
616
+	 */
617
+    if ((search_list_used==1)&&(fullname_rd!=0)) {
618
+		rd=(struct rdata*) local_malloc(sizeof(struct rdata)+name_len+1-1);
619
+		if (rd==0){
620
+			LOG(L_ERR, "ERROR: get_record: out of memory\n");
621
+			goto error;
622
+		}
623
+		rd->type=T_CNAME;
624
+		rd->class=fullname_rd->class;
625
+		rd->ttl=fullname_rd->ttl;
626
+		rd->next=head;
627
+		memcpy(rd->name, name, name_len);
628
+		rd->name[name_len]=0;
629
+		rd->name_len=name_len;
630
+		/* alloc sizeof struct + space for the null terminated name */
631
+		rd->rdata=(void*)local_malloc(sizeof(struct cname_rdata)-1+head->name_len+1);
632
+		if(rd->rdata==0){
633
+			LOG(L_ERR, "ERROR: get_record: out of memory\n");
634
+			goto error_rd;
635
+		}
636
+		((struct cname_rdata*)(rd->rdata))->name_len=fullname_rd->name_len;
637
+		memcpy(((struct cname_rdata*)(rd->rdata))->name, fullname_rd->name, fullname_rd->name_len);
638
+		((struct cname_rdata*)(rd->rdata))->name[head->name_len]=0;
639
+		head=rd;
640
+	}
641
+
613 642
 	return head;
614 643
 error_boundary:
615 644
 		LOG(L_ERR, "ERROR: get_record: end of query buff reached\n");
... ...
@@ -620,6 +702,7 @@ error_parse:
620 620
 						" rtype=%d, class=%d, ttl=%d, rdlength=%d \n",
621 621
 				name, type,
622 622
 				p, end, rtype, class, ttl, rdlength);
623
+error_rd:
623 624
 		if (rd) local_free(rd); /* rd->rdata=0 & rd is not linked yet into
624 625
 								   the list */
625 626
 error:
... ...
@@ -45,6 +45,7 @@
45 45
 #include <sys/socket.h>
46 46
 #include <netdb.h>
47 47
 #include <arpa/nameser.h>
48
+#include <resolv.h>
48 49
 
49 50
 #ifdef __OS_darwin
50 51
 #include <arpa/nameser_compat.h>
... ...
@@ -151,7 +152,9 @@ struct cname_rdata {
151 151
 #define CNAME_RDATA_SIZE(s) (sizeof(struct cname_rdata)+(s).name_len)
152 152
 
153 153
 
154
-
154
+#ifdef HAVE_RESOLV_RES
155
+int match_search_list(const res_state res, char* name);
156
+#endif
155 157
 struct rdata* get_record(char* name, int type, int flags);
156 158
 void free_rdata_list(struct rdata* head);
157 159