Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 24 Oct 2012 10:28:12 +0000 (UTC)
From:      Gleb Smirnoff <glebius@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r241994 - stable/9/contrib/bsnmp/snmpd
Message-ID:  <201210241028.q9OASCoV094677@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: glebius
Date: Wed Oct 24 10:28:12 2012
New Revision: 241994
URL: http://svn.freebsd.org/changeset/base/241994

Log:
  Merge r240234, r240271, r240734 from head:
  
  r240234:
    The first part of check_priv() function, that attempts to obtain creds
    from the control message, actually never worked. This means check_priv()
    didn't work for local dgram sockets.
  
    The SCM_CREDS control messages is received only in two cases:
  
    1) If we did setsockopt(LOCAL_CREDS) on our socket, and in this case
       the message is struct sockcred.
    2) If sender did supplied SCM_CREDS control message in his sendmsg()
       syscall. In this case the message is struct cmsgcred.
  
    We can't rely on 2), so we will use 1) for dgram sockets. For stream
    sockets it is more reliable to obtain accept-time credentials, since
    SCM_CREDS control message is attached only on first read. Thus:
  
    o Do setsockopt(LOCAL_CREDS) on local dgram sockets.
    o Split check_priv() into check_priv_stream() and check_priv_dgram(),
      and call them from recv_stream() and recv_dgram() respectively.
    o Don't provide space for SCM_CREDS control message in recv_stream().
    o Provide space for SCM_CREDS control message in recv_dgram(), but there
      is no need to initialize anything in it.
    o In recv_dgram() do not blindly expect that first message is SCM_CREDS,
      instead use correct search cycle through control messages.
  
  r240271:
    For UDP transport set IP_RECVDSTADDR sockopt on the socket, and provide
    IP_SENDSRCADDR control with datagram message we reply with. This makes
    bsnmpd reply from exactly same address that request was sent to, thus
    successfully bypassing stateful firewalls or other kinds of strict checking.
  
  r240734:
    Re-do r240271:
    - Set IP_RECVDSTADDR sockopt on the socket only in case if
      it is INADDR_ANY bound.
    - Supply IP_SENDSRCADDR control message only if we did receive
      IP_RECVDSTADDR control message.
  
    This fixes operation of snmpd bound to a specific local IP address.
  
  PR:		bin/171279

Modified:
  stable/9/contrib/bsnmp/snmpd/main.c
  stable/9/contrib/bsnmp/snmpd/trans_lsock.c
  stable/9/contrib/bsnmp/snmpd/trans_udp.c
Directory Properties:
  stable/9/contrib/bsnmp/   (props changed)

Modified: stable/9/contrib/bsnmp/snmpd/main.c
==============================================================================
--- stable/9/contrib/bsnmp/snmpd/main.c	Wed Oct 24 10:14:21 2012	(r241993)
+++ stable/9/contrib/bsnmp/snmpd/main.c	Wed Oct 24 10:28:12 2012	(r241994)
@@ -1026,34 +1026,31 @@ snmp_input_consume(struct port_input *pi
 	pi->length -= pi->consumed;
 }
 
-struct credmsg {
-	struct cmsghdr hdr;
-	struct cmsgcred cred;
-};
+static void
+check_priv_dgram(struct port_input *pi, struct sockcred *cred)
+{
+
+	/* process explicitly sends credentials */
+	if (cred)
+		pi->priv = (cred->sc_euid == 0);
+	else
+		pi->priv = 0;
+}
 
 static void
-check_priv(struct port_input *pi, struct msghdr *msg)
+check_priv_stream(struct port_input *pi)
 {
-	struct credmsg *cmsg;
 	struct xucred ucred;
 	socklen_t ucredlen;
 
-	pi->priv = 0;
-
-	if (msg->msg_controllen == sizeof(*cmsg)) {
-		/* process explicitly sends credentials */
-
-		cmsg = (struct credmsg *)msg->msg_control;
-		pi->priv = (cmsg->cred.cmcred_euid == 0);
-		return;
-	}
-
-	/* ok, obtain the accept time credentials */
+	/* obtain the accept time credentials */
 	ucredlen = sizeof(ucred);
 
 	if (getsockopt(pi->fd, 0, LOCAL_PEERCRED, &ucred, &ucredlen) == 0 &&
 	    ucredlen >= sizeof(ucred) && ucred.cr_version == XUCRED_VERSION)
 		pi->priv = (ucred.cr_uid == 0);
+	else
+		pi->priv = 0;
 }
 
 /*
@@ -1065,7 +1062,6 @@ recv_stream(struct port_input *pi)
 	struct msghdr msg;
 	struct iovec iov[1];
 	ssize_t len;
-	struct credmsg cmsg;
 
 	if (pi->buf == NULL) {
 		/* no buffer yet - allocate one */
