1 /*	$FreeBSD: stable/9/contrib/ipfilter/ip_msnrpc_pxy.c 145519 2005-04-25 18:20:15Z darrenr $	*/
2 
3 /*
4  * Copyright (C) 2000-2003 by Darren Reed
5  *
6  * See the IPFILTER.LICENCE file for details on licencing.
7  *
8  * Simple DCE transparent proxy for MSN RPC.
9  *
10  * ******* NOTE: THIS PROXY DOES NOT DO ADDRESS TRANSLATION ********
11  *
12  * Id: ip_msnrpc_pxy.c,v 2.17.2.1 2005/02/04 10:22:55 darrenr Exp
13  */
14 
15 #define	IPF_MSNRPC_PROXY
16 
17 #define	IPF_MINMSNRPCLEN	24
18 #define	IPF_MSNRPCSKIP		(2 + 19 + 2 + 2 + 2 + 19 + 2 + 2)
19 
20 
21 typedef	struct	msnrpchdr	{
22 	u_char	mrh_major;	/* major # == 5 */
23 	u_char	mrh_minor;	/* minor # == 0 */
24 	u_char	mrh_type;
25 	u_char	mrh_flags;
26 	u_32_t	mrh_endian;
27 	u_short	mrh_dlen;	/* data size */
28 	u_short	mrh_alen;	/* authentication length */
29 	u_32_t	mrh_cid;	/* call identifier */
30 	u_32_t	mrh_hint;	/* allocation hint */
31 	u_short	mrh_ctxt;	/* presentation context hint */
32 	u_char	mrh_ccnt;	/* cancel count */
33 	u_char	mrh_ans;
34 } msnrpchdr_t;
35 
36 int ippr_msnrpc_init __P((void));
37 void ippr_msnrpc_fini __P((void));
38 int ippr_msnrpc_new __P((fr_info_t *, ap_session_t *, nat_t *));
39 int ippr_msnrpc_out __P((fr_info_t *, ap_session_t *, nat_t *));
40 int ippr_msnrpc_in __P((fr_info_t *, ap_session_t *, nat_t *));
41 int ippr_msnrpc_check __P((ip_t *, msnrpchdr_t *));
42 
43 static	frentry_t	msnfr;
44 
45 int	msn_proxy_init = 0;
46 
47 /*
48  * Initialize local structures.
49  */
ippr_msnrpc_init()50 int ippr_msnrpc_init()
51 {
52 	bzero((char *)&msnfr, sizeof(msnfr));
53 	msnfr.fr_ref = 1;
54 	msnfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
55 	MUTEX_INIT(&msnfr.fr_lock, "MSN RPC proxy rule lock");
56 	msn_proxy_init = 1;
57 
58 	return 0;
59 }
60 
61 
ippr_msnrpc_fini()62 void ippr_msnrpc_fini()
63 {
64 	if (msn_proxy_init == 1) {
65 		MUTEX_DESTROY(&msnfr.fr_lock);
66 		msn_proxy_init = 0;
67 	}
68 }
69 
70 
ippr_msnrpc_new(fin,aps,nat)71 int ippr_msnrpc_new(fin, aps, nat)
72 fr_info_t *fin;
73 ap_session_t *aps;
74 nat_t *nat;
75 {
76 	msnrpcinfo_t *mri;
77 
78 	KMALLOC(mri, msnrpcinfo_t *);
79 	if (mri == NULL)
80 		return -1;
81 	aps->aps_data = mri;
82 	aps->aps_psiz = sizeof(msnrpcinfo_t);
83 
84 	bzero((char *)mri, sizeof(*mri));
85 	mri->mri_cmd[0] = 0xff;
86 	mri->mri_cmd[1] = 0xff;
87 	return 0;
88 }
89 
90 
ippr_msnrpc_check(ip,mrh)91 int ippr_msnrpc_check(ip, mrh)
92 ip_t *ip;
93 msnrpchdr_t *mrh;
94 {
95 	if (mrh->mrh_major != 5)
96 		return -1;
97 	if (mrh->mrh_minor != 0)
98 		return -1;
99 	if (mrh->mrh_alen != 0)
100 		return -1;
101 	if (mrh->mrh_endian == 0x10) {
102 		/* Both gateway and packet match endian */
103 		if (mrh->mrh_dlen > ip->ip_len)
104 			return -1;
105 		if (mrh->mrh_type == 0 || mrh->mrh_type == 2)
106 			if (mrh->mrh_hint > ip->ip_len)
107 				return -1;
108 	} else if (mrh->mrh_endian == 0x10000000) {
109 		/* XXX - Endian mismatch - should be swapping! */
110 		return -1;
111 	} else {
112 		return -1;
113 	}
114 	return 0;
115 }
116 
117 
ippr_msnrpc_out(fin,ip,aps,nat)118 int ippr_msnrpc_out(fin, ip, aps, nat)
119 fr_info_t *fin;
120 ip_t *ip;
121 ap_session_t *aps;
122 nat_t *nat;
123 {
124 	msnrpcinfo_t *mri;
125 	msnrpchdr_t *mrh;
126 	tcphdr_t *tcp;
127 	int dlen;
128 
129 	mri = aps->aps_data;
130 	if (mri == NULL)
131 		return 0;
132 
133 	tcp = (tcphdr_t *)fin->fin_dp;
134 	dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2);
135 	if (dlen < IPF_MINMSNRPCLEN)
136 		return 0;
137 
138 	mrh = (msnrpchdr_t *)((char *)tcp + (TCP_OFF(tcp) << 2));
139 	if (ippr_msnrpc_check(ip, mrh))
140 		return 0;
141 
142 	mri->mri_valid++;
143 
144 	switch (mrh->mrh_type)
145 	{
146 	case 0x0b :	/* BIND */
147 	case 0x00 :	/* REQUEST */
148 		break;
149 	case 0x0c :	/* BIND ACK */
150 	case 0x02 :	/* RESPONSE */
151 	default:
152 		return 0;
153 	}
154 	mri->mri_cmd[1] = mrh->mrh_type;
155 	return 0;
156 }
157 
158 
ippr_msnrpc_in(fin,ip,aps,nat)159 int ippr_msnrpc_in(fin, ip, aps, nat)
160 fr_info_t *fin;
161 ip_t *ip;
162 ap_session_t *aps;
163 nat_t *nat;
164 {
165 	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
166 	int dlen, sz, sz2, i;
167 	msnrpcinfo_t *mri;
168 	msnrpchdr_t *mrh;
169 	fr_info_t fi;
170 	u_short len;
171 	char *s;
172 
173 	mri = aps->aps_data;
174 	if (mri == NULL)
175 		return 0;
176 	tcp = (tcphdr_t *)fin->fin_dp;
177 	dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2);
178 	if (dlen < IPF_MINMSNRPCLEN)
179 		return 0;
180 
181 	mrh = (msnrpchdr_t *)((char *)tcp + (TCP_OFF(tcp) << 2));
182 	if (ippr_msnrpc_check(ip, mrh))
183 		return 0;
184 
185 	mri->mri_valid++;
186 
187 	switch (mrh->mrh_type)
188 	{
189 	case 0x0c :	/* BIND ACK */
190 		if (mri->mri_cmd[1] != 0x0b)
191 			return 0;
192 		break;
193 	case 0x02 :	/* RESPONSE */
194 		if (mri->mri_cmd[1] != 0x00)
195 			return 0;
196 		break;
197 	case 0x0b :	/* BIND */
198 	case 0x00 :	/* REQUEST */
199 	default:
200 		return 0;
201 	}
202 	mri->mri_cmd[0] = mrh->mrh_type;
203 	dlen -= sizeof(*mrh);
204 
205 	/*
206 	 * Only processes RESPONSE's
207 	 */
208 	if (mrh->mrh_type != 0x02)
209 		return 0;
210 
211 	/*
212 	 * Skip over some bytes...what are these really ?
213 	 */
214 	if (dlen <= 44)
215 		return 0;
216 	s = (char *)(mrh + 1) + 20;
217 	dlen -= 20;
218 	bcopy(s, (char *)&len, sizeof(len));
219 	if (len == 1) {
220 		s += 20;
221 		dlen -= 20;
222 	} else if (len == 2) {
223 		s += 24;
224 		dlen -= 24;
225 	} else
226 		return 0;
227 
228 	if (dlen <= 10)
229 		return 0;
230 	dlen -= 10;
231 	bcopy(s, (char *)&sz, sizeof(sz));
232 	s += sizeof(sz);
233 	bcopy(s, (char *)&sz2, sizeof(sz2));
234 	s += sizeof(sz2);
235 	if (sz2 != sz)
236 		return 0;
237 	if (sz > dlen)
238 		return 0;
239 	if (*s++ != 5)
240 		return 0;
241 	if (*s++ != 0)
242 		return 0;
243 	sz -= IPF_MSNRPCSKIP;
244 	s += IPF_MSNRPCSKIP;
245 	dlen -= IPF_MSNRPCSKIP;
246 
247 	do {
248 		if (sz < 7 || dlen < 7)
249 			break;
250 		bcopy(s, (char *)&len, sizeof(len));
251 		if (dlen < len)
252 			break;
253 		if (sz < len)
254 			break;
255 
256 		if (len != 1)
257 			break;
258 		sz -= 3;
259 		i = *(s + 2);
260 		s += 3;
261 		dlen -= 3;
262 
263 		bcopy(s, (char *)&len, sizeof(len));
264 		if (dlen < len)
265 			break;
266 		if (sz < len)
267 			break;
268 		s += sizeof(len);
269 
270 		switch (i)
271 		{
272 		case 7 :
273 			if (len == 2) {
274 				bcopy(s, (char *)&mri->mri_rport, 2);
275 				mri->mri_flags |= 1;
276 			}
277 			break;
278 		case 9 :
279 			if (len == 4) {
280 				bcopy(s, (char *)&mri->mri_raddr, 4);
281 				mri->mri_flags |= 2;
282 			}
283 			break;
284 		default :
285 			break;
286 		}
287 		sz -= len;
288 		s += len;
289 		dlen -= len;
290 	} while (sz > 0);
291 
292 	if (mri->mri_flags == 3) {
293 		int slen;
294 
295 		bcopy((char *)fin, (char *)&fi, sizeof(fi));
296 		bzero((char *)tcp2, sizeof(*tcp2));
297 
298 		slen = ip->ip_len;
299 		ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
300 		bcopy((char *)fin, (char *)&fi, sizeof(fi));
301 		bzero((char *)tcp2, sizeof(*tcp2));
302 		tcp2->th_win = htons(8192);
303 		TCP_OFF_A(tcp2, 5);
304 		fi.fin_data[0] = htons(mri->mri_rport);
305 		tcp2->th_sport = mri->mri_rport;
306 		fi.fin_data[1] = 0;
307 		tcp2->th_dport = 0;
308 		fi.fin_state = NULL;
309 		fi.fin_nat = NULL;
310 		fi.fin_dlen = sizeof(*tcp2);
311 		fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
312 		fi.fin_dp = (char *)tcp2;
313 		fi.fin_fi.fi_daddr = ip->ip_dst.s_addr;
314 		fi.fin_fi.fi_saddr = mri->mri_raddr.s_addr;
315 		if (!fi.fin_fr)
316 			fi.fin_fr = &msnfr;
317 		if (fr_stlookup(&fi, NULL, NULL)) {
318 			RWLOCK_EXIT(&ipf_state);
319 		} else {
320 			(void) fr_addstate(&fi, NULL, SI_W_DPORT|SI_CLONE);
321 			if (fi.fin_state != NULL)
322 				fr_statederef(&fi, (ipstate_t **)&fi.fin_state);
323 		}
324 		ip->ip_len = slen;
325 	}
326 	mri->mri_flags = 0;
327 	return 0;
328 }
329