Blame apps/sshtosnmp.c

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