Пример #1
0
static err_t
snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
{
    err_t derr;
    u16_t len, vb_len;
    u8_t  len_octets;
    u8_t type;

    /* variable binding list */
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
    if ((derr != ERR_OK) ||
            (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) {
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    ofs += (1 + len_octets);

    /* start with empty list */
    m_stat->invb.count = 0;
    m_stat->invb.head = NULL;
    m_stat->invb.tail = NULL;

    while (vb_len > 0) {
        struct snmp_obj_id oid, oid_value;
        struct snmp_varbind *vb;

        snmp_asn1_dec_type(p, ofs, &type);
        derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
        if ((derr != ERR_OK) ||
                (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
                (len == 0) || (len > vb_len)) {
            snmp_inc_snmpinasnparseerrs();
            /* free varbinds (if available) */
            snmp_varbind_list_free(&m_stat->invb);
            return ERR_ARG;
        }
        ofs += (1 + len_octets);
        vb_len -= (1 + len_octets);

        snmp_asn1_dec_type(p, ofs, &type);
        derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
        if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID))) {
            /* can't decode object name length */
            snmp_inc_snmpinasnparseerrs();
            /* free varbinds (if available) */
            snmp_varbind_list_free(&m_stat->invb);
            return ERR_ARG;
        }
        derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
        if (derr != ERR_OK) {
            /* can't decode object name */
            snmp_inc_snmpinasnparseerrs();
            /* free varbinds (if available) */
            snmp_varbind_list_free(&m_stat->invb);
            return ERR_ARG;
        }
        ofs += (1 + len_octets + len);
        vb_len -= (1 + len_octets + len);

        snmp_asn1_dec_type(p, ofs, &type);
        derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
        if (derr != ERR_OK) {
            /* can't decode object value length */
            snmp_inc_snmpinasnparseerrs();
            /* free varbinds (if available) */
            snmp_varbind_list_free(&m_stat->invb);
            return ERR_ARG;
        }

        switch (type) {
            case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
                vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
                if (vb != NULL) {
                    s32_t *vptr = (s32_t*)vb->value;

                    derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
                    snmp_varbind_tail_add(&m_stat->invb, vb);
                } else {
                    derr = ERR_ARG;
                }
                break;
            case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
            case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
            case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
                vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
                if (vb != NULL) {
                    u32_t *vptr = (u32_t*)vb->value;

                    derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
                    snmp_varbind_tail_add(&m_stat->invb, vb);
                } else {
                    derr = ERR_ARG;
                }
                break;
            case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
            case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
                LWIP_ASSERT("invalid length", len <= 0xff);
                vb = snmp_varbind_alloc(&oid, type, (u8_t)len);
                if (vb != NULL) {
                    derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
                    snmp_varbind_tail_add(&m_stat->invb, vb);
                } else {
                    derr = ERR_ARG;
                }
                break;
            case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
                vb = snmp_varbind_alloc(&oid, type, 0);
                if (vb != NULL) {
                    snmp_varbind_tail_add(&m_stat->invb, vb);
                    derr = ERR_OK;
                } else {
                    derr = ERR_ARG;
                }
                break;
            case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
                derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
                if (derr == ERR_OK) {
                    vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
                    if (vb != NULL) {
                        u8_t i = oid_value.len;
                        s32_t *vptr = (s32_t*)vb->value;

                        while(i > 0) {
                            i--;
                            vptr[i] = oid_value.id[i];
                        }
                        snmp_varbind_tail_add(&m_stat->invb, vb);
                        derr = ERR_OK;
                    } else {
                        derr = ERR_ARG;
                    }
                }
                break;
            case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
                if (len == 4) {
                    /* must be exactly 4 octets! */
                    vb = snmp_varbind_alloc(&oid, type, 4);
                    if (vb != NULL) {
                        derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
                        snmp_varbind_tail_add(&m_stat->invb, vb);
                    } else {
                        derr = ERR_ARG;
                    }
                } else {
                    derr = ERR_ARG;
                }
                break;
            default:
                derr = ERR_ARG;
                break;
        }
        if (derr != ERR_OK) {
            snmp_inc_snmpinasnparseerrs();
            /* free varbinds (if available) */
            snmp_varbind_list_free(&m_stat->invb);
            return ERR_ARG;
        }
        ofs += (1 + len_octets + len);
        vb_len -= (1 + len_octets + len);
    }

    if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ) {
        snmp_add_snmpintotalsetvars(m_stat->invb.count);
    } else {
        snmp_add_snmpintotalreqvars(m_stat->invb.count);
    }

    *ofs_ret = ofs;
    return ERR_OK;
}
Пример #2
0
/**
 * Checks and decodes incoming SNMP message header, logs header errors.
 *
 * @param p points to pbuf chain of SNMP message (UDP payload)
 * @param ofs points to first octet of SNMP message
 * @param pdu_len the length of the UDP payload
 * @param ofs_ret returns the ofset of the variable bindings
 * @param m_stat points to the current message request state return
 * @return
 * - ERR_OK SNMP header is sane and accepted
 * - ERR_ARG SNMP header is either malformed or rejected
 */
