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