Browse code

Handle A and MX

git-svn: trunk@3146

Nigel Horne authored on 2007/07/15 08:45:36
Showing 4 changed files
... ...
@@ -1,7 +1,11 @@
1
+Sat Jul 14 23:50:56 BST 2007 (njh)
2
+----------------------------------
3
+  * clamav-milter:	Experimental mode: Handle A and MX in SPF records
4
+
1 5
 Sat Jul 14 22:07:16 BST 2007 (njh)
2 6
 ----------------------------------
3 7
   * clamav-milter:	Experimental mode: basic SPF parser to reduce
4
-  				phish false-positives
8
+				phish false-positives
5 9
 			Possible fix for 487
6 10
 			Some small tidies
7 11
 
... ...
@@ -20,7 +24,7 @@ Thu Jul 12 11:41:15 BST 2007 (trog)
20 20
 Thu Jul 12 01:41:56 CEST 2007 (acab)
21 21
 ------------------------------------
22 22
   * libclamav: rename x86 macroes due to collisions on HPUX
23
-  		reported by njh
23
+		reported by njh
24 24
 
25 25
 Wed Jul 11 10:20:53 BST 2007 (njh)
26 26
 ----------------------------------
... ...
@@ -69,7 +73,7 @@ Tue Jul 10 22:02:15 CEST 2007 (tk)
69 69
 Sun Jul  8 17:25:04 CEST 2007 (acab)
70 70
 ------------------------------------
71 71
   * misc: Implement compiler indepenedent sign-extended signed right shift
72
-  	  when needed - reported by Michal Spadlinski <gim913 * gmail.com>
72
+	  when needed - reported by Michal Spadlinski <gim913 * gmail.com>
73 73
 
74 74
 Sat Jul 07 10:52:00 CEST 2007 (edwin)
75 75
 ----------------------------------
... ...
@@ -336,12 +336,13 @@ static	int	isLocal(const char *addr);
336 336
 static	void	clamdIsDown(void);
337 337
 static	void	*watchdog(void *a);
338 338
 static	int	check_and_reload_database(void);
339
-static	void	timeoutBlacklist(char *ip_address, int time_of_blacklist);
339
+static	void	timeoutBlacklist(char *ip_address, int time_of_blacklist, void *v);
340 340
 static	void	quit(void);
341 341
 static	void	broadcast(const char *mess);
342 342
 static	int	loadDatabase(void);
343 343
 static	int	increment_connexions(void);
344 344
 static	void	decrement_connexions(void);
345
+static	void	dump_blacklist(char *key, int value, void *v);
345 346
 
346 347
 #ifdef	SESSION
347 348
 static	pthread_mutex_t	version_mutex = PTHREAD_MUTEX_INITIALIZER;
... ...
@@ -562,11 +563,12 @@ static	void	print_trace(void);
562 562
 static	int	verifyIncomingSocketName(const char *sockName);
563 563
 static	int	isWhitelisted(const char *emailaddress, int to);
564 564
 static	int	isBlacklisted(const char *ip_address);
565
-static	void	mx(void);
565
+static	table_t	*mx(const char *host, table_t *t);
566 566
 #ifdef	HAVE_RESOLV_H
567
-static	void	resolve(const char *host);
567
+static	table_t	*resolve(const char *host, table_t *t);
568 568
 #ifdef	CL_EXPERIMENTAL
569 569
 static	void	spf(struct privdata *privdata);
570
+static	void	spf_ip(char *ip, int zero, void *v);
570 571
 #endif
571 572
 #endif
572 573
 static	sfsistat	black_hole(const struct privdata *privdata);
... ...
@@ -2006,7 +2008,14 @@ main(int argc, char **argv)
2006 2006
 	logg(_("*Debugging is on\n"));
2007 2007
 