static err_t
snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
{
    err_t derr;
    u16_t len, ofs_base;
    u8_t  len_octets;
    u8_t  type;
    s32_t version;

    ofs_base = ofs;
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) ||
            (pdu_len != (1 + len_octets + len)) ||
            (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) {
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    ofs += (1 + len_octets);
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) {
        /* can't decode or no integer (version) */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
    if (derr != ERR_OK) {
        /* can't decode */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    if (version != 0) {
        /* not version 1 */
        snmp_inc_snmpinbadversions();
        return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR))) {
        /* can't decode or no octet string (community) */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
    if (derr != ERR_OK) {
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    /* add zero terminator */
    len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
    m_stat->community[len] = 0;
    m_stat->com_strlen = (u8_t)len;
    if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0) {
        /** @todo: move this if we need to check more names */
        snmp_inc_snmpinbadcommunitynames();
        snmp_authfail_trap();
        return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if (derr != ERR_OK) {
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    switch(type) {
        case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
            /* GetRequest PDU */
            snmp_inc_snmpingetrequests();
            derr = ERR_OK;
            break;
        case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
            /* GetNextRequest PDU */
            snmp_inc_snmpingetnexts();
            derr = ERR_OK;
            break;
        case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
            /* GetResponse PDU */
            snmp_inc_snmpingetresponses();
            derr = ERR_ARG;
            break;
        case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
            /* SetRequest PDU */
            snmp_inc_snmpinsetrequests();
            derr = ERR_OK;
            break;
        case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
            /* Trap PDU */
            snmp_inc_snmpintraps();
            derr = ERR_ARG;
            break;
        default:
            snmp_inc_snmpinasnparseerrs();
            derr = ERR_ARG;
            break;
    }
    if (derr != ERR_OK) {
        /* unsupported input PDU for this agent (no parse error) */
        return ERR_ARG;
    }
    m_stat->rt = type & 0x1F;
    ofs += (1 + len_octets);
    if (len != (pdu_len - (ofs - ofs_base))) {
        /* decoded PDU length does not equal actual payload length */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) {
        /* can't decode or no integer (request ID) */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
    if (derr != ERR_OK) {
        /* can't decode */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) {
        /* can't decode or no integer (error-status) */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    /* must be noError (0) for incoming requests.
       log errors for mib-2 completeness and for debug purposes */
    derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
    if (derr != ERR_OK) {
        /* can't decode */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    switch (m_stat->error_status) {
        case SNMP_ES_TOOBIG:
            snmp_inc_snmpintoobigs();
            break;
        case SNMP_ES_NOSUCHNAME:
            snmp_inc_snmpinnosuchnames();
            break;
        case SNMP_ES_BADVALUE:
            snmp_inc_snmpinbadvalues();
            break;
        case SNMP_ES_READONLY:
            snmp_inc_snmpinreadonlys();
            break;
        case SNMP_ES_GENERROR:
            snmp_inc_snmpingenerrs();
            break;
    }
    ofs += (1 + len_octets + len);
    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) {
        /* can't decode or no integer (error-index) */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    /* must be 0 for incoming requests.
       decode anyway to catch bad integers (and dirty tricks) */
    derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
    if (derr != ERR_OK) {
        /* can't decode */
        snmp_inc_snmpinasnparseerrs();
        return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    *ofs_ret = ofs;
    return ERR_OK;
}
Пример #3
0
snmp_vb_enumerator_err_t
snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind)
{
  struct snmp_asn1_tlv tlv;
  u16_t  varbind_len;
  err_t  err;
  
  if (enumerator->pbuf_stream.length == 0)
  {
    return SNMP_VB_ENUMERATOR_ERR_EOVB;
  }
  enumerator->varbind_count++;

  /* decode varbind itself (parent container of a varbind) */
  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
  VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= enumerator->pbuf_stream.length));
  varbind_len = tlv.value_len;

  /* decode varbind name (object id) */
  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
  VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_OBJECT_ID) && (SNMP_ASN1_TLV_LENGTH(tlv) < varbind_len) && (tlv.value_len < enumerator->pbuf_stream.length));
   
  VB_PARSE_EXEC(snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, varbind->oid.id, &(varbind->oid.len), SNMP_MAX_OBJ_ID_LEN));
  varbind_len -= SNMP_ASN1_TLV_LENGTH(tlv);

  /* decode varbind value (object id) */
  VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
  VB_PARSE_ASSERT((SNMP_ASN1_TLV_LENGTH(tlv) == varbind_len) && (tlv.value_len <= enumerator->pbuf_stream.length));
  varbind->type = tlv.type;

  /* shall the value be decoded ? */
  if (varbind->value != NULL) {
    switch (varbind->type) {
      case SNMP_ASN1_TYPE_INTEGER:
        VB_PARSE_EXEC(snmp_asn1_dec_s32t(&(enumerator->pbuf_stream), tlv.value_len, (s32_t*)varbind->value));
        varbind->value_len = sizeof(s32_t*);
        break;
      case SNMP_ASN1_TYPE_COUNTER:
      case SNMP_ASN1_TYPE_GAUGE:
      case SNMP_ASN1_TYPE_TIMETICKS:
        VB_PARSE_EXEC(snmp_asn1_dec_u32t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value));
        varbind->value_len = sizeof(u32_t*);
        break;
      case SNMP_ASN1_TYPE_OCTET_STRING:
      case SNMP_ASN1_TYPE_OPAQUE:
        err = snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t*)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE);
        if (err == ERR_MEM) {
          return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
        }
        VB_PARSE_ASSERT(err == ERR_OK);
        break;
      case SNMP_ASN1_TYPE_NULL:
        varbind->value_len = 0;
        break;
      case SNMP_ASN1_TYPE_OBJECT_ID:
        /* misuse tlv.length_len as OID_length transporter */
        err = snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value, &tlv.length_len, SNMP_MAX_OBJ_ID_LEN);
        if (err == ERR_MEM) {
          return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
        }
        VB_PARSE_ASSERT(err == ERR_OK);
        varbind->value_len = tlv.length_len * sizeof(u32_t);
        break;
      case SNMP_ASN1_TYPE_IPADDR:
        if (tlv.value_len == 4) {
          /* must be exactly 4 octets! */
          VB_PARSE_EXEC(snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t*)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE));
        } else {
          VB_PARSE_ASSERT(0);
        }
        break;
      case SNMP_ASN1_TYPE_COUNTER64:
        VB_PARSE_EXEC(snmp_asn1_dec_u64t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value));
        varbind->value_len = 2 * sizeof(u32_t*);
        break;
      default:
        VB_PARSE_ASSERT(0);
        break;
    }
  } else {
    snmp_pbuf_stream_seek(&(enumerator->pbuf_stream), tlv.value_len);
    varbind->value_len = tlv.value_len;
  }

  return SNMP_VB_ENUMERATOR_ERR_OK;
}
Пример #4
0
/**
 * Checks and decodes incoming SNMP message header, logs header errors.
 *
 * @param request points to the current message request state return
 * @return
 * - ERR_OK SNMP header is sane and accepted
 * - ERR_VAL SNMP header is either malformed or rejected
 */
