1 /* $Id: upnppinhole.c,v 1.14 2019/05/21 08:39:45 nanard Exp $ */
2 /* vim: tabstop=4 shiftwidth=4 noexpandtab
3  * MiniUPnP project
4  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5  * (c) 2006-2019 Thomas Bernard
6  * This software is subject to the conditions detailed
7  * in the LICENCE file provided within the distribution */
8 
9 #include <stdlib.h>
10 #include <string.h>
11 #include <syslog.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <net/if.h>
16 #include <arpa/inet.h>
17 
18 #include <stdio.h>
19 #include <ctype.h>
20 #include <unistd.h>
21 
22 #include "macros.h"
23 #include "config.h"
24 #include "upnpredirect.h"
25 #include "upnpglobalvars.h"
26 #include "upnpevents.h"
27 #include "upnputils.h"
28 #include "upnppinhole.h"
29 #ifdef __APPLE__
30 /* XXX - Apple version of PF API seems to differ from what
31  * pf/pfpinhole.c expects so don't use that at least.. */
32 #ifdef USE_PF
33 #undef USE_PF
34 #endif /* USE_PF */
35 #endif /* __APPLE__ */
36 #if defined(USE_NETFILTER)
37 #include "netfilter/iptpinhole.h"
38 #endif
39 #if defined(USE_PF)
40 #include "pf/pfpinhole.h"
41 #endif
42 #if defined(USE_IPF)
43 #endif
44 #if defined(USE_IPFW)
45 #endif
46 
47 #ifdef ENABLE_UPNPPINHOLE
48 
49 #if 0
50 int
51 upnp_check_outbound_pinhole(int proto, int * timeout)
52 {
53 	int s, tmptimeout, tmptime_out;
54 	switch(proto)
55 	{
56 		case IPPROTO_UDP:
57 			s = retrieve_timeout("udp_timeout", timeout);
58 			return s;
59 			break;
60 		case IPPROTO_UDPLITE:
61 			s = retrieve_timeout("udp_timeout_stream", timeout);
62 			return s;
63 			break;
64 		case IPPROTO_TCP:
65 			s = retrieve_timeout("tcp_timeout_established", timeout);
66 			return s;
67 			break;
68 		case 65535:
69 			s = retrieve_timeout("udp_timeout", timeout);
70 			s = retrieve_timeout("udp_timeout_stream", &tmptimeout);
71 			s = retrieve_timeout("tcp_timeout_established", &tmptime_out);
72 			if(tmptimeout<tmptime_out)
73 			{
74 				if(tmptimeout<*timeout)
75 					*timeout = tmptimeout;
76 			}
77 			else
78 			{
79 				if(tmptime_out<*timeout)
80 					*timeout = tmptimeout;
81 			}
82 			return s;
83 			break;
84 		default:
85 			return -5;
86 			break;
87 	}
88 	return 0;
89 }
90 #endif
91 
92 int
upnp_find_inboundpinhole(const char * raddr,unsigned short rport,const char * iaddr,unsigned short iport,int proto,char * desc,int desc_len,unsigned int * leasetime)93 upnp_find_inboundpinhole(const char * raddr, unsigned short rport,
94                          const char * iaddr, unsigned short iport,
95                          int proto, char * desc, int desc_len, unsigned int * leasetime)
96 {
97 #if defined(USE_PF) || defined(USE_NETFILTER)
98 	int uid;
99 	uid = find_pinhole(NULL, raddr, rport, iaddr, iport, proto,
100 	                   desc, desc_len, leasetime);
101 	return uid;
102 #else
103 	return -42;
104 #endif
105 }
106 
107 /* upnp_add_inboundpinhole()
108  * returns:  1 on success
109  *          -1 Pinhole space exhausted
110  *          -4 invalid arguments
111  *         -42 not implemented
112  * TODO : return uid on success (positive) or error value (negative)
113  */
114 int
upnp_add_inboundpinhole(const char * raddr,unsigned short rport,const char * iaddr,unsigned short iport,int proto,char * desc,unsigned int leasetime,int * uid)115 upnp_add_inboundpinhole(const char * raddr,
116                         unsigned short rport,
117                         const char * iaddr,
118                         unsigned short iport,
119                         int proto,
120                         char * desc,
121                         unsigned int leasetime,
122                         int * uid)
123 {
124 	int r;
125 	time_t current;
126 	unsigned int timestamp;
127 	struct in6_addr address;
128 
129 	r = inet_pton(AF_INET6, iaddr, &address);
130 	if(r <= 0) {
131 		syslog(LOG_ERR, "inet_pton(%d, %s, %p) FAILED",
132 		       AF_INET6, iaddr, &address);
133 		return -4;
134 	}
135 	current = upnp_time();
136 	timestamp = current + leasetime;
137 	r = 0;
138 
139 	*uid = upnp_find_inboundpinhole(raddr, rport, iaddr, iport, proto, NULL, 0, NULL);
140 	if(*uid >= 0) {
141 		syslog(LOG_INFO, "Pinhole for inbound traffic from [%s]:%hu to [%s]:%hu with proto %d found uid=%d. Updating it.", raddr, rport, iaddr, iport, proto, *uid);
142 		r = upnp_update_inboundpinhole(*uid, timestamp);
143 		return (r >= 0) ? 1 : r;
144 	}
145 #if defined(USE_PF) || defined(USE_NETFILTER)
146 	*uid = add_pinhole (ext_if_name6, raddr, rport,
147 	                    iaddr, iport, proto, desc, timestamp);
148 	return *uid >= 0 ? 1 : -1;
149 #else
150 	return -42;	/* not implemented */
151 #endif
152 }
153 
154 #if 0
155 int
156 upnp_add_inboundpinhole_internal(const char * raddr, unsigned short rport,
157                        const char * iaddr, unsigned short iport,
158                        const char * proto, int * uid)
159 {
160 	int c = 9999;
161 	char cmd[256], cmd_raw[256], cuid[42];
162 #if 0
163 	static const char cmdval_full_udptcp[] = "ip6tables -I %s %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j ACCEPT";
164 	static const char cmdval_udptcp[] = "ip6tables -I %s %d -p %s -i %s --sport %hu -d %s --dport %hu -j ACCEPT";
165 	static const char cmdval_full_udplite[] = "ip6tables -I %s %d -p %s -i %s -s %s -d %s -j ACCEPT";
166 	static const char cmdval_udplite[] = "ip6tables -I %s %d -p %s -i %s -d %s -j ACCEPT";
167 	// raw table command
168 	static const char cmdval_full_udptcp_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j TRACE";
169 	static const char cmdval_udptcp_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s --sport %hu -d %s --dport %hu -j TRACE";
170 	static const char cmdval_full_udplite_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s -d %s -j TRACE";
171 	static const char cmdval_udplite_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s -d %s -j TRACE";
172 #endif
173 	/*printf("%s\n", raddr);*/
174 	if(raddr!=NULL)
175 	{
176 #ifdef IPPROTO_UDPLITE
177 		if(atoi(proto) == IPPROTO_UDPLITE)
178 		{
179 	/*		snprintf(cmd, sizeof(cmd), cmdval_full_udplite, miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, iaddr);
180 			snprintf(cmd_raw, sizeof(cmd_raw), cmdval_full_udplite_raw, line_number, proto, ext_if_name, raddr, iaddr);*/
181 		}
182 		else
183 #endif
184 		{
185 	/*		snprintf(cmd, sizeof(cmd), cmdval_full_udptcp, miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, rport, iaddr, iport);
186 			snprintf(cmd_raw, sizeof(cmd_raw), cmdval_full_udptcp_raw, line_number, proto, ext_if_name, raddr, rport, iaddr, iport);*/
187 		}
188 	}
189 	else
190 	{
191 #ifdef IPPROTO_UDPLITE
192 		if(atoi(proto) == IPPROTO_UDPLITE)
193 		{
194 			/*snprintf(cmd, sizeof(cmd), cmdval_udplite, miniupnpd_forward_chain, line_number, proto, ext_if_name, iaddr);
195 			snprintf(cmd_raw, sizeof(cmd_raw), cmdval_udplite_raw, line_number, proto, ext_if_name, iaddr);*/
196 		}
197 		else
198 #endif
199 		{
200 			/*snprintf(cmd, sizeof(cmd), cmdval_udptcp, miniupnpd_forward_chain, line_number, proto, ext_if_name, rport, iaddr, iport);
201 			snprintf(cmd_raw, sizeof(cmd_raw), cmdval_udptcp_raw, line_number, proto, ext_if_name, rport, iaddr, iport);
202 */
203 		}
204 	}
205 #ifdef DEBUG
206 	syslog(LOG_INFO, "Adding following ip6tables rule:");
207 	syslog(LOG_INFO, "  -> %s", cmd);
208 	syslog(LOG_INFO, "  -> %s", cmd_raw);
209 #endif
210 	/* TODO Add a better checking error.*/
211 	if(system(cmd) < 0 || system(cmd_raw) < 0)
212 	{
213 		return 0;
214 	}
215 	srand(time(NULL));
216 	snprintf(cuid, sizeof(cuid), "%.4d", rand()%c);
217 	*uid = atoi(cuid);
218 	printf("\t_add_ uid: %s\n", cuid);
219 	return 1;
220 }
221 #endif
222 
223 /* upnp_get_pinhole_info()
224  * return values :
225  *   0   OK
226  *  -1   Internal error
227  *  -2   NOT FOUND (no such entry)
228  *  ..
229  *  -42  Not implemented
230  */
231 int
upnp_get_pinhole_info(unsigned short uid,char * raddr,int raddrlen,unsigned short * rport,char * iaddr,int iaddrlen,unsigned short * iport,int * proto,char * desc,int desclen,unsigned int * leasetime,unsigned int * packets)232 upnp_get_pinhole_info(unsigned short uid,
233                       char * raddr, int raddrlen,
234                       unsigned short * rport,
235                       char * iaddr, int iaddrlen,
236                       unsigned short * iport,
237                       int * proto, char * desc, int desclen,
238                       unsigned int * leasetime,
239                       unsigned int * packets)
240 {
241 	/* Call Firewall specific code to get IPv6 pinhole infos */
242 #if defined(USE_PF) || defined(USE_NETFILTER)
243 	int r;
244 	unsigned int timestamp;
245 	u_int64_t packets_tmp;
246 	/*u_int64_t bytes_tmp;*/
247 
248 	r = get_pinhole_info(uid, raddr, raddrlen, rport,
249 	                     iaddr, iaddrlen, iport,
250 	                     proto, desc, desclen,
251 	                     leasetime ? &timestamp : NULL,
252 	                     packets ? &packets_tmp : NULL,
253 	                     NULL/*&bytes_tmp*/);
254 	if(r >= 0) {
255 		if(leasetime) {
256 			time_t current_time;
257 			current_time = upnp_time();
258 			if(timestamp > (unsigned int)current_time)
259 				*leasetime = timestamp - current_time;
260 			else
261 				*leasetime = 0;
262 		}
263 		if(packets)
264 			*packets = (unsigned int)packets_tmp;
265 	}
266 	return r;
267 #else
268 	UNUSED(uid);
269 	UNUSED(raddr); UNUSED(raddrlen); UNUSED(rport);
270 	UNUSED(iaddr); UNUSED(iaddrlen); UNUSED(iport);
271 	UNUSED(proto); UNUSED(desc); UNUSED(desclen);
272 	UNUSED(leasetime); UNUSED(packets);
273 	return -42;	/* not implemented */
274 #endif
275 }
276 
277 int
upnp_get_pinhole_uid_by_index(int index)278 upnp_get_pinhole_uid_by_index(int index)
279 {
280 #if defined (USE_NETFILTER)
281 	return get_pinhole_uid_by_index(index);
282 #else
283 	UNUSED(index);
284 	return -42;
285 #endif /* defined (USE_NETFILTER) */
286 }
287 
288 int
upnp_update_inboundpinhole(unsigned short uid,unsigned int leasetime)289 upnp_update_inboundpinhole(unsigned short uid, unsigned int leasetime)
290 {
291 #if defined(USE_PF) || defined(USE_NETFILTER)
292 	unsigned int timestamp;
293 
294 	timestamp = upnp_time() + leasetime;
295 	return update_pinhole(uid, timestamp);
296 #else
297 	UNUSED(uid); UNUSED(leasetime);
298 
299 	return -42; /* not implemented */
300 #endif
301 }
302 
303 int
upnp_delete_inboundpinhole(unsigned short uid)304 upnp_delete_inboundpinhole(unsigned short uid)
305 {
306 #if defined(USE_PF) || defined(USE_NETFILTER)
307 	return delete_pinhole(uid);
308 #else
309 	UNUSED(uid);
310 
311 	return -1;
312 #endif
313 }
314 
315 #if 0
316 /*
317  * Result:
318  * 	 1: Found Result
319  * 	-4: No result
320  * 	-5: Result in another table
321  * 	-6: Result in another chain
322  * 	-7: Result in a chain not a rule
323 */
324 int
325 upnp_check_pinhole_working(const char * uid,
326                            char * eaddr,
327                            char * iaddr,
328                            unsigned short * eport,
329                            unsigned short * iport,
330                            char * protocol,
331                            int * rulenum_used)
332 {
333 	/* TODO : to be implemented */
334 #if 0
335 	FILE * fd;
336 	time_t expire = upnp_time();
337 	char buf[1024], filename[] = "/var/log/kern.log", expire_time[32]="";
338 	int res = -4, str_len;
339 
340 	str_len = strftime(expire_time, sizeof(expire_time), "%b %d %H:%M:%S", localtime(&expire));
341 
342 	fd = fopen(filename, "r");
343 	if (fd==NULL)
344 	{
345 		syslog(LOG_ERR, "Get_rule: could not open file: %s", filename);
346 		return -1;
347 	}
348 
349 	syslog(LOG_INFO, "Get_rule: Starting getting info in file %s for %s\n", filename, uid);
350 	buf[sizeof(buf)-1] = 0;
351 	while(fgets(buf, sizeof(buf)-1, fd) != NULL && res != 1)
352 	{
353 		//printf("line: %s\n", buf);
354 		char * r, * t, * c, * p;
355 		// looking for something like filter:FORWARD:rule: or filter:MINIUPNPD:rule:
356 		r = strstr(buf, ":rule:");
357 		p = strstr(buf, ":policy:");
358 		t = strstr(buf, "TRACE:"); // table pointeur
359 		t += 7;
360 		c = t + 7; // chain pointeur
361 		if(r)
362 		{
363 			printf("\t** Found %.*s\n", 24 ,t);
364 			char * src, * dst, * sport, * dport, * proto, * line;
365 			char time[15]="", src_addr[40], dst_addr[40], proto_tmp[8];
366 			int proto_int;
367 			strncpy(time, buf, sizeof(time));
368 			/*if(compare_time(time, expire_time)<0)
369 			{
370 				printf("\t\tNot corresponding time\n");
371 				continue;
372 			}*/
373 
374 			line = r + 6;
375 			printf("\trule line = %d\n", atoi(line));
376 
377 			src = strstr(buf, "SRC=");
378 			src += 4;
379 			snprintf(src_addr, sizeof(src_addr), "%.*s", 39, src);
380 #if 0
381 			del_char(src_addr);
382 			add_char(src_addr);
383 #endif
384 
385 			dst = strstr(buf, "DST=");
386 			dst += 4;
387 			snprintf(dst_addr, sizeof(dst_addr), "%.*s", 39, dst);
388 #if 0
389 			del_char(dst_addr);
390 			add_char(dst_addr);
391 #endif
392 
393 			proto = strstr(buf, "PROTO=");
394 			proto += 6;
395 			proto_int = atoi(protocol);
396 			if(proto_int == IPPROTO_UDP)
397 				strcpy(proto_tmp, "UDP");
398 			else if(proto_int == IPPROTO_TCP)
399 				strcpy(proto_tmp, "TCP");
400 #ifdef IPPROTO_UDPLITE
401 			else if(proto_int == IPPROTO_UDPLITE)
402 				strcpy(proto_tmp, "UDPLITE");
403 #endif
404 			else
405 				strcpy(proto_tmp, "UnsupportedProto");
406 
407 	//		printf("\tCompare eaddr: %s // protocol: %s\n\t     to  addr: %s // protocol: %.*s\n", eaddr, proto_tmp, src_addr, strlen(proto_tmp), proto);
408 	//		printf("\tCompare iaddr: %s // protocol: %s\n\t     to  addr: %s // protocol: %.*s\n", iaddr, proto_tmp, dst_addr, strlen(proto_tmp), proto);
409 			// TODO Check time
410 			// Check that the paquet found in trace correspond to the one we are looking for
411 			if( /*(strcmp(eaddr, src_addr) == 0) &&*/ (strcmp(iaddr, dst_addr) == 0) && (strncmp(proto_tmp, proto, strlen(proto_tmp))==0))
412 			{
413 				sport = strstr(buf, "SPT=");
414 				sport += 4;
415 				dport = strstr(buf, "DPT=");
416 				dport += 4;
417 				printf("\tCompare eport: %hu\n\t     to   port: %d\n", *eport, atoi(sport));
418 				printf("\tCompare iport: %hu\n\t     to   port: %d\n", *iport, atoi(dport));
419 				if(/*eport != atoi(sport) &&*/ *iport != atoi(dport))
420 				{
421 					printf("\t\tPort not corresponding\n");
422 					continue;
423 				}
424 				printf("\ttable found: %.*s\n", 6, t);
425 				printf("\tchain found: %.*s\n", 9, c);
426 				// Check that the table correspond to the filter table
427 				if(strncmp(t, "filter", 6)==0)
428 				{
429 					// Check that the table correspond to the MINIUPNP table
430 					if(strncmp(c, "MINIUPNPD", 9)==0)
431 					{
432 						*rulenum_used = atoi(line);
433 						res = 1;
434 					}
435 					else
436 					{
437 						res = -6;
438 						continue;
439 					}
440 				}
441 				else
442 				{
443 					res = -5;
444 					continue;
445 				}
446 			}
447 			else
448 			{
449 				printf("Packet information not corresponding\n");
450 				continue;
451 			}
452 		}
453 		if(!r && p)
454 		{
455 			printf("\t** Policy case\n");
456 			char * src, * dst, * sport, * dport, * proto, * line;
457 			char time[15], src_addr[40], dst_addr[40], proto_tmp[8];
458 			int proto_int;
459 			strncpy(time, buf, sizeof(time));
460 			/*if(compare_time(time, expire_time)<0)
461 			{
462 				printf("\t\tNot corresponding time\n");
463 				continue;
464 			}*/
465 
466 			line = p + 8;
467 			printf("\trule line = %d\n", atoi(line));
468 
469 			src = strstr(buf, "SRC=");
470 			src += 4;
471 			snprintf(src_addr, sizeof(src_addr), "%.*s", 39, src);
472 #if 0
473 			del_char(src_addr);
474 			add_char(src_addr);
475 #endif
476 
477 			dst = strstr(buf, "DST=");
478 			dst += 4;
479 			snprintf(dst_addr, sizeof(dst_addr), "%.*s", 39, dst);
480 #if 0
481 			del_char(dst_addr);
482 			add_char(dst_addr);
483 #endif
484 
485 			proto = strstr(buf, "PROTO=");
486 			proto += 6;
487 			proto_int = atoi(protocol);
488 			if(proto_int == IPPROTO_UDP)
489 				strcpy(proto_tmp, "UDP");
490 			else if(proto_int == IPPROTO_TCP)
491 				strcpy(proto_tmp, "TCP");
492 #ifdef IPPROTO_UDPLITE
493 			else if(proto_int == IPPROTO_UDPLITE)
494 				strcpy(proto_tmp, "UDPLITE");
495 #endif
496 			else
497 				strcpy(proto_tmp, "UnsupportedProto");
498 
499 	//		printf("\tCompare eaddr: %s // protocol: %s\n\t     to  addr: %s // protocol: %.*s\n", eaddr, proto_tmp, src_addr, strlen(proto_tmp), proto);
500 	//		printf("\tCompare iaddr: %s // protocol: %s\n\t     to  addr: %s // protocol: %.*s\n", iaddr, proto_tmp, dst_addr, strlen(proto_tmp), proto);
501 			// Check that the paquet found in trace correspond to the one we are looking for
502 			if( (strcmp(eaddr, src_addr) == 0) && (strcmp(iaddr, dst_addr) == 0) && (strncmp(proto_tmp, proto, 5)==0))
503 			{
504 				sport = strstr(buf, "SPT=");
505 				sport += 4;
506 				dport = strstr(buf, "DPT=");
507 				dport += 4;
508 				printf("\tCompare eport: %hu\n\t     to   port: %d\n", *eport, atoi(sport));
509 				printf("\tCompare iport: %hu\n\t     to   port: %d\n", *iport, atoi(dport));
510 				if(*eport != atoi(sport) && *iport != atoi(dport))
511 				{
512 					printf("\t\tPort not corresponding\n");
513 					continue;
514 				}
515 				else
516 				{
517 					printf("Find a corresponding policy trace in the chain: %.*s\n", 10, c);
518 					res = -7;
519 					continue;
520 				}
521 			}
522 			else
523 				continue;
524 		}
525 	}
526 	fclose(fd);
527 	return res;
528 #else
529 	return -42;	/* to be implemented */
530 #endif
531 }
532 #endif
533 
534 int
upnp_clean_expired_pinholes(unsigned int * next_timestamp)535 upnp_clean_expired_pinholes(unsigned int * next_timestamp)
536 {
537 #if defined(USE_PF) || defined(USE_NETFILTER)
538 	return clean_pinhole_list(next_timestamp);
539 #else
540 	UNUSED(next_timestamp);
541 
542 	return 0;	/* nothing to do */
543 #endif
544 }
545 
546 #endif /* ENABLE_UPNPPINHOLE */
547