@@ -1084,17 +1080,8 @@ recv_stream(struct port_input *pi)
 	msg.msg_namelen = pi->peerlen;
 	msg.msg_iov = iov;
 	msg.msg_iovlen = 1;
-	if (pi->cred) {
-		msg.msg_control = &cmsg;
-		msg.msg_controllen = sizeof(cmsg);
-
-		cmsg.hdr.cmsg_len = sizeof(cmsg);
-		cmsg.hdr.cmsg_level = SOL_SOCKET;
-		cmsg.hdr.cmsg_type = SCM_CREDS;
-	} else {
-		msg.msg_control = NULL;
-		msg.msg_controllen = 0;
-	}
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
 	msg.msg_flags = 0;
 
 	iov[0].iov_base = pi->buf + pi->length;
@@ -1109,7 +1096,7 @@ recv_stream(struct port_input *pi)
 	pi->length += len;
 
 	if (pi->cred)
-		check_priv(pi, &msg);
+		check_priv_stream(pi);
 
 	return (0);
 }
@@ -1119,13 +1106,16 @@ recv_stream(struct port_input *pi)
  * Each receive should return one datagram.
  */
 static int
-recv_dgram(struct port_input *pi)
+recv_dgram(struct port_input *pi, struct in_addr *laddr)
 {
 	u_char embuf[1000];
+	char cbuf[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) +
+	    CMSG_SPACE(sizeof(struct in_addr))];
 	struct msghdr msg;
 	struct iovec iov[1];
 	ssize_t len;
-	struct credmsg cmsg;
+	struct cmsghdr *cmsg;
+	struct sockcred *cred = NULL;
 
 	if (pi->buf == NULL) {
 		/* no buffer yet - allocate one */
@@ -1145,17 +1135,9 @@ recv_dgram(struct port_input *pi)
 	msg.msg_namelen = pi->peerlen;
 	msg.msg_iov = iov;
 	msg.msg_iovlen = 1;
-	if (pi->cred) {
-		msg.msg_control = &cmsg;
-		msg.msg_controllen = sizeof(cmsg);
-
-		cmsg.hdr.cmsg_len = sizeof(cmsg);
-		cmsg.hdr.cmsg_level = SOL_SOCKET;
-		cmsg.hdr.cmsg_type = SCM_CREDS;
-	} else {
-		msg.msg_control = NULL;
-		msg.msg_controllen = 0;
-	}
+	memset(cbuf, 0, sizeof(cbuf));
+	msg.msg_control = cbuf;
+	msg.msg_controllen = sizeof(cbuf);
 	msg.msg_flags = 0;
 
 	iov[0].iov_base = pi->buf;
@@ -1176,8 +1158,18 @@ recv_dgram(struct port_input *pi)
 
 	pi->length = (size_t)len;
 
+	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+		if (cmsg->cmsg_level == IPPROTO_IP &&
+		    cmsg->cmsg_type == IP_RECVDSTADDR)
+			memcpy(laddr, CMSG_DATA(cmsg), sizeof(struct in_addr));
+		if (cmsg->cmsg_level == SOL_SOCKET &&
+		    cmsg->cmsg_type == SCM_CREDS)
+			cred = (struct sockcred *)CMSG_DATA(cmsg);
+	}
+
 	if (pi->cred)
-		check_priv(pi, &msg);
+		check_priv_dgram(pi, cred);
 
 	return (0);
 }