static err_t
snmp_parse_inbound_frame(struct snmp_request *request)
{
  struct snmp_pbuf_stream pbuf_stream;
  struct snmp_asn1_tlv tlv;
  s32_t parent_tlv_value_len;
  s32_t s32_value;
  err_t err;

  IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
  
  /* decode main container consisting of version, community and PDU */
  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
  IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len == pbuf_stream.length));
  parent_tlv_value_len = tlv.value_len;

  /* decode version */
  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
  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, &s32_value));
  if ((s32_value != SNMP_VERSION_1) && (s32_value != SNMP_VERSION_2c)) {
    /* unsupported SNMP version */
    snmp_stats.inbadversions++;
    return ERR_ABRT;
  }
  request->version = (u8_t)s32_value;

  /* 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;

  /* decode PDU type (next container level) */
  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
  IF_PARSE_ASSERT(tlv.value_len == pbuf_stream.length);
  parent_tlv_value_len = tlv.value_len;

  /* validate PDU type */
  switch(tlv.type) {
    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_REQ):
      /* GetRequest PDU */
      snmp_stats.ingetrequests++;
      break;
    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ):
      /* GetNextRequest PDU */
      snmp_stats.ingetnexts++;
      break;
    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ):
      /* GetBulkRequest PDU */
      if (request->version < SNMP_VERSION_2c) {
        /* RFC2089: invalid, drop packet */
        return ERR_ABRT;
      }
      break;
    case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_SET_REQ):
      /* SetRequest PDU */
      snmp_stats.insetrequests++;
      break;
    default:
      /* unsupported input PDU for this agent (no parse error) */
      LWIP_DEBUGF(SNMP_DEBUG, ("Unknown/Invalid SNMP PDU type received: %d", tlv.type)); \
      return ERR_ABRT;
      break;
  }
  request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK;

  /* 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) {
    /* community string was too long or really empty*/
    snmp_stats.inbadcommunitynames++;
    snmp_authfail_trap();
    return ERR_ABRT;
  } else if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
    if (strnlen(snmp_community_write, SNMP_MAX_COMMUNITY_STR_LEN) == 0) {
      /* our write community is empty, that means all our objects are readonly */
      request->error_status = SNMP_ERR_NOTWRITABLE;
      request->error_index  = 1;
    } else if (strncmp(snmp_community_write, (const char*)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) {
      /* community name does not match */
      snmp_stats.inbadcommunitynames++;
      snmp_authfail_trap();
      return ERR_ABRT;
    }
  } else { 
    if (strncmp(snmp_community, (const char*)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) {
      /* community name does not match */
      snmp_stats.inbadcommunitynames++;
      snmp_authfail_trap();
      return ERR_ABRT;
    }
  }
  
  /* decode request ID */
  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
  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->request_id));

  /* decode error status / non-repeaters */
  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
  IF_PARSE_ASSERT(parent_tlv_value_len > 0);

  if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->non_repeaters));
    if (request->non_repeaters < 0) {
      /* RFC 1905, 4.2.3 */
      request->non_repeaters = 0;
    }
  } else {
    /* only check valid value, don't touch 'request->error_status', maybe a response error status was already set to above; */
    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
    IF_PARSE_ASSERT(s32_value == SNMP_ERR_NOERROR);
  }

  /* decode error index / max-repetitions */
  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
  IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
  parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
  IF_PARSE_ASSERT(parent_tlv_value_len > 0);

  if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->max_repetitions));
    if (request->max_repetitions < 0) {
      /* RFC 1905, 4.2.3 */
      request->max_repetitions = 0;
    }
  } else {
    IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->error_index));
    IF_PARSE_ASSERT(s32_value == 0);
  }

  /* decode varbind-list type (next container level) */
  IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
  IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len == pbuf_stream.length));
  
  request->inbound_varbind_offset = pbuf_stream.offset;
  request->inbound_varbind_len    = pbuf_stream.length;
  snmp_vb_enumerator_init(&(request->inbound_varbind_enumerator), request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);

  return ERR_OK;
}
Пример #5
0
/**
 * Checks and decodes incoming SNMP message header, logs header errors.
 *
 * @param p points to pbuf chain of SNMP message (UDP payload)
 * @param ofs points to first octet of SNMP message
 * @param pdu_len the length of the UDP payload
 * @param ofs_ret returns the ofset of the variable bindings
 * @param m_stat points to the current message request state return
 * @return
 * - ERR_OK SNMP header is sane and accepted
 * - ERR_ARG SNMP header is either malformed or rejected
 */
