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