summaryrefslogtreecommitdiff
path: root/snmplib/snmpusm.c
diff options
context:
space:
mode:
Diffstat (limited to 'snmplib/snmpusm.c')
-rw-r--r--snmplib/snmpusm.c3801
1 files changed, 3801 insertions, 0 deletions
diff --git a/snmplib/snmpusm.c b/snmplib/snmpusm.c
new file mode 100644
index 0000000..5cfff50
--- /dev/null
+++ b/snmplib/snmpusm.c
@@ -0,0 +1,3801 @@
+/* Portions of this file are subject to the following copyright(s). See
+ * the Net-SNMP's COPYING file for more details and other copyrights
+ * that may apply:
+ */
+/*
+ * Portions of this file are copyrighted by:
+ * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms specified in the COPYING file
+ * distributed with the Net-SNMP package.
+ */
+/*
+ * snmpusm.c
+ *
+ * Routines to manipulate a information about a "user" as
+ * defined by the SNMP-USER-BASED-SM-MIB MIB.
+ *
+ * All functions usm_set_usmStateReference_*() return 0 on success, -1
+ * otherwise.
+ *
+ * !! Tab stops set to 4 in some parts of this file. !!
+ * (Designated on a per function.)
+ */
+
+#include <net-snmp/net-snmp-config.h>
+
+#include <sys/types.h>
+#if HAVE_WINSOCK_H
+#include <winsock.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if TIME_WITH_SYS_TIME
+# ifdef WIN32
+# include <sys/timeb.h>
+# else
+# include <sys/time.h>
+# endif
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.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/asn1.h>
+#include <net-snmp/library/snmp_api.h>
+#include <net-snmp/library/callback.h>
+#include <net-snmp/library/tools.h>
+#include <net-snmp/library/keytools.h>
+#include <net-snmp/library/snmpv3.h>
+#include <net-snmp/library/lcd_time.h>
+#include <net-snmp/library/scapi.h>
+#include <net-snmp/library/callback.h>
+#include <net-snmp/library/snmp_secmod.h>
+#include <net-snmp/library/snmpusm.h>
+
+oid usmNoAuthProtocol[10] = { 1, 3, 6, 1, 6, 3, 10, 1, 1, 1 };
+#ifndef NETSNMP_DISABLE_MD5
+oid usmHMACMD5AuthProtocol[10] =
+ { 1, 3, 6, 1, 6, 3, 10, 1, 1, 2 };
+#endif
+oid usmHMACSHA1AuthProtocol[10] =
+ { 1, 3, 6, 1, 6, 3, 10, 1, 1, 3 };
+oid usmNoPrivProtocol[10] = { 1, 3, 6, 1, 6, 3, 10, 1, 2, 1 };
+#ifndef NETSNMP_DISABLE_DES
+oid usmDESPrivProtocol[10] = { 1, 3, 6, 1, 6, 3, 10, 1, 2, 2 };
+#endif
+oid usmAESPrivProtocol[10] = { 1, 3, 6, 1, 6, 3, 10, 1, 2, 4 };
+/* backwards compat */
+oid *usmAES128PrivProtocol = usmAESPrivProtocol;
+
+static u_int dummy_etime, dummy_eboot; /* For ISENGINEKNOWN(). */
+
+/*
+ * Globals.
+ */
+static u_int salt_integer;
+#ifdef HAVE_AES
+static u_int salt_integer64_1, salt_integer64_2;
+#endif
+ /*
+ * 1/2 of seed for the salt. Cf. RFC2274, Sect 8.1.1.1.
+ */
+
+static struct usmUser *noNameUser = NULL;
+/*
+ * Local storage (LCD) of the default user list.
+ */
+static struct usmUser *userList = NULL;
+
+/*
+ * Prototypes
+ */
+int
+ usm_check_secLevel_vs_protocols(int level,
+ const oid * authProtocol,
+ u_int authProtocolLen,
+ const oid * privProtocol,
+ u_int privProtocolLen);
+int
+ usm_calc_offsets(size_t globalDataLen,
+ int secLevel, size_t secEngineIDLen,
+ size_t secNameLen, size_t scopedPduLen,
+ u_long engineboots, long engine_time,
+ size_t * theTotalLength,
+ size_t * authParamsOffset,
+ size_t * privParamsOffset,
+ size_t * dataOffset, size_t * datalen,
+ size_t * msgAuthParmLen,
+ size_t * msgPrivParmLen, size_t * otstlen,
+ size_t * seq_len, size_t * msgSecParmLen);
+/*
+ * Set a given field of the secStateRef.
+ *
+ * Allocate <len> bytes for type <type> pointed to by ref-><field>.
+ * Then copy in <item> and record its length in ref-><field_len>.
+ *
+ * Return 0 on success, -1 otherwise.
+ */
+#define MAKE_ENTRY( type, item, len, field, field_len ) \
+{ \
+ if (ref == NULL) \
+ return -1; \
+ if (ref->field != NULL) { \
+ SNMP_ZERO(ref->field, ref->field_len); \
+ SNMP_FREE(ref->field); \
+ } \
+ ref->field_len = 0; \
+ if (len == 0 || item == NULL) { \
+ return 0; \
+ } \
+ if ((ref->field = (type*) malloc (len * sizeof(type))) == NULL) \
+ { \
+ return -1; \
+ } \
+ \
+ memcpy (ref->field, item, len * sizeof(type)); \
+ ref->field_len = len; \
+ \
+ return 0; \
+}
+
+
+struct usmStateReference *
+usm_malloc_usmStateReference(void)
+{
+ struct usmStateReference *retval = (struct usmStateReference *)
+ calloc(1, sizeof(struct usmStateReference));
+
+ return retval;
+} /* end usm_malloc_usmStateReference() */
+
+
+void
+usm_free_usmStateReference(void *old)
+{
+ struct usmStateReference *old_ref = (struct usmStateReference *) old;
+
+ if (old_ref) {
+
+ SNMP_FREE(old_ref->usr_name);
+ SNMP_FREE(old_ref->usr_engine_id);
+ SNMP_FREE(old_ref->usr_auth_protocol);
+ SNMP_FREE(old_ref->usr_priv_protocol);
+
+ if (old_ref->usr_auth_key) {
+ SNMP_ZERO(old_ref->usr_auth_key, old_ref->usr_auth_key_length);
+ SNMP_FREE(old_ref->usr_auth_key);
+ }
+ if (old_ref->usr_priv_key) {
+ SNMP_ZERO(old_ref->usr_priv_key, old_ref->usr_priv_key_length);
+ SNMP_FREE(old_ref->usr_priv_key);
+ }
+
+ SNMP_ZERO(old_ref, sizeof(*old_ref));
+ SNMP_FREE(old_ref);
+
+ }
+
+} /* end usm_free_usmStateReference() */
+
+struct usmUser *
+usm_get_userList(void)
+{
+ return userList;
+}
+
+int
+usm_set_usmStateReference_name(struct usmStateReference *ref,
+ char *name, size_t name_len)
+{
+ MAKE_ENTRY(char, name, name_len, usr_name, usr_name_length);
+}
+
+int
+usm_set_usmStateReference_engine_id(struct usmStateReference *ref,
+ u_char * engine_id,
+ size_t engine_id_len)
+{
+ MAKE_ENTRY(u_char, engine_id, engine_id_len,
+ usr_engine_id, usr_engine_id_length);
+}
+
+int
+usm_set_usmStateReference_auth_protocol(struct usmStateReference *ref,
+ oid * auth_protocol,
+ size_t auth_protocol_len)
+{
+ MAKE_ENTRY(oid, auth_protocol, auth_protocol_len,
+ usr_auth_protocol, usr_auth_protocol_length);
+}
+
+int
+usm_set_usmStateReference_auth_key(struct usmStateReference *ref,
+ u_char * auth_key, size_t auth_key_len)
+{
+ MAKE_ENTRY(u_char, auth_key, auth_key_len,
+ usr_auth_key, usr_auth_key_length);
+}
+
+int
+usm_set_usmStateReference_priv_protocol(struct usmStateReference *ref,
+ oid * priv_protocol,
+ size_t priv_protocol_len)
+{
+ MAKE_ENTRY(oid, priv_protocol, priv_protocol_len,
+ usr_priv_protocol, usr_priv_protocol_length);
+}
+
+int
+usm_set_usmStateReference_priv_key(struct usmStateReference *ref,
+ u_char * priv_key, size_t priv_key_len)
+{
+ MAKE_ENTRY(u_char, priv_key, priv_key_len,
+ usr_priv_key, usr_priv_key_length);
+}
+
+int
+usm_set_usmStateReference_sec_level(struct usmStateReference *ref,
+ int sec_level)
+{
+ if (ref == NULL)
+ return -1;
+ ref->usr_sec_level = sec_level;
+ return 0;
+}
+
+
+
+#ifdef NETSNMP_ENABLE_TESTING_CODE
+/*******************************************************************-o-******
+ * emergency_print
+ *
+ * Parameters:
+ * *field
+ * length
+ *
+ * This is a print routine that is solely included so that it can be
+ * used in gdb. Don't use it as a function, it will be pulled before
+ * a real release of the code.
+ *
+ * tab stop 4
+ *
+ * XXX fflush() only works on FreeBSD; core dumps on Sun OS's
+ */
+void
+emergency_print(u_char * field, u_int length)
+{
+ int iindex;
+ int start = 0;
+ int stop = 25;
+
+ while (start < stop) {
+ for (iindex = start; iindex < stop; iindex++)
+ printf("%02X ", field[iindex]);
+
+ printf("\n");
+ start = stop;
+ stop = stop + 25 < length ? stop + 25 : length;
+ }
+ fflush(0);
+
+} /* end emergency_print() */
+#endif /* NETSNMP_ENABLE_TESTING_CODE */
+
+
+/*******************************************************************-o-******
+ * asn_predict_int_length
+ *
+ * Parameters:
+ * type (UNUSED)
+ * number
+ * len
+ *
+ * Returns:
+ * Number of bytes necessary to store the ASN.1 encoded value of 'number'.
+ *
+ *
+ * This gives the number of bytes that the ASN.1 encoder (in asn1.c) will
+ * use to encode a particular integer value.
+ *
+ * Returns the length of the integer -- NOT THE HEADER!
+ *
+ * Do this the same way as asn_build_int()...
+ */
+int
+asn_predict_int_length(int type, long number, size_t len)
+{
+ register u_long mask;
+
+
+ if (len != sizeof(long))
+ return -1;
+
+ mask = ((u_long) 0x1FF) << ((8 * (sizeof(long) - 1)) - 1);
+ /*
+ * mask is 0xFF800000 on a big-endian machine
+ */
+
+ while ((((number & mask) == 0) || ((number & mask) == mask))
+ && len > 1) {
+ len--;
+ number <<= 8;
+ }
+
+ return len;
+
+} /* end asn_predict_length() */
+
+
+
+
+/*******************************************************************-o-******
+ * asn_predict_length
+ *
+ * Parameters:
+ * type
+ * *ptr
+ * u_char_len
+ *
+ * Returns:
+ * Length in bytes: 1 + <n> + <u_char_len>, where
+ *
+ * 1 For the ASN.1 type.
+ * <n> # of bytes to store length of data.
+ * <u_char_len> Length of data associated with ASN.1 type.
+ *
+ * This gives the number of bytes that the ASN.1 encoder (in asn1.c) will
+ * use to encode a particular integer value. This is as broken as the
+ * currently used encoder.
+ *
+ * XXX How is <n> chosen, exactly??
+ */
+int
+asn_predict_length(int type, u_char * ptr, size_t u_char_len)
+{
+
+ if (type & ASN_SEQUENCE)
+ return 1 + 3 + u_char_len;
+
+ if (type & ASN_INTEGER) {
+ u_long value;
+ memcpy(&value, ptr, u_char_len);
+ u_char_len = asn_predict_int_length(type, value, u_char_len);
+ }
+
+ if (u_char_len < 0x80)
+ return 1 + 1 + u_char_len;
+ else if (u_char_len < 0xFF)
+ return 1 + 2 + u_char_len;
+ else
+ return 1 + 3 + u_char_len;
+
+} /* end asn_predict_length() */
+
+
+
+
+/*******************************************************************-o-******
+ * usm_calc_offsets
+ *
+ * Parameters:
+ * (See list below...)
+ *
+ * Returns:
+ * 0 On success,
+ * -1 Otherwise.
+ *
+ *
+ * This routine calculates the offsets into an outgoing message buffer
+ * for the necessary values. The outgoing buffer will generically
+ * look like this:
+ *
+ * SNMPv3 Message
+ * SEQ len[11]
+ * INT len version
+ * Header
+ * SEQ len
+ * INT len MsgID
+ * INT len msgMaxSize
+ * OST len msgFlags (OST = OCTET STRING)
+ * INT len msgSecurityModel
+ * MsgSecurityParameters
+ * [1] OST len[2]
+ * SEQ len[3]
+ * OST len msgAuthoritativeEngineID
+ * INT len msgAuthoritativeEngineBoots
+ * INT len msgAuthoritativeEngineTime
+ * OST len msgUserName
+ * OST len[4] [5] msgAuthenticationParameters
+ * OST len[6] [7] msgPrivacyParameters
+ * MsgData
+ * [8] OST len[9] [10] encryptedPDU
+ * or
+ * [8,10] SEQUENCE len[9] scopedPDU
+ * [12]
+ *
+ * The bracketed points will be needed to be identified ([x] is an index
+ * value, len[x] means a length value). Here is a semantic guide to them:
+ *
+ * [1] = globalDataLen (input)
+ * [2] = otstlen
+ * [3] = seq_len
+ * [4] = msgAuthParmLen (may be 0 or 12)
+ * [5] = authParamsOffset
+ * [6] = msgPrivParmLen (may be 0 or 8)
+ * [7] = privParamsOffset
+ * [8] = globalDataLen + msgSecParmLen
+ * [9] = datalen
+ * [10] = dataOffset
+ * [11] = theTotalLength - the length of the header itself
+ * [12] = theTotalLength
+ */
+int
+usm_calc_offsets(size_t globalDataLen, /* SNMPv3Message + HeaderData */
+ int secLevel, size_t secEngineIDLen, size_t secNameLen, size_t scopedPduLen, /* An BER encoded sequence. */
+ u_long engineboots, /* XXX (asn1.c works in long, not int.) */
+ long engine_time, /* XXX (asn1.c works in long, not int.) */
+ size_t * theTotalLength, /* globalDataLen + msgSecurityP. + msgData */
+ size_t * authParamsOffset, /* Distance to auth bytes. */
+ size_t * privParamsOffset, /* Distance to priv bytes. */
+ size_t * dataOffset, /* Distance to scopedPdu SEQ -or- the
+ * crypted (data) portion of msgData. */
+ size_t * datalen, /* Size of msgData OCTET STRING encoding. */
+ size_t * msgAuthParmLen, /* Size of msgAuthenticationParameters. */
+ size_t * msgPrivParmLen, /* Size of msgPrivacyParameters. */
+ size_t * otstlen, /* Size of msgSecurityP. O.S. encoding. */
+ size_t * seq_len, /* Size of msgSecurityP. SEQ data. */
+ size_t * msgSecParmLen)
+{ /* Size of msgSecurityP. SEQ. */
+ int engIDlen, /* Sizes of OCTET STRING and SEQ encodings */
+ engBtlen, /* for fields within */
+ engTmlen, /* msgSecurityParameters portion of */
+ namelen, /* SNMPv3Message. */
+ authlen, privlen;
+
+ /*
+ * If doing authentication, msgAuthParmLen = 12 else msgAuthParmLen = 0.
+ * If doing encryption, msgPrivParmLen = 8 else msgPrivParmLen = 0.
+ */
+ *msgAuthParmLen = (secLevel == SNMP_SEC_LEVEL_AUTHNOPRIV
+ || secLevel == SNMP_SEC_LEVEL_AUTHPRIV) ? 12 : 0;
+
+ *msgPrivParmLen = (secLevel == SNMP_SEC_LEVEL_AUTHPRIV) ? 8 : 0;
+
+
+ /*
+ * Calculate lengths.
+ */
+ if ((engIDlen = asn_predict_length(ASN_OCTET_STR,
+ 0, secEngineIDLen)) == -1) {
+ return -1;
+ }
+
+ if ((engBtlen = asn_predict_length(ASN_INTEGER,
+ (u_char *) & engineboots,
+ sizeof(long))) == -1) {
+ return -1;
+ }
+
+ if ((engTmlen = asn_predict_length(ASN_INTEGER,
+ (u_char *) & engine_time,
+ sizeof(long))) == -1) {
+ return -1;
+ }
+
+ if ((namelen = asn_predict_length(ASN_OCTET_STR, 0, secNameLen)) == -1) {
+ return -1;
+ }
+
+ if ((authlen = asn_predict_length(ASN_OCTET_STR,
+ 0, *msgAuthParmLen)) == -1) {
+ return -1;
+ }
+
+ if ((privlen = asn_predict_length(ASN_OCTET_STR,
+ 0, *msgPrivParmLen)) == -1) {
+ return -1;
+ }
+
+ *seq_len =
+ engIDlen + engBtlen + engTmlen + namelen + authlen + privlen;
+
+ if ((*otstlen = asn_predict_length(ASN_SEQUENCE, 0, *seq_len)) == -1) {
+ return -1;
+ }
+
+ if ((*msgSecParmLen = asn_predict_length(ASN_OCTET_STR,
+ 0, *otstlen)) == -1) {
+ return -1;
+ }
+
+ *authParamsOffset = globalDataLen + +(*msgSecParmLen - *seq_len)
+ + engIDlen + engBtlen + engTmlen + namelen
+ + (authlen - *msgAuthParmLen);
+
+ *privParamsOffset = *authParamsOffset + *msgAuthParmLen
+ + (privlen - *msgPrivParmLen);
+
+
+ /*
+ * Compute the size of the plaintext. Round up to account for cipher
+ * block size, if necessary.
+ *
+ * XXX This is hardwired for 1DES... If scopedPduLen is already
+ * a multiple of 8, then *add* 8 more; otherwise, round up
+ * to the next multiple of 8.
+ *
+ * FIX Calculation of encrypted portion of msgData and consequent
+ * setting and sanity checking of theTotalLength, et al. should
+ * occur *after* encryption has taken place.
+ */
+ if (secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
+ scopedPduLen = ROUNDUP8(scopedPduLen);
+
+ if ((*datalen =
+ asn_predict_length(ASN_OCTET_STR, 0, scopedPduLen)) == -1) {
+ return -1;
+ }
+ } else {
+ *datalen = scopedPduLen;
+ }
+
+ *dataOffset = globalDataLen + *msgSecParmLen +
+ (*datalen - scopedPduLen);
+ *theTotalLength = globalDataLen + *msgSecParmLen + *datalen;
+
+ return 0;
+
+} /* end usm_calc_offsets() */
+
+
+
+
+
+#ifndef NETSNMP_DISABLE_DES
+/*******************************************************************-o-******
+ * usm_set_salt
+ *
+ * Parameters:
+ * *iv (O) Buffer to contain IV.
+ * *iv_length (O) Length of iv.
+ * *priv_salt (I) Salt portion of private key.
+ * priv_salt_length (I) Length of priv_salt.
+ * *msgSalt (I/O) Pointer salt portion of outgoing msg buffer.
+ *
+ * Returns:
+ * 0 On success,
+ * -1 Otherwise.
+ *
+ * Determine the initialization vector for the DES-CBC encryption.
+ * (Cf. RFC 2274, 8.1.1.1.)
+ *
+ * iv is defined as the concatenation of engineBoots and the
+ * salt integer.
+ * The salt integer is incremented.
+ * The resulting salt is copied into the msgSalt buffer.
+ * The result of the concatenation is then XORed with the salt
+ * portion of the private key (last 8 bytes).
+ * The IV result is returned individually for further use.
+ */
+int
+usm_set_salt(u_char * iv,
+ size_t * iv_length,
+ u_char * priv_salt, size_t priv_salt_length, u_char * msgSalt)
+{
+ size_t propersize_salt = BYTESIZE(USM_DES_SALT_LENGTH);
+ int net_boots;
+ int net_salt_int;
+ /*
+ * net_* should be encoded in network byte order. XXX Why?
+ */
+ int iindex;
+
+
+ /*
+ * Sanity check.
+ */
+ if (!iv || !iv_length || !priv_salt || (*iv_length != propersize_salt)
+ || (priv_salt_length < propersize_salt)) {
+ return -1;
+ }
+
+
+ net_boots = htonl(snmpv3_local_snmpEngineBoots());
+ net_salt_int = htonl(salt_integer);
+
+ salt_integer += 1;
+
+ memcpy(iv, &net_boots, propersize_salt / 2);
+ memcpy(iv + (propersize_salt / 2), &net_salt_int, propersize_salt / 2);
+
+ if (msgSalt)
+ memcpy(msgSalt, iv, propersize_salt);
+
+
+ /*
+ * Turn the salt into an IV: XOR <boots, salt_int> with salt
+ * portion of priv_key.
+ */
+ for (iindex = 0; iindex < (int) propersize_salt; iindex++)
+ iv[iindex] ^= priv_salt[iindex];
+
+
+ return 0;
+
+} /* end usm_set_salt() */
+#endif
+
+#ifdef HAVE_AES
+/*******************************************************************-o-******
+ * usm_set_aes_iv
+ *
+ * Parameters:
+ * *iv (O) Buffer to contain IV.
+ * *iv_length (O) Length of iv.
+ * net_boots (I) the network byte order of the authEng boots val
+ * net_time (I) the network byte order of the authEng time val
+ * *salt (O) A buffer for the outgoing salt (= 8 bytes of iv)
+ *
+ * Returns:
+ * 0 On success,
+ * -1 Otherwise.
+ *
+ * Determine the initialization vector for AES encryption.
+ * (draft-blumenthal-aes-usm-03.txt, 3.1.2.2)
+ *
+ * iv is defined as the concatenation of engineBoots, engineTime
+ and a 64 bit salt-integer.
+ * The 64 bit salt integer is incremented.
+ * The resulting salt is copied into the salt buffer.
+ * The IV result is returned individually for further use.
+ */
+int
+usm_set_aes_iv(u_char * iv,
+ size_t * iv_length,
+ u_int net_boots,
+ u_int net_time,
+ u_char * salt)
+{
+ /*
+ * net_* should be encoded in network byte order.
+ */
+ int net_salt_int1, net_salt_int2;
+#define PROPER_AES_IV_SIZE 64
+
+ /*
+ * Sanity check.
+ */
+ if (!iv || !iv_length) {
+ return -1;
+ }
+
+ net_salt_int1 = htonl(salt_integer64_1);
+ net_salt_int2 = htonl(salt_integer64_2);
+
+ if ((salt_integer64_2 += 1) == 0)
+ salt_integer64_2 += 1;
+
+ /* XXX: warning: hard coded proper lengths */
+ memcpy(iv, &net_boots, 4);
+ memcpy(iv+4, &net_time, 4);
+ memcpy(iv+8, &net_salt_int1, 4);
+ memcpy(iv+12, &net_salt_int2, 4);
+
+ memcpy(salt, iv+8, 8); /* only copy the needed portion */
+ return 0;
+} /* end usm_set_salt() */
+#endif /* HAVE_AES */
+
+int
+usm_secmod_generate_out_msg(struct snmp_secmod_outgoing_params *parms)
+{
+ if (!parms)
+ return SNMPERR_GENERR;
+
+ return usm_generate_out_msg(parms->msgProcModel,
+ parms->globalData, parms->globalDataLen,
+ parms->maxMsgSize, parms->secModel,
+ parms->secEngineID, parms->secEngineIDLen,
+ parms->secName, parms->secNameLen,
+ parms->secLevel,
+ parms->scopedPdu, parms->scopedPduLen,
+ parms->secStateRef,
+ parms->secParams, parms->secParamsLen,
+ parms->wholeMsg, parms->wholeMsgLen);
+}
+
+/*******************************************************************-o-******
+ * usm_generate_out_msg
+ *
+ * Parameters:
+ * (See list below...)
+ *
+ * Returns:
+ * SNMPERR_SUCCESS On success.
+ * SNMPERR_USM_AUTHENTICATIONFAILURE
+ * SNMPERR_USM_ENCRYPTIONERROR
+ * SNMPERR_USM_GENERICERROR
+ * SNMPERR_USM_UNKNOWNSECURITYNAME
+ * SNMPERR_USM_GENERICERROR
+ * SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL
+ *
+ *
+ * Generates an outgoing message.
+ *
+ * XXX Beware of misnomers!
+ */
+int
+usm_generate_out_msg(int msgProcModel, /* (UNUSED) */
+ u_char * globalData, /* IN */
+ /*
+ * Pointer to msg header data will point to the beginning
+ * * of the entire packet buffer to be transmitted on wire,
+ * * memory will be contiguous with secParams, typically
+ * * this pointer will be passed back as beginning of
+ * * wholeMsg below. asn seq. length is updated w/ new length.
+ * *
+ * * While this points to a buffer that should be big enough
+ * * for the whole message, only the first two parts
+ * * of the message are completed, namely SNMPv3Message and
+ * * HeaderData. globalDataLen (next parameter) represents
+ * * the length of these two completed parts.
+ */
+ size_t globalDataLen, /* IN - Length of msg header data. */
+ int maxMsgSize, /* (UNUSED) */
+ int secModel, /* (UNUSED) */
+ u_char * secEngineID, /* IN - Pointer snmpEngineID. */
+ size_t secEngineIDLen, /* IN - SnmpEngineID length. */
+ char *secName, /* IN - Pointer to securityName. */
+ size_t secNameLen, /* IN - SecurityName length. */
+ int secLevel, /* IN - AuthNoPriv, authPriv etc. */
+ u_char * scopedPdu, /* IN */
+ /*
+ * Pointer to scopedPdu will be encrypted by USM if needed
+ * * and written to packet buffer immediately following
+ * * securityParameters, entire msg will be authenticated by
+ * * USM if needed.
+ */
+ size_t scopedPduLen, /* IN - scopedPdu length. */
+ void *secStateRef, /* IN */
+ /*
+ * secStateRef, pointer to cached info provided only for
+ * * Response, otherwise NULL.
+ */
+ u_char * secParams, /* OUT */
+ /*
+ * BER encoded securityParameters pointer to offset within
+ * * packet buffer where secParams should be written, the
+ * * entire BER encoded OCTET STRING (including header) is
+ * * written here by USM secParams = globalData +
+ * * globalDataLen.
+ */
+ size_t * secParamsLen, /* IN/OUT - Len available, len returned. */
+ u_char ** wholeMsg, /* OUT */
+ /*
+ * Complete authenticated/encrypted message - typically
+ * * the pointer to start of packet buffer provided in
+ * * globalData is returned here, could also be a separate
+ * * buffer.
+ */
+ size_t * wholeMsgLen)
+{ /* IN/OUT - Len available, len returned. */
+ size_t otstlen;
+ size_t seq_len;
+ size_t msgAuthParmLen;
+ size_t msgPrivParmLen;
+ size_t msgSecParmLen;
+ size_t authParamsOffset;
+ size_t privParamsOffset;
+ size_t datalen;
+ size_t dataOffset;
+ size_t theTotalLength;
+
+ u_char *ptr;
+ size_t ptr_len;
+ size_t remaining;
+ size_t offSet;
+ u_int boots_uint;
+ u_int time_uint;
+ long boots_long;
+ long time_long;
+
+ /*
+ * Indirection because secStateRef values override parameters.
+ *
+ * None of these are to be free'd - they are either pointing to
+ * what's in the secStateRef or to something either in the
+ * actual prarmeter list or the user list.
+ */
+
+ char *theName = NULL;
+ u_int theNameLength = 0;
+ u_char *theEngineID = NULL;
+ u_int theEngineIDLength = 0;
+ u_char *theAuthKey = NULL;
+ u_int theAuthKeyLength = 0;
+ const oid *theAuthProtocol = NULL;
+ u_int theAuthProtocolLength = 0;
+ u_char *thePrivKey = NULL;
+ u_int thePrivKeyLength = 0;
+ const oid *thePrivProtocol = NULL;
+ u_int thePrivProtocolLength = 0;
+ int theSecLevel = 0; /* No defined const for bad
+ * value (other then err).
+ */
+
+ DEBUGMSGTL(("usm", "USM processing has begun.\n"));
+
+ if (secStateRef != NULL) {
+ /*
+ * To hush the compiler for now. XXX
+ */
+ struct usmStateReference *ref
+ = (struct usmStateReference *) secStateRef;
+
+ theName = ref->usr_name;
+ theNameLength = ref->usr_name_length;
+ theEngineID = ref->usr_engine_id;
+ theEngineIDLength = ref->usr_engine_id_length;
+
+ if (!theEngineIDLength) {
+ theEngineID = secEngineID;
+ theEngineIDLength = secEngineIDLen;
+ }
+
+ theAuthProtocol = ref->usr_auth_protocol;
+ theAuthProtocolLength = ref->usr_auth_protocol_length;
+ theAuthKey = ref->usr_auth_key;
+ theAuthKeyLength = ref->usr_auth_key_length;
+ thePrivProtocol = ref->usr_priv_protocol;
+ thePrivProtocolLength = ref->usr_priv_protocol_length;
+ thePrivKey = ref->usr_priv_key;
+ thePrivKeyLength = ref->usr_priv_key_length;
+ theSecLevel = ref->usr_sec_level;
+ }
+
+ /*
+ * Identify the user record.
+ */
+ else {
+ struct usmUser *user;
+
+ /*
+ * we do allow an unknown user name for
+ * unauthenticated requests.
+ */
+ if ((user = usm_get_user(secEngineID, secEngineIDLen, secName))
+ == NULL && secLevel != SNMP_SEC_LEVEL_NOAUTH) {
+ DEBUGMSGTL(("usm", "Unknown User(%s)\n", secName));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_UNKNOWNSECURITYNAME;
+ }
+
+ theName = secName;
+ theNameLength = secNameLen;
+ theEngineID = secEngineID;
+ theSecLevel = secLevel;
+ theEngineIDLength = secEngineIDLen;
+ if (user) {
+ theAuthProtocol = user->authProtocol;
+ theAuthProtocolLength = user->authProtocolLen;
+ theAuthKey = user->authKey;
+ theAuthKeyLength = user->authKeyLen;
+ thePrivProtocol = user->privProtocol;
+ thePrivProtocolLength = user->privProtocolLen;
+ thePrivKey = user->privKey;
+ thePrivKeyLength = user->privKeyLen;
+ } else {
+ /*
+ * unknown users can not do authentication (obviously)
+ */
+ theAuthProtocol = usmNoAuthProtocol;
+ theAuthProtocolLength =
+ sizeof(usmNoAuthProtocol) / sizeof(oid);
+ theAuthKey = NULL;
+ theAuthKeyLength = 0;
+ thePrivProtocol = usmNoPrivProtocol;
+ thePrivProtocolLength =
+ sizeof(usmNoPrivProtocol) / sizeof(oid);
+ thePrivKey = NULL;
+ thePrivKeyLength = 0;
+ }
+ } /* endif -- secStateRef==NULL */
+
+
+ /*
+ * From here to the end of the function, avoid reference to
+ * secName, secEngineID, secLevel, and associated lengths.
+ */
+
+
+ /*
+ * Check to see if the user can use the requested sec services.
+ */
+ if (usm_check_secLevel_vs_protocols(theSecLevel,
+ theAuthProtocol,
+ theAuthProtocolLength,
+ thePrivProtocol,
+ thePrivProtocolLength) == 1) {
+ DEBUGMSGTL(("usm", "Unsupported Security Level (%d)\n",
+ theSecLevel));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL;
+ }
+
+
+ /*
+ * Retrieve the engine information.
+ *
+ * XXX No error is declared in the EoP when sending messages to
+ * unknown engines, processing continues w/ boots/time == (0,0).
+ */
+ if (get_enginetime(theEngineID, theEngineIDLength,
+ &boots_uint, &time_uint, FALSE) == -1) {
+ DEBUGMSGTL(("usm", "%s\n", "Failed to find engine data."));
+ }
+
+ boots_long = boots_uint;
+ time_long = time_uint;
+
+
+ /*
+ * Set up the Offsets.
+ */
+ if (usm_calc_offsets(globalDataLen, theSecLevel, theEngineIDLength,
+ theNameLength, scopedPduLen, boots_long,
+ time_long, &theTotalLength, &authParamsOffset,
+ &privParamsOffset, &dataOffset, &datalen,
+ &msgAuthParmLen, &msgPrivParmLen, &otstlen,
+ &seq_len, &msgSecParmLen) == -1) {
+ DEBUGMSGTL(("usm", "Failed calculating offsets.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_GENERICERROR;
+ }
+
+ /*
+ * So, we have the offsets for the three parts that need to be
+ * determined, and an overall length. Now we need to make
+ * sure all of this would fit in the outgoing buffer, and
+ * whether or not we need to make a new buffer, etc.
+ */
+
+
+ /*
+ * Set wholeMsg as a pointer to globalData. Sanity check for
+ * the proper size.
+ *
+ * Mark workspace in the message with bytes of all 1's to make it
+ * easier to find mistakes in raw message dumps.
+ */
+ ptr = *wholeMsg = globalData;
+ if (theTotalLength > *wholeMsgLen) {
+ DEBUGMSGTL(("usm", "Message won't fit in buffer.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_GENERICERROR;
+ }
+
+ ptr_len = *wholeMsgLen = theTotalLength;
+
+#ifdef NETSNMP_ENABLE_TESTING_CODE
+ memset(&ptr[globalDataLen], 0xFF, theTotalLength - globalDataLen);
+#endif /* NETSNMP_ENABLE_TESTING_CODE */
+
+ /*
+ * Do the encryption.
+ */
+ if (theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
+ size_t encrypted_length = theTotalLength - dataOffset;
+ size_t salt_length = BYTESIZE(USM_MAX_SALT_LENGTH);
+ u_char salt[BYTESIZE(USM_MAX_SALT_LENGTH)];
+
+ /*
+ * XXX Hardwired to seek into a 1DES private key!
+ */
+#ifdef HAVE_AES
+ if (ISTRANSFORM(thePrivProtocol, AESPriv)) {
+ if (!thePrivKey ||
+ usm_set_aes_iv(salt, &salt_length,
+ htonl(boots_uint), htonl(time_uint),
+ &ptr[privParamsOffset]) == -1) {
+ DEBUGMSGTL(("usm", "Can't set AES iv.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_GENERICERROR;
+ }
+ }
+#endif
+#ifndef NETSNMP_DISABLE_DES
+ if (ISTRANSFORM(thePrivProtocol, DESPriv)) {
+ if (!thePrivKey ||
+ (usm_set_salt(salt, &salt_length,
+ thePrivKey + 8, thePrivKeyLength - 8,
+ &ptr[privParamsOffset])
+ == -1)) {
+ DEBUGMSGTL(("usm", "Can't set DES-CBC salt.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_GENERICERROR;
+ }
+ }
+#endif
+
+ if (sc_encrypt(thePrivProtocol, thePrivProtocolLength,
+ thePrivKey, thePrivKeyLength,
+ salt, salt_length,
+ scopedPdu, scopedPduLen,
+ &ptr[dataOffset], &encrypted_length)
+ != SNMP_ERR_NOERROR) {
+ DEBUGMSGTL(("usm", "encryption error.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_ENCRYPTIONERROR;
+ }
+#ifdef NETSNMP_ENABLE_TESTING_CODE
+ if (debug_is_token_registered("usm/dump") == SNMPERR_SUCCESS) {
+ dump_chunk("usm/dump", "This data was encrypted:",
+ scopedPdu, scopedPduLen);
+ dump_chunk("usm/dump", "salt + Encrypted form:",
+ salt, salt_length);
+ dump_chunk("usm/dump", NULL,
+ &ptr[dataOffset], encrypted_length);
+ dump_chunk("usm/dump", "*wholeMsg:",
+ *wholeMsg, theTotalLength);
+ }
+#endif
+
+
+ ptr = *wholeMsg;
+ ptr_len = *wholeMsgLen = theTotalLength;
+
+
+ /*
+ * XXX Sanity check for salt length should be moved up
+ * under usm_calc_offsets() or tossed.
+ */
+ if ((encrypted_length != (theTotalLength - dataOffset))
+ || (salt_length != msgPrivParmLen)) {
+ DEBUGMSGTL(("usm", "encryption length error.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_ENCRYPTIONERROR;
+ }
+
+ DEBUGMSGTL(("usm", "Encryption successful.\n"));
+ }
+
+ /*
+ * No encryption for you!
+ */
+ else {
+ memcpy(&ptr[dataOffset], scopedPdu, scopedPduLen);
+ }
+
+
+
+ /*
+ * Start filling in the other fields (in prep for authentication).
+ *
+ * offSet is an octet string header, which is different from all
+ * the other headers.
+ */
+ remaining = ptr_len - globalDataLen;
+
+ offSet = ptr_len - remaining;
+ asn_build_header(&ptr[offSet], &remaining,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
+ ASN_OCTET_STR), otstlen);
+
+ offSet = ptr_len - remaining;
+ asn_build_sequence(&ptr[offSet], &remaining,
+ (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), seq_len);
+
+ offSet = ptr_len - remaining;
+ DEBUGDUMPHEADER("send", "msgAuthoritativeEngineID");
+ asn_build_string(&ptr[offSet], &remaining,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
+ ASN_OCTET_STR), theEngineID,
+ theEngineIDLength);
+ DEBUGINDENTLESS();
+
+ offSet = ptr_len - remaining;
+ DEBUGDUMPHEADER("send", "msgAuthoritativeEngineBoots");
+ asn_build_int(&ptr[offSet], &remaining,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+ &boots_long, sizeof(long));
+ DEBUGINDENTLESS();
+
+ offSet = ptr_len - remaining;
+ DEBUGDUMPHEADER("send", "msgAuthoritativeEngineTime");
+ asn_build_int(&ptr[offSet], &remaining,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+ &time_long, sizeof(long));
+ DEBUGINDENTLESS();
+
+ offSet = ptr_len - remaining;
+ DEBUGDUMPHEADER("send", "msgUserName");
+ asn_build_string(&ptr[offSet], &remaining,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
+ ASN_OCTET_STR), (u_char *) theName,
+ theNameLength);
+ DEBUGINDENTLESS();
+
+
+ /*
+ * Note: if there is no authentication being done,
+ * msgAuthParmLen is 0, and there is no effect (other than
+ * inserting a zero-length header) of the following
+ * statements.
+ */
+
+ offSet = ptr_len - remaining;
+ asn_build_header(&ptr[offSet],
+ &remaining,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
+ ASN_OCTET_STR), msgAuthParmLen);
+
+ if (theSecLevel == SNMP_SEC_LEVEL_AUTHNOPRIV
+ || theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
+ offSet = ptr_len - remaining;
+ memset(&ptr[offSet], 0, msgAuthParmLen);
+ }
+
+ remaining -= msgAuthParmLen;
+
+
+ /*
+ * Note: if there is no encryption being done, msgPrivParmLen
+ * is 0, and there is no effect (other than inserting a
+ * zero-length header) of the following statements.
+ */
+
+ offSet = ptr_len - remaining;
+ asn_build_header(&ptr[offSet],
+ &remaining,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
+ ASN_OCTET_STR), msgPrivParmLen);
+
+ remaining -= msgPrivParmLen; /* Skipping the IV already there. */
+
+
+ /*
+ * For privacy, need to add the octet string header for it.
+ */
+ if (theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
+ offSet = ptr_len - remaining;
+ asn_build_header(&ptr[offSet],
+ &remaining,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
+ ASN_OCTET_STR),
+ theTotalLength - dataOffset);
+ }
+
+
+ /*
+ * Adjust overall length and store it as the first SEQ length
+ * of the SNMPv3Message.
+ *
+ * FIX 4 is a magic number!
+ */
+ remaining = theTotalLength;
+ asn_build_sequence(ptr, &remaining,
+ (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR),
+ theTotalLength - 4);
+
+
+ /*
+ * Now, time to consider / do authentication.
+ */
+ if (theSecLevel == SNMP_SEC_LEVEL_AUTHNOPRIV
+ || theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
+ size_t temp_sig_len = msgAuthParmLen;
+ u_char *temp_sig = (u_char *) malloc(temp_sig_len);
+
+ if (temp_sig == NULL) {
+ DEBUGMSGTL(("usm", "Out of memory.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_GENERICERROR;
+ }
+
+ if (sc_generate_keyed_hash(theAuthProtocol, theAuthProtocolLength,
+ theAuthKey, theAuthKeyLength,
+ ptr, ptr_len, temp_sig, &temp_sig_len)
+ != SNMP_ERR_NOERROR) {
+ /*
+ * FIX temp_sig_len defined?!
+ */
+ SNMP_ZERO(temp_sig, temp_sig_len);
+ SNMP_FREE(temp_sig);
+ DEBUGMSGTL(("usm", "Signing failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_AUTHENTICATIONFAILURE;
+ }
+
+ if (temp_sig_len != msgAuthParmLen) {
+ SNMP_ZERO(temp_sig, temp_sig_len);
+ SNMP_FREE(temp_sig);
+ DEBUGMSGTL(("usm", "Signing lengths failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_AUTHENTICATIONFAILURE;
+ }
+
+ memcpy(&ptr[authParamsOffset], temp_sig, msgAuthParmLen);
+
+ SNMP_ZERO(temp_sig, temp_sig_len);
+ SNMP_FREE(temp_sig);
+
+ }
+
+ /*
+ * endif -- create keyed hash
+ */
+ usm_free_usmStateReference(secStateRef);
+
+ DEBUGMSGTL(("usm", "USM processing completed.\n"));
+
+ return SNMPERR_SUCCESS;
+
+} /* end usm_generate_out_msg() */
+
+#ifdef NETSNMP_USE_REVERSE_ASNENCODING
+int
+usm_secmod_rgenerate_out_msg(struct snmp_secmod_outgoing_params *parms)
+{
+ if (!parms)
+ return SNMPERR_GENERR;
+
+ return usm_rgenerate_out_msg(parms->msgProcModel,
+ parms->globalData, parms->globalDataLen,
+ parms->maxMsgSize, parms->secModel,
+ parms->secEngineID, parms->secEngineIDLen,
+ parms->secName, parms->secNameLen,
+ parms->secLevel,
+ parms->scopedPdu, parms->scopedPduLen,
+ parms->secStateRef,
+ parms->wholeMsg, parms->wholeMsgLen,
+ parms->wholeMsgOffset);
+}
+
+int
+usm_rgenerate_out_msg(int msgProcModel, /* (UNUSED) */
+ u_char * globalData, /* IN */
+ /*
+ * points at the msgGlobalData, which is of length given by next
+ * parameter.
+ */
+ size_t globalDataLen, /* IN - Length of msg header data. */
+ int maxMsgSize, /* (UNUSED) */
+ int secModel, /* (UNUSED) */
+ u_char * secEngineID, /* IN - Pointer snmpEngineID. */
+ size_t secEngineIDLen, /* IN - SnmpEngineID length. */
+ char *secName, /* IN - Pointer to securityName. */
+ size_t secNameLen, /* IN - SecurityName length. */
+ int secLevel, /* IN - AuthNoPriv, authPriv etc. */
+ u_char * scopedPdu, /* IN */
+ /*
+ * Pointer to scopedPdu will be encrypted by USM if needed
+ * * and written to packet buffer immediately following
+ * * securityParameters, entire msg will be authenticated by
+ * * USM if needed.
+ */
+ size_t scopedPduLen, /* IN - scopedPdu length. */
+ void *secStateRef, /* IN */
+ /*
+ * secStateRef, pointer to cached info provided only for
+ * * Response, otherwise NULL.
+ */
+ u_char ** wholeMsg, /* IN/OUT */
+ /*
+ * Points at the pointer to the packet buffer, which might get extended
+ * if necessary via realloc().
+ */
+ size_t * wholeMsgLen, /* IN/OUT */
+ /*
+ * Length of the entire packet buffer, **not** the length of the
+ * packet.
+ */
+ size_t * offset /* IN/OUT */
+ /*
+ * Offset from the end of the packet buffer to the start of the packet,
+ * also known as the packet length.
+ */
+ )
+{
+ size_t msgAuthParmLen = 0;
+#ifdef NETSNMP_ENABLE_TESTING_CODE
+ size_t theTotalLength;
+#endif
+
+ u_int boots_uint;
+ u_int time_uint;
+ long boots_long;
+ long time_long;
+
+ /*
+ * Indirection because secStateRef values override parameters.
+ *
+ * None of these are to be free'd - they are either pointing to
+ * what's in the secStateRef or to something either in the
+ * actual parameter list or the user list.
+ */
+
+ char *theName = NULL;
+ u_int theNameLength = 0;
+ u_char *theEngineID = NULL;
+ u_int theEngineIDLength = 0;
+ u_char *theAuthKey = NULL;
+ u_int theAuthKeyLength = 0;
+ const oid *theAuthProtocol = NULL;
+ u_int theAuthProtocolLength = 0;
+ u_char *thePrivKey = NULL;
+ u_int thePrivKeyLength = 0;
+ const oid *thePrivProtocol = NULL;
+ u_int thePrivProtocolLength = 0;
+ int theSecLevel = 0; /* No defined const for bad
+ * value (other then err). */
+ size_t salt_length = 0, save_salt_length = 0, save_salt_offset = 0;
+ u_char salt[BYTESIZE(USM_MAX_SALT_LENGTH)];
+ u_char authParams[USM_MAX_AUTHSIZE];
+ u_char iv[BYTESIZE(USM_MAX_SALT_LENGTH)];
+ size_t sp_offset = 0, mac_offset = 0;
+ int rc = 0;
+
+ DEBUGMSGTL(("usm", "USM processing has begun (offset %d)\n", *offset));
+
+ if (secStateRef != NULL) {
+ /*
+ * To hush the compiler for now. XXX
+ */
+ struct usmStateReference *ref
+ = (struct usmStateReference *) secStateRef;
+
+ theName = ref->usr_name;
+ theNameLength = ref->usr_name_length;
+ theEngineID = ref->usr_engine_id;
+ theEngineIDLength = ref->usr_engine_id_length;
+
+ if (!theEngineIDLength) {
+ theEngineID = secEngineID;
+ theEngineIDLength = secEngineIDLen;
+ }
+
+ theAuthProtocol = ref->usr_auth_protocol;
+ theAuthProtocolLength = ref->usr_auth_protocol_length;
+ theAuthKey = ref->usr_auth_key;
+ theAuthKeyLength = ref->usr_auth_key_length;
+ thePrivProtocol = ref->usr_priv_protocol;
+ thePrivProtocolLength = ref->usr_priv_protocol_length;
+ thePrivKey = ref->usr_priv_key;
+ thePrivKeyLength = ref->usr_priv_key_length;
+ theSecLevel = ref->usr_sec_level;
+ }
+
+ /*
+ * * Identify the user record.
+ */
+ else {
+ struct usmUser *user;
+
+ /*
+ * we do allow an unknown user name for
+ * unauthenticated requests.
+ */
+ if ((user = usm_get_user(secEngineID, secEngineIDLen, secName))
+ == NULL && secLevel != SNMP_SEC_LEVEL_NOAUTH) {
+ DEBUGMSGTL(("usm", "Unknown User\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_UNKNOWNSECURITYNAME;
+ }
+
+ theName = secName;
+ theNameLength = secNameLen;
+ theEngineID = secEngineID;
+ theSecLevel = secLevel;
+ theEngineIDLength = secEngineIDLen;
+ if (user) {
+ theAuthProtocol = user->authProtocol;
+ theAuthProtocolLength = user->authProtocolLen;
+ theAuthKey = user->authKey;
+ theAuthKeyLength = user->authKeyLen;
+ thePrivProtocol = user->privProtocol;
+ thePrivProtocolLength = user->privProtocolLen;
+ thePrivKey = user->privKey;
+ thePrivKeyLength = user->privKeyLen;
+ } else {
+ /*
+ * unknown users can not do authentication (obviously)
+ */
+ theAuthProtocol = usmNoAuthProtocol;
+ theAuthProtocolLength =
+ sizeof(usmNoAuthProtocol) / sizeof(oid);
+ theAuthKey = NULL;
+ theAuthKeyLength = 0;
+ thePrivProtocol = usmNoPrivProtocol;
+ thePrivProtocolLength =
+ sizeof(usmNoPrivProtocol) / sizeof(oid);
+ thePrivKey = NULL;
+ thePrivKeyLength = 0;
+ }
+ } /* endif -- secStateRef==NULL */
+
+
+ /*
+ * From here to the end of the function, avoid reference to
+ * secName, secEngineID, secLevel, and associated lengths.
+ */
+
+
+ /*
+ * Check to see if the user can use the requested sec services.
+ */
+ if (usm_check_secLevel_vs_protocols(theSecLevel,
+ theAuthProtocol,
+ theAuthProtocolLength,
+ thePrivProtocol,
+ thePrivProtocolLength) == 1) {
+ DEBUGMSGTL(("usm", "Unsupported Security Level or type (%d)\n",
+ theSecLevel));
+
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL;
+ }
+
+
+ /*
+ * * Retrieve the engine information.
+ * *
+ * * XXX No error is declared in the EoP when sending messages to
+ * * unknown engines, processing continues w/ boots/time == (0,0).
+ */
+ if (get_enginetime(theEngineID, theEngineIDLength,
+ &boots_uint, &time_uint, FALSE) == -1) {
+ DEBUGMSGTL(("usm", "%s\n", "Failed to find engine data."));
+ }
+
+ boots_long = boots_uint;
+ time_long = time_uint;
+
+ if (theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
+ /*
+ * Initially assume that the ciphertext will end up the same size as
+ * the plaintext plus some padding. Really sc_encrypt ought to be able
+ * to grow this for us, a la asn_realloc_rbuild_<type> functions, but
+ * this will do for now.
+ */
+ u_char *ciphertext = NULL;
+ size_t ciphertextlen = scopedPduLen + 64;
+
+ if ((ciphertext = (u_char *) malloc(ciphertextlen)) == NULL) {
+ DEBUGMSGTL(("usm",
+ "couldn't malloc %d bytes for encrypted PDU\n",
+ ciphertextlen));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_MALLOC;
+ }
+
+ /*
+ * XXX Hardwired to seek into a 1DES private key!
+ */
+#ifdef HAVE_AES
+ if (ISTRANSFORM(thePrivProtocol, AESPriv)) {
+ salt_length = BYTESIZE(USM_AES_SALT_LENGTH);
+ save_salt_length = BYTESIZE(USM_AES_SALT_LENGTH)/2;
+ save_salt_offset = 0;
+ if (!thePrivKey ||
+ usm_set_aes_iv(salt, &salt_length,
+ htonl(boots_uint), htonl(time_uint),
+ iv) == -1) {
+ DEBUGMSGTL(("usm", "Can't set AES iv.\n"));
+ usm_free_usmStateReference(secStateRef);
+ SNMP_FREE(ciphertext);
+ return SNMPERR_USM_GENERICERROR;
+ }
+ }
+#endif
+#ifndef NETSNMP_DISABLE_DES
+ if (ISTRANSFORM(thePrivProtocol, DESPriv)) {
+ salt_length = BYTESIZE(USM_DES_SALT_LENGTH);
+ save_salt_length = BYTESIZE(USM_DES_SALT_LENGTH);
+ save_salt_offset = 0;
+ if (!thePrivKey || (usm_set_salt(salt, &salt_length,
+ thePrivKey + 8,
+ thePrivKeyLength - 8,
+ iv) == -1)) {
+ DEBUGMSGTL(("usm", "Can't set DES-CBC salt.\n"));
+ usm_free_usmStateReference(secStateRef);
+ SNMP_FREE(ciphertext);
+ return SNMPERR_USM_GENERICERROR;
+ }
+ }
+#endif
+#ifdef NETSNMP_ENABLE_TESTING_CODE
+ if (debug_is_token_registered("usm/dump") == SNMPERR_SUCCESS) {
+ dump_chunk("usm/dump", "This data was encrypted:",
+ scopedPdu, scopedPduLen);
+ }
+#endif
+
+ if (sc_encrypt(thePrivProtocol, thePrivProtocolLength,
+ thePrivKey, thePrivKeyLength,
+ salt, salt_length,
+ scopedPdu, scopedPduLen,
+ ciphertext, &ciphertextlen) != SNMP_ERR_NOERROR) {
+ DEBUGMSGTL(("usm", "encryption error.\n"));
+ usm_free_usmStateReference(secStateRef);
+ SNMP_FREE(ciphertext);
+ return SNMPERR_USM_ENCRYPTIONERROR;
+ }
+
+ /*
+ * Write the encrypted scopedPdu back into the packet buffer.
+ */
+
+#ifdef NETSNMP_ENABLE_TESTING_CODE
+ theTotalLength = *wholeMsgLen;
+#endif
+ *offset = 0;
+ rc = asn_realloc_rbuild_string(wholeMsg, wholeMsgLen, offset, 1,
+ (u_char) (ASN_UNIVERSAL |
+ ASN_PRIMITIVE |
+ ASN_OCTET_STR),
+ ciphertext, ciphertextlen);
+ if (rc == 0) {
+ DEBUGMSGTL(("usm", "Encryption failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ SNMP_FREE(ciphertext);
+ return SNMPERR_USM_ENCRYPTIONERROR;
+ }
+#ifdef NETSNMP_ENABLE_TESTING_CODE
+ if (debug_is_token_registered("usm/dump") == SNMPERR_SUCCESS) {
+ dump_chunk("usm/dump", "salt + Encrypted form: ", salt,
+ salt_length);
+ dump_chunk("usm/dump", "wholeMsg:",
+ (*wholeMsg + *wholeMsgLen - *offset), *offset);
+ }
+#endif
+
+ DEBUGMSGTL(("usm", "Encryption successful.\n"));
+ SNMP_FREE(ciphertext);
+ } else {
+ /*
+ * theSecLevel != SNMP_SEC_LEVEL_AUTHPRIV
+ */
+ }
+
+ /*
+ * Start encoding the msgSecurityParameters.
+ */
+
+ sp_offset = *offset;
+
+ DEBUGDUMPHEADER("send", "msgPrivacyParameters");
+ /*
+ * msgPrivacyParameters (warning: assumes DES salt).
+ */
+ rc = asn_realloc_rbuild_string(wholeMsg, wholeMsgLen, offset, 1,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
+ | ASN_OCTET_STR),
+ iv,
+ save_salt_length);
+ DEBUGINDENTLESS();
+ if (rc == 0) {
+ DEBUGMSGTL(("usm", "building privParams failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_TOO_LONG;
+ }
+
+ DEBUGDUMPHEADER("send", "msgAuthenticationParameters");
+ /*
+ * msgAuthenticationParameters (warnings assumes 0x00 by 12).
+ */
+ if (theSecLevel == SNMP_SEC_LEVEL_AUTHNOPRIV
+ || theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
+ memset(authParams, 0, USM_MD5_AND_SHA_AUTH_LEN);
+ msgAuthParmLen = USM_MD5_AND_SHA_AUTH_LEN;
+ }
+
+ rc = asn_realloc_rbuild_string(wholeMsg, wholeMsgLen, offset, 1,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
+ | ASN_OCTET_STR), authParams,
+ msgAuthParmLen);
+ DEBUGINDENTLESS();
+ if (rc == 0) {
+ DEBUGMSGTL(("usm", "building authParams failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_TOO_LONG;
+ }
+
+ /*
+ * Remember where to put the actual HMAC we calculate later on. An
+ * encoded OCTET STRING of length USM_MD5_AND_SHA_AUTH_LEN has an ASN.1
+ * header of length 2, hence the fudge factor.
+ */
+
+ mac_offset = *offset - 2;
+
+ /*
+ * msgUserName.
+ */
+ DEBUGDUMPHEADER("send", "msgUserName");
+ rc = asn_realloc_rbuild_string(wholeMsg, wholeMsgLen, offset, 1,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
+ | ASN_OCTET_STR),
+ (u_char *) theName, theNameLength);
+ DEBUGINDENTLESS();
+ if (rc == 0) {
+ DEBUGMSGTL(("usm", "building authParams failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_TOO_LONG;
+ }
+
+ /*
+ * msgAuthoritativeEngineTime.
+ */
+ DEBUGDUMPHEADER("send", "msgAuthoritativeEngineTime");
+ rc = asn_realloc_rbuild_int(wholeMsg, wholeMsgLen, offset, 1,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
+ ASN_INTEGER), &time_long,
+ sizeof(long));
+ DEBUGINDENTLESS();
+ if (rc == 0) {
+ DEBUGMSGTL(("usm",
+ "building msgAuthoritativeEngineTime failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_TOO_LONG;
+ }
+
+ /*
+ * msgAuthoritativeEngineBoots.
+ */
+ DEBUGDUMPHEADER("send", "msgAuthoritativeEngineBoots");
+ rc = asn_realloc_rbuild_int(wholeMsg, wholeMsgLen, offset, 1,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
+ ASN_INTEGER), &boots_long,
+ sizeof(long));
+ DEBUGINDENTLESS();
+ if (rc == 0) {
+ DEBUGMSGTL(("usm",
+ "building msgAuthoritativeEngineBoots failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_TOO_LONG;
+ }
+
+ DEBUGDUMPHEADER("send", "msgAuthoritativeEngineID");
+ rc = asn_realloc_rbuild_string(wholeMsg, wholeMsgLen, offset, 1,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
+ | ASN_OCTET_STR), theEngineID,
+ theEngineIDLength);
+ DEBUGINDENTLESS();
+ if (rc == 0) {
+ DEBUGMSGTL(("usm", "building msgAuthoritativeEngineID failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_TOO_LONG;
+ }
+
+ /*
+ * USM msgSecurityParameters sequence header
+ */
+ rc = asn_realloc_rbuild_sequence(wholeMsg, wholeMsgLen, offset, 1,
+ (u_char) (ASN_SEQUENCE |
+ ASN_CONSTRUCTOR),
+ *offset - sp_offset);
+ if (rc == 0) {
+ DEBUGMSGTL(("usm", "building usm security parameters failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_TOO_LONG;
+ }
+
+ /*
+ * msgSecurityParameters OCTET STRING wrapper.
+ */
+ rc = asn_realloc_rbuild_header(wholeMsg, wholeMsgLen, offset, 1,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
+ | ASN_OCTET_STR),
+ *offset - sp_offset);
+
+ if (rc == 0) {
+ DEBUGMSGTL(("usm", "building msgSecurityParameters failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_TOO_LONG;
+ }
+
+ /*
+ * Copy in the msgGlobalData and msgVersion.
+ */
+ while ((*wholeMsgLen - *offset) < globalDataLen) {
+ if (!asn_realloc(wholeMsg, wholeMsgLen)) {
+ DEBUGMSGTL(("usm", "building global data failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_TOO_LONG;
+ }
+ }
+
+ *offset += globalDataLen;
+ memcpy(*wholeMsg + *wholeMsgLen - *offset, globalData, globalDataLen);
+
+ /*
+ * Total packet sequence.
+ */
+ rc = asn_realloc_rbuild_sequence(wholeMsg, wholeMsgLen, offset, 1,
+ (u_char) (ASN_SEQUENCE |
+ ASN_CONSTRUCTOR), *offset);
+ if (rc == 0) {
+ DEBUGMSGTL(("usm", "building master packet sequence failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_TOO_LONG;
+ }
+
+ /*
+ * Now consider / do authentication.
+ */
+
+ if (theSecLevel == SNMP_SEC_LEVEL_AUTHNOPRIV ||
+ theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
+ size_t temp_sig_len = msgAuthParmLen;
+ u_char *temp_sig = (u_char *) malloc(temp_sig_len);
+ u_char *proto_msg = *wholeMsg + *wholeMsgLen - *offset;
+ size_t proto_msg_len = *offset;
+
+
+ if (temp_sig == NULL) {
+ DEBUGMSGTL(("usm", "Out of memory.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_GENERICERROR;
+ }
+
+ if (sc_generate_keyed_hash(theAuthProtocol, theAuthProtocolLength,
+ theAuthKey, theAuthKeyLength,
+ proto_msg, proto_msg_len,
+ temp_sig, &temp_sig_len)
+ != SNMP_ERR_NOERROR) {
+ SNMP_FREE(temp_sig);
+ DEBUGMSGTL(("usm", "Signing failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_AUTHENTICATIONFAILURE;
+ }
+
+ if (temp_sig_len != msgAuthParmLen) {
+ SNMP_FREE(temp_sig);
+ DEBUGMSGTL(("usm", "Signing lengths failed.\n"));
+ usm_free_usmStateReference(secStateRef);
+ return SNMPERR_USM_AUTHENTICATIONFAILURE;
+ }
+
+ memcpy(*wholeMsg + *wholeMsgLen - mac_offset, temp_sig,
+ msgAuthParmLen);
+ SNMP_FREE(temp_sig);
+ }
+ /*
+ * endif -- create keyed hash
+ */
+ usm_free_usmStateReference(secStateRef);
+ DEBUGMSGTL(("usm", "USM processing completed.\n"));
+ return SNMPERR_SUCCESS;
+} /* end usm_rgenerate_out_msg() */
+
+#endif /* */
+
+
+
+/*******************************************************************-o-******
+ * usm_parse_security_parameters
+ *
+ * Parameters:
+ * (See list below...)
+ *
+ * Returns:
+ * 0 On success,
+ * -1 Otherwise.
+ *
+ * tab stop 4
+ *
+ * Extracts values from the security header and data portions of the
+ * incoming buffer.
+ */
+int
+usm_parse_security_parameters(u_char * secParams,
+ size_t remaining,
+ u_char * secEngineID,
+ size_t * secEngineIDLen,
+ u_int * boots_uint,
+ u_int * time_uint,
+ char *secName,
+ size_t * secNameLen,
+ u_char * signature,
+ size_t * signature_length,
+ u_char * salt,
+ size_t * salt_length, u_char ** data_ptr)
+{
+ u_char *parse_ptr = secParams;
+ u_char *value_ptr;
+ u_char *next_ptr;
+ u_char type_value;
+
+ size_t octet_string_length = remaining;
+ size_t sequence_length;
+ size_t remaining_bytes;
+
+ long boots_long;
+ long time_long;
+
+ u_int origNameLen;
+
+
+ /*
+ * Eat the first octet header.
+ */
+ if ((value_ptr = asn_parse_sequence(parse_ptr, &octet_string_length,
+ &type_value,
+ (ASN_UNIVERSAL | ASN_PRIMITIVE |
+ ASN_OCTET_STR),
+ "usm first octet")) == NULL) {
+ /*
+ * RETURN parse error
+ */ return -1;
+ }
+
+
+ /*
+ * Eat the sequence header.
+ */
+ parse_ptr = value_ptr;
+ sequence_length = octet_string_length;
+
+ if ((value_ptr = asn_parse_sequence(parse_ptr, &sequence_length,
+ &type_value,
+ (ASN_SEQUENCE | ASN_CONSTRUCTOR),
+ "usm sequence")) == NULL) {
+ /*
+ * RETURN parse error
+ */ return -1;
+ }
+
+
+ /*
+ * Retrieve the engineID.
+ */
+ parse_ptr = value_ptr;
+ remaining_bytes = sequence_length;
+
+ DEBUGDUMPHEADER("recv", "msgAuthoritativeEngineID");
+ if ((next_ptr
+ = asn_parse_string(parse_ptr, &remaining_bytes, &type_value,
+ secEngineID, secEngineIDLen)) == NULL) {
+ DEBUGINDENTLESS();
+ /*
+ * RETURN parse error
+ */ return -1;
+ }
+ DEBUGINDENTLESS();
+
+ if (type_value !=
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR)) {
+ /*
+ * RETURN parse error
+ */ return -1;
+ }
+
+
+ /*
+ * Retrieve the engine boots, notice switch in the way next_ptr and
+ * remaining_bytes are used (to accomodate the asn code).
+ */
+ DEBUGDUMPHEADER("recv", "msgAuthoritativeEngineBoots");
+ if ((next_ptr = asn_parse_int(next_ptr, &remaining_bytes, &type_value,
+ &boots_long, sizeof(long))) == NULL) {
+ DEBUGINDENTLESS();
+ /*
+ * RETURN parse error
+ */ return -1;
+ }
+ DEBUGINDENTLESS();
+
+ if (type_value !=
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER)) {
+ DEBUGINDENTLESS();
+ /*
+ * RETURN parse error
+ */ return -1;
+ }
+
+ *boots_uint = (u_int) boots_long;
+
+
+ /*
+ * Retrieve the time value.
+ */
+ DEBUGDUMPHEADER("recv", "msgAuthoritativeEngineTime");
+ if ((next_ptr = asn_parse_int(next_ptr, &remaining_bytes, &type_value,
+ &time_long, sizeof(long))) == NULL) {
+ /*
+ * RETURN parse error
+ */ return -1;
+ }
+ DEBUGINDENTLESS();
+
+ if (type_value !=
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER)) {
+ /*
+ * RETURN parse error
+ */ return -1;
+ }
+
+ *time_uint = (u_int) time_long;
+
+ if (*boots_uint > ENGINEBOOT_MAX || *time_uint > ENGINETIME_MAX) {
+ return -1;
+ }
+
+ /*
+ * Retrieve the secName.
+ */
+ origNameLen = *secNameLen;
+
+
+ DEBUGDUMPHEADER("recv", "msgUserName");
+ if ((next_ptr
+ = asn_parse_string(next_ptr, &remaining_bytes, &type_value,
+ (u_char *) secName, secNameLen)) == NULL) {
+ DEBUGINDENTLESS();
+ /*
+ * RETURN parse error
+ */ return -1;
+ }
+ DEBUGINDENTLESS();
+
+ /*
+ * FIX -- doesn't this also indicate a buffer overrun?
+ */
+ if ((int) origNameLen < *secNameLen + 1) {
+ /*
+ * RETURN parse error, but it's really a parameter error
+ */
+ return -1;
+ }
+
+ if (*secNameLen > 32) {
+ /*
+ * This is a USM-specific limitation over and above the above
+ * limitation (which will probably default to the length of an
+ * SnmpAdminString, i.e. 255). See RFC 2574, sec. 2.4.
+ */
+ return -1;
+ }
+
+ secName[*secNameLen] = '\0';
+
+ if (type_value !=
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR)) {
+ /*
+ * RETURN parse error
+ */ return -1;
+ }
+
+
+ /*
+ * Retrieve the signature and blank it if there.
+ */
+ DEBUGDUMPHEADER("recv", "msgAuthenticationParameters");
+ if ((next_ptr
+ = asn_parse_string(next_ptr, &remaining_bytes, &type_value,
+ signature, signature_length)) == NULL) {
+ DEBUGINDENTLESS();
+ /*
+ * RETURN parse error
+ */ return -1;
+ }
+ DEBUGINDENTLESS();
+
+ if (type_value !=
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR)) {
+ /*
+ * RETURN parse error
+ */ return -1;
+ }
+
+ if (*signature_length != 0) { /* Blanking for authentication step later */
+ memset(next_ptr - (u_long) * signature_length,
+ 0, *signature_length);
+ }
+
+
+ /*
+ * Retrieve the salt.
+ *
+ * Note that the next ptr is where the data section starts.
+ */
+ DEBUGDUMPHEADER("recv", "msgPrivacyParameters");
+ if ((*data_ptr
+ = asn_parse_string(next_ptr, &remaining_bytes, &type_value,
+ salt, salt_length)) == NULL) {
+ DEBUGINDENTLESS();
+ /*
+ * RETURN parse error
+ */ return -2;
+ }
+ DEBUGINDENTLESS();
+
+ if (type_value !=
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR)) {
+ /*
+ * RETURN parse error
+ */ return -2;
+ }
+
+ return 0;
+
+} /* end usm_parse_security_parameters() */
+
+
+
+
+/*******************************************************************-o-******
+ * usm_check_and_update_timeliness
+ *
+ * Parameters:
+ * *secEngineID
+ * secEngineIDen
+ * boots_uint
+ * time_uint
+ * *error
+ *
+ * Returns:
+ * 0 On success,
+ * -1 Otherwise.
+ *
+ *
+ * Performs the incoming timeliness checking and setting.
+ */
+int
+usm_check_and_update_timeliness(u_char * secEngineID,
+ size_t secEngineIDLen,
+ u_int boots_uint,
+ u_int time_uint, int *error)
+{
+ u_char myID[USM_MAX_ID_LENGTH];
+ u_long myIDLength =
+ snmpv3_get_engineID(myID, USM_MAX_ID_LENGTH);
+ u_int myBoots;
+ u_int myTime;
+
+
+
+ if ((myIDLength > USM_MAX_ID_LENGTH) || (myIDLength == 0)) {
+ /*
+ * We're probably already screwed...buffer overwrite. XXX?
+ */
+ DEBUGMSGTL(("usm", "Buffer overflow.\n"));
+ *error = SNMPERR_USM_GENERICERROR;
+ return -1;
+ }
+
+ myBoots = snmpv3_local_snmpEngineBoots();
+ myTime = snmpv3_local_snmpEngineTime();
+
+
+ /*
+ * IF the time involved is local
+ * Make sure message is inside the time window
+ * ELSE
+ * IF boots is higher or boots is the same and time is higher
+ * remember this new data
+ * ELSE
+ * IF !(boots same and time within USM_TIME_WINDOW secs)
+ * Message is too old
+ * ELSE
+ * Message is ok, but don't take time
+ * ENDIF
+ * ENDIF
+ * ENDIF
+ */
+
+ /*
+ * This is a local reference.
+ */
+ if ((int) secEngineIDLen == myIDLength
+ && memcmp(secEngineID, myID, myIDLength) == 0) {
+ u_int time_difference = myTime > time_uint ?
+ myTime - time_uint : time_uint - myTime;
+
+ if (boots_uint == ENGINEBOOT_MAX
+ || boots_uint != myBoots
+ || time_difference > USM_TIME_WINDOW) {
+ if (snmp_increment_statistic(STAT_USMSTATSNOTINTIMEWINDOWS) ==
+ 0) {
+ DEBUGMSGTL(("usm", "%s\n",
+ "Failed to increment statistic."));
+ }
+
+ DEBUGMSGTL(("usm",
+ "boot_uint %u myBoots %u time_diff %u => not in time window\n",
+ boots_uint, myBoots, time_difference));
+ *error = SNMPERR_USM_NOTINTIMEWINDOW;
+ return -1;
+ }
+
+ *error = SNMPERR_SUCCESS;
+ return 0;
+ }
+
+ /*
+ * This is a remote reference.
+ */
+ else {
+ u_int theirBoots, theirTime, theirLastTime;
+ u_int time_difference;
+
+ if (get_enginetime_ex(secEngineID, secEngineIDLen,
+ &theirBoots, &theirTime,
+ &theirLastTime, TRUE)
+ != SNMPERR_SUCCESS) {
+ DEBUGMSGTL(("usm", "%s\n",
+ "Failed to get remote engine's times."));
+
+ *error = SNMPERR_USM_GENERICERROR;
+ return -1;
+ }
+
+ time_difference = theirTime > time_uint ?
+ theirTime - time_uint : time_uint - theirTime;
+
+
+ /*
+ * XXX Contrary to the pseudocode:
+ * See if boots is invalid first.
+ */
+ if (theirBoots == ENGINEBOOT_MAX || theirBoots > boots_uint) {
+ DEBUGMSGTL(("usm", "%s\n", "Remote boot count invalid."));
+
+ *error = SNMPERR_USM_NOTINTIMEWINDOW;
+ return -1;
+ }
+
+
+ /*
+ * Boots is ok, see if the boots is the same but the time
+ * is old.
+ */
+ if (theirBoots == boots_uint && time_uint < theirLastTime) {
+ if (time_difference > USM_TIME_WINDOW) {
+ DEBUGMSGTL(("usm", "%s\n", "Message too old."));
+ *error = SNMPERR_USM_NOTINTIMEWINDOW;
+ return -1;
+ }
+
+ else { /* Old, but acceptable */
+
+ *error = SNMPERR_SUCCESS;
+ return 0;
+ }
+ }
+
+
+ /*
+ * Message is ok, either boots has been advanced, or
+ * time is greater than before with the same boots.
+ */
+
+ if (set_enginetime(secEngineID, secEngineIDLen,
+ boots_uint, time_uint, TRUE)
+ != SNMPERR_SUCCESS) {
+ DEBUGMSGTL(("usm", "%s\n",
+ "Failed updating remote boot/time."));
+ *error = SNMPERR_USM_GENERICERROR;
+ return -1;
+ }
+
+ *error = SNMPERR_SUCCESS;
+ return 0; /* Fresh message and time updated */
+
+ } /* endif -- local or remote time reference. */
+
+
+} /* end usm_check_and_update_timeliness() */
+
+
+
+int
+usm_secmod_process_in_msg(struct snmp_secmod_incoming_params *parms)
+{
+ if (!parms)
+ return SNMPERR_GENERR;
+
+ return usm_process_in_msg(parms->msgProcModel,
+ parms->maxMsgSize,
+ parms->secParams,
+ parms->secModel,
+ parms->secLevel,
+ parms->wholeMsg,
+ parms->wholeMsgLen,
+ parms->secEngineID,
+ parms->secEngineIDLen,
+ parms->secName,
+ parms->secNameLen,
+ parms->scopedPdu,
+ parms->scopedPduLen,
+ parms->maxSizeResponse,
+ parms->secStateRef,
+ parms->sess, parms->msg_flags);
+}
+
+/*******************************************************************-o-******
+ * usm_process_in_msg
+ *
+ * Parameters:
+ * (See list below...)
+ *
+ * Returns:
+ * SNMPERR_SUCCESS On success.
+ * SNMPERR_USM_AUTHENTICATIONFAILURE
+ * SNMPERR_USM_DECRYPTIONERROR
+ * SNMPERR_USM_GENERICERROR
+ * SNMPERR_USM_PARSEERROR
+ * SNMPERR_USM_UNKNOWNENGINEID
+ * SNMPERR_USM_PARSEERROR
+ * SNMPERR_USM_UNKNOWNSECURITYNAME
+ * SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL
+ *
+ *
+ * ASSUMES size of decrypt_buf will always be >= size of encrypted sPDU.
+ *
+ * FIX Memory leaks if secStateRef is allocated and a return occurs
+ * without cleaning up. May contain secrets...
+ */
+int
+usm_process_in_msg(int msgProcModel, /* (UNUSED) */
+ size_t maxMsgSize, /* IN - Used to calc maxSizeResponse. */
+ u_char * secParams, /* IN - BER encoded securityParameters. */
+ int secModel, /* (UNUSED) */
+ int secLevel, /* IN - AuthNoPriv, authPriv etc. */
+ u_char * wholeMsg, /* IN - Original v3 message. */
+ size_t wholeMsgLen, /* IN - Msg length. */
+ u_char * secEngineID, /* OUT - Pointer snmpEngineID. */
+ size_t * secEngineIDLen, /* IN/OUT - Len available, len returned. */
+ /*
+ * NOTE: Memory provided by caller.
+ */
+ char *secName, /* OUT - Pointer to securityName. */
+ size_t * secNameLen, /* IN/OUT - Len available, len returned. */
+ u_char ** scopedPdu, /* OUT - Pointer to plaintext scopedPdu. */
+ size_t * scopedPduLen, /* IN/OUT - Len available, len returned. */
+ size_t * maxSizeResponse, /* OUT - Max size of Response PDU. */
+ void **secStateRf, /* OUT - Ref to security state. */
+ netsnmp_session * sess, /* IN - session which got the message */
+ u_char msg_flags)
+{ /* IN - v3 Message flags. */
+ size_t remaining = wholeMsgLen - (u_int)
+ ((u_long) * secParams - (u_long) * wholeMsg);
+ u_int boots_uint;
+ u_int time_uint;
+#ifdef HAVE_AES
+ u_int net_boots, net_time;
+#endif
+ u_char signature[BYTESIZE(USM_MAX_KEYEDHASH_LENGTH)];
+ size_t signature_length = BYTESIZE(USM_MAX_KEYEDHASH_LENGTH);
+ u_char salt[BYTESIZE(USM_MAX_SALT_LENGTH)];
+ size_t salt_length = BYTESIZE(USM_MAX_SALT_LENGTH);
+ u_char iv[BYTESIZE(USM_MAX_SALT_LENGTH)];
+ u_int iv_length = BYTESIZE(USM_MAX_SALT_LENGTH);
+ u_char *data_ptr;
+ u_char *value_ptr;
+ u_char type_value;
+ u_char *end_of_overhead = NULL;
+ int error;
+ int i, rc = 0;
+ struct usmStateReference **secStateRef =
+ (struct usmStateReference **) secStateRf;
+
+ struct usmUser *user;
+
+
+ DEBUGMSGTL(("usm", "USM processing begun...\n"));
+
+
+ if (secStateRef) {
+ usm_free_usmStateReference(*secStateRef);
+ *secStateRef = usm_malloc_usmStateReference();
+ if (*secStateRef == NULL) {
+ DEBUGMSGTL(("usm", "Out of memory.\n"));
+ return SNMPERR_USM_GENERICERROR;
+ }
+ }
+
+
+ /*
+ * Make sure the *secParms is an OCTET STRING.
+ * Extract the user name, engine ID, and security level.
+ */
+ if ((rc = usm_parse_security_parameters(secParams, remaining,
+ secEngineID, secEngineIDLen,
+ &boots_uint, &time_uint,
+ secName, secNameLen,
+ signature, &signature_length,
+ salt, &salt_length,
+ &data_ptr)) < 0) {
+ DEBUGMSGTL(("usm", "Parsing failed (rc %d).\n", rc));
+ if (rc == -2) {
+ /*
+ * This indicates a decryptionError.
+ */
+ if (snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS) ==
+ 0) {
+ DEBUGMSGTL(("usm", "%s\n",
+ "Failed to increment statistic."));
+ }
+ return SNMPERR_USM_DECRYPTIONERROR;
+ }
+ if (snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0) {
+ DEBUGMSGTL(("usm", "%s\n", "Failed to increment statistic."));
+ }
+ return SNMPERR_USM_PARSEERROR;
+ }
+
+ /*
+ * RFC 2574 section 8.3.2
+ * 1) If the privParameters field is not an 8-octet OCTET STRING,
+ * then an error indication (decryptionError) is returned to the
+ * calling module.
+ */
+ if ((secLevel == SNMP_SEC_LEVEL_AUTHPRIV) && (salt_length != 8)) {
+ if (snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS) ==
+ 0) {
+ DEBUGMSGTL(("usm", "%s\n", "Failed increment statistic."));
+ }
+ return SNMPERR_USM_DECRYPTIONERROR;
+ }
+
+ if (secLevel != SNMP_SEC_LEVEL_AUTHPRIV) {
+ /*
+ * pull these out now so reports can use them
+ */
+ *scopedPdu = data_ptr;
+ *scopedPduLen = wholeMsgLen - (data_ptr - wholeMsg);
+ end_of_overhead = data_ptr;
+ }
+
+ if (secStateRef) {
+ /*
+ * Cache the name, engine ID, and security level,
+ * * per step 2 (section 3.2)
+ */
+ if (usm_set_usmStateReference_name
+ (*secStateRef, secName, *secNameLen) == -1) {
+ DEBUGMSGTL(("usm", "%s\n", "Couldn't cache name."));
+ return SNMPERR_USM_GENERICERROR;
+ }
+
+ if (usm_set_usmStateReference_engine_id
+ (*secStateRef, secEngineID, *secEngineIDLen) == -1) {
+ DEBUGMSGTL(("usm", "%s\n", "Couldn't cache engine id."));
+ return SNMPERR_USM_GENERICERROR;
+ }
+
+ if (usm_set_usmStateReference_sec_level(*secStateRef, secLevel) ==
+ -1) {
+ DEBUGMSGTL(("usm", "%s\n", "Couldn't cache security level."));
+ return SNMPERR_USM_GENERICERROR;
+ }
+ }
+
+
+ /*
+ * Locate the engine ID record.
+ * If it is unknown, then either create one or note this as an error.
+ */
+ if ((sess && (sess->isAuthoritative == SNMP_SESS_AUTHORITATIVE ||
+ (sess->isAuthoritative == SNMP_SESS_UNKNOWNAUTH &&
+ (msg_flags & SNMP_MSG_FLAG_RPRT_BIT)))) ||
+ (!sess && (msg_flags & SNMP_MSG_FLAG_RPRT_BIT))) {
+ if (ISENGINEKNOWN(secEngineID, *secEngineIDLen) == FALSE) {
+ DEBUGMSGTL(("usm", "Unknown Engine ID.\n"));
+ if (snmp_increment_statistic(STAT_USMSTATSUNKNOWNENGINEIDS) ==
+ 0) {
+ DEBUGMSGTL(("usm", "%s\n",
+ "Failed to increment statistic."));
+ }
+ return SNMPERR_USM_UNKNOWNENGINEID;
+ }
+ } else {
+ if (ENSURE_ENGINE_RECORD(secEngineID, *secEngineIDLen)
+ != SNMPERR_SUCCESS) {
+ DEBUGMSGTL(("usm", "%s\n", "Couldn't ensure engine record."));
+ return SNMPERR_USM_GENERICERROR;
+ }
+
+ }
+
+
+ /*
+ * Locate the User record.
+ * If the user/engine ID is unknown, report this as an error.
+ */
+ if ((user = usm_get_user_from_list(secEngineID, *secEngineIDLen,
+ secName, userList,
+ (((sess && sess->isAuthoritative ==
+ SNMP_SESS_AUTHORITATIVE) ||
+ (!sess)) ? 0 : 1)))
+ == NULL) {
+ DEBUGMSGTL(("usm", "Unknown User(%s)\n", secName));
+ if (snmp_increment_statistic(STAT_USMSTATSUNKNOWNUSERNAMES) == 0) {
+ DEBUGMSGTL(("usm", "%s\n", "Failed to increment statistic."));
+ }
+ return SNMPERR_USM_UNKNOWNSECURITYNAME;
+ }
+
+
+ /*
+ * Make sure the security level is appropriate.
+ */
+ if (usm_check_secLevel(secLevel, user) == 1) {
+ DEBUGMSGTL(("usm", "Unsupported Security Level (%d).\n",
+ secLevel));
+ if (snmp_increment_statistic
+ (STAT_USMSTATSUNSUPPORTEDSECLEVELS) == 0) {
+ DEBUGMSGTL(("usm", "%s\n", "Failed to increment statistic."));
+ }
+ return SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL;
+ }
+
+
+ /*
+ * Check the authentication credentials of the message.
+ */
+ if (secLevel == SNMP_SEC_LEVEL_AUTHNOPRIV
+ || secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
+ if (sc_check_keyed_hash(user->authProtocol, user->authProtocolLen,
+ user->authKey, user->authKeyLen,
+ wholeMsg, wholeMsgLen,
+ signature, signature_length)
+ != SNMP_ERR_NOERROR) {
+ DEBUGMSGTL(("usm", "Verification failed.\n"));
+ if (snmp_increment_statistic(STAT_USMSTATSWRONGDIGESTS) == 0) {
+ DEBUGMSGTL(("usm", "%s\n",
+ "Failed to increment statistic."));
+ }
+ snmp_log(LOG_WARNING, "Authentication failed for %s\n",
+ user->name);
+ return SNMPERR_USM_AUTHENTICATIONFAILURE;
+ }
+
+ DEBUGMSGTL(("usm", "Verification succeeded.\n"));
+ }
+
+
+ /*
+ * Steps 10-11 user is already set - relocated before timeliness
+ * check in case it fails - still save user data for response.
+ *
+ * Cache the keys and protocol oids, per step 11 (s3.2).
+ */
+ if (secStateRef) {
+ if (usm_set_usmStateReference_auth_protocol(*secStateRef,
+ user->authProtocol,
+ user->
+ authProtocolLen) ==
+ -1) {
+ DEBUGMSGTL(("usm", "%s\n",
+ "Couldn't cache authentication protocol."));
+ return SNMPERR_USM_GENERICERROR;
+ }
+
+ if (usm_set_usmStateReference_auth_key(*secStateRef,
+ user->authKey,
+ user->authKeyLen) == -1) {
+ DEBUGMSGTL(("usm", "%s\n",
+ "Couldn't cache authentication key."));
+ return SNMPERR_USM_GENERICERROR;
+ }
+
+ if (usm_set_usmStateReference_priv_protocol(*secStateRef,
+ user->privProtocol,
+ user->
+ privProtocolLen) ==
+ -1) {
+ DEBUGMSGTL(("usm", "%s\n",
+ "Couldn't cache privacy protocol."));
+ return SNMPERR_USM_GENERICERROR;
+ }
+
+ if (usm_set_usmStateReference_priv_key(*secStateRef,
+ user->privKey,
+ user->privKeyLen) == -1) {
+ DEBUGMSGTL(("usm", "%s\n", "Couldn't cache privacy key."));
+ return SNMPERR_USM_GENERICERROR;
+ }
+ }
+
+
+ /*
+ * Perform the timeliness/time manager functions.
+ */
+ if (secLevel == SNMP_SEC_LEVEL_AUTHNOPRIV
+ || secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
+ if (usm_check_and_update_timeliness(secEngineID, *secEngineIDLen,
+ boots_uint, time_uint,
+ &error) == -1) {
+ return error;
+ }
+ }
+#ifdef LCD_TIME_SYNC_OPT
+ /*
+ * Cache the unauthenticated time to use in case we don't have
+ * anything better - this guess will be no worse than (0,0)
+ * that we normally use.
+ */
+ else {
+ set_enginetime(secEngineID, *secEngineIDLen,
+ boots_uint, time_uint, FALSE);
+ }
+#endif /* LCD_TIME_SYNC_OPT */
+
+
+ /*
+ * If needed, decrypt the scoped PDU.
+ */
+ if (secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
+ remaining = wholeMsgLen - (data_ptr - wholeMsg);
+
+ if ((value_ptr = asn_parse_sequence(data_ptr, &remaining,
+ &type_value,
+ (ASN_UNIVERSAL | ASN_PRIMITIVE
+ | ASN_OCTET_STR),
+ "encrypted sPDU")) == NULL) {
+ DEBUGMSGTL(("usm", "%s\n",
+ "Failed while parsing encrypted sPDU."));
+ if (snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0) {
+ DEBUGMSGTL(("usm", "%s\n", "Failed increment statistic."));
+ }
+ usm_free_usmStateReference(*secStateRef);
+ *secStateRef = NULL;
+ return SNMPERR_USM_PARSEERROR;
+ }
+
+#ifndef NETSNMP_DISABLE_DES
+ if (ISTRANSFORM(user->privProtocol, DESPriv)) {
+ /*
+ * From RFC2574:
+ *
+ * "Before decryption, the encrypted data length is verified.
+ * If the length of the OCTET STRING to be decrypted is not
+ * an integral multiple of 8 octets, the decryption process
+ * is halted and an appropriate exception noted."
+ */
+
+ if (remaining % 8 != 0) {
+ DEBUGMSGTL(("usm",
+ "Ciphertext is %lu bytes, not an integer multiple of 8 (rem %d)\n",
+ remaining, remaining % 8));
+ if (snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS) ==
+ 0) {
+ DEBUGMSGTL(("usm", "%s\n", "Failed increment statistic."));
+ }
+ usm_free_usmStateReference(*secStateRef);
+ *secStateRef = NULL;
+ return SNMPERR_USM_DECRYPTIONERROR;
+ }
+
+ end_of_overhead = value_ptr;
+
+ if ( !user->privKey ) {
+ DEBUGMSGTL(("usm", "No privacy pass phrase for %s\n", user->secName));
+ if (snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS) ==
+ 0) {
+ DEBUGMSGTL(("usm", "%s\n", "Failed increment statistic."));
+ }
+ usm_free_usmStateReference(*secStateRef);
+ *secStateRef = NULL;
+ return SNMPERR_USM_DECRYPTIONERROR;
+ }
+
+ /*
+ * XOR the salt with the last (iv_length) bytes
+ * of the priv_key to obtain the IV.
+ */
+ iv_length = BYTESIZE(USM_DES_SALT_LENGTH);
+ for (i = 0; i < (int) iv_length; i++)
+ iv[i] = salt[i] ^ user->privKey[iv_length + i];
+ }
+#endif
+#ifdef HAVE_AES
+ if (ISTRANSFORM(user->privProtocol, AESPriv)) {
+ iv_length = BYTESIZE(USM_AES_SALT_LENGTH);
+ net_boots = ntohl(boots_uint);
+ net_time = ntohl(time_uint);
+ memcpy(iv, &net_boots, 4);
+ memcpy(iv+4, &net_time, 4);
+ memcpy(iv+8, salt, salt_length);
+ }
+#endif
+
+ if (sc_decrypt(user->privProtocol, user->privProtocolLen,
+ user->privKey, user->privKeyLen,
+ iv, iv_length,
+ value_ptr, remaining, *scopedPdu, scopedPduLen)
+ != SNMP_ERR_NOERROR) {
+ DEBUGMSGTL(("usm", "%s\n", "Failed decryption."));
+ if (snmp_increment_statistic
+ (STAT_USMSTATSDECRYPTIONERRORS) == 0) {
+ DEBUGMSGTL(("usm", "%s\n", "Failed increment statistic."));
+ }
+ return SNMPERR_USM_DECRYPTIONERROR;
+ }
+#ifdef NETSNMP_ENABLE_TESTING_CODE
+ if (debug_is_token_registered("usm/dump") == SNMPERR_SUCCESS) {
+ dump_chunk("usm/dump", "Cypher Text", value_ptr, remaining);
+ dump_chunk("usm/dump", "salt + Encrypted form:",
+ salt, salt_length);
+ dump_chunk("usm/dump", "IV + Encrypted form:", iv, iv_length);
+ dump_chunk("usm/dump", "Decrypted chunk:",
+ *scopedPdu, *scopedPduLen);
+ }
+#endif
+ }
+ /*
+ * sPDU is plaintext.
+ */
+ else {
+ *scopedPdu = data_ptr;
+ *scopedPduLen = wholeMsgLen - (data_ptr - wholeMsg);
+ end_of_overhead = data_ptr;
+
+ } /* endif -- PDU decryption */
+
+
+ /*
+ * Calculate the biggest sPDU for the response (i.e., whole - ovrhd).
+ *
+ * FIX Correct?
+ */
+ *maxSizeResponse = maxMsgSize - (int)
+ ((u_long) end_of_overhead - (u_long) wholeMsg);
+
+
+ DEBUGMSGTL(("usm", "USM processing completed.\n"));
+
+ return SNMPERR_SUCCESS;
+
+} /* end usm_process_in_msg() */
+
+void
+usm_handle_report(void *sessp,
+ netsnmp_transport *transport, netsnmp_session *session,
+ int result, netsnmp_pdu *pdu)
+{
+ /*
+ * handle reportable errors
+ */
+
+ /* this will get in our way */
+ usm_free_usmStateReference(pdu->securityStateRef);
+ pdu->securityStateRef = NULL;
+
+ switch (result) {
+ case SNMPERR_USM_AUTHENTICATIONFAILURE:
+ {
+ int res = session->s_snmp_errno;
+ session->s_snmp_errno = result;
+ if (session->callback) {
+ session->callback(NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE,
+ session, pdu->reqid, pdu,
+ session->callback_magic);
+ }
+ session->s_snmp_errno = res;
+ }
+ case SNMPERR_USM_UNKNOWNENGINEID:
+ case SNMPERR_USM_UNKNOWNSECURITYNAME:
+ case SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL:
+ case SNMPERR_USM_NOTINTIMEWINDOW:
+ case SNMPERR_USM_DECRYPTIONERROR:
+
+ if (SNMP_CMD_CONFIRMED(pdu->command) ||
+ (pdu->command == 0
+ && (pdu->flags & SNMP_MSG_FLAG_RPRT_BIT))) {
+ netsnmp_pdu *pdu2;
+ int flags = pdu->flags;
+
+ pdu->flags |= UCD_MSG_FLAG_FORCE_PDU_COPY;
+ pdu2 = snmp_clone_pdu(pdu);
+ pdu->flags = pdu2->flags = flags;
+ snmpv3_make_report(pdu2, result);
+ if (0 == snmp_sess_send(sessp, pdu2)) {
+ snmp_free_pdu(pdu2);
+ /*
+ * TODO: indicate error
+ */
+ }
+ }
+ break;
+ }
+}
+void
+init_usm(void)
+{
+ struct snmp_secmod_def *def;
+
+ DEBUGMSGTL(("init_usm", "unit_usm: %d %d\n", usmNoPrivProtocol[0],
+ usmNoPrivProtocol[1]));
+
+ sc_init(); /* initalize scapi code */
+
+ /*
+ * register ourselves as a security service
+ */
+ def = SNMP_MALLOC_STRUCT(snmp_secmod_def);
+ /*
+ * XXX: def->init_sess_secmod move stuff from snmp_api.c
+ */
+ def->encode_reverse = usm_secmod_rgenerate_out_msg;
+ def->encode_forward = usm_secmod_generate_out_msg;
+ def->decode = usm_secmod_process_in_msg;
+ def->pdu_free_state_ref = usm_free_usmStateReference;
+ def->handle_report = usm_handle_report;
+ register_sec_mod(USM_SEC_MODEL_NUMBER, "usm", def);
+
+ snmp_register_callback(SNMP_CALLBACK_LIBRARY,
+ SNMP_CALLBACK_POST_PREMIB_READ_CONFIG,
+ init_usm_post_config, NULL);
+
+ snmp_register_callback(SNMP_CALLBACK_LIBRARY,
+ SNMP_CALLBACK_SHUTDOWN,
+ deinit_usm_post_config, NULL);
+
+ snmp_register_callback(SNMP_CALLBACK_LIBRARY,
+ SNMP_CALLBACK_SHUTDOWN,
+ free_engineID, NULL);
+
+}
+
+void
+init_usm_conf(const char *app)
+{
+ register_config_handler(app, "usmUser",
+ usm_parse_config_usmUser, NULL, NULL);
+ register_config_handler(app, "createUser",
+ usm_parse_create_usmUser, NULL,
+ "username [-e ENGINEID] (MD5|SHA) authpassphrase [DES [privpassphrase]]");
+
+ /*
+ * we need to be called back later
+ */
+ snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA,
+ usm_store_users, NULL);
+}
+
+/*
+ * initializations for the USM.
+ *
+ * Should be called after the (engineid) configuration files have been read.
+ *
+ * Set "arbitrary" portion of salt to a random number.
+ */
+int
+init_usm_post_config(int majorid, int minorid, void *serverarg,
+ void *clientarg)
+{
+ size_t salt_integer_len = sizeof(salt_integer);
+
+ if (sc_random((u_char *) & salt_integer, &salt_integer_len) !=
+ SNMPERR_SUCCESS) {
+ DEBUGMSGTL(("usm", "sc_random() failed: using time() as salt.\n"));
+ salt_integer = (u_int) time(NULL);
+ }
+
+#ifdef HAVE_AES
+ salt_integer_len = sizeof (salt_integer64_1);
+ if (sc_random((u_char *) & salt_integer64_1, &salt_integer_len) !=
+ SNMPERR_SUCCESS) {
+ DEBUGMSGTL(("usm", "sc_random() failed: using time() as aes1 salt.\n"));
+ salt_integer64_1 = (u_int) time(NULL);
+ }
+ salt_integer_len = sizeof (salt_integer64_1);
+ if (sc_random((u_char *) & salt_integer64_2, &salt_integer_len) !=
+ SNMPERR_SUCCESS) {
+ DEBUGMSGTL(("usm", "sc_random() failed: using time() as aes2 salt.\n"));
+ salt_integer64_2 = (u_int) time(NULL);
+ }
+#endif
+
+#ifndef NETSNMP_DISABLE_MD5
+ noNameUser = usm_create_initial_user("", usmHMACMD5AuthProtocol,
+ USM_LENGTH_OID_TRANSFORM,
+#ifndef NETSNMP_DISABLE_DES
+ usmDESPrivProtocol,
+#else
+ usmAESPrivProtocol,
+#endif
+ USM_LENGTH_OID_TRANSFORM);
+#else
+ noNameUser = usm_create_initial_user("", usmHMACSHA1AuthProtocol,
+ USM_LENGTH_OID_TRANSFORM,
+#ifndef NETSNMP_DISABLE_DES
+ usmDESPrivProtocol,
+#else
+ usmAESPrivProtocol,
+#endif
+ USM_LENGTH_OID_TRANSFORM);
+#endif
+
+ if ( noNameUser ) {
+ SNMP_FREE(noNameUser->engineID);
+ noNameUser->engineIDLen = 0;
+ }
+
+ return SNMPERR_SUCCESS;
+} /* end init_usm_post_config() */
+
+int
+deinit_usm_post_config(int majorid, int minorid, void *serverarg,
+ void *clientarg)
+{
+ if (usm_free_user(noNameUser) != NULL) {
+ DEBUGMSGTL(("deinit_usm_post_config", "could not free initial user\n"));
+ return SNMPERR_GENERR;
+ }
+ noNameUser = NULL;
+
+ DEBUGMSGTL(("deinit_usm_post_config", "initial user removed\n"));
+ return SNMPERR_SUCCESS;
+} /* end deinit_usm_post_config() */
+
+void
+clear_user_list(void)
+{
+ struct usmUser *tmp = userList, *next = NULL;
+
+ while (tmp != NULL) {
+ next = tmp->next;
+ usm_free_user(tmp);
+ tmp = next;
+ }
+ userList = NULL;
+
+}
+
+/*******************************************************************-o-******
+ * usm_check_secLevel
+ *
+ * Parameters:
+ * level
+ * *user
+ *
+ * Returns:
+ * 0 On success,
+ * -1 Otherwise.
+ *
+ * Checks that a given security level is valid for a given user.
+ */
+int
+usm_check_secLevel(int level, struct usmUser *user)
+{
+
+ if (user->userStatus != RS_ACTIVE)
+ return -1;
+
+ DEBUGMSGTL(("comparex", "Comparing: %d %d ", usmNoPrivProtocol[0],
+ usmNoPrivProtocol[1]));
+ DEBUGMSGOID(("comparex", usmNoPrivProtocol,
+ sizeof(usmNoPrivProtocol) / sizeof(oid)));
+ DEBUGMSG(("comparex", "\n"));
+ if (level == SNMP_SEC_LEVEL_AUTHPRIV
+ && (netsnmp_oid_equals(user->privProtocol, user->privProtocolLen,
+ usmNoPrivProtocol,
+ sizeof(usmNoPrivProtocol) / sizeof(oid)) ==
+ 0)) {
+ DEBUGMSGTL(("usm", "Level: %d\n", level));
+ DEBUGMSGTL(("usm", "User (%s) Auth Protocol: ", user->name));
+ DEBUGMSGOID(("usm", user->authProtocol, user->authProtocolLen));
+ DEBUGMSG(("usm", ", User Priv Protocol: "));
+ DEBUGMSGOID(("usm", user->privProtocol, user->privProtocolLen));
+ DEBUGMSG(("usm", "\n"));
+ return 1;
+ }
+ if ((level == SNMP_SEC_LEVEL_AUTHPRIV
+ || level == SNMP_SEC_LEVEL_AUTHNOPRIV)
+ &&
+ (netsnmp_oid_equals
+ (user->authProtocol, user->authProtocolLen, usmNoAuthProtocol,
+ sizeof(usmNoAuthProtocol) / sizeof(oid)) == 0)) {
+ DEBUGMSGTL(("usm", "Level: %d\n", level));
+ DEBUGMSGTL(("usm", "User (%s) Auth Protocol: ", user->name));
+ DEBUGMSGOID(("usm", user->authProtocol, user->authProtocolLen));
+ DEBUGMSG(("usm", ", User Priv Protocol: "));
+ DEBUGMSGOID(("usm", user->privProtocol, user->privProtocolLen));
+ DEBUGMSG(("usm", "\n"));
+ return 1;
+ }
+
+ return 0;
+
+} /* end usm_check_secLevel() */
+
+
+
+
+/*******************************************************************-o-******
+ * usm_check_secLevel_vs_protocols
+ *
+ * Parameters:
+ * level
+ * *authProtocol
+ * authProtocolLen
+ * *privProtocol
+ * privProtocolLen
+ *
+ * Returns:
+ * 0 On success,
+ * 1 Otherwise.
+ *
+ * Same as above but with explicitly named transform types instead of taking
+ * from the usmUser structure.
+ */
+int
+usm_check_secLevel_vs_protocols(int level,
+ const oid * authProtocol,
+ u_int authProtocolLen,
+ const oid * privProtocol,
+ u_int privProtocolLen)
+{
+
+ if (level == SNMP_SEC_LEVEL_AUTHPRIV
+ &&
+ (netsnmp_oid_equals
+ (privProtocol, privProtocolLen, usmNoPrivProtocol,
+ sizeof(usmNoPrivProtocol) / sizeof(oid)) == 0)) {
+ DEBUGMSGTL(("usm", "Level: %d\n", level));
+ DEBUGMSGTL(("usm", "Auth Protocol: "));
+ DEBUGMSGOID(("usm", authProtocol, authProtocolLen));
+ DEBUGMSG(("usm", ", Priv Protocol: "));
+ DEBUGMSGOID(("usm", privProtocol, privProtocolLen));
+ DEBUGMSG(("usm", "\n"));
+ return 1;
+ }
+ if ((level == SNMP_SEC_LEVEL_AUTHPRIV
+ || level == SNMP_SEC_LEVEL_AUTHNOPRIV)
+ &&
+ (netsnmp_oid_equals
+ (authProtocol, authProtocolLen, usmNoAuthProtocol,
+ sizeof(usmNoAuthProtocol) / sizeof(oid)) == 0)) {
+ DEBUGMSGTL(("usm", "Level: %d\n", level));
+ DEBUGMSGTL(("usm", "Auth Protocol: "));
+ DEBUGMSGOID(("usm", authProtocol, authProtocolLen));
+ DEBUGMSG(("usm", ", Priv Protocol: "));
+ DEBUGMSGOID(("usm", privProtocol, privProtocolLen));
+ DEBUGMSG(("usm", "\n"));
+ return 1;
+ }
+
+ return 0;
+
+} /* end usm_check_secLevel_vs_protocols() */
+
+
+
+
+/*
+ * usm_get_user(): Returns a user from userList based on the engineID,
+ * engineIDLen and name of the requested user.
+ */
+
+struct usmUser *
+usm_get_user(u_char * engineID, size_t engineIDLen, char *name)
+{
+ DEBUGMSGTL(("usm", "getting user %s\n", name));
+ return usm_get_user_from_list(engineID, engineIDLen, name, userList,
+ 1);
+}
+
+struct usmUser *
+usm_get_user_from_list(u_char * engineID, size_t engineIDLen,
+ char *name, struct usmUser *puserList,
+ int use_default)
+{
+ struct usmUser *ptr;
+ char noName[] = "";
+ if (name == NULL)
+ name = noName;
+ for (ptr = puserList; ptr != NULL; ptr = ptr->next) {
+ if (ptr->name && !strcmp(ptr->name, name)) {
+ DEBUGMSGTL(("usm", "match on user %s\n", ptr->name));
+ if (ptr->engineIDLen == engineIDLen &&
+ ((ptr->engineID == NULL && engineID == NULL) ||
+ (ptr->engineID != NULL && engineID != NULL &&
+ memcmp(ptr->engineID, engineID, engineIDLen) == 0)))
+ return ptr;
+ DEBUGMSGTL(("usm", "no match on engineID ("));
+ DEBUGMSGHEX(("usm", engineID, engineIDLen));
+ DEBUGMSG(("usm", ")\n"));
+ }
+ }
+
+ /*
+ * return "" user used to facilitate engineID discovery
+ */
+ if (use_default && !strcmp(name, ""))
+ return noNameUser;
+ return NULL;
+}
+
+/*
+ * usm_add_user(): Add's a user to the userList, sorted by the
+ * engineIDLength then the engineID then the name length then the name
+ * to facilitate getNext calls on a usmUser table which is indexed by
+ * these values.
+ *
+ * returns the head of the list (which could change due to this add).
+ */
+
+struct usmUser *
+usm_add_user(struct usmUser *user)
+{
+ struct usmUser *uptr;
+ uptr = usm_add_user_to_list(user, userList);
+ if (uptr != NULL)
+ userList = uptr;
+ return uptr;
+}
+
+struct usmUser *
+usm_add_user_to_list(struct usmUser *user, struct usmUser *puserList)
+{
+ struct usmUser *nptr, *pptr, *optr;
+
+ /*
+ * loop through puserList till we find the proper, sorted place to
+ * insert the new user
+ */
+ /* XXX - how to handle a NULL user->name ?? */
+ /* XXX - similarly for a NULL nptr->name ?? */
+ for (nptr = puserList, pptr = NULL; nptr != NULL;
+ pptr = nptr, nptr = nptr->next) {
+ if (nptr->engineIDLen > user->engineIDLen)
+ break;
+
+ if (user->engineID == NULL && nptr->engineID != NULL)
+ break;
+
+ if (nptr->engineIDLen == user->engineIDLen &&
+ (nptr->engineID != NULL && user->engineID != NULL &&
+ memcmp(nptr->engineID, user->engineID,
+ user->engineIDLen) > 0))
+ break;
+
+ if (!(nptr->engineID == NULL && user->engineID != NULL)) {
+ if (nptr->engineIDLen == user->engineIDLen &&
+ ((nptr->engineID == NULL && user->engineID == NULL) ||
+ memcmp(nptr->engineID, user->engineID,
+ user->engineIDLen) == 0)
+ && strlen(nptr->name) > strlen(user->name))
+ break;
+
+ if (nptr->engineIDLen == user->engineIDLen &&
+ ((nptr->engineID == NULL && user->engineID == NULL) ||
+ memcmp(nptr->engineID, user->engineID,
+ user->engineIDLen) == 0)
+ && strlen(nptr->name) == strlen(user->name)
+ && strcmp(nptr->name, user->name) > 0)
+ break;
+
+ if (nptr->engineIDLen == user->engineIDLen &&
+ ((nptr->engineID == NULL && user->engineID == NULL) ||
+ memcmp(nptr->engineID, user->engineID,
+ user->engineIDLen) == 0)
+ && strlen(nptr->name) == strlen(user->name)
+ && strcmp(nptr->name, user->name) == 0) {
+ /*
+ * the user is an exact match of a previous entry.
+ * Credentials may be different, though, so remove
+ * the old entry (and add the new one)!
+ */
+ if (pptr) { /* change prev's next pointer */
+ pptr->next = nptr->next;
+ }
+ if (nptr->next) { /* change next's prev pointer */
+ nptr->next->prev = pptr;
+ }
+ optr = nptr;
+ nptr = optr->next; /* add new user at this position */
+ /* free the old user */
+ optr->next=NULL;
+ optr->prev=NULL;
+ usm_free_user(optr);
+ break; /* new user will be added below */
+ }
+ }
+ }
+
+ /*
+ * nptr should now point to the user that we need to add ourselves
+ * in front of, and pptr should be our new 'prev'.
+ */
+
+ /*
+ * change our pointers
+ */
+ user->prev = pptr;
+ user->next = nptr;
+
+ /*
+ * change the next's prev pointer
+ */
+ if (user->next)
+ user->next->prev = user;
+
+ /*
+ * change the prev's next pointer
+ */
+ if (user->prev)
+ user->prev->next = user;
+
+ /*
+ * rewind to the head of the list and return it (since the new head
+ * could be us, we need to notify the above routine who the head now is.
+ */
+ for (pptr = user; pptr->prev != NULL; pptr = pptr->prev);
+ return pptr;
+}
+
+/*
+ * usm_remove_user(): finds and removes a user from a list
+ */
+struct usmUser *
+usm_remove_user(struct usmUser *user)
+{
+ return usm_remove_user_from_list(user, &userList);
+}
+
+struct usmUser *
+usm_remove_user_from_list(struct usmUser *user,
+ struct usmUser **ppuserList)
+{
+ struct usmUser *nptr, *pptr;
+
+ /*
+ * NULL pointers aren't allowed
+ */
+ if (ppuserList == NULL)
+ return NULL;
+
+ if (*ppuserList == NULL)
+ return NULL;
+
+ /*
+ * find the user in the list
+ */
+ for (nptr = *ppuserList, pptr = NULL; nptr != NULL;
+ pptr = nptr, nptr = nptr->next) {
+ if (nptr == user)
+ break;
+ }
+
+ if (nptr) {
+ /*
+ * remove the user from the linked list
+ */
+ if (pptr) {
+ pptr->next = nptr->next;
+ }
+ if (nptr->next) {
+ nptr->next->prev = pptr;
+ }
+ } else {
+ /*
+ * user didn't exist
+ */
+ return NULL;
+ }
+ if (nptr == *ppuserList) /* we're the head of the list, need to change
+ * * the head to the next user */
+ *ppuserList = nptr->next;
+ return *ppuserList;
+} /* end usm_remove_user_from_list() */
+
+
+
+
+/*
+ * usm_free_user(): calls free() on all needed parts of struct usmUser and
+ * the user himself.
+ *
+ * Note: This should *not* be called on an object in a list (IE,
+ * remove it from the list first, and set next and prev to NULL), but
+ * will try to reconnect the list pieces again if it is called this
+ * way. If called on the head of the list, the entire list will be
+ * lost.
+ */
+struct usmUser *
+usm_free_user(struct usmUser *user)
+{
+ if (user == NULL)
+ return NULL;
+
+ SNMP_FREE(user->engineID);
+ SNMP_FREE(user->name);
+ SNMP_FREE(user->secName);
+ SNMP_FREE(user->cloneFrom);
+ SNMP_FREE(user->userPublicString);
+ SNMP_FREE(user->authProtocol);
+ SNMP_FREE(user->privProtocol);
+
+ if (user->authKey != NULL) {
+ SNMP_ZERO(user->authKey, user->authKeyLen);
+ SNMP_FREE(user->authKey);
+ }
+
+ if (user->privKey != NULL) {
+ SNMP_ZERO(user->privKey, user->privKeyLen);
+ SNMP_FREE(user->privKey);
+ }
+
+
+ /*
+ * FIX Why not put this check *first?*
+ */
+ if (user->prev != NULL) { /* ack, this shouldn't happen */
+ user->prev->next = user->next;
+ }
+ if (user->next != NULL) {
+ user->next->prev = user->prev;
+ if (user->prev != NULL) /* ack this is really bad, because it means
+ * * we'll loose the head of some structure tree */
+ DEBUGMSGTL(("usm",
+ "Severe: Asked to free the head of a usmUser tree somewhere."));
+ }
+
+
+ SNMP_ZERO(user, sizeof(*user));
+ SNMP_FREE(user);
+
+ return NULL; /* for convenience to returns from calling functions */
+
+} /* end usm_free_user() */
+
+
+
+
+/*
+ * take a given user and clone the security info into another
+ */
+struct usmUser *
+usm_cloneFrom_user(struct usmUser *from, struct usmUser *to)
+{
+ /*
+ * copy the authProtocol oid row pointer
+ */
+ SNMP_FREE(to->authProtocol);
+
+ if ((to->authProtocol =
+ snmp_duplicate_objid(from->authProtocol,
+ from->authProtocolLen)) != NULL)
+ to->authProtocolLen = from->authProtocolLen;
+ else
+ to->authProtocolLen = 0;
+
+
+ /*
+ * copy the authKey
+ */
+ SNMP_FREE(to->authKey);
+
+ if (from->authKeyLen > 0 &&
+ (to->authKey = (u_char *) malloc(from->authKeyLen))
+ != NULL) {
+ to->authKeyLen = from->authKeyLen;
+ memcpy(to->authKey, from->authKey, to->authKeyLen);
+ } else {
+ to->authKey = NULL;
+ to->authKeyLen = 0;
+ }
+
+
+ /*
+ * copy the privProtocol oid row pointer
+ */
+ SNMP_FREE(to->privProtocol);
+
+ if ((to->privProtocol =
+ snmp_duplicate_objid(from->privProtocol,
+ from->privProtocolLen)) != NULL)
+ to->privProtocolLen = from->privProtocolLen;
+ else
+ to->privProtocolLen = 0;
+
+ /*
+ * copy the privKey
+ */
+ SNMP_FREE(to->privKey);
+
+ if (from->privKeyLen > 0 &&
+ (to->privKey = (u_char *) malloc(from->privKeyLen))
+ != NULL) {
+ to->privKeyLen = from->privKeyLen;
+ memcpy(to->privKey, from->privKey, to->privKeyLen);
+ } else {
+ to->privKey = NULL;
+ to->privKeyLen = 0;
+ }
+ return to;
+}
+
+/*
+ * usm_create_user(void):
+ * create a default empty user, instantiating only the auth/priv
+ * protocols to noAuth and noPriv OID pointers
+ */
+struct usmUser *
+usm_create_user(void)
+{
+ struct usmUser *newUser;
+
+ /*
+ * create the new user
+ */
+ newUser = (struct usmUser *) calloc(1, sizeof(struct usmUser));
+ if (newUser == NULL)
+ return NULL;
+
+ /*
+ * fill the auth/priv protocols
+ */
+ if ((newUser->authProtocol =
+ snmp_duplicate_objid(usmNoAuthProtocol,
+ sizeof(usmNoAuthProtocol) / sizeof(oid))) ==
+ NULL)
+ return usm_free_user(newUser);
+ newUser->authProtocolLen = sizeof(usmNoAuthProtocol) / sizeof(oid);
+
+ if ((newUser->privProtocol =
+ snmp_duplicate_objid(usmNoPrivProtocol,
+ sizeof(usmNoPrivProtocol) / sizeof(oid))) ==
+ NULL)
+ return usm_free_user(newUser);
+ newUser->privProtocolLen = sizeof(usmNoPrivProtocol) / sizeof(oid);
+
+ /*
+ * set the storage type to nonvolatile, and the status to ACTIVE
+ */
+ newUser->userStorageType = ST_NONVOLATILE;
+ newUser->userStatus = RS_ACTIVE;
+ return newUser;
+
+} /* end usm_clone_user() */
+
+
+
+
+/*
+ * usm_create_initial_user(void):
+ * creates an initial user, filled with the defaults defined in the
+ * USM document.
+ */
+struct usmUser *
+usm_create_initial_user(const char *name,
+ const oid * authProtocol, size_t authProtocolLen,
+ const oid * privProtocol, size_t privProtocolLen)
+{
+ struct usmUser *newUser = usm_create_user();
+ if (newUser == NULL)
+ return NULL;
+
+ if ((newUser->name = strdup(name)) == NULL)
+ return usm_free_user(newUser);
+
+ if ((newUser->secName = strdup(name)) == NULL)
+ return usm_free_user(newUser);
+
+ if ((newUser->engineID =
+ snmpv3_generate_engineID(&newUser->engineIDLen)) == NULL)
+ return usm_free_user(newUser);
+
+ if ((newUser->cloneFrom = (oid *) malloc(sizeof(oid) * 2)) == NULL)
+ return usm_free_user(newUser);
+ newUser->cloneFrom[0] = 0;
+ newUser->cloneFrom[1] = 0;
+ newUser->cloneFromLen = 2;
+
+ SNMP_FREE(newUser->privProtocol);
+ if ((newUser->privProtocol = snmp_duplicate_objid(privProtocol,
+ privProtocolLen)) ==
+ NULL) {
+ return usm_free_user(newUser);
+ }
+ newUser->privProtocolLen = privProtocolLen;
+
+ SNMP_FREE(newUser->authProtocol);
+ if ((newUser->authProtocol = snmp_duplicate_objid(authProtocol,
+ authProtocolLen)) ==
+ NULL) {
+ return usm_free_user(newUser);
+ }
+ newUser->authProtocolLen = authProtocolLen;
+
+ newUser->userStatus = RS_ACTIVE;
+ newUser->userStorageType = ST_READONLY;
+
+ return newUser;
+}
+
+/*
+ * this is a callback that can store all known users based on a
+ * previously registered application ID
+ */
+int
+usm_store_users(int majorID, int minorID, void *serverarg, void *clientarg)
+{
+ /*
+ * figure out our application name
+ */
+ char *appname = (char *) clientarg;
+ if (appname == NULL) {
+ appname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_APPTYPE);
+ }
+
+ /*
+ * save the user base
+ */
+ usm_save_users("usmUser", appname);
+
+ /*
+ * never fails
+ */
+ return SNMPERR_SUCCESS;
+}
+
+
+/*
+ * usm_save_users(): saves a list of users to the persistent cache
+ */
+void
+usm_save_users(const char *token, const char *type)
+{
+ usm_save_users_from_list(userList, token, type);
+}
+
+void
+usm_save_users_from_list(struct usmUser *puserList, const char *token,
+ const char *type)
+{
+ struct usmUser *uptr;
+ for (uptr = puserList; uptr != NULL; uptr = uptr->next) {
+ if (uptr->userStorageType == ST_NONVOLATILE)
+ usm_save_user(uptr, token, type);
+ }
+}
+
+/*
+ * usm_save_user(): saves a user to the persistent cache
+ */
+void
+usm_save_user(struct usmUser *user, const char *token, const char *type)
+{
+ char line[4096];
+ char *cptr;
+
+ memset(line, 0, sizeof(line));
+
+ sprintf(line, "%s %d %d ", token, user->userStatus,
+ user->userStorageType);
+ cptr = &line[strlen(line)]; /* the NULL */
+ cptr =
+ read_config_save_octet_string(cptr, user->engineID,
+ user->engineIDLen);
+ *cptr++ = ' ';
+ cptr = read_config_save_octet_string(cptr, (u_char *) user->name,
+ (user->name == NULL) ? 0 :
+ strlen(user->name) + 1);
+ *cptr++ = ' ';
+ cptr = read_config_save_octet_string(cptr, (u_char *) user->secName,
+ (user->secName == NULL) ? 0 :
+ strlen(user->secName) + 1);
+ *cptr++ = ' ';
+ cptr =
+ read_config_save_objid(cptr, user->cloneFrom, user->cloneFromLen);
+ *cptr++ = ' ';
+ cptr = read_config_save_objid(cptr, user->authProtocol,
+ user->authProtocolLen);
+ *cptr++ = ' ';
+ cptr =
+ read_config_save_octet_string(cptr, user->authKey,
+ user->authKeyLen);
+ *cptr++ = ' ';
+ cptr = read_config_save_objid(cptr, user->privProtocol,
+ user->privProtocolLen);
+ *cptr++ = ' ';
+ cptr =
+ read_config_save_octet_string(cptr, user->privKey,
+ user->privKeyLen);
+ *cptr++ = ' ';
+ cptr = read_config_save_octet_string(cptr, user->userPublicString,
+ (user->userPublicString ==
+ NULL) ? 0 : strlen((char *)
+ user->
+ userPublicString)
+ + 1);
+ read_config_store(type, line);
+}
+
+/*
+ * usm_parse_user(): reads in a line containing a saved user profile
+ * and returns a pointer to a newly created struct usmUser.
+ */
+struct usmUser *
+usm_read_user(char *line)
+{
+ struct usmUser *user;
+ size_t len;
+ size_t expected_privKeyLen = 0;
+
+ user = usm_create_user();
+ if (user == NULL)
+ return NULL;
+
+ user->userStatus = atoi(line);
+ line = skip_token(line);
+ user->userStorageType = atoi(line);
+ line = skip_token(line);
+ line = read_config_read_octet_string(line, &user->engineID,
+ &user->engineIDLen);
+
+ /*
+ * set the lcd entry for this engineID to the minimum boots/time
+ * values so that its a known engineid and won't return a report pdu.
+ * This is mostly important when receiving v3 traps so that the usm
+ * will at least continue processing them.
+ */
+ set_enginetime(user->engineID, user->engineIDLen, 1, 0, 0);
+
+ line = read_config_read_octet_string(line, (u_char **) & user->name,
+ &len);
+ line = read_config_read_octet_string(line, (u_char **) & user->secName,
+ &len);
+ SNMP_FREE(user->cloneFrom);
+ user->cloneFromLen = 0;
+
+ line =
+ read_config_read_objid(line, &user->cloneFrom,
+ &user->cloneFromLen);
+
+ SNMP_FREE(user->authProtocol);
+ user->authProtocolLen = 0;
+
+ line = read_config_read_objid(line, &user->authProtocol,
+ &user->authProtocolLen);
+ line = read_config_read_octet_string(line, &user->authKey,
+ &user->authKeyLen);
+ SNMP_FREE(user->privProtocol);
+ user->privProtocolLen = 0;
+
+ line = read_config_read_objid(line, &user->privProtocol,
+ &user->privProtocolLen);
+ line = read_config_read_octet_string(line, &user->privKey,
+ &user->privKeyLen);
+#ifndef NETSNMP_DISABLE_DES
+ if (ISTRANSFORM(user->privProtocol, DESPriv)) {
+ /* DES uses a 128 bit key, 64 bits of which is a salt */
+ expected_privKeyLen = 16;
+ }
+#endif
+#ifdef HAVE_AES
+ if (ISTRANSFORM(user->privProtocol, AESPriv)) {
+ expected_privKeyLen = 16;
+ }
+#endif
+ /* For backwards compatibility */
+ if (user->privKeyLen > expected_privKeyLen) {
+ user->privKeyLen = expected_privKeyLen;
+ }
+
+ line = read_config_read_octet_string(line, &user->userPublicString,
+ &len);
+ return user;
+}
+
+/*
+ * snmpd.conf parsing routines
+ */
+void
+usm_parse_config_usmUser(const char *token, char *line)
+{
+ struct usmUser *uptr;
+
+ uptr = usm_read_user(line);
+ if ( uptr)
+ usm_add_user(uptr);
+}
+
+
+
+
+/*******************************************************************-o-******
+ * usm_set_password
+ *
+ * Parameters:
+ * *token
+ * *line
+ *
+ *
+ * format: userSetAuthPass secname engineIDLen engineID pass
+ * or: userSetPrivPass secname engineIDLen engineID pass
+ * or: userSetAuthKey secname engineIDLen engineID KuLen Ku
+ * or: userSetPrivKey secname engineIDLen engineID KuLen Ku
+ * or: userSetAuthLocalKey secname engineIDLen engineID KulLen Kul
+ * or: userSetPrivLocalKey secname engineIDLen engineID KulLen Kul
+ *
+ * type is: 1=passphrase; 2=Ku; 3=Kul.
+ *
+ *
+ * ASSUMES Passwords are null-terminated printable strings.
+ */
+void
+usm_set_password(const char *token, char *line)
+{
+ char *cp;
+ char nameBuf[SNMP_MAXBUF];
+ u_char *engineID;
+ size_t engineIDLen;
+ struct usmUser *user;
+
+ cp = copy_nword(line, nameBuf, sizeof(nameBuf));
+ if (cp == NULL) {
+ config_perror("invalid name specifier");
+ return;
+ }
+
+ DEBUGMSGTL(("usm", "comparing: %s and %s\n", cp, WILDCARDSTRING));
+ if (strncmp(cp, WILDCARDSTRING, strlen(WILDCARDSTRING)) == 0) {
+ /*
+ * match against all engineIDs we know about
+ */
+ cp = skip_token(cp);
+ for (user = userList; user != NULL; user = user->next) {
+ if (user->secName && strcmp(user->secName, nameBuf) == 0) {
+ usm_set_user_password(user, token, cp);
+ }
+ }
+ } else {
+ cp = read_config_read_octet_string(cp, &engineID, &engineIDLen);
+ if (cp == NULL) {
+ config_perror("invalid engineID specifier");
+ return;
+ }
+
+ user = usm_get_user(engineID, engineIDLen, nameBuf);
+ if (user == NULL) {
+ config_perror("not a valid user/engineID pair");
+ return;
+ }
+ usm_set_user_password(user, token, cp);
+ }
+}
+
+/*
+ * uses the rest of LINE to configure USER's password of type TOKEN
+ */
+void
+usm_set_user_password(struct usmUser *user, const char *token, char *line)
+{
+ char *cp = line;
+ u_char *engineID = user->engineID;
+ size_t engineIDLen = user->engineIDLen;
+
+ u_char **key;
+ size_t *keyLen;
+ u_char userKey[SNMP_MAXBUF_SMALL];
+ size_t userKeyLen = SNMP_MAXBUF_SMALL;
+ u_char *userKeyP = userKey;
+ int type, ret;
+
+ /*
+ * Retrieve the "old" key and set the key type.
+ */
+ if (!token) {
+ return;
+ } else if (strcmp(token, "userSetAuthPass") == 0) {
+ key = &user->authKey;
+ keyLen = &user->authKeyLen;
+ type = 0;
+ } else if (strcmp(token, "userSetPrivPass") == 0) {
+ key = &user->privKey;
+ keyLen = &user->privKeyLen;
+ type = 0;
+ } else if (strcmp(token, "userSetAuthKey") == 0) {
+ key = &user->authKey;
+ keyLen = &user->authKeyLen;
+ type = 1;
+ } else if (strcmp(token, "userSetPrivKey") == 0) {
+ key = &user->privKey;
+ keyLen = &user->privKeyLen;
+ type = 1;
+ } else if (strcmp(token, "userSetAuthLocalKey") == 0) {
+ key = &user->authKey;
+ keyLen = &user->authKeyLen;
+ type = 2;
+ } else if (strcmp(token, "userSetPrivLocalKey") == 0) {
+ key = &user->privKey;
+ keyLen = &user->privKeyLen;
+ type = 2;
+ } else {
+ /*
+ * no old key, or token was not recognized
+ */
+ return;
+ }
+
+ if (*key) {
+ /*
+ * (destroy and) free the old key
+ */
+ memset(*key, 0, *keyLen);
+ SNMP_FREE(*key);
+ }
+
+ if (type == 0) {
+ /*
+ * convert the password into a key
+ */
+ if (cp == NULL) {
+ config_perror("missing user password");
+ return;
+ }
+ ret = generate_Ku(user->authProtocol, user->authProtocolLen,
+ (u_char *) cp, strlen(cp), userKey, &userKeyLen);
+
+ if (ret != SNMPERR_SUCCESS) {
+ config_perror("setting key failed (in sc_genKu())");
+ return;
+ }
+ } else if (type == 1) {
+ cp = read_config_read_octet_string(cp, &userKeyP, &userKeyLen);
+
+ if (cp == NULL) {
+ config_perror("invalid user key");
+ return;
+ }
+ }
+
+ if (type < 2) {
+ *key = (u_char *) malloc(SNMP_MAXBUF_SMALL);
+ *keyLen = SNMP_MAXBUF_SMALL;
+ ret = generate_kul(user->authProtocol, user->authProtocolLen,
+ engineID, engineIDLen,
+ userKey, userKeyLen, *key, keyLen);
+ if (ret != SNMPERR_SUCCESS) {
+ config_perror("setting key failed (in generate_kul())");
+ return;
+ }
+
+ /*
+ * (destroy and) free the old key
+ */
+ memset(userKey, 0, sizeof(userKey));
+
+ } else {
+ /*
+ * the key is given, copy it in
+ */
+ cp = read_config_read_octet_string(cp, key, keyLen);
+
+ if (cp == NULL) {
+ config_perror("invalid localized user key");
+ return;
+ }
+ }
+} /* end usm_set_password() */