static err_t
snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
{
  err_t derr;
  u16_t len, ofs_base;
  u8_t  len_octets;
  u8_t  type;
  s32_t version;

  ofs_base = ofs;
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) ||
      (pdu_len != (1 + len_octets + len)) ||
      (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
  {
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  ofs += (1 + len_octets);
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
  {
    /* can't decode or no integer (version) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
  if (derr != ERR_OK)
  {
    /* can't decode */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  if (version != 0)
  {
    /* not version 1 */
    snmp_inc_snmpinbadversions();
    return ERR_ARG;
  }
  ofs += (1 + len_octets + len);
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)))
  {
    /* can't decode or no octet string (community) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
  if (derr != ERR_OK)
  {
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  /* add zero terminator */
  len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
  m_stat->community[len] = 0;
  m_stat->com_strlen = (u8_t)len;
  ofs += (1 + len_octets + len);
  snmp_asn1_dec_type(p, ofs, &type);
#if SNMP_COMMUNITY_EXT
  if (strncmp(snmp_community_write, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
  {
    /* community does not match the write-access community, check if this is a SetRequest */
    if (type == (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ))
    {
      /* wrong community for SetRequest PDU */
      snmp_inc_snmpinbadcommunitynames();
      snmp_authfail_trap();
      return ERR_ARG;
    }
#else /* SNMP_COMMUNITY_EXT */
  {
#endif /* SNMP_COMMUNITY_EXT */
    if (strncmp(snmp_community, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
    {
      snmp_inc_snmpinbadcommunitynames();
      snmp_authfail_trap();
      return ERR_ARG;
    }
  }
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if (derr != ERR_OK)
  {
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  switch(type)
  {
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
      /* GetRequest PDU */
      snmp_inc_snmpingetrequests();
      derr = ERR_OK;
      break;
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
      /* GetNextRequest PDU */
      snmp_inc_snmpingetnexts();
      derr = ERR_OK;
      break;
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
      /* GetResponse PDU */
      snmp_inc_snmpingetresponses();
      derr = ERR_ARG;
      break;
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
      /* SetRequest PDU */
      snmp_inc_snmpinsetrequests();
      derr = ERR_OK;
      break;
    case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
      /* Trap PDU */
      snmp_inc_snmpintraps();
      derr = ERR_ARG;
      break;
    default:
      snmp_inc_snmpinasnparseerrs();
      derr = ERR_ARG;
      break;
  }
  if (derr != ERR_OK)
  {
    /* unsupported input PDU for this agent (no parse error) */
    return ERR_ARG;
  }
  m_stat->rt = type & 0x1F;
  ofs += (1 + len_octets);
  if (len != (pdu_len - (ofs - ofs_base)))
  {
    /* decoded PDU length does not equal actual payload length */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
  {
    /* can't decode or no integer (request ID) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
  if (derr != ERR_OK)
  {
    /* can't decode */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  ofs += (1 + len_octets + len);
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
  {
    /* can't decode or no integer (error-status) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  /* must be noError (0) for incoming requests.
     log errors for mib-2 completeness and for debug purposes */
  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
  if (derr != ERR_OK)
  {
    /* can't decode */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  switch (m_stat->error_status)
  {
    case SNMP_ES_NOERROR:
      /* nothing to do */
      break;
    case SNMP_ES_TOOBIG:
      snmp_inc_snmpintoobigs();
      break;
    case SNMP_ES_NOSUCHNAME:
      snmp_inc_snmpinnosuchnames();
      break;
    case SNMP_ES_BADVALUE:
      snmp_inc_snmpinbadvalues();
      break;
    case SNMP_ES_READONLY:
      snmp_inc_snmpinreadonlys();
      break;
    case SNMP_ES_GENERROR:
      snmp_inc_snmpingenerrs();
      break;
    default:
      LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check(): unknown error_status: %d\n", (int)m_stat->error_status));
      break;
  }
  ofs += (1 + len_octets + len);
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
  if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
  {
    /* can't decode or no integer (error-index) */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  /* must be 0 for incoming requests.
     decode anyway to catch bad integers (and dirty tricks) */
  derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
  if (derr != ERR_OK)
  {
    /* can't decode */
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  ofs += (1 + len_octets + len);
  *ofs_ret = ofs;
  return ERR_OK;
}

static err_t
snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
{
  err_t derr;
  u16_t len, vb_len;
  u8_t  len_octets;
  u8_t type;

  /* variable binding list */
  snmp_asn1_dec_type(p, ofs, &type);
  derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
  if ((derr != ERR_OK) ||
      (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
  {
    snmp_inc_snmpinasnparseerrs();
    return ERR_ARG;
  }
  ofs += (1 + len_octets);

  /* start with empty list */
  m_stat->invb.count = 0;
  m_stat->invb.head = NULL;
  m_stat->invb.tail = NULL;

  while (vb_len > 0)
  {
    struct snmp_obj_id oid, oid_value;
    struct snmp_varbind *vb;

    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) ||
        (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
        (len == 0) || (len > vb_len))
    {
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }
    ofs += (1 + len_octets);
    vb_len -= (1 + len_octets);

    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)))
    {
      /* can't decode object name length */
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }
    derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
    if (derr != ERR_OK)
    {
      /* can't decode object name */
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    vb_len -= (1 + len_octets + len);

    snmp_asn1_dec_type(p, ofs, &type);
    derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    if (derr != ERR_OK)
    {
      /* can't decode object value length */
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }

    switch (type)
    {
      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
        vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
        if (vb != NULL)
        {
          s32_t *vptr = (s32_t*)vb->value;

          derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
          snmp_varbind_tail_add(&m_stat->invb, vb);
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
        vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
        if (vb != NULL)
        {
          u32_t *vptr = (u32_t*)vb->value;

          derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
          snmp_varbind_tail_add(&m_stat->invb, vb);
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
        LWIP_ASSERT("invalid length", len <= 0xff);
        vb = snmp_varbind_alloc(&oid, type, (u8_t)len);
        if (vb != NULL)
        {
          derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
          snmp_varbind_tail_add(&m_stat->invb, vb);
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
        vb = snmp_varbind_alloc(&oid, type, 0);
        if (vb != NULL)
        {
          snmp_varbind_tail_add(&m_stat->invb, vb);
          derr = ERR_OK;
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
        derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
        if (derr == ERR_OK)
        {
          vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
          if (vb != NULL)
          {
            u8_t i = oid_value.len;
            s32_t *vptr = (s32_t*)vb->value;

            while(i > 0)
            {
              i--;
              vptr[i] = oid_value.id[i];
            }
            snmp_varbind_tail_add(&m_stat->invb, vb);
            derr = ERR_OK;
          }
          else
          {
            derr = ERR_ARG;
          }
        }
        break;
      case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
        if (len == 4)
        {
          /* must be exactly 4 octets! */
          vb = snmp_varbind_alloc(&oid, type, 4);
          if (vb != NULL)
          {
            derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
            snmp_varbind_tail_add(&m_stat->invb, vb);
          }
          else
          {
            derr = ERR_ARG;
          }
        }
        else
        {
          derr = ERR_ARG;
        }
        break;
      default:
        derr = ERR_ARG;
        break;
    }
    if (derr != ERR_OK)
    {
      snmp_inc_snmpinasnparseerrs();
      /* free varbinds (if available) */
      snmp_varbind_list_free(&m_stat->invb);
      return ERR_ARG;
    }
    ofs += (1 + len_octets + len);
    vb_len -= (1 + len_octets + len);
  }

  if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ)
  {
    snmp_add_snmpintotalsetvars(m_stat->invb.count);
  }
  else
  {
    snmp_add_snmpintotalreqvars(m_stat->invb.count);
  }

  *ofs_ret = ofs;
  return ERR_OK;
}