1 /*
2  *  UFTP - UDP based FTP with multicast
3  *
4  *  Copyright (C) 2001-2020   Dennis A. Bush, Jr.   bush@tcnj.edu
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  Additional permission under GNU GPL version 3 section 7
20  *
21  *  If you modify this program, or any covered work, by linking or
22  *  combining it with the OpenSSL project's OpenSSL library (or a
23  *  modified version of that library), containing parts covered by the
24  *  terms of the OpenSSL or SSLeay licenses, the copyright holder
25  *  grants you additional permission to convey the resulting work.
26  *  Corresponding Source for a non-source form of such a combination
27  *  shall include the source code for the parts of OpenSSL used as well
28  *  as that of the covered work.
29  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 #include <errno.h>
36 
37 #ifdef WINDOWS
38 
39 #include <process.h>
40 #include "win_func.h"
41 
42 #else  // if WINDOWS
43 
44 #include <unistd.h>
45 #include <netdb.h>
46 #include <arpa/inet.h>
47 #include <netinet/in.h>
48 #include <sys/socket.h>
49 
50 #endif
51 
52 #include "proxy.h"
53 #include "proxy_config.h"
54 
55 /**
56  * Global command line values and sockets
57  */
58 SOCKET listener;
59 char pidfile[MAXPATHNAME];
60 char keyfile[MAXLIST][MAXPATHNAME], keyinfo[MAXLIST][MAXPATHNAME];
61 int proxy_type, debug, rcvbuf, dscp, keyfile_count, keyinfo_count;
62 int hb_interval, priority, user_abort, use_ssm;
63 unsigned int ttl;
64 char portname[PORTNAME_LEN], out_portname[PORTNAME_LEN];
65 int port, out_port;
66 union sockaddr_u down_addr;
67 int have_down_fingerprint;
68 uint8_t down_fingerprint[HMAC_LEN];
69 uint32_t down_nonce, uid;
70 union sockaddr_u hb_hosts[MAXLIST];
71 union sockaddr_u pub_multi[MAX_INTERFACES];
72 struct fp_list_t server_fp[MAXLIST], client_fp[MAXPROXYDEST];
73 struct iflist ifl[MAX_INTERFACES], m_interface[MAX_INTERFACES];
74 struct timeval next_hb_time, last_key_req;
75 int ifl_len, hbhost_count, server_fp_count, client_fp_count;
76 int keyfile_count, key_count, pub_multi_count, interface_count, sys_keys;
77 struct iflist out_if;
78 union key_t privkey[MAXLIST];
79 int privkey_type[MAXLIST];
80 union key_t v4_dhkey;
81 uint8_t v4_ecdh_curve;
82 struct pr_group_list_t group_list[MAXLIST];
83 
84 extern char *optarg;
85 extern int optind;
86 
87 /**
88  * Adds a host and its fingerprint to the given list
89  */
add_hosts_by_name(struct fp_list_t * list,int * list_count,const char * filename,int expect_ip)90 void add_hosts_by_name(struct fp_list_t *list, int *list_count,
91                        const char *filename, int expect_ip)
92 {
93     char line[1000], *hostid, *ipstr, *fingerprint;
94     FILE *hostfile;
95     struct addrinfo ai_hints, *ai_rval;
96     uint32_t remote_uid;
97     int rval;
98 
99     if ((hostfile = fopen(filename, "r")) == NULL) {
100         fprintf(stderr,"Couldn't open server/client list %s: %s\n",
101                 filename, strerror(errno));
102         exit(ERR_PARAM);
103     }
104     while (fgets(line, sizeof(line), hostfile)) {
105         while (line[strlen(line)-1] == '\r' || line[strlen(line)-1] == '\n') {
106             line[strlen(line)-1] = '\x0';
107         }
108         if ((line[0] == '#') || (line[0] == '\x0')) {
109             continue;
110         }
111         hostid = line;
112         ipstr = strchr(hostid, '|');
113         if (ipstr) {
114             *ipstr = '\x0';
115             ipstr++;
116             if (expect_ip) {
117                 fingerprint = strchr(ipstr, '|');
118                 if (fingerprint) {
119                     *fingerprint = '\x0';
120                     fingerprint++;
121                 }
122             } else {
123                 fingerprint = ipstr;
124                 ipstr = NULL;
125             }
126         } else {
127             fingerprint = NULL;
128         }
129 
130         remote_uid = strtoul(hostid, NULL, 16);
131         if ((remote_uid == 0xffffffff) || (remote_uid == 0)) {
132             fprintf(stderr, "Invalid UID %s\n", hostid);
133             exit(ERR_PARAM);
134         }
135 
136         list[*list_count].uid = htonl(remote_uid);
137         if (expect_ip) {
138             memset(&ai_hints, 0, sizeof(ai_hints));
139             ai_hints.ai_family = AF_UNSPEC;
140             ai_hints.ai_socktype = SOCK_DGRAM;
141             ai_hints.ai_protocol = 0;
142             ai_hints.ai_flags = 0;
143             if ((rval = getaddrinfo(ipstr, NULL, &ai_hints, &ai_rval)) != 0) {
144                 fprintf(stderr, "Invalid host name/address %s: %s\n",
145                         ipstr, gai_strerror(rval));
146                 exit(ERR_PARAM);
147             }
148             memcpy(&list[*list_count].addr, ai_rval->ai_addr,
149                     ai_rval->ai_addrlen);
150             freeaddrinfo(ai_rval);
151         }
152         list[*list_count].has_fingerprint =
153             parse_fingerprint(list[*list_count].fingerprint, fingerprint);
154         (*list_count)++;
155     }
156     if (!feof(hostfile) && ferror(hostfile)) {
157         perror("Failed to read from server/client list file");
158         exit(ERR_PARAM);
159     }
160     fclose(hostfile);
161 }
162 
163 /**
164  * Set defaults for all command line arguments
165  */
set_defaults(void)166 void set_defaults(void)
167 {
168     proxy_type = UNDEF_PROXY;
169     hbhost_count =  0;
170     memset(hb_hosts, 0, sizeof(hb_hosts));
171     hb_interval = DEF_HB_INT;
172     debug = 0;
173     log_level = DEF_LOG_LEVEL;
174     strncpy(portname, DEF_PORT, sizeof(portname)-1);
175     portname[sizeof(portname)-1] = '\x0';
176     port = atoi(portname);
177     memset(&out_if, 0, sizeof(out_if));
178     ttl = DEF_TTL;
179     dscp = DEF_DSCP;
180     strncpy(out_portname, DEF_PORT, sizeof(out_portname)-1);
181     out_portname[sizeof(out_portname)-1] = '\x0';
182     out_port = atoi(out_portname);
183     uid = 0;
184     rcvbuf = 0;
185     strncpy(logfile, DEF_LOGFILE, sizeof(logfile)-1);
186     logfile[sizeof(logfile)-1] = '\x0';
187     memset(pidfile, 0, sizeof(pidfile));
188     server_fp_count = 0;
189     client_fp_count = 0;
190     key_count = 0;
191     keyfile_count = 0;
192     keyinfo_count = 0;
193     interface_count = 0;
194     pub_multi_count = 0;
195     sys_keys = 0;
196     priority = 0;
197     v4_ecdh_curve = DEF_CURVE;
198     max_log_size = 0;
199     max_log_count = DEF_MAX_LOG_COUNT;
200     user_abort = 0;
201     use_ssm = 0;
202 }
203 
204 /**
205  * Set argument defaults, read and validate command line options
206  */
process_args(int argc,char * argv[])207 void process_args(int argc, char *argv[])
208 {
209     struct addrinfo ai_hints, *ai_rval;
210     int c, i, listidx, rval;
211     long tmpval;
212     char *p, *p2, *hoststr, *portstr, pubname[INET6_ADDRSTRLEN];
213     const char opts[] = "s:crdx:p:t:Q:N:O:U:q:mh:H:g:n:B:L:P:C:oS:e:k:K:I:M:";
214 
215     set_defaults();
216     srand((unsigned int)time(NULL) ^ getpid());
217 
218     // read lettered arguments
219     while ((c = getopt(argc, argv, opts)) != EOF) {
220         switch (c) {
221         case 's':
222             if (proxy_type != UNDEF_PROXY) {
223                 fprintf(stderr, "Only one of -s, -c, -r may be specified\n");
224                 exit(ERR_PARAM);
225             }
226             proxy_type = SERVER_PROXY;
227             memset(&down_addr, 0, sizeof(down_addr));
228             if (!strncmp(optarg, "fp=", 3)) {
229                 have_down_fingerprint =
230                         parse_fingerprint(down_fingerprint, optarg + 3);
231                 if (!have_down_fingerprint) {
232                     fprintf(stderr, "Failed to parse downstream fingerprint\n");
233                     exit(ERR_PARAM);
234                 }
235                 down_nonce = rand32();
236             } else {
237                 have_down_fingerprint = 0;
238                 memset(&ai_hints, 0, sizeof(ai_hints));
239                 ai_hints.ai_family = AF_UNSPEC;
240                 ai_hints.ai_socktype = SOCK_DGRAM;
241                 ai_hints.ai_protocol = 0;
242                 ai_hints.ai_flags = 0;
243                 if ((rval = getaddrinfo(optarg, NULL, &ai_hints,
244                         &ai_rval)) != 0) {
245                     fprintf(stderr, "Invalid host name: %s: %s\n",
246                             optarg, gai_strerror(rval));
247                     exit(ERR_PARAM);
248                 }
249                 memcpy(&down_addr, ai_rval->ai_addr, ai_rval->ai_addrlen);
250                 freeaddrinfo(ai_rval);
251             }
252             break;
253         case 'c':
254             if (proxy_type != UNDEF_PROXY) {
255                 fprintf(stderr, "Only one of -s, -c, -r may be specified\n");
256                 exit(ERR_PARAM);
257             }
258             proxy_type = CLIENT_PROXY;
259             break;
260         case 'r':
261             if (proxy_type != UNDEF_PROXY) {
262                 fprintf(stderr, "Only one of -s, -c, -r may be specified\n");
263                 exit(ERR_PARAM);
264             }
265             proxy_type = RESPONSE_PROXY;
266             break;
267         case 'd':
268             debug = 1;
269             break;
270         case 'x':
271             log_level = atoi(optarg);
272             if (log_level < 0) {
273                 fprintf(stderr, "Invalid log level\n");
274                 exit(ERR_PARAM);
275             }
276             break;
277         case 'p':
278             strncpy(portname, optarg, sizeof(portname)-1);
279             portname[sizeof(portname)-1] = '\x0';
280             port = atoi(portname);
281             if (port == 0) {
282                 fprintf(stderr, "Invalid port\n");
283                 exit(ERR_PARAM);
284             }
285             break;
286         case 't':
287             tmpval = atoi(optarg);
288             if ((tmpval <= 0) || (tmpval > 255)) {
289                 fprintf(stderr, "Invalid ttl\n");
290                 exit(ERR_PARAM);
291             }
292             ttl = (char)tmpval;
293             break;
294         case 'Q':
295             tmpval = strtol(optarg, NULL, 0);
296             if ((tmpval < 0) || (tmpval > 63)) {
297                 fprintf(stderr, "Invalid dscp\n");
298                 exit(ERR_PARAM);
299             }
300             dscp = (tmpval & 0xFF) << 2;
301             break;
302         case 'N':
303             priority = atoi(optarg);
304             if (!valid_priority(priority)) {
305                 fprintf(stderr, "Invalid priority value\n");
306                 exit(ERR_PARAM);
307             }
308             break;
309         case 'O':
310             if ((listidx = getifbyname(optarg, ifl, ifl_len)) != -1) {
311                 out_if = ifl[listidx];
312                 break;
313             }
314             memset(&ai_hints, 0, sizeof(ai_hints));
315             ai_hints.ai_family = AF_UNSPEC;
316             ai_hints.ai_socktype = SOCK_DGRAM;
317             ai_hints.ai_protocol = 0;
318             ai_hints.ai_flags = 0;
319             if ((rval = getaddrinfo(optarg, NULL, &ai_hints, &ai_rval)) != 0) {
320                 fprintf(stderr, "Invalid name/address %s: %s\n",
321                         optarg, gai_strerror(rval));
322                 exit(ERR_PARAM);
323             }
324             // Just use the first addrinfo entry
325             if ((listidx = getifbyaddr((union sockaddr_u *)ai_rval->ai_addr,
326                     ifl, ifl_len)) == -1) {
327                 fprintf(stderr, "Interface %s not found", optarg);
328                 exit(ERR_PARAM);
329             }
330             out_if = ifl[listidx];
331             freeaddrinfo(ai_rval);
332             break;
333         case 'U':
334             errno = 0;
335             uid = strtoul(optarg, NULL, 16);
336             if (errno) {
337                 perror("Invalid UID");
338                 exit(ERR_PARAM);
339             }
340             uid = htonl(uid);
341             break;
342         case 'q':
343             strncpy(out_portname, optarg, sizeof(out_portname)-1);
344             out_portname[sizeof(out_portname)-1] = '\x0';
345             out_port = atoi(out_portname);
346             if (out_port == 0) {
347                 fprintf(stderr, "Invalid outgoing port\n");
348                 exit(ERR_PARAM);
349             }
350             break;
351         case 'm':
352             sys_keys = 1;
353             break;
354         case 'H':
355             p = strtok(optarg, ",");
356             while (p != NULL) {
357                 p2 = strchr(p, ':');
358                 if (p2) {
359                     hoststr = p;
360                     hoststr[p2 - p] = '\x0';
361                     portstr = p2 + 1;
362                 } else {
363                     hoststr = p;
364                     portstr = NULL;
365                 }
366                 memset(&ai_hints, 0, sizeof(ai_hints));
367                 ai_hints.ai_family = AF_UNSPEC;
368                 ai_hints.ai_socktype = SOCK_DGRAM;
369                 ai_hints.ai_protocol = 0;
370                 ai_hints.ai_flags = 0;
371                 if ((rval = getaddrinfo(hoststr, portstr ? portstr : DEF_PORT,
372                         &ai_hints, &ai_rval)) != 0) {
373                     fprintf(stderr, "Invalid name/address %s: %s\n",
374                             hoststr, gai_strerror(rval));
375                     exit(ERR_PARAM);
376                 }
377                 memcpy(&hb_hosts[hbhost_count++], ai_rval->ai_addr,
378                         ai_rval->ai_addrlen);
379                 freeaddrinfo(ai_rval);
380                 p = strtok(NULL, ",");
381             }
382             break;
383         case 'h':
384             hb_interval = atoi(optarg);
385             if ((hb_interval <= 0) || (hb_interval > 3600)) {
386                 fprintf(stderr, "Invalid heartbeat interval\n");
387                 exit(ERR_PARAM);
388             }
389             break;
390         case 'g':
391             max_log_size = atoi(optarg);
392             if ((max_log_size < 1) || (max_log_size > 1024)) {
393                 fprintf(stderr, "Invalid max log size\n");
394                 exit(ERR_PARAM);
395             }
396             max_log_size *= 1000000;
397             break;
398         case 'n':
399             max_log_count = atoi(optarg);
400             if ((max_log_count < 1) || (max_log_count > 1000)) {
401                 fprintf(stderr, "Invalid max log count\n");
402                 exit(ERR_PARAM);
403             }
404             break;
405         case 'B':
406             rcvbuf = atoi(optarg);
407             if ((rcvbuf < 65536) || (rcvbuf > 104857600)) {
408                 fprintf(stderr, "Invalid buffer size\n");
409                 exit(ERR_PARAM);
410             }
411             break;
412         case 'L':
413             strncpy(logfile, optarg, sizeof(logfile)-1);
414             logfile[sizeof(logfile)-1] = '\x0';
415             break;
416         case 'P':
417             strncpy(pidfile, optarg, sizeof(pidfile)-1);
418             pidfile[sizeof(pidfile)-1] = '\x0';
419             break;
420         case 'C':
421             add_hosts_by_name(client_fp, &client_fp_count, optarg, 0);
422             break;
423         case 'o':
424             use_ssm = 1;
425             break;
426         case 'S':
427             add_hosts_by_name(server_fp, &server_fp_count, optarg, 1);
428             break;
429         case 'e':
430             v4_ecdh_curve = get_curve(optarg);
431             if (v4_ecdh_curve == 0) {
432                 fprintf(stderr, "Invalid curve\n");
433                 exit(ERR_PARAM);
434             }
435             break;
436         case 'k':
437             p = strtok(optarg, ",");
438             while (p != NULL) {
439                 strncpy(keyfile[keyfile_count], p, sizeof(keyfile[0])-1);
440                 keyfile[keyfile_count][sizeof(keyfile[0])-1] = '\x0';
441                 keyfile_count++;
442                 p = strtok(NULL, ",");
443             }
444             break;
445         case 'K':
446             p = strtok(optarg, ",");
447             while (p != NULL) {
448                 strncpy(keyinfo[keyinfo_count], p, sizeof(keyinfo[0])-1);
449                 keyinfo[keyinfo_count][sizeof(keyinfo[0])-1] = '\x0';
450                 keyinfo_count++;
451                 p = strtok(NULL, ",");
452             }
453             break;
454         case 'I':
455             p = strtok(optarg, ",");
456             while (p != NULL) {
457                 if ((listidx = getifbyname(p, ifl, ifl_len)) != -1) {
458                     m_interface[interface_count++] = ifl[listidx];
459                     p = strtok(NULL, ",");
460                     continue;
461                 }
462                 memset(&ai_hints, 0, sizeof(ai_hints));
463                 ai_hints.ai_family = AF_UNSPEC;
464                 ai_hints.ai_socktype = SOCK_DGRAM;
465                 ai_hints.ai_protocol = 0;
466                 ai_hints.ai_flags = 0;
467                 if ((rval = getaddrinfo(p, NULL,
468                         &ai_hints, &ai_rval)) != 0) {
469                     fprintf(stderr, "Invalid name/address %s: %s\n",
470                             p, gai_strerror(rval));
471                     exit(ERR_PARAM);
472                 }
473                 if ((listidx = getifbyaddr((union sockaddr_u *)ai_rval->ai_addr,
474                         ifl, ifl_len)) == -1) {
475                     fprintf(stderr, "Interface %s not found\n", p);
476                     exit(ERR_PARAM);
477                 }
478                 m_interface[interface_count++] = ifl[listidx];
479                 freeaddrinfo(ai_rval);
480                 p = strtok(NULL, ",");
481             }
482             break;
483         case 'M':
484             p = strtok(optarg, ",");
485             while (p != NULL) {
486                 memset(&ai_hints, 0, sizeof(ai_hints));
487                 ai_hints.ai_family = AF_UNSPEC;
488                 ai_hints.ai_socktype = SOCK_DGRAM;
489                 ai_hints.ai_protocol = 0;
490                 ai_hints.ai_flags = 0;
491                 if ((rval = getaddrinfo(p, NULL,
492                         &ai_hints, &ai_rval)) != 0) {
493                     fprintf(stderr, "Invalid multicast address %s: %s\n",
494                             p, gai_strerror(rval));
495                     exit(ERR_PARAM);
496                 }
497                 memcpy(&pub_multi[pub_multi_count], ai_rval->ai_addr,
498                         ai_rval->ai_addrlen);
499                 pub_multi_count++;
500                 freeaddrinfo(ai_rval);
501                 p = strtok(NULL, ",");
502             }
503             break;
504         case '?':
505             fprintf(stderr, USAGE);
506             exit(ERR_PARAM);
507         }
508     }
509 
510     if (proxy_type == UNDEF_PROXY) {
511         fprintf(stderr, "Either -s, -c, or -r must be specified\n");
512         fprintf(stderr, USAGE);
513         exit(ERR_PARAM);
514     }
515     if (proxy_type == RESPONSE_PROXY) {
516         out_port = port;
517     }
518     if (proxy_type == SERVER_PROXY) {
519         if (down_addr.ss.ss_family == AF_INET6) {
520             down_addr.sin6.sin6_port = htons(out_port);
521         } else {
522             down_addr.sin.sin_port = htons(out_port);
523         }
524     }
525     if (proxy_type != CLIENT_PROXY) {
526         if (use_ssm) {
527             for (i = 0; i < pub_multi_count; i++) {
528                 if (!is_multicast(&pub_multi[i], 1)) {
529                     if ((rval = getnameinfo((struct sockaddr *)&pub_multi[i],
530                             family_len(pub_multi[i]), pubname, sizeof(pubname),
531                             NULL, 0, NI_NUMERICHOST)) != 0) {
532                         fprintf(stderr,"getnameinfo failed: %s",
533                                 gai_strerror(rval));
534                     }
535                     fprintf(stderr, "Invalid source specific "
536                             "multicast address: %s\n", pubname);
537                     exit(ERR_PARAM);
538                 }
539             }
540             if (pub_multi_count == 0) {
541                 fprintf(stderr, "Default multicast address %s invalid "
542                         "for source specific multicast\n", DEF_PUB_MULTI);
543                 exit(ERR_PARAM);
544             }
545         }
546     }
547     if ((keyfile_count != 0) && (keyinfo_count != 0) &&
548             (keyfile_count != keyinfo_count)) {
549         fprintf(stderr, "Must list same number of items for -k and -K\n");
550         exit(ERR_PARAM);
551     }
552     for (i = 0; i < pub_multi_count; i++) {
553         if (pub_multi[i].ss.ss_family == AF_INET6) {
554             pub_multi[i].sin6.sin6_port = htons(out_port);
555         } else {
556             pub_multi[i].sin.sin_port = htons(out_port);
557         }
558     }
559 }
560 
561