/* * snmpv3.c */ #include #ifdef HAVE_LIMITS_H #include #endif #include #include #if TIME_WITH_SYS_TIME # ifdef WIN32 # include # else # include # endif # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_SYS_TIMES_H #include #endif #if HAVE_STRING_H #include #else #include #endif #include #if HAVE_NETINET_IN_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_WINSOCK_H #include #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_NETDB_H #include #endif #if HAVE_STDLIB_H # include #endif /* * Stuff needed for getHwAddress(...) */ #ifdef HAVE_SYS_IOCTL_H # include #endif #ifdef HAVE_NET_IF_H # include #endif #if HAVE_DMALLOC_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include static u_long engineBoots = 1; static unsigned int engineIDType = ENGINEID_TYPE_NETSNMP_RND; static unsigned char *engineID = NULL; static size_t engineIDLength = 0; static unsigned char *engineIDNic = NULL; static unsigned int engineIDIsSet = 0; /* flag if ID set by config */ static unsigned char *oldEngineID = NULL; static size_t oldEngineIDLength = 0; static struct timeval snmpv3starttime; /* * Set up default snmpv3 parameter value storage. */ static const oid *defaultAuthType = NULL; static size_t defaultAuthTypeLen = 0; static const oid *defaultPrivType = NULL; static size_t defaultPrivTypeLen = 0; /* this is probably an over-kill ifdef, but why not */ #if defined(HAVE_SYS_TIMES_H) && defined(HAVE_UNISTD_H) && defined(HAVE_TIMES) && defined(_SC_CLK_TCK) && defined(HAVE_SYSCONF) && defined(UINT_MAX) #define SNMP_USE_TIMES 1 static clock_t snmpv3startClock; static long clockticks = 0; static unsigned int lastcalltime = 0; static unsigned int wrapcounter = 0; #endif /* times() tests */ #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR) static int getHwAddress(const char *networkDevice, char *addressOut); #endif void snmpv3_authtype_conf(const char *word, char *cptr) { #ifndef DISABLE_MD5 if (strcasecmp(cptr, "MD5") == 0) defaultAuthType = usmHMACMD5AuthProtocol; else #endif if (strcasecmp(cptr, "SHA") == 0) defaultAuthType = usmHMACSHA1AuthProtocol; else config_perror("Unknown authentication type"); defaultAuthTypeLen = USM_LENGTH_OID_TRANSFORM; DEBUGMSGTL(("snmpv3", "set default authentication type: %s\n", cptr)); } const oid * get_default_authtype(size_t * len) { if (defaultAuthType == NULL) { defaultAuthType = SNMP_DEFAULT_AUTH_PROTO; defaultAuthTypeLen = SNMP_DEFAULT_AUTH_PROTOLEN; } if (len) *len = defaultAuthTypeLen; return defaultAuthType; } void snmpv3_privtype_conf(const char *word, char *cptr) { int testcase = 0; #ifndef DISABLE_DES if (strcasecmp(cptr, "DES") == 0) { testcase = 1; defaultPrivType = usmDESPrivProtocol; } #endif #if HAVE_AES /* XXX AES: assumes oid length == des oid length */ if (strcasecmp(cptr, "AES128") == 0 || strcasecmp(cptr, "AES") == 0) { testcase = 1; defaultPrivType = usmAES128PrivProtocol; } #endif if (testcase == 0) config_perror("Unknown privacy type"); defaultPrivTypeLen = SNMP_DEFAULT_PRIV_PROTOLEN; DEBUGMSGTL(("snmpv3", "set default privacy type: %s\n", cptr)); } const oid * get_default_privtype(size_t * len) { if (defaultPrivType == NULL) { #ifndef DISABLE_DES defaultPrivType = usmDESPrivProtocol; #else defaultPrivType = usmAESPrivProtocol; #endif defaultPrivTypeLen = USM_LENGTH_OID_TRANSFORM; } if (len) *len = defaultPrivTypeLen; return defaultPrivType; } /*******************************************************************-o-****** * snmpv3_secLevel_conf * * Parameters: * *word * *cptr * * Line syntax: * defSecurityLevel "noAuthNoPriv" | "authNoPriv" | "authPriv" */ void snmpv3_secLevel_conf(const char *word, char *cptr) { char buf[1024]; if (strcasecmp(cptr, "noAuthNoPriv") == 0 || strcmp(cptr, "1") == 0 || strcasecmp(cptr, "nanp") == 0) { netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECLEVEL, SNMP_SEC_LEVEL_NOAUTH); } else if (strcasecmp(cptr, "authNoPriv") == 0 || strcmp(cptr, "2") == 0 || strcasecmp(cptr, "anp") == 0) { netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECLEVEL, SNMP_SEC_LEVEL_AUTHNOPRIV); } else if (strcasecmp(cptr, "authPriv") == 0 || strcmp(cptr, "3") == 0 || strcasecmp(cptr, "ap") == 0) { netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECLEVEL, SNMP_SEC_LEVEL_AUTHPRIV); } else { snprintf(buf, sizeof(buf), "Unknown security level: %s", cptr); buf[ sizeof(buf)-1 ] = 0; config_perror(buf); } DEBUGMSGTL(("snmpv3", "default secLevel set to: %s = %d\n", cptr, netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECLEVEL))); } int snmpv3_options(char *optarg, netsnmp_session * session, char **Apsz, char **Xpsz, int argc, char *const *argv) { char *cp = optarg; int testcase; optarg++; /* * Support '... -3x=value ....' syntax */ if (*optarg == '=') { optarg++; } /* * and '.... "-3x value" ....' (*with* the quotes) */ while (*optarg && isspace(*optarg)) { optarg++; } /* * Finally, handle ".... -3x value ...." syntax * (*without* surrounding quotes) */ if (!*optarg) { /* * We've run off the end of the argument * so move on the the next. */ optarg = argv[optind++]; if (optind > argc) { fprintf(stderr, "Missing argument after SNMPv3 '-3%c' option.\n", *cp); return (-1); } } switch (*cp) { case 'Z': session->engineBoots = strtoul(optarg, NULL, 10); if (session->engineBoots == 0 || !isdigit(optarg[0])) { fprintf(stderr, "Need engine boots value after -3Z flag.\n"); return (-1); } cp = strchr(optarg, ','); if (cp && *(++cp) && isdigit(*cp)) session->engineTime = strtoul(cp, NULL, 10); else { fprintf(stderr, "Need engine time value after -3Z flag.\n"); return (-1); } break; case 'e':{ size_t ebuf_len = 32, eout_len = 0; u_char *ebuf = (u_char *) malloc(ebuf_len); if (ebuf == NULL) { fprintf(stderr, "malloc failure processing -3e flag.\n"); return (-1); } if (!snmp_hex_to_binary (&ebuf, &ebuf_len, &eout_len, 1, optarg)) { fprintf(stderr, "Bad engine ID value after -3e flag.\n"); SNMP_FREE(ebuf); return (-1); } session->securityEngineID = ebuf; session->securityEngineIDLen = eout_len; break; } case 'E':{ size_t ebuf_len = 32, eout_len = 0; u_char *ebuf = (u_char *) malloc(ebuf_len); if (ebuf == NULL) { fprintf(stderr, "malloc failure processing -3E flag.\n"); return (-1); } if (!snmp_hex_to_binary (&ebuf, &ebuf_len, &eout_len, 1, optarg)) { fprintf(stderr, "Bad engine ID value after -3E flag.\n"); SNMP_FREE(ebuf); return (-1); } session->contextEngineID = ebuf; session->contextEngineIDLen = eout_len; break; } case 'n': session->contextName = optarg; session->contextNameLen = strlen(optarg); break; case 'u': session->securityName = optarg; session->securityNameLen = strlen(optarg); break; case 'l': if (!strcasecmp(optarg, "noAuthNoPriv") || !strcmp(optarg, "1") || !strcasecmp(optarg, "nanp")) { session->securityLevel = SNMP_SEC_LEVEL_NOAUTH; } else if (!strcasecmp(optarg, "authNoPriv") || !strcmp(optarg, "2") || !strcasecmp(optarg, "anp")) { session->securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; } else if (!strcasecmp(optarg, "authPriv") || !strcmp(optarg, "3") || !strcasecmp(optarg, "ap")) { session->securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; } else { fprintf(stderr, "Invalid security level specified after -3l flag: %s\n", optarg); return (-1); } break; case 'a': #ifndef DISABLE_MD5 if (!strcasecmp(optarg, "MD5")) { session->securityAuthProto = usmHMACMD5AuthProtocol; session->securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN; } else #endif if (!strcasecmp(optarg, "SHA")) { session->securityAuthProto = usmHMACSHA1AuthProtocol; session->securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN; } else { fprintf(stderr, "Invalid authentication protocol specified after -3a flag: %s\n", optarg); return (-1); } break; case 'x': testcase = 0; #ifndef DISABLE_DES if (!strcasecmp(optarg, "DES")) { session->securityPrivProto = usmDESPrivProtocol; session->securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN; testcase = 1; } #endif #ifdef HAVE_AES if (!strcasecmp(optarg, "AES128") || strcasecmp(optarg, "AES")) { session->securityPrivProto = usmAES128PrivProtocol; session->securityPrivProtoLen = USM_PRIV_PROTO_AES128_LEN; testcase = 1; } #endif if (testcase == 0) { fprintf(stderr, "Invalid privacy protocol specified after -3x flag: %s\n", optarg); return (-1); } break; case 'A': *Apsz = optarg; break; case 'X': *Xpsz = optarg; break; case 'm': { size_t bufSize = sizeof(session->securityAuthKey); u_char *tmpp = session->securityAuthKey; if (!snmp_hex_to_binary(&tmpp, &bufSize, &session->securityAuthKeyLen, 0, optarg)) { fprintf(stderr, "Bad key value after -3m flag.\n"); return (-1); } break; } case 'M': { size_t bufSize = sizeof(session->securityPrivKey); u_char *tmpp = session->securityPrivKey; if (!snmp_hex_to_binary(&tmpp, &bufSize, &session->securityPrivKeyLen, 0, optarg)) { fprintf(stderr, "Bad key value after -3M flag.\n"); return (-1); } break; } case 'k': { size_t kbuf_len = 32, kout_len = 0; u_char *kbuf = (u_char *) malloc(kbuf_len); if (kbuf == NULL) { fprintf(stderr, "malloc failure processing -3k flag.\n"); return (-1); } if (!snmp_hex_to_binary (&kbuf, &kbuf_len, &kout_len, 1, optarg)) { fprintf(stderr, "Bad key value after -3k flag.\n"); SNMP_FREE(kbuf); return (-1); } session->securityAuthLocalKey = kbuf; session->securityAuthLocalKeyLen = kout_len; break; } case 'K': { size_t kbuf_len = 32, kout_len = 0; u_char *kbuf = (u_char *) malloc(kbuf_len); if (kbuf == NULL) { fprintf(stderr, "malloc failure processing -3K flag.\n"); return (-1); } if (!snmp_hex_to_binary (&kbuf, &kbuf_len, &kout_len, 1, optarg)) { fprintf(stderr, "Bad key value after -3K flag.\n"); SNMP_FREE(kbuf); return (-1); } session->securityPrivLocalKey = kbuf; session->securityPrivLocalKeyLen = kout_len; break; } default: fprintf(stderr, "Unknown SNMPv3 option passed to -3: %c.\n", *cp); return -1; } return 0; } /*******************************************************************-o-****** * setup_engineID * * Parameters: * **eidp * *text Printable (?) text to be plugged into the snmpEngineID. * * Return: * Length of allocated engineID string in bytes, -OR- * -1 on error. * * * Create an snmpEngineID using text and the local IP address. If eidp * is defined, use it to return a pointer to the newly allocated data. * Otherwise, use the result to define engineID defined in this module. * * Line syntax: * engineID | NULL * * XXX What if a node has multiple interfaces? * XXX What if multiple engines all choose the same address? * (answer: You're screwed, because you might need a kul database * which is dependant on the current engineID. Enumeration and other * tricks won't work). */ int setup_engineID(u_char ** eidp, const char *text) { int enterpriseid = htonl(ENTERPRISE_OID), netsnmpoid = htonl(NETSNMP_OID), localsetup = (eidp) ? 0 : 1; /* * Use local engineID if *eidp == NULL. */ #ifdef HAVE_GETHOSTNAME u_char buf[SNMP_MAXBUF_SMALL]; struct hostent *hent = NULL; #endif u_char *bufp = NULL; size_t len; int localEngineIDType = engineIDType; int tmpint; time_t tmptime; engineIDIsSet = 1; #ifdef HAVE_GETHOSTNAME #ifdef AF_INET6 /* * see if they selected IPV4 or IPV6 support */ if ((ENGINEID_TYPE_IPV6 == localEngineIDType) || (ENGINEID_TYPE_IPV4 == localEngineIDType)) { /* * get the host name and save the information */ gethostname((char *) buf, sizeof(buf)); hent = gethostbyname((char *) buf); if (hent && hent->h_addrtype == AF_INET6) { localEngineIDType = ENGINEID_TYPE_IPV6; } else { /* * Not IPV6 so we go with default */ localEngineIDType = ENGINEID_TYPE_IPV4; } } #else /* * No IPV6 support. Check if they selected IPV6 engineID type. * If so make it IPV4 instead */ if (ENGINEID_TYPE_IPV6 == localEngineIDType) { localEngineIDType = ENGINEID_TYPE_IPV4; } if (ENGINEID_TYPE_IPV4 == localEngineIDType) { /* * get the host name and save the information */ gethostname((char *) buf, sizeof(buf)); hent = gethostbyname((char *) buf); } #endif #endif /* HAVE_GETHOSTNAME */ /* * Determine if we have text and if so setup our localEngineIDType * * appropriately. */ if (NULL != text) { engineIDType = localEngineIDType = ENGINEID_TYPE_TEXT; } /* * Determine length of the engineID string. */ len = 5; /* always have 5 leading bytes */ switch (localEngineIDType) { case ENGINEID_TYPE_TEXT: len += strlen(text); /* 5 leading bytes+text. No NULL char */ break; #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR) case ENGINEID_TYPE_MACADDR: /* MAC address */ len += 6; /* + 6 bytes for MAC address */ break; #endif case ENGINEID_TYPE_IPV4: /* IPv4 */ len += 4; /* + 4 byte IPV4 address */ break; case ENGINEID_TYPE_IPV6: /* IPv6 */ len += 16; /* + 16 byte IPV6 address */ break; case ENGINEID_TYPE_NETSNMP_RND: /* Net-SNMP specific encoding */ if (engineID) /* already setup, keep current value */ return engineIDLength; if (oldEngineID) { len = oldEngineIDLength; } else { len += sizeof(int) + sizeof(time_t); } break; default: snmp_log(LOG_ERR, "Unknown EngineID type requested for setup (%d). Using IPv4.\n", localEngineIDType); localEngineIDType = ENGINEID_TYPE_IPV4; /* make into IPV4 */ len += 4; /* + 4 byte IPv4 address */ break; } /* switch */ /* * Allocate memory and store enterprise ID. */ if ((bufp = (u_char *) malloc(len)) == NULL) { snmp_log_perror("setup_engineID malloc"); return -1; } if (localEngineIDType == ENGINEID_TYPE_NETSNMP_RND) /* * we must use the net-snmp enterprise id here, regardless */ memcpy(bufp, &netsnmpoid, sizeof(netsnmpoid)); /* XXX Must be 4 bytes! */ else memcpy(bufp, &enterpriseid, sizeof(enterpriseid)); /* XXX Must be 4 bytes! */ bufp[0] |= 0x80; /* * Store the given text -OR- the first found IP address * -OR- the MAC address -OR- random elements * (the latter being the recommended default) */ switch (localEngineIDType) { case ENGINEID_TYPE_NETSNMP_RND: if (oldEngineID) { /* * keep our previous notion of the engineID */ memcpy(bufp, oldEngineID, oldEngineIDLength); } else { /* * Here we've desigend our own ENGINEID that is not based on * an address which may change and may even become conflicting * in the future like most of the default v3 engineID types * suffer from. * * Ours is built from 2 fairly random elements: a random number and * the current time in seconds. This method suffers from boxes * that may not have a correct clock setting and random number * seed at startup, but few OSes should have that problem. */ bufp[4] = ENGINEID_TYPE_NETSNMP_RND; tmpint = random(); memcpy(bufp + 5, &tmpint, sizeof(tmpint)); tmptime = time(NULL); memcpy(bufp + 5 + sizeof(tmpint), &tmptime, sizeof(tmptime)); } break; case ENGINEID_TYPE_TEXT: bufp[4] = ENGINEID_TYPE_TEXT; memcpy((char *) bufp + 5, text, strlen(text)); break; #ifdef HAVE_GETHOSTNAME #ifdef AF_INET6 case ENGINEID_TYPE_IPV6: bufp[4] = ENGINEID_TYPE_IPV6; memcpy(bufp + 5, hent->h_addr_list[0], hent->h_length); break; #endif #endif #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR) case ENGINEID_TYPE_MACADDR: { int x; bufp[4] = ENGINEID_TYPE_MACADDR; /* * use default NIC if none provided */ if (NULL == engineIDNic) { x = getHwAddress(DEFAULT_NIC, &bufp[5]); } else { x = getHwAddress(engineIDNic, &bufp[5]); } if (0 != x) /* * function failed fill MAC address with zeros */ { memset(&bufp[5], 0, 6); } } break; #endif case ENGINEID_TYPE_IPV4: default: bufp[4] = ENGINEID_TYPE_IPV4; #ifdef HAVE_GETHOSTNAME if (hent && hent->h_addrtype == AF_INET) { memcpy(bufp + 5, hent->h_addr_list[0], hent->h_length); } else { /* Unknown address type. Default to 127.0.0.1. */ bufp[5] = 127; bufp[6] = 0; bufp[7] = 0; bufp[8] = 1; } #else /* HAVE_GETHOSTNAME */ /* * Unknown address type. Default to 127.0.0.1. */ bufp[5] = 127; bufp[6] = 0; bufp[7] = 0; bufp[8] = 1; #endif /* HAVE_GETHOSTNAME */ break; } /* * Pass the string back to the calling environment, or use it for * our local engineID. */ if (localsetup) { SNMP_FREE(engineID); engineID = bufp; engineIDLength = len; } else { *eidp = bufp; } return len; } /* end setup_engineID() */ int free_engineID(int majorid, int minorid, void *serverarg, void *clientarg) { SNMP_FREE(engineID); SNMP_FREE(engineIDNic); SNMP_FREE(oldEngineID); return 0; } int free_enginetime_on_shutdown(int majorid, int minorid, void *serverarg, void *clientarg) { DEBUGMSGTL(("snmpv3", "free enginetime callback called\n")); if (engineID != NULL) free_enginetime(engineID, engineIDLength); return 0; } void usm_parse_create_usmUser(const char *token, char *line) { char *cp; char buf[SNMP_MAXBUF_MEDIUM]; struct usmUser *newuser; u_char userKey[SNMP_MAXBUF_SMALL], *tmpp; size_t userKeyLen = SNMP_MAXBUF_SMALL; size_t privKeyLen = 0; size_t ret; int testcase; newuser = usm_create_user(); /* * READ: Security Name */ cp = copy_nword(line, buf, sizeof(buf)); /* * might be a -e ENGINEID argument */ if (strcmp(buf, "-e") == 0) { size_t ebuf_len = 32, eout_len = 0; u_char *ebuf = (u_char *) malloc(ebuf_len); if (ebuf == NULL) { config_perror("malloc failure processing -e flag"); usm_free_user(newuser); return; } /* * Get the specified engineid from the line. */ cp = copy_nword(cp, buf, sizeof(buf)); if (!snmp_hex_to_binary(&ebuf, &ebuf_len, &eout_len, 1, buf)) { config_perror("invalid EngineID argument to -e"); usm_free_user(newuser); SNMP_FREE(ebuf); return; } newuser->engineID = ebuf; newuser->engineIDLen = eout_len; cp = copy_nword(cp, buf, sizeof(buf)); } else { newuser->engineID = snmpv3_generate_engineID(&ret); if (ret == 0) { usm_free_user(newuser); return; } newuser->engineIDLen = ret; } newuser->secName = strdup(buf); newuser->name = strdup(buf); if (!cp) goto add; /* no authentication or privacy type */ /* * READ: Authentication Type */ #ifndef DISABLE_MD5 if (strncmp(cp, "MD5", 3) == 0) { memcpy(newuser->authProtocol, usmHMACMD5AuthProtocol, sizeof(usmHMACMD5AuthProtocol)); } else #endif if (strncmp(cp, "SHA", 3) == 0) { memcpy(newuser->authProtocol, usmHMACSHA1AuthProtocol, sizeof(usmHMACSHA1AuthProtocol)); } else { config_perror("Unknown authentication protocol"); usm_free_user(newuser); return; } cp = skip_token(cp); /* * READ: Authentication Pass Phrase or key */ if (!cp) { config_perror("no authentication pass phrase"); usm_free_user(newuser); return; } cp = copy_nword(cp, buf, sizeof(buf)); if (strcmp(buf,"-m") == 0) { /* a master key is specified */ cp = copy_nword(cp, buf, sizeof(buf)); ret = sizeof(userKey); tmpp = userKey; userKeyLen = 0; if (!snmp_hex_to_binary(&tmpp, &ret, &userKeyLen, 0, buf)) { config_perror("invalid key value argument to -m"); usm_free_user(newuser); return; } } else if (strcmp(buf,"-l") != 0) { /* a password is specified */ userKeyLen = sizeof(userKey); ret = generate_Ku(newuser->authProtocol, newuser->authProtocolLen, (u_char *) buf, strlen(buf), userKey, &userKeyLen); if (ret != SNMPERR_SUCCESS) { config_perror("could not generate the authentication key from the " "suppiled pass phrase."); usm_free_user(newuser); return; } } /* * And turn it into a localized key */ ret = sc_get_properlength(newuser->authProtocol, newuser->authProtocolLen); if (ret <= 0) { config_perror("Could not get proper authentication protocol key length"); return; } newuser->authKey = (u_char *) malloc(ret); if (strcmp(buf,"-l") == 0) { /* a local key is directly specified */ cp = copy_nword(cp, buf, sizeof(buf)); newuser->authKeyLen = 0; if (!snmp_hex_to_binary(&newuser->authKey, &ret, &newuser->authKeyLen, 0, buf)) { config_perror("invalid key value argument to -l"); usm_free_user(newuser); return; } if (ret != newuser->authKeyLen) { config_perror("improper key length to -l"); usm_free_user(newuser); return; } } else { newuser->authKeyLen = ret; ret = generate_kul(newuser->authProtocol, newuser->authProtocolLen, newuser->engineID, newuser->engineIDLen, userKey, userKeyLen, newuser->authKey, &newuser->authKeyLen); if (ret != SNMPERR_SUCCESS) { config_perror("could not generate localized authentication key " "(Kul) from the master key (Ku)."); usm_free_user(newuser); return; } } if (!cp) goto add; /* no privacy type (which is legal) */ /* * READ: Privacy Type */ testcase = 0; #ifndef DISABLE_DES if (strncmp(cp, "DES", 3) == 0) { memcpy(newuser->privProtocol, usmDESPrivProtocol, sizeof(usmDESPrivProtocol)); testcase = 1; /* DES uses a 128 bit key, 64 bits of which is a salt */ privKeyLen = 16; } #endif #ifdef HAVE_AES if (strncmp(cp, "AES128", 6) == 0 || strncmp(cp, "AES", 3) == 0) { memcpy(newuser->privProtocol, usmAESPrivProtocol, sizeof(usmAESPrivProtocol)); testcase = 1; privKeyLen = 16; } #endif if (testcase == 0) { config_perror("Unknown privacy protocol"); usm_free_user(newuser); return; } cp = skip_token(cp); /* * READ: Encryption Pass Phrase or key */ if (!cp) { /* * assume the same as the authentication key */ memdup(&newuser->privKey, newuser->authKey, newuser->authKeyLen); newuser->privKeyLen = newuser->authKeyLen; } else { cp = copy_nword(cp, buf, sizeof(buf)); if (strcmp(buf,"-m") == 0) { /* a master key is specified */ cp = copy_nword(cp, buf, sizeof(buf)); ret = sizeof(userKey); tmpp = userKey; userKeyLen = 0; if (!snmp_hex_to_binary(&tmpp, &ret, &userKeyLen, 0, buf)) { config_perror("invalid key value argument to -m"); usm_free_user(newuser); return; } } else if (strcmp(buf,"-l") != 0) { /* a password is specified */ userKeyLen = sizeof(userKey); ret = generate_Ku(newuser->authProtocol, newuser->authProtocolLen, (u_char *) buf, strlen(buf), userKey, &userKeyLen); if (ret != SNMPERR_SUCCESS) { config_perror("could not generate the privacy key from the " "suppiled pass phrase."); usm_free_user(newuser); return; } } /* * And turn it into a localized key */ ret = sc_get_properlength(newuser->authProtocol, newuser->authProtocolLen); if (ret < 0) { config_perror("could not get proper key length to use for the " "privacy algorithm."); usm_free_user(newuser); return; } newuser->privKey = (u_char *) malloc(ret); if (strcmp(buf,"-l") == 0) { /* a local key is directly specified */ cp = copy_nword(cp, buf, sizeof(buf)); newuser->privKeyLen = 0; if (!snmp_hex_to_binary(&newuser->privKey, &ret, &newuser->privKeyLen, 0, buf)) { config_perror("invalid key value argument to -l"); usm_free_user(newuser); return; } } else { newuser->privKeyLen = ret; ret = generate_kul(newuser->authProtocol, newuser->authProtocolLen, newuser->engineID, newuser->engineIDLen, userKey, userKeyLen, newuser->privKey, &newuser->privKeyLen); if (ret != SNMPERR_SUCCESS) { config_perror("could not generate localized privacy key " "(Kul) from the master key (Ku)."); usm_free_user(newuser); return; } } } if ((newuser->privKeyLen >= privKeyLen) || (privKeyLen == 0)){ newuser->privKeyLen = privKeyLen; } else { /* The privKey length is smaller than required by privProtocol */ usm_free_user(newuser); return; } add: usm_add_user(newuser); DEBUGMSGTL(("usmUser", "created a new user %s at ", newuser->secName)); DEBUGMSGHEX(("usmUser", newuser->engineID, newuser->engineIDLen)); DEBUGMSG(("usmUser", "\n")); } /*******************************************************************-o-****** * engineBoots_conf * * Parameters: * *word * *cptr * * Line syntax: * engineBoots */ void engineBoots_conf(const char *word, char *cptr) { engineBoots = atoi(cptr) + 1; DEBUGMSGTL(("snmpv3", "engineBoots: %d\n", engineBoots)); } /*******************************************************************-o-****** * engineIDType_conf * * Parameters: * *word * *cptr * * Line syntax: * engineIDType <1 or 3> * 1 is default for IPv4 engine ID type. Will automatically * chose between IPv4 & IPv6 if either 1 or 2 is specified. * 2 is for IPv6. * 3 is hardware (MAC) address, currently supported under Linux */ void engineIDType_conf(const char *word, char *cptr) { engineIDType = atoi(cptr); /* * verify valid type selected */ switch (engineIDType) { case ENGINEID_TYPE_IPV4: /* IPv4 */ case ENGINEID_TYPE_IPV6: /* IPv6 */ /* * IPV? is always good */ break; #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR) case ENGINEID_TYPE_MACADDR: /* MAC address */ break; #endif default: /* * unsupported one chosen */ config_perror("Unsupported enginedIDType, forcing IPv4"); engineIDType = ENGINEID_TYPE_IPV4; } DEBUGMSGTL(("snmpv3", "engineIDType: %d\n", engineIDType)); } /*******************************************************************-o-****** * engineIDNic_conf * * Parameters: * *word * *cptr * * Line syntax: * engineIDNic * eth0 is default */ void engineIDNic_conf(const char *word, char *cptr) { /* * Make sure they haven't already specified the engineID via the * * configuration file */ if (0 == engineIDIsSet) /* * engineID has NOT been set via configuration file */ { /* * See if already set if so erase & release it */ if (NULL != engineIDNic) { SNMP_FREE(engineIDNic); } engineIDNic = (u_char *) malloc(strlen(cptr) + 1); if (NULL != engineIDNic) { strcpy((char *) engineIDNic, cptr); DEBUGMSGTL(("snmpv3", "Initializing engineIDNic: %s\n", engineIDNic)); } else { DEBUGMSGTL(("snmpv3", "Error allocating memory for engineIDNic!\n")); } } else { DEBUGMSGTL(("snmpv3", "NOT setting engineIDNic, engineID already set\n")); } } /*******************************************************************-o-****** * engineID_conf * * Parameters: * *word * *cptr * * This function reads a string from the configuration file and uses that * string to initialize the engineID. It's assumed to be human readable. */ void engineID_conf(const char *word, char *cptr) { setup_engineID(NULL, cptr); DEBUGMSGTL(("snmpv3", "initialized engineID with: %s\n", cptr)); } void version_conf(const char *word, char *cptr) { int valid = 0; #ifndef DISABLE_SNMPV1 if ((strcmp(cptr, "1") == 0) || (strcmp(cptr, "v1") == 0)) { netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SNMPVERSION, NETSNMP_DS_SNMP_VERSION_1); /* bogus value */ valid = 1; } #endif #ifndef DISABLE_SNMPV2C if ((strcasecmp(cptr, "2c") == 0) || (strcasecmp(cptr, "v2c") == 0)) { netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SNMPVERSION, NETSNMP_DS_SNMP_VERSION_2c); valid = 1; } #endif if ((strcasecmp(cptr, "3" ) == 0) || (strcasecmp(cptr, "v3" ) == 0)) { netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SNMPVERSION, NETSNMP_DS_SNMP_VERSION_3); valid = 1; } if (!valid) { config_perror("Unknown version specification"); return; } DEBUGMSGTL(("snmpv3", "set default version to %d\n", netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SNMPVERSION))); } /* * engineID_old_conf(const char *, char *): * * Reads a octet string encoded engineID into the oldEngineID and * oldEngineIDLen pointers. */ void oldengineID_conf(const char *word, char *cptr) { read_config_read_octet_string(cptr, &oldEngineID, &oldEngineIDLength); } /* * merely call */ void get_enginetime_alarm(unsigned int regnum, void *clientargs) { /* we do this every so (rarely) often just to make sure we watch wrapping of the times() output */ snmpv3_local_snmpEngineTime(); } /*******************************************************************-o-****** * init_snmpv3 * * Parameters: * *type Label for the config file "type" used by calling entity. * * Set time and engineID. * Set parsing functions for config file tokens. * Initialize SNMP Crypto API (SCAPI). */ void init_snmpv3(const char *type) { #if SNMP_USE_TIMES struct tms dummy; /* fixme: -1 is fault code... */ snmpv3startClock = times(&dummy); /* remember how many ticks per second there are, since times() returns this */ clockticks = sysconf(_SC_CLK_TCK); #endif /* SNMP_USE_TIMES */ gettimeofday(&snmpv3starttime, NULL); if (!type) type = "__snmpapp__"; /* * we need to be called back later */ snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_POST_READ_CONFIG, init_snmpv3_post_config, NULL); snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_POST_PREMIB_READ_CONFIG, init_snmpv3_post_premib_config, NULL); /* * we need to be called back later */ snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA, snmpv3_store, (void *) strdup(type)); /* * Free stuff at shutdown time */ snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN, free_enginetime_on_shutdown, NULL); /* * initialize submodules */ /* * NOTE: this must be after the callbacks are registered above, * since they need to be called before the USM callbacks. */ init_secmod(); /* * register all our configuration handlers (ack, there's a lot) */ /* * handle engineID setup before everything else which may depend on it */ register_prenetsnmp_mib_handler(type, "engineID", engineID_conf, NULL, "string"); register_prenetsnmp_mib_handler(type, "oldEngineID", oldengineID_conf, NULL, NULL); register_prenetsnmp_mib_handler(type, "engineIDType", engineIDType_conf, NULL, "num"); register_prenetsnmp_mib_handler(type, "engineIDNic", engineIDNic_conf, NULL, "string"); register_config_handler(type, "engineBoots", engineBoots_conf, NULL, NULL); /* * default store config entries */ netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defSecurityName", NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECNAME); netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defContext", NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CONTEXT); netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defPassphrase", NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PASSPHRASE); netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defAuthPassphrase", NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_AUTHPASSPHRASE); netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defPrivPassphrase", NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRIVPASSPHRASE); netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defAuthMasterKey", NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_AUTHMASTERKEY); netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defPrivMasterKey", NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRIVMASTERKEY); netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defAuthLocalizedKey", NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_AUTHLOCALIZEDKEY); netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defPrivLocalizedKey", NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRIVLOCALIZEDKEY); register_config_handler("snmp", "defVersion", version_conf, NULL, "1|2c|3"); register_config_handler("snmp", "defAuthType", snmpv3_authtype_conf, NULL, "MD5|SHA"); register_config_handler("snmp", "defPrivType", snmpv3_privtype_conf, NULL, #ifdef HAVE_AES "DES (AES support not available)"); #else "DES|AES"); #endif register_config_handler("snmp", "defSecurityLevel", snmpv3_secLevel_conf, NULL, "noAuthNoPriv|authNoPriv|authPriv"); register_config_handler(type, "userSetAuthPass", usm_set_password, NULL, NULL); register_config_handler(type, "userSetPrivPass", usm_set_password, NULL, NULL); register_config_handler(type, "userSetAuthKey", usm_set_password, NULL, NULL); register_config_handler(type, "userSetPrivKey", usm_set_password, NULL, NULL); register_config_handler(type, "userSetAuthLocalKey", usm_set_password, NULL, NULL); register_config_handler(type, "userSetPrivLocalKey", usm_set_password, NULL, NULL); } /* * initializations for SNMPv3 to be called after the configuration files * have been read. */ int init_snmpv3_post_config(int majorid, int minorid, void *serverarg, void *clientarg) { size_t engineIDLen; u_char *c_engineID; c_engineID = snmpv3_generate_engineID(&engineIDLen); if (engineIDLen == 0) { /* * Somethine went wrong - help! */ return SNMPERR_GENERR; } /* * if our engineID has changed at all, the boots record must be set to 1 */ if (engineIDLen != (int) oldEngineIDLength || oldEngineID == NULL || c_engineID == NULL || memcmp(oldEngineID, c_engineID, engineIDLen) != 0) { engineBoots = 1; } /* * set our local engineTime in the LCD timing cache */ set_enginetime(c_engineID, engineIDLen, snmpv3_local_snmpEngineBoots(), snmpv3_local_snmpEngineTime(), TRUE); SNMP_FREE(c_engineID); return SNMPERR_SUCCESS; } int init_snmpv3_post_premib_config(int majorid, int minorid, void *serverarg, void *clientarg) { if (!engineIDIsSet) setup_engineID(NULL, NULL); return SNMPERR_SUCCESS; } /*******************************************************************-o-****** * store_snmpv3 * * Parameters: * *type */ int snmpv3_store(int majorID, int minorID, void *serverarg, void *clientarg) { char line[SNMP_MAXBUF_SMALL]; u_char c_engineID[SNMP_MAXBUF_SMALL]; int engineIDLen; const char *type = (const char *) clientarg; if (type == NULL) /* should never happen, since the arg is ours */ type = "unknown"; sprintf(line, "engineBoots %ld", engineBoots); read_config_store(type, line); engineIDLen = snmpv3_get_engineID(c_engineID, SNMP_MAXBUF_SMALL); if (engineIDLen) { /* * store the engineID used for this run */ sprintf(line, "oldEngineID "); read_config_save_octet_string(line + strlen(line), c_engineID, engineIDLen); read_config_store(type, line); } return SNMPERR_SUCCESS; } /* snmpv3_store() */ u_long snmpv3_local_snmpEngineBoots(void) { return engineBoots; } /*******************************************************************-o-****** * snmpv3_get_engineID * * Parameters: * *buf * buflen * * Returns: * Length of engineID On Success * SNMPERR_GENERR Otherwise. * * * Store engineID in buf; return the length. * */ size_t snmpv3_get_engineID(u_char * buf, size_t buflen) { /* * Sanity check. */ if (!buf || (buflen < engineIDLength)) { return 0; } memcpy(buf, engineID, engineIDLength); return engineIDLength; } /* end snmpv3_get_engineID() */ /*******************************************************************-o-****** * snmpv3_clone_engineID * * Parameters: * **dest * *dest_len * src * srclen * * Returns: * Length of engineID On Success * 0 Otherwise. * * * Clones engineID, creates memory * */ int snmpv3_clone_engineID(u_char ** dest, size_t * destlen, u_char * src, size_t srclen) { if (!dest || !destlen) return 0; if (*dest) { SNMP_FREE(*dest); *dest = NULL; } *destlen = 0; if (srclen && src) { *dest = (u_char *) malloc(srclen); if (*dest == NULL) return 0; memmove(*dest, src, srclen); *destlen = srclen; } return *destlen; } /* end snmpv3_clone_engineID() */ /*******************************************************************-o-****** * snmpv3_generate_engineID * * Parameters: * *length * * Returns: * Pointer to copy of engineID On Success. * NULL If malloc() or snmpv3_get_engineID() * fail. * * Generates a malloced copy of our engineID. * * 'length' is set to the length of engineID -OR- < 0 on failure. */ u_char * snmpv3_generate_engineID(size_t * length) { u_char *newID; newID = (u_char *) malloc(engineIDLength); if (newID) { *length = snmpv3_get_engineID(newID, engineIDLength); } if (*length == 0) { SNMP_FREE(newID); newID = NULL; } return newID; } /* end snmpv3_generate_engineID() */ /* * snmpv3_local_snmpEngineTime(): return the number of seconds since the * snmpv3 engine last incremented engine_boots */ u_long snmpv3_local_snmpEngineTime(void) { #ifdef SNMP_USE_TIMES struct tms dummy; clock_t now = times(&dummy); /* fixme: -1 is fault code... */ unsigned int result; if (now < snmpv3startClock) { result = UINT_MAX - (snmpv3startClock - now); } else { result = now - snmpv3startClock; } if (result < lastcalltime) { /* wrapped */ wrapcounter++; } lastcalltime = result; result = (UINT_MAX/clockticks)*wrapcounter + result/clockticks; return result; #else /* !SNMP_USE_TIMES */ struct timeval now; gettimeofday(&now, NULL); return calculate_sectime_diff(&now, &snmpv3starttime); #endif /* HAVE_SYS_TIMES_H */ } /* * Code only for Linux systems */ #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR) static int getHwAddress(const char *networkDevice, /* e.g. "eth0", "eth1" */ char *addressOut) { /* return address. Len=IFHWADDRLEN */ /* * getHwAddress(...) * * * * This function will return a Network Interfaces Card's Hardware * * address (aka MAC address). * * * * Input Parameter(s): * * networkDevice - a null terminated string with the name of a network * * device. Examples: eth0, eth1, etc... * * * * Output Parameter(s): * * addressOut - This is the binary value of the hardware address. * * This value is NOT converted into a hexadecimal string. * * The caller must pre-allocate for a return value of * * length IFHWADDRLEN * * * * Return value: This function will return zero (0) for success. If * * an error occurred the function will return -1. * * * * Caveats: This has only been tested on Ethernet networking cards. */ int sock; /* our socket */ struct ifreq request; /* struct which will have HW address */ if ((NULL == networkDevice) || (NULL == addressOut)) { return -1; } /* * In order to find out the hardware (MAC) address of our system under * * Linux we must do the following: * * 1. Create a socket * * 2. Do an ioctl(...) call with the SIOCGIFHWADDRLEN operation. */ sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { return -1; } /* * erase the request block */ memset(&request, 0, sizeof(request)); /* * copy the name of the net device we want to find the HW address for */ strncpy(request.ifr_name, networkDevice, IFNAMSIZ - 1); /* * Get the HW address */ if (ioctl(sock, SIOCGIFHWADDR, &request)) { close(sock); return -1; } close(sock); memcpy(addressOut, request.ifr_hwaddr.sa_data, IFHWADDRLEN); return 0; } #endif #ifdef SNMP_TESTING_CODE /* * snmpv3_set_engineBootsAndTime(): this function does not exist. Go away. */ /* * It certainly should never be used, unless in a testing scenero, * which is why it was created */ void snmpv3_set_engineBootsAndTime(int boots, int ttime) { engineBoots = boots; gettimeofday(&snmpv3starttime, NULL); snmpv3starttime.tv_sec -= ttime; } #endif