Added handling invalid packets in SNMPv3.

This commit is contained in:
Marco 2017-03-01 16:13:45 +01:00 committed by Dirk Ziegelmeier
parent 78806001e5
commit f092d09121
5 changed files with 262 additions and 54 deletions

View File

@ -55,6 +55,13 @@ extern "C" {
*/
#define SNMP_ERR_TOOBIG 1
#define SNMP_ERR_AUTHORIZATIONERROR 16
#define SNMP_ERR_UNKNOWN_ENGINEID 30
#define SNMP_ERR_UNKNOWN_SECURITYNAME 31
#define SNMP_ERR_UNSUPPORTED_SECLEVEL 32
#define SNMP_ERR_NOTINTIMEWINDOW 33
#define SNMP_ERR_DECRYIPTION_ERROR 34
#define SNMP_ERR_NOSUCHOBJECT SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT
#define SNMP_ERR_ENDOFMIBVIEW SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW

View File

@ -294,6 +294,70 @@ snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t por
err = snmp_process_set_request(&request);
}
}
else {
struct snmp_varbind vb;
vb.next = NULL;
vb.prev = NULL;
vb.type = SNMP_ASN1_TYPE_COUNTER32;
vb.value_len = sizeof(u32_t);
switch (request.error_status) {
case SNMP_ERR_AUTHORIZATIONERROR:
{
struct snmp_obj_id oid = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 5, 0 } };
vb.value = &snmp_stats.wrongdigests;
MEMCPY(&vb.oid, &oid, sizeof(struct snmp_obj_id));
}
break;
case SNMP_ERR_UNKNOWN_ENGINEID:
{
struct snmp_obj_id oid = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0 } };
vb.value = &snmp_stats.unknownengineids;
MEMCPY(&vb.oid, &oid, sizeof(struct snmp_obj_id));
}
break;
case SNMP_ERR_UNKNOWN_SECURITYNAME:
{
struct snmp_obj_id oid = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 3, 0 } };
vb.value = &snmp_stats.unknownusernames;
MEMCPY(&vb.oid, &oid, sizeof(struct snmp_obj_id));
}
break;
case SNMP_ERR_UNSUPPORTED_SECLEVEL:
{
struct snmp_obj_id oid = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 1, 0 } };
vb.value = &snmp_stats.unsupportedseclevels;
MEMCPY(&vb.oid, &oid, sizeof(struct snmp_obj_id));
}
break;
case SNMP_ERR_NOTINTIMEWINDOW:
{
struct snmp_obj_id oid = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0 } };
vb.value = &snmp_stats.notintimewindows;
MEMCPY(&vb.oid, &oid, sizeof(struct snmp_obj_id));
}
break;
case SNMP_ERR_DECRYIPTION_ERROR:
{
struct snmp_obj_id oid = { 11, { 1, 3, 6, 1, 6, 3, 15, 1, 1, 6, 0 } };
vb.value = &snmp_stats.decryptionerrors;
MEMCPY(&vb.oid, &oid, sizeof(struct snmp_obj_id));
}
break;
default:
/* Unknown or unhandled error_status */
err = ERR_ARG;
}
if (err == ERR_OK) {
snmp_append_outbound_varbind(&(request.outbound_pbuf_stream), &vb);
request.error_status = SNMP_ERR_NOERROR;
}
request.request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_REPORT);
request.request_id = request.msg_id;
}
if (err == ERR_OK) {
err = snmp_complete_outbound_frame(&request);
@ -703,6 +767,9 @@ snmp_parse_inbound_frame(struct snmp_request *request)
s32_t parent_tlv_value_len;
s32_t s32_value;
err_t err;
#if LWIP_SNMP_V3
u8_t auth, priv;
#endif
IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
@ -828,7 +895,6 @@ snmp_parse_inbound_frame(struct snmp_request *request)
parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
IF_PARSE_ASSERT(parent_tlv_value_len > 0);
IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_time));
/* @todo: Implement time window checking */
/* msgUserName */
IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
@ -839,8 +905,6 @@ snmp_parse_inbound_frame(struct snmp_request *request)
IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_user_name,
&u16_value, SNMP_V3_MAX_USER_LENGTH));
request->msg_user_name_len = (u8_t)u16_value;
/* @todo: Implement unknown user error response */
IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, NULL, NULL));
/* msgAuthenticationParameters */
memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
@ -850,40 +914,12 @@ snmp_parse_inbound_frame(struct snmp_request *request)
IF_PARSE_ASSERT(parent_tlv_value_len > 0);
/* Remember position */
inbound_msgAuthenticationParameters_offset = pbuf_stream.offset;
LWIP_UNUSED_ARG(inbound_msgAuthenticationParameters_offset);
LWIP_UNUSED_ARG(inbound_msgAuthenticationParameters_offset);
/* Read auth parameters */
IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH);
//IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH);
IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authentication_parameters,
&u16_value, tlv.value_len));
#if LWIP_SNMP_V3_CRYPTO
if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
const u8_t zero_arr[SNMP_V3_MAX_AUTH_PARAM_LENGTH] = { 0 };
u8_t key[20];
u8_t algo;
u8_t hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)];
struct snmp_pbuf_stream auth_stream;
/* Rewind stream */
IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&pbuf_stream, inbound_msgAuthenticationParameters_offset));
/* Set auth parameters to zero for verification */
IF_PARSE_EXEC(snmp_asn1_enc_raw(&pbuf_stream, zero_arr, tlv.value_len));
/* Verify authentication */
IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL));
IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, algo, hmac));
/* @todo: Implement error response */
IF_PARSE_EXEC(memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
}
#else
/* Ungraceful exit if we encounter cryptography and don't support it.
* @todo: Implement error response
*/
IF_PARSE_ASSERT(!(request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)));
#endif
request->msg_authentication_parameters_len = (u8_t)u16_value;
/* msgPrivacyParameters */
memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
@ -894,10 +930,145 @@ snmp_parse_inbound_frame(struct snmp_request *request)
IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_privacy_parameters,
&u16_value, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
request->msg_privacy_parameters_len = (u8_t)u16_value;
/* validate securityParameters here (do this after decoding because we don't want to increase other counters for wrong frames) */
// 1) securityParameters was correctly serialized if we reach here.
// 2) securityParameters are already cached.
// 3) if msgAuthoritativeEngineID is unknown, zero-length or too long:
// b) https://tools.ietf.org/html/rfc3414#section-7
{
const char *eid;
u8_t eid_len;
snmpv3_get_engine_id(&eid, &eid_len);
if ((request->msg_authoritative_engine_id_len == 0) ||
(request->msg_authoritative_engine_id_len != eid_len) ||
(memcmp(eid, request->msg_authoritative_engine_id, eid_len) != 0)) {
snmp_stats.unknownengineids++;
request->msg_flags = 0; // noauthnopriv
request->error_status = SNMP_ERR_UNKNOWN_ENGINEID;
return ERR_OK;
}
}
// 4) verify username
if(snmpv3_get_user((char*)request->msg_user_name, &auth, NULL, &priv, NULL)) {
snmp_stats.unknownusernames++;
request->msg_flags = 0; // noauthnopriv
request->error_status = SNMP_ERR_UNKNOWN_SECURITYNAME;
return ERR_OK;
}
// 5) verify security level
switch(request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)) {
case SNMP_V3_NOAUTHNOPRIV:
if ((auth != SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
/* Invalid security level for user */
snmp_stats.unsupportedseclevels++;
request->msg_flags = 0; // noauthnopriv
request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
return ERR_OK;
}
break;
#if LWIP_SNMP_V3_CRYPTO
case SNMP_V3_AUTHNOPRIV:
if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
/* Invalid security level for user */
snmp_stats.unsupportedseclevels++;
request->msg_flags = 0; // noauthnopriv
request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
return ERR_OK;
}
break;
case SNMP_V3_AUTHPRIV:
if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv == SNMP_V3_PRIV_ALGO_INVAL)) {
/* Invalid security level for user */
snmp_stats.unsupportedseclevels++;
request->msg_flags = 0; // noauthnopriv
request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
return ERR_OK;
}
break;
#endif
default:
snmp_stats.unsupportedseclevels++;
request->msg_flags = 0; // noauthnopriv
request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
return ERR_OK;
}
// 6) if securitylevel specifies authentication, authenticate message.
#if LWIP_SNMP_V3_CRYPTO
if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
const u8_t zero_arr[SNMP_V3_MAX_AUTH_PARAM_LENGTH] = { 0 };
u8_t key[20];
u8_t algo;
u8_t hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)];
struct snmp_pbuf_stream auth_stream;
if (request->msg_authentication_parameters_len > SNMP_V3_MAX_AUTH_PARAM_LENGTH) {
snmp_stats.wrongdigests++;
request->msg_flags = 0; // noauthnopriv
request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
return ERR_OK;
}
/* Rewind stream */
IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&auth_stream, inbound_msgAuthenticationParameters_offset));
/* Set auth parameters to zero for verification */
IF_PARSE_EXEC(snmp_asn1_enc_raw(&auth_stream, zero_arr, request->msg_authentication_parameters_len));
/* Verify authentication */
IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL));
IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, algo, hmac));
if(memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH)) {
snmp_stats.wrongdigests++;
request->msg_flags = 0; // noauthnopriv
request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
return ERR_OK;
}
// 7) if securitylevel specifies authentication, verify engineboots, enginetime and lastenginetime
{
u32_t boots = snmpv3_get_engine_boots_internal();
if ((request->msg_authoritative_engine_boots != boots) || (boots == 2147483647UL)) {
snmp_stats.notintimewindows++;
request->msg_flags = SNMP_V3_AUTHNOPRIV; // authnopriv
request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
return ERR_OK;
}
}
{
u32_t time = snmpv3_get_engine_time_internal();
if (request->msg_authoritative_engine_time > time) {
snmp_stats.notintimewindows++;
request->msg_flags = SNMP_V3_AUTHNOPRIV; // authnopriv
request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
return ERR_OK;
}
else if (time > 150) {
if (request->msg_authoritative_engine_time < time - 150) {
snmp_stats.notintimewindows++;
request->msg_flags = SNMP_V3_AUTHNOPRIV; // authnopriv
request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
return ERR_OK;
}
}
}
}
#endif
// 8) if securitylevel specifies privacy, decrypt message.
#if LWIP_SNMP_V3_CRYPTO
/* Decrypt message */
if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
/* Decrypt message */
u8_t key[20];
u8_t algo;
@ -907,11 +1078,20 @@ snmp_parse_inbound_frame(struct snmp_request *request)
IF_PARSE_ASSERT(parent_tlv_value_len > 0);
IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, &algo, key));
IF_PARSE_EXEC(snmpv3_crypt(&pbuf_stream, tlv.value_len, key,
if(snmpv3_crypt(&pbuf_stream, tlv.value_len, key,
request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_DECRYPT));
request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_DECRYPT) != ERR_OK) {
snmp_stats.decryptionerrors++;
request->msg_flags = SNMP_V3_AUTHNOPRIV; // authnopriv
request->error_status = SNMP_ERR_DECRYIPTION_ERROR;
return ERR_OK;
}
}
#endif
// 9) calculate max size of scoped pdu?
// 10) securityname for user is retrieved from usertable?
// 11) security data is cached?
// 12)
/* Scoped PDU
* Encryption context
@ -930,6 +1110,7 @@ snmp_parse_inbound_frame(struct snmp_request *request)
IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_engine_id,
&u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
request->context_engine_id_len = (u8_t)u16_value;
// TODO: do we need to verify this contextengineid too?
/* contextName */
IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
@ -940,25 +1121,26 @@ snmp_parse_inbound_frame(struct snmp_request *request)
IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_name,
&u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
request->context_name_len = (u8_t)u16_value;
// TODO: do we need to verify this contextname too?
} else
#endif
{
/* decode community */
IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
IF_PARSE_ASSERT(parent_tlv_value_len > 0);
/* decode community */
IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
IF_PARSE_ASSERT(parent_tlv_value_len > 0);
err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN);
if (err == ERR_MEM) {
/* community string does not fit in our buffer -> its too long -> its invalid */
request->community_strlen = 0;
snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len);
} else {
IF_PARSE_ASSERT(err == ERR_OK);
}
/* add zero terminator */
request->community[request->community_strlen] = 0;
err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN);
if (err == ERR_MEM) {
/* community string does not fit in our buffer -> its too long -> its invalid */
request->community_strlen = 0;
snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len);
} else {
IF_PARSE_ASSERT(err == ERR_OK);
}
/* add zero terminator */
request->community[request->community_strlen] = 0;
}
/* decode PDU type (next container level) */
@ -995,6 +1177,7 @@ snmp_parse_inbound_frame(struct snmp_request *request)
break;
}
request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK;
request->request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP);
/* validate community (do this after decoding PDU type because we don't want to increase 'inbadcommunitynames' for wrong frame types */
if (request->community_strlen == 0) {
@ -1244,7 +1427,7 @@ snmp_prepare_outbound_frame(struct snmp_request *request)
/* 'PDU' sequence */
request->outbound_pdu_offset = pbuf_stream->offset;
SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3, 0);
SNMP_ASN1_SET_TLV_PARAMS(tlv, request->request_out_type, 3, 0);
OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
/* request ID */
@ -1525,7 +1708,7 @@ snmp_complete_outbound_frame(struct snmp_request *request)
#endif
/* complete missing length in 'PDU' sequence */
SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3,
SNMP_ASN1_SET_TLV_PARAMS(tlv, request->request_out_type, 3,
frame_size - request->outbound_pdu_offset - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_pdu_offset) );
OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );

View File

@ -116,6 +116,9 @@ struct snmp_request
/* max-repetitions (getBulkRequest (SNMPv2c)) */
s32_t max_repetitions;
/* Usually response-pdu (2). When snmpv3 errors are detected report-pdu(8) */
u8_t request_out_type;
#if LWIP_SNMP_V3
s32_t msg_id;
s32_t msg_max_size;
@ -128,7 +131,9 @@ struct snmp_request
u8_t msg_user_name[SNMP_V3_MAX_USER_LENGTH];
u8_t msg_user_name_len;
u8_t msg_authentication_parameters[SNMP_V3_MAX_AUTH_PARAM_LENGTH];
u8_t msg_authentication_parameters_len;
u8_t msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH];
u8_t msg_privacy_parameters_len;
u8_t context_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
u8_t context_engine_id_len;
u8_t context_name[SNMP_V3_MAX_ENGINE_ID_LENGTH];

View File

@ -51,6 +51,11 @@
#define SNMP_V3_AUTH_FLAG 0x01
#define SNMP_V3_PRIV_FLAG 0x02
/* Security levels */
#define SNMP_V3_NOAUTHNOPRIV 0x00
#define SNMP_V3_AUTHNOPRIV SNMP_V3_AUTH_FLAG
#define SNMP_V3_AUTHPRIV (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)
#define SNMP_V3_MD5_LEN 16
#define SNMP_V3_SHA_LEN 20

View File

@ -351,6 +351,14 @@ struct snmp_statistics
u32_t outsetrequests;
u32_t outgetresponses;
u32_t outtraps;
#if LWIP_SNMP_V3
u32_t unsupportedseclevels;
u32_t notintimewindows;
u32_t unknownusernames;
u32_t unknownengineids;
u32_t wrongdigests;
u32_t decryptionerrors;
#endif
};
extern struct snmp_statistics snmp_stats;