@@ -1199,12 +1191,35 @@ snmpd_input(struct port_input *pi, struc
 #ifdef USE_TCPWRAPPERS
 	char client[16];
 #endif
+	struct msghdr msg;
+	struct iovec iov[1];
+	char cbuf[CMSG_SPACE(sizeof(struct in_addr))];
+	struct cmsghdr *cmsgp;
 
 	/* get input depending on the transport */
 	if (pi->stream) {
+		msg.msg_control = NULL;
+		msg.msg_controllen = 0;
+
 		ret = recv_stream(pi);
 	} else {
-		ret = recv_dgram(pi);
+		struct in_addr *laddr;
+
+		memset(cbuf, 0, CMSG_SPACE(sizeof(struct in_addr)));
+		msg.msg_control = cbuf;
+		msg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
+		cmsgp = CMSG_FIRSTHDR(&msg);
+		cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+		cmsgp->cmsg_level = IPPROTO_IP;
+		cmsgp->cmsg_type = IP_SENDSRCADDR;
+		laddr = (struct in_addr *)CMSG_DATA(cmsgp);
+		
+		ret = recv_dgram(pi, laddr);
+
+		if (laddr->s_addr == 0) {
+			msg.msg_control = NULL;
+			msg.msg_controllen = 0;
+		}
 	}
 
 	if (ret == -1)
@@ -1349,11 +1364,19 @@ snmpd_input(struct port_input *pi, struc
 	    sndbuf, &sndlen, "SNMP", ierr, vi, NULL);
 
 	if (ferr == SNMPD_INPUT_OK) {
-		slen = sendto(pi->fd, sndbuf, sndlen, 0, pi->peer, pi->peerlen);
+		msg.msg_name = pi->peer;
+		msg.msg_namelen = pi->peerlen;
+		msg.msg_iov = iov;
+		msg.msg_iovlen = 1;
+		msg.msg_flags = 0;
+		iov[0].iov_base = sndbuf;
+		iov[0].iov_len = sndlen;
+
+		slen = sendmsg(pi->fd, &msg, 0);
 		if (slen == -1)
-			syslog(LOG_ERR, "sendto: %m");
+			syslog(LOG_ERR, "sendmsg: %m");
 		else if ((size_t)slen != sndlen)
-			syslog(LOG_ERR, "sendto: short write %zu/%zu",
+			syslog(LOG_ERR, "sendmsg: short write %zu/%zu",
 			    sndlen, (size_t)slen);
 	}
 	snmp_pdu_free(&pdu);

Modified: stable/9/contrib/bsnmp/snmpd/trans_lsock.c
==============================================================================
--- stable/9/contrib/bsnmp/snmpd/trans_lsock.c	Wed Oct 24 10:14:21 2012	(r241993)
+++ stable/9/contrib/bsnmp/snmpd/trans_lsock.c	Wed Oct 24 10:28:12 2012	(r241994)
@@ -343,6 +343,7 @@ lsock_init_port(struct tport *tp)
 		}
 	} else {
 		struct lsock_peer *peer;
+		const int on = 1;
 
 		peer = LIST_FIRST(&p->peers);
 
@@ -351,6 +352,14 @@ lsock_init_port(struct tport *tp)
 			return (SNMP_ERR_RES_UNAVAIL);
 		}
 
+		if (setsockopt(peer->input.fd, 0, LOCAL_CREDS, &on,
+		    sizeof(on)) == -1) {
+			syslog(LOG_ERR, "setsockopt(LOCAL_CREDS): %m");
+			close(peer->input.fd);
+			peer->input.fd = -1;
+			return (SNMP_ERR_GENERR);
+		}
+
 		strcpy(sa.sun_path, p->name);
 		sa.sun_family = AF_LOCAL;
 		sa.sun_len = strlen(p->name) +

Modified: stable/9/contrib/bsnmp/snmpd/trans_udp.c
==============================================================================
--- stable/9/contrib/bsnmp/snmpd/trans_udp.c	Wed Oct 24 10:14:21 2012	(r241993)
+++ stable/9/contrib/bsnmp/snmpd/trans_udp.c	Wed Oct 24 10:28:12 2012	(r241994)
@@ -103,6 +103,7 @@ udp_init_port(struct tport *tp)
 	struct udp_port *p = (struct udp_port *)tp;
 	struct sockaddr_in addr;
 	u_int32_t ip;
+	const int on = 1;
 
 	if ((p->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
 		syslog(LOG_ERR, "creating UDP socket: %m");
@@ -115,6 +116,14 @@ udp_init_port(struct tport *tp)
 	addr.sin_port = htons(p->port);
 	addr.sin_family = AF_INET;
 	addr.sin_len = sizeof(addr);
+	if (addr.sin_addr.s_addr == INADDR_ANY &&
+	    setsockopt(p->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
+	    sizeof(on)) == -1) {
+		syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
+		close(p->input.fd);
+		p->input.fd = -1;
+		return (SNMP_ERR_GENERR);
+	}
 	if (bind(p->input.fd, (struct sockaddr *)&addr, sizeof(addr))) {
 		if (errno == EADDRNOTAVAIL) {
 			close(p->input.fd);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201210241028.q9OASCoV094677>