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 ? ×tamp : 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