/src/net-snmp/snmplib/transports/snmpUnixDomain.c
Line | Count | Source |
1 | | #include <net-snmp/net-snmp-config.h> |
2 | | #include <net-snmp/net-snmp-features.h> |
3 | | |
4 | | #include <sys/types.h> |
5 | | #include <net-snmp/library/snmpUnixDomain.h> |
6 | | |
7 | | #include <stddef.h> |
8 | | #include <stdio.h> |
9 | | #include <ctype.h> |
10 | | #include <errno.h> |
11 | | |
12 | | #ifdef HAVE_STRING_H |
13 | | #include <string.h> |
14 | | #else |
15 | | #include <strings.h> |
16 | | #endif |
17 | | #ifdef HAVE_STDLIB_H |
18 | | #include <stdlib.h> |
19 | | #endif |
20 | | #ifdef HAVE_UNISTD_H |
21 | | #include <unistd.h> |
22 | | #endif |
23 | | #ifdef HAVE_SYS_SOCKET_H |
24 | | #include <sys/socket.h> |
25 | | #endif |
26 | | |
27 | | #include <net-snmp/types.h> |
28 | | #include <net-snmp/output_api.h> |
29 | | #include <net-snmp/config_api.h> |
30 | | |
31 | | #include <net-snmp/library/snmp.h> |
32 | | #include <net-snmp/library/snmp_impl.h> |
33 | | #include <net-snmp/library/snmp_transport.h> |
34 | | #include <net-snmp/library/snmpSocketBaseDomain.h> |
35 | | #include <net-snmp/library/system.h> /* mkdirhier */ |
36 | | #include <net-snmp/library/tools.h> |
37 | | |
38 | | #ifndef NETSNMP_NO_SYSTEMD |
39 | | #include <net-snmp/library/sd-daemon.h> |
40 | | #endif |
41 | | |
42 | | netsnmp_feature_child_of(transport_unix_socket_all, transport_all); |
43 | | netsnmp_feature_child_of(unix_socket_paths, transport_unix_socket_all); |
44 | | |
45 | | #ifndef NETSNMP_STREAM_QUEUE_LEN |
46 | | #define NETSNMP_STREAM_QUEUE_LEN 5 |
47 | | #endif |
48 | | |
49 | | #undef SUN_LEN |
50 | | /* |
51 | | * Evaluate to actual length of the `sockaddr_un' structure. |
52 | | */ |
53 | 0 | #define SUN_LEN(ptr) ((size_t)&(((struct sockaddr_un *)NULL)->sun_path) \ |
54 | 0 | + strlen ((ptr)->sun_path)) |
55 | | |
56 | | const oid netsnmp_UnixDomain[] = { TRANSPORT_DOMAIN_LOCAL }; |
57 | | static netsnmp_tdomain unixDomain; |
58 | | |
59 | | |
60 | | /* |
61 | | * This is the structure we use to hold transport-specific data. |
62 | | */ |
63 | | |
64 | | typedef struct _sockaddr_un_pair { |
65 | | int local; |
66 | | struct sockaddr_un server; |
67 | | struct sockaddr_un client; |
68 | | } sockaddr_un_pair; |
69 | | |
70 | | |
71 | | /* |
72 | | * Return a string representing the address in data, or else the "far end" |
73 | | * address if data is NULL. |
74 | | */ |
75 | | |
76 | | static char * |
77 | | netsnmp_unix_fmtaddr(netsnmp_transport *t, const void *data, int len) |
78 | 0 | { |
79 | 0 | const struct sockaddr_un *to = NULL; |
80 | |
|
81 | 0 | if (data != NULL) |
82 | 0 | to = (const struct sockaddr_un *) data; |
83 | 0 | else if (t != NULL && t->data != NULL) |
84 | 0 | to = &(((const sockaddr_un_pair *) t->data)->server); |
85 | 0 | if (to == NULL) { |
86 | | /* |
87 | | * "Local IPC" is the Posix.1g term for Unix domain protocols, |
88 | | * according to W. R. Stevens, ``Unix Network Programming Volume I |
89 | | * Second Edition'', p. 374. |
90 | | */ |
91 | 0 | return strdup("Local IPC: unknown"); |
92 | 0 | } else if (to->sun_path[0] == 0) { |
93 | | /* |
94 | | * This is an abstract name. We could render it as hex or something |
95 | | * but let's not worry about that for now. |
96 | | */ |
97 | 0 | return strdup("Local IPC: abstract"); |
98 | 0 | } else { |
99 | 0 | char *tmp; |
100 | |
|
101 | 0 | if (asprintf(&tmp, "Local IPC: %s", to->sun_path) < 0) |
102 | 0 | tmp = NULL; |
103 | 0 | return tmp; |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | | static void |
108 | | netsnmp_unix_get_taddr(netsnmp_transport *t, void **addr, size_t *addr_len) |
109 | 0 | { |
110 | 0 | *addr_len = t->remote_length; |
111 | 0 | *addr = netsnmp_memdup(t->remote, *addr_len); |
112 | 0 | } |
113 | | |
114 | | /* |
115 | | * You can write something into opaque that will subsequently get passed back |
116 | | * to your send function if you like. For instance, you might want to |
117 | | * remember where a PDU came from, so that you can send a reply there... |
118 | | */ |
119 | | |
120 | | static int |
121 | | netsnmp_unix_recv(netsnmp_transport *t, void *buf, int size, |
122 | | void **opaque, int *olength) |
123 | 0 | { |
124 | 0 | int rc = -1; |
125 | 0 | socklen_t tolen = sizeof(struct sockaddr_un); |
126 | 0 | struct sockaddr *to; |
127 | | |
128 | |
|
129 | 0 | if (t != NULL && t->sock >= 0) { |
130 | 0 | to = (struct sockaddr *) malloc(sizeof(struct sockaddr_un)); |
131 | 0 | if (to == NULL) { |
132 | 0 | *opaque = NULL; |
133 | 0 | *olength = 0; |
134 | 0 | return -1; |
135 | 0 | } else { |
136 | 0 | memset(to, 0, tolen); |
137 | 0 | } |
138 | 0 | if(getsockname(t->sock, to, &tolen) != 0){ |
139 | 0 | free(to); |
140 | 0 | *opaque = NULL; |
141 | 0 | *olength = 0; |
142 | 0 | return -1; |
143 | 0 | }; |
144 | 0 | while (rc < 0) { |
145 | 0 | #ifdef MSG_DONTWAIT |
146 | 0 | rc = recvfrom(t->sock, buf, size, MSG_DONTWAIT, NULL, NULL); |
147 | | #else |
148 | | rc = recvfrom(t->sock, buf, size, 0, NULL, NULL); |
149 | | #endif |
150 | 0 | if (rc < 0 && errno != EINTR) { |
151 | 0 | DEBUGMSGTL(("netsnmp_unix", "recv fd %d err %d (\"%s\")\n", |
152 | 0 | t->sock, errno, strerror(errno))); |
153 | 0 | free(to); |
154 | 0 | return rc; |
155 | 0 | } |
156 | 0 | *opaque = (void*)to; |
157 | 0 | *olength = sizeof(struct sockaddr_un); |
158 | 0 | } |
159 | 0 | DEBUGMSGTL(("netsnmp_unix", "recv fd %d got %d bytes\n", t->sock, rc)); |
160 | 0 | } |
161 | 0 | return rc; |
162 | 0 | } |
163 | | |
164 | | |
165 | | |
166 | | static int |
167 | | netsnmp_unix_send(netsnmp_transport *t, const void *buf, int size, |
168 | | void **opaque, int *olength) |
169 | 0 | { |
170 | 0 | int rc = -1; |
171 | |
|
172 | 0 | if (t != NULL && t->sock >= 0) { |
173 | 0 | DEBUGMSGTL(("netsnmp_unix", "send %d bytes to %p on fd %d\n", |
174 | 0 | size, buf, t->sock)); |
175 | 0 | while (rc < 0) { |
176 | 0 | rc = sendto(t->sock, buf, size, 0, NULL, 0); |
177 | 0 | if (rc < 0 && errno != EINTR) { |
178 | 0 | break; |
179 | 0 | } |
180 | 0 | } |
181 | 0 | } |
182 | 0 | return rc; |
183 | 0 | } |
184 | | |
185 | | |
186 | | |
187 | | static int |
188 | | netsnmp_unix_close(netsnmp_transport *t) |
189 | 0 | { |
190 | 0 | int rc = 0; |
191 | 0 | sockaddr_un_pair *sup = (sockaddr_un_pair *) t->data; |
192 | |
|
193 | 0 | if (t->sock >= 0) { |
194 | 0 | #ifndef HAVE_CLOSESOCKET |
195 | 0 | rc = close(t->sock); |
196 | | #else |
197 | | rc = closesocket(t->sock); |
198 | | #endif |
199 | 0 | t->sock = -1; |
200 | 0 | if (sup != NULL) { |
201 | 0 | if (sup->local) { |
202 | 0 | if (sup->server.sun_path[0] != 0) { |
203 | 0 | DEBUGMSGTL(("netsnmp_unix", "close: server unlink(\"%s\")\n", |
204 | 0 | sup->server.sun_path)); |
205 | 0 | unlink(sup->server.sun_path); |
206 | 0 | } |
207 | 0 | } else { |
208 | 0 | if (sup->client.sun_path[0] != 0) { |
209 | 0 | DEBUGMSGTL(("netsnmp_unix", "close: client unlink(\"%s\")\n", |
210 | 0 | sup->client.sun_path)); |
211 | 0 | unlink(sup->client.sun_path); |
212 | 0 | } |
213 | 0 | } |
214 | 0 | } |
215 | 0 | return rc; |
216 | 0 | } else { |
217 | 0 | return -1; |
218 | 0 | } |
219 | 0 | } |
220 | | |
221 | | |
222 | | |
223 | | static int |
224 | | netsnmp_unix_accept(netsnmp_transport *t) |
225 | 0 | { |
226 | 0 | struct sockaddr *farend = NULL; |
227 | 0 | int newsock = -1; |
228 | 0 | socklen_t farendlen = sizeof(struct sockaddr_un); |
229 | |
|
230 | 0 | farend = (struct sockaddr *) malloc(farendlen); |
231 | |
|
232 | 0 | if (farend == NULL) { |
233 | | /* |
234 | | * Indicate that the acceptance of this socket failed. |
235 | | */ |
236 | 0 | DEBUGMSGTL(("netsnmp_unix", "accept: malloc failed\n")); |
237 | 0 | return -1; |
238 | 0 | } |
239 | 0 | memset(farend, 0, farendlen); |
240 | |
|
241 | 0 | if (t != NULL && t->sock >= 0) { |
242 | 0 | newsock = accept(t->sock, farend, &farendlen); |
243 | |
|
244 | 0 | if (newsock < 0) { |
245 | 0 | DEBUGMSGTL(("netsnmp_unix","accept failed rc %d errno %d \"%s\"\n", |
246 | 0 | newsock, errno, strerror(errno))); |
247 | 0 | free(farend); |
248 | 0 | return newsock; |
249 | 0 | } |
250 | | |
251 | 0 | if (t->data != NULL) { |
252 | 0 | free(t->data); |
253 | 0 | } |
254 | |
|
255 | 0 | DEBUGMSGTL(("netsnmp_unix", "accept succeeded (farend %p len %d)\n", |
256 | 0 | farend, (int) farendlen)); |
257 | 0 | t->data = farend; |
258 | 0 | t->data_length = sizeof(struct sockaddr_un); |
259 | 0 | netsnmp_sock_buffer_set(newsock, SO_SNDBUF, 1, 0); |
260 | 0 | netsnmp_sock_buffer_set(newsock, SO_RCVBUF, 1, 0); |
261 | 0 | return newsock; |
262 | 0 | } else { |
263 | 0 | free(farend); |
264 | 0 | return -1; |
265 | 0 | } |
266 | 0 | } |
267 | | |
268 | | static int create_path = 0; |
269 | | static mode_t create_mode; |
270 | | |
271 | | #ifndef NETSNMP_FEATURE_REMOVE_UNIX_SOCKET_PATHS |
272 | | /** If trying to create unix sockets in non-existing directories then |
273 | | * try to create the directory with mask mode. |
274 | | */ |
275 | | void netsnmp_unix_create_path_with_mode(int mode) |
276 | 0 | { |
277 | 0 | create_path = 1; |
278 | 0 | create_mode = mode; |
279 | 0 | } |
280 | | |
281 | | /** If trying to create unix sockets in non-existing directories then |
282 | | * fail. |
283 | | */ |
284 | | void netsnmp_unix_dont_create_path(void) |
285 | 0 | { |
286 | 0 | create_path = 0; |
287 | 0 | } |
288 | | #endif /* NETSNMP_FEATURE_REMOVE_UNIX_SOCKET_PATHS */ |
289 | | |
290 | | /* |
291 | | * Open a Unix-domain transport for SNMP. Local is TRUE if addr is the local |
292 | | * address to bind to (i.e. this is a server-type session); otherwise addr is |
293 | | * the remote address to send things to (and we make up a temporary name for |
294 | | * the local end of the connection). |
295 | | */ |
296 | | |
297 | | netsnmp_transport * |
298 | | netsnmp_unix_transport(const struct sockaddr_un *addr, int local) |
299 | 0 | { |
300 | 0 | netsnmp_transport *t = NULL; |
301 | 0 | sockaddr_un_pair *sup = NULL; |
302 | 0 | int rc = 0; |
303 | 0 | int socket_initialized = 0; |
304 | |
|
305 | | #ifdef NETSNMP_NO_LISTEN_SUPPORT |
306 | | /* SPECIAL CIRCUMSTANCE: We still want AgentX to be able to operate, |
307 | | so we allow for unix domain sockets to still listen when everything |
308 | | else isn't allowed to. Thus, we ignore this define in this file. |
309 | | */ |
310 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
311 | |
|
312 | 0 | if (addr == NULL || addr->sun_family != AF_UNIX) { |
313 | 0 | return NULL; |
314 | 0 | } |
315 | | |
316 | 0 | t = SNMP_MALLOC_TYPEDEF(netsnmp_transport); |
317 | 0 | if (t == NULL) { |
318 | 0 | return NULL; |
319 | 0 | } |
320 | | |
321 | 0 | DEBUGIF("netsnmp_unix") { |
322 | 0 | char *str = netsnmp_unix_fmtaddr(NULL, addr, |
323 | 0 | sizeof(struct sockaddr_un)); |
324 | 0 | DEBUGMSGTL(("netsnmp_unix", "open %s %s\n", local ? "local" : "remote", |
325 | 0 | str)); |
326 | 0 | free(str); |
327 | 0 | } |
328 | |
|
329 | 0 | t->domain = netsnmp_UnixDomain; |
330 | 0 | t->domain_length = |
331 | 0 | sizeof(netsnmp_UnixDomain) / sizeof(netsnmp_UnixDomain[0]); |
332 | |
|
333 | 0 | t->data = malloc(sizeof(sockaddr_un_pair)); |
334 | 0 | if (t->data == NULL) { |
335 | 0 | netsnmp_transport_free(t); |
336 | 0 | return NULL; |
337 | 0 | } |
338 | 0 | memset(t->data, 0, sizeof(sockaddr_un_pair)); |
339 | 0 | t->data_length = sizeof(sockaddr_un_pair); |
340 | 0 | sup = (sockaddr_un_pair *) t->data; |
341 | |
|
342 | 0 | #ifndef NETSNMP_NO_SYSTEMD |
343 | | /* |
344 | | * Maybe the socket was already provided by systemd... |
345 | | */ |
346 | 0 | if (local) { |
347 | 0 | t->sock = netsnmp_sd_find_unix_socket(SOCK_STREAM, 1, addr->sun_path); |
348 | 0 | if (t->sock >= 0) |
349 | 0 | socket_initialized = 1; |
350 | 0 | } |
351 | 0 | #endif |
352 | 0 | if (!socket_initialized) |
353 | 0 | t->sock = socket(PF_UNIX, SOCK_STREAM, 0); |
354 | 0 | if (t->sock < 0) { |
355 | 0 | netsnmp_transport_free(t); |
356 | 0 | return NULL; |
357 | 0 | } |
358 | | |
359 | 0 | t->flags = NETSNMP_TRANSPORT_FLAG_STREAM; |
360 | |
|
361 | 0 | if (local) { |
362 | 0 | t->local_length = strlen(addr->sun_path); |
363 | 0 | t->local = strdup(addr->sun_path); |
364 | 0 | if (t->local == NULL) { |
365 | 0 | netsnmp_unix_close(t); |
366 | 0 | netsnmp_transport_free(t); |
367 | 0 | return NULL; |
368 | 0 | } |
369 | | |
370 | | /* |
371 | | * This session is intended as a server, so we must bind to the given |
372 | | * path (unlinking it first, to avoid errors). |
373 | | */ |
374 | | |
375 | 0 | t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN; |
376 | |
|
377 | 0 | if (!socket_initialized) { |
378 | 0 | unlink(addr->sun_path); |
379 | 0 | rc = bind(t->sock, (const struct sockaddr *)addr, SUN_LEN(addr)); |
380 | 0 | if (rc != 0 && errno == ENOENT && create_path) { |
381 | 0 | rc = mkdirhier(addr->sun_path, create_mode, 1); |
382 | 0 | if (rc != 0) { |
383 | 0 | netsnmp_unix_close(t); |
384 | 0 | netsnmp_transport_free(t); |
385 | 0 | return NULL; |
386 | 0 | } |
387 | 0 | rc = bind(t->sock, (const struct sockaddr *)addr, |
388 | 0 | SUN_LEN(addr)); |
389 | 0 | } |
390 | 0 | if (rc != 0) { |
391 | 0 | DEBUGMSGTL(("netsnmp_unix_transport", |
392 | 0 | "couldn't bind \"%s\", errno %d (%s)\n", |
393 | 0 | addr->sun_path, errno, strerror(errno))); |
394 | 0 | netsnmp_unix_close(t); |
395 | 0 | netsnmp_transport_free(t); |
396 | 0 | return NULL; |
397 | 0 | } |
398 | 0 | } |
399 | | |
400 | | /* |
401 | | * Save the address in the transport-specific data pointer for later |
402 | | * use by netsnmp_unix_close. |
403 | | */ |
404 | | |
405 | 0 | sup->server.sun_family = AF_UNIX; |
406 | 0 | strcpy(sup->server.sun_path, addr->sun_path); |
407 | 0 | sup->local = 1; |
408 | | |
409 | | /* |
410 | | * Now sit here and listen for connections to arrive. |
411 | | */ |
412 | |
|
413 | 0 | if (!socket_initialized) { |
414 | 0 | rc = listen(t->sock, NETSNMP_STREAM_QUEUE_LEN); |
415 | 0 | if (rc != 0) { |
416 | 0 | DEBUGMSGTL(("netsnmp_unix_transport", |
417 | 0 | "couldn't listen to \"%s\", errno %d (%s)\n", |
418 | 0 | addr->sun_path, errno, strerror(errno))); |
419 | 0 | netsnmp_unix_close(t); |
420 | 0 | netsnmp_transport_free(t); |
421 | 0 | return NULL; |
422 | 0 | } |
423 | 0 | } |
424 | 0 | } else { |
425 | 0 | t->remote_length = strlen(addr->sun_path); |
426 | 0 | t->remote = strdup(addr->sun_path); |
427 | 0 | if (t->remote == NULL) { |
428 | 0 | netsnmp_transport_free(t); |
429 | 0 | return NULL; |
430 | 0 | } |
431 | | |
432 | 0 | rc = connect(t->sock, (const struct sockaddr *)addr, |
433 | 0 | sizeof(struct sockaddr_un)); |
434 | 0 | if (rc != 0) { |
435 | 0 | DEBUGMSGTL(("netsnmp_unix_transport", |
436 | 0 | "couldn't connect to \"%s\", errno %d (%s)\n", |
437 | 0 | addr->sun_path, errno, strerror(errno))); |
438 | 0 | netsnmp_unix_close(t); |
439 | 0 | netsnmp_transport_free(t); |
440 | 0 | return NULL; |
441 | 0 | } |
442 | | |
443 | | /* |
444 | | * Save the remote address in the transport-specific data pointer for |
445 | | * later use by netsnmp_unix_send. |
446 | | */ |
447 | | |
448 | 0 | sup->server.sun_family = AF_UNIX; |
449 | 0 | strcpy(sup->server.sun_path, addr->sun_path); |
450 | 0 | sup->local = 0; |
451 | 0 | netsnmp_sock_buffer_set(t->sock, SO_SNDBUF, local, 0); |
452 | 0 | netsnmp_sock_buffer_set(t->sock, SO_RCVBUF, local, 0); |
453 | 0 | } |
454 | | |
455 | | /* |
456 | | * Message size is not limited by this transport (hence msgMaxSize |
457 | | * is equal to the maximum legal size of an SNMP message). |
458 | | */ |
459 | | |
460 | 0 | t->msgMaxSize = SNMP_MAX_PACKET_LEN; |
461 | 0 | t->f_recv = netsnmp_unix_recv; |
462 | 0 | t->f_send = netsnmp_unix_send; |
463 | 0 | t->f_close = netsnmp_unix_close; |
464 | 0 | t->f_accept = netsnmp_unix_accept; |
465 | 0 | t->f_fmtaddr = netsnmp_unix_fmtaddr; |
466 | 0 | t->f_get_taddr = netsnmp_unix_get_taddr; |
467 | |
|
468 | 0 | return t; |
469 | 0 | } |
470 | | |
471 | | netsnmp_transport * |
472 | | netsnmp_unix_create_tstring(const char *string, int local, |
473 | | const char *default_target) |
474 | 0 | { |
475 | 0 | struct sockaddr_un addr; |
476 | |
|
477 | 0 | if (string && *string != '\0') { |
478 | 0 | } else if (default_target && *default_target != '\0') { |
479 | 0 | string = default_target; |
480 | 0 | } |
481 | |
|
482 | 0 | if ((string != NULL && *string != '\0') && |
483 | 0 | (strlen(string) < sizeof(addr.sun_path))) { |
484 | 0 | addr.sun_family = AF_UNIX; |
485 | 0 | memset(addr.sun_path, 0, sizeof(addr.sun_path)); |
486 | 0 | strlcpy(addr.sun_path, string, sizeof(addr.sun_path)); |
487 | 0 | return netsnmp_unix_transport(&addr, local); |
488 | 0 | } else { |
489 | 0 | if (string != NULL && *string != '\0') { |
490 | 0 | snmp_log(LOG_ERR, "Path too long for Unix domain transport\n"); |
491 | 0 | } |
492 | 0 | return NULL; |
493 | 0 | } |
494 | 0 | } |
495 | | |
496 | | |
497 | | |
498 | | netsnmp_transport * |
499 | | netsnmp_unix_create_ostring(const void *ostring, size_t o_len, int local) |
500 | 0 | { |
501 | 0 | struct sockaddr_un addr; |
502 | |
|
503 | 0 | if (o_len > 0 && o_len < (sizeof(addr.sun_path) - 1)) { |
504 | 0 | addr.sun_family = AF_UNIX; |
505 | 0 | memset(addr.sun_path, 0, sizeof(addr.sun_path)); |
506 | 0 | strlcpy(addr.sun_path, ostring, sizeof(addr.sun_path)); |
507 | 0 | return netsnmp_unix_transport(&addr, local); |
508 | 0 | } else { |
509 | 0 | if (o_len > 0) { |
510 | 0 | snmp_log(LOG_ERR, "Path too long for Unix domain transport\n"); |
511 | 0 | } |
512 | 0 | } |
513 | 0 | return NULL; |
514 | 0 | } |
515 | | |
516 | | |
517 | | |
518 | | void |
519 | | netsnmp_unix_ctor(void) |
520 | 3.98k | { |
521 | 3.98k | unixDomain.name = netsnmp_UnixDomain; |
522 | 3.98k | unixDomain.name_length = OID_LENGTH(netsnmp_UnixDomain); |
523 | 3.98k | unixDomain.prefix = calloc(2, sizeof(char *)); |
524 | 3.98k | if (!unixDomain.prefix) { |
525 | 0 | snmp_log(LOG_ERR, "calloc() failed - out of memory\n"); |
526 | 0 | return; |
527 | 0 | } |
528 | 3.98k | unixDomain.prefix[0] = "unix"; |
529 | | |
530 | 3.98k | unixDomain.f_create_from_tstring_new = netsnmp_unix_create_tstring; |
531 | 3.98k | unixDomain.f_create_from_ostring = netsnmp_unix_create_ostring; |
532 | | |
533 | 3.98k | netsnmp_tdomain_register(&unixDomain); |
534 | 3.98k | } |
535 | | |
536 | | #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) |
537 | | /* support for SNMPv1 and SNMPv2c on unix domain*/ |
538 | | |
539 | 153 | #define EXAMPLE_COMMUNITY "COMMUNITY" |
540 | | typedef struct com2SecUnixEntry_s { |
541 | | const char* sockpath; |
542 | | const char* secName; |
543 | | const char* contextName; |
544 | | struct com2SecUnixEntry_s *next; |
545 | | unsigned short pathlen; |
546 | | const char community[1]; |
547 | | } com2SecUnixEntry; |
548 | | |
549 | | static com2SecUnixEntry *com2SecUnixList = NULL, *com2SecUnixListLast = NULL; |
550 | | |
551 | | |
552 | | int |
553 | | netsnmp_unix_getSecName(void *opaque, int olength, |
554 | | const char *community, |
555 | | size_t community_len, |
556 | | const char **secName, const char **contextName) |
557 | 0 | { |
558 | 0 | const com2SecUnixEntry *c; |
559 | 0 | struct sockaddr_un *to = (struct sockaddr_un *) opaque; |
560 | 0 | char *ztcommunity = NULL; |
561 | |
|
562 | 0 | if (secName != NULL) { |
563 | 0 | *secName = NULL; /* Haven't found anything yet */ |
564 | 0 | } |
565 | | |
566 | | /* |
567 | | * Special case if there are NO entries (as opposed to no MATCHING |
568 | | * entries). |
569 | | */ |
570 | |
|
571 | 0 | if (com2SecUnixList == NULL) { |
572 | 0 | DEBUGMSGTL(("netsnmp_unix_getSecName", "no com2sec entries\n")); |
573 | 0 | return 0; |
574 | 0 | } |
575 | | |
576 | | /* |
577 | | * If there is no unix socket path, then there can be no valid security |
578 | | * name. |
579 | | */ |
580 | | |
581 | 0 | if (opaque == NULL || olength != sizeof(struct sockaddr_un) || |
582 | 0 | to->sun_family != AF_UNIX) { |
583 | 0 | DEBUGMSGTL(("netsnmp_unix_getSecName", |
584 | 0 | "no unix destine address in PDU?\n")); |
585 | 0 | return 1; |
586 | 0 | } |
587 | | |
588 | 0 | DEBUGIF("netsnmp_unix_getSecName") { |
589 | 0 | ztcommunity = (char *)malloc(community_len + 1); |
590 | 0 | if (ztcommunity != NULL) { |
591 | 0 | memcpy(ztcommunity, community, community_len); |
592 | 0 | ztcommunity[community_len] = '\0'; |
593 | 0 | } |
594 | |
|
595 | 0 | DEBUGMSGTL(("netsnmp_unix_getSecName", "resolve <\"%s\">\n", |
596 | 0 | ztcommunity ? ztcommunity : "<malloc error>")); |
597 | 0 | } |
598 | |
|
599 | 0 | for (c = com2SecUnixList; c != NULL; c = c->next) { |
600 | 0 | DEBUGMSGTL(("netsnmp_unix_getSecName","compare <\"%s\",to socket %s>", |
601 | 0 | c->community, c->sockpath )); |
602 | 0 | if ((community_len == strlen(c->community)) && |
603 | 0 | (memcmp(community, c->community, community_len) == 0) && |
604 | | /* compare sockpath, if pathlen == 0, always match */ |
605 | 0 | (strlen(to->sun_path) == c->pathlen || c->pathlen == 0) && |
606 | 0 | (memcmp(to->sun_path, c->sockpath, c->pathlen) == 0) |
607 | 0 | ) { |
608 | 0 | DEBUGMSG(("netsnmp_unix_getSecName", "... SUCCESS\n")); |
609 | 0 | if (secName != NULL) { |
610 | 0 | *secName = c->secName; |
611 | 0 | *contextName = c->contextName; |
612 | 0 | } |
613 | 0 | break; |
614 | 0 | } |
615 | 0 | DEBUGMSG(("netsnmp_unix_getSecName", "... nope\n")); |
616 | 0 | } |
617 | 0 | if (ztcommunity != NULL) { |
618 | 0 | free(ztcommunity); |
619 | 0 | } |
620 | 0 | return 1; |
621 | 0 | } |
622 | | |
623 | | void |
624 | | netsnmp_unix_parse_security(const char *token, char *param) |
625 | 210 | { |
626 | 210 | char secName[VACMSTRINGLEN + 1]; |
627 | 210 | size_t secNameLen; |
628 | 210 | char contextName[VACMSTRINGLEN + 1]; |
629 | 210 | size_t contextNameLen; |
630 | 210 | char community[COMMUNITY_MAX_LEN + 1]; |
631 | 210 | size_t communityLen; |
632 | 210 | char sockpath[sizeof(((struct sockaddr_un*)0)->sun_path) + 1]; |
633 | 210 | size_t sockpathLen; |
634 | | |
635 | 210 | param = copy_nword( param, secName, sizeof(secName)); |
636 | 210 | if (strcmp(secName, "-Cn") == 0) { |
637 | 11 | if (!param) { |
638 | 1 | config_perror("missing CONTEXT_NAME parameter"); |
639 | 1 | return; |
640 | 1 | } |
641 | 10 | param = copy_nword( param, contextName, sizeof(contextName)); |
642 | 10 | contextNameLen = strlen(contextName) + 1; |
643 | 10 | if (contextNameLen > VACMSTRINGLEN) { |
644 | 1 | config_perror("context name too long"); |
645 | 1 | return; |
646 | 1 | } |
647 | 9 | if (!param) { |
648 | 1 | config_perror("missing NAME parameter"); |
649 | 1 | return; |
650 | 1 | } |
651 | 8 | param = copy_nword( param, secName, sizeof(secName)); |
652 | 199 | } else { |
653 | 199 | contextNameLen = 0; |
654 | 199 | } |
655 | | |
656 | 207 | secNameLen = strlen(secName) + 1; |
657 | 207 | if (secNameLen == 1) { |
658 | 34 | config_perror("empty NAME parameter"); |
659 | 34 | return; |
660 | 173 | } else if (secNameLen > VACMSTRINGLEN) { |
661 | 15 | config_perror("security name too long"); |
662 | 15 | return; |
663 | 15 | } |
664 | | |
665 | 158 | if (!param) { |
666 | 33 | config_perror("missing SOCKPATH parameter"); |
667 | 33 | return; |
668 | 33 | } |
669 | 125 | param = copy_nword( param, sockpath, sizeof(sockpath)); |
670 | 125 | if (sockpath[0] == '\0') { |
671 | 1 | config_perror("empty SOCKPATH parameter"); |
672 | 1 | return; |
673 | 1 | } |
674 | 124 | sockpathLen = strlen(sockpath) + 1; |
675 | 124 | if (sockpathLen > sizeof(((struct sockaddr_un*)0)->sun_path)) { |
676 | 0 | config_perror("sockpath too long"); |
677 | 0 | return; |
678 | 0 | } |
679 | | |
680 | 124 | if (!param) { |
681 | 22 | config_perror("missing COMMUNITY parameter"); |
682 | 22 | return; |
683 | 22 | } |
684 | 102 | param = copy_nword( param, community, sizeof(community)); |
685 | 102 | if (community[0] == '\0') { |
686 | 1 | config_perror("empty COMMUNITY parameter"); |
687 | 1 | return; |
688 | 1 | } |
689 | 101 | communityLen = strlen(community) + 1; |
690 | 101 | if (communityLen >= COMMUNITY_MAX_LEN) { |
691 | 0 | config_perror("community name too long"); |
692 | 0 | return; |
693 | 0 | } |
694 | 101 | if (communityLen == sizeof(EXAMPLE_COMMUNITY) && |
695 | 26 | memcmp(community, EXAMPLE_COMMUNITY, sizeof(EXAMPLE_COMMUNITY)) == 0) { |
696 | 1 | config_perror("example config COMMUNITY not properly configured"); |
697 | 1 | return; |
698 | 1 | } |
699 | | |
700 | | /* Deal with the "default" case */ |
701 | 100 | if(strcmp(sockpath, "default") == 0) { |
702 | 1 | sockpathLen = 0; |
703 | 1 | } |
704 | | |
705 | 100 | { |
706 | 100 | char *last; |
707 | 100 | com2SecUnixEntry* e = malloc(offsetof(com2SecUnixEntry, community) + |
708 | 100 | communityLen + sockpathLen + secNameLen + |
709 | 100 | contextNameLen); |
710 | 100 | if (!e) { |
711 | 0 | config_perror("memory allocation failed"); |
712 | 0 | return; |
713 | 0 | } |
714 | | |
715 | 100 | last = (char *)e + offsetof(com2SecUnixEntry, community); |
716 | | |
717 | 100 | DEBUGMSGTL(("netsnmp_unix_parse_security", |
718 | 100 | "<\"%s\", \"%.*s\"> => \"%s\"\n", |
719 | 100 | community, (int)sockpathLen, sockpath, secName)); |
720 | | |
721 | 100 | memcpy(last, community, communityLen); |
722 | 100 | last += communityLen; |
723 | | |
724 | 100 | if (sockpathLen) { |
725 | 99 | e->sockpath = last; |
726 | 99 | memcpy(last, sockpath, sockpathLen); |
727 | 99 | last += sockpathLen; |
728 | 99 | e->pathlen = sockpathLen - 1; |
729 | 99 | } else { |
730 | 1 | e->sockpath = last - 1; |
731 | 1 | e->pathlen = 0; |
732 | 1 | } |
733 | | |
734 | 100 | e->secName = last; |
735 | 100 | memcpy(last, secName, secNameLen); |
736 | 100 | last += secNameLen; |
737 | | |
738 | 100 | if (contextNameLen) { |
739 | 8 | e->contextName = last; |
740 | 8 | memcpy(last, contextName, contextNameLen); |
741 | 8 | last += contextNameLen; |
742 | 8 | } else |
743 | 92 | e->contextName = last - 1; |
744 | | |
745 | 100 | e->next = NULL; |
746 | | |
747 | 100 | if (com2SecUnixListLast != NULL) { |
748 | 99 | com2SecUnixListLast->next = e; |
749 | 99 | com2SecUnixListLast = e; |
750 | 99 | } else { |
751 | 1 | com2SecUnixListLast = com2SecUnixList = e; |
752 | 1 | } |
753 | 100 | } |
754 | 100 | } |
755 | | |
756 | | void |
757 | | netsnmp_unix_com2SecList_free(void) |
758 | 6.48k | { |
759 | 6.48k | com2SecUnixEntry *e = com2SecUnixList; |
760 | 6.48k | while (e != NULL) { |
761 | 0 | com2SecUnixEntry *tmp = e; |
762 | 0 | e = e->next; |
763 | 0 | free(tmp); |
764 | 0 | } |
765 | 6.48k | com2SecUnixList = com2SecUnixListLast = NULL; |
766 | 6.48k | } |
767 | | #endif /* support for community based SNMP */ |
768 | | |
769 | | void |
770 | | netsnmp_unix_agent_config_tokens_register(void) |
771 | 3.24k | { |
772 | 3.24k | #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) |
773 | 3.24k | register_app_config_handler("com2secunix", netsnmp_unix_parse_security, |
774 | 3.24k | netsnmp_unix_com2SecList_free, |
775 | 3.24k | "[-Cn CONTEXT] secName sockpath community"); |
776 | 3.24k | #endif /* support for community based SNMP */ |
777 | 3.24k | } |