ip_ftp_pxy.c revision 92685
153642Sguido/* 253642Sguido * Simple FTP transparent proxy for in-kernel use. For use with the NAT 353642Sguido * code. 472006Sdarrenr * 557126Sguido * $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 92685 2002-03-19 11:44:16Z darrenr $ 653642Sguido */ 753642Sguido#if SOLARIS && defined(_KERNEL) 853642Sguidoextern kmutex_t ipf_rw; 953642Sguido#endif 1053642Sguido 1153642Sguido#define isdigit(x) ((x) >= '0' && (x) <= '9') 1263523Sdarrenr#define isupper(x) (((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z')) 1363523Sdarrenr#define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z')) 1463523Sdarrenr#define isalpha(x) (isupper(x) || islower(x)) 1563523Sdarrenr#define toupper(x) (isupper(x) ? (x) : (x) - 'a' + 'A') 1653642Sguido 1753642Sguido#define IPF_FTP_PROXY 1853642Sguido 1953642Sguido#define IPF_MINPORTLEN 18 2053642Sguido#define IPF_MAXPORTLEN 30 2153642Sguido#define IPF_MIN227LEN 39 2253642Sguido#define IPF_MAX227LEN 51 2360857Sdarrenr#define IPF_FTPBUFSZ 96 /* This *MUST* be >= 53! */ 2453642Sguido 2580482Sdarrenr#define FTPXY_GO 0 2680482Sdarrenr#define FTPXY_INIT 1 2780482Sdarrenr#define FTPXY_USER_1 2 2880482Sdarrenr#define FTPXY_USOK_1 3 2980482Sdarrenr#define FTPXY_PASS_1 4 3080482Sdarrenr#define FTPXY_PAOK_1 5 3180482Sdarrenr#define FTPXY_AUTH_1 6 3280482Sdarrenr#define FTPXY_AUOK_1 7 3380482Sdarrenr#define FTPXY_ADAT_1 8 3480482Sdarrenr#define FTPXY_ADOK_1 9 3580482Sdarrenr#define FTPXY_ACCT_1 10 3680482Sdarrenr#define FTPXY_ACOK_1 11 3780482Sdarrenr#define FTPXY_USER_2 12 3880482Sdarrenr#define FTPXY_USOK_2 13 3980482Sdarrenr#define FTPXY_PASS_2 14 4080482Sdarrenr#define FTPXY_PAOK_2 15 4153642Sguido 4260857Sdarrenrint ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); 4360857Sdarrenrint ippr_ftp_complete __P((char *, size_t)); 4460857Sdarrenrint ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); 4553642Sguidoint ippr_ftp_init __P((void)); 4660857Sdarrenrint ippr_ftp_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); 4753642Sguidoint ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); 4860857Sdarrenrint ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); 4960857Sdarrenrint ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); 5060857Sdarrenrint ippr_ftp_process __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); 5160857Sdarrenrint ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); 5292685Sdarrenrint ippr_ftp_valid __P((int, char *, size_t)); 5392685Sdarrenrint ippr_ftp_server_valid __P((char *, size_t)); 5492685Sdarrenrint ippr_ftp_client_valid __P((char *, size_t)); 5560857Sdarrenru_short ippr_ftp_atoi __P((char **)); 5653642Sguido 5792685Sdarrenrstatic frentry_t ftppxyfr; 5860857Sdarrenrint ippr_ftp_pasvonly = 0; 5963523Sdarrenrint ippr_ftp_insecure = 0; 6053642Sguido 6153642Sguido 6253642Sguido/* 6353642Sguido * Initialize local structures. 6453642Sguido */ 6553642Sguidoint ippr_ftp_init() 6653642Sguido{ 6792685Sdarrenr bzero((char *)&ftppxyfr, sizeof(ftppxyfr)); 6892685Sdarrenr ftppxyfr.fr_ref = 1; 6992685Sdarrenr ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 7053642Sguido return 0; 7153642Sguido} 7253642Sguido 7353642Sguido 7460857Sdarrenrint ippr_ftp_new(fin, ip, aps, nat) 7560857Sdarrenrfr_info_t *fin; 7660857Sdarrenrip_t *ip; 7760857Sdarrenrap_session_t *aps; 7860857Sdarrenrnat_t *nat; 7953642Sguido{ 8060857Sdarrenr ftpinfo_t *ftp; 8160857Sdarrenr ftpside_t *f; 8253642Sguido 8360857Sdarrenr KMALLOC(ftp, ftpinfo_t *); 8460857Sdarrenr if (ftp == NULL) 8560857Sdarrenr return -1; 8660857Sdarrenr aps->aps_data = ftp; 8760857Sdarrenr aps->aps_psiz = sizeof(ftpinfo_t); 8860857Sdarrenr 8960857Sdarrenr bzero((char *)ftp, sizeof(*ftp)); 9060857Sdarrenr f = &ftp->ftp_side[0]; 9160857Sdarrenr f->ftps_rptr = f->ftps_buf; 9260857Sdarrenr f->ftps_wptr = f->ftps_buf; 9360857Sdarrenr f = &ftp->ftp_side[1]; 9460857Sdarrenr f->ftps_rptr = f->ftps_buf; 9560857Sdarrenr f->ftps_wptr = f->ftps_buf; 9680482Sdarrenr ftp->ftp_passok = FTPXY_INIT; 9760857Sdarrenr return 0; 9853642Sguido} 9953642Sguido 10053642Sguido 10160857Sdarrenrint ippr_ftp_port(fin, ip, nat, f, dlen) 10253642Sguidofr_info_t *fin; 10353642Sguidoip_t *ip; 10453642Sguidonat_t *nat; 10560857Sdarrenrftpside_t *f; 10660857Sdarrenrint dlen; 10753642Sguido{ 10853642Sguido tcphdr_t *tcp, tcph, *tcp2 = &tcph; 10960857Sdarrenr char newbuf[IPF_FTPBUFSZ], *s; 11053642Sguido u_int a1, a2, a3, a4; 11153642Sguido struct in_addr swip; 11292685Sdarrenr u_short a5, a6, sp; 11360857Sdarrenr size_t nlen, olen; 11453642Sguido fr_info_t fi; 11560857Sdarrenr int inc, off; 11653642Sguido nat_t *ipn; 11753642Sguido mb_t *m; 11853642Sguido#if SOLARIS 11953642Sguido mb_t *m1; 12053642Sguido#endif 12153642Sguido 12253642Sguido tcp = (tcphdr_t *)fin->fin_dp; 12360857Sdarrenr /* 12460857Sdarrenr * Check for client sending out PORT message. 12560857Sdarrenr */ 12660857Sdarrenr if (dlen < IPF_MINPORTLEN) 12753642Sguido return 0; 12863523Sdarrenr off = fin->fin_hlen + (tcp->th_off << 2); 12960857Sdarrenr /* 13053642Sguido * Skip the PORT command + space 13153642Sguido */ 13260857Sdarrenr s = f->ftps_rptr + 5; 13353642Sguido /* 13453642Sguido * Pick out the address components, two at a time. 13553642Sguido */ 13660857Sdarrenr a1 = ippr_ftp_atoi(&s); 13753642Sguido if (!s) 13853642Sguido return 0; 13960857Sdarrenr a2 = ippr_ftp_atoi(&s); 14053642Sguido if (!s) 14153642Sguido return 0; 14253642Sguido /* 14353642Sguido * check that IP address in the PORT/PASV reply is the same as the 14453642Sguido * sender of the command - prevents using PORT for port scanning. 14553642Sguido */ 14653642Sguido a1 <<= 16; 14753642Sguido a1 |= a2; 14853642Sguido if (a1 != ntohl(nat->nat_inip.s_addr)) 14953642Sguido return 0; 15053642Sguido 15160857Sdarrenr a5 = ippr_ftp_atoi(&s); 15253642Sguido if (!s) 15353642Sguido return 0; 15453642Sguido if (*s == ')') 15553642Sguido s++; 15653642Sguido 15753642Sguido /* 15853642Sguido * check for CR-LF at the end. 15953642Sguido */ 16053642Sguido if (*s == '\n') 16153642Sguido s--; 16253642Sguido if ((*s == '\r') && (*(s + 1) == '\n')) { 16353642Sguido s += 2; 16453642Sguido a6 = a5 & 0xff; 16553642Sguido } else 16653642Sguido return 0; 16753642Sguido a5 >>= 8; 16867614Sdarrenr a5 &= 0xff; 16953642Sguido /* 17053642Sguido * Calculate new address parts for PORT command 17153642Sguido */ 17253642Sguido a1 = ntohl(ip->ip_src.s_addr); 17353642Sguido a2 = (a1 >> 16) & 0xff; 17453642Sguido a3 = (a1 >> 8) & 0xff; 17553642Sguido a4 = a1 & 0xff; 17653642Sguido a1 >>= 24; 17760857Sdarrenr olen = s - f->ftps_rptr; 17892685Sdarrenr /* DO NOT change this to snprintf! */ 17955929Sguido (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", 18053642Sguido "PORT", a1, a2, a3, a4, a5, a6); 18153642Sguido 18253642Sguido nlen = strlen(newbuf); 18353642Sguido inc = nlen - olen; 18460857Sdarrenr if ((inc + ip->ip_len) > 65535) 18560857Sdarrenr return 0; 18660857Sdarrenr 18753642Sguido#if SOLARIS 18860857Sdarrenr m = fin->fin_qfm; 18953642Sguido for (m1 = m; m1->b_cont; m1 = m1->b_cont) 19053642Sguido ; 19153642Sguido if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { 19253642Sguido mblk_t *nm; 19353642Sguido 19453642Sguido /* alloc enough to keep same trailer space for lower driver */ 19553642Sguido nm = allocb(nlen, BPRI_MED); 19653642Sguido PANIC((!nm),("ippr_ftp_out: allocb failed")); 19753642Sguido 19853642Sguido nm->b_band = m1->b_band; 19953642Sguido nm->b_wptr += nlen; 20053642Sguido 20153642Sguido m1->b_wptr -= olen; 20253642Sguido PANIC((m1->b_wptr < m1->b_rptr), 20353642Sguido ("ippr_ftp_out: cannot handle fragmented data block")); 20453642Sguido 20553642Sguido linkb(m1, nm); 20653642Sguido } else { 20753642Sguido if (m1->b_datap->db_struiolim == m1->b_wptr) 20853642Sguido m1->b_datap->db_struiolim += inc; 20953642Sguido m1->b_datap->db_struioflag &= ~STRUIO_IP; 21053642Sguido m1->b_wptr += inc; 21153642Sguido } 21253642Sguido copyin_mblk(m, off, nlen, newbuf); 21353642Sguido#else 21460857Sdarrenr m = *((mb_t **)fin->fin_mp); 21553642Sguido if (inc < 0) 21653642Sguido m_adj(m, inc); 21753642Sguido /* the mbuf chain will be extended if necessary by m_copyback() */ 21853642Sguido m_copyback(m, off, nlen, newbuf); 21963523Sdarrenr# ifdef M_PKTHDR 22063523Sdarrenr if (!(m->m_flags & M_PKTHDR)) 22163523Sdarrenr m->m_pkthdr.len += inc; 22263523Sdarrenr# endif 22353642Sguido#endif 22453642Sguido if (inc != 0) { 22553642Sguido#if SOLARIS || defined(__sgi) 22653642Sguido register u_32_t sum1, sum2; 22753642Sguido 22853642Sguido sum1 = ip->ip_len; 22953642Sguido sum2 = ip->ip_len + inc; 23053642Sguido 23153642Sguido /* Because ~1 == -2, We really need ~1 == -1 */ 23253642Sguido if (sum1 > sum2) 23353642Sguido sum2--; 23453642Sguido sum2 -= sum1; 23553642Sguido sum2 = (sum2 & 0xffff) + (sum2 >> 16); 23653642Sguido 23780482Sdarrenr fix_outcksum(fin, &ip->ip_sum, sum2); 23853642Sguido#endif 23953642Sguido ip->ip_len += inc; 24053642Sguido } 24153642Sguido 24253642Sguido /* 24353642Sguido * Add skeleton NAT entry for connection which will come back the 24453642Sguido * other way. 24553642Sguido */ 24692685Sdarrenr sp = (a5 << 8 | a6); 24753642Sguido /* 24860857Sdarrenr * Don't allow the PORT command to specify a port < 1024 due to 24960857Sdarrenr * security crap. 25060857Sdarrenr */ 25192685Sdarrenr if (sp < 1024) 25260857Sdarrenr return 0; 25360857Sdarrenr /* 25453642Sguido * The server may not make the connection back from port 20, but 25553642Sguido * it is the most likely so use it here to check for a conflicting 25653642Sguido * mapping. 25753642Sguido */ 25892685Sdarrenr bcopy((char *)fin, (char *)&fi, sizeof(fi)); 25992685Sdarrenr fi.fin_data[0] = sp; 26092685Sdarrenr fi.fin_data[1] = fin->fin_data[1] - 1; 26192685Sdarrenr ipn = nat_outlookup(&fi, IPN_TCP, nat->nat_p, nat->nat_inip, 26292685Sdarrenr ip->ip_dst, 0); 26353642Sguido if (ipn == NULL) { 26460857Sdarrenr int slen; 26560857Sdarrenr 26660857Sdarrenr slen = ip->ip_len; 26760857Sdarrenr ip->ip_len = fin->fin_hlen + sizeof(*tcp2); 26853642Sguido bzero((char *)tcp2, sizeof(*tcp2)); 26953642Sguido tcp2->th_win = htons(8192); 27092685Sdarrenr tcp2->th_sport = htons(sp); 27160857Sdarrenr tcp2->th_off = 5; 27253642Sguido tcp2->th_dport = 0; /* XXX - don't specify remote port */ 27353642Sguido fi.fin_data[1] = 0; 27467853Sdarrenr fi.fin_dlen = sizeof(*tcp2); 27553642Sguido fi.fin_dp = (char *)tcp2; 27692685Sdarrenr fi.fin_fr = &ftppxyfr; 27780482Sdarrenr fi.fin_out = 1; 27853642Sguido swip = ip->ip_src; 27972006Sdarrenr fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; 28053642Sguido ip->ip_src = nat->nat_inip; 28192685Sdarrenr ipn = nat_new(&fi, ip, nat->nat_ptr, NULL, IPN_TCP|FI_W_DPORT, 28253642Sguido NAT_OUTBOUND); 28353642Sguido if (ipn != NULL) { 28453642Sguido ipn->nat_age = fr_defnatage; 28592685Sdarrenr (void) fr_addstate(ip, &fi, NULL, 28692685Sdarrenr FI_W_DPORT|FI_IGNOREPKT); 28753642Sguido } 28860857Sdarrenr ip->ip_len = slen; 28953642Sguido ip->ip_src = swip; 29053642Sguido } 29164580Sdarrenr return APR_INC(inc); 29253642Sguido} 29353642Sguido 29453642Sguido 29560857Sdarrenrint ippr_ftp_client(fin, ip, nat, ftp, dlen) 29653642Sguidofr_info_t *fin; 29760857Sdarrenrnat_t *nat; 29860857Sdarrenrftpinfo_t *ftp; 29953642Sguidoip_t *ip; 30060857Sdarrenrint dlen; 30153642Sguido{ 30263523Sdarrenr char *rptr, *wptr, cmd[6], c; 30360857Sdarrenr ftpside_t *f; 30463523Sdarrenr int inc, i; 30560857Sdarrenr 30660857Sdarrenr inc = 0; 30760857Sdarrenr f = &ftp->ftp_side[0]; 30860857Sdarrenr rptr = f->ftps_rptr; 30960857Sdarrenr wptr = f->ftps_wptr; 31060857Sdarrenr 31163523Sdarrenr for (i = 0; (i < 5) && (i < dlen); i++) { 31263523Sdarrenr c = rptr[i]; 31363523Sdarrenr if (isalpha(c)) { 31463523Sdarrenr cmd[i] = toupper(c); 31563523Sdarrenr } else { 31663523Sdarrenr cmd[i] = c; 31763523Sdarrenr } 31863523Sdarrenr } 31963523Sdarrenr cmd[i] = '\0'; 32063523Sdarrenr 32180482Sdarrenr ftp->ftp_incok = 0; 32280482Sdarrenr if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) { 32380482Sdarrenr if (ftp->ftp_passok == FTPXY_ADOK_1 || 32480482Sdarrenr ftp->ftp_passok == FTPXY_AUOK_1) { 32580482Sdarrenr ftp->ftp_passok = FTPXY_USER_2; 32680482Sdarrenr ftp->ftp_incok = 1; 32780482Sdarrenr } else { 32880482Sdarrenr ftp->ftp_passok = FTPXY_USER_1; 32980482Sdarrenr ftp->ftp_incok = 1; 33080482Sdarrenr } 33180482Sdarrenr } else if (!strncmp(cmd, "AUTH ", 5)) { 33280482Sdarrenr ftp->ftp_passok = FTPXY_AUTH_1; 33380482Sdarrenr ftp->ftp_incok = 1; 33480482Sdarrenr } else if (!strncmp(cmd, "PASS ", 5)) { 33580482Sdarrenr if (ftp->ftp_passok == FTPXY_USOK_1) { 33680482Sdarrenr ftp->ftp_passok = FTPXY_PASS_1; 33780482Sdarrenr ftp->ftp_incok = 1; 33880482Sdarrenr } else if (ftp->ftp_passok == FTPXY_USOK_2) { 33980482Sdarrenr ftp->ftp_passok = FTPXY_PASS_2; 34080482Sdarrenr ftp->ftp_incok = 1; 34180482Sdarrenr } 34280482Sdarrenr } else if ((ftp->ftp_passok == FTPXY_AUOK_1) && 34380482Sdarrenr !strncmp(cmd, "ADAT ", 5)) { 34480482Sdarrenr ftp->ftp_passok = FTPXY_ADAT_1; 34580482Sdarrenr ftp->ftp_incok = 1; 34692685Sdarrenr } else if ((ftp->ftp_passok == FTPXY_PAOK_1 || 34792685Sdarrenr ftp->ftp_passok == FTPXY_PAOK_2) && 34880482Sdarrenr !strncmp(cmd, "ACCT ", 5)) { 34980482Sdarrenr ftp->ftp_passok = FTPXY_ACCT_1; 35080482Sdarrenr ftp->ftp_incok = 1; 35180482Sdarrenr } else if ((ftp->ftp_passok == FTPXY_GO) && !ippr_ftp_pasvonly && 35263523Sdarrenr !strncmp(cmd, "PORT ", 5)) { 35360857Sdarrenr inc = ippr_ftp_port(fin, ip, nat, f, dlen); 35463523Sdarrenr } else if (ippr_ftp_insecure && !ippr_ftp_pasvonly && 35563523Sdarrenr !strncmp(cmd, "PORT ", 5)) { 35663523Sdarrenr inc = ippr_ftp_port(fin, ip, nat, f, dlen); 35760857Sdarrenr } 35860857Sdarrenr 35960857Sdarrenr while ((*rptr++ != '\n') && (rptr < wptr)) 36060857Sdarrenr ; 36160857Sdarrenr f->ftps_rptr = rptr; 36260857Sdarrenr return inc; 36353642Sguido} 36453642Sguido 36553642Sguido 36660857Sdarrenrint ippr_ftp_pasv(fin, ip, nat, f, dlen) 36753642Sguidofr_info_t *fin; 36853642Sguidoip_t *ip; 36953642Sguidonat_t *nat; 37060857Sdarrenrftpside_t *f; 37160857Sdarrenrint dlen; 37253642Sguido{ 37360857Sdarrenr tcphdr_t *tcp, tcph, *tcp2 = &tcph; 37453642Sguido struct in_addr swip, swip2; 37553642Sguido u_int a1, a2, a3, a4; 37692685Sdarrenr u_short a5, a6, dp; 37753642Sguido fr_info_t fi; 37853642Sguido nat_t *ipn; 37963523Sdarrenr int inc; 38060857Sdarrenr char *s; 38153642Sguido 38280482Sdarrenr#define PASV_REPLEN 24 38360857Sdarrenr /* 38460857Sdarrenr * Check for PASV reply message. 38560857Sdarrenr */ 38660857Sdarrenr if (dlen < IPF_MIN227LEN) 38760857Sdarrenr return 0; 38880482Sdarrenr else if (strncmp(f->ftps_rptr, "227 Entering Passive Mod", PASV_REPLEN)) 38960857Sdarrenr return 0; 39060857Sdarrenr 39153642Sguido tcp = (tcphdr_t *)fin->fin_dp; 39253642Sguido 39353642Sguido /* 39453642Sguido * Skip the PORT command + space 39553642Sguido */ 39680482Sdarrenr s = f->ftps_rptr + PASV_REPLEN; 39753642Sguido while (*s && !isdigit(*s)) 39853642Sguido s++; 39953642Sguido /* 40053642Sguido * Pick out the address components, two at a time. 40153642Sguido */ 40260857Sdarrenr a1 = ippr_ftp_atoi(&s); 40353642Sguido if (!s) 40453642Sguido return 0; 40560857Sdarrenr a2 = ippr_ftp_atoi(&s); 40653642Sguido if (!s) 40753642Sguido return 0; 40853642Sguido 40953642Sguido /* 41053642Sguido * check that IP address in the PORT/PASV reply is the same as the 41153642Sguido * sender of the command - prevents using PORT for port scanning. 41253642Sguido */ 41353642Sguido a1 <<= 16; 41453642Sguido a1 |= a2; 41553642Sguido if (a1 != ntohl(nat->nat_oip.s_addr)) 41653642Sguido return 0; 41753642Sguido 41860857Sdarrenr a5 = ippr_ftp_atoi(&s); 41953642Sguido if (!s) 42053642Sguido return 0; 42153642Sguido 42253642Sguido if (*s == ')') 42353642Sguido s++; 42480482Sdarrenr if (*s == '.') 42580482Sdarrenr s++; 42653642Sguido if (*s == '\n') 42753642Sguido s--; 42853642Sguido /* 42953642Sguido * check for CR-LF at the end. 43053642Sguido */ 43153642Sguido if ((*s == '\r') && (*(s + 1) == '\n')) { 43253642Sguido s += 2; 43353642Sguido a6 = a5 & 0xff; 43453642Sguido } else 43553642Sguido return 0; 43653642Sguido a5 >>= 8; 43753642Sguido /* 43853642Sguido * Calculate new address parts for 227 reply 43953642Sguido */ 44053642Sguido a1 = ntohl(ip->ip_src.s_addr); 44153642Sguido a2 = (a1 >> 16) & 0xff; 44253642Sguido a3 = (a1 >> 8) & 0xff; 44353642Sguido a4 = a1 & 0xff; 44453642Sguido a1 >>= 24; 44560857Sdarrenr inc = 0; 44660857Sdarrenr#if 0 44760857Sdarrenr olen = s - f->ftps_rptr; 44853642Sguido (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", 44953642Sguido "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6); 45053642Sguido nlen = strlen(newbuf); 45153642Sguido inc = nlen - olen; 45260857Sdarrenr if ((inc + ip->ip_len) > 65535) 45360857Sdarrenr return 0; 45460857Sdarrenr 45553642Sguido#if SOLARIS 45660857Sdarrenr m = fin->fin_qfm; 45753642Sguido for (m1 = m; m1->b_cont; m1 = m1->b_cont) 45853642Sguido ; 45953642Sguido if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { 46053642Sguido mblk_t *nm; 46153642Sguido 46253642Sguido /* alloc enough to keep same trailer space for lower driver */ 46353642Sguido nm = allocb(nlen, BPRI_MED); 46453642Sguido PANIC((!nm),("ippr_ftp_out: allocb failed")); 46553642Sguido 46653642Sguido nm->b_band = m1->b_band; 46753642Sguido nm->b_wptr += nlen; 46853642Sguido 46953642Sguido m1->b_wptr -= olen; 47053642Sguido PANIC((m1->b_wptr < m1->b_rptr), 47153642Sguido ("ippr_ftp_out: cannot handle fragmented data block")); 47253642Sguido 47353642Sguido linkb(m1, nm); 47453642Sguido } else { 47553642Sguido m1->b_wptr += inc; 47653642Sguido } 47760857Sdarrenr /*copyin_mblk(m, off, nlen, newbuf);*/ 47863523Sdarrenr#else /* SOLARIS */ 47960857Sdarrenr m = *((mb_t **)fin->fin_mp); 48053642Sguido if (inc < 0) 48153642Sguido m_adj(m, inc); 48253642Sguido /* the mbuf chain will be extended if necessary by m_copyback() */ 48360857Sdarrenr /*m_copyback(m, off, nlen, newbuf);*/ 48463523Sdarrenr#endif /* SOLARIS */ 48553642Sguido if (inc != 0) { 48653642Sguido#if SOLARIS || defined(__sgi) 48753642Sguido register u_32_t sum1, sum2; 48853642Sguido 48953642Sguido sum1 = ip->ip_len; 49053642Sguido sum2 = ip->ip_len + inc; 49153642Sguido 49253642Sguido /* Because ~1 == -2, We really need ~1 == -1 */ 49353642Sguido if (sum1 > sum2) 49453642Sguido sum2--; 49553642Sguido sum2 -= sum1; 49653642Sguido sum2 = (sum2 & 0xffff) + (sum2 >> 16); 49753642Sguido 49880482Sdarrenr fix_outcksum(fin, &ip->ip_sum, sum2); 49963523Sdarrenr#endif /* SOLARIS || defined(__sgi) */ 50053642Sguido ip->ip_len += inc; 50153642Sguido } 50263523Sdarrenr#endif /* 0 */ 50353642Sguido 50453642Sguido /* 50553642Sguido * Add skeleton NAT entry for connection which will come back the 50653642Sguido * other way. 50753642Sguido */ 50892685Sdarrenr bcopy((char *)fin, (char *)&fi, sizeof(fi)); 50992685Sdarrenr fi.fin_data[0] = 0; 51053642Sguido dp = htons(fin->fin_data[1] - 1); 51192685Sdarrenr fi.fin_data[1] = ntohs(dp); 51292685Sdarrenr ipn = nat_outlookup(&fi, IPN_TCP, nat->nat_p, nat->nat_inip, 51392685Sdarrenr ip->ip_dst, 0); 51453642Sguido if (ipn == NULL) { 51560857Sdarrenr int slen; 51660857Sdarrenr 51760857Sdarrenr slen = ip->ip_len; 51860857Sdarrenr ip->ip_len = fin->fin_hlen + sizeof(*tcp2); 51953642Sguido bzero((char *)tcp2, sizeof(*tcp2)); 52053642Sguido tcp2->th_win = htons(8192); 52153642Sguido tcp2->th_sport = 0; /* XXX - fake it for nat_new */ 52260857Sdarrenr tcp2->th_off = 5; 52392685Sdarrenr fi.fin_data[1] = a5 << 8 | a6; 52467853Sdarrenr fi.fin_dlen = sizeof(*tcp2); 52592685Sdarrenr tcp2->th_dport = htons(fi.fin_data[1]); 52692685Sdarrenr fi.fin_data[0] = 0; 52753642Sguido fi.fin_dp = (char *)tcp2; 52892685Sdarrenr fi.fin_fr = &ftppxyfr; 52980482Sdarrenr fi.fin_out = 1; 53053642Sguido swip = ip->ip_src; 53153642Sguido swip2 = ip->ip_dst; 53272006Sdarrenr fi.fin_fi.fi_daddr = ip->ip_src.s_addr; 53372006Sdarrenr fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; 53453642Sguido ip->ip_dst = ip->ip_src; 53553642Sguido ip->ip_src = nat->nat_inip; 53692685Sdarrenr ipn = nat_new(&fi, ip, nat->nat_ptr, NULL, IPN_TCP|FI_W_SPORT, 53753642Sguido NAT_OUTBOUND); 53853642Sguido if (ipn != NULL) { 53953642Sguido ipn->nat_age = fr_defnatage; 54092685Sdarrenr (void) fr_addstate(ip, &fi, NULL, 54192685Sdarrenr FI_W_SPORT|FI_IGNOREPKT); 54253642Sguido } 54360857Sdarrenr ip->ip_len = slen; 54453642Sguido ip->ip_src = swip; 54553642Sguido ip->ip_dst = swip2; 54653642Sguido } 54753642Sguido return inc; 54853642Sguido} 54953642Sguido 55053642Sguido 55160857Sdarrenrint ippr_ftp_server(fin, ip, nat, ftp, dlen) 55260857Sdarrenrfr_info_t *fin; 55360857Sdarrenrip_t *ip; 55460857Sdarrenrnat_t *nat; 55560857Sdarrenrftpinfo_t *ftp; 55660857Sdarrenrint dlen; 55760857Sdarrenr{ 55860857Sdarrenr char *rptr, *wptr; 55960857Sdarrenr ftpside_t *f; 56060857Sdarrenr int inc; 56160857Sdarrenr 56260857Sdarrenr inc = 0; 56360857Sdarrenr f = &ftp->ftp_side[1]; 56460857Sdarrenr rptr = f->ftps_rptr; 56560857Sdarrenr wptr = f->ftps_wptr; 56660857Sdarrenr 56780482Sdarrenr if (!isdigit(*rptr) || !isdigit(*(rptr + 1)) || !isdigit(*(rptr + 2))) 56880482Sdarrenr return inc; 56980482Sdarrenr if (ftp->ftp_passok == FTPXY_GO) { 57080482Sdarrenr if (!strncmp(rptr, "227 ", 4)) 57180482Sdarrenr inc = ippr_ftp_pasv(fin, ip, nat, f, dlen); 57263523Sdarrenr } else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) { 57363523Sdarrenr inc = ippr_ftp_pasv(fin, ip, nat, f, dlen); 57480482Sdarrenr } else if (*rptr == '5' || *rptr == '4') 57580482Sdarrenr ftp->ftp_passok = FTPXY_INIT; 57680482Sdarrenr else if (ftp->ftp_incok) { 57780482Sdarrenr if (*rptr == '3') { 57880482Sdarrenr if (ftp->ftp_passok == FTPXY_ACCT_1) 57980482Sdarrenr ftp->ftp_passok = FTPXY_GO; 58080482Sdarrenr else 58180482Sdarrenr ftp->ftp_passok++; 58280482Sdarrenr } else if (*rptr == '2') { 58380482Sdarrenr switch (ftp->ftp_passok) 58480482Sdarrenr { 58580482Sdarrenr case FTPXY_USER_1 : 58680482Sdarrenr case FTPXY_USER_2 : 58780482Sdarrenr case FTPXY_PASS_1 : 58880482Sdarrenr case FTPXY_PASS_2 : 58980482Sdarrenr case FTPXY_ACCT_1 : 59080482Sdarrenr ftp->ftp_passok = FTPXY_GO; 59180482Sdarrenr break; 59280482Sdarrenr default : 59380482Sdarrenr ftp->ftp_passok += 3; 59480482Sdarrenr break; 59580482Sdarrenr } 59680482Sdarrenr } 59760857Sdarrenr } 59880482Sdarrenr ftp->ftp_incok = 0; 59960857Sdarrenr while ((*rptr++ != '\n') && (rptr < wptr)) 60060857Sdarrenr ; 60160857Sdarrenr f->ftps_rptr = rptr; 60260857Sdarrenr return inc; 60360857Sdarrenr} 60460857Sdarrenr 60560857Sdarrenr 60660857Sdarrenr/* 60760857Sdarrenr * Look to see if the buffer starts with something which we recognise as 60860857Sdarrenr * being the correct syntax for the FTP protocol. 60960857Sdarrenr */ 61092685Sdarrenrint ippr_ftp_client_valid(buf, len) 61160857Sdarrenrchar *buf; 61260857Sdarrenrsize_t len; 61360857Sdarrenr{ 61460857Sdarrenr register char *s, c; 61560857Sdarrenr register size_t i = len; 61660857Sdarrenr 61760857Sdarrenr if (i < 5) 61860857Sdarrenr return 2; 61960857Sdarrenr s = buf; 62060857Sdarrenr c = *s++; 62160857Sdarrenr i--; 62260857Sdarrenr 62392685Sdarrenr if (isalpha(c)) { 62460857Sdarrenr c = *s++; 62560857Sdarrenr i--; 62692685Sdarrenr if (isalpha(c)) { 62760857Sdarrenr c = *s++; 62860857Sdarrenr i--; 62992685Sdarrenr if (isalpha(c)) { 63060857Sdarrenr c = *s++; 63160857Sdarrenr i--; 63292685Sdarrenr if (isalpha(c)) { 63392685Sdarrenr c = *s++; 63492685Sdarrenr i--; 63592685Sdarrenr if ((c != ' ') && (c != '\r')) 63692685Sdarrenr return 1; 63792685Sdarrenr } else if ((c != ' ') && (c != '\r')) 63860857Sdarrenr return 1; 63960857Sdarrenr } else 64060857Sdarrenr return 1; 64160857Sdarrenr } else 64260857Sdarrenr return 1; 64392685Sdarrenr } else 64492685Sdarrenr return 1; 64592685Sdarrenr for (; i; i--) { 64660857Sdarrenr c = *s++; 64792685Sdarrenr if (c == '\n') 64892685Sdarrenr return 0; 64992685Sdarrenr } 65092685Sdarrenr return 2; 65192685Sdarrenr} 65292685Sdarrenr 65392685Sdarrenr 65492685Sdarrenrint ippr_ftp_server_valid(buf, len) 65592685Sdarrenrchar *buf; 65692685Sdarrenrsize_t len; 65792685Sdarrenr{ 65892685Sdarrenr register char *s, c; 65992685Sdarrenr register size_t i = len; 66092685Sdarrenr 66192685Sdarrenr if (i < 5) 66292685Sdarrenr return 2; 66392685Sdarrenr s = buf; 66492685Sdarrenr c = *s++; 66592685Sdarrenr i--; 66692685Sdarrenr 66792685Sdarrenr if (isdigit(c)) { 66892685Sdarrenr c = *s++; 66960857Sdarrenr i--; 67092685Sdarrenr if (isdigit(c)) { 67160857Sdarrenr c = *s++; 67260857Sdarrenr i--; 67392685Sdarrenr if (isdigit(c)) { 67460857Sdarrenr c = *s++; 67560857Sdarrenr i--; 67692685Sdarrenr if ((c != '-') && (c != ' ')) 67760857Sdarrenr return 1; 67860857Sdarrenr } else 67960857Sdarrenr return 1; 68060857Sdarrenr } else 68160857Sdarrenr return 1; 68260857Sdarrenr } else 68360857Sdarrenr return 1; 68460857Sdarrenr for (; i; i--) { 68560857Sdarrenr c = *s++; 68660857Sdarrenr if (c == '\n') 68760857Sdarrenr return 0; 68860857Sdarrenr } 68960857Sdarrenr return 2; 69060857Sdarrenr} 69160857Sdarrenr 69260857Sdarrenr 69392685Sdarrenrint ippr_ftp_valid(side, buf, len) 69492685Sdarrenrint side; 69592685Sdarrenrchar *buf; 69692685Sdarrenrsize_t len; 69792685Sdarrenr{ 69892685Sdarrenr int ret; 69992685Sdarrenr 70092685Sdarrenr if (side == 0) 70192685Sdarrenr ret = ippr_ftp_client_valid(buf, len); 70292685Sdarrenr else 70392685Sdarrenr ret = ippr_ftp_server_valid(buf, len); 70492685Sdarrenr return ret; 70592685Sdarrenr} 70692685Sdarrenr 70792685Sdarrenr 70860857Sdarrenrint ippr_ftp_process(fin, ip, nat, ftp, rv) 70960857Sdarrenrfr_info_t *fin; 71060857Sdarrenrip_t *ip; 71160857Sdarrenrnat_t *nat; 71260857Sdarrenrftpinfo_t *ftp; 71360857Sdarrenrint rv; 71460857Sdarrenr{ 71563523Sdarrenr int mlen, len, off, inc, i, sel; 71660857Sdarrenr char *rptr, *wptr; 71763523Sdarrenr ftpside_t *f, *t; 71860857Sdarrenr tcphdr_t *tcp; 71960857Sdarrenr mb_t *m; 72060857Sdarrenr 72160857Sdarrenr tcp = (tcphdr_t *)fin->fin_dp; 72260857Sdarrenr off = fin->fin_hlen + (tcp->th_off << 2); 72360857Sdarrenr 72460857Sdarrenr#if SOLARIS 72560857Sdarrenr m = fin->fin_qfm; 72660857Sdarrenr#else 72760857Sdarrenr m = *((mb_t **)fin->fin_mp); 72860857Sdarrenr#endif 72960857Sdarrenr 73060857Sdarrenr#if SOLARIS 73160857Sdarrenr mlen = msgdsize(m) - off; 73260857Sdarrenr#else 73360857Sdarrenr mlen = mbufchainlen(m) - off; 73460857Sdarrenr#endif 73572006Sdarrenr 73663523Sdarrenr t = &ftp->ftp_side[1 - rv]; 73772006Sdarrenr f = &ftp->ftp_side[rv]; 73863523Sdarrenr if (!mlen) { 73972006Sdarrenr if (!t->ftps_seq || 74072006Sdarrenr (int)ntohl(tcp->th_ack) - (int)t->ftps_seq > 0) 74172006Sdarrenr t->ftps_seq = ntohl(tcp->th_ack); 74272006Sdarrenr f->ftps_len = 0; 74360857Sdarrenr return 0; 74463523Sdarrenr } 74560857Sdarrenr 74660857Sdarrenr inc = 0; 74760857Sdarrenr rptr = f->ftps_rptr; 74860857Sdarrenr wptr = f->ftps_wptr; 74960857Sdarrenr 75063523Sdarrenr sel = nat->nat_aps->aps_sel[1 - rv]; 75163523Sdarrenr if (rv) 75263523Sdarrenr i = nat->nat_aps->aps_ackoff[sel]; 75363523Sdarrenr else 75463523Sdarrenr i = nat->nat_aps->aps_seqoff[sel]; 75560857Sdarrenr /* 75660857Sdarrenr * XXX - Ideally, this packet should get dropped because we now know 75760857Sdarrenr * that it is out of order (and there is no real danger in doing so 75860857Sdarrenr * apart from causing packets to go through here ordered). 75960857Sdarrenr */ 76072006Sdarrenr if (f->ftps_len + f->ftps_seq == ntohl(tcp->th_seq)) 76172006Sdarrenr f->ftps_seq = ntohl(tcp->th_seq); 76272006Sdarrenr else if (ntohl(tcp->th_seq) + i != f->ftps_seq) { 76392685Sdarrenr return APR_ERR(1); 76460857Sdarrenr } 76572006Sdarrenr f->ftps_len = mlen; 76660857Sdarrenr 76760857Sdarrenr while (mlen > 0) { 76860857Sdarrenr len = MIN(mlen, FTP_BUFSZ / 2); 76960857Sdarrenr 77060857Sdarrenr#if SOLARIS 77160857Sdarrenr copyout_mblk(m, off, len, wptr); 77260857Sdarrenr#else 77360857Sdarrenr m_copydata(m, off, len, wptr); 77460857Sdarrenr#endif 77560857Sdarrenr mlen -= len; 77660857Sdarrenr off += len; 77760857Sdarrenr wptr += len; 77860857Sdarrenr f->ftps_wptr = wptr; 77960857Sdarrenr if (f->ftps_junk == 2) 78092685Sdarrenr f->ftps_junk = ippr_ftp_valid(rv, rptr, wptr - rptr); 78160857Sdarrenr 78260857Sdarrenr while ((f->ftps_junk == 0) && (wptr > rptr)) { 78392685Sdarrenr f->ftps_junk = ippr_ftp_valid(rv, rptr, wptr - rptr); 78460857Sdarrenr if (f->ftps_junk == 0) { 78592685Sdarrenr f->ftps_cmds++; 78660857Sdarrenr len = wptr - rptr; 78760857Sdarrenr f->ftps_rptr = rptr; 78860857Sdarrenr if (rv) 78960857Sdarrenr inc += ippr_ftp_server(fin, ip, nat, 79060857Sdarrenr ftp, len); 79160857Sdarrenr else 79260857Sdarrenr inc += ippr_ftp_client(fin, ip, nat, 79360857Sdarrenr ftp, len); 79460857Sdarrenr rptr = f->ftps_rptr; 79592685Sdarrenr wptr = f->ftps_wptr; 79660857Sdarrenr } 79760857Sdarrenr } 79860857Sdarrenr 79992685Sdarrenr /* 80092685Sdarrenr * Off to a bad start so lets just forget about using the 80192685Sdarrenr * ftp proxy for this connection. 80292685Sdarrenr */ 80392685Sdarrenr if ((f->ftps_cmds == 0) && (f->ftps_junk == 1)) 80492685Sdarrenr return APR_ERR(2); 80592685Sdarrenr 80660857Sdarrenr while ((f->ftps_junk == 1) && (rptr < wptr)) { 80760857Sdarrenr while ((rptr < wptr) && (*rptr != '\r')) 80860857Sdarrenr rptr++; 80960857Sdarrenr 81067614Sdarrenr if (*rptr == '\r') { 81167614Sdarrenr if (rptr + 1 < wptr) { 81267614Sdarrenr if (*(rptr + 1) == '\n') { 81367614Sdarrenr rptr += 2; 81467614Sdarrenr f->ftps_junk = 0; 81567614Sdarrenr } else 81667614Sdarrenr rptr++; 81760857Sdarrenr } else 81867614Sdarrenr break; 81960857Sdarrenr } 82060857Sdarrenr } 82167614Sdarrenr f->ftps_rptr = rptr; 82260857Sdarrenr 82360857Sdarrenr if (rptr == wptr) { 82460857Sdarrenr rptr = wptr = f->ftps_buf; 82560857Sdarrenr } else { 82660857Sdarrenr if ((wptr > f->ftps_buf + FTP_BUFSZ / 2)) { 82760857Sdarrenr i = wptr - rptr; 82860857Sdarrenr if ((rptr == f->ftps_buf) || 82960857Sdarrenr (wptr - rptr > FTP_BUFSZ / 2)) { 83060857Sdarrenr f->ftps_junk = 1; 83160857Sdarrenr rptr = wptr = f->ftps_buf; 83260857Sdarrenr } else { 83360857Sdarrenr bcopy(rptr, f->ftps_buf, i); 83460857Sdarrenr wptr = f->ftps_buf + i; 83560857Sdarrenr rptr = f->ftps_buf; 83660857Sdarrenr } 83760857Sdarrenr } 83860857Sdarrenr f->ftps_rptr = rptr; 83960857Sdarrenr f->ftps_wptr = wptr; 84060857Sdarrenr } 84160857Sdarrenr } 84260857Sdarrenr 84363523Sdarrenr t->ftps_seq = ntohl(tcp->th_ack); 84460857Sdarrenr f->ftps_rptr = rptr; 84560857Sdarrenr f->ftps_wptr = wptr; 84664580Sdarrenr return APR_INC(inc); 84760857Sdarrenr} 84860857Sdarrenr 84960857Sdarrenr 85060857Sdarrenrint ippr_ftp_out(fin, ip, aps, nat) 85160857Sdarrenrfr_info_t *fin; 85260857Sdarrenrip_t *ip; 85360857Sdarrenrap_session_t *aps; 85460857Sdarrenrnat_t *nat; 85560857Sdarrenr{ 85660857Sdarrenr ftpinfo_t *ftp; 85760857Sdarrenr 85860857Sdarrenr ftp = aps->aps_data; 85960857Sdarrenr if (ftp == NULL) 86060857Sdarrenr return 0; 86160857Sdarrenr return ippr_ftp_process(fin, ip, nat, ftp, 0); 86260857Sdarrenr} 86360857Sdarrenr 86460857Sdarrenr 86553642Sguidoint ippr_ftp_in(fin, ip, aps, nat) 86653642Sguidofr_info_t *fin; 86753642Sguidoip_t *ip; 86853642Sguidoap_session_t *aps; 86953642Sguidonat_t *nat; 87053642Sguido{ 87160857Sdarrenr ftpinfo_t *ftp; 87253642Sguido 87360857Sdarrenr ftp = aps->aps_data; 87460857Sdarrenr if (ftp == NULL) 87560857Sdarrenr return 0; 87660857Sdarrenr return ippr_ftp_process(fin, ip, nat, ftp, 1); 87753642Sguido} 87860857Sdarrenr 87960857Sdarrenr 88060857Sdarrenr/* 88160857Sdarrenr * ippr_ftp_atoi - implement a version of atoi which processes numbers in 88260857Sdarrenr * pairs separated by commas (which are expected to be in the range 0 - 255), 88360857Sdarrenr * returning a 16 bit number combining either side of the , as the MSB and 88460857Sdarrenr * LSB. 88560857Sdarrenr */ 88660857Sdarrenru_short ippr_ftp_atoi(ptr) 88760857Sdarrenrchar **ptr; 88860857Sdarrenr{ 88960857Sdarrenr register char *s = *ptr, c; 89060857Sdarrenr register u_char i = 0, j = 0; 89160857Sdarrenr 89260857Sdarrenr while ((c = *s++) && isdigit(c)) { 89360857Sdarrenr i *= 10; 89460857Sdarrenr i += c - '0'; 89560857Sdarrenr } 89660857Sdarrenr if (c != ',') { 89760857Sdarrenr *ptr = NULL; 89860857Sdarrenr return 0; 89960857Sdarrenr } 90060857Sdarrenr while ((c = *s++) && isdigit(c)) { 90160857Sdarrenr j *= 10; 90260857Sdarrenr j += c - '0'; 90360857Sdarrenr } 90460857Sdarrenr *ptr = s; 90567614Sdarrenr i &= 0xff; 90667614Sdarrenr j &= 0xff; 90760857Sdarrenr return (i << 8) | j; 90860857Sdarrenr} 909