Просмотр исходного кода

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 18 лет назад
Родитель
Сommit
740a5978ac
7 измененных файлов с 120 добавлено и 11 удалено
  1. 5 0
      NEWS
  2. 4 0
      cfg.lex
  3. 4 0
      cfg.y
  4. 16 7
      doc/dns.txt
  5. 1 0
      globals.h
  6. 86 3
      resolve.c
  7. 4 1
      resolve.h

+ 5 - 0
NEWS

@@ -103,6 +103,8 @@ core:
              - devel: new PROC_INIT rank, init_child(PROC_INIT) called first
              - futex support on linux (better behaviour when waiting on 
                long held locks, almost no performance impact otherwise)
+             - when dns search list was used for resolution, store the "link"
+               between the short name and long name in cache as CNAME record
 
 new config variables:
   pmtu_discovery = 0 | 1 (default 0) - set DF bit in outbound IP if enabled
@@ -117,6 +119,9 @@ new config variables:
     dns_udp_pref=1). To completely ignore NAPTR records for a specific 
     protocol, set the corresponding protocol preference to -1 (or any other 
     negative number).  (see doc/dns.txt for more info)
+  dns_search_full_match = yes | no (default yes) - when name was resolved using
+    dns search list, check the domain added in the answer matches with one from
+    the search list (small performance hit, but more safe)
   mlock_pages = yes |no (default no) - locks all ser pages into memory making 
     it unswappable (in general one doesn't want his sip proxy swapped out :-))
   shm_force_alloc = yes | no (default no) - tries to pre-fault all the 

+ 4 - 0
cfg.lex

@@ -71,6 +71,7 @@
  *  2007-06-18  added DNS_{UDP,TCP,TLS}_PREF (andrei)
  *  2007-09-10  introduced phone2tel option which allows NOT to consider
  *              user=phone URIs as TEL URIs (jiri)
+ *  2007-10-10  added DNS_SEARCH_FMATCH (mma)
 */
 
 
@@ -244,6 +245,7 @@ DNS_RETR_TIME	dns_retr_time
 DNS_RETR_NO		dns_retr_no
 DNS_SERVERS_NO	dns_servers_no
 DNS_USE_SEARCH	dns_use_search_list
+DNS_SEARCH_FMATCH	dns_search_full_match
 /* dns cache */
 DNS_USE_CACHE	use_dns_cache
 DNS_USE_FAILOVER	use_dns_failover
@@ -485,6 +487,8 @@ EAT_ABLE	[\ \t\b\r]
 								return DNS_SERVERS_NO; }
 <INITIAL>{DNS_USE_SEARCH}	{ count(); yylval.strval=yytext;
 								return DNS_USE_SEARCH; }
