| diff -ur ../pidentd-3.0.12-orig/src/k_linux.c ./src/k_linux.c |
| --- ../pidentd-3.0.12-orig/src/k_linux.c Sat Jan 12 00:44:05 2002 |
| +++ ./src/k_linux.c Sat Nov 3 07:51:28 2001 |
| @@ -26,12 +26,65 @@ |
| |
| #include "pidentd.h" |
| |
| +#define NETLINK_TCPDIAG 4 |
| +#define TCPDIAG_GETSOCK 18 |
| + |
| +#include <linux/uio.h> |
| +#include <linux/netlink.h> |
| + |
| +/* Socket identity */ |
| +struct tcpdiag_sockid |
| +{ |
| + __u16 tcpdiag_sport; |
| + __u16 tcpdiag_dport; |
| + __u32 tcpdiag_src[4]; |
| + __u32 tcpdiag_dst[4]; |
| + __u32 tcpdiag_if; |
| + __u32 tcpdiag_cookie[2]; |
| +#define TCPDIAG_NOCOOKIE (~0U) |
| +}; |
| + |
| +/* Request structure */ |
| + |
| +struct tcpdiagreq |
| +{ |
| + __u8 tcpdiag_family; /* Family of addresses. */ |
| + __u8 tcpdiag_src_len; |
| + __u8 tcpdiag_dst_len; |
| + __u8 tcpdiag_ext; /* Query extended information */ |
| + |
| + struct tcpdiag_sockid id; |
| + |
| + __u32 tcpdiag_states; /* States to dump */ |
| + __u32 tcpdiag_dbs; /* Tables to dump (NI) */ |
| +}; |
| + |
| +struct tcpdiagmsg |
| +{ |
| + __u8 tcpdiag_family; |
| + __u8 tcpdiag_state; |
| + __u8 tcpdiag_timer; |
| + __u8 tcpdiag_retrans; |
| + |
| + struct tcpdiag_sockid id; |
| + |
| + __u32 tcpdiag_expires; |
| + __u32 tcpdiag_rqueue; |
| + __u32 tcpdiag_wqueue; |
| + __u32 tcpdiag_uid; |
| + __u32 tcpdiag_inode; |
| +}; |
| + |
| + |
| +int tcpdiag_fd = -1; |
| + |
| /* |
| ** Make sure we are running on a supported OS version |
| */ |
| int |
| ka_init(void) |
| { |
| + tcpdiag_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_TCPDIAG); |
| return 0; /* We always succeed */ |
| } |
| |
| @@ -56,6 +109,144 @@ |
| } |
| |
| |
| + |
| +int k_lookup_tcpdiag(struct kernel *kp) |
| +{ |
| + struct sockaddr_nl nladdr; |
| + struct { |
| + struct nlmsghdr nlh; |
| + struct tcpdiagreq r; |
| + } req; |
| + struct msghdr msg; |
| + char buf[8192]; |
| + struct iovec iov[1]; |
| + struct tcpdiagmsg *r; |
| + static unsigned seqno = 123456; |
| + |
| + memset(&nladdr, 0, sizeof(nladdr)); |
| + nladdr.nl_family = AF_NETLINK; |
| + |
| + req.nlh.nlmsg_len = sizeof(req); |
| + req.nlh.nlmsg_type = TCPDIAG_GETSOCK; |
| + req.nlh.nlmsg_flags = NLM_F_REQUEST; |
| + req.nlh.nlmsg_pid = 0; |
| + req.nlh.nlmsg_seq = ++seqno; |
| + memset(&req.r, 0, sizeof(req.r)); |
| + req.r.tcpdiag_family = AF_INET; |
| + req.r.tcpdiag_states = ~0; |
| + |
| + req.r.id.tcpdiag_dport = kp->remote.sin_port; |
| + req.r.id.tcpdiag_sport = kp->local.sin_port; |
| + req.r.id.tcpdiag_dst[0] = kp->remote.sin_addr.s_addr; |
| + req.r.id.tcpdiag_src[0] = kp->local.sin_addr.s_addr; |
| + req.r.id.tcpdiag_cookie[0] = TCPDIAG_NOCOOKIE; |
| + req.r.id.tcpdiag_cookie[1] = TCPDIAG_NOCOOKIE; |
| + kp->ruid = NO_UID; |
| + |
| + iov[0] = (struct iovec){ &req, sizeof(req) }; |
| + |
| + msg = (struct msghdr) { |
| + (void*)&nladdr, sizeof(nladdr), |
| + iov, 1, |
| + NULL, 0, |
| + 0 |
| + }; |
| + |
| + if (sendmsg(tcpdiag_fd, &msg, 0) < 0) { |
| + if (errno == ECONNREFUSED) { |
| + close(tcpdiag_fd); |
| + tcpdiag_fd = -1; |
| + return 0; |
| + } |
| + syslog(LOG_ERR, "system error on tcpdiag sendmsg: %m"); |
| + return -1; |
| + } |
| + |
| + iov[0] = (struct iovec){ buf, sizeof(buf) }; |
| + |
| + while (1) { |
| + int status; |
| + struct nlmsghdr *h; |
| + |
| + msg = (struct msghdr) { |
| + (void*)&nladdr, sizeof(nladdr), |
| + iov, 1, |
| + NULL, 0, |
| + 0 |
| + }; |
| + |
| + status = recvmsg(tcpdiag_fd, &msg, 0); |
| + |
| + if (status < 0) { |
| + if (errno == EINTR || errno == EAGAIN) |
| + continue; |
| + return -1; |
| + } |
| + if (status == 0) { |
| + return -1; |
| + } |
| + |
| + h = (struct nlmsghdr*)buf; |
| + while (NLMSG_OK(h, status)) { |
| + int err; |
| + |
| + if (/*h->nlmsg_pid != rth->local.nl_pid ||*/ |
| + h->nlmsg_seq != seqno) |
| + goto skip_it; |
| + |
| + if (h->nlmsg_type == NLMSG_DONE) |
| + return -1; |
| + if (h->nlmsg_type == NLMSG_ERROR) { |
| + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); |
| + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { |
| + return -1; |
| + } else { |
| + errno = -err->error; |
| + if (errno == ECONNREFUSED) { |
| + close(tcpdiag_fd); |
| + tcpdiag_fd = -1; |
| + return 0; |
| + } |
| + if (errno != ENOENT) |
| + syslog(LOG_ERR, "tcpdiag answers: %m"); |
| + } |
| + return -1; |
| + } |
| + |
| + r = NLMSG_DATA(h); |
| + |
| + /* Lookup _may_ return listening socket, if no |
| + * better matches are found. */ |
| + if (r->id.tcpdiag_dport == kp->remote.sin_port && |
| + r->id.tcpdiag_dst[0] == kp->remote.sin_addr.s_addr) { |
| + kp->ruid = r->tcpdiag_uid; |
| + if (!r->tcpdiag_inode && !r->tcpdiag_uid) { |
| + /* _NEVER_ return "root" for closed |
| + * sockets. Otherwise people think |
| + * that it is sysadmin who abuses their |
| + * poor ircd. :-) */ |
| + syslog(LOG_NOTICE, |
| + "Req for stale socket(%d) %d from %x/%d", |
| + r->tcpdiag_state, ntohs(r->id.tcpdiag_sport), |
| + r->id.tcpdiag_dst[0], ntohs(r->id.tcpdiag_dport)); |
| + return -1; |
| + } |
| + return 1; |
| + } |
| + |
| + return -1; |
| + |
| +skip_it: |
| + h = NLMSG_NEXT(h, status); |
| + } |
| + if ((msg.msg_flags & MSG_TRUNC) || status) { |
| + syslog(LOG_ERR, "truncated tcp_diag message"); |
| + return -1; |
| + } |
| + } |
| +} |
| + |
| + |
| int |
| ka_lookup(void *vp, struct kernel *kp) |
| { |
| @@ -64,16 +255,23 @@ |
| long r_laddr, r_raddr, myladdr, myraddr; |
| int r_lport, r_rport, mylport, myrport; |
| int euid; |
| - |
| - |
| + |
| + if (tcpdiag_fd >= 0) { |
| + int res; |
| + if ((res = k_lookup_tcpdiag(kp)) != 0) |
| + return res; |
| + syslog(LOG_ERR, "tcp_diag is not loaded, fallback to proc"); |
| + } |
| + |
| + |
| r_rport = ntohs(kp->remote.sin_port); |
| r_lport = ntohs(kp->local.sin_port); |
| r_raddr = kp->remote.sin_addr.s_addr; |
| r_laddr = kp->local.sin_addr.s_addr; |
| + kp->ruid = NO_UID; |
| |
| fp = (FILE *) vp; |
| |
| - kp->ruid = NO_UID; |
| rewind(fp); |
| |
| /* eat header */ |
| @@ -82,13 +280,26 @@ |
| |
| while (fgets(buf, sizeof(buf)-1, fp) != NULL) |
| { |
| - if (sscanf(buf, "%*d: %lx:%x %lx:%x %*x %*x:%*x %*x:%*x %*x %d %*d %*d", |
| - &myladdr, &mylport, &myraddr, &myrport, &euid) == 5) |
| + int state, ino; |
| + if (sscanf(buf, "%*d: %x:%x %x:%x %x %*x:%*x %*x:%*x %*x %d %*d %u", |
| + &myladdr, &mylport, &myraddr, &myrport, |
| + &state, &euid, &ino) == 7) |
| { |
| if (myladdr == r_laddr && mylport == r_lport && |
| myraddr == r_raddr && myrport == r_rport) |
| { |
| kp->euid = euid; |
| + if (ino == 0 && euid == 0) |
| + { |
| + /* _NEVER_ return "root" for closed |
| + * sockets. Otherwise people think |
| + * that it is sysadmin who abuses their |
| + * poor ircd. :-) */ |
| + syslog(LOG_NOTICE, |
| + "Req for stale socket(%d) %d from %x/%d", |
| + state, r_rport, r_raddr, r_lport); |
| + return -1; |
| + } |
| return 1; |
| } |
| } |