1 /* Portions of this file are subject to the following copyright(s).  See
2  * the Net-SNMP's COPYING file for more details and other copyrights
3  * that may apply:
4  */
5 /*
6  * Portions of this file are copyrighted by:
7  * Copyright Copyright 2003 Sun Microsystems, Inc. All rights reserved.
8  * Use is subject to license terms specified in the COPYING file
9  * distributed with the Net-SNMP package.
10  *
11  * Portions of this file are copyrighted by:
12  * Copyright (c) 2016 VMware, Inc. All rights reserved.
13  * Use is subject to license terms specified in the COPYING file
14  * distributed with the Net-SNMP package.
15  */
16 
17 #include <net-snmp/net-snmp-config.h>
18 
19 #include <net-snmp/types.h>
20 #include <net-snmp/library/snmpIPBaseDomain.h>
21 #include <net-snmp/library/snmpUDPDomain.h>
22 #include <net-snmp/library/snmpUDPIPv4BaseDomain.h>
23 
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <ctype.h>
28 #include <errno.h>
29 
30 #if HAVE_STRING_H
31 #include <string.h>
32 #else
33 #include <strings.h>
34 #endif
35 #if HAVE_STDLIB_H
36 #include <stdlib.h>
37 #endif
38 #if HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #if HAVE_SYS_SOCKET_H
42 #include <sys/socket.h>
43 #endif
44 #if HAVE_NETINET_IN_H
45 #include <netinet/in.h>
46 #endif
47 #if HAVE_ARPA_INET_H
48 #include <arpa/inet.h>
49 #endif
50 #if HAVE_NETDB_H
51 #include <netdb.h>
52 #endif
53 #if HAVE_SYS_UIO_H
54 #include <sys/uio.h>
55 #endif
56 
57 #include <net-snmp/types.h>
58 #include <net-snmp/output_api.h>
59 #include <net-snmp/config_api.h>
60 
61 #include <net-snmp/library/snmp_transport.h>
62 #include <net-snmp/library/snmpSocketBaseDomain.h>
63 #include <net-snmp/library/system.h>
64 #include <net-snmp/library/tools.h>
65 
66 #include "inet_ntop.h"
67 #include "inet_pton.h"
68 
69 #ifndef INADDR_NONE
70 #define INADDR_NONE	-1
71 #endif
72 
73 #ifndef INET_ADDRSTRLEN
74 #define INET_ADDRSTRLEN 16
75 #endif
76 
77 static netsnmp_tdomain udpDomain;
78 
79 /*
80  * needs to be in sync with the definitions in snmplib/snmpTCPDomain.c
81  * and perl/agent/agent.xs
82  */
83 typedef netsnmp_indexed_addr_pair netsnmp_udp_addr_pair;
84 
85 int
86 netsnmp_sockaddr_in2(struct sockaddr_in *addr,
87                      const char *inpeername, const char *default_target);
88 
89 /*
90  * Return a string representing the address in data, or else the "far end"
91  * address if data is NULL.
92  */
93 
94 char *
netsnmp_udp_fmtaddr(netsnmp_transport * t,const void * data,int len)95 netsnmp_udp_fmtaddr(netsnmp_transport *t, const void *data, int len)
96 {
97     return netsnmp_ipv4_fmtaddr("UDP", t, data, len);
98 }
99 
100 
101 #if defined(HAVE_IP_PKTINFO) || (defined(HAVE_IP_RECVDSTADDR) && defined(HAVE_IP_SENDSRCADDR))
102 
netsnmp_udp_recvfrom(int s,void * buf,int len,struct sockaddr * from,socklen_t * fromlen,struct sockaddr * dstip,socklen_t * dstlen,int * if_index)103 int netsnmp_udp_recvfrom(int s, void *buf, int len, struct sockaddr *from, socklen_t *fromlen, struct sockaddr *dstip, socklen_t *dstlen, int *if_index)
104 {
105     /** udpipv4 just calls udpbase. should we skip directly to there? */
106     return netsnmp_udpipv4_recvfrom(s, buf, len, from, fromlen, dstip, dstlen,
107                                     if_index);
108 }
109 
netsnmp_udp_sendto(int fd,const struct in_addr * srcip,int if_index,const struct sockaddr * remote,const void * data,int len)110 int netsnmp_udp_sendto(int fd, const struct in_addr *srcip, int if_index,
111                        const struct sockaddr *remote, const void *data, int len)
112 {
113     /** udpipv4 just calls udpbase. should we skip directly to there? */
114     return netsnmp_udpipv4_sendto(fd, srcip, if_index, remote, data, len);
115 }
116 #endif /* HAVE_IP_PKTINFO || HAVE_IP_RECVDSTADDR */
117 
118 /*
119  * Common initialization of udp transport.
120  */
121 
122 static netsnmp_transport *
netsnmp_udp_transport_base(netsnmp_transport * t)123 netsnmp_udp_transport_base(netsnmp_transport *t)
124 {
125     if (NULL == t) {
126         return NULL;
127     }
128 
129     /*
130      * Set Domain
131      */
132 
133     t->domain = netsnmpUDPDomain;
134     t->domain_length = netsnmpUDPDomain_len;
135 
136     /*
137      * 16-bit length field, 8 byte UDP header, 20 byte IPv4 header
138      */
139 
140     t->msgMaxSize = 0xffff - 8 - 20;
141     t->f_recv     = netsnmp_udpbase_recv;
142     t->f_send     = netsnmp_udpbase_send;
143     t->f_close    = netsnmp_socketbase_close;
144     t->f_accept   = NULL;
145     t->f_fmtaddr  = netsnmp_udp_fmtaddr;
146     t->f_get_taddr = netsnmp_ipv4_get_taddr;
147 
148     return t;
149 }
150 
151 /*
152  * Open a UDP-based transport for SNMP.  Local is TRUE if addr is the local
153  * address to bind to (i.e. this is a server-type session); otherwise addr is
154  * the remote address to send things to.
155  */
156 netsnmp_transport *
netsnmp_udp_transport(const struct netsnmp_ep * ep,int local)157 netsnmp_udp_transport(const struct netsnmp_ep *ep, int local)
158 {
159     netsnmp_transport *t = NULL;
160 
161     t = netsnmp_udpipv4base_transport(ep, local);
162     if (NULL != t) {
163         netsnmp_udp_transport_base(t);
164     }
165     return t;
166 }
167 
168 /*
169  * Open a UDP-based transport for SNMP.  Local is TRUE if addr is the local
170  * address to bind to (i.e. this is a server-type session); otherwise addr is
171  * the remote address to send things to and src_addr is the optional addr
172  * to send from.
173  */
174 netsnmp_transport *
netsnmp_udp_transport_with_source(const struct netsnmp_ep * ep,int local,const struct netsnmp_ep * src_addr)175 netsnmp_udp_transport_with_source(const struct netsnmp_ep *ep, int local,
176                                   const struct netsnmp_ep *src_addr)
177 
178 {
179     netsnmp_transport *t = NULL;
180 
181     t = netsnmp_udpipv4base_transport_with_source(ep, local, src_addr);
182     if (NULL != t) {
183         netsnmp_udp_transport_base(t);
184     }
185     return t;
186 }
187 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
188 /*
189  * The following functions provide the "com2sec" configuration token
190  * functionality for compatibility.
191  */
192 
193 #define EXAMPLE_NETWORK		"NETWORK"
194 #define EXAMPLE_COMMUNITY	"COMMUNITY"
195 
196 struct com2SecEntry_s {
197     const char *secName;
198     const char *contextName;
199     struct com2SecEntry_s *next;
200     in_addr_t   network;
201     in_addr_t   mask;
202     int         negate;
203     const char  community[1];
204 };
205 
206 static com2SecEntry   *com2SecList = NULL, *com2SecListLast = NULL;
207 
208 int
netsnmp_udp_com2SecEntry_create(com2SecEntry ** entryp,const char * community,const char * secName,const char * contextName,struct in_addr * network,struct in_addr * mask,int negate)209 netsnmp_udp_com2SecEntry_create(com2SecEntry **entryp, const char *community,
210                     const char *secName, const char *contextName,
211                     struct in_addr *network, struct in_addr *mask,
212                     int negate)
213 {
214     int communityLen, secNameLen, contextNameLen, len;
215     com2SecEntry* e;
216     char* last;
217     struct in_addr dflt_network, dflt_mask;
218 
219     if (NULL != entryp)
220         *entryp = NULL;
221 
222     if (NULL == community || NULL == secName)
223         return C2SE_ERR_MISSING_ARG;
224 
225     if (NULL == network) {
226         network = &dflt_network;
227         dflt_network.s_addr = 0;
228     }
229     if (NULL == mask) {
230         mask = &dflt_mask;
231         dflt_mask.s_addr = 0;
232     }
233 
234     /** Check that the network and mask are consistent. */
235     if (network->s_addr & ~mask->s_addr)
236         return C2SE_ERR_MASK_MISMATCH;
237 
238     communityLen = strlen(community);
239     if (communityLen > COMMUNITY_MAX_LEN)
240         return C2SE_ERR_COMMUNITY_TOO_LONG;
241 
242     secNameLen = strlen(secName);
243     if (secNameLen > VACM_MAX_STRING)
244         return C2SE_ERR_SECNAME_TOO_LONG;
245 
246     contextNameLen = contextName ? strlen(contextName) : 0;
247     if (contextNameLen > VACM_MAX_STRING)
248         return C2SE_ERR_CONTEXT_TOO_LONG;
249 
250     /** alloc space for struct + 3 strings with NULLs */
251     len = offsetof(com2SecEntry, community) + communityLen + secNameLen +
252         contextNameLen + 3;
253     e = (com2SecEntry*)calloc(len, 1);
254     if (e == NULL)
255         return C2SE_ERR_MEMORY;
256     last = ((char*)e) + offsetof(com2SecEntry, community);
257 
258     DEBUGIF("netsnmp_udp_parse_security") {
259         char buf1[INET_ADDRSTRLEN];
260         char buf2[INET_ADDRSTRLEN];
261         DEBUGMSGTL(("netsnmp_udp_parse_security",
262                     "<\"%s\", %s/%s> => \"%s\"\n", community,
263                     inet_ntop(AF_INET, network, buf1, sizeof(buf1)),
264                     inet_ntop(AF_INET, mask, buf2, sizeof(buf2)),
265                     secName));
266     }
267 
268     memcpy(last, community, communityLen);
269     last += communityLen + 1;
270     memcpy(last, secName, secNameLen);
271     e->secName = last;
272     last += secNameLen + 1;
273     if (contextNameLen) {
274         memcpy(last, contextName, contextNameLen);
275         e->contextName = last;
276     } else
277         e->contextName = last - 1;
278     e->network = network->s_addr;
279     e->mask = mask->s_addr;
280     e->negate = negate;
281     e->next = NULL;
282 
283     if (com2SecListLast != NULL) {
284         com2SecListLast->next = e;
285         com2SecListLast = e;
286     } else {
287         com2SecListLast = com2SecList = e;
288     }
289 
290     if (NULL != entryp)
291         *entryp = e;
292 
293     return C2SE_ERR_SUCCESS;
294 }
295 
296 void
netsnmp_udp_parse_security(const char * token,char * param)297 netsnmp_udp_parse_security(const char *token, char *param)
298 {
299     /** copy_nword does null term, so we need vars of max size + 2. */
300     /** (one for null, one to detect param too long */
301     char            secName[VACMSTRINGLEN]; /* == VACM_MAX_STRING + 2 */
302     char            contextName[VACMSTRINGLEN];
303     char            community[COMMUNITY_MAX_LEN + 2];
304     char            source[271]; /* !(1)+dns-name(253)+/(1)+mask(15)+\0(1) */
305     char            *sourcep;
306     struct in_addr  network, mask;
307     int             negate;
308     int rc;
309 
310     /*
311      * Get security, source address/netmask and community strings.
312      */
313 
314     param = copy_nword( param, secName, sizeof(secName));
315     if (strcmp(secName, "-Cn") == 0) {
316         if (!param) {
317             config_perror("missing CONTEXT_NAME parameter");
318             return;
319         }
320         param = copy_nword( param, contextName, sizeof(contextName));
321         if (!param) {
322             config_perror("missing NAME parameter");
323             return;
324         }
325         param = copy_nword( param, secName, sizeof(secName));
326     } else
327         contextName[0] = '\0';
328 
329     if (secName[0] == '\0') {
330         config_perror("empty NAME parameter");
331         return;
332     }
333 
334     if (!param) {
335         config_perror("missing SOURCE parameter");
336         return;
337     }
338     param = copy_nword( param, source, sizeof(source));
339     if (source[0] == '\0') {
340         config_perror("empty SOURCE parameter");
341         return;
342     }
343     if (strncmp(source, EXAMPLE_NETWORK, strlen(EXAMPLE_NETWORK)) == 0) {
344         config_perror("example config NETWORK not properly configured");
345         return;
346     }
347 
348     if (!param) {
349         config_perror("missing COMMUNITY parameter");
350         return;
351     }
352     param = copy_nword( param, community, sizeof(community));
353     if (community[0] == '\0') {
354         config_perror("empty COMMUNITY parameter");
355         return;
356     }
357     if ((strlen(community) + 1) == sizeof(EXAMPLE_COMMUNITY) &&
358         memcmp(community, EXAMPLE_COMMUNITY, sizeof(EXAMPLE_COMMUNITY)) == 0) {
359         config_perror("example config COMMUNITY not properly configured");
360         return;
361     }
362 
363     /* Deal with the "default" case first. */
364     if (strcmp(source, "default") == 0) {
365         network.s_addr = 0;
366         mask.s_addr = 0;
367         negate = 0;
368     } else {
369         char *strmask;
370         if (*source == '!') {
371             negate = 1;
372             sourcep = source + 1;
373         } else {
374             negate = 0;
375             sourcep = source;
376         }
377 
378         /* Split the source/netmask parts */
379         strmask = strchr(sourcep, '/');
380         if (strmask != NULL)
381             /* Mask given. */
382             *strmask++ = '\0';
383 
384         /* Try interpreting as a dotted quad. */
385         if (inet_pton(AF_INET, sourcep, &network) == 0) {
386             /* Nope, wasn't a dotted quad.  Must be a hostname. */
387             int ret = netsnmp_gethostbyname_v4(sourcep, &network.s_addr);
388             if (ret < 0) {
389                 config_perror("cannot resolve source hostname");
390                 return;
391             }
392         }
393 
394         /* Now work out the mask. */
395         if (strmask == NULL || *strmask == '\0') {
396             /* No mask was given. Assume /32 */
397             mask.s_addr = (in_addr_t)(~0UL);
398         } else {
399             /* Try to interpret mask as a "number of 1 bits". */
400             char* cp;
401             long maskLen = strtol(strmask, &cp, 10);
402             if (*cp == '\0') {
403                 if (0 < maskLen && maskLen <= 32)
404                     mask.s_addr = htonl((in_addr_t)(~0UL << (32 - maskLen)));
405                 else if (0 == maskLen)
406                     mask.s_addr = 0;
407                 else {
408                     config_perror("bad mask length");
409                     return;
410                 }
411             }
412             /* Try to interpret mask as a dotted quad. */
413             else if (inet_pton(AF_INET, strmask, &mask) == 0) {
414                 config_perror("bad mask");
415                 return;
416             }
417 
418             /* Check that the network and mask are consistent. */
419             if (network.s_addr & ~mask.s_addr) {
420                 config_perror("source/mask mismatch");
421                 return;
422             }
423         }
424     }
425 
426     /*
427      * Everything is okay.  Copy the parameters to the structure allocated
428      * above and add it to END of the list.
429      */
430     rc = netsnmp_udp_com2SecEntry_create(NULL, community, secName, contextName,
431                                          &network, &mask, negate);
432     switch(rc) {
433         case C2SE_ERR_SUCCESS:
434             break;
435         case C2SE_ERR_CONTEXT_TOO_LONG:
436             config_perror("context name too long");
437             break;
438         case C2SE_ERR_COMMUNITY_TOO_LONG:
439             config_perror("community name too long");
440             break;
441         case C2SE_ERR_SECNAME_TOO_LONG:
442             config_perror("security name too long");
443             break;
444         case C2SE_ERR_MASK_MISMATCH:
445             config_perror("source/mask mismatch");
446             break;
447         case C2SE_ERR_MISSING_ARG:
448         default:
449             config_perror("unexpected error; could not create com2SecEntry");
450     }
451 }
452 
453 void
netsnmp_udp_com2Sec_free(com2SecEntry * e)454 netsnmp_udp_com2Sec_free(com2SecEntry *e)
455 {
456     free(e);
457 }
458 
459 int
netsnmp_udp_com2SecList_remove(com2SecEntry * e)460 netsnmp_udp_com2SecList_remove(com2SecEntry *e)
461 {
462     com2SecEntry   *c = com2SecList, *p = NULL;
463     for (; c != NULL; p = c, c = c->next) {
464         if (e == c)
465             break;
466     }
467     if (NULL == c)
468         return 1;
469 
470     if (NULL == p)
471         com2SecList = e->next;
472     else
473         p->next = e->next;
474     e->next = NULL;
475 
476     if (e == com2SecListLast)
477         com2SecListLast = p;
478 
479     return 0;
480 }
481 
482 void
netsnmp_udp_com2SecList_free(void)483 netsnmp_udp_com2SecList_free(void)
484 {
485     com2SecEntry   *e = com2SecList;
486     while (e != NULL) {
487         com2SecEntry   *tmp = e;
488         e = e->next;
489         netsnmp_udp_com2Sec_free(tmp);
490     }
491     com2SecList = com2SecListLast = NULL;
492 }
493 #endif /* support for community based SNMP */
494 
495 void
netsnmp_udp_agent_config_tokens_register(void)496 netsnmp_udp_agent_config_tokens_register(void)
497 {
498 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
499     register_app_config_handler("com2sec", netsnmp_udp_parse_security,
500                                 netsnmp_udp_com2SecList_free,
501                                 "[-Cn CONTEXT] secName IPv4-network-address[/netmask] community");
502 #endif /* support for community based SNMP */
503 }
504 
505 
506 
507 /*
508  * Return 0 if there are no com2sec entries, or return 1 if there ARE com2sec
509  * entries.  On return, if a com2sec entry matched the passed parameters,
510  * then *secName points at the appropriate security name, or is NULL if the
511  * parameters did not match any com2sec entry.
512  */
513 
514 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
515 int
netsnmp_udp_getSecName(void * opaque,int olength,const char * community,size_t community_len,const char ** secName,const char ** contextName)516 netsnmp_udp_getSecName(void *opaque, int olength,
517                        const char *community,
518                        size_t community_len, const char **secName,
519                        const char **contextName)
520 {
521     const com2SecEntry *c;
522     netsnmp_udp_addr_pair *addr_pair = (netsnmp_udp_addr_pair *) opaque;
523     struct sockaddr_in *from = (struct sockaddr_in *) &(addr_pair->remote_addr);
524     char           *ztcommunity = NULL;
525 
526     if (secName != NULL) {
527         *secName = NULL;  /* Haven't found anything yet */
528     }
529 
530     /*
531      * Special case if there are NO entries (as opposed to no MATCHING
532      * entries).
533      */
534 
535     if (com2SecList == NULL) {
536         DEBUGMSGTL(("netsnmp_udp_getSecName", "no com2sec entries\n"));
537         return 0;
538     }
539 
540     /*
541      * If there is no IPv4 source address, then there can be no valid security
542      * name.
543      */
544 
545    DEBUGMSGTL(("netsnmp_udp_getSecName", "opaque = %p (len = %d), sizeof = %d, family = %d (%d)\n",
546    opaque, olength, (int)sizeof(netsnmp_udp_addr_pair), from->sin_family, AF_INET));
547     if (opaque == NULL || olength != sizeof(netsnmp_udp_addr_pair) ||
548         from->sin_family != AF_INET) {
549         DEBUGMSGTL(("netsnmp_udp_getSecName",
550 		    "no IPv4 source address in PDU?\n"));
551         return 1;
552     }
553 
554     DEBUGIF("netsnmp_udp_getSecName") {
555 	ztcommunity = (char *)malloc(community_len + 1);
556 	if (ztcommunity != NULL) {
557 	    memcpy(ztcommunity, community, community_len);
558 	    ztcommunity[community_len] = '\0';
559 	}
560 
561 	DEBUGMSGTL(("netsnmp_udp_getSecName", "resolve <\"%s\", 0x%08lx>\n",
562 		    ztcommunity ? ztcommunity : "<malloc error>",
563 		    (unsigned long)(from->sin_addr.s_addr)));
564     }
565 
566     for (c = com2SecList; c != NULL; c = c->next) {
567         {
568             char buf1[INET_ADDRSTRLEN];
569             char buf2[INET_ADDRSTRLEN];
570             DEBUGMSGTL(("netsnmp_udp_getSecName","compare <\"%s\", %s/%s>",
571                         c->community,
572                         inet_ntop(AF_INET, &c->network, buf1, sizeof(buf1)),
573                         inet_ntop(AF_INET, &c->mask, buf2, sizeof(buf2))));
574         }
575         if ((community_len == strlen(c->community)) &&
576 	    (memcmp(community, c->community, community_len) == 0) &&
577             ((from->sin_addr.s_addr & c->mask) == c->network)) {
578             DEBUGMSG(("netsnmp_udp_getSecName", "... SUCCESS\n"));
579             if (c->negate) {
580                 /*
581                  * If we matched a negative entry, then we are done - claim that we
582                  * matched nothing.
583                  */
584                 DEBUGMSG(("netsnmp_udp_getSecName", "... <negative entry>\n"));
585                 break;
586             }
587             if (secName != NULL) {
588                 *secName = c->secName;
589                 *contextName = c->contextName;
590             }
591             break;
592         }
593         DEBUGMSG(("netsnmp_udp_getSecName", "... nope\n"));
594     }
595     if (ztcommunity != NULL) {
596         free(ztcommunity);
597     }
598     return 1;
599 }
600 #endif /* support for community based SNMP */
601 
602 
603 netsnmp_transport *
netsnmp_udp_create_tstring(const char * str,int local,const char * default_target)604 netsnmp_udp_create_tstring(const char *str, int local,
605 			   const char *default_target)
606 {
607     struct netsnmp_ep addr;
608 
609     if (netsnmp_sockaddr_in3(&addr, str, default_target)) {
610         return netsnmp_udp_transport(&addr, local);
611     } else {
612         return NULL;
613     }
614 }
615 
616 netsnmp_transport *
netsnmp_udp_create_tspec(netsnmp_tdomain_spec * tspec)617 netsnmp_udp_create_tspec(netsnmp_tdomain_spec *tspec)
618 {
619     netsnmp_transport *t = netsnmp_udpipv4base_tspec_transport(tspec);
620     if (NULL != t) {
621         netsnmp_udp_transport_base(t);
622     }
623     return t;
624 
625 }
626 
627 netsnmp_transport *
netsnmp_udp_create_ostring(const void * o,size_t o_len,int local)628 netsnmp_udp_create_ostring(const void *o, size_t o_len, int local)
629 {
630     struct netsnmp_ep ep;
631 
632     memset(&ep, 0, sizeof(ep));
633     if (netsnmp_ipv4_ostring_to_sockaddr(&ep.a.sin, o, o_len))
634         return netsnmp_udp_transport(&ep, local);
635     return NULL;
636 }
637 
638 
639 void
netsnmp_udp_ctor(void)640 netsnmp_udp_ctor(void)
641 {
642     udpDomain.name = netsnmpUDPDomain;
643     udpDomain.name_length = netsnmpUDPDomain_len;
644     udpDomain.prefix = (const char**)calloc(2, sizeof(char *));
645     udpDomain.prefix[0] = "udp";
646 
647     udpDomain.f_create_from_tstring_new = netsnmp_udp_create_tstring;
648     udpDomain.f_create_from_tspec       = netsnmp_udp_create_tspec;
649     udpDomain.f_create_from_ostring     = netsnmp_udp_create_ostring;
650 
651     netsnmp_tdomain_register(&udpDomain);
652 }
653