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