1/* 2 * Copyright (C) 2012 by Darren Reed. 3 * 4 * See the IPFILTER.LICENCE file for details on licencing. 5 * 6 * $Id: ip_tftp_pxy.c,v 1.1.2.9 2012/07/22 08:04:23 darren_r Exp $ 7 */ 8 9#define IPF_TFTP_PROXY 10 11typedef struct ipf_tftp_softc_s { 12 int ipf_p_tftp_readonly; 13 ipftuneable_t *ipf_p_tftp_tune; 14} ipf_tftp_softc_t; 15 16int ipf_p_tftp_backchannel(fr_info_t *, ap_session_t *, nat_t *); 17int ipf_p_tftp_client(ipf_tftp_softc_t *, fr_info_t *, ap_session_t *, 18 nat_t *); 19int ipf_p_tftp_in(void *, fr_info_t *, ap_session_t *, nat_t *); 20void ipf_p_tftp_main_load(void); 21void ipf_p_tftp_main_unload(void); 22int ipf_p_tftp_new(void *, fr_info_t *, ap_session_t *, nat_t *); 23void ipf_p_tftp_del(ipf_main_softc_t *, ap_session_t *); 24int ipf_p_tftp_out(void *, fr_info_t *, ap_session_t *, nat_t *); 25int ipf_p_tftp_server(ipf_tftp_softc_t *, fr_info_t *, ap_session_t *, 26 nat_t *); 27void *ipf_p_tftp_soft_create(ipf_main_softc_t *); 28void ipf_p_tftp_soft_destroy(ipf_main_softc_t *, void *); 29 30static frentry_t tftpfr; 31static int tftp_proxy_init = 0; 32 33typedef enum tftp_cmd_e { 34 TFTP_CMD_READ = 1, 35 TFTP_CMD_WRITE = 2, 36 TFTP_CMD_DATA = 3, 37 TFTP_CMD_ACK = 4, 38 TFTP_CMD_ERROR = 5 39} tftp_cmd_t; 40 41typedef struct tftpinfo { 42 tftp_cmd_t ti_lastcmd; 43 int ti_nextblk; 44 int ti_lastblk; 45 int ti_lasterror; 46 char ti_filename[80]; 47 ipnat_t *ti_rule; 48} tftpinfo_t; 49 50static ipftuneable_t ipf_tftp_tuneables[] = { 51 { { (void *)offsetof(ipf_tftp_softc_t, ipf_p_tftp_readonly) }, 52 "tftp_read_only", 0, 1, 53 stsizeof(ipf_tftp_softc_t, ipf_p_tftp_readonly), 54 0, NULL, NULL }, 55 { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } 56}; 57 58 59/* 60 * TFTP application proxy initialization. 61 */ 62void 63ipf_p_tftp_main_load() 64{ 65 66 bzero((char *)&tftpfr, sizeof(tftpfr)); 67 tftpfr.fr_ref = 1; 68 tftpfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 69 MUTEX_INIT(&tftpfr.fr_lock, "TFTP proxy rule lock"); 70 tftp_proxy_init = 1; 71} 72 73 74void 75ipf_p_tftp_main_unload() 76{ 77 78 if (tftp_proxy_init == 1) { 79 MUTEX_DESTROY(&tftpfr.fr_lock); 80 tftp_proxy_init = 0; 81 } 82} 83 84 85void * 86ipf_p_tftp_soft_create(softc) 87 ipf_main_softc_t *softc; 88{ 89 ipf_tftp_softc_t *softt; 90 91 KMALLOC(softt, ipf_tftp_softc_t *); 92 if (softt == NULL) 93 return NULL; 94 95 bzero((char *)softt, sizeof(*softt)); 96 97 softt->ipf_p_tftp_tune = ipf_tune_array_copy(softt, 98 sizeof(ipf_tftp_tuneables), 99 ipf_tftp_tuneables); 100 if (softt->ipf_p_tftp_tune == NULL) { 101 ipf_p_tftp_soft_destroy(softc, softt); 102 return NULL; 103 } 104 if (ipf_tune_array_link(softc, softt->ipf_p_tftp_tune) == -1) { 105 ipf_p_tftp_soft_destroy(softc, softt); 106 return NULL; 107 } 108 109 softt->ipf_p_tftp_readonly = 1; 110 111 return softt; 112} 113 114 115void 116ipf_p_tftp_soft_destroy(softc, arg) 117 ipf_main_softc_t *softc; 118 void *arg; 119{ 120 ipf_tftp_softc_t *softt = arg; 121 122 if (softt->ipf_p_tftp_tune != NULL) { 123 ipf_tune_array_unlink(softc, softt->ipf_p_tftp_tune); 124 KFREES(softt->ipf_p_tftp_tune, sizeof(ipf_tftp_tuneables)); 125 softt->ipf_p_tftp_tune = NULL; 126 } 127 128 KFREE(softt); 129} 130 131 132int 133ipf_p_tftp_out(arg, fin, aps, nat) 134 void *arg; 135 fr_info_t *fin; 136 ap_session_t *aps; 137 nat_t *nat; 138{ 139 ipf_tftp_softc_t *softt = arg; 140 141 fin->fin_flx |= FI_NOWILD; 142 if (nat->nat_dir == NAT_OUTBOUND) 143 return ipf_p_tftp_client(softt, fin, aps, nat); 144 return ipf_p_tftp_server(softt, fin, aps, nat); 145} 146 147 148int 149ipf_p_tftp_in(arg, fin, aps, nat) 150 void *arg; 151 fr_info_t *fin; 152 ap_session_t *aps; 153 nat_t *nat; 154{ 155 ipf_tftp_softc_t *softt = arg; 156 157 fin->fin_flx |= FI_NOWILD; 158 if (nat->nat_dir == NAT_INBOUND) 159 return ipf_p_tftp_client(softt, fin, aps, nat); 160 return ipf_p_tftp_server(softt, fin, aps, nat); 161} 162 163 164int 165ipf_p_tftp_new(arg, fin, aps, nat) 166 void *arg; 167 fr_info_t *fin; 168 ap_session_t *aps; 169 nat_t *nat; 170{ 171 udphdr_t *udp; 172 tftpinfo_t *ti; 173 ipnat_t *ipn; 174 ipnat_t *np; 175 int size; 176 177 fin = fin; /* LINT */ 178 179 np = nat->nat_ptr; 180 size = np->in_size; 181 182 KMALLOC(ti, tftpinfo_t *); 183 if (ti == NULL) 184 return -1; 185 KMALLOCS(ipn, ipnat_t *, size); 186 if (ipn == NULL) { 187 KFREE(ti); 188 return -1; 189 } 190 191 aps->aps_data = ti; 192 aps->aps_psiz = sizeof(*ti); 193 bzero((char *)ti, sizeof(*ti)); 194 bzero((char *)ipn, size); 195 ti->ti_rule = ipn; 196 197 udp = (udphdr_t *)fin->fin_dp; 198 aps->aps_sport = udp->uh_sport; 199 aps->aps_dport = udp->uh_dport; 200 201 ipn->in_size = size; 202 ipn->in_apr = NULL; 203 ipn->in_use = 1; 204 ipn->in_hits = 1; 205 ipn->in_ippip = 1; 206 ipn->in_pr[0] = IPPROTO_UDP; 207 ipn->in_pr[1] = IPPROTO_UDP; 208 ipn->in_ifps[0] = nat->nat_ifps[0]; 209 ipn->in_ifps[1] = nat->nat_ifps[1]; 210 ipn->in_v[0] = nat->nat_ptr->in_v[1]; 211 ipn->in_v[1] = nat->nat_ptr->in_v[0]; 212 ipn->in_flags = IPN_UDP|IPN_FIXEDDPORT|IPN_PROXYRULE; 213 214 ipn->in_nsrcip6 = nat->nat_odst6; 215 ipn->in_osrcip6 = nat->nat_ndst6; 216 217 if ((np->in_redir & NAT_REDIRECT) != 0) { 218 ipn->in_redir = NAT_MAP; 219 if (ipn->in_v[0] == 4) { 220 ipn->in_snip = ntohl(nat->nat_odstaddr); 221 ipn->in_dnip = ntohl(nat->nat_nsrcaddr); 222 } else { 223#ifdef USE_INET6 224 ipn->in_snip6 = nat->nat_odst6; 225 ipn->in_dnip6 = nat->nat_nsrc6; 226#endif 227 } 228 ipn->in_ndstip6 = nat->nat_nsrc6; 229 ipn->in_odstip6 = nat->nat_osrc6; 230 } else { 231 ipn->in_redir = NAT_REDIRECT; 232 if (ipn->in_v[0] == 4) { 233 ipn->in_snip = ntohl(nat->nat_odstaddr); 234 ipn->in_dnip = ntohl(nat->nat_osrcaddr); 235 } else { 236#ifdef USE_INET6 237 ipn->in_snip6 = nat->nat_odst6; 238 ipn->in_dnip6 = nat->nat_osrc6; 239#endif 240 } 241 ipn->in_ndstip6 = nat->nat_osrc6; 242 ipn->in_odstip6 = nat->nat_nsrc6; 243 } 244 ipn->in_odport = htons(fin->fin_sport); 245 ipn->in_ndport = htons(fin->fin_sport); 246 247 IP6_SETONES(&ipn->in_osrcmsk6); 248 IP6_SETONES(&ipn->in_nsrcmsk6); 249 IP6_SETONES(&ipn->in_odstmsk6); 250 IP6_SETONES(&ipn->in_ndstmsk6); 251 MUTEX_INIT(&ipn->in_lock, "tftp proxy NAT rule"); 252 253 ipn->in_namelen = np->in_namelen; 254 bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen); 255 ipn->in_ifnames[0] = np->in_ifnames[0]; 256 ipn->in_ifnames[1] = np->in_ifnames[1]; 257 258 ti->ti_lastcmd = 0; 259 260 return 0; 261} 262 263 264void 265ipf_p_tftp_del(softc, aps) 266 ipf_main_softc_t *softc; 267 ap_session_t *aps; 268{ 269 tftpinfo_t *tftp; 270 271 tftp = aps->aps_data; 272 if (tftp != NULL) { 273 tftp->ti_rule->in_flags |= IPN_DELETE; 274 ipf_nat_rule_deref(softc, &tftp->ti_rule); 275 } 276} 277 278 279/* 280 * Setup for a new TFTP proxy. 281 */ 282int 283ipf_p_tftp_backchannel(fin, aps, nat) 284 fr_info_t *fin; 285 ap_session_t *aps; 286 nat_t *nat; 287{ 288 ipf_main_softc_t *softc = fin->fin_main_soft; 289#ifdef USE_MUTEXES 290 ipf_nat_softc_t *softn = softc->ipf_nat_soft; 291#endif 292#ifdef USE_INET6 293 i6addr_t swip6, sw2ip6; 294 ip6_t *ip6; 295#endif 296 struct in_addr swip, sw2ip; 297 tftpinfo_t *ti; 298 udphdr_t udp; 299 fr_info_t fi; 300 u_short slen = 0; /* silence gcc */ 301 nat_t *nat2; 302 int nflags; 303 ip_t *ip; 304 int dir; 305 306 ti = aps->aps_data; 307 /* 308 * Add skeleton NAT entry for connection which will come back the 309 * other way. 310 */ 311 bcopy((char *)fin, (char *)&fi, sizeof(fi)); 312 fi.fin_flx |= FI_IGNORE; 313 fi.fin_data[1] = 0; 314 315 bzero((char *)&udp, sizeof(udp)); 316 udp.uh_sport = 0; /* XXX - don't specify remote port */ 317 udp.uh_dport = ti->ti_rule->in_ndport; 318 udp.uh_ulen = htons(sizeof(udp)); 319 udp.uh_sum = 0; 320 321 fi.fin_fr = &tftpfr; 322 fi.fin_dp = (char *)&udp; 323 fi.fin_sport = 0; 324 fi.fin_dport = ntohs(ti->ti_rule->in_ndport); 325 fi.fin_dlen = sizeof(udp); 326 fi.fin_plen = fi.fin_hlen + sizeof(udp); 327 fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; 328 nflags = NAT_SLAVE|IPN_UDP|SI_W_SPORT; 329#ifdef USE_INET6 330 ip6 = (ip6_t *)fin->fin_ip; 331#endif 332 ip = fin->fin_ip; 333 sw2ip.s_addr = 0; 334 swip.s_addr = 0; 335 336 fi.fin_src6 = nat->nat_ndst6; 337 fi.fin_dst6 = nat->nat_nsrc6; 338 if (nat->nat_v[0] == 4) { 339 slen = ip->ip_len; 340 ip->ip_len = htons(fin->fin_hlen + sizeof(udp)); 341 swip = ip->ip_src; 342 sw2ip = ip->ip_dst; 343 ip->ip_src = nat->nat_ndstip; 344 ip->ip_dst = nat->nat_nsrcip; 345 } else { 346#ifdef USE_INET6 347 slen = ip6->ip6_plen; 348 ip6->ip6_plen = htons(sizeof(udp)); 349 swip6.in6 = ip6->ip6_src; 350 sw2ip6.in6 = ip6->ip6_dst; 351 ip6->ip6_src = nat->nat_ndst6.in6; 352 ip6->ip6_dst = nat->nat_nsrc6.in6; 353#endif 354 } 355 356 if (nat->nat_dir == NAT_INBOUND) { 357 dir = NAT_OUTBOUND; 358 fi.fin_out = 1; 359 } else { 360 dir = NAT_INBOUND; 361 fi.fin_out = 0; 362 } 363 nflags |= NAT_NOTRULEPORT; 364 365 MUTEX_ENTER(&softn->ipf_nat_new); 366#ifdef USE_INET6 367 if (nat->nat_v[0] == 6) 368 nat2 = ipf_nat6_add(&fi, ti->ti_rule, NULL, nflags, dir); 369 else 370#endif 371 nat2 = ipf_nat_add(&fi, ti->ti_rule, NULL, nflags, dir); 372 MUTEX_EXIT(&softn->ipf_nat_new); 373 if (nat2 != NULL) { 374 (void) ipf_nat_proto(&fi, nat2, IPN_UDP); 375 ipf_nat_update(&fi, nat2); 376 fi.fin_ifp = NULL; 377 if (ti->ti_rule->in_redir == NAT_MAP) { 378 fi.fin_src6 = nat->nat_ndst6; 379 fi.fin_dst6 = nat->nat_nsrc6; 380 if (nat->nat_v[0] == 4) { 381 ip->ip_src = nat->nat_ndstip; 382 ip->ip_dst = nat->nat_nsrcip; 383 } else { 384#ifdef USE_INET6 385 ip6->ip6_src = nat->nat_ndst6.in6; 386 ip6->ip6_dst = nat->nat_nsrc6.in6; 387#endif 388 } 389 } else { 390 fi.fin_src6 = nat->nat_odst6; 391 fi.fin_dst6 = nat->nat_osrc6; 392 if (fin->fin_v == 4) { 393 ip->ip_src = nat->nat_odstip; 394 ip->ip_dst = nat->nat_osrcip; 395 } else { 396#ifdef USE_INET6 397 ip6->ip6_src = nat->nat_odst6.in6; 398 ip6->ip6_dst = nat->nat_osrc6.in6; 399#endif 400 } 401 } 402 if (ipf_state_add(softc, &fi, NULL, SI_W_SPORT) != 0) { 403 ipf_nat_setpending(softc, nat2); 404 } 405 } 406 if (nat->nat_v[0] == 4) { 407 ip->ip_len = slen; 408 ip->ip_src = swip; 409 ip->ip_dst = sw2ip; 410 } else { 411#ifdef USE_INET6 412 ip6->ip6_plen = slen; 413 ip6->ip6_src = swip6.in6; 414 ip6->ip6_dst = sw2ip6.in6; 415#endif 416 } 417 return 0; 418} 419 420 421int 422ipf_p_tftp_client(softt, fin, aps, nat) 423 ipf_tftp_softc_t *softt; 424 fr_info_t *fin; 425 ap_session_t *aps; 426 nat_t *nat; 427{ 428 u_char *msg, *s, *t; 429 tftpinfo_t *ti; 430 u_short opcode; 431 udphdr_t *udp; 432 int len; 433 434 if (fin->fin_dlen < 4) 435 return 0; 436 437 ti = aps->aps_data; 438 msg = fin->fin_dp; 439 msg += sizeof(udphdr_t); 440 opcode = (msg[0] << 8) | msg[1]; 441 DT3(tftp_cmd, fr_info_t *, fin, int, opcode, nat_t *, nat); 442 443 switch (opcode) 444 { 445 case TFTP_CMD_WRITE : 446 if (softt->ipf_p_tftp_readonly != 0) 447 break; 448 /* FALLTHROUGH */ 449 case TFTP_CMD_READ : 450 len = fin->fin_dlen - sizeof(*udp) - 2; 451 if (len > sizeof(ti->ti_filename) - 1) 452 len = sizeof(ti->ti_filename) - 1; 453 s = msg + 2; 454 for (t = (u_char *)ti->ti_filename; (len > 0); len--, s++) { 455 *t++ = *s; 456 if (*s == '\0') 457 break; 458 } 459 ipf_p_tftp_backchannel(fin, aps, nat); 460 break; 461 default : 462 return -1; 463 } 464 465 ti = aps->aps_data; 466 ti->ti_lastcmd = opcode; 467 return 0; 468} 469 470 471int 472ipf_p_tftp_server(softt, fin, aps, nat) 473 ipf_tftp_softc_t *softt; 474 fr_info_t *fin; 475 ap_session_t *aps; 476 nat_t *nat; 477{ 478 tftpinfo_t *ti; 479 u_short opcode; 480 u_short arg; 481 u_char *msg; 482 483 if (fin->fin_dlen < 4) 484 return 0; 485 486 ti = aps->aps_data; 487 msg = fin->fin_dp; 488 msg += sizeof(udphdr_t); 489 arg = (msg[2] << 8) | msg[3]; 490 opcode = (msg[0] << 8) | msg[1]; 491 492 switch (opcode) 493 { 494 case TFTP_CMD_ACK : 495 ti->ti_lastblk = arg; 496 break; 497 498 case TFTP_CMD_ERROR : 499 ti->ti_lasterror = arg; 500 break; 501 502 default : 503 return -1; 504 } 505 506 ti->ti_lastcmd = opcode; 507 return 0; 508} 509