2008 2008
 	if(blacklist_time) {
2009
-		mx();
2009
+		char name[MAXHOSTNAMELEN + 1];
2010
+
2011
+		if(gethostname(name, sizeof(name)) < 0) {
2012
+			perror("gethostname");
2013
+			return EX_UNAVAILABLE;
2014
+		}
2015
+
2016
+		blacklist = mx(name, NULL);
2010 2017
 		if(blacklist)
2011 2018
 			/* We must never blacklist ourself */
2012 2019
 			tableInsert(blacklist, "127.0.0.1", 0);
... ...
@@ -2016,11 +2025,11 @@ main(int argc, char **argv)
2016 2016
 
2017 2017
 			i = 0;
2018 2018
 			while((w = cli_strtok(wont_blacklist, i++, ",")) != NULL) {
2019
-				logg(_("Won't blacklist %s\n"), w);
2020 2019
 				(void)tableInsert(blacklist, w, 0);
2021 2020
 				free(w);
2022 2021
 			}
2023 2022
 		}
2023
+		tableIterate(blacklist, dump_blacklist, NULL);
2024 2024
 	}
2025 2025
 
2026 2026
 	cli_dbgmsg("Started: %s\n", clamav_version);
... ...
@@ -5361,7 +5370,7 @@ watchdog(void *a)
5361 5361
 		/* Garbage collect IP addresses no longer blacklisted */
5362 5362
 		if(blacklist) {
5363 5363
 			pthread_mutex_lock(&blacklist_mutex);
5364
-			tableIterate(blacklist, timeoutBlacklist);
5364
+			tableIterate(blacklist, timeoutBlacklist, NULL);
5365 5365
 			pthread_mutex_unlock(&blacklist_mutex);
5366 5366
 		}
5367 5367
 	}
... ...
@@ -5424,7 +5433,7 @@ watchdog(void *a)
5424 5424
 		/* Garbage collect IP addresses no longer blacklisted */
5425 5425
 		if(blacklist) {
5426 5426
 			pthread_mutex_lock(&blacklist_mutex);
5427
-			tableIterate(blacklist, timeoutBlacklist);
5427
+			tableIterate(blacklist, timeoutBlacklist, NULL);
5428 5428
 			pthread_mutex_unlock(&blacklist_mutex);
5429 5429
 		}
5430 5430
 	}
... ...
@@ -5467,7 +5476,7 @@ check_and_reload_database(void)
5467 5467
 }
5468 5468
 
5469 5469
 static void
