1 /* Copyright 2009 SPARTA, Inc. All rights reserved
2  * Use is subject to license terms specified in the COPYING file
3  * distributed with the Net-SNMP package.
4  */
5 
6 /*
7  * This is merely a wrapper around stdin/out for sshd to call.  It
8  * simply passes traffic to the running snmpd through a unix domain
9  * socket after first passing any needed SSH Domain information.
10  */
11 
12 #include <net-snmp/net-snmp-config.h>
13 
14 #ifdef HAVE_SYS_PARAM_H
15 #include <sys/param.h>
16 #endif
17 #ifdef HAVE_SYS_SOCKET_H
18 #include <sys/socket.h>
19 #endif
20 #if HAVE_SYS_UN_H
21 #include <sys/un.h>
22 #endif
23 
24 #include <sys/select.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <stdio.h>
29 
30 #ifndef MAXPATHLEN
31 #warning no system max path length detected
32 #define MAXPATHLEN 2048
33 #endif
34 
35 #define DEFAULT_SOCK_PATH "/var/net-snmp/sshdomainsocket"
36 
37 #define NETSNMP_SSHTOSNMP_VERSION_NUMBER 1
38 
39 
40 
41 /*
42  * Extra debugging output for, um, debugging.
43  */
44 
45 #undef DEBUGGING
46 
47 #define DEBUG(x) deb(x)
48 #ifdef DEBUGGING
49 FILE *debf = NULL;
50 static void
deb(const char * string)51 deb(const char *string) {
52     if (NULL == debf) {
53         debf = fopen("/tmp/sshtosnmp.log", "a");
54     }
55     if (NULL != debf) {
56         fprintf(debf, "%s\n", string);
57         fflush(debf);
58     }
59 }
60 #else  /* !DEBUGGING */
61 NETSNMP_STATIC_INLINE void
deb(const char * string)62 deb(const char *string) { }
63 #endif /* DEBUGGING code */
64 
65 int
main(int argc,char ** argv)66 main(int argc, char **argv) {
67 
68     int sock;
69     struct sockaddr_un addr;
70     u_char buf[4096];
71     size_t buf_len = sizeof(buf);
72     int rc = 0, pktsize = 0;
73 
74     fd_set read_set;
75 
76     DEBUG("----------\nstarting up");
77 
78     /* Open a connection to the UNIX domain socket or fail */
79 
80     addr.sun_family = AF_UNIX;
81     snprintf(addr.sun_path, sizeof(addr.sun_path), "%s",
82              argc > 1 ? argv[1] : DEFAULT_SOCK_PATH);
83 
84     sock = socket(PF_UNIX, SOCK_STREAM, 0);
85     DEBUG("created socket");
86     if (sock <= 0) {
87         exit(1);
88     }
89 
90     /* set the SO_PASSCRED option so we can pass uid */
91     /* XXX: according to the unix(1) manual this shouldn't be needed
92        on the sending side? */
93     {
94         int one = 1;
95         setsockopt(sock, SOL_SOCKET, SO_PASSCRED, (void *) &one,
96                    sizeof(one));
97     }
98 
99     if (connect(sock, (struct sockaddr *) &addr,
100                 sizeof(struct sockaddr_un)) != 0) {
101         DEBUG("FAIL CONNECT");
102         exit(1);
103     }
104 
105     DEBUG("opened socket");
106 
107     /*
108      * we are running as the user that ssh authenticated us as, and this
109      * is the name/uid that the agent needs for processing as a SNMPv3
110      * security name.  So this is the only thing needed to pass to the
111      * agent.
112      */
113 
114     /* version 1 of our internal ssh to snmp wrapper is just a single
115        byte version number and indicates we're also passing unix
116        socket credentials containing our user id */
117 
118     /* In case of future changes, we'll pass a version number first */
119 
120     buf[0] = NETSNMP_SSHTOSNMP_VERSION_NUMBER;
121     buf_len = 1;
122 
123     /* send the prelim message and the credentials together using sendmsg() */
124     {
125         struct msghdr m;
126         /*
127          * Ancillary data buffer, wrapped in a union in order to ensure it is
128          * suitably aligned.
129          */
130         union {
131             char buf[CMSG_SPACE(sizeof(struct ucred))];
132             struct cmsghdr cm;
133         } cmsg;
134         struct ucred *const ouruser = (void *)CMSG_DATA(&cmsg.cm);
135         struct iovec iov = { buf, buf_len };
136 
137         /* Make sure that even padding fields get initialized.*/
138         memset(&cmsg, 0, sizeof(cmsg));
139         memset(&m, 0, sizeof(m));
140 
141         /* set up the message header */
142         cmsg.cm.cmsg_len = sizeof(cmsg);
143         cmsg.cm.cmsg_level = SOL_SOCKET;
144         cmsg.cm.cmsg_type = SCM_CREDENTIALS;
145 
146         ouruser->uid = getuid();
147         ouruser->gid = getgid();
148         ouruser->pid = getpid();
149 
150         m.msg_iov               = &iov;
151         m.msg_iovlen            = 1;
152         m.msg_control           = &cmsg;
153         m.msg_controllen        = sizeof(cmsg);
154         m.msg_flags             = 0;
155 
156         DEBUG("sending to sock");
157         rc = sendmsg(sock, &m, MSG_NOSIGNAL|MSG_DONTWAIT);
158         if (rc < 0) {
159             fprintf(stderr, "failed to send startup message\n");
160             DEBUG("failed to send startup message\n");
161             exit(1);
162         }
163     }
164 
165     DEBUG("sent name");
166 
167     /* now we just send and receive from both the socket and stdin/stdout */
168 
169     while(1) {
170         /* read from stdin and the socket */
171         FD_ZERO(&read_set);
172         FD_SET(sock, &read_set);
173         FD_SET(STDIN_FILENO, &read_set);
174 
175         /* blocking without a timeout be fine fine */
176         select(sock+1, &read_set, NULL, NULL, NULL);
177 
178         if (FD_ISSET(STDIN_FILENO, &read_set)) {
179             /* read from stdin to get stuff from sshd to send to the agent */
180             DEBUG("data from stdin");
181             rc = read(STDIN_FILENO, buf, sizeof(buf));
182 
183             if (rc <= 0) {
184                 /* end-of-file */
185 #ifndef HAVE_CLOSESOCKET
186                 rc = close(sock);
187 #else
188                 rc = closesocket(sock);
189 #endif
190                 exit(0);
191             }
192             DEBUG("read from stdin");
193 
194             /* send it up the pipe */
195             pktsize = rc;
196             rc = -1;
197             while (rc < 0) {
198                 DEBUG("sending to socket");
199                 rc = sendto(sock, buf, pktsize, 0, NULL, 0);
200                 DEBUG("back from sendto");
201                 if (rc < 0)
202                     DEBUG("sentto failed");
203                 if (rc < 0 && errno != EINTR) {
204                     break;
205                 }
206             }
207             if (rc > 0)
208                 DEBUG("sent to socket");
209             else
210                 DEBUG("failed to send to socket!!");
211         }
212 
213         if (FD_ISSET(sock, &read_set)) {
214             /* read from the socket and send to to stdout which goes to sshd */
215             DEBUG("data on unix socket");
216 
217             rc = -1;
218             while (rc < 0) {
219                 rc = recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL);
220                 if (rc < 0 && errno != EINTR) {
221                     close(sock);
222                     exit(0);
223                 }
224             }
225             DEBUG("read from socket");
226 
227             pktsize = rc;
228             rc = write(STDOUT_FILENO, buf, pktsize);
229             /* XXX: check that counts match */
230             if (rc > 0) {
231                 DEBUG("wrote to stdout");
232             } else {
233                 DEBUG("failed to write to stdout");
234             }
235         }
236     }
237 }
238