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