5470
-timeoutBlacklist(char *ip_address, int time_of_blacklist)
5470
+timeoutBlacklist(char *ip_address, int time_of_blacklist, void *v)
5471 5471
 {
5472 5472
 	if(time_of_blacklist == 0)	/* Must not blacklist this IP address */
5473 5473
 		return;
... ...
@@ -5961,8 +5970,8 @@ isBlacklisted(const char *ip_address)
5961 5961
  * This is only ever called once, which is wrong, but the overheard of calling
5962 5962
  * this from the watchdog isn't worth it
5963 5963
  */
5964
-static void
5965
-mx(void)
5964
+static table_t *
5965
+mx(const char *host, table_t *t)
5966 5966
 {
5967 5967
 	u_char *p, *end;
5968 5968
 	const HEADER *hp;
... ...
@@ -5971,37 +5980,31 @@ mx(void)
5971 5971
 		HEADER h;
5972 5972
 		u_char u[PACKETSZ];
5973 5973
 	} q;
5974
-	char name[MAXHOSTNAMELEN + 1];
5975 5974
 	char buf[BUFSIZ];
5976 5975
 
5977
-	if(gethostname(name, sizeof(name)) < 0) {
5978
-		perror("gethostname");
5979
-		return;
5980
-	}
5981
-
5982
-	if(blacklist == NULL) {
5983
-		blacklist = tableCreate();
5976
+	if(t == NULL) {
5977
+		t = tableCreate();
5984 5978
 
5985
-		if(blacklist == NULL)
5986
-			return;
5979
+		if(t == NULL)
5980
+			return NULL;
5987 5981
 	}
5988 5982
 
5989 5983
 	was_initialised = _res.options & RES_INIT;
5990 5984
 
5991 5985
 	if((!was_initialised) && res_init() < 0)
5992
-		return;
5986
+		return t;
5993 5987
 
5994
-	len = res_query(name, C_IN, T_MX, (u_char *)&q, sizeof(q));
5988
+	len = res_query(host, C_IN, T_MX, (u_char *)&q, sizeof(q));
5995 5989
 	if(len < 0) {
5996 5990
 		if(!was_initialised)
5997 5991
 			res_close();
5998
-		return;	/* Host has no MX records */
5992
+		return t;	/* Host has no MX records */
5999 5993
 	}
6000 5994
 
6001 5995
 	if((unsigned int)len > sizeof(q)) {
6002 5996
 		if(!was_initialised)
6003 5997
 			res_close();
6004
-		return;
5998
+		return t;
6005 5999
 	}
6006 6000
 
6007 6001
 	hp = &(q.h);
... ...
@@ -6012,7 +6015,7 @@ mx(void)
6012 6012
 		if((len = dn_skipname(p, end)) < 0) {
6013 6013
 			if(!was_initialised)
6014 6014
 				res_close();
6015
-			return;
6015
+			return t;
6016 6016
 		}
6017 6017
 
6018 6018
 	i = ntohs(hp->ancount);
... ...
@@ -6043,21 +6046,22 @@ mx(void)
6043 6043
 #else
6044 6044
 		if(addr != (in_addr_t)-1) {
6045 6045
 #endif
6046
-			logg(_("Won't blacklist %s\n"), buf);
6047
-			(void)tableInsert(blacklist, buf, 0);
6046
+			(void)tableInsert(t, buf, 0);
6048 6047
 		} else
6049
-			resolve(buf);
6048
+			t = resolve(buf, t);
6050 6049
 	}
6051 6050
 	if(!was_initialised)
6052 6051
 		res_close();
6052
+
6053
+	return t;
6053 6054
 }
6054 6055
 
6055 6056
 /*
6056 6057
  * If the MX record points to a name, we need to resolve that name. This routine
6057 6058
  * does that
6058 6059
  */
6059
-static void
6060
-resolve(const char *host)
6060
+static table_t *
6061
+resolve(const char *host, table_t *t)
6061 6062
 {
6062 6063
 	u_char *p, *end;
6063 6064
 	const HEADER *hp;
... ...
@@ -6069,14 +6073,14 @@ resolve(const char *host)
6069 6069
 	char buf[BUFSIZ];
6070 6070
 
6071 6071
 	if((host == NULL) || (*host == '\0'))
6072
-		return;
6072
+		return t;
6073 6073
 
6074 6074
 	len = res_query(host, C_IN, T_A, (u_char *)&q, sizeof(q));
6075 6075
 	if(len < 0)
6076
-		return;	/* Host has no A records */
6076
+		return t;	/* Host has no A records */
6077 6077
 
6078 6078
 	if((unsigned int)len > sizeof(q))
6079
-		return;
6079
+		return t;
6080 6080
 
6081 6081
 	hp = &(q.h);
6082 6082
 	p = q.u + HFIXEDSZ;
... ...
@@ -6084,18 +6088,18 @@ resolve(const char *host)
6084 6084
 
6085 6085
 	for(i = ntohs(hp->qdcount); i--; p += len + QFIXEDSZ)
6086 6086
 		if((len = dn_skipname(p, end)) < 0)
6087
-			return;
6087
+			return t;
6088 6088
 
6089 6089
 	i = ntohs(hp->ancount);
6090 6090
 
6091 6091
 	while((--i >= 0) && (p < end)) {
6092 6092
 		u_short type;
6093 6093
 		u_long ttl;
6094
-		struct in_addr addr;
6095 6094
 		const char *ip;
6095
+		struct in_addr addr;
6096 6096
 
6097 6097
 		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0)
6098
-			 return;
6098
+			 return t;
6099 6099
 		p += len;
6100 6100
 		GETSHORT(type, p);
6101 6101
 		p += INT16SZ;
... ...
@@ -6109,10 +6113,16 @@ resolve(const char *host)
6109 6109
 		p += 4;	/* Should check len == 4 */
6110 6110
 		ip = inet_ntoa(addr);
6111 6111
 		if(ip) {
6112
-			logg(_("Won't blacklist %s\n"), ip);
6113
-			(void)tableInsert(blacklist, ip, 0);
6112
+			if(t == NULL) {
6113
+				t = tableCreate();
6114
+
6115
+				if(t == NULL)
6116
+					return NULL;
6117
+			}
6118
+			(void)tableInsert(t, ip, 0);
6114 6119
 		}
6115 6120
 	}
