/** * Parse a Linux connection table (/proc/net/tcp or /proc/net/udp) and filter * connection: only keep session user connections in state "SYN packet sent". * Add connections to the our table using tcptable_add(). */ int parse_tcptable_file(nuauth_session_t * session, conntable_t * ct, char *filename, FILE ** file, int protocol, int use_ipv6) { char *buf; char fullbuf[1024*256]; conn_t c; const char state_char = '2'; /* TCP_SYN_SENT written in hexadecimal */ int state_pos; int uid_pos; char session_uid[20]; int session_uid_len; int ret; char *pos; int fdfile; int i = 0; int readlen = 0; fdfile = open(filename, O_RDONLY); if (fdfile == -1) { panic("Unable to open proc file"); } /* read all file */ buf = fullbuf; while ( ((1024*256 - (buf - fullbuf)) > 0) && (i = read(fdfile, buf, 1024*256 - (buf - fullbuf)))) { buf += i; readlen += i; } close(fdfile); /* convert session user identifier to string */ secure_snprintf(session_uid, sizeof(session_uid), "%5lu", (long)session->userid); session_uid_len = strlen(session_uid); /* get state field position in header */ pos = strstr(fullbuf, " st "); if (pos == NULL) panic ("Can't find position of state field in /proc/net/tcp header!"); state_pos = pos - fullbuf + 2; /* get user identifier position in header (it's just after 'retrnsmt' field) */ pos = strstr(fullbuf, " retrnsmt "); if (pos == NULL) panic ("Can't find position of user identifier field in /proc/net/tcp header!"); uid_pos = pos - fullbuf + strlen(" retrnsmt "); buf = fullbuf; while (buf - fullbuf < readlen - uid_pos) { buf = strchr(buf, '\n') + 1; /* only keep connections in state "SYN packet sent" */ if (buf[state_pos] != state_char) { continue; } /* only keep session user connections */ if (strncmp(buf + uid_pos, session_uid, session_uid_len) != 0) { continue; } /* get all fields */ if (!use_ipv6) { uint32_t src, dst; ret = sscanf(buf, "%*d: " "%" SCNx32 ":%hx " "%" SCNx32 ":%hx " "%*x %*x:%*x %*x:%*x %x " "%lu %*d %lu", &src, &c.port_src, &dst, &c.port_dst, &c.retransmit, &c.uid, &c.inode); if (ret != 7) { continue; } uint32_to_ipv6(src, &c.ip_src); uint32_to_ipv6(dst, &c.ip_dst); } else { char ip_src[33]; char ip_dst[33]; ret = sscanf(buf, "%*d: " "%32s" ":%hx " "%32s" ":%hx " "%*x %*x:%*x %*x:%*x %x " "%lu %*d %lu", ip_src, &c.port_src, ip_dst, &c.port_dst, &c.retransmit, &c.uid, &c.inode); if (ret != 7) { continue; } if (!hex2ipv6(ip_src, &c.ip_src)) continue; if (!hex2ipv6(ip_dst, &c.ip_dst)) continue; } /* skip nul inodes */ if (c.inode == 0) { continue; } #if DEBUG /* Check if there is a matching rule in the filters list */ printf("Packet dst = %ld (%lx)\n", c.rmt, c.rmt); #endif c.protocol = protocol; tcptable_add(ct, &c); } return 1; }
static int ip2bin(struct ulogd_key* inp, int index, int oindex) { char family = ikey_get_u8(&inp[KEY_OOB_FAMILY]); char convfamily = family; unsigned char *addr8; struct in6_addr *addr; struct in6_addr ip4_addr; char *buffer; int i, written; if (family == AF_BRIDGE) { if (!pp_is_valid(inp, KEY_OOB_PROTOCOL)) { ulogd_log(ULOGD_NOTICE, "No protocol inside AF_BRIDGE packet\n"); return ULOGD_IRET_ERR; } switch (ikey_get_u16(&inp[KEY_OOB_PROTOCOL])) { case ETH_P_IPV6: convfamily = AF_INET6; break; case ETH_P_IP: convfamily = AF_INET; break; case ETH_P_ARP: convfamily = AF_INET; break; default: ulogd_log(ULOGD_NOTICE, "Unknown protocol inside AF_BRIDGE packet\n"); return ULOGD_IRET_ERR; } } switch (convfamily) { case AF_INET6: addr = (struct in6_addr *)ikey_get_u128(&inp[index]); break; case AF_INET: /* Convert IPv4 to IPv4 in IPv6 */ addr = &ip4_addr; uint32_to_ipv6(ikey_get_u32(&inp[index]), addr); break; default: /* TODO handle error */ ulogd_log(ULOGD_NOTICE, "Unknown protocol family\n"); return ULOGD_IRET_ERR; } buffer = ipbin_array[oindex]; /* format IPv6 to BINARY(16) as "0x..." */ buffer[0] = '0'; buffer[1] = 'x'; buffer += 2; addr8 = &addr->s6_addr[0]; for (i = 0; i < 4; i++) { written = sprintf(buffer, "%02x%02x%02x%02x", addr8[0], addr8[1], addr8[2], addr8[3]); if (written != 2 * 4) { buffer[0] = 0; return ULOGD_IRET_ERR; } buffer += written; addr8 += 4; } buffer[0] = 0; return ULOGD_IRET_OK; }
/** * On Linux: Parse connection table /proc/net/tcp and /proc/net/udp to get * connections in state "SYN sent" from session user. * * On FreeBSD: Use sysctl with "net.inet.tcp.pcblist" to get the connection * table. Add connections to the our table using tcptable_add(). */ int tcptable_read(nuauth_session_t * session, conntable_t * ct) { #ifdef LINUX static FILE *fd_tcp = NULL; static FILE *fd_udp = NULL; #ifdef IPV6 static FILE *fd_tcp6 = NULL; #endif #if DEBUG assert(ct != NULL); assert(TCP_SYN_SENT == 2); #endif if (!parse_tcptable_file (session, ct, "/proc/net/tcp", &fd_tcp, IPPROTO_TCP, 0)) return 0; #ifdef IPV6 parse_tcptable_file(session, ct, "/proc/net/tcp6", &fd_tcp6, IPPROTO_TCP, 1); #endif if (!parse_tcptable_file (session, ct, "/proc/net/udp", &fd_udp, IPPROTO_UDP, 0)) return 0; return 1; #elif defined(FREEBSD) conn_t c; int istcp; char *buf; const char *mibvar; struct tcpcb *tp = NULL; struct inpcb *inp; struct xinpgen *xig, *oxig; struct xsocket *so; size_t len; int proto = IPPROTO_TCP; #if 0 istcp = 0; switch (proto) { case IPPROTO_TCP: #endif istcp = 1; mibvar = "net.inet.tcp.pcblist"; #if 0 break; case IPPROTO_UDP: mibvar = "net.inet.udp.pcblist"; break; } #endif /* get connection table size, and then allocate a buffer */ len = 0; if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) { if (errno != ENOENT) printf("sysctl: %s", mibvar); return 0; } buf = malloc(len); if (buf == NULL) { printf("malloc %lu bytes", (u_long) len); return 0; } /* read connection table */ if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) { printf("sysctl: %s", mibvar); free(buf); return 0; } oxig = xig = (struct xinpgen *) buf; for (xig = (struct xinpgen *) ((char *) xig + xig->xig_len); xig->xig_len > sizeof(struct xinpgen); xig = (struct xinpgen *) ((char *) xig + xig->xig_len)) { if (istcp) { tp = &((struct xtcpcb *) xig)->xt_tp; inp = &((struct xtcpcb *) xig)->xt_inp; so = &((struct xtcpcb *) xig)->xt_socket; } else { inp = &((struct xinpcb *) xig)->xi_inp; so = &((struct xinpcb *) xig)->xi_socket; } /* Ignore sockets for protocols other than the desired one. */ if (so->xso_protocol != (int) proto) continue; /* Ignore PCBs which were freed during copyout. */ if (inp->inp_gencnt > oxig->xig_gen) continue; /* only do IPV4 for now */ if ((inp->inp_vflag & INP_IPV4) == 0) continue; /* check SYN_SENT and get rid of NULL address */ if ((istcp && tp->t_state != TCPS_SYN_SENT) || (inet_lnaof(inp->inp_laddr) == INADDR_ANY)) continue; uint32_to_ipv6(inp->inp_laddr.s_addr, &c.ip_src); c.port_src = ntohs(inp->inp_lport); uint32_to_ipv6(inp->inp_faddr.s_addr, &c.ip_dst); c.port_dst = ntohs(inp->inp_fport); c.protocol = IPPROTO_TCP; tcptable_add(ct, &c); } free(buf); return 1; #endif }