1145519Sdarrenr/*	$FreeBSD$	*/
2145510Sdarrenr
3145510Sdarrenr/*
4145510Sdarrenr * Copyright (C) 2000-2003 by Darren Reed
5145510Sdarrenr *
6145510Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
7145510Sdarrenr *
8145510Sdarrenr * Simple DCE transparent proxy for MSN RPC.
9145510Sdarrenr *
10145510Sdarrenr * ******* NOTE: THIS PROXY DOES NOT DO ADDRESS TRANSLATION ********
11145510Sdarrenr *
12145510Sdarrenr * Id: ip_msnrpc_pxy.c,v 2.17.2.1 2005/02/04 10:22:55 darrenr Exp
13145510Sdarrenr */
14145510Sdarrenr
15145510Sdarrenr#define	IPF_MSNRPC_PROXY
16145510Sdarrenr
17145510Sdarrenr#define	IPF_MINMSNRPCLEN	24
18145510Sdarrenr#define	IPF_MSNRPCSKIP		(2 + 19 + 2 + 2 + 2 + 19 + 2 + 2)
19145510Sdarrenr
20145510Sdarrenr
21145510Sdarrenrtypedef	struct	msnrpchdr	{
22145510Sdarrenr	u_char	mrh_major;	/* major # == 5 */
23145510Sdarrenr	u_char	mrh_minor;	/* minor # == 0 */
24145510Sdarrenr	u_char	mrh_type;
25145510Sdarrenr	u_char	mrh_flags;
26145510Sdarrenr	u_32_t	mrh_endian;
27145510Sdarrenr	u_short	mrh_dlen;	/* data size */
28145510Sdarrenr	u_short	mrh_alen;	/* authentication length */
29145510Sdarrenr	u_32_t	mrh_cid;	/* call identifier */
30145510Sdarrenr	u_32_t	mrh_hint;	/* allocation hint */
31145510Sdarrenr	u_short	mrh_ctxt;	/* presentation context hint */
32145510Sdarrenr	u_char	mrh_ccnt;	/* cancel count */
33145510Sdarrenr	u_char	mrh_ans;
34145510Sdarrenr} msnrpchdr_t;
35145510Sdarrenr
36145510Sdarrenrint ippr_msnrpc_init __P((void));
37145510Sdarrenrvoid ippr_msnrpc_fini __P((void));
38145510Sdarrenrint ippr_msnrpc_new __P((fr_info_t *, ap_session_t *, nat_t *));
39145510Sdarrenrint ippr_msnrpc_out __P((fr_info_t *, ap_session_t *, nat_t *));
40145510Sdarrenrint ippr_msnrpc_in __P((fr_info_t *, ap_session_t *, nat_t *));
41145510Sdarrenrint ippr_msnrpc_check __P((ip_t *, msnrpchdr_t *));
42145510Sdarrenr
43145510Sdarrenrstatic	frentry_t	msnfr;
44145510Sdarrenr
45145510Sdarrenrint	msn_proxy_init = 0;
46145510Sdarrenr
47145510Sdarrenr/*
48145510Sdarrenr * Initialize local structures.
49145510Sdarrenr */
50145510Sdarrenrint ippr_msnrpc_init()
51145510Sdarrenr{
52145510Sdarrenr	bzero((char *)&msnfr, sizeof(msnfr));
53145510Sdarrenr	msnfr.fr_ref = 1;
54145510Sdarrenr	msnfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
55145510Sdarrenr	MUTEX_INIT(&msnfr.fr_lock, "MSN RPC proxy rule lock");
56145510Sdarrenr	msn_proxy_init = 1;
57145510Sdarrenr
58145510Sdarrenr	return 0;
59145510Sdarrenr}
60145510Sdarrenr
61145510Sdarrenr
62145510Sdarrenrvoid ippr_msnrpc_fini()
63145510Sdarrenr{
64145510Sdarrenr	if (msn_proxy_init == 1) {
65145510Sdarrenr		MUTEX_DESTROY(&msnfr.fr_lock);
66145510Sdarrenr		msn_proxy_init = 0;
67145510Sdarrenr	}
68145510Sdarrenr}
69145510Sdarrenr
70145510Sdarrenr
71145510Sdarrenrint ippr_msnrpc_new(fin, aps, nat)
72145510Sdarrenrfr_info_t *fin;
73145510Sdarrenrap_session_t *aps;
74145510Sdarrenrnat_t *nat;
75145510Sdarrenr{
76145510Sdarrenr	msnrpcinfo_t *mri;
77145510Sdarrenr
78145510Sdarrenr	KMALLOC(mri, msnrpcinfo_t *);
79145510Sdarrenr	if (mri == NULL)
80145510Sdarrenr		return -1;
81145510Sdarrenr	aps->aps_data = mri;
82145510Sdarrenr	aps->aps_psiz = sizeof(msnrpcinfo_t);
83145510Sdarrenr
84145510Sdarrenr	bzero((char *)mri, sizeof(*mri));
85145510Sdarrenr	mri->mri_cmd[0] = 0xff;
86145510Sdarrenr	mri->mri_cmd[1] = 0xff;
87145510Sdarrenr	return 0;
88145510Sdarrenr}
89145510Sdarrenr
90145510Sdarrenr
91145510Sdarrenrint ippr_msnrpc_check(ip, mrh)
92145510Sdarrenrip_t *ip;
93145510Sdarrenrmsnrpchdr_t *mrh;
94145510Sdarrenr{
95145510Sdarrenr	if (mrh->mrh_major != 5)
96145510Sdarrenr		return -1;
97145510Sdarrenr	if (mrh->mrh_minor != 0)
98145510Sdarrenr		return -1;
99145510Sdarrenr	if (mrh->mrh_alen != 0)
100145510Sdarrenr		return -1;
101145510Sdarrenr	if (mrh->mrh_endian == 0x10) {
102145510Sdarrenr		/* Both gateway and packet match endian */
103145510Sdarrenr		if (mrh->mrh_dlen > ip->ip_len)
104145510Sdarrenr			return -1;
105145510Sdarrenr		if (mrh->mrh_type == 0 || mrh->mrh_type == 2)
106145510Sdarrenr			if (mrh->mrh_hint > ip->ip_len)
107145510Sdarrenr				return -1;
108145510Sdarrenr	} else if (mrh->mrh_endian == 0x10000000) {
109145510Sdarrenr		/* XXX - Endian mismatch - should be swapping! */
110145510Sdarrenr		return -1;
111145510Sdarrenr	} else {
112145510Sdarrenr		return -1;
113145510Sdarrenr	}
114145510Sdarrenr	return 0;
115145510Sdarrenr}
116145510Sdarrenr
117145510Sdarrenr
118145510Sdarrenrint ippr_msnrpc_out(fin, ip, aps, nat)
119145510Sdarrenrfr_info_t *fin;
120145510Sdarrenrip_t *ip;
121145510Sdarrenrap_session_t *aps;
122145510Sdarrenrnat_t *nat;
123145510Sdarrenr{
124145510Sdarrenr	msnrpcinfo_t *mri;
125145510Sdarrenr	msnrpchdr_t *mrh;
126145510Sdarrenr	tcphdr_t *tcp;
127145510Sdarrenr	int dlen;
128145510Sdarrenr
129145510Sdarrenr	mri = aps->aps_data;
130145510Sdarrenr	if (mri == NULL)
131145510Sdarrenr		return 0;
132145510Sdarrenr
133145510Sdarrenr	tcp = (tcphdr_t *)fin->fin_dp;
134145510Sdarrenr	dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2);
135145510Sdarrenr	if (dlen < IPF_MINMSNRPCLEN)
136145510Sdarrenr		return 0;
137145510Sdarrenr
138145510Sdarrenr	mrh = (msnrpchdr_t *)((char *)tcp + (TCP_OFF(tcp) << 2));
139145510Sdarrenr	if (ippr_msnrpc_check(ip, mrh))
140145510Sdarrenr		return 0;
141145510Sdarrenr
142145510Sdarrenr	mri->mri_valid++;
143145510Sdarrenr
144145510Sdarrenr	switch (mrh->mrh_type)
145145510Sdarrenr	{
146145510Sdarrenr	case 0x0b :	/* BIND */
147145510Sdarrenr	case 0x00 :	/* REQUEST */
148145510Sdarrenr		break;
149145510Sdarrenr	case 0x0c :	/* BIND ACK */
150145510Sdarrenr	case 0x02 :	/* RESPONSE */
151145510Sdarrenr	default:
152145510Sdarrenr		return 0;
153145510Sdarrenr	}
154145510Sdarrenr	mri->mri_cmd[1] = mrh->mrh_type;
155145510Sdarrenr	return 0;
156145510Sdarrenr}
157145510Sdarrenr
158145510Sdarrenr
159145510Sdarrenrint ippr_msnrpc_in(fin, ip, aps, nat)
160145510Sdarrenrfr_info_t *fin;
161145510Sdarrenrip_t *ip;
162145510Sdarrenrap_session_t *aps;
163145510Sdarrenrnat_t *nat;
164145510Sdarrenr{
165145510Sdarrenr	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
166145510Sdarrenr	int dlen, sz, sz2, i;
167145510Sdarrenr	msnrpcinfo_t *mri;
168145510Sdarrenr	msnrpchdr_t *mrh;
169145510Sdarrenr	fr_info_t fi;
170145510Sdarrenr	u_short len;
171145510Sdarrenr	char *s;
172145510Sdarrenr
173145510Sdarrenr	mri = aps->aps_data;
174145510Sdarrenr	if (mri == NULL)
175145510Sdarrenr		return 0;
176145510Sdarrenr	tcp = (tcphdr_t *)fin->fin_dp;
177145510Sdarrenr	dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2);
178145510Sdarrenr	if (dlen < IPF_MINMSNRPCLEN)
179145510Sdarrenr		return 0;
180145510Sdarrenr
181145510Sdarrenr	mrh = (msnrpchdr_t *)((char *)tcp + (TCP_OFF(tcp) << 2));
182145510Sdarrenr	if (ippr_msnrpc_check(ip, mrh))
183145510Sdarrenr		return 0;
184145510Sdarrenr
185145510Sdarrenr	mri->mri_valid++;
186145510Sdarrenr
187145510Sdarrenr	switch (mrh->mrh_type)
188145510Sdarrenr	{
189145510Sdarrenr	case 0x0c :	/* BIND ACK */
190145510Sdarrenr		if (mri->mri_cmd[1] != 0x0b)
191145510Sdarrenr			return 0;
192145510Sdarrenr		break;
193145510Sdarrenr	case 0x02 :	/* RESPONSE */
194145510Sdarrenr		if (mri->mri_cmd[1] != 0x00)
195145510Sdarrenr			return 0;
196145510Sdarrenr		break;
197145510Sdarrenr	case 0x0b :	/* BIND */
198145510Sdarrenr	case 0x00 :	/* REQUEST */
199145510Sdarrenr	default:
200145510Sdarrenr		return 0;
201145510Sdarrenr	}
202145510Sdarrenr	mri->mri_cmd[0] = mrh->mrh_type;
203145510Sdarrenr	dlen -= sizeof(*mrh);
204145510Sdarrenr
205145510Sdarrenr	/*
206145510Sdarrenr	 * Only processes RESPONSE's
207145510Sdarrenr	 */
208145510Sdarrenr	if (mrh->mrh_type != 0x02)
209145510Sdarrenr		return 0;
210145510Sdarrenr
211145510Sdarrenr	/*
212145510Sdarrenr	 * Skip over some bytes...what are these really ?
213145510Sdarrenr	 */
214145510Sdarrenr	if (dlen <= 44)
215145510Sdarrenr		return 0;
216145510Sdarrenr	s = (char *)(mrh + 1) + 20;
217145510Sdarrenr	dlen -= 20;
218145510Sdarrenr	bcopy(s, (char *)&len, sizeof(len));
219145510Sdarrenr	if (len == 1) {
220145510Sdarrenr		s += 20;
221145510Sdarrenr		dlen -= 20;
222145510Sdarrenr	} else if (len == 2) {
223145510Sdarrenr		s += 24;
224145510Sdarrenr		dlen -= 24;
225145510Sdarrenr	} else
226145510Sdarrenr		return 0;
227145510Sdarrenr
228145510Sdarrenr	if (dlen <= 10)
229145510Sdarrenr		return 0;
230145510Sdarrenr	dlen -= 10;
231145510Sdarrenr	bcopy(s, (char *)&sz, sizeof(sz));
232145510Sdarrenr	s += sizeof(sz);
233145510Sdarrenr	bcopy(s, (char *)&sz2, sizeof(sz2));
234145510Sdarrenr	s += sizeof(sz2);
235145510Sdarrenr	if (sz2 != sz)
236145510Sdarrenr		return 0;
237145510Sdarrenr	if (sz > dlen)
238145510Sdarrenr		return 0;
239145510Sdarrenr	if (*s++ != 5)
240145510Sdarrenr		return 0;
241145510Sdarrenr	if (*s++ != 0)
242145510Sdarrenr		return 0;
243145510Sdarrenr	sz -= IPF_MSNRPCSKIP;
244145510Sdarrenr	s += IPF_MSNRPCSKIP;
245145510Sdarrenr	dlen -= IPF_MSNRPCSKIP;
246145510Sdarrenr
247145510Sdarrenr	do {
248145510Sdarrenr		if (sz < 7 || dlen < 7)
249145510Sdarrenr			break;
250145510Sdarrenr		bcopy(s, (char *)&len, sizeof(len));
251145510Sdarrenr		if (dlen < len)
252145510Sdarrenr			break;
253145510Sdarrenr		if (sz < len)
254145510Sdarrenr			break;
255145510Sdarrenr
256145510Sdarrenr		if (len != 1)
257145510Sdarrenr			break;
258145510Sdarrenr		sz -= 3;
259145510Sdarrenr		i = *(s + 2);
260145510Sdarrenr		s += 3;
261145510Sdarrenr		dlen -= 3;
262145510Sdarrenr
263145510Sdarrenr		bcopy(s, (char *)&len, sizeof(len));
264145510Sdarrenr		if (dlen < len)
265145510Sdarrenr			break;
266145510Sdarrenr		if (sz < len)
267145510Sdarrenr			break;
268145510Sdarrenr		s += sizeof(len);
269145510Sdarrenr
270145510Sdarrenr		switch (i)
271145510Sdarrenr		{
272145510Sdarrenr		case 7 :
273145510Sdarrenr			if (len == 2) {
274145510Sdarrenr				bcopy(s, (char *)&mri->mri_rport, 2);
275145510Sdarrenr				mri->mri_flags |= 1;
276145510Sdarrenr			}
277145510Sdarrenr			break;
278145510Sdarrenr		case 9 :
279145510Sdarrenr			if (len == 4) {
280145510Sdarrenr				bcopy(s, (char *)&mri->mri_raddr, 4);
281145510Sdarrenr				mri->mri_flags |= 2;
282145510Sdarrenr			}
283145510Sdarrenr			break;
284145510Sdarrenr		default :
285145510Sdarrenr			break;
286145510Sdarrenr		}
287145510Sdarrenr		sz -= len;
288145510Sdarrenr		s += len;
289145510Sdarrenr		dlen -= len;
290145510Sdarrenr	} while (sz > 0);
291145510Sdarrenr
292145510Sdarrenr	if (mri->mri_flags == 3) {
293145510Sdarrenr		int slen;
294145510Sdarrenr
295145510Sdarrenr		bcopy((char *)fin, (char *)&fi, sizeof(fi));
296145510Sdarrenr		bzero((char *)tcp2, sizeof(*tcp2));
297145510Sdarrenr
298145510Sdarrenr		slen = ip->ip_len;
299145510Sdarrenr		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
300145510Sdarrenr		bcopy((char *)fin, (char *)&fi, sizeof(fi));
301145510Sdarrenr		bzero((char *)tcp2, sizeof(*tcp2));
302145510Sdarrenr		tcp2->th_win = htons(8192);
303145510Sdarrenr		TCP_OFF_A(tcp2, 5);
304145510Sdarrenr		fi.fin_data[0] = htons(mri->mri_rport);
305145510Sdarrenr		tcp2->th_sport = mri->mri_rport;
306145510Sdarrenr		fi.fin_data[1] = 0;
307145510Sdarrenr		tcp2->th_dport = 0;
308145510Sdarrenr		fi.fin_state = NULL;
309145510Sdarrenr		fi.fin_nat = NULL;
310145510Sdarrenr		fi.fin_dlen = sizeof(*tcp2);
311145510Sdarrenr		fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
312145510Sdarrenr		fi.fin_dp = (char *)tcp2;
313145510Sdarrenr		fi.fin_fi.fi_daddr = ip->ip_dst.s_addr;
314145510Sdarrenr		fi.fin_fi.fi_saddr = mri->mri_raddr.s_addr;
315145510Sdarrenr		if (!fi.fin_fr)
316145510Sdarrenr			fi.fin_fr = &msnfr;
317145510Sdarrenr		if (fr_stlookup(&fi, NULL, NULL)) {
318145510Sdarrenr			RWLOCK_EXIT(&ipf_state);
319145510Sdarrenr		} else {
320145510Sdarrenr			(void) fr_addstate(&fi, NULL, SI_W_DPORT|SI_CLONE);
321145510Sdarrenr			if (fi.fin_state != NULL)
322145510Sdarrenr				fr_statederef(&fi, (ipstate_t **)&fi.fin_state);
323145510Sdarrenr		}
324145510Sdarrenr		ip->ip_len = slen;
325145510Sdarrenr	}
326145510Sdarrenr	mri->mri_flags = 0;
327145510Sdarrenr	return 0;
328145510Sdarrenr}
329