diff options
Diffstat (limited to 'snmplib/snmpv3.c')
-rw-r--r-- | snmplib/snmpv3.c | 1273 |
1 files changed, 1273 insertions, 0 deletions
diff --git a/snmplib/snmpv3.c b/snmplib/snmpv3.c new file mode 100644 index 0000000..435cafd --- /dev/null +++ b/snmplib/snmpv3.c @@ -0,0 +1,1273 @@ +/* + * snmpv3.c + */ + +#include <net-snmp/net-snmp-config.h> +#include <errno.h> +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#include <stdio.h> +#include <sys/types.h> + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif +#if HAVE_SYS_TIMES_H +#include <sys/times.h> +#endif +#if HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif +#include <ctype.h> +#if HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#if HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#if HAVE_NETDB_H +#include <netdb.h> +#endif +#if HAVE_STDLIB_H +# include <stdlib.h> +#endif + +/* + * Stuff needed for getHwAddress(...) + */ +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif +#ifdef HAVE_NET_IF_H +# include <net/if.h> +#endif + +#if HAVE_DMALLOC_H +#include <dmalloc.h> +#endif + +#include <net-snmp/types.h> +#include <net-snmp/output_api.h> +#include <net-snmp/config_api.h> +#include <net-snmp/utilities.h> + +#include <net-snmp/library/snmpv3.h> +#include <net-snmp/library/callback.h> +#include <net-snmp/library/snmp_api.h> +#include <net-snmp/library/lcd_time.h> +#include <net-snmp/library/scapi.h> +#include <net-snmp/library/keytools.h> +#include <net-snmp/library/lcd_time.h> +#include <net-snmp/library/snmp_secmod.h> +#include <net-snmp/library/snmpusm.h> +#include <net-snmp/library/transform_oids.h> + +#include <net-snmp/net-snmp-features.h> + +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; + +#if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR) +static int getHwAddress(const char *networkDevice, char *addressOut); +#endif + +/*******************************************************************-o-****** + * snmpv3_secLevel_conf + * + * Parameters: + * *word + * *cptr + * + * Line syntax: + * defSecurityLevel "noAuthNoPriv" | "authNoPriv" | "authPriv" + */ + +int +parse_secLevel_conf(const char *word, char *cptr) { + if (strcasecmp(cptr, "noAuthNoPriv") == 0 || strcmp(cptr, "1") == 0 || + strcasecmp(cptr, "nanp") == 0) { + return SNMP_SEC_LEVEL_NOAUTH; + } else if (strcasecmp(cptr, "authNoPriv") == 0 || strcmp(cptr, "2") == 0 || + strcasecmp(cptr, "anp") == 0) { + return SNMP_SEC_LEVEL_AUTHNOPRIV; + } else if (strcasecmp(cptr, "authPriv") == 0 || strcmp(cptr, "3") == 0 || + strcasecmp(cptr, "ap") == 0) { + return SNMP_SEC_LEVEL_AUTHPRIV; + } else { + return -1; + } +} + +void +snmpv3_secLevel_conf(const char *word, char *cptr) +{ + int secLevel; + + if ((secLevel = parse_secLevel_conf( word, cptr )) >= 0 ) { + netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_SECLEVEL, secLevel); + } else { + netsnmp_config_error("Unknown security level: %s", cptr); + } + DEBUGMSGTL(("snmpv3", "default secLevel set to: %s = %d\n", cptr, + netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_SECLEVEL))); +} + + +NETSNMP_IMPORT int +snmpv3_options(char *optarg, netsnmp_session * session, char **Apsz, + char **Xpsz, int argc, char *const *argv); +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((unsigned char)(*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': + errno=0; + session->engineBoots = strtoul(optarg, &cp, 10); + if (errno || cp == optarg) { + fprintf(stderr, "Need engine boots value after -3Z flag.\n"); + return (-1); + } + if (*cp == ',') { + char *endptr; + cp++; + session->engineTime = strtoul(cp, &endptr, 10); + if (errno || cp == endptr) { + fprintf(stderr, "Need engine time after \"-3Z engineBoot,\".\n"); + return (-1); + } + } else { + fprintf(stderr, "Need engine time after \"-3Z engineBoot,\".\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; + +#ifdef NETSNMP_SECMOD_USM + case 'a': +#ifndef NETSNMP_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 NETSNMP_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; +#endif /* NETSNMP_SECMOD_USM */ + + 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 <text> | 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(NETSNMP_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 = netsnmp_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 = netsnmp_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: + if (NULL == text) { + snmp_log(LOG_ERR, + "Can't set up engineID of type text from an empty string.\n"); + return -1; + } + 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, (char *)&bufp[5]); + } else { + x = getHwAddress((char *)engineIDNic, (char *)&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); + engineIDIsSet = 0; + return 0; +} + +/*******************************************************************-o-****** + * engineBoots_conf + * + * Parameters: + * *word + * *cptr + * + * Line syntax: + * engineBoots <num_boots> + */ +void +engineBoots_conf(const char *word, char *cptr) +{ + engineBoots = atoi(cptr) + 1; + DEBUGMSGTL(("snmpv3", "engineBoots: %lu\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 <string> + * 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 + */ + 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 NETSNMP_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 NETSNMP_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))); +} + +/* + * oldengineID_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); +} + +/* + * exactEngineID_conf(const char *, char *): + * + * Reads a octet string encoded engineID into the engineID and + * engineIDLen pointers. + */ +void +exactEngineID_conf(const char *word, char *cptr) +{ + read_config_read_octet_string(cptr, &engineID, &engineIDLength); + if (engineIDLength > MAX_ENGINEID_LENGTH) { + netsnmp_config_error( + "exactEngineID '%s' too long; truncating to %d bytes", + cptr, MAX_ENGINEID_LENGTH); + engineID[MAX_ENGINEID_LENGTH - 1] = '\0'; + engineIDLength = MAX_ENGINEID_LENGTH; + } + engineIDIsSet = 1; + engineIDType = ENGINEID_TYPE_EXACT; +} + + +/* + * merely call + */ +netsnmp_feature_child_of(get_enginetime_alarm, netsnmp_unused) +#ifndef NETSNMP_FEATURE_REMOVE_GET_ENGINETIME_ALARM +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(); +} +#endif /* NETSNMP_FEATURE_REMOVE_GET_ENGINETIME_ALARM */ + +/*******************************************************************-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) +{ + netsnmp_get_monotonic_clock(&snmpv3starttime); + + 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)); + + /* + * 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, "exactEngineID", exactEngineID_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", "defSecurityLevel", + snmpv3_secLevel_conf, NULL, + "noAuthNoPriv|authNoPriv|authPriv"); +} + +/* + * 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 || !c_engineID) { + /* + * Somethine went wrong - help! + */ + SNMP_FREE(c_engineID); + return SNMPERR_GENERR; + } + + /* + * if our engineID has changed at all, the boots record must be set to 1 + */ + if (engineIDLen != oldEngineIDLength || + oldEngineID == NULL || c_engineID == NULL || + memcmp(oldEngineID, c_engineID, engineIDLen) != 0) { + engineBoots = 1; + } + +#ifdef NETSNMP_SECMOD_USM + /* + * for USM set our local engineTime in the LCD timing cache + */ + set_enginetime(c_engineID, engineIDLen, + snmpv3_local_snmpEngineBoots(), + snmpv3_local_snmpEngineTime(), TRUE); +#endif /* NETSNMP_SECMOD_USM */ + + 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; + } + if (!engineID) { + 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; + + SNMP_FREE(*dest); + *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() */ + +/** + * Return the value of snmpEngineTime. According to RFC 3414 snmpEngineTime + * is a 31-bit counter. engineBoots must be incremented every time that + * counter wraps around. + * + * @see See also <a href="http://tools.ietf.org/html/rfc3414">RFC 3414</a>. + * + * @note It is assumed that this function is called at least once every + * 2**31 seconds. + */ +u_long +snmpv3_local_snmpEngineTime(void) +{ +#ifdef NETSNMP_FEATURE_CHECKING + netsnmp_feature_require(calculate_sectime_diff) +#endif /* NETSNMP_FEATURE_CHECKING */ + + static uint32_t last_engineTime; + struct timeval now; + uint32_t engineTime; + + netsnmp_get_monotonic_clock(&now); + engineTime = calculate_sectime_diff(&now, &snmpv3starttime) & 0x7fffffffL; + if (engineTime < last_engineTime) + engineBoots++; + last_engineTime = engineTime; + return engineTime; +} + + + +/* + * 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 + */ + strlcpy(request.ifr_name, networkDevice, IFNAMSIZ); + /* + * 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 NETSNMP_ENABLE_TESTING_CODE +/** + * Set SNMPv3 engineBoots and start time. + * + * @note This function does not exist. Go away. It certainly should never be + * used, unless in a testing scenario, which is why it was created + */ +void +snmpv3_set_engineBootsAndTime(int boots, int ttime) +{ + engineBoots = boots; + netsnmp_get_monotonic_clock(&snmpv3starttime); + snmpv3starttime.tv_sec -= ttime; +} +#endif |