+<INITIAL>{DNS_SEARCH_FMATCH}	{ count(); yylval.strval=yytext;
+								return DNS_SEARCH_FMATCH; }
 <INITIAL>{DNS_USE_CACHE}	{ count(); yylval.strval=yytext;
 								return DNS_USE_CACHE; }
 <INITIAL>{DNS_USE_FAILOVER}	{ count(); yylval.strval=yytext;

+ 4 - 0
cfg.y

@@ -84,6 +84,7 @@
  * 2007-06-16  added DDNS_SRV_LB, DNS_TRY_NAPTR (andrei)
  * 2007-09-10  introduced phone2tel option which allows NOT to consider
  *             user=phone URIs as TEL URIs (jiri)
+ * 2007-10-10  added DNS_SEARCH_FMATCH (mma)
 */
 
 %{
@@ -284,6 +285,7 @@ static struct socket_id* mk_listen_id(char*, int, int);
 %token DNS_RETR_NO
 %token DNS_SERVERS_NO
 %token DNS_USE_SEARCH
+%token DNS_SEARCH_FMATCH
 %token DNS_USE_CACHE
 %token DNS_USE_FAILOVER
 %token DNS_CACHE_FLAGS
@@ -607,6 +609,8 @@ assign_stm:
 	| DNS_SERVERS_NO error { yyerror("number expected"); }
 	| DNS_USE_SEARCH EQUAL NUMBER   { dns_search_list=$3; }
 	| DNS_USE_SEARCH error { yyerror("boolean value expected"); }
+	| DNS_SEARCH_FMATCH EQUAL NUMBER   { dns_search_fmatch=$3; }
+	| DNS_SEARCH_FMATCH error { yyerror("boolean value expected"); }
 	| DNS_USE_CACHE EQUAL NUMBER   { IF_DNS_CACHE(use_dns_cache=$3); }
 	| DNS_USE_CACHE error { yyerror("boolean value expected"); }
 	| DNS_USE_FAILOVER EQUAL NUMBER   { IF_DNS_FAILOVER(use_dns_failover=$3);}

+ 16 - 7
doc/dns.txt

@@ -97,14 +97,15 @@ DNS Resolver Options
       (according to RFC2915). If the values are positive but different, ser
       will select the NAPTR record whose protocol it prefers the most
       (the protocol with the highest dns_<proto>_pref number). If there are 
-       several NAPTR records with the same preferred protocol, ser will select        among them based on their order and preference (see RFC2915).
+      several NAPTR records with the same preferred protocol, ser will select
+      among them based on their order and preference (see RFC2915).
       To completely disable selecting a specific protocol, use  a negative
-       number. For example dns_tcp_pref=-1 will completely disable selection
-       of tcp NAPTR records, even if this will result in the NAPTR lookup
-       failure.
-       Default: dns_udp_pref=3, dns_tcp_pref=2 and dns_tls_pref=1
-       (prefer udp, but if no udp NAPTR record found or no SRV-resolvable 
-        udp NAPTR record found use tcp records and if this fails too use tls)
+      number. For example dns_tcp_pref=-1 will completely disable selection
+      of tcp NAPTR records, even if this will result in the NAPTR lookup
+      failure.
+      Default: dns_udp_pref=3, dns_tcp_pref=2 and dns_tls_pref=1
+      (prefer udp, but if no udp NAPTR record found or no SRV-resolvable 
+      udp NAPTR record found use tcp records and if this fails too use tls)
 
    dns_tcp_pref = number  (see dns_udp_pref above)
 
@@ -128,6 +129,14 @@ DNS Resolver Options
       fact search "" (so even if the search list is empty/missing there will
       still be 2 dns queries, eg. foo+'.' and foo+""+'.')
 
+   dns_search_full_match = yes/no - controls the check of the name part
+      which is found in the answer expanding the searched name before
+      the answer is treated as correct and "link" (fake CNAME record)
+      between the short name (query) and long name (answer) is created
+      which is then stored in dns_cache and reused for next queries.
+      If set to no - no additional check is done.
+      If set to yes - the additional part is checked against the search list.
+
  The maximum time a dns request can take (before failing) is:
  (dns_retr_time*dns_retr_no)*(search_list_domains) If dns_try_ipv6 is yes,
  mutliply it again by 2.

+ 1 - 0
globals.h

@@ -196,6 +196,7 @@ extern int dns_retr_time;
 extern int dns_retr_no;
 extern int dns_servers_no;
 extern int dns_search_list;
+extern int dns_search_fmatch;
 #ifdef USE_DNS_CACHE
 extern int use_dns_cache; /* 1 if the cache is enabled, 0 otherwise */
 extern int use_dns_failover; /* 1 if failover is enabled, 0 otherwise */

+ 86 - 3
resolve.c

@@ -36,6 +36,11 @@
  *  2006-08-18  get_record can append also the additional records to the
  *               returned list (andrei)
  *  2007-06-15  naptr support (andrei)
+ *  2007-10-10  short name resolution using search list supported (mma)
+ *              set dns_use_search_list=1 (default on)
+ *              new option dns_search_full_match (default on) controls
+ *              whether rest of the name is matched against search list
+ *              or blindly accepted (better performance but exploitable)
  */ 
 
 
@@ -84,7 +89,7 @@ int dns_retr_time=-1;
 int dns_retr_no=-1;
 int dns_servers_no=-1;
 int dns_search_list=-1;
-
+int dns_search_fmatch=-1;
 
 #ifdef USE_NAPTR
 void init_naptr_proto_prefs()
@@ -412,7 +417,20 @@ void free_rdata_list(struct rdata* head)
 	}
 }
 
