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