int ber_decode(const struct ber_tag *tags, unsigned int num_tags, const uint8_t *ptr, size_t len, void *priv) { const uint8_t *end = ptr + len; unsigned int i; uint32_t clen; // printf("BER DECODE:\n"); // hex_dump(ptr, len, 16, 0); for(i = 0; ptr < end; ptr += clen) { const uint8_t *idb; const struct ber_tag *tag; size_t tag_len; idb = decode_tag(&ptr, end, &tag_len); if ( ptr >= end ) return 0; clen = decode_len(&ptr, end); if ( ptr + clen > end ) return 0; tag = find_tag(tags, num_tags, idb, tag_len); if ( tag ) { if ( tag->op && !(*tag->op)(ptr, clen, priv) ) return 0; i++; }else{ size_t i; printf("unknown tag: "); for(i = 0; i < tag_len; i++) printf("%.2x ", idb[i]); printf("\n"); hex_dump(ptr, clen, 16, 0); } } return i; }
size_t ber_decode_len(const uint8_t **ptr, const uint8_t *end) { return decode_len(ptr, end); }
static int decode_snmp_request(request_t *request, client_t *client) { int type; size_t pos = 0, len = 0; const char *header_msg = "Unexpected SNMP header"; const char *error_msg = "Unexpected SNMP error"; const char *request_msg = "Unexpected SNMP request"; const char *varbind_msg = "Unexpected SNMP varbindings"; const char *commun_msg = "SNMP community"; const char *version_msg = "SNMP version"; /* The SNMP message is enclosed in a sequence */ if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) return -1; if (type != BER_TYPE_SEQUENCE || len != (client->size - pos)) { lprintf(LOG_DEBUG, "%s type %02X length %zu\n", header_msg, type, len); errno = EINVAL; return -1; } /* The first element of the sequence is the version */ if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) return -1; if (type != BER_TYPE_INTEGER || len != 1) { lprintf(LOG_DEBUG, "Unexpected %s type %02X length %zu\n", version_msg, type, len); errno = EINVAL; return -1; } if (decode_int(client->packet, client->size, &pos, len, &request->version) == -1) return -1; if (request->version != SNMP_VERSION_1 && request->version != SNMP_VERSION_2C) { lprintf(LOG_DEBUG, "Unsupported %s %d\n", version_msg, request->version); errno = EINVAL; return -1; } /* The second element of the sequence is the community string */ if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) return -1; if (type != BER_TYPE_OCTET_STRING || len >= sizeof(request->community)) { lprintf(LOG_DEBUG, "Unexpected %s type %02X length %zu\n", commun_msg, type, len); errno = EINVAL; return -1; } if (decode_str(client->packet, client->size, &pos, len, request->community, sizeof(request->community)) == -1) return -1; if (strlen(request->community) < 1) { lprintf(LOG_DEBUG, "unsupported %s '%s'\n", commun_msg, request->community); errno = EINVAL; return -1; } /* The third element of the sequence is the SNMP request */ if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) return -1; if (len != (client->size - pos)) { lprintf(LOG_DEBUG, "%s type type %02X length %zu\n", request_msg, type, len); errno = EINVAL; return -1; } request->type = type; /* The first element of the SNMP request is the request ID */ if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) return -1; if (type != BER_TYPE_INTEGER || len < 1) { lprintf(LOG_DEBUG, "%s id type %02X length %zu\n", request_msg, type, len); errno = EINVAL; return -1; } if (decode_int(client->packet, client->size, &pos, len, &request->id) == -1) return -1; /* The second element of the SNMP request is the error state / non repeaters (0..2147483647) */ if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) return -1; if (type != BER_TYPE_INTEGER || len < 1) { lprintf(LOG_DEBUG, "%s state type %02X length %zu\n", error_msg, type, len); errno = EINVAL; return -1; } if (decode_cnt(client->packet, client->size, &pos, len, &request->non_repeaters) == -1) return -1; /* The third element of the SNMP request is the error index / max repetitions (0..2147483647) */ if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) return -1; if (type != BER_TYPE_INTEGER || len < 1) { lprintf(LOG_DEBUG, "%s index type %02X length %zu\n", error_msg, type, len); errno = EINVAL; return -1; } if (decode_cnt(client->packet, client->size, &pos, len, &request->max_repetitions) == -1) return -1; /* The fourth element of the SNMP request are the variable bindings */ if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) return -1; if (type != BER_TYPE_SEQUENCE || len != (client->size - pos)) { lprintf(LOG_DEBUG, "%s type %02X length %zu\n", varbind_msg, type, len); errno = EINVAL; return -1; } /* Loop through the variable bindings */ request->oid_list_length = 0; while (pos < client->size) { /* If there is not enough room in the OID list, bail out now */ if (request->oid_list_length >= MAX_NR_OIDS) { lprintf(LOG_DEBUG, "Overflow in OID list\n"); errno = EFAULT; return -1; } /* Each variable binding is a sequence describing the variable */ if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) return -1; if (type != BER_TYPE_SEQUENCE || len < 1) { lprintf(LOG_DEBUG, "%s type %02X length %zu\n", varbind_msg, type, len); errno = EINVAL; return -1; } /* The first element of the variable binding is the OID */ if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) return -1; if (type != BER_TYPE_OID || len < 1) { lprintf(LOG_DEBUG, "%s OID type %02X length %zu\n", varbind_msg, type, len); errno = EINVAL; return -1; } if (decode_oid(client->packet, client->size, &pos, len, &request->oid_list[request->oid_list_length]) == -1) return -1; /* The second element of the variable binding is the new type and value */ if (decode_len(client->packet, client->size, &pos, &type, &len) == -1) return -1; if ((type == BER_TYPE_NULL && len) || (type != BER_TYPE_NULL && !len)) { lprintf(LOG_DEBUG, "%s value type %02X length %zu\n", varbind_msg, type, len); errno = EINVAL; return -1; } if (decode_ptr(client->packet, client->size, &pos, len) == -1) return -1; /* Now the OID list has one more entry */ request->oid_list_length++; } return 0; }