6121
+	return t;
6116 6122
 }
6117 6123
 
6118 6124
 #ifdef	CL_EXPERIMENTAL
... ...
@@ -6127,7 +6137,7 @@ resolve(const char *host)
6127 6127
 static void
6128 6128
 spf(struct privdata *privdata)
6129 6129
 {
6130
-	char *mailaddr, *ptr;
6130
+	char *host, *ptr;
6131 6131
 	u_char *p, *end;
6132 6132
 	const HEADER *hp;
6133 6133
 	int len, i;
... ...
@@ -6152,27 +6162,27 @@ spf(struct privdata *privdata)
6152 6152
 
6153 6153
 	if(privdata->from == NULL)
6154 6154
 		return;
6155
-	if((mailaddr = strchr(privdata->from, '@')) == NULL)
6155
+	if((host = strchr(privdata->from, '@')) == NULL)
6156 6156
 		return;
6157 6157
 
6158
-	mailaddr = cli_strdup(++mailaddr);
6158
+	host = cli_strdup(++host);
6159 6159
 
6160
-	if(mailaddr == NULL)
6160
+	if(host == NULL)
6161 6161
 		return;
6162 6162
 
6163
-	ptr = strchr(mailaddr, '>');
6163
+	ptr = strchr(host, '>');
6164 6164
 
6165 6165
 	if(ptr)
6166 6166
 		*ptr = '\0';
6167 6167
 
6168
-	len = res_query(mailaddr, C_IN, T_TXT, (u_char *)&q, sizeof(q));
6168
+	len = res_query(host, C_IN, T_TXT, (u_char *)&q, sizeof(q));
6169 6169
 	if(len < 0) {
6170
-		free(mailaddr);
6170
+		free(host);
6171 6171
 		return;	/* Host has no TXT records */
6172 6172
 	}
6173 6173
 
6174 6174
 	if((unsigned int)len > sizeof(q)) {
6175
-		free(mailaddr);
6175
+		free(host);
6176 6176
 		return;
6177 6177
 	}
6178 6178
 
... ...
@@ -6182,7 +6192,7 @@ spf(struct privdata *privdata)
6182 6182
 
6183 6183
 	for(i = ntohs(hp->qdcount); i--; p += len + QFIXEDSZ)
6184 6184
 		if((len = dn_skipname(p, end)) < 0) {
6185
-			free(mailaddr);
6185
+			free(host);
6186 6186
 			return;
6187 6187
 		}
6188 6188
 
... ...
@@ -6194,7 +6204,7 @@ spf(struct privdata *privdata)
6194 6194
 		char txt[BUFSIZ];
6195 6195
 
6196 6196
 		if((len = dn_expand(q.u, end, p, buf, sizeof(buf) - 1)) < 0) {
6197
-			free(mailaddr);
6197
+			free(host);
6198 6198
 			return;
6199 6199
 		}
6200 6200
 		p += len;
... ...
@@ -6214,9 +6224,9 @@ spf(struct privdata *privdata)
6214 6214
 			struct in_addr remote_ip;	/* IP connecting to us */
6215 6215
 
6216 6216
 			logg("%s(%s): SPF record %s\n",
6217
-				mailaddr, privdata->ip, txt);
6217
+				host, privdata->ip, txt);
6218 6218
 			/*
6219
-			 * This is were the beef of the check will go. This
6219
+			 * This is where the beef of the check will go. This
6220 6220
 			 * trivial check is of little real benefit, but it
6221 6221
 			 * won't create false positives.
6222 6222
 			 */
