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