1 /*
2  * Copyright (C) 2000-2003 Darren Reed
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  *
6  * $Id: ip_irc_pxy.c,v 2.39.2.4 2005/02/04 10:22:55 darrenr Exp $
7  *
8  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
9  * Use is subject to license terms.
10  */
11 
12 #pragma ident	"%Z%%M%	%I%	%E% SMI"
13 
14 #define	IPF_IRC_PROXY
15 
16 #define	IPF_IRCBUFSZ	96	/* This *MUST* be >= 64! */
17 
18 typedef struct ifs_ircpxy {
19 	frentry_t	ircnatfr;
20 	int		irc_proxy_init;
21 } ifs_ircpxy_t;
22 
23 
24 int ippr_irc_init __P((void **, ipf_stack_t *));
25 void ippr_irc_fini __P((void **, ipf_stack_t *));
26 int ippr_irc_new __P((fr_info_t *, ap_session_t *, nat_t *, void *));
27 int ippr_irc_out __P((fr_info_t *, ap_session_t *, nat_t *, void *));
28 int ippr_irc_send __P((fr_info_t *, nat_t *, ifs_ircpxy_t *));
29 int ippr_irc_complete __P((ircinfo_t *, char *, size_t));
30 u_short ipf_irc_atoi __P((char **));
31 
32 /*
33  * Initialize local structures.
34  */
35 /*ARGSUSED*/
36 int ippr_irc_init(private, ifs)
37 void **private;
38 ipf_stack_t *ifs;
39 {
40 	ifs_ircpxy_t *ifsirc;
41 
42 	KMALLOC(ifsirc, ifs_ircpxy_t *);
43 	if (ifsirc == NULL)
44 		return -1;
45 
46 	bzero((char *)&ifsirc->ircnatfr, sizeof(ifsirc->ircnatfr));
47 	ifsirc->ircnatfr.fr_ref = 1;
48 	ifsirc->ircnatfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
49 	MUTEX_INIT(&ifsirc->ircnatfr.fr_lock, "IRC proxy rule lock");
50 	ifsirc->irc_proxy_init = 1;
51 
52 	*private = (void *)ifsirc;
53 
54 	return 0;
55 }
56 
57 
58 /*ARGSUSED*/
59 void ippr_irc_fini(private, ifs)
60 void **private;
61 ipf_stack_t *ifs;
62 {
63 	ifs_ircpxy_t *ifsirc = *((ifs_ircpxy_t **)private);
64 
65 	if (ifsirc->irc_proxy_init == 1) {
66 		MUTEX_DESTROY(&ifsirc->ircnatfr.fr_lock);
67 		ifsirc->irc_proxy_init = 0;
68 	}
69 
70 	KFREE(ifsirc);
71 	*private = NULL;
72 }
73 
74 
75 static char *ippr_irc_dcctypes[] = {
76 	"CHAT ",	/* CHAT chat ipnumber portnumber */
77 	"SEND ",	/* SEND filename ipnumber portnumber */
78 	"MOVE ",
79 	"TSEND ",
80 	"SCHAT ",
81 	NULL,
82 };
83 
84 
85 /*
86  * :A PRIVMSG B :^ADCC CHAT chat 0 0^A\r\n
87  * PRIVMSG B ^ADCC CHAT chat 0 0^A\r\n
88  */
89 
90 
91 int ippr_irc_complete(ircp, buf, len)
92 ircinfo_t *ircp;
93 char *buf;
94 size_t len;
95 {
96 	register char *s, c;
97 	register size_t i;
98 	u_32_t l;
99 	int j, k;
100 
101 	ircp->irc_ipnum = 0;
102 	ircp->irc_port = 0;
103 
104 	if (len < 31)
105 		return 0;
106 	s = buf;
107 	c = *s++;
108 	i = len - 1;
109 
110 	if ((c != ':') && (c != 'P'))
111 		return 0;
112 
113 	if (c == ':') {
114 		/*
115 		 * Loosely check that the source is a nickname of some sort
116 		 */
117 		s++;
118 		c = *s;
119 		ircp->irc_snick = s;
120 		if (!ISALPHA(c))
121 			return 0;
122 		i--;
123 		for (c = *s; !ISSPACE(c) && (i > 0); i--)
124 			c = *s++;
125 		if (i < 31)
126 			return 0;
127 		if (c != 'P')
128 			return 0;
129 	} else
130 		ircp->irc_snick = NULL;
131 
132 	/*
133 	 * Check command string
134 	 */
135 	if (strncmp(s, "PRIVMSG ", 8))
136 		return 0;
137 	i -= 8;
138 	s += 8;
139 	c = *s;
140 	ircp->irc_dnick = s;
141 
142 	/*
143 	 * Loosely check that the destination is a nickname of some sort
144 	 */
145 	if (!ISALPHA(c))
146 		return 0;
147 	for (; !ISSPACE(c) && (i > 0); i--)
148 		c = *s++;
149 	if (i < 20)
150 		return 0;
151 	s++,
152 	i--;
153 
154 	/*
155 	 * Look for a ^A to start the DCC
156 	 */
157 	c = *s;
158 	if (c == ':') {
159 		s++;
160 		c = *s;
161 	}
162 
163 	if (strncmp(s, "\001DCC ", 4))
164 		return 0;
165 
166 	i -= 4;
167 	s += 4;
168 
169 	/*
170 	 * Check for a recognised DCC command
171 	 */
172 	for (j = 0, k = 0; ippr_irc_dcctypes[j]; j++) {
173 		k = MIN(strlen(ippr_irc_dcctypes[j]), i);
174 		if (!strncmp(ippr_irc_dcctypes[j], s, k))
175 			break;
176 	}
177 	if (!ippr_irc_dcctypes[j])
178 		return 0;
179 
180 	ircp->irc_type = s;
181 	i -= k;
182 	s += k;
183 
184 	if (i < 11)
185 		return 0;
186 
187 	/*
188 	 * Check for the arg
189 	 */
190 	c = *s;
191 	if (ISSPACE(c))
192 		return 0;
193 	ircp->irc_arg = s;
194 	for (; (c != ' ') && (c != '\001') && (i > 0); i--)
195 		c = *s++;
196 
197 	if (c == '\001')	/* In reality a ^A can quote another ^A...*/
198 		return 0;
199 
200 	if (i < 5)
201 		return 0;
202 
203 	s++;
204 	i--;
205 	c = *s;
206 	if (!ISDIGIT(c))
207 		return 0;
208 	ircp->irc_addr = s;
209 	/*
210 	 * Get the IP#
211 	 */
212 	for (l = 0; ISDIGIT(c) && (i > 0); i--) {
213 		l *= 10;
214 		l += c - '0';
215 		c = *s++;
216 	}
217 
218 	if (i < 4)
219 		return 0;
220 
221 	if (c != ' ')
222 		return 0;
223 
224 	ircp->irc_ipnum = l;
225 	s++;
226 	i--;
227 	c = *s;
228 	if (!ISDIGIT(c))
229 		return 0;
230 	/*
231 	 * Get the port#
232 	 */
233 	for (l = 0; ISDIGIT(c) && (i > 0); i--) {
234 		l *= 10;
235 		l += c - '0';
236 		c = *s++;
237 	}
238 	if (i < 3)
239 		return 0;
240 	if (strncmp(s, "\001\r\n", 3))
241 		return 0;
242 	s += 3;
243 	ircp->irc_len = s - buf;
244 	ircp->irc_port = l;
245 	return 1;
246 }
247 
248 
249 /*ARGSUSED*/
250 int ippr_irc_new(fin, aps, nat, private)
251 fr_info_t *fin;
252 ap_session_t *aps;
253 nat_t *nat;
254 void *private;
255 {
256 	ircinfo_t *irc;
257 
258 	KMALLOC(irc, ircinfo_t *);
259 	if (irc == NULL)
260 		return -1;
261 
262 	fin = fin;	/* LINT */
263 	nat = nat;	/* LINT */
264 
265 	aps->aps_data = irc;
266 	aps->aps_psiz = sizeof(ircinfo_t);
267 
268 	bzero((char *)irc, sizeof(*irc));
269 	return 0;
270 }
271 
272 
273 int ippr_irc_send(fin, nat, ifsirc)
274 fr_info_t *fin;
275 nat_t *nat;
276 ifs_ircpxy_t *ifsirc;
277 {
278 	char ctcpbuf[IPF_IRCBUFSZ], newbuf[IPF_IRCBUFSZ];
279 	tcphdr_t *tcp, tcph, *tcp2 = &tcph;
280 	int off, inc = 0, i, dlen;
281 	size_t nlen = 0, olen;
282 	struct in_addr swip;
283 	u_short a5, sp;
284 	ircinfo_t *irc;
285 	fr_info_t fi;
286 	nat_t *nat2;
287 	u_int a1;
288 	ip_t *ip;
289 	mb_t *m;
290 #ifdef	MENTAT
291 	mb_t *m1;
292 #endif
293 	ipf_stack_t *ifs = fin->fin_ifs;
294 
295 	m = fin->fin_m;
296 	ip = fin->fin_ip;
297 	tcp = (tcphdr_t *)fin->fin_dp;
298 	bzero(ctcpbuf, sizeof(ctcpbuf));
299 	off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
300 
301 #ifdef __sgi
302 	dlen = fin->fin_plen - off;
303 #else
304 	dlen = MSGDSIZE(m) - off;
305 #endif
306 	if (dlen <= 0)
307 		return 0;
308 	COPYDATA(m, off, MIN(sizeof(ctcpbuf), dlen), ctcpbuf);
309 
310 	if (dlen <= 0)
311 		return 0;
312 	ctcpbuf[sizeof(ctcpbuf) - 1] = '\0';
313 	*newbuf = '\0';
314 
315 	irc = nat->nat_aps->aps_data;
316 	if (ippr_irc_complete(irc, ctcpbuf, dlen) == 0)
317 		return 0;
318 
319 	/*
320 	 * check that IP address in the PORT/PASV reply is the same as the
321 	 * sender of the command - prevents using PORT for port scanning.
322 	 */
323 	if (irc->irc_ipnum != ntohl(nat->nat_inip.s_addr))
324 		return 0;
325 
326 	a5 = irc->irc_port;
327 
328 	/*
329 	 * Calculate new address parts for the DCC command
330 	 */
331 	a1 = ntohl(ip->ip_src.s_addr);
332 	olen = irc->irc_len;
333 	i = irc->irc_addr - ctcpbuf;
334 	i++;
335 	(void) strncpy(newbuf, ctcpbuf, i);
336 	/* DO NOT change these! */
337 #if defined(SNPRINTF) && defined(KERNEL)
338 	(void) SNPRINTF(newbuf, sizeof(newbuf) - i, "%u %u\001\r\n", a1, a5);
339 #else
340 	(void) sprintf(newbuf, "%u %u\001\r\n", a1, a5);
341 #endif
342 
343 	nlen = strlen(newbuf);
344 	inc = nlen - olen;
345 
346 	if ((inc + ip->ip_len) > 65535)
347 		return 0;
348 
349 #ifdef	MENTAT
350 	for (m1 = m; m1->b_cont; m1 = m1->b_cont)
351 		;
352 	if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
353 		mblk_t *nm;
354 
355 		/* alloc enough to keep same trailer space for lower driver */
356 		nm = allocb(nlen, BPRI_MED);
357 		PANIC((!nm),("ippr_irc_out: allocb failed"));
358 
359 		nm->b_band = m1->b_band;
360 		nm->b_wptr += nlen;
361 
362 		m1->b_wptr -= olen;
363 		PANIC((m1->b_wptr < m1->b_rptr),
364 		      ("ippr_irc_out: cannot handle fragmented data block"));
365 
366 		linkb(m1, nm);
367 	} else {
368 # if SOLARIS && defined(ICK_VALID)
369 		if (m1->b_datap->db_struiolim == m1->b_wptr)
370 			m1->b_datap->db_struiolim += inc;
371 		m1->b_datap->db_struioflag &= ~STRUIO_IP;
372 # endif
373 		m1->b_wptr += inc;
374 	}
375 #else
376 	if (inc < 0)
377 		m_adj(m, inc);
378 	/* the mbuf chain will be extended if necessary by m_copyback() */
379 #endif
380 	COPYBACK(m, off, nlen, newbuf);
381 
382 	if (inc != 0) {
383 #if defined(MENTAT) || defined(__sgi)
384 		register u_32_t	sum1, sum2;
385 
386 		sum1 = ip->ip_len;
387 		sum2 = ip->ip_len + inc;
388 
389 		/* Because ~1 == -2, We really need ~1 == -1 */
390 		if (sum1 > sum2)
391 			sum2--;
392 		sum2 -= sum1;
393 		sum2 = (sum2 & 0xffff) + (sum2 >> 16);
394 
395 		fix_outcksum(&ip->ip_sum, sum2);
396 #endif
397 		ip->ip_len += inc;
398 	}
399 
400 	/*
401 	 * Add skeleton NAT entry for connection which will come back the
402 	 * other way.
403 	 */
404 	sp = htons(a5);
405 	/*
406 	 * Don't allow the PORT command to specify a port < 1024 due to
407 	 * security crap.
408 	 */
409 	if (ntohs(sp) < 1024)
410 		return 0;
411 
412 	/*
413 	 * The server may not make the connection back from port 20, but
414 	 * it is the most likely so use it here to check for a conflicting
415 	 * mapping.
416 	 */
417 	bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi));
418 	fi.fin_data[0] = sp;
419 	fi.fin_data[1] = fin->fin_data[1];
420 	nat2 = nat_outlookup(fin, IPN_TCP, nat->nat_p, nat->nat_inip,
421 			     ip->ip_dst);
422 	if (nat2 == NULL) {
423 		bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi));
424 		bzero((char *)tcp2, sizeof(*tcp2));
425 		tcp2->th_win = htons(8192);
426 		tcp2->th_sport = sp;
427 		tcp2->th_dport = 0; /* XXX - don't specify remote port */
428 		fi.fin_state = NULL;
429 		fi.fin_nat = NULL;
430 		fi.fin_data[0] = ntohs(sp);
431 		fi.fin_data[1] = 0;
432 		fi.fin_dp = (char *)tcp2;
433 		fi.fin_fr = &ifsirc->ircnatfr;
434 		fi.fin_dlen = sizeof(*tcp2);
435 		fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
436 		swip = ip->ip_src;
437 		ip->ip_src = nat->nat_inip;
438 		nat2 = nat_new(&fi, nat->nat_ptr, NULL,
439 			       NAT_SLAVE|IPN_TCP|SI_W_DPORT, NAT_OUTBOUND);
440 		if (nat2 != NULL) {
441 			(void) nat_proto(&fi, nat2, 0);
442 			nat_update(&fi, nat2, nat2->nat_ptr);
443 
444 			(void) fr_addstate(&fi, NULL, SI_W_DPORT);
445 			if (fi.fin_state != NULL)
446 				fr_statederef((ipstate_t **)&fi.fin_state, ifs);
447 		}
448 		ip->ip_src = swip;
449 	}
450 	return inc;
451 }
452 
453 
454 int ippr_irc_out(fin, aps, nat, private)
455 fr_info_t *fin;
456 ap_session_t *aps;
457 nat_t *nat;
458 void *private;
459 {
460 	aps = aps;	/* LINT */
461 	return ippr_irc_send(fin, nat, (ifs_ircpxy_t *)private);
462 }
463