static int arp_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request) { int i; uint8_t const *p = request->packet->data, *end = p + request->packet->data_len; fr_cursor_t cursor; fr_cursor_init(&cursor, &request->packet->vps); for (i = 0; header_names[i].name != NULL; i++) { ssize_t ret; size_t len; fr_dict_attr_t const *da; VALUE_PAIR *vp = NULL; len = header_names[i].len; if (!fr_cond_assert((size_t)(end - p) < len)) return -1; /* Should have been detected in socket_recv */ da = fr_dict_attr_by_name(dict_arp, header_names[i].name); if (!da) return 0; MEM(vp = fr_pair_afrom_da(request->packet, da)); ret = fr_value_box_from_network(vp, &vp->data, da->type, da, p, len, true); if (ret <= 0) { fr_pair_to_unknown(vp); fr_pair_value_memcpy(vp, p, len); } DEBUG2("&%pP", vp); fr_cursor_insert(&cursor, vp); } return 0; }
/** * * FIXME do something with mandatory */ ssize_t eap_fast_decode_pair(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len, void *decoder_ctx) { fr_dict_attr_t const *da; uint8_t const *p = data, *end = p + data_len; /* * Decode the TLVs */ while (p < end) { ssize_t ret; uint16_t attr; uint16_t len; VALUE_PAIR *vp; attr = fr_ntoh16_bin(p) & EAP_FAST_TLV_TYPE; p += 2; len = fr_ntoh16_bin(p); p += 2; da = fr_dict_attr_child_by_num(parent, attr); if (!da) { MEM(vp = fr_pair_afrom_child_num(ctx, parent, attr)); } else if (da->type == FR_TYPE_TLV) { p += (size_t) eap_fast_decode_pair(ctx, cursor, parent, p, len, decoder_ctx); continue; } else { MEM(vp = fr_pair_afrom_da(ctx, da)); } ret = fr_value_box_from_network(vp, &vp->data, vp->vp_type, vp->da, p, len, true); if (ret != len) { fr_pair_to_unknown(vp); fr_pair_value_memcpy(vp, p, len, true); } fr_cursor_append(cursor, vp); p += len; } return p - data; }
/** Builds attribute representing OID string and adds 'index' attributes where required * * Will convert an OID string in the format @verbatim .1.2.3.4.5.0 @endverbatim * into a pair with a #fr_dict_attr_t of the dictionary attribute matching the OID * string, as evaluated from the specified parent. * * If an OID component does not match a child of a previous OID component, but a child * with attribute number 0 exists, and a child with attribute number 1 also exists, * the child with attribute number 0 will be used as an 'index' pair, and will be * created with the value of the non matching OID component. * * Parsing will then resume using the child with attribute number 1. * * This allows traversals of SNMP tables to be represented by the sequence of pairs * and allows the full range of entry indexes which would not be possible if we represented * table index numbers as TLV attributes. * * @param[in] ctx to allocate new pairs in. * @param[in] conf radsnmp config. * @param[in] cursor to add pairs to. * @param[in] oid string to evaluate. * @param[in] type SNMP value type. * @param[in] value to assign to OID attribute (SET operations only). * @return * - >0 on success (how much of the OID string we parsed). * - <=0 on failure (where format error occurred). */ static ssize_t radsnmp_pair_from_oid(TALLOC_CTX *ctx, radsnmp_conf_t *conf, fr_cursor_t *cursor, char const *oid, int type, char const *value) { ssize_t slen; char const *p = oid; unsigned int attr; fr_dict_attr_t const *index_attr, *da; fr_dict_attr_t const *parent = conf->snmp_root; VALUE_PAIR *vp; int ret; if (!oid) return 0; fr_cursor_tail(cursor); /* * Trim first. */ if (p[0] == '.') p++; /* * Support for indexes. If we can't find an attribute * matching a child at a given level in the OID tree, * look for attribute 0 (type integer) at that level. * We use this to represent the index instead. */ for (;;) { unsigned int num = 0; slen = fr_dict_attr_by_oid(conf->dict, &parent, &attr, p); if (slen > 0) break; p += -(slen); if (fr_dict_oid_component(&num, &p) < 0) break; /* Just advances the pointer */ assert(attr == num); p++; /* * Check for an index attribute */ index_attr = fr_dict_attr_child_by_num(parent, 0); if (!index_attr) { fr_strerror_printf("Unknown OID component: No index attribute at this level"); break; } if (index_attr->type != FR_TYPE_UINT32) { fr_strerror_printf("Index is not a \"integer\""); break; } /* * By convention SNMP entries are at .1 */ parent = fr_dict_attr_child_by_num(parent, 1); if (!parent) { fr_strerror_printf("Unknown OID component: No entry attribute at this level"); break; } /* * Entry must be a TLV type */ if (parent->type != FR_TYPE_TLV) { fr_strerror_printf("Entry is not \"tlv\""); break; } /* * We've skipped over the index attribute, and * the index number should be available in attr. */ MEM(vp = fr_pair_afrom_da(ctx, index_attr)); vp->vp_uint32 = attr; fr_cursor_append(cursor, vp); } /* * We errored out processing the OID. */ if (slen <= 0) { error: fr_cursor_free_list(cursor); return slen; } fr_strerror(); /* Clear pending errors */ /* * SNMP requests the leaf under the OID with .0. */ if (attr != 0) { da = fr_dict_attr_child_by_num(parent, attr); if (!da) { fr_strerror_printf("Unknown leaf attribute %i", attr); return -(slen); } } else { da = parent; } vp = fr_pair_afrom_da(ctx, da); if (!vp) { fr_strerror_printf("Failed allocating OID attribute"); return -(slen); } /* * VALUE_PAIRs with no value need a 1 byte value buffer. */ if (!value) { switch (da->type) { /* * We can blame the authors of RFC 6929 for * this hack. Apparently the presence or absence * of an attribute isn't considered a useful means * of conveying information, so empty TLVs are * disallowed. */ case FR_TYPE_TLV: fr_pair_to_unknown(vp); /* FALL-THROUGH */ case FR_TYPE_OCTETS: fr_pair_value_memcpy(vp, (uint8_t const *)"\0", 1, true); break; case FR_TYPE_STRING: fr_pair_value_bstrncpy(vp, "\0", 1); break; /* * Fine to leave other values zeroed out. */ default: break; } fr_cursor_append(cursor, vp); return slen; } if (da->type == FR_TYPE_TLV) { fr_strerror_printf("TLVs cannot hold values"); return -(slen); } ret = fr_pair_value_from_str(vp, value, strlen(value), '\0', true); if (ret < 0) { slen = -(slen); goto error; } vp = fr_pair_afrom_da(ctx, attr_freeradius_snmp_type); if (!vp) { slen = -(slen); goto error; } vp->vp_uint32 = type; fr_cursor_append(cursor, vp); return slen; }
/* * Decode ONE value into a VP */ static ssize_t decode_value_internal(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_attr_t const *da, uint8_t const *data, size_t data_len) { VALUE_PAIR *vp; uint8_t const *p = data; FR_PROTO_TRACE("%s called to parse %zu bytes", __FUNCTION__, data_len); FR_PROTO_HEX_DUMP(data, data_len, NULL); vp = fr_pair_afrom_da(ctx, da); if (!vp) return -1; /* * Unknown attributes always get converted to * octet types, so there's no way there could * be multiple attributes, so its safe to * steal the unknown attribute into the context * of the pair. */ if (da->flags.is_unknown) talloc_steal(vp, da); if (vp->da->type == FR_TYPE_STRING) { uint8_t const *q, *end; q = end = data + data_len; /* * Not allowed to be an array, copy the whole value */ if (!vp->da->flags.array) { fr_pair_value_bstrncpy(vp, (char const *)p, end - p); p = end; goto finish; } for (;;) { q = memchr(p, '\0', q - p); /* Malformed but recoverable */ if (!q) q = end; fr_pair_value_bstrncpy(vp, (char const *)p, q - p); p = q + 1; vp->vp_tainted = true; /* Need another VP for the next round */ if (p < end) { fr_cursor_append(cursor, vp); vp = fr_pair_afrom_da(ctx, da); if (!vp) return -1; continue; } break; } goto finish; } switch (vp->da->type) { /* * Doesn't include scope, whereas the generic format can */ case FR_TYPE_IPV6_ADDR: memcpy(&vp->vp_ipv6addr, p, sizeof(vp->vp_ipv6addr)); vp->vp_ip.af = AF_INET6; vp->vp_ip.scope_id = 0; vp->vp_ip.prefix = 128; vp->vp_tainted = true; p += sizeof(vp->vp_ipv6addr); break; case FR_TYPE_IPV6_PREFIX: memcpy(&vp->vp_ipv6addr, p + 1, sizeof(vp->vp_ipv6addr)); vp->vp_ip.af = AF_INET6; vp->vp_ip.scope_id = 0; vp->vp_ip.prefix = p[0]; vp->vp_tainted = true; p += sizeof(vp->vp_ipv6addr) + 1; break; default: { ssize_t ret; ret = fr_value_box_from_network(vp, &vp->data, vp->da->type, da, p, data_len, true); if (ret < 0) { FR_PROTO_TRACE("decoding as unknown type"); if (fr_pair_to_unknown(vp) < 0) return -1; fr_pair_value_memcpy(vp, p, data_len); ret = data_len; } p += (size_t) ret; } } finish: FR_PROTO_TRACE("decoding value complete, adding new pair and returning %zu byte(s)", p - data); fr_cursor_append(cursor, vp); return p - data; }