summaryrefslogtreecommitdiff
path: root/apps/sshtosnmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/sshtosnmp.c')
-rw-r--r--apps/sshtosnmp.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/apps/sshtosnmp.c b/apps/sshtosnmp.c
new file mode 100644
index 0000000..d26067d
--- /dev/null
+++ b/apps/sshtosnmp.c
@@ -0,0 +1,233 @@
+/* Copyright 2009 SPARTA, Inc. All rights reserved
+ * Use is subject to license terms specified in the COPYING file
+ * distributed with the Net-SNMP package.
+ */
+
+/*
+ * This is merely a wrapper around stdin/out for sshd to call. It
+ * simply passes traffic to the running snmpd through a unix domain
+ * socket after first passing any needed SSH Domain information.
+ */
+
+#include <net-snmp/net-snmp-config.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#include <sys/select.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+
+#ifndef MAXPATHLEN
+#warn no system max path length detected
+#define MAXPATHLEN 2048
+#endif
+
+#define DEFAULT_SOCK_PATH "/var/net-snmp/sshdomainsocket"
+
+#define NETSNMP_SSHTOSNMP_VERSION_NUMBER 1
+
+
+
+/*
+ * Extra debugging output for, um, debugging.
+ */
+
+#undef DEBUGGING
+
+#ifdef DEBUGGING
+#define DEBUG(x) deb(x)
+FILE *debf = NULL;
+void
+deb(const char *string) {
+ if (NULL == debf) {
+ debf = fopen("/tmp/sshtosnmp.log", "a");
+ }
+ if (NULL != debf) {
+ fprintf(debf, "%s\n", string);
+ fflush(debf);
+ }
+}
+#else /* !DEBUGGING */
+#define DEBUG(x)
+#endif /* DEBUGGING code */
+
+int
+main(int argc, char **argv) {
+
+ int sock;
+ struct sockaddr_un addr;
+ u_char buf[4096];
+ size_t buf_len = sizeof(buf);
+ int rc = 0, pktsize = 0;
+
+ fd_set read_set;
+
+ DEBUG("----------\nstarting up");
+
+ /* Open a connection to the UNIX domain socket or fail */
+
+ addr.sun_family = AF_UNIX;
+ if (argc > 1) {
+ strcpy(addr.sun_path, argv[1]);
+ } else {
+ strcpy(addr.sun_path, DEFAULT_SOCK_PATH);
+ }
+
+ sock = socket(PF_UNIX, SOCK_STREAM, 0);
+ DEBUG("created socket");
+ if (sock <= 0) {
+ exit(1);
+ }
+
+ /* set the SO_PASSCRED option so we can pass uid */
+ /* XXX: according to the unix(1) manual this shouldn't be needed
+ on the sending side? */
+ {
+ int one = 1;
+ setsockopt(sock, SOL_SOCKET, SO_PASSCRED, (void *) &one,
+ sizeof(one));
+ }
+
+ if (connect(sock, (struct sockaddr *) &addr,
+ sizeof(struct sockaddr_un)) != 0) {
+ DEBUG("FAIL CONNECT");
+ exit(1);
+ }
+
+ DEBUG("opened socket");
+
+ /*
+ * we are running as the user that ssh authenticated us as, and this
+ * is the name/uid that the agent needs for processing as a SNMPv3
+ * security name. So this is the only thing needed to pass to the
+ * agent.
+ */
+
+ /* version 1 of our internal ssh to snmp wrapper is just a single
+ byte version number and indicates we're also passing unix
+ socket credentials containing our user id */
+
+ /* In case of future changes, we'll pass a version number first */
+
+ buf[0] = NETSNMP_SSHTOSNMP_VERSION_NUMBER;
+ buf_len = 1;
+
+ /* send the prelim message and the credentials together using sendmsg() */
+ {
+ struct msghdr m;
+ struct {
+ struct cmsghdr cm;
+ struct ucred ouruser;
+ } cmsg;
+ struct iovec iov = { buf, buf_len };
+
+ /* Make sure that even padding fields get initialized.*/
+ memset(&cmsg, 0, sizeof(cmsg));
+ memset(&m, 0, sizeof(m));
+
+ /* set up the basic message */
+ cmsg.cm.cmsg_len = sizeof(struct cmsghdr) + sizeof(struct ucred);
+ cmsg.cm.cmsg_level = SOL_SOCKET;
+ cmsg.cm.cmsg_type = SCM_CREDENTIALS;
+
+ cmsg.ouruser.uid = getuid();
+ cmsg.ouruser.gid = getgid();
+ cmsg.ouruser.pid = getpid();
+
+ m.msg_iov = &iov;
+ m.msg_iovlen = 1;
+ m.msg_control = &cmsg;
+ m.msg_controllen = sizeof(cmsg);
+ m.msg_flags = 0;
+
+ DEBUG("sending to sock");
+ rc = sendmsg(sock, &m, MSG_NOSIGNAL|MSG_DONTWAIT);
+ if (rc < 0) {
+ fprintf(stderr, "failed to send startup message\n");
+ DEBUG("failed to send startup message\n");
+ exit(1);
+ }
+ }
+
+ DEBUG("sent name");
+
+ /* now we just send and receive from both the socket and stdin/stdout */
+
+ while(1) {
+ /* read from stdin and the socket */
+ FD_SET(sock, &read_set);
+ FD_SET(STDIN_FILENO, &read_set);
+
+ /* blocking without a timeout be fine fine */
+ select(sock+1, &read_set, NULL, NULL, NULL);
+
+ if (FD_ISSET(STDIN_FILENO, &read_set)) {
+ /* read from stdin to get stuff from sshd to send to the agent */
+ DEBUG("data from stdin");
+ rc = read(STDIN_FILENO, buf, sizeof(buf));
+
+ if (rc <= 0) {
+ /* end-of-file */
+#ifndef HAVE_CLOSESOCKET
+ rc = close(sock);
+#else
+ rc = closesocket(sock);
+#endif
+ exit(0);
+ }
+ DEBUG("read from stdin");
+
+ /* send it up the pipe */
+ pktsize = rc;
+ rc = -1;
+ while (rc < 0) {
+ DEBUG("sending to socket");
+ rc = sendto(sock, buf, pktsize, 0, NULL, 0);
+ DEBUG("back from sendto");
+ if (rc < 0)
+ DEBUG("sentto failed");
+ if (rc < 0 && errno != EINTR) {
+ break;
+ }
+ }
+ if (rc > 0)
+ DEBUG("sent to socket");
+ else
+ DEBUG("failed to send to socket!!");
+ }
+
+ if (FD_ISSET(sock, &read_set)) {
+ /* read from the socket and send to to stdout which goes to sshd */
+ DEBUG("data on unix socket");
+
+ rc = -1;
+ while (rc < 0) {
+ rc = recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL);
+ if (rc < 0 && errno != EINTR) {
+ close(sock);
+ exit(0);
+ }
+ }
+ DEBUG("read from socket");
+
+ pktsize = rc;
+ rc = write(STDOUT_FILENO, buf, pktsize);
+ /* XXX: check that counts match */
+ if (rc > 0) {
+ DEBUG("wrote to stdout");
+ } else {
+ DEBUG("failed to write to stdout");
+ }
+ }
+ }
+}