1 /*
2  * snmpv3.c
3  *
4  * Portions of this file are subject to the following copyright(s).  See
5  * the Net-SNMP's COPYING file for more details and other copyrights
6  * that may apply:
7  *
8  * Portions of this file are copyrighted by:
9  * Copyright (c) 2016 VMware, Inc. All rights reserved.
10  * Use is subject to license terms specified in the COPYING file
11  * distributed with the Net-SNMP package.
12  */
13 
14 #include <net-snmp/net-snmp-config.h>
15 #include <errno.h>
16 #ifdef HAVE_INTTYPES_H
17 #include <inttypes.h>
18 #endif
19 #ifdef HAVE_LIMITS_H
20 #include <limits.h>
21 #endif
22 #include <stdio.h>
23 #include <sys/types.h>
24 
25 #if TIME_WITH_SYS_TIME
26 # include <sys/time.h>
27 # include <time.h>
28 #else
29 # if HAVE_SYS_TIME_H
30 #  include <sys/time.h>
31 # else
32 #  include <time.h>
33 # endif
34 #endif
35 #if HAVE_SYS_TIMES_H
36 #include <sys/times.h>
37 #endif
38 #if HAVE_STRING_H
39 #include <string.h>
40 #else
41 #include <strings.h>
42 #endif
43 #include <ctype.h>
44 #if HAVE_NETINET_IN_H
45 #include <netinet/in.h>
46 #endif
47 #if HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50 #if HAVE_SYS_SOCKET_H
51 #include <sys/socket.h>
52 #endif
53 #if HAVE_NETDB_H
54 #include <netdb.h>
55 #endif
56 #if HAVE_STDLIB_H
57 #       include <stdlib.h>
58 #endif
59 
60 /*
61  * Stuff needed for getHwAddress(...)
62  */
63 #ifdef HAVE_SYS_IOCTL_H
64 #	include <sys/ioctl.h>
65 #endif
66 #ifdef HAVE_NET_IF_H
67 #	include <net/if.h>
68 #endif
69 
70 #include <net-snmp/types.h>
71 #include <net-snmp/output_api.h>
72 #include <net-snmp/config_api.h>
73 #include <net-snmp/utilities.h>
74 
75 #include <net-snmp/library/snmpv3.h>
76 #include <net-snmp/library/callback.h>
77 #include <net-snmp/library/snmp_api.h>
78 #include <net-snmp/library/lcd_time.h>
79 #include <net-snmp/library/scapi.h>
80 #include <net-snmp/library/keytools.h>
81 #include <net-snmp/library/lcd_time.h>
82 #include <net-snmp/library/snmp_secmod.h>
83 #include <net-snmp/library/snmpusm.h>
84 #include <net-snmp/library/transform_oids.h>
85 
86 #include <net-snmp/net-snmp-features.h>
87 
88 static u_long   engineBoots = 1;
89 static unsigned int engineIDType = ENGINEID_TYPE_NETSNMP_RND;
90 static unsigned char *engineID = NULL;
91 static size_t   engineIDLength = 0;
92 static unsigned char *engineIDNic = NULL;
93 static unsigned int engineIDIsSet = 0;  /* flag if ID set by config */
94 static unsigned char *oldEngineID = NULL;
95 static size_t   oldEngineIDLength = 0;
96 static struct timeval snmpv3starttime;
97 
98 #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR)
99 static int      getHwAddress(const char *networkDevice, char *addressOut);
100 #endif
101 
102 /*******************************************************************-o-******
103  * snmpv3_secLevel_conf
104  *
105  * Parameters:
106  *	*word
107  *	*cptr
108  *
109  * Line syntax:
110  *	defSecurityLevel "noAuthNoPriv" | "authNoPriv" | "authPriv"
111  */
112 
113 int
parse_secLevel_conf(const char * word,char * cptr)114 parse_secLevel_conf(const char *word, char *cptr) {
115     if (strcasecmp(cptr, "noAuthNoPriv") == 0 || strcmp(cptr, "1") == 0 ||
116 	strcasecmp(cptr, "nanp") == 0) {
117         return SNMP_SEC_LEVEL_NOAUTH;
118     } else if (strcasecmp(cptr, "authNoPriv") == 0 || strcmp(cptr, "2") == 0 ||
119 	       strcasecmp(cptr, "anp") == 0) {
120         return SNMP_SEC_LEVEL_AUTHNOPRIV;
121     } else if (strcasecmp(cptr, "authPriv") == 0 || strcmp(cptr, "3") == 0 ||
122 	       strcasecmp(cptr, "ap") == 0) {
123         return SNMP_SEC_LEVEL_AUTHPRIV;
124     } else {
125         return -1;
126     }
127 }
128 
129 void
snmpv3_secLevel_conf(const char * word,char * cptr)130 snmpv3_secLevel_conf(const char *word, char *cptr)
131 {
132     int             secLevel;
133 
134     if ((secLevel = parse_secLevel_conf( word, cptr )) >= 0 ) {
135         netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID,
136 			   NETSNMP_DS_LIB_SECLEVEL, secLevel);
137     } else {
138 	netsnmp_config_error("Unknown security level: %s", cptr);
139     }
140     DEBUGMSGTL(("snmpv3", "default secLevel set to: %s = %d\n", cptr,
141                 netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
142 				   NETSNMP_DS_LIB_SECLEVEL)));
143 }
144 
145 
146 int
snmpv3_parse_arg(int arg,char * optarg,netsnmp_session * session,char ** Apsz,char ** Xpsz,int argc,char * const * argv,int flags)147 snmpv3_parse_arg(int arg, char *optarg, netsnmp_session *session, char **Apsz,
148                  char **Xpsz, int argc, char *const *argv, int flags)
149 {
150     int        priv_type;
151     char      *cp;
152     int        zero_sensitive = !( flags & NETSNMP_PARSE_ARGS_NOZERO );
153 
154     switch (arg) {
155 
156     case 'Z':
157         errno=0;
158         session->engineBoots = strtoul(optarg, &cp, 10);
159         if (errno || cp == optarg) {
160             fprintf(stderr, "Need engine boots value after -3Z flag.\n");
161             return (-1);
162         }
163         if (*cp == ',') {
164             char *endptr;
165             cp++;
166             session->engineTime = strtoul(cp, &endptr, 10);
167             if (errno || cp == endptr) {
168                 fprintf(stderr, "Need engine time after \"-3Z engineBoot,\".\n");
169                 return (-1);
170             }
171         }
172         /*
173          * Handle previous '-Z boot time' syntax
174          */
175         else if (optind < argc) {
176             session->engineTime = strtoul(argv[optind], &cp, 10);
177             if (errno || cp == argv[optind]) {
178                 fprintf(stderr, "Need engine time after \"-Z engineBoot\".\n");
179                 return (-1);
180             }
181         } else {
182             fprintf(stderr, "Need engine time after \"-3Z engineBoot,\".\n");
183             return (-1);
184         }
185         break;
186 
187     case 'e':{
188             size_t          ebuf_len = 32, eout_len = 0;
189             u_char         *ebuf = (u_char *) malloc(ebuf_len);
190 
191             if (ebuf == NULL) {
192                 fprintf(stderr, "malloc failure processing -3e flag.\n");
193                 return (-1);
194             }
195             if (!snmp_hex_to_binary
196                 (&ebuf, &ebuf_len, &eout_len, 1, optarg)) {
197                 fprintf(stderr, "Bad engine ID value after -3e flag.\n");
198                 SNMP_FREE(ebuf);
199                 return (-1);
200             }
201             if ((eout_len < 5) || (eout_len > 32)) {
202                 fprintf(stderr, "Invalid engine ID value after -e flag.\n");
203                 free(ebuf);
204                 return (-1);
205             }
206             session->securityEngineID = ebuf;
207             session->securityEngineIDLen = eout_len;
208             break;
209         }
210 
211     case 'E':{
212             size_t          ebuf_len = 32, eout_len = 0;
213             u_char         *ebuf = (u_char *) malloc(ebuf_len);
214 
215             if (ebuf == NULL) {
216                 fprintf(stderr, "malloc failure processing -3E flag.\n");
217                 return (-1);
218             }
219             if (!snmp_hex_to_binary
220                 (&ebuf, &ebuf_len, &eout_len, 1, optarg)) {
221                 fprintf(stderr, "Bad engine ID value after -3E flag.\n");
222                 SNMP_FREE(ebuf);
223                 return (-1);
224             }
225             if ((eout_len < 5) || (eout_len > 32)) {
226                 fprintf(stderr, "Invalid engine ID value after -E flag.\n");
227                 free(ebuf);
228                 return (-1);
229             }
230             session->contextEngineID = ebuf;
231             session->contextEngineIDLen = eout_len;
232             break;
233         }
234 
235     case 'n':
236         session->contextName = optarg;
237         session->contextNameLen = strlen(optarg);
238         break;
239 
240     case 'u':
241         session->securityName = optarg;
242         session->securityNameLen = strlen(optarg);
243         break;
244 
245     case 'l':
246         if (!strcasecmp(optarg, "noAuthNoPriv") || !strcmp(optarg, "1") ||
247             !strcasecmp(optarg, "noauth") || !strcasecmp(optarg, "nanp")) {
248             session->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
249         } else if (!strcasecmp(optarg, "authNoPriv")
250                    || !strcasecmp(optarg, "auth")
251                    || !strcmp(optarg, "2") || !strcasecmp(optarg, "anp")) {
252             session->securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
253         } else if (!strcasecmp(optarg, "authPriv") || !strcmp(optarg, "3")
254                    || !strcasecmp(optarg, "priv")
255                    || !strcasecmp(optarg, "ap")) {
256             session->securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
257         } else {
258             fprintf(stderr,
259                     "Invalid security level specified after -3l flag: %s\n",
260                     optarg);
261             return (-1);
262         }
263         break;
264 
265 #ifdef NETSNMP_SECMOD_USM
266     case 'a': {
267         int auth_type = usm_lookup_auth_type(optarg);
268         if (auth_type > 0) {
269             session->securityAuthProto =
270                 sc_get_auth_oid(auth_type, &session->securityAuthProtoLen);
271          } else {
272             fprintf(stderr,
273                     "Invalid authentication protocol specified after -3a flag: %s\n",
274                     optarg);
275             return (-1);
276         }
277     }
278         break;
279 
280     case 'x':
281         priv_type = usm_lookup_priv_type(optarg);
282         if (priv_type < 0) {
283             fprintf(stderr,
284                     "Invalid privacy protocol specified after -3x flag: %s\n",
285                     optarg);
286             return (-1);
287         }
288         session->securityPrivProto =
289             sc_get_priv_oid(priv_type, &session->securityPrivProtoLen);
290         break;
291 
292     case 'A':
293         *Apsz = strdup(optarg);
294         if (NULL == *Apsz) {
295             fprintf(stderr, "malloc failure processing -%c flag.\n",
296                     (char)arg);
297             return -1;
298         }
299         if (zero_sensitive)
300             memset(optarg, 0x0, strlen(optarg));
301         break;
302 
303     case 'X':
304         *Xpsz = strdup(optarg);
305         if (NULL == *Xpsz) {
306             fprintf(stderr, "malloc failure processing -%c flag.\n",
307                     (char)arg);
308             return -1;
309         }
310         if (zero_sensitive)
311             memset(optarg, 0x0, strlen(optarg));
312         break;
313 #endif /* NETSNMP_SECMOD_USM */
314 
315     case 'm': {
316         size_t bufSize = sizeof(session->securityAuthKey);
317         u_char *tmpp = session->securityAuthKey;
318         if (!snmp_hex_to_binary(&tmpp, &bufSize,
319                                 &session->securityAuthKeyLen, 0, optarg)) {
320             fprintf(stderr, "Bad key value after -3m flag.\n");
321             return (-1);
322         }
323         break;
324     }
325 
326     case 'M': {
327         size_t bufSize = sizeof(session->securityPrivKey);
328         u_char *tmpp = session->securityPrivKey;
329         if (!snmp_hex_to_binary(&tmpp, &bufSize,
330              &session->securityPrivKeyLen, 0, optarg)) {
331             fprintf(stderr, "Bad key value after -3M flag.\n");
332             return (-1);
333         }
334         break;
335     }
336 
337     case 'k': {
338         size_t          kbuf_len = 32, kout_len = 0;
339         u_char         *kbuf = (u_char *) malloc(kbuf_len);
340 
341         if (kbuf == NULL) {
342             fprintf(stderr, "malloc failure processing -3k flag.\n");
343             return (-1);
344         }
345         if (!snmp_hex_to_binary
346             (&kbuf, &kbuf_len, &kout_len, 1, optarg)) {
347             fprintf(stderr, "Bad key value after -3k flag.\n");
348             SNMP_FREE(kbuf);
349             return (-1);
350         }
351         session->securityAuthLocalKey = kbuf;
352         session->securityAuthLocalKeyLen = kout_len;
353         break;
354     }
355 
356     case 'K': {
357         size_t          kbuf_len = 32, kout_len = 0;
358         u_char         *kbuf = (u_char *) malloc(kbuf_len);
359 
360         if (kbuf == NULL) {
361             fprintf(stderr, "malloc failure processing -3K flag.\n");
362             return (-1);
363         }
364         if (!snmp_hex_to_binary
365             (&kbuf, &kbuf_len, &kout_len, 1, optarg)) {
366             fprintf(stderr, "Bad key value after -3K flag.\n");
367             SNMP_FREE(kbuf);
368             return (-1);
369         }
370         session->securityPrivLocalKey = kbuf;
371         session->securityPrivLocalKeyLen = kout_len;
372         break;
373     }
374 
375     default:
376         fprintf(stderr, "Unknown SNMPv3 option passed to -3: %c.\n", arg);
377         return -1;
378     }
379     return 0;
380 }
381 
382 int
snmpv3_parse_args(char * optarg,netsnmp_session * session,char ** Apsz,char ** Xpsz,int argc,char * const * argv,int flags)383 snmpv3_parse_args(char *optarg, netsnmp_session * session, char **Apsz,
384                   char **Xpsz, int argc, char *const *argv, int flags)
385 {
386     char           *cp = optarg;
387     optarg++;
388     /*
389      * Support '... -3x=value ....' syntax
390      */
391     if (*optarg == '=') {
392         optarg++;
393     }
394     /*
395      * and '.... "-3x value" ....'  (*with* the quotes)
396      */
397     while (*optarg && isspace((unsigned char)(*optarg))) {
398         optarg++;
399     }
400     /*
401      * Finally, handle ".... -3x value ...." syntax
402      *   (*without* surrounding quotes)
403      */
404     if (!*optarg) {
405         /*
406          * We've run off the end of the argument
407          *  so move on the the next.
408          */
409         if (optind >= argc) {
410             fprintf(stderr,
411                     "Missing argument after SNMPv3 '-3%c' option.\n", *cp);
412             return (-1);
413         }
414         optarg = argv[optind++];
415     }
416 
417     return snmpv3_parse_arg(*cp, optarg, session, Apsz, Xpsz, argc, argv,
418                             flags);
419 }
420 
421 /*******************************************************************-o-******
422  * setup_engineID
423  *
424  * Parameters:
425  *	**eidp
426  *	 *text	Printable (?) text to be plugged into the snmpEngineID.
427  *
428  * Return:
429  *	Length of allocated engineID string in bytes,  -OR-
430  *	-1 on error.
431  *
432  *
433  * Create an snmpEngineID using text and the local IP address.  If eidp
434  * is defined, use it to return a pointer to the newly allocated data.
435  * Otherwise, use the result to define engineID defined in this module.
436  *
437  * Line syntax:
438  *	engineID <text> | NULL
439  *
440  * XXX	What if a node has multiple interfaces?
441  * XXX	What if multiple engines all choose the same address?
442  *      (answer:  You're screwed, because you might need a kul database
443  *       which is dependant on the current engineID.  Enumeration and other
444  *       tricks won't work).
445  */
446 int
setup_engineID(u_char ** eidp,const char * text)447 setup_engineID(u_char ** eidp, const char *text)
448 {
449     int             enterpriseid = htonl(NETSNMP_ENTERPRISE_OID),
450         netsnmpoid = htonl(NETSNMP_OID),
451         localsetup = (eidp) ? 0 : 1;
452 
453     /*
454      * Use local engineID if *eidp == NULL.
455      */
456 #ifdef HAVE_GETHOSTNAME
457     u_char          buf[SNMP_MAXBUF_SMALL];
458     struct hostent *hent = NULL;
459 #endif
460     u_char         *bufp = NULL;
461     size_t          len;
462     int             localEngineIDType = engineIDType;
463     int             tmpint;
464     time_t          tmptime;
465 
466     engineIDIsSet = 1;
467 
468 #ifdef HAVE_GETHOSTNAME
469 #ifdef AF_INET6
470     /*
471      * see if they selected IPV4 or IPV6 support
472      */
473     if ((ENGINEID_TYPE_IPV6 == localEngineIDType) ||
474         (ENGINEID_TYPE_IPV4 == localEngineIDType)) {
475         /*
476          * get the host name and save the information
477          */
478         gethostname((char *) buf, sizeof(buf));
479         hent = netsnmp_gethostbyname((char *) buf);
480         if (hent && hent->h_addrtype == AF_INET6) {
481             localEngineIDType = ENGINEID_TYPE_IPV6;
482         } else {
483             /*
484              * Not IPV6 so we go with default
485              */
486             localEngineIDType = ENGINEID_TYPE_IPV4;
487         }
488     }
489 #else
490     /*
491      * No IPV6 support.  Check if they selected IPV6 engineID type.
492      *  If so make it IPV4 instead
493      */
494     if (ENGINEID_TYPE_IPV6 == localEngineIDType) {
495         localEngineIDType = ENGINEID_TYPE_IPV4;
496     }
497     if (ENGINEID_TYPE_IPV4 == localEngineIDType) {
498         /*
499          * get the host name and save the information
500          */
501         gethostname((char *) buf, sizeof(buf));
502         hent = netsnmp_gethostbyname((char *) buf);
503     }
504 #endif
505 #endif                          /* HAVE_GETHOSTNAME */
506 
507     /*
508      * Determine if we have text and if so setup our localEngineIDType
509      * * appropriately.
510      */
511     if (NULL != text) {
512         engineIDType = localEngineIDType = ENGINEID_TYPE_TEXT;
513     }
514     /*
515      * Determine length of the engineID string.
516      */
517     len = 5;                    /* always have 5 leading bytes */
518     switch (localEngineIDType) {
519     case ENGINEID_TYPE_TEXT:
520         if (NULL == text) {
521             snmp_log(LOG_ERR,
522                      "Can't set up engineID of type text from an empty string.\n");
523             return -1;
524         }
525         len += strlen(text);    /* 5 leading bytes+text. No NULL char */
526         break;
527 #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR)
528     case ENGINEID_TYPE_MACADDR:        /* MAC address */
529         len += 6;               /* + 6 bytes for MAC address */
530         break;
531 #endif
532     case ENGINEID_TYPE_IPV4:   /* IPv4 */
533         len += 4;               /* + 4 byte IPV4 address */
534         break;
535     case ENGINEID_TYPE_IPV6:   /* IPv6 */
536         len += 16;              /* + 16 byte IPV6 address */
537         break;
538     case ENGINEID_TYPE_NETSNMP_RND:        /* Net-SNMP specific encoding */
539         if (engineID)           /* already setup, keep current value */
540             return engineIDLength;
541         if (oldEngineID) {
542             len = oldEngineIDLength;
543         } else {
544             len += sizeof(int) + sizeof(time_t);
545         }
546         break;
547     default:
548         snmp_log(LOG_ERR,
549                  "Unknown EngineID type requested for setup (%d).  Using IPv4.\n",
550                  localEngineIDType);
551         localEngineIDType = ENGINEID_TYPE_IPV4; /* make into IPV4 */
552         len += 4;               /* + 4 byte IPv4 address */
553         break;
554     }                           /* switch */
555 
556 
557     /*
558      * Allocate memory and store enterprise ID.
559      */
560     if ((bufp = (u_char *) calloc(1, len)) == NULL) {
561         snmp_log_perror("setup_engineID malloc");
562         return -1;
563     }
564     if (localEngineIDType == ENGINEID_TYPE_NETSNMP_RND)
565         /*
566          * we must use the net-snmp enterprise id here, regardless
567          */
568         memcpy(bufp, &netsnmpoid, sizeof(netsnmpoid));    /* XXX Must be 4 bytes! */
569     else
570         memcpy(bufp, &enterpriseid, sizeof(enterpriseid));      /* XXX Must be 4 bytes! */
571 
572     bufp[0] |= 0x80;
573 
574 
575     /*
576      * Store the given text  -OR-   the first found IP address
577      *  -OR-  the MAC address  -OR-  random elements
578      * (the latter being the recommended default)
579      */
580     switch (localEngineIDType) {
581     case ENGINEID_TYPE_NETSNMP_RND:
582         if (oldEngineID) {
583             /*
584              * keep our previous notion of the engineID
585              */
586             memcpy(bufp, oldEngineID, oldEngineIDLength);
587         } else {
588             /*
589              * Here we've desigend our own ENGINEID that is not based on
590              * an address which may change and may even become conflicting
591              * in the future like most of the default v3 engineID types
592              * suffer from.
593              *
594              * Ours is built from 2 fairly random elements: a random number and
595              * the current time in seconds.  This method suffers from boxes
596              * that may not have a correct clock setting and random number
597              * seed at startup, but few OSes should have that problem.
598              */
599             bufp[4] = ENGINEID_TYPE_NETSNMP_RND;
600             tmpint = netsnmp_random();
601             memcpy(bufp + 5, &tmpint, sizeof(tmpint));
602             tmptime = time(NULL);
603             memcpy(bufp + 5 + sizeof(tmpint), &tmptime, sizeof(tmptime));
604         }
605         break;
606     case ENGINEID_TYPE_TEXT:
607         bufp[4] = ENGINEID_TYPE_TEXT;
608         memcpy((char *) bufp + 5, (text), strlen(text));
609         break;
610 #ifdef HAVE_GETHOSTNAME
611 #ifdef AF_INET6
612     case ENGINEID_TYPE_IPV6:
613         bufp[4] = ENGINEID_TYPE_IPV6;
614         if (hent)
615             memcpy(bufp + 5, hent->h_addr_list[0], hent->h_length);
616         break;
617 #endif
618 #endif
619 #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR)
620     case ENGINEID_TYPE_MACADDR:
621         {
622             int             x;
623             bufp[4] = ENGINEID_TYPE_MACADDR;
624             /*
625              * use default NIC if none provided
626              */
627             if (NULL == engineIDNic) {
628 	      x = getHwAddress(DEFAULT_NIC, (char *)&bufp[5]);
629             } else {
630 	      x = getHwAddress((char *)engineIDNic, (char *)&bufp[5]);
631             }
632             if (0 != x)
633                 /*
634                  * function failed fill MAC address with zeros
635                  */
636             {
637                 memset(&bufp[5], 0, 6);
638             }
639         }
640         break;
641 #endif
642     case ENGINEID_TYPE_IPV4:
643     default:
644         bufp[4] = ENGINEID_TYPE_IPV4;
645 #ifdef HAVE_GETHOSTNAME
646         if (hent && hent->h_addrtype == AF_INET) {
647             memcpy(bufp + 5, hent->h_addr_list[0], hent->h_length);
648         } else {                /* Unknown address type.  Default to 127.0.0.1. */
649 
650             bufp[5] = 127;
651             bufp[6] = 0;
652             bufp[7] = 0;
653             bufp[8] = 1;
654         }
655 #else                           /* HAVE_GETHOSTNAME */
656         /*
657          * Unknown address type.  Default to 127.0.0.1.
658          */
659         bufp[5] = 127;
660         bufp[6] = 0;
661         bufp[7] = 0;
662         bufp[8] = 1;
663 #endif                          /* HAVE_GETHOSTNAME */
664         break;
665     }
666 
667     /*
668      * Pass the string back to the calling environment, or use it for
669      * our local engineID.
670      */
671     if (localsetup) {
672         SNMP_FREE(engineID);
673         engineID = bufp;
674         engineIDLength = len;
675 
676     } else {
677         *eidp = bufp;
678     }
679 
680 
681     return len;
682 
683 }                               /* end setup_engineID() */
684 
685 int
free_engineID(int majorid,int minorid,void * serverarg,void * clientarg)686 free_engineID(int majorid, int minorid, void *serverarg,
687 	      void *clientarg)
688 {
689     SNMP_FREE(engineID);
690     SNMP_FREE(engineIDNic);
691     SNMP_FREE(oldEngineID);
692     engineIDIsSet = 0;
693     return 0;
694 }
695 
696 /*******************************************************************-o-******
697  * engineBoots_conf
698  *
699  * Parameters:
700  *	*word
701  *	*cptr
702  *
703  * Line syntax:
704  *	engineBoots <num_boots>
705  */
706 void
engineBoots_conf(const char * word,char * cptr)707 engineBoots_conf(const char *word, char *cptr)
708 {
709     engineBoots = atoi(cptr) + 1;
710     DEBUGMSGTL(("snmpv3", "engineBoots: %lu\n", engineBoots));
711 }
712 
713 /*******************************************************************-o-******
714  * engineIDType_conf
715  *
716  * Parameters:
717  *	*word
718  *	*cptr
719  *
720  * Line syntax:
721  *	engineIDType <1 or 3>
722  *		1 is default for IPv4 engine ID type.  Will automatically
723  *		    chose between IPv4 & IPv6 if either 1 or 2 is specified.
724  *		2 is for IPv6.
725  *		3 is hardware (MAC) address, currently supported under Linux
726  */
727 void
engineIDType_conf(const char * word,char * cptr)728 engineIDType_conf(const char *word, char *cptr)
729 {
730     engineIDType = atoi(cptr);
731     /*
732      * verify valid type selected
733      */
734     switch (engineIDType) {
735     case ENGINEID_TYPE_IPV4:   /* IPv4 */
736     case ENGINEID_TYPE_IPV6:   /* IPv6 */
737         /*
738          * IPV? is always good
739          */
740         break;
741 #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR)
742     case ENGINEID_TYPE_MACADDR:        /* MAC address */
743         break;
744 #endif
745     default:
746         /*
747          * unsupported one chosen
748          */
749         config_perror("Unsupported enginedIDType, forcing IPv4");
750         engineIDType = ENGINEID_TYPE_IPV4;
751     }
752     DEBUGMSGTL(("snmpv3", "engineIDType: %d\n", engineIDType));
753 }
754 
755 /*******************************************************************-o-******
756  * engineIDNic_conf
757  *
758  * Parameters:
759  *	*word
760  *	*cptr
761  *
762  * Line syntax:
763  *	engineIDNic <string>
764  *		eth0 is default
765  */
766 void
engineIDNic_conf(const char * word,char * cptr)767 engineIDNic_conf(const char *word, char *cptr)
768 {
769     /*
770      * Make sure they haven't already specified the engineID via the
771      * * configuration file
772      */
773     if (0 == engineIDIsSet)
774         /*
775          * engineID has NOT been set via configuration file
776          */
777     {
778         /*
779          * See if already set if so erase & release it
780          */
781         SNMP_FREE(engineIDNic);
782         engineIDNic = (u_char *) malloc(strlen(cptr) + 1);
783         if (NULL != engineIDNic) {
784             strcpy((char *) engineIDNic, cptr);
785             DEBUGMSGTL(("snmpv3", "Initializing engineIDNic: %s\n",
786                         engineIDNic));
787         } else {
788             DEBUGMSGTL(("snmpv3",
789                         "Error allocating memory for engineIDNic!\n"));
790         }
791     } else {
792         DEBUGMSGTL(("snmpv3",
793                     "NOT setting engineIDNic, engineID already set\n"));
794     }
795 }
796 
797 /*******************************************************************-o-******
798  * engineID_conf
799  *
800  * Parameters:
801  *	*word
802  *	*cptr
803  *
804  * This function reads a string from the configuration file and uses that
805  * string to initialize the engineID.  It's assumed to be human readable.
806  */
807 void
engineID_conf(const char * word,char * cptr)808 engineID_conf(const char *word, char *cptr)
809 {
810     setup_engineID(NULL, cptr);
811     DEBUGMSGTL(("snmpv3", "initialized engineID with: %s\n", cptr));
812 }
813 
814 void
version_conf(const char * word,char * cptr)815 version_conf(const char *word, char *cptr)
816 {
817     int valid = 0;
818 #ifndef NETSNMP_DISABLE_SNMPV1
819     if ((strcmp(cptr,  "1") == 0) ||
820         (strcmp(cptr, "v1") == 0)) {
821         netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SNMPVERSION,
822 			   NETSNMP_DS_SNMP_VERSION_1);       /* bogus value */
823         valid = 1;
824     }
825 #endif
826 #ifndef NETSNMP_DISABLE_SNMPV2C
827     if ((strcasecmp(cptr,  "2c") == 0) ||
828                (strcasecmp(cptr, "v2c") == 0)) {
829         netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SNMPVERSION,
830 			   NETSNMP_DS_SNMP_VERSION_2c);
831         valid = 1;
832     }
833 #endif
834     if ((strcasecmp(cptr,  "3" ) == 0) ||
835                (strcasecmp(cptr, "v3" ) == 0)) {
836         netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SNMPVERSION,
837 			   NETSNMP_DS_SNMP_VERSION_3);
838         valid = 1;
839     }
840     if (!valid) {
841         config_perror("Unknown version specification");
842         return;
843     }
844     DEBUGMSGTL(("snmpv3", "set default version to %d\n",
845                 netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
846 				   NETSNMP_DS_LIB_SNMPVERSION)));
847 }
848 
849 /*
850  * oldengineID_conf(const char *, char *):
851  *
852  * Reads a octet string encoded engineID into the oldEngineID and
853  * oldEngineIDLen pointers.
854  */
855 void
oldengineID_conf(const char * word,char * cptr)856 oldengineID_conf(const char *word, char *cptr)
857 {
858     read_config_read_octet_string(cptr, &oldEngineID, &oldEngineIDLength);
859 }
860 
861 /*
862  * set_exact_engineID_conf(const oid *, size_t):
863  *
864  * Specifies an exact engineID OID.
865  */
866 int
set_exact_engineID(const u_char * id,size_t len)867 set_exact_engineID(const u_char *id, size_t len)
868 {
869     int rc = SNMPERR_SUCCESS;
870     u_char *newID = NULL;
871 
872     if (NULL == id || 0 == len)
873         return SNMPERR_GENERR;
874 
875     if (len > MAX_ENGINEID_LENGTH)
876         return SNMPERR_TOO_LONG;
877 
878     newID = malloc(len+1);
879     if (NULL == newID) {
880         snmp_log(LOG_ERR, "malloc failed for engineID\n");
881         return SNMPERR_GENERR;
882     }
883     if (NULL != engineID)
884         free(engineID);
885 
886     memcpy(newID, id, len);
887     newID[len] = 0;
888 
889     engineID = newID;
890     engineIDLength = len;
891     engineIDIsSet = 1;
892     engineIDType = ENGINEID_TYPE_EXACT;
893 
894     return rc;
895 }
896 
897 /*
898  * exactEngineID_conf(const char *, char *):
899  *
900  * Reads a octet string encoded engineID into the engineID and
901  * engineIDLen pointers.
902  */
903 void
exactEngineID_conf(const char * word,char * cptr)904 exactEngineID_conf(const char *word, char *cptr)
905 {
906     /** we want buf > max so we know if there is truncation */
907     u_char new_engineID[MAX_ENGINEID_LENGTH+2],
908         *new_engineIDptr = new_engineID;
909     size_t new_engineIDLength = sizeof(new_engineID);
910 
911     read_config_read_octet_string(cptr, &new_engineIDptr, &new_engineIDLength);
912     if (new_engineIDLength > MAX_ENGINEID_LENGTH) {
913         new_engineIDLength = MAX_ENGINEID_LENGTH;
914 	netsnmp_config_error(
915 	    "exactEngineID '%s' too long; truncating to %d bytes",
916 	    cptr, MAX_ENGINEID_LENGTH);
917     }
918 
919     set_exact_engineID( new_engineIDptr, new_engineIDLength);
920 }
921 
922 
923 /*
924  * merely call
925  */
926 netsnmp_feature_child_of(get_enginetime_alarm, netsnmp_unused);
927 #ifndef NETSNMP_FEATURE_REMOVE_GET_ENGINETIME_ALARM
928 void
get_enginetime_alarm(unsigned int regnum,void * clientargs)929 get_enginetime_alarm(unsigned int regnum, void *clientargs)
930 {
931     /* we do this every so (rarely) often just to make sure we watch
932        wrapping of the times() output */
933     snmpv3_local_snmpEngineTime();
934 }
935 #endif /* NETSNMP_FEATURE_REMOVE_GET_ENGINETIME_ALARM */
936 
937 /*******************************************************************-o-******
938  * init_snmpv3
939  *
940  * Parameters:
941  *	*type	Label for the config file "type" used by calling entity.
942  *
943  * Set time and engineID.
944  * Set parsing functions for config file tokens.
945  * Initialize SNMP Crypto API (SCAPI).
946  */
947 void
init_snmpv3(const char * type)948 init_snmpv3(const char *type)
949 {
950     netsnmp_get_monotonic_clock(&snmpv3starttime);
951 
952     if (!type)
953         type = "__snmpapp__";
954 
955     /*
956      * we need to be called back later
957      */
958     snmp_register_callback(SNMP_CALLBACK_LIBRARY,
959                            SNMP_CALLBACK_POST_READ_CONFIG,
960                            init_snmpv3_post_config, NULL);
961 
962     snmp_register_callback(SNMP_CALLBACK_LIBRARY,
963                            SNMP_CALLBACK_POST_PREMIB_READ_CONFIG,
964                            init_snmpv3_post_premib_config, NULL);
965     /*
966      * we need to be called back later
967      */
968     snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA,
969                            snmpv3_store, (void *) strdup(type));
970 
971     /*
972      * initialize submodules
973      */
974     /*
975      * NOTE: this must be after the callbacks are registered above,
976      * since they need to be called before the USM callbacks.
977      */
978     init_secmod();
979 
980     /*
981      * register all our configuration handlers (ack, there's a lot)
982      */
983 
984     /*
985      * handle engineID setup before everything else which may depend on it
986      */
987     register_prenetsnmp_mib_handler(type, "engineID", engineID_conf, NULL,
988                                     "string");
989     register_prenetsnmp_mib_handler(type, "oldEngineID", oldengineID_conf,
990                                     NULL, NULL);
991     register_prenetsnmp_mib_handler(type, "exactEngineID", exactEngineID_conf,
992                                     NULL, NULL);
993     register_prenetsnmp_mib_handler(type, "engineIDType",
994                                     engineIDType_conf, NULL, "num");
995     register_prenetsnmp_mib_handler(type, "engineIDNic", engineIDNic_conf,
996                                     NULL, "string");
997     register_config_handler(type, "engineBoots", engineBoots_conf, NULL,
998                             NULL);
999 
1000     /*
1001      * default store config entries
1002      */
1003     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defSecurityName",
1004 			       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECNAME);
1005     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defContext",
1006 			       NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CONTEXT);
1007     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defPassphrase",
1008                                NETSNMP_DS_LIBRARY_ID,
1009                                NETSNMP_DS_LIB_PASSPHRASE);
1010     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defAuthPassphrase",
1011                                NETSNMP_DS_LIBRARY_ID,
1012                                NETSNMP_DS_LIB_AUTHPASSPHRASE);
1013     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defPrivPassphrase",
1014                                NETSNMP_DS_LIBRARY_ID,
1015                                NETSNMP_DS_LIB_PRIVPASSPHRASE);
1016     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defAuthMasterKey",
1017                                NETSNMP_DS_LIBRARY_ID,
1018                                NETSNMP_DS_LIB_AUTHMASTERKEY);
1019     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defPrivMasterKey",
1020                                NETSNMP_DS_LIBRARY_ID,
1021                                NETSNMP_DS_LIB_PRIVMASTERKEY);
1022     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defAuthLocalizedKey",
1023                                NETSNMP_DS_LIBRARY_ID,
1024                                NETSNMP_DS_LIB_AUTHLOCALIZEDKEY);
1025     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defPrivLocalizedKey",
1026                                NETSNMP_DS_LIBRARY_ID,
1027                                NETSNMP_DS_LIB_PRIVLOCALIZEDKEY);
1028     register_config_handler("snmp", "defVersion", version_conf, NULL,
1029                             "1|2c|3");
1030 
1031     register_config_handler("snmp", "defSecurityLevel",
1032                             snmpv3_secLevel_conf, NULL,
1033                             "noAuthNoPriv|authNoPriv|authPriv");
1034 }
1035 
1036 /*
1037  * initializations for SNMPv3 to be called after the configuration files
1038  * have been read.
1039  */
1040 
1041 int
init_snmpv3_post_config(int majorid,int minorid,void * serverarg,void * clientarg)1042 init_snmpv3_post_config(int majorid, int minorid, void *serverarg,
1043                         void *clientarg)
1044 {
1045 
1046     size_t          engineIDLen;
1047     u_char         *c_engineID;
1048 
1049     c_engineID = snmpv3_generate_engineID(&engineIDLen);
1050 
1051     if (engineIDLen == 0 || !c_engineID) {
1052         /*
1053          * Somethine went wrong - help!
1054          */
1055         SNMP_FREE(c_engineID);
1056         return SNMPERR_GENERR;
1057     }
1058 
1059     /*
1060      * if our engineID has changed at all, the boots record must be set to 1
1061      */
1062     if (engineIDLen != oldEngineIDLength ||
1063         oldEngineID == NULL || c_engineID == NULL ||
1064         memcmp(oldEngineID, c_engineID, engineIDLen) != 0) {
1065         engineBoots = 1;
1066     }
1067 
1068 #ifdef NETSNMP_SECMOD_USM
1069     /*
1070      * for USM set our local engineTime in the LCD timing cache
1071      */
1072     set_enginetime(c_engineID, engineIDLen,
1073                    snmpv3_local_snmpEngineBoots(),
1074                    snmpv3_local_snmpEngineTime(), TRUE);
1075 #endif /* NETSNMP_SECMOD_USM */
1076 
1077     SNMP_FREE(c_engineID);
1078     return SNMPERR_SUCCESS;
1079 }
1080 
1081 int
init_snmpv3_post_premib_config(int majorid,int minorid,void * serverarg,void * clientarg)1082 init_snmpv3_post_premib_config(int majorid, int minorid, void *serverarg,
1083                                void *clientarg)
1084 {
1085     if (!engineIDIsSet)
1086         setup_engineID(NULL, NULL);
1087 
1088     return SNMPERR_SUCCESS;
1089 }
1090 
1091 /*******************************************************************-o-******
1092  * store_snmpv3
1093  *
1094  * Parameters:
1095  *	*type
1096  */
1097 int
snmpv3_store(int majorID,int minorID,void * serverarg,void * clientarg)1098 snmpv3_store(int majorID, int minorID, void *serverarg, void *clientarg)
1099 {
1100     char            line[SNMP_MAXBUF_SMALL];
1101     u_char          c_engineID[SNMP_MAXBUF_SMALL];
1102     int             engineIDLen;
1103     const char     *type = (const char *) clientarg;
1104 
1105     if (type == NULL)           /* should never happen, since the arg is ours */
1106         type = "unknown";
1107 
1108     sprintf(line, "engineBoots %ld", engineBoots);
1109     read_config_store(type, line);
1110 
1111     engineIDLen = snmpv3_get_engineID(c_engineID, SNMP_MAXBUF_SMALL);
1112 
1113     if (engineIDLen) {
1114         /*
1115          * store the engineID used for this run
1116          */
1117         sprintf(line, "oldEngineID ");
1118         read_config_save_octet_string(line + strlen(line), c_engineID,
1119                                       engineIDLen);
1120         read_config_store(type, line);
1121     }
1122     return SNMPERR_SUCCESS;
1123 }                               /* snmpv3_store() */
1124 
1125 u_long
snmpv3_local_snmpEngineBoots(void)1126 snmpv3_local_snmpEngineBoots(void)
1127 {
1128     return engineBoots;
1129 }
1130 
1131 
1132 /*******************************************************************-o-******
1133  * snmpv3_get_engineID
1134  *
1135  * Parameters:
1136  *	*buf
1137  *	 buflen
1138  *
1139  * Returns:
1140  *	Length of engineID	On Success
1141  *	SNMPERR_GENERR		Otherwise.
1142  *
1143  *
1144  * Store engineID in buf; return the length.
1145  *
1146  */
1147 size_t
snmpv3_get_engineID(u_char * buf,size_t buflen)1148 snmpv3_get_engineID(u_char * buf, size_t buflen)
1149 {
1150     /*
1151      * Sanity check.
1152      */
1153     if (!buf || (buflen < engineIDLength)) {
1154         return 0;
1155     }
1156     if (!engineID) {
1157         return 0;
1158     }
1159 
1160     memcpy(buf, engineID, engineIDLength);
1161     return engineIDLength;
1162 
1163 }                               /* end snmpv3_get_engineID() */
1164 
1165 /*******************************************************************-o-******
1166  * snmpv3_clone_engineID
1167  *
1168  * Parameters:
1169  *	**dest
1170  *       *dest_len
1171  *       src
1172  *	 srclen
1173  *
1174  * Returns:
1175  *	Length of engineID	On Success
1176  *	0		        Otherwise.
1177  *
1178  *
1179  * Clones engineID, creates memory
1180  *
1181  */
1182 int
snmpv3_clone_engineID(u_char ** dest,size_t * destlen,u_char * src,size_t srclen)1183 snmpv3_clone_engineID(u_char ** dest, size_t * destlen, u_char * src,
1184                       size_t srclen)
1185 {
1186     if (!dest || !destlen)
1187         return 0;
1188 
1189     SNMP_FREE(*dest);
1190     *destlen = 0;
1191 
1192     if (srclen && src) {
1193         *dest = (u_char *) malloc(srclen);
1194         if (*dest == NULL)
1195             return 0;
1196         memmove(*dest, src, srclen);
1197         *destlen = srclen;
1198     }
1199     return *destlen;
1200 }                               /* end snmpv3_clone_engineID() */
1201 
1202 
1203 /*******************************************************************-o-******
1204  * snmpv3_generate_engineID
1205  *
1206  * Parameters:
1207  *	*length
1208  *
1209  * Returns:
1210  *	Pointer to copy of engineID	On Success.
1211  *	NULL				If malloc() or snmpv3_get_engineID()
1212  *						fail.
1213  *
1214  * Generates a malloced copy of our engineID.
1215  *
1216  * 'length' is set to the length of engineID  -OR-  < 0 on failure.
1217  */
1218 u_char         *
snmpv3_generate_engineID(size_t * length)1219 snmpv3_generate_engineID(size_t * length)
1220 {
1221     u_char         *newID;
1222     newID = (u_char *) malloc(engineIDLength);
1223 
1224     if (newID) {
1225         *length = snmpv3_get_engineID(newID, engineIDLength);
1226         if (*length == 0) {
1227             SNMP_FREE(newID);
1228             newID = NULL;
1229         }
1230     }
1231     return newID;
1232 
1233 }                               /* end snmpv3_generate_engineID() */
1234 
1235 /**
1236  * Return the value of snmpEngineTime. According to RFC 3414 snmpEngineTime
1237  * is a 31-bit counter. engineBoots must be incremented every time that
1238  * counter wraps around.
1239  *
1240  * @see See also <a href="http://tools.ietf.org/html/rfc3414">RFC 3414</a>.
1241  *
1242  * @note It is assumed that this function is called at least once every
1243  *   2**31 seconds.
1244  */
1245 u_long
snmpv3_local_snmpEngineTime(void)1246 snmpv3_local_snmpEngineTime(void)
1247 {
1248 #ifdef NETSNMP_FEATURE_CHECKING
1249     netsnmp_feature_require(calculate_sectime_diff)
1250 #endif /* NETSNMP_FEATURE_CHECKING */
1251 
1252     static uint32_t last_engineTime;
1253     struct timeval  now;
1254     uint32_t engineTime;
1255 
1256     netsnmp_get_monotonic_clock(&now);
1257     engineTime = calculate_sectime_diff(&now, &snmpv3starttime) & 0x7fffffffL;
1258     if (engineTime < last_engineTime)
1259         engineBoots++;
1260     last_engineTime = engineTime;
1261     return engineTime;
1262 }
1263 
1264 
1265 
1266 /*
1267  * Code only for Linux systems
1268  */
1269 #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR)
1270 static int
getHwAddress(const char * networkDevice,char * addressOut)1271 getHwAddress(const char *networkDevice, /* e.g. "eth0", "eth1" */
1272              char *addressOut)
1273 {                               /* return address. Len=IFHWADDRLEN */
1274     /*
1275      * getHwAddress(...)
1276      * *
1277      * *  This function will return a Network Interfaces Card's Hardware
1278      * *  address (aka MAC address).
1279      * *
1280      * *  Input Parameter(s):
1281      * *      networkDevice - a null terminated string with the name of a network
1282      * *                      device.  Examples: eth0, eth1, etc...
1283      * *
1284      * *  Output Parameter(s):
1285      * *      addressOut -    This is the binary value of the hardware address.
1286      * *                      This value is NOT converted into a hexadecimal string.
1287      * *                      The caller must pre-allocate for a return value of
1288      * *                      length IFHWADDRLEN
1289      * *
1290      * *  Return value:   This function will return zero (0) for success.  If
1291      * *                  an error occurred the function will return -1.
1292      * *
1293      * *  Caveats:    This has only been tested on Ethernet networking cards.
1294      */
1295     int             sock;       /* our socket */
1296     struct ifreq    request;    /* struct which will have HW address */
1297 
1298     if ((NULL == networkDevice) || (NULL == addressOut)) {
1299         return -1;
1300     }
1301     /*
1302      * In order to find out the hardware (MAC) address of our system under
1303      * * Linux we must do the following:
1304      * * 1.  Create a socket
1305      * * 2.  Do an ioctl(...) call with the SIOCGIFHWADDRLEN operation.
1306      */
1307     sock = socket(AF_INET, SOCK_DGRAM, 0);
1308     if (sock < 0) {
1309         return -1;
1310     }
1311     /*
1312      * erase the request block
1313      */
1314     memset(&request, 0, sizeof(request));
1315     /*
1316      * copy the name of the net device we want to find the HW address for
1317      */
1318     strlcpy(request.ifr_name, networkDevice, IFNAMSIZ);
1319     /*
1320      * Get the HW address
1321      */
1322     if (ioctl(sock, SIOCGIFHWADDR, &request)) {
1323         close(sock);
1324         return -1;
1325     }
1326     close(sock);
1327     memcpy(addressOut, request.ifr_hwaddr.sa_data, IFHWADDRLEN);
1328     return 0;
1329 }
1330 #endif
1331 
1332 #ifdef NETSNMP_ENABLE_TESTING_CODE
1333 /**
1334  * Set SNMPv3 engineBoots and start time.
1335  *
1336  * @note This function does not exist. Go away. It certainly should never be
1337  *   used, unless in a testing scenario, which is why it was created
1338  */
1339 void
snmpv3_set_engineBootsAndTime(int boots,int ttime)1340 snmpv3_set_engineBootsAndTime(int boots, int ttime)
1341 {
1342     engineBoots = boots;
1343     netsnmp_get_monotonic_clock(&snmpv3starttime);
1344     snmpv3starttime.tv_sec -= ttime;
1345 }
1346 #endif
1347