blob: 435cafd23c1dbec1c1110bb279a2aec15a359187 [file] [log] [blame]
/*
* 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