... ...
@@ -6267,18 +6277,45 @@ spf(struct privdata *privdata)
6267 6267
 #endif
6268 6268
 					mask = MAKEMASK(preflen);
6269 6269
 					if((ntohl(remote_ip.s_addr) & mask) == (ntohl(spf_range.s_addr) & mask)) {
6270
-						free(record);
6271 6270
 						logg("SPF ip4 pass\n");
6272 6271
 						privdata->spf_ok = 1;
6273
-						break;
6272
+					}
6273
+				} else if(strcmp(record, "mx") == 0) {
6274
+					table_t *t = mx(host, NULL);
6275
+
6276
+					if(t) {
6277
+						tableIterate(t, spf_ip,
6278
+							(void *)privdata);
6279
+						tableDestroy(t);
6280
+					}
6281
+				} else if(strcmp(record, "a") == 0) {
6282
+					table_t *t = resolve(host, NULL);
6283
+
6284
+					if(t) {
6285
+						tableIterate(t, spf_ip,
6286
+							(void *)privdata);
6287
+						tableDestroy(t);
6274 6288
 					}
6275 6289
 				}
6276 6290
 				free(record);
6291
+				if(privdata->spf_ok)
6292
+					break;
6277 6293
 			}
6278 6294
 		}
6279 6295
 		p += len;
6280 6296
 	}
6281
-	free(mailaddr);
6297
+	free(host);
6298
+}
6299
+
6300
+static void
6301
+spf_ip(char *ip, int zero, void *v)
6302
+{
6303
+	struct privdata *privdata = (struct privdata *)v;
6304
+
6305
+	if(strcmp(ip, privdata->ip) == 0) {
6306
+		logg("SPF mx/a pass %s\n", ip);
6307
+		privdata->spf_ok = 1;
6308
+	}
6282 6309
 }
6283 6310
 
6284 6311
 #endif	/*CL_EXPERIMENTAL*/
... ...
@@ -6466,3 +6503,9 @@ decrement_connexions(void)
6466 6466
 		pthread_mutex_unlock(&n_children_mutex);
6467 6467
 	}
6468 6468
 }
6469
+
6470
+static void
6471
+dump_blacklist(char *key, int value, void *v)
6472
+{
6473
+	logg(_("Won't blacklist %s\n"), key);
6474
+}
... ...
@@ -199,7 +199,7 @@ tableRemove(table_t *table, const char *key)
199 199
 }
200 200
 
201 201
 void
202
-tableIterate(table_t *table, void(*callback)(char *key, int value))
202
+tableIterate(table_t *table, void(*callback)(char *key, int value, void *arg), void *arg)
203 203
 {
204 204
 	tableEntry *tableItem;
205 205
 
... ...
@@ -208,5 +208,5 @@ tableIterate(table_t *table, void(*callback)(char *key, int value))
208 208
 
209 209
 	for(tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
210 210
 		if(tableItem->key)	/* check node has not been deleted */
211
-			(*callback)(tableItem->key, tableItem->value);
211
+			(*callback)(tableItem->key, tableItem->value, arg);
212 212
 }
... ...
@@ -40,4 +40,4 @@ int	tableInsert(table_t *table, const char *key, int value);
40 40
 int	tableUpdate(table_t *table, const char *key, int new_value);
41 41
 int	tableFind(const table_t *table, const char *key);
42 42
 void	tableRemove(table_t *table, const char *key);
43
-void	tableIterate(table_t *table, void(*callback)(char *key, int value));
43
+void	tableIterate(table_t *table, void(*callback)(char *key, int value, void *arg), void *arg);