-
+#ifdef HAVE_RESOLV_RES
+/* checks whether supplied name exists in the resolver search list
+ * returns 1 if found
+ *         0 if not found
+ */
+int match_search_list(const res_state res, char* name) {
+	int i;
+	for (i=0; (i<MAXDNSRCH) && (res->dnsrch[i]); i++) {
+		if (strcasecmp(name, res->dnsrch[i])==0) 
+			return 1;
+	}
+	return 0;
+}
+#endif
 
 /* gets the DNS records for name:type
  * 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)
 	struct rdata* rd;
 	struct srv_rdata* srv_rd;
 	struct srv_rdata* crt_srv;
+	int search_list_used;
+	int name_len;
+	struct rdata* fullname_rd;
 	
+	if (dns_search_list==0) {
+		search_list_used=0;
+		name_len=0;
+	} else {
+		search_list_used=1;
+		name_len=strlen(name);
+	}
+	fullname_rd=0;
+
 	size=res_search(name, C_IN, type, buff.buff, sizeof(buff));
 	if (size<0) {
 		DBG("get_record: lookup(%s, %d) failed\n", name, type);
@@ -527,6 +557,29 @@ again:
 		memcpy(rd->name, rec_name, rec_name_len);
 		rd->name[rec_name_len]=0;
 		rd->name_len=rec_name_len;
+		/* check if full name matches */
+		if ((search_list_used==1)&&(fullname_rd==0)&&
+				(rec_name_len>=name_len)&&
+				(strncasecmp(rec_name, name, name_len)==0)) {
+			/* now we have record which's name is the same (up-to the name_len
+			 * with the searched one
+			 * if the length is the same - we found full match, no fake cname
+			 *   needed, just clear the flag
+			 * if the length of the name differs - it has matched using search list
+			 *   remember the rd, so we can create fake CNAME record when all answers
+			 *   are used and no better match found
+			 */
+			if (rec_name_len==name_len)
+				search_list_used=0;
+			/* this is safe.... here was rec_name_len > name_len */
+			else if (rec_name[name_len]=='.') {
+#ifdef HAVE_RESOLV_RES
+				if ((dns_search_fmatch==0) ||
+						(match_search_list(&_res, rec_name+name_len+1)!=0))
+#endif
+					fullname_rd=rd;
+			}
+		}
 		switch(rtype){
 			case T_SRV:
 				srv_rd= dns_srv_parser(buff.buff, end, p);
@@ -609,7 +662,36 @@ again:
 #endif
 		goto again; /* add also the additional records */
 	}
-			
+
+    /* if the name was expanded using DNS search list
+	 * create fake CNAME record to convert the short name
+	 * (queried) to long name (answered)
+	 */
+    if ((search_list_used==1)&&(fullname_rd!=0)) {
+		rd=(struct rdata*) local_malloc(sizeof(struct rdata)+name_len+1-1);
+		if (rd==0){
+			LOG(L_ERR, "ERROR: get_record: out of memory\n");
+			goto error;
+		}
+		rd->type=T_CNAME;
+		rd->class=fullname_rd->class;
+		rd->ttl=fullname_rd->ttl;
+		rd->next=head;
+		memcpy(rd->name, name, name_len);
+		rd->name[name_len]=0;
+		rd->name_len=name_len;
+		/* alloc sizeof struct + space for the null terminated name */
+		rd->rdata=(void*)local_malloc(sizeof(struct cname_rdata)-1+head->name_len+1);
+		if(rd->rdata==0){
+			LOG(L_ERR, "ERROR: get_record: out of memory\n");
+			goto error_rd;
+		}
+		((struct cname_rdata*)(rd->rdata))->name_len=fullname_rd->name_len;
+		memcpy(((struct cname_rdata*)(rd->rdata))->name, fullname_rd->name, fullname_rd->name_len);
+		((struct cname_rdata*)(rd->rdata))->name[head->name_len]=0;
+		head=rd;
+	}
+
 	return head;
 error_boundary:
 		LOG(L_ERR, "ERROR: get_record: end of query buff reached\n");
@@ -620,6 +702,7 @@ error_parse:
 						" rtype=%d, class=%d, ttl=%d, rdlength=%d \n",
 				name, type,
 				p, end, rtype, class, ttl, rdlength);
+error_rd:
 		if (rd) local_free(rd); /* rd->rdata=0 & rd is not linked yet into
 								   the list */
 error:

+ 4 - 1
resolve.h

@@ -45,6 +45,7 @@
 #include <sys/socket.h>
 #include <netdb.h>
 #include <arpa/nameser.h>
+#include <resolv.h>
 
 #ifdef __OS_darwin
 #include <arpa/nameser_compat.h>
@@ -151,7 +152,9 @@ struct cname_rdata {
 #define CNAME_RDATA_SIZE(s) (sizeof(struct cname_rdata)+(s).name_len)
 
 
-
+#ifdef HAVE_RESOLV_RES
+int match_search_list(const res_state res, char* name);
+#endif
 struct rdata* get_record(char* name, int type, int flags);
 void free_rdata_list(struct rdata* head);