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