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