net-snmp 5.7
snmpv3.c
00001 /*
00002  * snmpv3.c
00003  */
00004 
00005 #include <net-snmp/net-snmp-config.h>
00006 #include <errno.h>
00007 #ifdef HAVE_LIMITS_H
00008 #include <limits.h>
00009 #endif
00010 #include <stdio.h>
00011 #include <sys/types.h>
00012 
00013 #if TIME_WITH_SYS_TIME
00014 # include <sys/time.h>
00015 # include <time.h>
00016 #else
00017 # if HAVE_SYS_TIME_H
00018 #  include <sys/time.h>
00019 # else
00020 #  include <time.h>
00021 # endif
00022 #endif
00023 #if HAVE_SYS_TIMES_H
00024 #include <sys/times.h>
00025 #endif
00026 #if HAVE_STRING_H
00027 #include <string.h>
00028 #else
00029 #include <strings.h>
00030 #endif
00031 #include <ctype.h>
00032 #if HAVE_NETINET_IN_H
00033 #include <netinet/in.h>
00034 #endif
00035 #if HAVE_UNISTD_H
00036 #include <unistd.h>
00037 #endif
00038 #if HAVE_SYS_SOCKET_H
00039 #include <sys/socket.h>
00040 #endif
00041 #if HAVE_NETDB_H
00042 #include <netdb.h>
00043 #endif
00044 #if HAVE_STDLIB_H
00045 #       include <stdlib.h>
00046 #endif
00047 
00048 /*
00049  * Stuff needed for getHwAddress(...) 
00050  */
00051 #ifdef HAVE_SYS_IOCTL_H
00052 #       include <sys/ioctl.h>
00053 #endif
00054 #ifdef HAVE_NET_IF_H
00055 #       include <net/if.h>
00056 #endif
00057 
00058 #if HAVE_DMALLOC_H
00059 #include <dmalloc.h>
00060 #endif
00061 
00062 #include <net-snmp/types.h>
00063 #include <net-snmp/output_api.h>
00064 #include <net-snmp/config_api.h>
00065 #include <net-snmp/utilities.h>
00066 
00067 #include <net-snmp/library/snmpv3.h>
00068 #include <net-snmp/library/callback.h>
00069 #include <net-snmp/library/snmp_api.h>
00070 #include <net-snmp/library/lcd_time.h>
00071 #include <net-snmp/library/scapi.h>
00072 #include <net-snmp/library/keytools.h>
00073 #include <net-snmp/library/lcd_time.h>
00074 #include <net-snmp/library/snmp_secmod.h>
00075 #include <net-snmp/library/snmpusm.h>
00076 #include <net-snmp/library/transform_oids.h>
00077 
00078 #include <net-snmp/net-snmp-features.h>
00079 
00080 static u_long   engineBoots = 1;
00081 static unsigned int engineIDType = ENGINEID_TYPE_NETSNMP_RND;
00082 static unsigned char *engineID = NULL;
00083 static size_t   engineIDLength = 0;
00084 static unsigned char *engineIDNic = NULL;
00085 static unsigned int engineIDIsSet = 0;  /* flag if ID set by config */
00086 static unsigned char *oldEngineID = NULL;
00087 static size_t   oldEngineIDLength = 0;
00088 static struct timeval snmpv3starttime;
00089 
00090 /* this is probably an over-kill ifdef, but why not */
00091 #if defined(HAVE_SYS_TIMES_H) && defined(HAVE_UNISTD_H) && defined(HAVE_TIMES) && defined(_SC_CLK_TCK) && defined(HAVE_SYSCONF) && defined(UINT_MAX)
00092 
00093 #define SNMP_USE_TIMES 1
00094 
00095 static clock_t snmpv3startClock;
00096 static long clockticks = 0;
00097 static unsigned int lastcalltime = 0;
00098 static unsigned int wrapcounter = 0;
00099 
00100 #endif /* times() tests */
00101 
00102 #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR)
00103 static int      getHwAddress(const char *networkDevice, char *addressOut);
00104 #endif
00105 
00106 /*******************************************************************-o-******
00107  * snmpv3_secLevel_conf
00108  *
00109  * Parameters:
00110  *      *word
00111  *      *cptr
00112  *
00113  * Line syntax:
00114  *      defSecurityLevel "noAuthNoPriv" | "authNoPriv" | "authPriv"
00115  */
00116 
00117 int
00118 parse_secLevel_conf(const char *word, char *cptr) {
00119     if (strcasecmp(cptr, "noAuthNoPriv") == 0 || strcmp(cptr, "1") == 0 ||
00120         strcasecmp(cptr, "nanp") == 0) {
00121         return SNMP_SEC_LEVEL_NOAUTH;
00122     } else if (strcasecmp(cptr, "authNoPriv") == 0 || strcmp(cptr, "2") == 0 ||
00123                strcasecmp(cptr, "anp") == 0) {
00124         return SNMP_SEC_LEVEL_AUTHNOPRIV;
00125     } else if (strcasecmp(cptr, "authPriv") == 0 || strcmp(cptr, "3") == 0 ||
00126                strcasecmp(cptr, "ap") == 0) {
00127         return SNMP_SEC_LEVEL_AUTHPRIV;
00128     } else {
00129         return -1;
00130     }
00131 }
00132 
00133 void
00134 snmpv3_secLevel_conf(const char *word, char *cptr)
00135 {
00136     int             secLevel;
00137 
00138     if ((secLevel = parse_secLevel_conf( word, cptr )) >= 0 ) {
00139         netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, 
00140                            NETSNMP_DS_LIB_SECLEVEL, secLevel);
00141     } else {
00142         netsnmp_config_error("Unknown security level: %s", cptr);
00143     }
00144     DEBUGMSGTL(("snmpv3", "default secLevel set to: %s = %d\n", cptr,
00145                 netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
00146                                    NETSNMP_DS_LIB_SECLEVEL)));
00147 }
00148 
00149 
00150 NETSNMP_IMPORT int
00151 snmpv3_options(char *optarg, netsnmp_session * session, char **Apsz,
00152                char **Xpsz, int argc, char *const *argv);
00153 int
00154 snmpv3_options(char *optarg, netsnmp_session * session, char **Apsz,
00155                char **Xpsz, int argc, char *const *argv)
00156 {
00157     char           *cp = optarg;
00158     int testcase;
00159     optarg++;
00160     /*
00161      * Support '... -3x=value ....' syntax
00162      */
00163     if (*optarg == '=') {
00164         optarg++;
00165     }
00166     /*
00167      * and '.... "-3x value" ....'  (*with* the quotes)
00168      */
00169     while (*optarg && isspace((unsigned char)(*optarg))) {
00170         optarg++;
00171     }
00172     /*
00173      * Finally, handle ".... -3x value ...." syntax
00174      *   (*without* surrounding quotes)
00175      */
00176     if (!*optarg) {
00177         /*
00178          * We've run off the end of the argument
00179          *  so move on the the next.
00180          */
00181         optarg = argv[optind++];
00182         if (optind > argc) {
00183             fprintf(stderr,
00184                     "Missing argument after SNMPv3 '-3%c' option.\n", *cp);
00185             return (-1);
00186         }
00187     }
00188 
00189     switch (*cp) {
00190 
00191     case 'Z':
00192         errno=0;
00193         session->engineBoots = strtoul(optarg, &cp, 10);
00194         if (errno || cp == optarg) {
00195             fprintf(stderr, "Need engine boots value after -3Z flag.\n");
00196             return (-1);
00197         }
00198         if (*cp == ',') {
00199             char *endptr;
00200             cp++;
00201             session->engineTime = strtoul(cp, &endptr, 10);
00202             if (errno || cp == endptr) {
00203                 fprintf(stderr, "Need engine time after \"-3Z engineBoot,\".\n");
00204                 return (-1);
00205             }
00206         } else {
00207             fprintf(stderr, "Need engine time after \"-3Z engineBoot,\".\n");
00208             return (-1);
00209         }
00210         break;
00211 
00212     case 'e':{
00213             size_t          ebuf_len = 32, eout_len = 0;
00214             u_char         *ebuf = (u_char *) malloc(ebuf_len);
00215 
00216             if (ebuf == NULL) {
00217                 fprintf(stderr, "malloc failure processing -3e flag.\n");
00218                 return (-1);
00219             }
00220             if (!snmp_hex_to_binary
00221                 (&ebuf, &ebuf_len, &eout_len, 1, optarg)) {
00222                 fprintf(stderr, "Bad engine ID value after -3e flag.\n");
00223                 SNMP_FREE(ebuf);
00224                 return (-1);
00225             }
00226             session->securityEngineID = ebuf;
00227             session->securityEngineIDLen = eout_len;
00228             break;
00229         }
00230 
00231     case 'E':{
00232             size_t          ebuf_len = 32, eout_len = 0;
00233             u_char         *ebuf = (u_char *) malloc(ebuf_len);
00234 
00235             if (ebuf == NULL) {
00236                 fprintf(stderr, "malloc failure processing -3E flag.\n");
00237                 return (-1);
00238             }
00239             if (!snmp_hex_to_binary
00240                 (&ebuf, &ebuf_len, &eout_len, 1, optarg)) {
00241                 fprintf(stderr, "Bad engine ID value after -3E flag.\n");
00242                 SNMP_FREE(ebuf);
00243                 return (-1);
00244             }
00245             session->contextEngineID = ebuf;
00246             session->contextEngineIDLen = eout_len;
00247             break;
00248         }
00249 
00250     case 'n':
00251         session->contextName = optarg;
00252         session->contextNameLen = strlen(optarg);
00253         break;
00254 
00255     case 'u':
00256         session->securityName = optarg;
00257         session->securityNameLen = strlen(optarg);
00258         break;
00259 
00260     case 'l':
00261         if (!strcasecmp(optarg, "noAuthNoPriv") || !strcmp(optarg, "1") ||
00262             !strcasecmp(optarg, "nanp")) {
00263             session->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
00264         } else if (!strcasecmp(optarg, "authNoPriv")
00265                    || !strcmp(optarg, "2") || !strcasecmp(optarg, "anp")) {
00266             session->securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
00267         } else if (!strcasecmp(optarg, "authPriv") || !strcmp(optarg, "3")
00268                    || !strcasecmp(optarg, "ap")) {
00269             session->securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
00270         } else {
00271             fprintf(stderr,
00272                     "Invalid security level specified after -3l flag: %s\n",
00273                     optarg);
00274             return (-1);
00275         }
00276 
00277         break;
00278 
00279 #ifdef NETSNMP_SECMOD_USM
00280     case 'a':
00281 #ifndef NETSNMP_DISABLE_MD5
00282         if (!strcasecmp(optarg, "MD5")) {
00283             session->securityAuthProto = usmHMACMD5AuthProtocol;
00284             session->securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
00285         } else
00286 #endif
00287             if (!strcasecmp(optarg, "SHA")) {
00288             session->securityAuthProto = usmHMACSHA1AuthProtocol;
00289             session->securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN;
00290         } else {
00291             fprintf(stderr,
00292                     "Invalid authentication protocol specified after -3a flag: %s\n",
00293                     optarg);
00294             return (-1);
00295         }
00296         break;
00297 
00298     case 'x':
00299         testcase = 0;
00300 #ifndef NETSNMP_DISABLE_DES
00301         if (!strcasecmp(optarg, "DES")) {
00302             session->securityPrivProto = usmDESPrivProtocol;
00303             session->securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN;
00304             testcase = 1;
00305         }
00306 #endif
00307 #ifdef HAVE_AES
00308         if (!strcasecmp(optarg, "AES128") ||
00309             strcasecmp(optarg, "AES")) {
00310             session->securityPrivProto = usmAES128PrivProtocol;
00311             session->securityPrivProtoLen = USM_PRIV_PROTO_AES128_LEN;
00312             testcase = 1;
00313         }
00314 #endif
00315         if (testcase == 0) {
00316             fprintf(stderr,
00317                     "Invalid privacy protocol specified after -3x flag: %s\n",
00318                     optarg);
00319             return (-1);
00320         }
00321         break;
00322 
00323     case 'A':
00324         *Apsz = optarg;
00325         break;
00326 
00327     case 'X':
00328         *Xpsz = optarg;
00329         break;
00330 #endif /* NETSNMP_SECMOD_USM */
00331 
00332     case 'm': {
00333         size_t bufSize = sizeof(session->securityAuthKey);
00334         u_char *tmpp = session->securityAuthKey;
00335         if (!snmp_hex_to_binary(&tmpp, &bufSize,
00336                                 &session->securityAuthKeyLen, 0, optarg)) {
00337             fprintf(stderr, "Bad key value after -3m flag.\n");
00338             return (-1);
00339         }
00340         break;
00341     }
00342 
00343     case 'M': {
00344         size_t bufSize = sizeof(session->securityPrivKey);
00345         u_char *tmpp = session->securityPrivKey;
00346         if (!snmp_hex_to_binary(&tmpp, &bufSize,
00347              &session->securityPrivKeyLen, 0, optarg)) {
00348             fprintf(stderr, "Bad key value after -3M flag.\n");
00349             return (-1);
00350         }
00351         break;
00352     }
00353 
00354     case 'k': {
00355         size_t          kbuf_len = 32, kout_len = 0;
00356         u_char         *kbuf = (u_char *) malloc(kbuf_len);
00357 
00358         if (kbuf == NULL) {
00359             fprintf(stderr, "malloc failure processing -3k flag.\n");
00360             return (-1);
00361         }
00362         if (!snmp_hex_to_binary
00363             (&kbuf, &kbuf_len, &kout_len, 1, optarg)) {
00364             fprintf(stderr, "Bad key value after -3k flag.\n");
00365             SNMP_FREE(kbuf);
00366             return (-1);
00367         }
00368         session->securityAuthLocalKey = kbuf;
00369         session->securityAuthLocalKeyLen = kout_len;
00370         break;
00371     }
00372 
00373     case 'K': {
00374         size_t          kbuf_len = 32, kout_len = 0;
00375         u_char         *kbuf = (u_char *) malloc(kbuf_len);
00376 
00377         if (kbuf == NULL) {
00378             fprintf(stderr, "malloc failure processing -3K flag.\n");
00379             return (-1);
00380         }
00381         if (!snmp_hex_to_binary
00382             (&kbuf, &kbuf_len, &kout_len, 1, optarg)) {
00383             fprintf(stderr, "Bad key value after -3K flag.\n");
00384             SNMP_FREE(kbuf);
00385             return (-1);
00386         }
00387         session->securityPrivLocalKey = kbuf;
00388         session->securityPrivLocalKeyLen = kout_len;
00389         break;
00390     }
00391         
00392     default:
00393         fprintf(stderr, "Unknown SNMPv3 option passed to -3: %c.\n", *cp);
00394         return -1;
00395     }
00396     return 0;
00397 }
00398 
00399 /*******************************************************************-o-******
00400  * setup_engineID
00401  *
00402  * Parameters:
00403  *      **eidp
00404  *       *text  Printable (?) text to be plugged into the snmpEngineID.
00405  *
00406  * Return:
00407  *      Length of allocated engineID string in bytes,  -OR-
00408  *      -1 on error.
00409  *
00410  *
00411  * Create an snmpEngineID using text and the local IP address.  If eidp
00412  * is defined, use it to return a pointer to the newly allocated data.
00413  * Otherwise, use the result to define engineID defined in this module.
00414  *
00415  * Line syntax:
00416  *      engineID <text> | NULL
00417  *
00418  * XXX  What if a node has multiple interfaces?
00419  * XXX  What if multiple engines all choose the same address?
00420  *      (answer:  You're screwed, because you might need a kul database
00421  *       which is dependant on the current engineID.  Enumeration and other
00422  *       tricks won't work). 
00423  */
00424 int
00425 setup_engineID(u_char ** eidp, const char *text)
00426 {
00427     int             enterpriseid = htonl(NETSNMP_ENTERPRISE_OID),
00428         netsnmpoid = htonl(NETSNMP_OID),
00429         localsetup = (eidp) ? 0 : 1;
00430 
00431     /*
00432      * Use local engineID if *eidp == NULL.  
00433      */
00434 #ifdef HAVE_GETHOSTNAME
00435     u_char          buf[SNMP_MAXBUF_SMALL];
00436     struct hostent *hent = NULL;
00437 #endif
00438     u_char         *bufp = NULL;
00439     size_t          len;
00440     int             localEngineIDType = engineIDType;
00441     int             tmpint;
00442     time_t          tmptime;
00443 
00444     engineIDIsSet = 1;
00445 
00446 #ifdef HAVE_GETHOSTNAME
00447 #ifdef AF_INET6
00448     /*
00449      * see if they selected IPV4 or IPV6 support 
00450      */
00451     if ((ENGINEID_TYPE_IPV6 == localEngineIDType) ||
00452         (ENGINEID_TYPE_IPV4 == localEngineIDType)) {
00453         /*
00454          * get the host name and save the information 
00455          */
00456         gethostname((char *) buf, sizeof(buf));
00457         hent = netsnmp_gethostbyname((char *) buf);
00458         if (hent && hent->h_addrtype == AF_INET6) {
00459             localEngineIDType = ENGINEID_TYPE_IPV6;
00460         } else {
00461             /*
00462              * Not IPV6 so we go with default 
00463              */
00464             localEngineIDType = ENGINEID_TYPE_IPV4;
00465         }
00466     }
00467 #else
00468     /*
00469      * No IPV6 support.  Check if they selected IPV6 engineID type.
00470      *  If so make it IPV4 instead 
00471      */
00472     if (ENGINEID_TYPE_IPV6 == localEngineIDType) {
00473         localEngineIDType = ENGINEID_TYPE_IPV4;
00474     }
00475     if (ENGINEID_TYPE_IPV4 == localEngineIDType) {
00476         /*
00477          * get the host name and save the information 
00478          */
00479         gethostname((char *) buf, sizeof(buf));
00480         hent = netsnmp_gethostbyname((char *) buf);
00481     }
00482 #endif
00483 #endif                          /* HAVE_GETHOSTNAME */
00484 
00485     /*
00486      * Determine if we have text and if so setup our localEngineIDType
00487      * * appropriately.  
00488      */
00489     if (NULL != text) {
00490         engineIDType = localEngineIDType = ENGINEID_TYPE_TEXT;
00491     }
00492     /*
00493      * Determine length of the engineID string. 
00494      */
00495     len = 5;                    /* always have 5 leading bytes */
00496     switch (localEngineIDType) {
00497     case ENGINEID_TYPE_TEXT:
00498         if (NULL == text) {
00499             snmp_log(LOG_ERR,
00500                      "Can't set up engineID of type text from an empty string.\n");
00501             return -1;
00502         }
00503         len += strlen(text);    /* 5 leading bytes+text. No NULL char */
00504         break;
00505 #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR)
00506     case ENGINEID_TYPE_MACADDR:        /* MAC address */
00507         len += 6;               /* + 6 bytes for MAC address */
00508         break;
00509 #endif
00510     case ENGINEID_TYPE_IPV4:   /* IPv4 */
00511         len += 4;               /* + 4 byte IPV4 address */
00512         break;
00513     case ENGINEID_TYPE_IPV6:   /* IPv6 */
00514         len += 16;              /* + 16 byte IPV6 address */
00515         break;
00516     case ENGINEID_TYPE_NETSNMP_RND:        /* Net-SNMP specific encoding */
00517         if (engineID)           /* already setup, keep current value */
00518             return engineIDLength;
00519         if (oldEngineID) {
00520             len = oldEngineIDLength;
00521         } else {
00522             len += sizeof(int) + sizeof(time_t);
00523         }
00524         break;
00525     default:
00526         snmp_log(LOG_ERR,
00527                  "Unknown EngineID type requested for setup (%d).  Using IPv4.\n",
00528                  localEngineIDType);
00529         localEngineIDType = ENGINEID_TYPE_IPV4; /* make into IPV4 */
00530         len += 4;               /* + 4 byte IPv4 address */
00531         break;
00532     }                           /* switch */
00533 
00534 
00535     /*
00536      * Allocate memory and store enterprise ID.
00537      */
00538     if ((bufp = (u_char *) malloc(len)) == NULL) {
00539         snmp_log_perror("setup_engineID malloc");
00540         return -1;
00541     }
00542     if (localEngineIDType == ENGINEID_TYPE_NETSNMP_RND)
00543         /*
00544          * we must use the net-snmp enterprise id here, regardless 
00545          */
00546         memcpy(bufp, &netsnmpoid, sizeof(netsnmpoid));    /* XXX Must be 4 bytes! */
00547     else
00548         memcpy(bufp, &enterpriseid, sizeof(enterpriseid));      /* XXX Must be 4 bytes! */
00549 
00550     bufp[0] |= 0x80;
00551 
00552 
00553     /*
00554      * Store the given text  -OR-   the first found IP address
00555      *  -OR-  the MAC address  -OR-  random elements
00556      * (the latter being the recommended default)
00557      */
00558     switch (localEngineIDType) {
00559     case ENGINEID_TYPE_NETSNMP_RND:
00560         if (oldEngineID) {
00561             /*
00562              * keep our previous notion of the engineID 
00563              */
00564             memcpy(bufp, oldEngineID, oldEngineIDLength);
00565         } else {
00566             /*
00567              * Here we've desigend our own ENGINEID that is not based on
00568              * an address which may change and may even become conflicting
00569              * in the future like most of the default v3 engineID types
00570              * suffer from.
00571              * 
00572              * Ours is built from 2 fairly random elements: a random number and
00573              * the current time in seconds.  This method suffers from boxes
00574              * that may not have a correct clock setting and random number
00575              * seed at startup, but few OSes should have that problem.
00576              */
00577             bufp[4] = ENGINEID_TYPE_NETSNMP_RND;
00578             tmpint = random();
00579             memcpy(bufp + 5, &tmpint, sizeof(tmpint));
00580             tmptime = time(NULL);
00581             memcpy(bufp + 5 + sizeof(tmpint), &tmptime, sizeof(tmptime));
00582         }
00583         break;
00584     case ENGINEID_TYPE_TEXT:
00585         bufp[4] = ENGINEID_TYPE_TEXT;
00586         memcpy((char *) bufp + 5, (text), strlen(text));
00587         break;
00588 #ifdef HAVE_GETHOSTNAME
00589 #ifdef AF_INET6
00590     case ENGINEID_TYPE_IPV6:
00591         bufp[4] = ENGINEID_TYPE_IPV6;
00592         memcpy(bufp + 5, hent->h_addr_list[0], hent->h_length);
00593         break;
00594 #endif
00595 #endif
00596 #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR)
00597     case ENGINEID_TYPE_MACADDR:
00598         {
00599             int             x;
00600             bufp[4] = ENGINEID_TYPE_MACADDR;
00601             /*
00602              * use default NIC if none provided 
00603              */
00604             if (NULL == engineIDNic) {
00605               x = getHwAddress(DEFAULT_NIC, (char *)&bufp[5]);
00606             } else {
00607               x = getHwAddress((char *)engineIDNic, (char *)&bufp[5]);
00608             }
00609             if (0 != x)
00610                 /*
00611                  * function failed fill MAC address with zeros 
00612                  */
00613             {
00614                 memset(&bufp[5], 0, 6);
00615             }
00616         }
00617         break;
00618 #endif
00619     case ENGINEID_TYPE_IPV4:
00620     default:
00621         bufp[4] = ENGINEID_TYPE_IPV4;
00622 #ifdef HAVE_GETHOSTNAME
00623         if (hent && hent->h_addrtype == AF_INET) {
00624             memcpy(bufp + 5, hent->h_addr_list[0], hent->h_length);
00625         } else {                /* Unknown address type.  Default to 127.0.0.1. */
00626 
00627             bufp[5] = 127;
00628             bufp[6] = 0;
00629             bufp[7] = 0;
00630             bufp[8] = 1;
00631         }
00632 #else                           /* HAVE_GETHOSTNAME */
00633         /*
00634          * Unknown address type.  Default to 127.0.0.1. 
00635          */
00636         bufp[5] = 127;
00637         bufp[6] = 0;
00638         bufp[7] = 0;
00639         bufp[8] = 1;
00640 #endif                          /* HAVE_GETHOSTNAME */
00641         break;
00642     }
00643 
00644     /*
00645      * Pass the string back to the calling environment, or use it for
00646      * our local engineID.
00647      */
00648     if (localsetup) {
00649         SNMP_FREE(engineID);
00650         engineID = bufp;
00651         engineIDLength = len;
00652 
00653     } else {
00654         *eidp = bufp;
00655     }
00656 
00657 
00658     return len;
00659 
00660 }                               /* end setup_engineID() */
00661 
00662 int
00663 free_engineID(int majorid, int minorid, void *serverarg,
00664               void *clientarg)
00665 {
00666     SNMP_FREE(engineID);
00667     SNMP_FREE(engineIDNic);
00668     SNMP_FREE(oldEngineID);
00669     engineIDIsSet = 0;
00670     return 0;
00671 }
00672 
00673 /*******************************************************************-o-******
00674  * engineBoots_conf
00675  *
00676  * Parameters:
00677  *      *word
00678  *      *cptr
00679  *
00680  * Line syntax:
00681  *      engineBoots <num_boots>
00682  */
00683 void
00684 engineBoots_conf(const char *word, char *cptr)
00685 {
00686     engineBoots = atoi(cptr) + 1;
00687     DEBUGMSGTL(("snmpv3", "engineBoots: %lu\n", engineBoots));
00688 }
00689 
00690 /*******************************************************************-o-******
00691  * engineIDType_conf
00692  *
00693  * Parameters:
00694  *      *word
00695  *      *cptr
00696  *
00697  * Line syntax:
00698  *      engineIDType <1 or 3>
00699  *              1 is default for IPv4 engine ID type.  Will automatically
00700  *                  chose between IPv4 & IPv6 if either 1 or 2 is specified.
00701  *              2 is for IPv6.
00702  *              3 is hardware (MAC) address, currently supported under Linux
00703  */
00704 void
00705 engineIDType_conf(const char *word, char *cptr)
00706 {
00707     engineIDType = atoi(cptr);
00708     /*
00709      * verify valid type selected 
00710      */
00711     switch (engineIDType) {
00712     case ENGINEID_TYPE_IPV4:   /* IPv4 */
00713     case ENGINEID_TYPE_IPV6:   /* IPv6 */
00714         /*
00715          * IPV? is always good 
00716          */
00717         break;
00718 #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR)
00719     case ENGINEID_TYPE_MACADDR:        /* MAC address */
00720         break;
00721 #endif
00722     default:
00723         /*
00724          * unsupported one chosen 
00725          */
00726         config_perror("Unsupported enginedIDType, forcing IPv4");
00727         engineIDType = ENGINEID_TYPE_IPV4;
00728     }
00729     DEBUGMSGTL(("snmpv3", "engineIDType: %d\n", engineIDType));
00730 }
00731 
00732 /*******************************************************************-o-******
00733  * engineIDNic_conf
00734  *
00735  * Parameters:
00736  *      *word
00737  *      *cptr
00738  *
00739  * Line syntax:
00740  *      engineIDNic <string>
00741  *              eth0 is default
00742  */
00743 void
00744 engineIDNic_conf(const char *word, char *cptr)
00745 {
00746     /*
00747      * Make sure they haven't already specified the engineID via the
00748      * * configuration file 
00749      */
00750     if (0 == engineIDIsSet)
00751         /*
00752          * engineID has NOT been set via configuration file 
00753          */
00754     {
00755         /*
00756          * See if already set if so erase & release it 
00757          */
00758         SNMP_FREE(engineIDNic);
00759         engineIDNic = (u_char *) malloc(strlen(cptr) + 1);
00760         if (NULL != engineIDNic) {
00761             strcpy((char *) engineIDNic, cptr);
00762             DEBUGMSGTL(("snmpv3", "Initializing engineIDNic: %s\n",
00763                         engineIDNic));
00764         } else {
00765             DEBUGMSGTL(("snmpv3",
00766                         "Error allocating memory for engineIDNic!\n"));
00767         }
00768     } else {
00769         DEBUGMSGTL(("snmpv3",
00770                     "NOT setting engineIDNic, engineID already set\n"));
00771     }
00772 }
00773 
00774 /*******************************************************************-o-******
00775  * engineID_conf
00776  *
00777  * Parameters:
00778  *      *word
00779  *      *cptr
00780  *
00781  * This function reads a string from the configuration file and uses that
00782  * string to initialize the engineID.  It's assumed to be human readable.
00783  */
00784 void
00785 engineID_conf(const char *word, char *cptr)
00786 {
00787     setup_engineID(NULL, cptr);
00788     DEBUGMSGTL(("snmpv3", "initialized engineID with: %s\n", cptr));
00789 }
00790 
00791 void
00792 version_conf(const char *word, char *cptr)
00793 {
00794     int valid = 0;
00795 #ifndef NETSNMP_DISABLE_SNMPV1
00796     if ((strcmp(cptr,  "1") == 0) ||
00797         (strcmp(cptr, "v1") == 0)) {
00798         netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SNMPVERSION, 
00799                            NETSNMP_DS_SNMP_VERSION_1);       /* bogus value */
00800         valid = 1;
00801     }
00802 #endif
00803 #ifndef NETSNMP_DISABLE_SNMPV2C
00804     if ((strcasecmp(cptr,  "2c") == 0) ||
00805                (strcasecmp(cptr, "v2c") == 0)) {
00806         netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SNMPVERSION, 
00807                            NETSNMP_DS_SNMP_VERSION_2c);
00808         valid = 1;
00809     }
00810 #endif
00811     if ((strcasecmp(cptr,  "3" ) == 0) ||
00812                (strcasecmp(cptr, "v3" ) == 0)) {
00813         netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SNMPVERSION, 
00814                            NETSNMP_DS_SNMP_VERSION_3);
00815         valid = 1;
00816     }
00817     if (!valid) {
00818         config_perror("Unknown version specification");
00819         return;
00820     }
00821     DEBUGMSGTL(("snmpv3", "set default version to %d\n",
00822                 netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
00823                                    NETSNMP_DS_LIB_SNMPVERSION)));
00824 }
00825 
00826 /*
00827  * oldengineID_conf(const char *, char *):
00828  * 
00829  * Reads a octet string encoded engineID into the oldEngineID and
00830  * oldEngineIDLen pointers.
00831  */
00832 void
00833 oldengineID_conf(const char *word, char *cptr)
00834 {
00835     read_config_read_octet_string(cptr, &oldEngineID, &oldEngineIDLength);
00836 }
00837 
00838 /*
00839  * exactEngineID_conf(const char *, char *):
00840  * 
00841  * Reads a octet string encoded engineID into the engineID and
00842  * engineIDLen pointers.
00843  */
00844 void
00845 exactEngineID_conf(const char *word, char *cptr)
00846 {
00847     read_config_read_octet_string(cptr, &engineID, &engineIDLength);
00848     if (engineIDLength > MAX_ENGINEID_LENGTH) {
00849         netsnmp_config_error(
00850             "exactEngineID '%s' too long; truncating to %d bytes",
00851             cptr, MAX_ENGINEID_LENGTH);
00852         engineID[MAX_ENGINEID_LENGTH - 1] = '\0';
00853         engineIDLength = MAX_ENGINEID_LENGTH;
00854     }
00855     engineIDIsSet = 1;
00856     engineIDType = ENGINEID_TYPE_EXACT;
00857 }
00858 
00859 
00860 /*
00861  * merely call 
00862  */
00863 netsnmp_feature_child_of(get_enginetime_alarm, netsnmp_unused)
00864 #ifndef NETSNMP_FEATURE_REMOVE_GET_ENGINETIME_ALARM
00865 void
00866 get_enginetime_alarm(unsigned int regnum, void *clientargs)
00867 {
00868     /* we do this every so (rarely) often just to make sure we watch
00869        wrapping of the times() output */
00870     snmpv3_local_snmpEngineTime();
00871 }
00872 #endif /* NETSNMP_FEATURE_REMOVE_GET_ENGINETIME_ALARM */
00873 
00874 /*******************************************************************-o-******
00875  * init_snmpv3
00876  *
00877  * Parameters:
00878  *      *type   Label for the config file "type" used by calling entity.
00879  *      
00880  * Set time and engineID.
00881  * Set parsing functions for config file tokens.
00882  * Initialize SNMP Crypto API (SCAPI).
00883  */
00884 void
00885 init_snmpv3(const char *type)
00886 {
00887 #if SNMP_USE_TIMES
00888   struct tms dummy;
00889 
00890   /* fixme: -1 is fault code... */
00891   snmpv3startClock = times(&dummy);
00892 
00893   /* remember how many ticks per second there are, since times() returns this */
00894 
00895   clockticks = sysconf(_SC_CLK_TCK);
00896 
00897 #endif /* SNMP_USE_TIMES */
00898 
00899     gettimeofday(&snmpv3starttime, NULL);
00900 
00901     if (!type)
00902         type = "__snmpapp__";
00903 
00904     /*
00905      * we need to be called back later 
00906      */
00907     snmp_register_callback(SNMP_CALLBACK_LIBRARY,
00908                            SNMP_CALLBACK_POST_READ_CONFIG,
00909                            init_snmpv3_post_config, NULL);
00910 
00911     snmp_register_callback(SNMP_CALLBACK_LIBRARY,
00912                            SNMP_CALLBACK_POST_PREMIB_READ_CONFIG,
00913                            init_snmpv3_post_premib_config, NULL);
00914     /*
00915      * we need to be called back later 
00916      */
00917     snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA,
00918                            snmpv3_store, (void *) strdup(type));
00919 
00920     /*
00921      * initialize submodules 
00922      */
00923     /*
00924      * NOTE: this must be after the callbacks are registered above,
00925      * since they need to be called before the USM callbacks. 
00926      */
00927     init_secmod();
00928 
00929     /*
00930      * register all our configuration handlers (ack, there's a lot) 
00931      */
00932 
00933     /*
00934      * handle engineID setup before everything else which may depend on it 
00935      */
00936     register_prenetsnmp_mib_handler(type, "engineID", engineID_conf, NULL,
00937                                     "string");
00938     register_prenetsnmp_mib_handler(type, "oldEngineID", oldengineID_conf,
00939                                     NULL, NULL);
00940     register_prenetsnmp_mib_handler(type, "exactEngineID", exactEngineID_conf,
00941                                     NULL, NULL);
00942     register_prenetsnmp_mib_handler(type, "engineIDType",
00943                                     engineIDType_conf, NULL, "num");
00944     register_prenetsnmp_mib_handler(type, "engineIDNic", engineIDNic_conf,
00945                                     NULL, "string");
00946     register_config_handler(type, "engineBoots", engineBoots_conf, NULL,
00947                             NULL);
00948 
00949     /*
00950      * default store config entries 
00951      */
00952     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defSecurityName",
00953                                NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECNAME);
00954     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defContext", 
00955                                NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CONTEXT);
00956     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defPassphrase",
00957                                NETSNMP_DS_LIBRARY_ID,
00958                                NETSNMP_DS_LIB_PASSPHRASE);
00959     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defAuthPassphrase",
00960                                NETSNMP_DS_LIBRARY_ID,
00961                                NETSNMP_DS_LIB_AUTHPASSPHRASE);
00962     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defPrivPassphrase",
00963                                NETSNMP_DS_LIBRARY_ID,
00964                                NETSNMP_DS_LIB_PRIVPASSPHRASE);
00965     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defAuthMasterKey",
00966                                NETSNMP_DS_LIBRARY_ID,
00967                                NETSNMP_DS_LIB_AUTHMASTERKEY);
00968     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defPrivMasterKey",
00969                                NETSNMP_DS_LIBRARY_ID,
00970                                NETSNMP_DS_LIB_PRIVMASTERKEY);
00971     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defAuthLocalizedKey",
00972                                NETSNMP_DS_LIBRARY_ID,
00973                                NETSNMP_DS_LIB_AUTHLOCALIZEDKEY);
00974     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defPrivLocalizedKey",
00975                                NETSNMP_DS_LIBRARY_ID,
00976                                NETSNMP_DS_LIB_PRIVLOCALIZEDKEY);
00977     register_config_handler("snmp", "defVersion", version_conf, NULL,
00978                             "1|2c|3");
00979 
00980     register_config_handler("snmp", "defSecurityLevel",
00981                             snmpv3_secLevel_conf, NULL,
00982                             "noAuthNoPriv|authNoPriv|authPriv");
00983 }
00984 
00985 /*
00986  * initializations for SNMPv3 to be called after the configuration files
00987  * have been read.
00988  */
00989 
00990 int
00991 init_snmpv3_post_config(int majorid, int minorid, void *serverarg,
00992                         void *clientarg)
00993 {
00994 
00995     size_t          engineIDLen;
00996     u_char         *c_engineID;
00997 
00998     c_engineID = snmpv3_generate_engineID(&engineIDLen);
00999 
01000     if (engineIDLen == 0 || !c_engineID) {
01001         /*
01002          * Somethine went wrong - help! 
01003          */
01004         SNMP_FREE(c_engineID);
01005         return SNMPERR_GENERR;
01006     }
01007 
01008     /*
01009      * if our engineID has changed at all, the boots record must be set to 1 
01010      */
01011     if (engineIDLen != oldEngineIDLength ||
01012         oldEngineID == NULL || c_engineID == NULL ||
01013         memcmp(oldEngineID, c_engineID, engineIDLen) != 0) {
01014         engineBoots = 1;
01015     }
01016 
01017 #ifdef NETSNMP_SECMOD_USM
01018     /*
01019      * for USM set our local engineTime in the LCD timing cache 
01020      */
01021     set_enginetime(c_engineID, engineIDLen,
01022                    snmpv3_local_snmpEngineBoots(),
01023                    snmpv3_local_snmpEngineTime(), TRUE);
01024 #endif /* NETSNMP_SECMOD_USM */
01025 
01026     SNMP_FREE(c_engineID);
01027     return SNMPERR_SUCCESS;
01028 }
01029 
01030 int
01031 init_snmpv3_post_premib_config(int majorid, int minorid, void *serverarg,
01032                                void *clientarg)
01033 {
01034     if (!engineIDIsSet)
01035         setup_engineID(NULL, NULL);
01036 
01037     return SNMPERR_SUCCESS;
01038 }
01039 
01040 /*******************************************************************-o-******
01041  * store_snmpv3
01042  *
01043  * Parameters:
01044  *      *type
01045  */
01046 int
01047 snmpv3_store(int majorID, int minorID, void *serverarg, void *clientarg)
01048 {
01049     char            line[SNMP_MAXBUF_SMALL];
01050     u_char          c_engineID[SNMP_MAXBUF_SMALL];
01051     int             engineIDLen;
01052     const char     *type = (const char *) clientarg;
01053 
01054     if (type == NULL)           /* should never happen, since the arg is ours */
01055         type = "unknown";
01056 
01057     sprintf(line, "engineBoots %ld", engineBoots);
01058     read_config_store(type, line);
01059 
01060     engineIDLen = snmpv3_get_engineID(c_engineID, SNMP_MAXBUF_SMALL);
01061 
01062     if (engineIDLen) {
01063         /*
01064          * store the engineID used for this run 
01065          */
01066         sprintf(line, "oldEngineID ");
01067         read_config_save_octet_string(line + strlen(line), c_engineID,
01068                                       engineIDLen);
01069         read_config_store(type, line);
01070     }
01071     return SNMPERR_SUCCESS;
01072 }                               /* snmpv3_store() */
01073 
01074 u_long
01075 snmpv3_local_snmpEngineBoots(void)
01076 {
01077     return engineBoots;
01078 }
01079 
01080 
01081 /*******************************************************************-o-******
01082  * snmpv3_get_engineID
01083  *
01084  * Parameters:
01085  *      *buf
01086  *       buflen
01087  *      
01088  * Returns:
01089  *      Length of engineID      On Success
01090  *      SNMPERR_GENERR          Otherwise.
01091  *
01092  *
01093  * Store engineID in buf; return the length.
01094  *
01095  */
01096 size_t
01097 snmpv3_get_engineID(u_char * buf, size_t buflen)
01098 {
01099     /*
01100      * Sanity check.
01101      */
01102     if (!buf || (buflen < engineIDLength)) {
01103         return 0;
01104     }
01105     if (!engineID) {
01106         return 0;
01107     }
01108 
01109     memcpy(buf, engineID, engineIDLength);
01110     return engineIDLength;
01111 
01112 }                               /* end snmpv3_get_engineID() */
01113 
01114 /*******************************************************************-o-******
01115  * snmpv3_clone_engineID
01116  *
01117  * Parameters:
01118  *      **dest
01119  *       *dest_len
01120  *       src
01121  *       srclen
01122  *      
01123  * Returns:
01124  *      Length of engineID      On Success
01125  *      0                       Otherwise.
01126  *
01127  *
01128  * Clones engineID, creates memory
01129  *
01130  */
01131 int
01132 snmpv3_clone_engineID(u_char ** dest, size_t * destlen, u_char * src,
01133                       size_t srclen)
01134 {
01135     if (!dest || !destlen)
01136         return 0;
01137 
01138     SNMP_FREE(*dest);
01139     *destlen = 0;
01140 
01141     if (srclen && src) {
01142         *dest = (u_char *) malloc(srclen);
01143         if (*dest == NULL)
01144             return 0;
01145         memmove(*dest, src, srclen);
01146         *destlen = srclen;
01147     }
01148     return *destlen;
01149 }                               /* end snmpv3_clone_engineID() */
01150 
01151 
01152 /*******************************************************************-o-******
01153  * snmpv3_generate_engineID
01154  *
01155  * Parameters:
01156  *      *length
01157  *      
01158  * Returns:
01159  *      Pointer to copy of engineID     On Success.
01160  *      NULL                            If malloc() or snmpv3_get_engineID()
01161  *                                              fail.
01162  *
01163  * Generates a malloced copy of our engineID.
01164  *
01165  * 'length' is set to the length of engineID  -OR-  < 0 on failure.
01166  */
01167 u_char         *
01168 snmpv3_generate_engineID(size_t * length)
01169 {
01170     u_char         *newID;
01171     newID = (u_char *) malloc(engineIDLength);
01172 
01173     if (newID) {
01174         *length = snmpv3_get_engineID(newID, engineIDLength);
01175     }
01176 
01177     if (*length == 0) {
01178         SNMP_FREE(newID);
01179         newID = NULL;
01180     }
01181 
01182     return newID;
01183 
01184 }                               /* end snmpv3_generate_engineID() */
01185 
01186 /*
01187  * snmpv3_local_snmpEngineTime(): return the number of seconds since the
01188  * snmpv3 engine last incremented engine_boots 
01189  */
01190 u_long
01191 snmpv3_local_snmpEngineTime(void)
01192 {
01193 #ifdef SNMP_USE_TIMES
01194   struct tms dummy;
01195   clock_t now = times(&dummy);
01196   /* fixme: -1 is fault code... */
01197   unsigned int result;
01198 
01199   if (now < snmpv3startClock) {
01200       result = UINT_MAX - (snmpv3startClock - now);
01201   } else {
01202       result = now - snmpv3startClock;
01203   }
01204   if (result < lastcalltime) {
01205       /* wrapped */
01206       wrapcounter++;
01207   }
01208   lastcalltime = result;
01209   result =  (UINT_MAX/clockticks)*wrapcounter + result/clockticks;
01210 
01211   return result;
01212 #else /* !SNMP_USE_TIMES */
01213 #ifdef NETSNMP_FEATURE_CHECKING
01214   netsnmp_feature_require(calculate_sectime_diff)
01215 #endif /* NETSNMP_FEATURE_CHECKING */
01216     struct timeval  now;
01217 
01218     gettimeofday(&now, NULL);
01219     return calculate_sectime_diff(&now, &snmpv3starttime);
01220 #endif /* HAVE_SYS_TIMES_H */
01221 }
01222 
01223 
01224 
01225 /*
01226  * Code only for Linux systems 
01227  */
01228 #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR)
01229 static int
01230 getHwAddress(const char *networkDevice, /* e.g. "eth0", "eth1" */
01231              char *addressOut)
01232 {                               /* return address. Len=IFHWADDRLEN */
01233     /*
01234      * getHwAddress(...)
01235      * *
01236      * *  This function will return a Network Interfaces Card's Hardware
01237      * *  address (aka MAC address).
01238      * *
01239      * *  Input Parameter(s):
01240      * *      networkDevice - a null terminated string with the name of a network
01241      * *                      device.  Examples: eth0, eth1, etc...
01242      * *
01243      * *  Output Parameter(s):
01244      * *      addressOut -    This is the binary value of the hardware address.
01245      * *                      This value is NOT converted into a hexadecimal string.
01246      * *                      The caller must pre-allocate for a return value of
01247      * *                      length IFHWADDRLEN
01248      * *
01249      * *  Return value:   This function will return zero (0) for success.  If
01250      * *                  an error occurred the function will return -1.
01251      * *
01252      * *  Caveats:    This has only been tested on Ethernet networking cards.
01253      */
01254     int             sock;       /* our socket */
01255     struct ifreq    request;    /* struct which will have HW address */
01256 
01257     if ((NULL == networkDevice) || (NULL == addressOut)) {
01258         return -1;
01259     }
01260     /*
01261      * In order to find out the hardware (MAC) address of our system under
01262      * * Linux we must do the following:
01263      * * 1.  Create a socket
01264      * * 2.  Do an ioctl(...) call with the SIOCGIFHWADDRLEN operation.
01265      */
01266     sock = socket(AF_INET, SOCK_DGRAM, 0);
01267     if (sock < 0) {
01268         return -1;
01269     }
01270     /*
01271      * erase the request block 
01272      */
01273     memset(&request, 0, sizeof(request));
01274     /*
01275      * copy the name of the net device we want to find the HW address for 
01276      */
01277     strncpy(request.ifr_name, networkDevice, IFNAMSIZ - 1);
01278     /*
01279      * Get the HW address 
01280      */
01281     if (ioctl(sock, SIOCGIFHWADDR, &request)) {
01282         close(sock);
01283         return -1;
01284     }
01285     close(sock);
01286     memcpy(addressOut, request.ifr_hwaddr.sa_data, IFHWADDRLEN);
01287     return 0;
01288 }
01289 #endif
01290 
01291 #ifdef NETSNMP_ENABLE_TESTING_CODE
01292 /*
01293  * snmpv3_set_engineBootsAndTime(): this function does not exist.  Go away. 
01294  */
01295 /*
01296  * It certainly should never be used, unless in a testing scenero,
01297  * which is why it was created 
01298  */
01299 void
01300 snmpv3_set_engineBootsAndTime(int boots, int ttime)
01301 {
01302     engineBoots = boots;
01303     gettimeofday(&snmpv3starttime, NULL);
01304     snmpv3starttime.tv_sec -= ttime;
01305 }
01306 #endif