/* 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. * * Portions of this file are copyrighted by: * Copyright (c) 2016 VMware, 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 #include #include #include #ifdef HAVE_STDLIB_H #include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_STRING_H #include #else #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_DMALLOC_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include netsnmp_feature_child_of(usm_all, libnetsnmp) netsnmp_feature_child_of(usm_support, usm_all) netsnmp_feature_require(usm_support) oid usmNoAuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, NETSNMP_USMAUTH_NOAUTH }; #ifndef NETSNMP_DISABLE_MD5 oid usmHMACMD5AuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, NETSNMP_USMAUTH_HMACMD5 }; #endif oid usmHMACSHA1AuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, NETSNMP_USMAUTH_HMACSHA1 }; #ifdef HAVE_EVP_SHA384 oid usmHMAC384SHA512AuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, NETSNMP_USMAUTH_HMAC384SHA512 }; oid usmHMAC256SHA384AuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, NETSNMP_USMAUTH_HMAC256SHA384 }; #endif /* HAVE_EVP_SHA384 */ #ifdef HAVE_EVP_SHA224 oid usmHMAC192SHA256AuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, NETSNMP_USMAUTH_HMAC192SHA256 }; oid usmHMAC128SHA224AuthProtocol[10] = { NETSNMP_USMAUTH_BASE_OID, NETSNMP_USMAUTH_HMAC128SHA224 }; #endif /* HAVE_EVP_SHA384 */ 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; #ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04 /* OIDs from http://www.snmp.com/eso/esoConsortiumMIB.txt */ oid usmAES192PrivProtocol[9] = { 1,3,6,1,4,1,14832,1,3 }; oid usmAES256PrivProtocol[9] = { 1,3,6,1,4,1,14832,1,4 }; /* OIDs from CISCO MIB */ oid usmAES192CiscoPrivProtocol[11] = { 1,3,6,1,4,1,9,12,6,1,1 }; oid usmAES256CiscoPrivProtocol[11] = { 1,3,6,1,4,1,9,12,6,1,2 }; /* * these OIDs are in pySNMP source as OIDs for AES+Reeder. We'll just * use OIDS from CISCO-SNMP-USM-OIDS-MIB * oid usmAES192Cisco2PrivProtocol[11] = { 1,3,6,1,4,1,9,12,6,1,101 }; oid usmAES256Cisco2PrivProtocol[11] = { 1,3,6,1,4,1,9,12,6,1,102 }; */ #endif /* NETSNMP_DRAFT_BLUMENTHAL_AES_04 */ typedef struct usm_alg_type_s { const char *label; int value; } usm_alg_type_t; static usm_alg_type_t usm_auth_type[] = { { "NOAUTH", NETSNMP_USMAUTH_NOAUTH }, { "SHA", NETSNMP_USMAUTH_HMACSHA1 }, { "SHA-1", NETSNMP_USMAUTH_HMACSHA1 }, { "SHA1", NETSNMP_USMAUTH_HMACSHA1 }, #ifndef NETSNMP_DISABLE_MD5 { "MD5", NETSNMP_USMAUTH_HMACMD5 }, #endif #ifdef HAVE_EVP_SHA224 { "SHA-224", NETSNMP_USMAUTH_HMAC128SHA224 }, { "SHA224", NETSNMP_USMAUTH_HMAC128SHA224 }, { "SHA-256", NETSNMP_USMAUTH_HMAC192SHA256 }, { "SHA256", NETSNMP_USMAUTH_HMAC192SHA256 }, #endif #ifdef HAVE_EVP_SHA384 { "SHA-384", NETSNMP_USMAUTH_HMAC256SHA384 }, { "SHA384", NETSNMP_USMAUTH_HMAC256SHA384 }, { "SHA-512", NETSNMP_USMAUTH_HMAC384SHA512 }, { "SHA512", NETSNMP_USMAUTH_HMAC384SHA512 }, #endif { NULL, -1 } }; static usm_alg_type_t usm_priv_type[] = { { "NOPRIV", USM_CREATE_USER_PRIV_NONE }, #ifndef NETSNMP_DISABLE_DES { "DES", USM_CREATE_USER_PRIV_DES }, #endif #ifdef HAVE_AES { "AES", USM_CREATE_USER_PRIV_AES }, { "AES-128", USM_CREATE_USER_PRIV_AES }, { "AES128", USM_CREATE_USER_PRIV_AES }, #ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04 { "AES-192", USM_CREATE_USER_PRIV_AES192 }, { "AES192", USM_CREATE_USER_PRIV_AES192 }, { "AES-256", USM_CREATE_USER_PRIV_AES256 }, { "AES256", USM_CREATE_USER_PRIV_AES256 }, /** cisco / pysnmp variations */ { "AES-192-C", USM_CREATE_USER_PRIV_AES192_CISCO }, { "AES192C", USM_CREATE_USER_PRIV_AES192_CISCO }, { "AES-256-C", USM_CREATE_USER_PRIV_AES256_CISCO }, { "AES256C", USM_CREATE_USER_PRIV_AES256_CISCO }, #endif #endif { NULL, -1 }, }; static u_int dummy_etime, dummy_eboot; /* For ISENGINEKNOWN(). */ /* * Set up default snmpv3 parameter value storage. */ #ifdef NETSNMP_SECMOD_USM static const oid *defaultAuthType = NULL; static size_t defaultAuthTypeLen = 0; static const oid *defaultPrivType = NULL; static size_t defaultPrivTypeLen = 0; #endif /* NETSNMP_SECMOD_USM */ /* * 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 bytes for type pointed to by ref->. * Then copy in and record its length in ref->. * * 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; \ } int free_enginetime_on_shutdown(int majorid, int minorid, void *serverarg, void *clientarg) { u_char engineID[SNMP_MAX_ENG_SIZE]; size_t engineID_len = sizeof(engineID); DEBUGMSGTL(("snmpv3", "free enginetime callback called\n")); engineID_len = snmpv3_get_engineID(engineID, engineID_len); if (engineID_len > 0) free_enginetime(engineID, engineID_len); return 0; } struct usmStateReference * usm_malloc_usmStateReference(void) { struct usmStateReference *retval; retval = calloc(1, sizeof(struct usmStateReference)); if (retval) retval->refcnt = 1; return retval; } /* end usm_malloc_usmStateReference() */ static int usm_clone(netsnmp_pdu *pdu, netsnmp_pdu *new_pdu) { struct usmStateReference *ref = pdu->securityStateRef; struct usmStateReference **new_ref = (struct usmStateReference **)&new_pdu->securityStateRef; int ret = 0; if (!ref) return ret; if (pdu->command == SNMP_MSG_TRAP2) { netsnmp_assert(pdu->securityModel == SNMP_DEFAULT_SECMODEL); ret = usm_clone_usmStateReference(ref, new_ref); } else { netsnmp_assert(ref == *new_ref); ref->refcnt++; } return ret; } void usm_free_usmStateReference(void *old) { struct usmStateReference *ref = old; if (!ref) return; if (--ref->refcnt > 0) return; SNMP_FREE(ref->usr_name); SNMP_FREE(ref->usr_engine_id); SNMP_FREE(ref->usr_auth_protocol); SNMP_FREE(ref->usr_priv_protocol); if (ref->usr_auth_key_length && ref->usr_auth_key) { SNMP_ZERO(ref->usr_auth_key, ref->usr_auth_key_length); SNMP_FREE(ref->usr_auth_key); } if (ref->usr_priv_key_length && ref->usr_priv_key) { SNMP_ZERO(ref->usr_priv_key, ref->usr_priv_key_length); SNMP_FREE(ref->usr_priv_key); } SNMP_FREE(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; } int usm_clone_usmStateReference(struct usmStateReference *from, struct usmStateReference **to) { struct usmStateReference *cloned_usmStateRef; if (from == NULL || to == NULL) return -1; *to = usm_malloc_usmStateReference(); cloned_usmStateRef = *to; if (usm_set_usmStateReference_name(cloned_usmStateRef, from->usr_name, from->usr_name_length) || usm_set_usmStateReference_engine_id(cloned_usmStateRef, from->usr_engine_id, from->usr_engine_id_length) || usm_set_usmStateReference_auth_protocol(cloned_usmStateRef, from->usr_auth_protocol, from->usr_auth_protocol_length) || usm_set_usmStateReference_auth_key(cloned_usmStateRef, from->usr_auth_key, from->usr_auth_key_length) || usm_set_usmStateReference_priv_protocol(cloned_usmStateRef, from->usr_priv_protocol, from->usr_priv_protocol_length) || usm_set_usmStateReference_priv_key(cloned_usmStateRef, from->usr_priv_key, from->usr_priv_key_length) || usm_set_usmStateReference_sec_level(cloned_usmStateRef, from->usr_sec_level)) { usm_free_usmStateReference(*to); *to = NULL; return -1; } 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 + + , where * * 1 For the ASN.1 type. * # of bytes to store length of data. * 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 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, ret; /* * 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, NULL, 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, NULL, secNameLen)) == -1) { return -1; } if ((authlen = asn_predict_length(ASN_OCTET_STR, NULL, *msgAuthParmLen)) == -1) { return -1; } if ((privlen = asn_predict_length(ASN_OCTET_STR, NULL, *msgPrivParmLen)) == -1) { return -1; } *seq_len = engIDlen + engBtlen + engTmlen + namelen + authlen + privlen; if ((ret = asn_predict_length(ASN_SEQUENCE, NULL, *seq_len)) == -1) { return -1; } *otstlen = (size_t)ret; if ((ret = asn_predict_length(ASN_OCTET_STR, NULL, *otstlen)) == -1) { return -1; } *msgSecParmLen = (size_t)ret; *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 ((ret = asn_predict_length(ASN_OCTET_STR, NULL, scopedPduLen)) == -1) { return -1; } *datalen = (size_t)ret; } 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 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)); 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)); 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")); 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")); 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)]; int priv_type = sc_get_privtype(thePrivProtocol, thePrivProtocolLength); #ifdef HAVE_AES if (USM_CREATE_USER_PRIV_AES == (priv_type & USM_PRIV_MASK_ALG)) { 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")); return SNMPERR_USM_GENERICERROR; } } #endif #ifndef NETSNMP_DISABLE_DES /* * XXX Hardwired to seek into a 1DES private key! */ if (USM_CREATE_USER_PRIV_DES == (priv_type & USM_PRIV_MASK_ALG)) { if (!thePrivKey || (usm_set_salt(salt, &salt_length, thePrivKey + 8, thePrivKeyLength - 8, &ptr[privParamsOffset]) == -1)) { DEBUGMSGTL(("usm", "Can't set DES-CBC salt.\n")); 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")); 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")); 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")); 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")); 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")); 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 */ 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; 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; 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", (int)*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")); 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)); 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_ functions, but * this will do for now. */ u_char *ciphertext = NULL; size_t ciphertextlen = scopedPduLen + 64; int priv_type = sc_get_privtype(thePrivProtocol, thePrivProtocolLength); if ((ciphertext = (u_char *) malloc(ciphertextlen)) == NULL) { DEBUGMSGTL(("usm", "couldn't malloc %d bytes for encrypted PDU\n", (int)ciphertextlen)); return SNMPERR_MALLOC; } /* * XXX Hardwired to seek into a 1DES private key! */ #ifdef HAVE_AES if (USM_CREATE_USER_PRIV_AES == (priv_type & USM_PRIV_MASK_ALG)) { salt_length = BYTESIZE(USM_AES_SALT_LENGTH); save_salt_length = BYTESIZE(USM_AES_SALT_LENGTH)/2; 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")); SNMP_FREE(ciphertext); return SNMPERR_USM_GENERICERROR; } } #endif #ifndef NETSNMP_DISABLE_DES if (USM_CREATE_USER_PRIV_DES == (priv_type & USM_PRIV_MASK_ALG)) { salt_length = BYTESIZE(USM_DES_SALT_LENGTH); save_salt_length = BYTESIZE(USM_DES_SALT_LENGTH); if (!thePrivKey || (usm_set_salt(salt, &salt_length, thePrivKey + 8, thePrivKeyLength - 8, iv) == -1)) { DEBUGMSGTL(("usm", "Can't set DES-CBC salt.\n")); 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")); SNMP_FREE(ciphertext); return SNMPERR_USM_ENCRYPTIONERROR; } /* * Write the encrypted scopedPdu back into the packet buffer. */ *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")); 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")); return SNMPERR_TOO_LONG; } DEBUGDUMPHEADER("send", "msgAuthenticationParameters"); /* * msgAuthenticationParameters. */ if (theSecLevel == SNMP_SEC_LEVEL_AUTHNOPRIV || theSecLevel == SNMP_SEC_LEVEL_AUTHPRIV) { memset(authParams, 0, sizeof(authParams)); msgAuthParmLen = sc_get_auth_maclen(sc_get_authtype(theAuthProtocol, theAuthProtocolLength)); } 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")); 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. This works as long as * auth lengths stay < 127. */ 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")); 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")); 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")); 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")); 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")); 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")); 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")); 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")); 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")); 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")); return SNMPERR_USM_AUTHENTICATIONFAILURE; } if (temp_sig_len != msgAuthParmLen) { SNMP_FREE(temp_sig); DEBUGMSGTL(("usm", "Signing lengths failed.\n")); return SNMPERR_USM_AUTHENTICATIONFAILURE; } memcpy(*wholeMsg + *wholeMsgLen - mac_offset, temp_sig, msgAuthParmLen); SNMP_FREE(temp_sig); } /* * endif -- create keyed hash */ 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 (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 (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) { snmp_increment_statistic(STAT_USMSTATSNOTINTIMEWINDOWS); 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 #ifndef NETSNMP_DISABLE_DES int i; #endif u_char signature[USM_MAX_AUTHSIZE]; size_t signature_length = USM_MAX_AUTHSIZE; 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 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. */ snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS); return SNMPERR_USM_DECRYPTIONERROR; } snmp_increment_statistic(STAT_SNMPINASNPARSEERRS); 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)) { snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS); 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")); snmp_increment_statistic(STAT_USMSTATSUNKNOWNENGINEIDS); 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)); snmp_increment_statistic(STAT_USMSTATSUNKNOWNUSERNAMES); return SNMPERR_USM_UNKNOWNSECURITYNAME; } /* ensure the user is active */ if (user->userStatus != RS_ACTIVE) { DEBUGMSGTL(("usm", "Attempt to use an inactive user.\n")); return SNMPERR_USM_UNKNOWNSECURITYNAME; } /* * Make sure the security level is appropriate. */ rc = usm_check_secLevel(secLevel, user); if (1 == rc) { DEBUGMSGTL(("usm", "Unsupported Security Level (%d).\n", secLevel)); snmp_increment_statistic(STAT_USMSTATSUNSUPPORTEDSECLEVELS); return SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL; } else if (rc != 0) { DEBUGMSGTL(("usm", "Unknown issue.\n")); return SNMPERR_USM_GENERICERROR; } /* * 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")); snmp_increment_statistic(STAT_USMSTATSWRONGDIGESTS); 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) { int priv_type = sc_get_privtype(user->privProtocol, user->privProtocolLen); 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.")); snmp_increment_statistic(STAT_SNMPINASNPARSEERRS); usm_free_usmStateReference(*secStateRef); *secStateRef = NULL; return SNMPERR_USM_PARSEERROR; } #ifndef NETSNMP_DISABLE_DES if (USM_CREATE_USER_PRIV_DES == (priv_type & USM_PRIV_MASK_ALG)) { /* * 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 %lu)\n", (unsigned long)remaining, (unsigned long)remaining % 8)); snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS); 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)); snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS); 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 (USM_CREATE_USER_PRIV_AES == (priv_type & USM_PRIV_MASK_ALG)) { 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 #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); } #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.")); snmp_increment_statistic(STAT_USMSTATSDECRYPTIONERRORS); return SNMPERR_USM_DECRYPTIONERROR; } #ifdef NETSNMP_ENABLE_TESTING_CODE if (debug_is_token_registered("usm/dump") == SNMPERR_SUCCESS) { 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 - (end_of_overhead - 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; } /* fallthrough */ 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; } } /** utility function to call netsnmp_extend_kul for a usmUser */ int usm_extend_user_kul(struct usmUser *user, u_int privKeyBufSize) { netsnmp_priv_alg_info *pai; DEBUGMSGTL(("usm", "extending key\n")); if (NULL == user) { DEBUGMSGTL(("usm", "null user!\n")); return SNMPERR_GENERR; } pai = sc_get_priv_alg_byoid(user->privProtocol, user->privProtocolLen); if (NULL == pai) { DEBUGMSGTL(("usm", "privProtocol lookup failed!\n")); return SNMPERR_GENERR; } return netsnmp_extend_kul(pai->proper_length, user->authProtocol, user->authProtocolLen, pai->type, user->engineID, user->engineIDLen, &user->privKey, &user->privKeyLen, privKeyBufSize); } /* sets up initial default session parameters */ int usm_session_init(netsnmp_session *in_session, netsnmp_session *session) { char *cp; size_t i; if (in_session->securityAuthProtoLen > 0) { session->securityAuthProto = snmp_duplicate_objid(in_session->securityAuthProto, in_session->securityAuthProtoLen); if (session->securityAuthProto == NULL) { in_session->s_snmp_errno = SNMPERR_MALLOC; return SNMPERR_MALLOC; } } else if (get_default_authtype(&i) != NULL) { session->securityAuthProto = snmp_duplicate_objid(get_default_authtype(NULL), i); session->securityAuthProtoLen = i; } if (in_session->securityPrivProtoLen > 0) { session->securityPrivProto = snmp_duplicate_objid(in_session->securityPrivProto, in_session->securityPrivProtoLen); if (session->securityPrivProto == NULL) { in_session->s_snmp_errno = SNMPERR_MALLOC; return SNMPERR_MALLOC; } } else if (get_default_privtype(&i) != NULL) { session->securityPrivProto = snmp_duplicate_objid(get_default_privtype(NULL), i); session->securityPrivProtoLen = i; } if ((in_session->securityAuthKeyLen <= 0) && ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_AUTHMASTERKEY)))) { size_t buflen = sizeof(session->securityAuthKey); u_char *tmpp = session->securityAuthKey; session->securityAuthKeyLen = 0; /* it will be a hex string */ if (!snmp_hex_to_binary(&tmpp, &buflen, &session->securityAuthKeyLen, 0, cp)) { snmp_set_detail("error parsing authentication master key"); return SNMP_ERR_GENERR; } } else if ((in_session->securityAuthKeyLen <= 0) && ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_AUTHPASSPHRASE)) || (cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PASSPHRASE)))) { session->securityAuthKeyLen = USM_AUTH_KU_LEN; if (generate_Ku(session->securityAuthProto, session->securityAuthProtoLen, (u_char *) cp, strlen(cp), session->securityAuthKey, &session->securityAuthKeyLen) != SNMPERR_SUCCESS) { snmp_set_detail ("Error generating a key (Ku) from the supplied authentication pass phrase."); return SNMP_ERR_GENERR; } } if ((in_session->securityPrivKeyLen <= 0) && ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRIVMASTERKEY)))) { size_t buflen = sizeof(session->securityPrivKey); u_char *tmpp = session->securityPrivKey; session->securityPrivKeyLen = 0; /* it will be a hex string */ if (!snmp_hex_to_binary(&tmpp, &buflen, &session->securityPrivKeyLen, 0, cp)) { snmp_set_detail("error parsing encryption master key"); return SNMP_ERR_GENERR; } } else if ((in_session->securityPrivKeyLen <= 0) && ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRIVPASSPHRASE)) || (cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PASSPHRASE)))) { session->securityPrivKeyLen = USM_PRIV_KU_LEN; if (generate_Ku(session->securityAuthProto, session->securityAuthProtoLen, (u_char *) cp, strlen(cp), session->securityPrivKey, &session->securityPrivKeyLen) != SNMPERR_SUCCESS) { snmp_set_detail ("Error generating a key (Ku) from the supplied privacy pass phrase."); return SNMP_ERR_GENERR; } } return SNMPERR_SUCCESS; } /* * usm_create_user_from_session(netsnmp_session *session): * * creates a user in the usm table from the information in a session. * If the user already exists, it is updated with the current * information from the session * * Parameters: * session -- IN: pointer to the session to use when creating the user. * * Returns: * SNMPERR_SUCCESS * SNMPERR_GENERR */ int usm_create_user_from_session(netsnmp_session * session) { struct usmUser *user; int user_just_created = 0; char *cp; /* * - don't create-another/copy-into user for this session by default * - bail now (no error) if we don't have an engineID */ if (SNMP_FLAGS_USER_CREATED == (session->flags & SNMP_FLAGS_USER_CREATED) || session->securityModel != SNMP_SEC_MODEL_USM || session->version != SNMP_VERSION_3 || session->securityNameLen == 0 || session->securityEngineIDLen == 0) return SNMPERR_SUCCESS; DEBUGMSGTL(("usm", "no flag defined... continuing\n")); session->flags |= SNMP_FLAGS_USER_CREATED; /* * now that we have the engineID, create an entry in the USM list * for this user using the information in the session */ user = usm_get_user_from_list(session->securityEngineID, session->securityEngineIDLen, session->securityName, usm_get_userList(), 0); if (NULL != user) DEBUGMSGTL(("usm", "user exists x=%p\n", user)); else if (user == NULL) { DEBUGMSGTL(("usm", "Building user %s...\n", session->securityName)); /* * user doesn't exist so we create and add it */ user = (struct usmUser *) calloc(1, sizeof(struct usmUser)); if (user == NULL) return SNMPERR_GENERR; /* * copy in the securityName */ if (session->securityName) { user->name = strdup(session->securityName); user->secName = strdup(session->securityName); if (user->name == NULL || user->secName == NULL) { usm_free_user(user); return SNMPERR_GENERR; } } /* * copy in the engineID */ user->engineID = netsnmp_memdup(session->securityEngineID, session->securityEngineIDLen); if (session->securityEngineID && !user->engineID) { usm_free_user(user); return SNMPERR_GENERR; } user->engineIDLen = session->securityEngineIDLen; user_just_created = 1; } /* * copy the auth protocol */ if (user->authProtocol == NULL && session->securityAuthProto != NULL) { SNMP_FREE(user->authProtocol); user->authProtocol = snmp_duplicate_objid(session->securityAuthProto, session->securityAuthProtoLen); if (user->authProtocol == NULL) { usm_free_user(user); return SNMPERR_GENERR; } user->authProtocolLen = session->securityAuthProtoLen; } /* * copy the priv protocol */ if (user->privProtocol == NULL && session->securityPrivProto != NULL) { SNMP_FREE(user->privProtocol); user->privProtocol = snmp_duplicate_objid(session->securityPrivProto, session->securityPrivProtoLen); if (user->privProtocol == NULL) { usm_free_user(user); return SNMPERR_GENERR; } user->privProtocolLen = session->securityPrivProtoLen; } /* * copy in the authentication Key. If not localized, localize it */ if (user->authKey == NULL) { if (session->securityAuthLocalKey != NULL && session->securityAuthLocalKeyLen != 0) { /* already localized key passed in. use it */ SNMP_FREE(user->authKey); user->authKey = netsnmp_memdup(session->securityAuthLocalKey, session->securityAuthLocalKeyLen); if (!user->authKey) { usm_free_user(user); return SNMPERR_GENERR; } user->authKeyLen = session->securityAuthLocalKeyLen; } else if (session->securityAuthKey != NULL && session->securityAuthKeyLen != 0) { SNMP_FREE(user->authKey); user->authKey = (u_char *) calloc(1, USM_LENGTH_KU_HASHBLOCK); user->authKeyLen = USM_LENGTH_KU_HASHBLOCK; if ((user->authKey == NULL) || generate_kul(user->authProtocol, user->authProtocolLen, user->engineID, user->engineIDLen, session->securityAuthKey, session->securityAuthKeyLen, user->authKey, &user->authKeyLen) != SNMPERR_SUCCESS) { usm_free_user(user); return SNMPERR_GENERR; } } else if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_AUTHLOCALIZEDKEY))) { size_t buflen = USM_AUTH_KU_LEN; SNMP_FREE(user->authKey); user->authKey = (u_char *)malloc(buflen); /* max length needed */ user->authKeyLen = 0; /* it will be a hex string */ if ((NULL == user->authKey) || !snmp_hex_to_binary(&user->authKey, &buflen, &user->authKeyLen, 0, cp)) { usm_free_user(user); return SNMPERR_GENERR; } } } /* * copy in the privacy Key. If not localized, localize it */ if (user->privKey == NULL) { /** save buffer size in case we need to extend key */ int keyBufSize = USM_PRIV_KU_LEN; DEBUGMSGTL(("usm", "copying privKey\n")); if (session->securityPrivLocalKey != NULL && session->securityPrivLocalKeyLen != 0) { /* already localized key passed in. use it */ SNMP_FREE(user->privKey); user->privKey = netsnmp_memdup(session->securityPrivLocalKey, session->securityPrivLocalKeyLen); if (!user->privKey) { usm_free_user(user); return SNMPERR_GENERR; } keyBufSize = user->privKeyLen = session->securityPrivLocalKeyLen; } else if (session->securityPrivKey != NULL && session->securityPrivKeyLen != 0) { SNMP_FREE(user->privKey); user->privKey = (u_char *) calloc(1, keyBufSize); user->privKeyLen = keyBufSize; if ((user->privKey == NULL) || generate_kul(user->authProtocol, user->authProtocolLen, user->engineID, user->engineIDLen, session->securityPrivKey, session->securityPrivKeyLen, user->privKey, &user->privKeyLen) != SNMPERR_SUCCESS) { usm_free_user(user); return SNMPERR_GENERR; } } else if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRIVLOCALIZEDKEY))) { size_t buflen = keyBufSize; user->privKey = (u_char *)malloc(buflen); /* max length needed */ user->privKeyLen = 0; /* it will be a hex string */ if ((NULL == user->privKey) || !snmp_hex_to_binary(&user->privKey, &buflen, &user->privKeyLen, 0, cp)) { usm_free_user(user); return SNMPERR_GENERR; } } if (usm_extend_user_kul(user, keyBufSize) != SNMPERR_SUCCESS) { usm_free_user(user); return SNMPERR_GENERR; } } if (user_just_created) { /* * add the user into the database */ user->userStatus = RS_ACTIVE; user->userStorageType = ST_READONLY; usm_add_user(user); } DEBUGMSGTL(("9:usm", "user created\n")); return SNMPERR_SUCCESS; } /* A wrapper around the hook */ int usm_create_user_from_session_hook(void *slp, netsnmp_session *session) { DEBUGMSGTL(("usm", "potentially bootstrapping the USM table from session data\n")); return usm_create_user_from_session(session); } static int usm_build_probe_pdu(netsnmp_pdu **pdu) { struct usmUser *user; /* * create the pdu */ if (!pdu) return -1; *pdu = snmp_pdu_create(SNMP_MSG_GET); if (!(*pdu)) return -1; (*pdu)->version = SNMP_VERSION_3; (*pdu)->securityName = strdup(""); (*pdu)->securityNameLen = strlen((*pdu)->securityName); (*pdu)->securityLevel = SNMP_SEC_LEVEL_NOAUTH; (*pdu)->securityModel = SNMP_SEC_MODEL_USM; /* * create the empty user */ user = usm_get_user(NULL, 0, (*pdu)->securityName); if (user == NULL) { user = (struct usmUser *) calloc(1, sizeof(struct usmUser)); if (user == NULL) { snmp_free_pdu(*pdu); *pdu = (netsnmp_pdu *) NULL; return -1; } user->name = strdup((*pdu)->securityName); user->secName = strdup((*pdu)->securityName); user->authProtocolLen = sizeof(usmNoAuthProtocol) / sizeof(oid); user->authProtocol = snmp_duplicate_objid(usmNoAuthProtocol, user->authProtocolLen); user->privProtocolLen = sizeof(usmNoPrivProtocol) / sizeof(oid); user->privProtocol = snmp_duplicate_objid(usmNoPrivProtocol, user->privProtocolLen); usm_add_user(user); } return 0; } int usm_discover_engineid(void *slpv, netsnmp_session *session) { netsnmp_pdu *pdu = NULL, *response = NULL; int status, i; struct session_list *slp = (struct session_list *) slpv; if (usm_build_probe_pdu(&pdu) != 0) { DEBUGMSGTL(("snmp_api", "unable to create probe PDU\n")); return SNMP_ERR_GENERR; } DEBUGMSGTL(("snmp_api", "probing for engineID...\n")); session->flags |= SNMP_FLAGS_DONT_PROBE; /* prevent recursion */ status = snmp_sess_synch_response(slp, pdu, &response); if ((response == NULL) && (status == STAT_SUCCESS)) { status = STAT_ERROR; } switch (status) { case STAT_SUCCESS: session->s_snmp_errno = SNMPERR_INVALID_MSG; /* XX?? */ DEBUGMSGTL(("snmp_sess_open", "error: expected Report as response to probe: %s (%ld)\n", snmp_errstring(response->errstat), response->errstat)); break; case STAT_ERROR: /* this is what we expected -> Report == STAT_ERROR */ session->s_snmp_errno = SNMPERR_UNKNOWN_ENG_ID; break; case STAT_TIMEOUT: session->s_snmp_errno = SNMPERR_TIMEOUT; break; default: DEBUGMSGTL(("snmp_sess_open", "unable to connect with remote engine: %s (%d)\n", snmp_api_errstring(session->s_snmp_errno), session->s_snmp_errno)); break; } if (slp->session->securityEngineIDLen == 0) { DEBUGMSGTL(("snmp_api", "unable to determine remote engine ID\n")); /* clear the flag so that probe occurs on next inform */ session->flags &= ~SNMP_FLAGS_DONT_PROBE; return SNMP_ERR_GENERR; } session->s_snmp_errno = SNMPERR_SUCCESS; if (snmp_get_do_debugging()) { DEBUGMSGTL(("snmp_sess_open", " probe found engineID: ")); for (i = 0; i < slp->session->securityEngineIDLen; i++) DEBUGMSG(("snmp_sess_open", "%02x", slp->session->securityEngineID[i])); DEBUGMSG(("snmp_sess_open", "\n")); } /* * if boot/time supplied set it for this engineID */ if (session->engineBoots || session->engineTime) { set_enginetime(session->securityEngineID, session->securityEngineIDLen, session->engineBoots, session->engineTime, TRUE); } return SNMPERR_SUCCESS; } void init_usm(void) { struct snmp_secmod_def *def; char *type; DEBUGMSGTL(("init_usm", "unit_usm: %" NETSNMP_PRIo "u %" NETSNMP_PRIo "u\n", usmNoPrivProtocol[0], usmNoPrivProtocol[1])); sc_init(); /* initalize scapi code */ /* * register ourselves as a security service */ def = SNMP_MALLOC_STRUCT(snmp_secmod_def); if (def == NULL) return; /* * 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_clone = usm_clone; def->pdu_free_state_ref = usm_free_usmStateReference; def->session_setup = usm_session_init; def->handle_report = usm_handle_report; def->probe_engineid = usm_discover_engineid; def->post_probe_engineid = usm_create_user_from_session_hook; if (register_sec_mod(USM_SEC_MODEL_NUMBER, "usm", def) != SNMPERR_SUCCESS) { SNMP_FREE(def); snmp_log(LOG_ERR, "could not register usm sec mod\n"); return; } 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); register_config_handler("snmp", "defAuthType", snmpv3_authtype_conf, NULL, "MD5|SHA|SHA-512|SHA-384|SHA-256|SHA-224"); register_config_handler("snmp", "defPrivType", snmpv3_privtype_conf, NULL, "DES" #ifdef HAVE_AES "|AES|AES-128" #ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04 "|AES-192|AES-256" #endif /* NETSNMP_DRAFT_BLUMENTHAL_AES_04 */ #else " (AES support not available)" #endif ); /* * Free stuff at shutdown time */ snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN, free_enginetime_on_shutdown, NULL); type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE); register_config_handler(type, "userSetAuthPass", usm_set_password, NULL, NULL); register_config_handler(type, "userSetPrivPass", usm_set_password, NULL, NULL); register_config_handler(type, "userSetAuthKey", usm_set_password, NULL, NULL); register_config_handler(type, "userSetPrivKey", usm_set_password, NULL, NULL); register_config_handler(type, "userSetAuthLocalKey", usm_set_password, NULL, NULL); register_config_handler(type, "userSetPrivLocalKey", usm_set_password, NULL, NULL); } int usm_lookup_alg_type(const char *str, usm_alg_type_t *types) { int i, l; l = strlen(str); for (i = 0; types[i].label; ++i) { if (0 == strncasecmp(types[i].label, str, l)) return types[i].value; } return -1; } const char * usm_lookup_alg_str(int value, usm_alg_type_t *types) { int i; for (i = 0; types[i].label; ++i) if (value == types[i].value) return types[i].label; return NULL; } int usm_lookup_auth_type(const char *str) { return usm_lookup_alg_type(str, usm_auth_type ); } int usm_lookup_priv_type(const char *str) { return usm_lookup_alg_type(str, usm_priv_type ); } const char * usm_lookup_auth_str(int value) { return usm_lookup_alg_str(value, usm_auth_type ); } const char * usm_lookup_priv_str(int value) { return usm_lookup_alg_str(value, usm_priv_type ); } 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|SHA-512|SHA-384|SHA-256|SHA-224|default) authpassphrase [(DES|AES|default) [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, OID_LENGTH(usmHMACMD5AuthProtocol), SNMP_DEFAULT_PRIV_PROTO, SNMP_DEFAULT_PRIV_PROTOLEN); #else noNameUser = usm_create_initial_user("", usmHMACSHA1AuthProtocol, OID_LENGTH(usmHMACSHA1AuthProtocol), SNMP_DEFAULT_PRIV_PROTO, SNMP_DEFAULT_PRIV_PROTOLEN); #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; } void shutdown_usm(void) { free_etimelist(); clear_user_list(); } /*******************************************************************-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: %" NETSNMP_PRIo "u %" NETSNMP_PRIo "u ", 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 (")); if (engineID) { DEBUGMSGHEX(("usm", engineID, engineIDLen)); } else { DEBUGMSGTL(("usm", "Empty EngineID")); } 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); } /* * usm_remove_usmUser remove user from (optional) list * * if list is not specified, defaults to global userList. * * returns SNMPERR_SUCCESS or SNMPERR_USM_UNKNOWNSECURITYNAME */ int usm_remove_usmUser_from_list(struct usmUser *user, struct usmUser **ppuserList) { struct usmUser *nptr, *pptr; /* * NULL pointers aren't allowed */ if (ppuserList == NULL) ppuserList = &userList; if (*ppuserList == NULL) return SNMPERR_USM_UNKNOWNSECURITYNAME; /* * 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 SNMPERR_USM_UNKNOWNSECURITYNAME; } if (nptr == *ppuserList) /* we're the head of the list, need to change * * the head to the next user */ *ppuserList = nptr->next; return SNMPERR_SUCCESS; } /* end usm_remove_user_from_list() */ int usm_remove_usmUser(struct usmUser *user) { return usm_remove_usmUser_from_list(user, &userList); } /* * usm_remove_user_from_list * * removes user from list. * * returns new list head on success, or NULL on error. * * NOTE: if there was only one user in the list, list head will be NULL. * So NULL can also mean success. Use the newer usm_remove_usmUser() for * more specific return codes. This function is kept for backwards * compatability with this ambiguous behaviour. */ struct usmUser * usm_remove_user_from_list(struct usmUser *user, struct usmUser **ppuserList) { int rc = usm_remove_usmUser_from_list(user, ppuserList); if (rc != SNMPERR_SUCCESS || NULL == ppuserList) return NULL; 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); } if (user->authKeyKu != NULL) { SNMP_ZERO(user->authKeyKu, user->authKeyKuLen); SNMP_FREE(user->authKeyKu); } if (user->privKeyKu != NULL) { SNMP_ZERO(user->privKeyKu, user->privKeyKuLen); SNMP_FREE(user->privKeyKu); } /* * 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() */ #ifndef NETSNMP_NO_WRITE_SUPPORT /* * take a given user and clone the security info into another */ struct usmUser * usm_cloneFrom_user(struct usmUser *from, struct usmUser *to) { to->flags = from->flags; /* * 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 authKeyKu */ SNMP_FREE(to->authKeyKu); if (from->authKeyKuLen > 0 && (to->authKeyKu = (u_char *) malloc(from->authKeyKuLen)) != NULL) { to->authKeyKuLen = from->authKeyKuLen; memcpy(to->authKeyKu, from->authKeyKu, to->authKeyKuLen); } else { to->authKeyKu = NULL; to->authKeyKuLen = 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; } /* * copy the privKeyKu */ SNMP_FREE(to->privKeyKu); if (from->privKeyKuLen > 0 && (to->privKeyKu = (u_char *) malloc(from->privKeyKuLen)) != NULL) { to->privKeyKuLen = from->privKeyKuLen; memcpy(to->privKeyKu, from->privKeyKu, to->privKeyKuLen); } else { to->privKeyKu = NULL; to->privKeyKuLen = 0; } return to; } #endif /* NETSNMP_NO_WRITE_SUPPORT */ /* * 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)); *cptr++ = ' '; cptr = read_config_save_octet_string(cptr, (u_char *) user->secName, (user->secName == NULL) ? 0 : strlen(user->secName)); *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->userPublicStringLen); 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(const char *line) { struct usmUser *user; size_t len, proper_length, privtype; user = usm_create_user(); if (user == NULL) return NULL; user->userStatus = atoi(line); line = skip_token_const(line); user->userStorageType = atoi(line); line = skip_token_const(line); line = read_config_read_octet_string_const(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_const(line, &user->cloneFrom, &user->cloneFromLen); SNMP_FREE(user->authProtocol); user->authProtocolLen = 0; line = read_config_read_objid_const(line, &user->authProtocol, &user->authProtocolLen); line = read_config_read_octet_string_const(line, &user->authKey, &user->authKeyLen); SNMP_FREE(user->privProtocol); user->privProtocolLen = 0; line = read_config_read_objid_const(line, &user->privProtocol, &user->privProtocolLen); line = read_config_read_octet_string(line, &user->privKey, &user->privKeyLen); privtype = sc_get_privtype(user->privProtocol, user->privProtocolLen); proper_length = sc_get_proper_priv_length_bytype(privtype); if (USM_CREATE_USER_PRIV_DES == privtype) proper_length *= 2; /* ?? we store salt with key */ /* For backwards compatibility */ if (user->privKeyLen > proper_length) { user->privKeyLen = proper_length; } line = read_config_read_octet_string(line, &user->userPublicString, &user->userPublicStringLen); 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 = NULL; size_t engineIDLen = 0; 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"); SNMP_FREE(engineID); return; } user = usm_get_user(engineID, engineIDLen, nameBuf); if (user == NULL) { config_perror("not a valid user/engineID pair"); SNMP_FREE(engineID); return; } usm_set_user_password(user, token, cp); SNMP_FREE(engineID); } } /* * 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; } /* save master key */ if (user->flags & USMUSER_FLAG_KEEP_MASTER_KEY) { if (userKey == user->privKey) { user->privKeyKu = netsnmp_memdup(userKey, userKeyLen); user->privKeyKuLen = userKeyLen; } else if (userKey == user->authKey) { user->authKeyKu = netsnmp_memdup(userKey, userKeyLen); user->authKeyKuLen = userKeyLen; } } } 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; } } if (key == &user->privKey) { ret = usm_extend_user_kul(user, *keyLen); if (SNMPERR_SUCCESS != ret) { config_perror("error extending localized user key"); return; } } } /* end usm_set_password() */ /* * create a usm user from a string. * * The format for the string is described in the createUser * secion of the snmpd.conf man page. * * On success, a pointer to the created usmUser struct is returned. * On error, a NULL pointer is returned. In this case, if a pointer to a * char pointer is provided in errorMsg, an error string is returned. * This error string points to a static message, and should not be * freed. */ struct usmUser * usm_create_usmUser_from_string(char *line, const char **errorMsg) { char *cp; const char *dummy; char buf[SNMP_MAXBUF_MEDIUM]; struct usmUser *newuser; u_char userKey[SNMP_MAXBUF_SMALL], *tmpp; size_t userKeyLen = SNMP_MAXBUF_SMALL; size_t privKeySize; size_t ret; int ret2, properLen, properPrivKeyLen; const oid *def_auth_prot, *def_priv_prot; size_t def_auth_prot_len, def_priv_prot_len; netsnmp_priv_alg_info *pai; def_auth_prot = get_default_authtype(&def_auth_prot_len); def_priv_prot = get_default_privtype(&def_priv_prot_len); if (NULL == line) return NULL; #ifdef NETSNMP_ENABLE_TESTING_CODE DEBUGMSGTL(("usmUser", "new user %s\n", line)); /* logs passphrases */ #endif if (NULL == errorMsg) errorMsg = &dummy; *errorMsg = NULL; /* no errors yet */ newuser = usm_create_user(); if (newuser == NULL) { *errorMsg = "malloc failure creating new user"; goto fail; } /* * READ: Security Name */ cp = copy_nword(line, buf, sizeof(buf)); /* * check for (undocumented) 'keep master key' flag. so far, this is * just used for users for informs (who need non-localized keys). */ if (strcmp(buf, "-M") == 0) { newuser->flags |= USMUSER_FLAG_KEEP_MASTER_KEY; cp = copy_nword(cp, buf, sizeof(buf)); } /* * might be a -e ENGINEID argument */ if (strcmp(buf, "-e") == 0) { size_t ebuf_len = 32, eout_len = 0; u_char *ebuf = (u_char *) malloc(ebuf_len); if (ebuf == NULL) { *errorMsg = "malloc failure processing -e flag"; goto fail; } /* * Get the specified engineid from the line. */ cp = copy_nword(cp, buf, sizeof(buf)); if (!snmp_hex_to_binary(&ebuf, &ebuf_len, &eout_len, 1, buf)) { *errorMsg = "invalid EngineID argument to -e"; SNMP_FREE(ebuf); goto fail; } newuser->engineID = ebuf; newuser->engineIDLen = eout_len; cp = copy_nword(cp, buf, sizeof(buf)); } else { newuser->engineID = snmpv3_generate_engineID(&ret); if (ret == 0) { goto fail; } newuser->engineIDLen = ret; } newuser->secName = strdup(buf); newuser->name = strdup(buf); if (!cp) { #ifdef NETSNMP_FORCE_SYSTEM_V3_AUTHPRIV /** no passwords ok iff defaults are noauth/nopriv */ if (snmp_oid_compare(usmNoAuthProtocol, OID_LENGTH(usmNoAuthProtocol), def_auth_prot, def_auth_prot_len) != 0) { *errorMsg = "no authentication pass phrase"; goto fail; } if (snmp_oid_compare(usmNoPrivProtocol, OID_LENGTH(usmNoPrivProtocol), def_priv_prot, def_priv_prot_len) != 0) { *errorMsg = "no privacy pass phrase"; goto fail; } #endif /* NETSNMP_FORCE_SYSTEM_V3_AUTHPRIV */ goto add; /* no authentication or privacy type */ } /* * READ: Authentication Type */ newuser->authProtocol[0] = 0; cp = copy_nword(cp, buf, sizeof(buf)); if ((strncmp(cp, "default", 7) == 0) && (NULL != def_auth_prot)) { SNMP_FREE(newuser->authProtocol); newuser->authProtocol = snmp_duplicate_objid(def_auth_prot, def_auth_prot_len); if (newuser->authProtocol == NULL) { *errorMsg = "malloc failed"; goto fail; } newuser->authProtocolLen = def_auth_prot_len; } else { const oid *auth_prot; int auth_type = usm_lookup_auth_type(buf); if (auth_type < 0) { *errorMsg = "unknown authProtocol"; goto fail; } auth_prot = sc_get_auth_oid(auth_type, &newuser->authProtocolLen); if (auth_prot) { SNMP_FREE(newuser->authProtocol); newuser->authProtocol = snmp_duplicate_objid(auth_prot, newuser->authProtocolLen); } if (newuser->authProtocol == NULL) { *errorMsg = "malloc failed"; goto fail; } } if (0 == newuser->authProtocol[0]) { *errorMsg = "Unknown authentication protocol"; goto fail; } #ifdef NETSNMP_FORCE_SYSTEM_V3_AUTHPRIV if (snmp_oid_compare(newuser->authProtocol, newuser->authProtocolLen, def_auth_prot, def_auth_prot_len) != 0) { *errorMsg = "auth protocol does not match system policy"; goto fail; } #endif /* NETSNMP_FORCE_SYSTEM_V3_AUTHPRIV */ /* * READ: Authentication Pass Phrase or key */ cp = copy_nword(cp, buf, sizeof(buf)); if (strcmp(buf,"-m") == 0) { /* a master key is specified */ cp = copy_nword(cp, buf, sizeof(buf)); ret = sizeof(userKey); tmpp = userKey; userKeyLen = 0; if (!snmp_hex_to_binary(&tmpp, &ret, &userKeyLen, 0, buf)) { *errorMsg = "invalid key value argument to -m"; goto fail; } /* save master key */ if (newuser->flags & USMUSER_FLAG_KEEP_MASTER_KEY) { newuser->authKeyKu = netsnmp_memdup(userKey, userKeyLen); newuser->authKeyKuLen = userKeyLen; } } else if (strcmp(buf,"-l") != 0) { /* a password is specified */ userKeyLen = sizeof(userKey); ret2 = generate_Ku(newuser->authProtocol, newuser->authProtocolLen, (u_char *) buf, strlen(buf), userKey, &userKeyLen); if (ret2 != SNMPERR_SUCCESS) { *errorMsg = "could not generate the authentication key from the supplied pass phrase."; goto fail; } /* save master key */ if (newuser->flags & USMUSER_FLAG_KEEP_MASTER_KEY) { newuser->authKeyKu = netsnmp_memdup(userKey, userKeyLen); newuser->authKeyKuLen = userKeyLen; } } /* * And turn it into a localized key */ properLen = sc_get_proper_auth_length_bytype( sc_get_authtype(newuser->authProtocol, newuser->authProtocolLen)); if (properLen <= 0) { *errorMsg = "Could not get proper authentication protocol key length"; goto fail; } newuser->authKey = (u_char *) malloc(properLen); newuser->authKeyLen = properLen; if (strcmp(buf,"-l") == 0) { /* a local key is directly specified */ cp = copy_nword(cp, buf, sizeof(buf)); ret = newuser->authKeyLen; newuser->authKeyLen = 0; if (!snmp_hex_to_binary(&newuser->authKey, &ret, &newuser->authKeyLen, 0, buf)) { *errorMsg = "invalid key value argument to -l"; goto fail; } if (properLen != newuser->authKeyLen) { *errorMsg = "improper key length to -l"; goto fail; } } else { ret2 = generate_kul(newuser->authProtocol, newuser->authProtocolLen, newuser->engineID, newuser->engineIDLen, userKey, userKeyLen, newuser->authKey, &newuser->authKeyLen); if (ret2 != SNMPERR_SUCCESS) { *errorMsg = "could not generate localized authentication key (Kul) from the master key (Ku)."; goto fail; } } if (!cp) { #ifndef NETSNMP_FORCE_SYSTEM_V3_AUTHPRIV goto add; /* no privacy type (which is legal) */ #else if (snmp_oid_compare(usmNoPrivProtocol, OID_LENGTH(usmNoPrivProtocol), def_priv_prot, def_priv_prot_len) == 0) goto add; else { *errorMsg = "priv protocol does not match system policy"; goto fail; } #endif /* NETSNMP_FORCE_SYSTEM_V3_AUTHPRIV */ } /* * READ: Privacy Type */ newuser->privProtocol[0] = 0; cp = copy_nword(cp, buf, sizeof(buf)); if ((strncmp(buf, "default", 7) == 0) && (NULL != def_priv_prot)) { SNMP_FREE(newuser->privProtocol); newuser->privProtocol = snmp_duplicate_objid(def_priv_prot, def_priv_prot_len); if (newuser->privProtocol == NULL) { *errorMsg = "malloc failed"; goto fail; } newuser->privProtocolLen = def_priv_prot_len; pai = sc_get_priv_alg_byoid(newuser->privProtocol, newuser->privProtocolLen); } else { int priv_type = usm_lookup_priv_type(buf); if (priv_type < 0) { *errorMsg = "unknown privProtocol"; DEBUGMSGTL(("usmUser", "%s %s\n", *errorMsg, buf)); goto fail; } DEBUGMSGTL(("9:usmUser", "privProtocol %s\n", buf)); pai = sc_get_priv_alg_bytype(priv_type); if (pai) { SNMP_FREE(newuser->privProtocol); newuser->privProtocolLen = pai->oid_len; newuser->privProtocol = snmp_duplicate_objid(pai->alg_oid, newuser->privProtocolLen); DEBUGMSGTL(("9:usmUser", "pai %s\n", pai->name)); if (newuser->privProtocol == NULL) { *errorMsg = "malloc failed"; goto fail; } } } if (NULL == pai) { *errorMsg = "priv protocol lookup failed"; goto fail; } if (0 == newuser->privProtocol[0] && NULL == *errorMsg) *errorMsg = "Unknown privacy protocol"; if (NULL != *errorMsg) goto fail; #ifdef NETSNMP_FORCE_SYSTEM_V3_AUTHPRIV if (snmp_oid_compare(newuser->privProtocol, newuser->privProtocolLen, def_priv_prot, def_priv_prot_len) != 0) { *errorMsg = "priv protocol does not match system policy"; goto fail; } #endif /* NETSNMP_FORCE_SYSTEM_V3_AUTHPRIV */ properPrivKeyLen = pai->proper_length; if (USM_CREATE_USER_PRIV_DES == pai->type) properPrivKeyLen *= 2; /* ?? we store salt with key */ /* * READ: Encryption Pass Phrase or key */ if (!cp) { /* * assume the same as the authentication key */ newuser->privKey = netsnmp_memdup(newuser->authKey, newuser->authKeyLen); privKeySize = newuser->privKeyLen = newuser->authKeyLen; if (newuser->flags & USMUSER_FLAG_KEEP_MASTER_KEY) { newuser->privKeyKu = netsnmp_memdup(newuser->authKeyKu, newuser->authKeyKuLen); newuser->privKeyKuLen = newuser->authKeyKuLen; } } else { cp = copy_nword(cp, buf, sizeof(buf)); if (strcmp(buf,"-m") == 0) { /* a master key is specified */ cp = copy_nword(cp, buf, sizeof(buf)); ret = sizeof(userKey); tmpp = userKey; userKeyLen = 0; if (!snmp_hex_to_binary(&tmpp, &ret, &userKeyLen, 0, buf)) { *errorMsg = "invalid key value argument to -m"; goto fail; } /* save master key */ if (newuser->flags & USMUSER_FLAG_KEEP_MASTER_KEY) { newuser->privKeyKu = netsnmp_memdup(userKey, userKeyLen); newuser->privKeyKuLen = userKeyLen; } } else if (strcmp(buf,"-l") != 0) { /* a password is specified */ userKeyLen = sizeof(userKey); ret2 = generate_Ku(newuser->authProtocol, newuser->authProtocolLen, (u_char*)buf, strlen(buf), userKey, &userKeyLen); if (ret2 != SNMPERR_SUCCESS) { *errorMsg = "could not generate the privacy key from the supplied pass phrase."; goto fail; } /* save master key */ if (newuser->flags & USMUSER_FLAG_KEEP_MASTER_KEY) { newuser->privKeyKu = netsnmp_memdup(userKey, userKeyLen); newuser->privKeyKuLen = userKeyLen; } } /* * And turn it into a localized key * Allocate enough space for greater of auth mac and privKey len. */ privKeySize = SNMP_MAX(properPrivKeyLen, properLen); newuser->privKey = (u_char *) malloc(privKeySize); newuser->privKeyLen = privKeySize; if (strcmp(buf,"-l") == 0) { /* a local key is directly specified */ cp = copy_nword(cp, buf, sizeof(buf)); ret = newuser->privKeyLen; newuser->privKeyLen = 0; if (!snmp_hex_to_binary(&newuser->privKey, &ret, &newuser->privKeyLen, 0, buf)) { *errorMsg = "invalid key value argument to -l"; goto fail; } } else { ret2 = generate_kul(newuser->authProtocol, newuser->authProtocolLen, newuser->engineID, newuser->engineIDLen, userKey, userKeyLen, newuser->privKey, &newuser->privKeyLen); if (ret2 != SNMPERR_SUCCESS) { *errorMsg = "could not generate localized privacy key (Kul) from the master key (Ku)."; goto fail; } } if (newuser->privKeyLen < properPrivKeyLen) { ret = usm_extend_user_kul(newuser, properPrivKeyLen); if (ret != SNMPERR_SUCCESS) { *errorMsg = "could not extend localized privacy key to required length."; goto fail; } } } if ((newuser->privKeyLen >= properPrivKeyLen) || (properPrivKeyLen == 0)){ DEBUGMSGTL(("9:usmUser", "truncating privKeyLen from %" NETSNMP_PRIz "d to %d\n", newuser->privKeyLen, properPrivKeyLen)); newuser->privKeyLen = properPrivKeyLen; } else { DEBUGMSGTL(("usmUser", "privKey length %" NETSNMP_PRIz "d < %d required by privProtocol\n", newuser->privKeyLen, properPrivKeyLen)); *errorMsg = "privKey length is less than required by privProtocol"; goto fail; } add: usm_add_user(newuser); DEBUGMSGTL(("usmUser", "created a new user %s at ", newuser->secName)); DEBUGMSGHEX(("usmUser", newuser->engineID, newuser->engineIDLen)); DEBUGMSG(("usmUser", "\n")); return newuser; fail: usm_free_user(newuser); return NULL; } void usm_parse_create_usmUser(const char *token, char *line) { const char *error = NULL; usm_create_usmUser_from_string(line, &error); if (error) config_perror(error); } struct usmUser * usm_create_usmUser(const char *userName, const char *engineID, u_int flags, int authType, const char *authPass, int privType, const char *privPass, const char **errorMsg) { const char *errorMsgLoc, *str; char line[SPRINT_MAX_LEN]; int len; if (NULL == errorMsg) errorMsg = &errorMsgLoc; *errorMsg = NULL; /** [-M] [-e ENGINEID] username * (MD5|SHA|SHA512|SHA384|SHA256|SHA224|default) authpassphrase * [DES|AES|default] [privpassphrase] */ line[0] = 0; if (flags & USMUSER_FLAG_KEEP_MASTER_KEY) strlcat(line, "-M ", sizeof(line)); if (engineID) { strlcat(line, "-e ", sizeof(line)); strlcat(line, engineID, sizeof(line)); strlcat(line, " ", sizeof(line)); } len = strlcat(line, userName, sizeof(line)); if (0 == authType) goto create; str = usm_lookup_auth_str(authType); if (NULL == str) { *errorMsg = "unknown authType"; return NULL; } if (NULL == authPass) { *errorMsg = "missing authpassphrase"; return NULL; } strlcat(line, " ", sizeof(line)); strlcat(line, str, sizeof(line)); strlcat(line, " ", sizeof(line)); len = strlcat(line, authPass, sizeof(line)); if (0 == privType) goto create; str = usm_lookup_priv_str(privType); if (NULL == str) { *errorMsg = "Unknown privacy protocol"; return NULL; } if (NULL == privPass) { *errorMsg = "missing privpassphrase"; return NULL; } strlcat(line, " ", sizeof(line)); strlcat(line, str, sizeof(line)); strlcat(line, " ", sizeof(line)); len = strlcat(line, privPass, sizeof(line)); create: if (len >= sizeof(line)) { *errorMsg = "line exceeded buffer space"; return NULL; }; return usm_create_usmUser_from_string(line, errorMsg); } void snmpv3_authtype_conf(const char *word, char *cptr) { int auth_type = usm_lookup_auth_type(cptr); if (auth_type < 0) config_perror("Unknown authentication type"); defaultAuthType = sc_get_auth_oid(auth_type, &defaultAuthTypeLen); DEBUGMSGTL(("snmpv3", "set default authentication type: %s\n", cptr)); } const oid * get_default_authtype(size_t * len) { if (defaultAuthType == NULL) { defaultAuthType = SNMP_DEFAULT_AUTH_PROTO; defaultAuthTypeLen = SNMP_DEFAULT_AUTH_PROTOLEN; } if (len) *len = defaultAuthTypeLen; return defaultAuthType; } void snmpv3_privtype_conf(const char *word, char *cptr) { int priv_type = usm_lookup_priv_type(cptr); if (priv_type < 0) config_perror("Unknown privacy type"); defaultPrivType = sc_get_priv_oid(priv_type, &defaultPrivTypeLen); DEBUGMSGTL(("snmpv3", "set default privacy type: %s\n", cptr)); } const oid * get_default_privtype(size_t * len) { if (defaultPrivType == NULL) { defaultPrivType = SNMP_DEFAULT_PRIV_PROTO; defaultPrivTypeLen = SNMP_DEFAULT_PRIV_PROTOLEN; } if (len) *len = defaultPrivTypeLen; return defaultPrivType; }