/* * get the vps and put them in perl hash * If one VP have multiple values it is added as array_ref * Example for this is Cisco-AVPair that holds multiple values. * Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'} */ static void perl_store_vps(VALUE_PAIR *vp, HV *rad_hv) { VALUE_PAIR *nvp, *vpa, *vpn; AV *av; char namebuf[256], *name; char buffer[1024]; int attr, vendor, len; hv_undef(rad_hv); nvp = paircopy(vp); while (nvp != NULL) { name = nvp->name; attr = nvp->attribute; vendor = nvp->vendor; vpa = paircopy2(nvp, attr, vendor); if (vpa->next) { av = newAV(); vpn = vpa; while (vpn) { len = vp_prints_value(buffer, sizeof(buffer), vpn, FALSE); av_push(av, newSVpv(buffer, len)); vpn = vpn->next; } hv_store(rad_hv, nvp->name, strlen(nvp->name), newRV_noinc((SV *) av), 0); } else { if ((vpa->flags.has_tag) && (vpa->flags.tag != 0)) { snprintf(namebuf, sizeof(namebuf), "%s:%d", nvp->name, nvp->flags.tag); name = namebuf; } len = vp_prints_value(buffer, sizeof(buffer), vpa, FALSE); hv_store(rad_hv, name, strlen(name), newSVpv(buffer, len), 0); } pairfree(&vpa); vpa = nvp; while ((vpa != NULL) && (vpa->attribute == attr) && (vpa->vendor == vendor)) vpa = vpa->next; pairdelete(&nvp, attr, vendor); nvp = vpa; } }
/** Copy pairs matching a VPT in the current request * * @param out where to write the copied vps. * @param request current request. * @param vpt the value pair template * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found. */ int radius_vpt_copy_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR **vps, *vp; REQUEST *current = request; if (out) *out = NULL; if (radius_request(¤t, vpt->request) < 0) { return -3; } vps = radius_list(request, vpt->list); if (!vps) { return -2; } switch (vpt->type) { /* * May not may not be found, but it *is* a known name. */ case VPT_TYPE_ATTR: vp = paircopy2(request, *vps, vpt->da->attr, vpt->da->vendor, TAG_ANY); if (!vp) { return -1; } break; case VPT_TYPE_LIST: vp = paircopy(request, *vps); break; default: /* * literal, xlat, regex, exec, data. * no attribute. */ return -1; } if (out) { *out = vp; } return 0; }
/* * get the vps and put them in perl hash * If one VP have multiple values it is added as array_ref * Example for this is Cisco-AVPair that holds multiple values. * Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'} */ static void perl_store_vps(VALUE_PAIR *vp, HV *rad_hv) { VALUE_PAIR *nvp, *vpa, *vpn; AV *av; char buffer[1024]; int attr, len; hv_undef(rad_hv); nvp = paircopy(vp); while (nvp != NULL) { attr = nvp->attribute; vpa = paircopy2(nvp,attr); if (vpa->next) { av = newAV(); vpn = vpa; while (vpn) { len = vp_prints_value(buffer, sizeof(buffer), vpn, FALSE); av_push(av, newSVpv(buffer, len)); vpn = vpn->next; } hv_store(rad_hv, nvp->name, strlen(nvp->name), newRV_noinc((SV *) av), 0); } else { len = vp_prints_value(buffer, sizeof(buffer), vpa, FALSE); hv_store(rad_hv, vpa->name, strlen(vpa->name), newSVpv(buffer, len), 0); } pairfree(&vpa); vpa = nvp; while ((vpa != NULL) && (vpa->attribute == attr)) vpa = vpa->next; pairdelete(&nvp, attr); nvp = vpa; } }
static int respond_eap_md5(RADIUS_PACKET *req, RADIUS_PACKET *rep) { VALUE_PAIR *vp, *id, *state; size_t valuesize; uint8_t identifier; uint8_t const *value; FR_MD5_CTX context; uint8_t response[16]; cleanresp(rep); if ((state = paircopy2(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL) { fprintf(stderr, "radeapclient: no state attribute found\n"); return 0; } if ((id = paircopy2(NULL, req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY)) == NULL) { fprintf(stderr, "radeapclient: no EAP-ID attribute found\n"); return 0; } identifier = id->vp_integer; if ((vp = pairfind(req->vps, ATTRIBUTE_EAP_BASE+PW_EAP_MD5, 0, TAG_ANY)) == NULL) { fprintf(stderr, "radeapclient: no EAP-MD5 attribute found\n"); return 0; } /* got the details of the MD5 challenge */ valuesize = vp->vp_octets[0]; value = &vp->vp_octets[1]; /* sanitize items */ if(valuesize > vp->length) { fprintf(stderr, "radeapclient: md5 valuesize if too big (%u > %u)\n", (unsigned int) valuesize, (unsigned int) vp->length); return 0; } /* now do the CHAP operation ourself, rather than build the * buffer. We could also call rad_chap_encode, but it wants * a CHAP-Challenge, which we don't want to bother with. */ fr_md5_init(&context); fr_md5_update(&context, &identifier, 1); fr_md5_update(&context, (uint8_t *) password, strlen(password)); fr_md5_update(&context, value, valuesize); fr_md5_final(response, &context); { uint8_t *p; uint8_t lg_response; vp = paircreate(rep, ATTRIBUTE_EAP_BASE+PW_EAP_MD5, 0); vp->length = 17; p = talloc_zero_array(vp, uint8_t, 17); lg_response = 16; memcpy(p, &lg_response, 1); memcpy(p + 1, response, 16); pairmemsteal(vp, p); } pairreplace(&(rep->vps), vp); pairreplace(&(rep->vps), id); /* copy the state object in */ pairreplace(&(rep->vps), state); return 1; }
/* * this code runs the EAP-SIM client state machine. * the *request* is from the server. * the *reponse* is to the server. * */ static int respond_eap_sim(RADIUS_PACKET *req, RADIUS_PACKET *resp) { enum eapsim_clientstates state, newstate; enum eapsim_subtype subtype; VALUE_PAIR *vp, *statevp, *radstate, *eapid; char statenamebuf[32], subtypenamebuf[32]; if ((radstate = paircopy2(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL) { return 0; } if ((eapid = paircopy2(NULL, req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY)) == NULL) { return 0; } /* first, dig up the state from the request packet, setting * outselves to be in EAP-SIM-Start state if there is none. */ if((statevp = pairfind(resp->vps, ATTRIBUTE_EAP_SIM_STATE, 0, TAG_ANY)) == NULL) { /* must be initial request */ statevp = paircreate(resp, ATTRIBUTE_EAP_SIM_STATE, 0); statevp->vp_integer = eapsim_client_init; pairreplace(&(resp->vps), statevp); } state = statevp->vp_integer; /* * map the attributes, and authenticate them. */ unmap_eapsim_types(req); if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_SUBTYPE, 0, TAG_ANY)) == NULL) { return 0; } subtype = vp->vp_integer; /* * look for the appropriate state, and process incoming message */ switch(state) { case eapsim_client_init: switch(subtype) { case eapsim_start: newstate = process_eap_start(req, resp); break; case eapsim_challenge: case eapsim_notification: case eapsim_reauth: default: fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n", sim_state2name(state, statenamebuf, sizeof(statenamebuf)), sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf))); /* invalid state, drop message */ return 0; } break; case eapsim_client_start: switch(subtype) { case eapsim_start: /* NOT SURE ABOUT THIS ONE, retransmit, I guess */ newstate = process_eap_start(req, resp); break; case eapsim_challenge: newstate = process_eap_challenge(req, resp); break; default: fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n", sim_state2name(state, statenamebuf, sizeof(statenamebuf)), sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf))); /* invalid state, drop message */ return 0; } break; default: fprintf(stderr, "radeapclient: sim in illegal state %s\n", sim_state2name(state, statenamebuf, sizeof(statenamebuf))); return 0; } /* copy the eap state object in */ pairreplace(&(resp->vps), eapid); /* update stete info, and send new packet */ map_eapsim_types(resp); /* copy the radius state object in */ pairreplace(&(resp->vps), radstate); statevp->vp_integer = newstate; return 1; }
/* * get the vps and put them in perl hash * If one VP have multiple values it is added as array_ref * Example for this is Cisco-AVPair that holds multiple values. * Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'} */ static void perl_store_vps(VALUE_PAIR *vp, HV *rad_hv) { VALUE_PAIR *nvp, *vpa, *vpn; AV *av; const char *name; char namebuf[256]; char buffer[1024]; int len; hv_undef(rad_hv); /* * Copy the valuepair list so we can remove attributes we've * already processed. */ nvp = paircopy(vp); while (nvp != NULL) { /* * Tagged attributes are added to the hash with name * <attribute>:<tag>, others just use the normal attribute * name as the key. */ if (nvp->flags.has_tag && (nvp->flags.tag != 0)) { snprintf(namebuf, sizeof(namebuf), "%s:%d", nvp->name, nvp->flags.tag); name = namebuf; } else { name = nvp->name; } /* * Create a new list with all the attributes like this one * which are in the same tag group. */ vpa = paircopy2(nvp, nvp->attribute, nvp->vendor, nvp->flags.tag); /* * Attribute has multiple values */ if (vpa->next) { av = newAV(); for (vpn = vpa; vpn; vpn = vpn->next) { len = vp_prints_value(buffer, sizeof(buffer), vpn, FALSE); av_push(av, newSVpv(buffer, len)); } (void)hv_store(rad_hv, name, strlen(name), newRV_noinc((SV *)av), 0); /* * Attribute has a single value, so its value just gets * added to the hash. */ } else { len = vp_prints_value(buffer, sizeof(buffer), vpa, FALSE); (void)hv_store(rad_hv, name, strlen(name), newSVpv(buffer, len), 0); } pairfree(&vpa); /* * Find the next attribute which we won't have processed, * we need to do this so we know it won't be freed on * pairdelete. */ vpa = nvp->next; while ((vpa != NULL) && (vpa->attribute == nvp->attribute) && (vpa->vendor == nvp->vendor) && (vpa->flags.tag == nvp->flags.tag)) { vpa = vpa->next; } /* * Finally remove all the VPs we processed from our copy * of the list. */ pairdelete(&nvp, nvp->attribute, nvp->vendor, nvp->flags.tag); nvp = vpa; } }
int eaptls_success(EAP_HANDLER *handler, int peap_flag) { EAPTLS_PACKET reply; VALUE_PAIR *vp, *vps = NULL; REQUEST *request = handler->request; tls_session_t *tls_session = handler->opaque; reply.code = EAPTLS_SUCCESS; reply.length = TLS_HEADER_LEN; reply.flags = peap_flag; reply.data = NULL; reply.dlen = 0; /* * If there's no session resumption, delete the entry * from the cache. This means either it's disabled * globally for this SSL context, OR we were told to * disable it for this user. * * This also means you can't turn it on just for one * user. */ if ((!tls_session->allow_session_resumption) || (((vp = pairfind(request->config_items, 1127)) != NULL) && (vp->vp_integer == 0))) { SSL_CTX_remove_session(tls_session->ctx, tls_session->ssl->session); tls_session->allow_session_resumption = 0; /* * If we're in a resumed session and it's * not allowed, */ if (SSL_session_reused(tls_session->ssl)) { RDEBUG("FAIL: Forcibly stopping session resumption as it is not allowed."); return eaptls_fail(handler, peap_flag); } /* * Else resumption IS allowed, so we store the * user data in the cache. */ } else if (!SSL_session_reused(tls_session->ssl)) { RDEBUG2("Saving response in the cache"); vp = paircopy2(request->reply->vps, PW_USER_NAME); pairadd(&vps, vp); vp = paircopy2(request->packet->vps, PW_STRIPPED_USER_NAME); pairadd(&vps, vp); if (vps) { SSL_SESSION_set_ex_data(tls_session->ssl->session, eaptls_session_idx, vps); } else { RDEBUG2("WARNING: No information to cache: session caching will be disabled for this session."); SSL_CTX_remove_session(tls_session->ctx, tls_session->ssl->session); } /* * Else the session WAS allowed. Copy the cached * reply. */ } else { vp = SSL_SESSION_get_ex_data(tls_session->ssl->session, eaptls_session_idx); if (!vp) { RDEBUG("WARNING: No information in cached session!"); return eaptls_fail(handler, peap_flag); } else { RDEBUG("Adding cached attributes to the reply:"); debug_pair_list(vp); pairadd(&request->reply->vps, paircopy(vp)); /* * Mark the request as resumed. */ vp = pairmake("EAP-Session-Resumed", "1", T_OP_SET); if (vp) pairadd(&request->packet->vps, vp); } } /* * Call compose AFTER checking for cached data. */ eaptls_compose(handler->eap_ds, &reply); /* * Automatically generate MPPE keying material. */ if (tls_session->prf_label) { eaptls_gen_mppe_keys(&handler->request->reply->vps, tls_session->ssl, tls_session->prf_label); } else { RDEBUG("WARNING: Not adding MPPE keys because there is no PRF label"); } return 1; }
/** Convert a map to a VALUE_PAIR. * * @param[out] out Where to write the VALUE_PAIR(s). * @param[in] request structure (used only for talloc) * @param[in] map the map. The LHS (dst) has to be VPT_TYPE_ATTR or VPT_TYPE_LIST. * @param[in] ctx unused * @return 0 on success, -1 on failure, -2 on attribute not found/equivalent */ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx) { int rcode = 0; VALUE_PAIR *vp = NULL, *found, **from = NULL; DICT_ATTR const *da; REQUEST *context; vp_cursor_t cursor; rad_assert(request != NULL); rad_assert(map != NULL); *out = NULL; /* * Special case for !*, we don't need to parse the value, just allocate an attribute with * the right operator. */ if (map->op == T_OP_CMP_FALSE) { vp = pairalloc(request, map->dst->da); if (!vp) return -1; vp->op = map->op; *out = vp; return 0; } /* * List to list found, this is a special case because we don't need * to allocate any attributes, just found the current list, and change * the op. */ if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) { from = radius_list(request, map->src->list); if (!from) return -2; found = paircopy(request, *from); /* * List to list copy is invalid if the src list has no attributes. */ if (!found) return -2; for (vp = paircursor(&cursor, &found); vp; vp = pairnext(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } /* * Deal with all non-list founding operations. */ da = map->dst->da ? map->dst->da : map->src->da; switch (map->src->type) { case VPT_TYPE_XLAT: case VPT_TYPE_LITERAL: case VPT_TYPE_DATA: vp = pairalloc(request, da); if (!vp) return -1; vp->op = map->op; break; default: break; } /* * And parse the RHS */ switch (map->src->type) { case VPT_TYPE_XLAT: rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */ /* * Don't call unnecessary expansions */ if (strchr(map->src->name, '%') != NULL) { ssize_t slen; char *str = NULL; slen = radius_axlat(&str, request, map->src->name, NULL, NULL); if (slen < 0) { rcode = slen; goto error; } rcode = pairparsevalue(vp, str); talloc_free(str); if (!rcode) { pairfree(&vp); rcode = -1; goto error; } break; } /* FALL-THROUGH */ case VPT_TYPE_LITERAL: if (!pairparsevalue(vp, map->src->name)) { rcode = -2; goto error; } break; case VPT_TYPE_ATTR: rad_assert(!map->dst->da || (map->src->da->type == map->dst->da->type) || (map->src->da->type == PW_TYPE_OCTETS) || (map->dst->da->type == PW_TYPE_OCTETS)); context = request; if (radius_request(&context, map->src->request) == 0) { from = radius_list(context, map->src->list); } /* * Can't add the attribute if the list isn't * valid. */ if (!from) { rcode = -2; goto error; } /* * Special case, destination is a list, found all instance of an attribute. */ if (map->dst->type == VPT_TYPE_LIST) { found = paircopy2(request, *from, map->src->da->attr, map->src->da->vendor, TAG_ANY); if (!found) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } for (vp = paircursor(&cursor, &found); vp; vp = pairnext(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } /* * FIXME: allow tag references? */ found = pairfind(*from, map->src->da->attr, map->src->da->vendor, TAG_ANY); if (!found) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } /* * Copy the data over verbatim, assuming it's * actually data. */ // rad_assert(found->type == VT_DATA); vp = paircopyvpdata(request, da, found); if (!vp) { return -1; } vp->op = map->op; break; case VPT_TYPE_DATA: rad_assert(map->src->da->type == map->dst->da->type); memcpy(&vp->data, map->src->vpd, sizeof(vp->data)); vp->length = map->src->length; break; /* * This essentially does the same as rlm_exec xlat, except it's non-configurable. * It's only really here as a convenience for people who expect the contents of * backticks to be executed in a shell. * * exec string is xlat expanded and arguments are shell escaped. */ case VPT_TYPE_EXEC: return radius_mapexec(out, request, map); default: rad_assert(0); /* Should of been caught at parse time */ error: pairfree(&vp); return rcode; } *out = vp; return 0; }
static int respond_eap_md5(RADIUS_PACKET *req, RADIUS_PACKET *rep) { VALUE_PAIR *vp, *id, *state; int valuesize, namesize; unsigned char identifier; unsigned char *value; unsigned char *name; MD5_CTX context; char response[16]; cleanresp(rep); if ((state = paircopy2(req->vps, PW_STATE)) == NULL) { fprintf(stderr, "radeapclient: no state attribute found\n"); return 0; } if ((id = paircopy2(req->vps, ATTRIBUTE_EAP_ID)) == NULL) { fprintf(stderr, "radeapclient: no EAP-ID attribute found\n"); return 0; } identifier = id->lvalue; if ((vp = pairfind(req->vps, ATTRIBUTE_EAP_BASE+PW_EAP_MD5)) == NULL) { fprintf(stderr, "radeapclient: no EAP-MD5 attribute found\n"); return 0; } /* got the details of the MD5 challenge */ valuesize = vp->strvalue[0]; value = &vp->strvalue[1]; name = &vp->strvalue[valuesize+1]; namesize = vp->length - (valuesize + 1); /* sanitize items */ if(valuesize > vp->length) { fprintf(stderr, "radeapclient: md5 valuesize if too big (%d > %d)\n", valuesize, vp->length); return 0; } /* now do the CHAP operation ourself, rather than build the * buffer. We could also call rad_chap_encode, but it wants * a CHAP-Challenge, which we don't want to bother with. */ librad_MD5Init(&context); librad_MD5Update(&context, &identifier, 1); librad_MD5Update(&context, password, strlen(password)); librad_MD5Update(&context, value, valuesize); librad_MD5Final(response, &context); vp = paircreate(ATTRIBUTE_EAP_BASE+PW_EAP_MD5, PW_TYPE_OCTETS); vp->strvalue[0]=16; memcpy(&vp->strvalue[1], response, 16); vp->length = 17; pairreplace(&(rep->vps), vp); pairreplace(&(rep->vps), id); /* copy the state object in */ pairreplace(&(rep->vps), state); return 1; }
/** Convert a map to a VALUE_PAIR. * * @param[out] out Where to write the VALUE_PAIR(s). * @param[in] request structure (used only for talloc) * @param[in] map the map. The LHS (dst) has to be VPT_TYPE_ATTR or VPT_TYPE_LIST. * @param[in] ctx unused * @return 0 on success, -1 on failure, -2 on attribute not found/equivalent */ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx) { int rcode = 0; VALUE_PAIR *vp = NULL, *found, **from = NULL; DICT_ATTR const *da; REQUEST *context = request; vp_cursor_t cursor; rad_assert(request != NULL); rad_assert(map != NULL); *out = NULL; /* * Special case for !*, we don't need to parse RHS as this is a unary operator. */ if (map->op == T_OP_CMP_FALSE) { /* * Were deleting all the attributes in a list. This isn't like the other * mappings because lists aren't represented as attributes (yet), * so we can't return a <list> attribute with the !* operator for * radius_pairmove() to consume, and need to do the work here instead. */ if (map->dst->type == VPT_TYPE_LIST) { if (radius_request(&context, map->dst->request) == 0) { from = radius_list(context, map->dst->list); } if (!from) return -2; pairfree(from); /* @fixme hacky! */ if (map->dst->list == PAIR_LIST_REQUEST) { context->username = NULL; context->password = NULL; } return 0; } /* Not a list, but an attribute, radius_pairmove() will perform that actual delete */ vp = pairalloc(request, map->dst->da); if (!vp) return -1; vp->op = map->op; *out = vp; return 0; } /* * List to list found, this is a special case because we don't need * to allocate any attributes, just finding the current list, and change * the op. */ if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) { if (radius_request(&context, map->src->request) == 0) { from = radius_list(context, map->src->list); } if (!from) return -2; found = paircopy(request, *from); /* * List to list copy is invalid if the src list has no attributes. */ if (!found) return -2; for (vp = fr_cursor_init(&cursor, &found); vp; vp = fr_cursor_next(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } /* * Deal with all non-list operations. */ da = map->dst->da ? map->dst->da : map->src->da; switch (map->src->type) { case VPT_TYPE_XLAT: case VPT_TYPE_XLAT_STRUCT: case VPT_TYPE_LITERAL: case VPT_TYPE_DATA: vp = pairalloc(request, da); if (!vp) return -1; vp->op = map->op; break; default: break; } /* * And parse the RHS */ switch (map->src->type) { ssize_t slen; char *str; case VPT_TYPE_XLAT_STRUCT: rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */ rad_assert(map->src->xlat != NULL); str = NULL; slen = radius_axlat_struct(&str, request, map->src->xlat, NULL, NULL); if (slen < 0) { rcode = slen; goto error; } /* * We do the debug printing because radius_axlat_struct * doesn't have access to the original string. It's been * mangled during the parsing to xlat_exp_t */ RDEBUG2("EXPAND %s", map->src->name); RDEBUG2(" --> %s", str); rcode = pairparsevalue(vp, str); talloc_free(str); if (!rcode) { pairfree(&vp); rcode = -1; goto error; } break; case VPT_TYPE_XLAT: rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */ str = NULL; slen = radius_axlat(&str, request, map->src->name, NULL, NULL); if (slen < 0) { rcode = slen; goto error; } rcode = pairparsevalue(vp, str); talloc_free(str); if (!rcode) { pairfree(&vp); rcode = -1; goto error; } break; case VPT_TYPE_LITERAL: if (!pairparsevalue(vp, map->src->name)) { rcode = -2; goto error; } break; case VPT_TYPE_ATTR: rad_assert(!map->dst->da || (map->src->da->type == map->dst->da->type) || (map->src->da->type == PW_TYPE_OCTETS) || (map->dst->da->type == PW_TYPE_OCTETS)); /* * Special case, destination is a list, found all instance of an attribute. */ if (map->dst->type == VPT_TYPE_LIST) { context = request; if (radius_request(&context, map->src->request) == 0) { from = radius_list(context, map->src->list); } /* * Can't add the attribute if the list isn't * valid. */ if (!from) { rcode = -2; goto error; } found = paircopy2(request, *from, map->src->da->attr, map->src->da->vendor, TAG_ANY); if (!found) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } for (vp = fr_cursor_init(&cursor, &found); vp; vp = fr_cursor_next(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } if (radius_vpt_get_vp(&found, request, map->src) < 0) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } /* * Copy the data over verbatim, assuming it's * actually data. */ vp = paircopyvpdata(request, da, found); if (!vp) { return -1; } vp->op = map->op; break; case VPT_TYPE_DATA: rad_assert(map->src && map->src->da); rad_assert(map->dst && map->dst->da); rad_assert(map->src->da->type == map->dst->da->type); memcpy(&vp->data, map->src->vpd, sizeof(vp->data)); vp->length = map->src->length; break; /* * This essentially does the same as rlm_exec xlat, except it's non-configurable. * It's only really here as a convenience for people who expect the contents of * backticks to be executed in a shell. * * exec string is xlat expanded and arguments are shell escaped. */ case VPT_TYPE_EXEC: return radius_mapexec(out, request, map); default: rad_assert(0); /* Should of been caught at parse time */ error: pairfree(&vp); return rcode; } *out = vp; return 0; }
/* * Process the "diameter" contents of the tunneled data. */ int eapttls_process(EAP_HANDLER *handler, tls_session_t *tls_session) { int rcode = PW_AUTHENTICATION_REJECT; REQUEST *fake; VALUE_PAIR *vp; ttls_tunnel_t *t; const uint8_t *data; size_t data_len; REQUEST *request = handler->request; rad_assert(request != NULL); /* * Just look at the buffer directly, without doing * record_minus. */ data_len = tls_session->clean_out.used; tls_session->clean_out.used = 0; data = tls_session->clean_out.data; t = (ttls_tunnel_t *) tls_session->opaque; /* * If there's no data, maybe this is an ACK to an * MS-CHAP2-Success. */ if (data_len == 0) { if (t->authenticated) { RDEBUG("Got ACK, and the user was already authenticated."); return PW_AUTHENTICATION_ACK; } /* else no session, no data, die. */ /* * FIXME: Call SSL_get_error() to see what went * wrong. */ RDEBUG2("SSL_read Error"); return PW_AUTHENTICATION_REJECT; } #ifndef NDEBUG if ((debug_flag > 2) && fr_log_fp) { size_t i; for (i = 0; i < data_len; i++) { if ((i & 0x0f) == 0) fprintf(fr_log_fp, " TTLS tunnel data in %04x: ", (int) i); fprintf(fr_log_fp, "%02x ", data[i]); if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n"); } if ((data_len & 0x0f) != 0) fprintf(fr_log_fp, "\n"); } #endif if (!diameter_verify(request, data, data_len)) { return PW_AUTHENTICATION_REJECT; } /* * Allocate a fake REQUEST structe. */ fake = request_alloc_fake(request); rad_assert(fake->packet->vps == NULL); /* * Add the tunneled attributes to the fake request. */ fake->packet->vps = diameter2vp(request, tls_session->ssl, data, data_len); if (!fake->packet->vps) { request_free(&fake); return PW_AUTHENTICATION_REJECT; } /* * Tell the request that it's a fake one. */ vp = pairmake("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ); if (vp) { pairadd(&fake->packet->vps, vp); } if ((debug_flag > 0) && fr_log_fp) { RDEBUG("Got tunneled request"); debug_pair_list(fake->packet->vps); } /* * Update other items in the REQUEST data structure. */ fake->username = pairfind(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY); fake->password = pairfind(fake->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY); /* * No User-Name, try to create one from stored data. */ if (!fake->username) { /* * No User-Name in the stored data, look for * an EAP-Identity, and pull it out of there. */ if (!t->username) { vp = pairfind(fake->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY); if (vp && (vp->length >= EAP_HEADER_LEN + 2) && (vp->vp_strvalue[0] == PW_EAP_RESPONSE) && (vp->vp_strvalue[EAP_HEADER_LEN] == PW_EAP_IDENTITY) && (vp->vp_strvalue[EAP_HEADER_LEN + 1] != 0)) { /* * Create & remember a User-Name */ t->username = pairmake("User-Name", "", T_OP_EQ); rad_assert(t->username != NULL); memcpy(t->username->vp_strvalue, vp->vp_strvalue + 5, vp->length - 5); t->username->length = vp->length - 5; t->username->vp_strvalue[t->username->length] = 0; RDEBUG("Got tunneled identity of %s", t->username->vp_strvalue); /* * If there's a default EAP type, * set it here. */ if (t->default_eap_type != 0) { RDEBUG("Setting default EAP type for tunneled EAP session."); vp = paircreate(PW_EAP_TYPE, 0); rad_assert(vp != NULL); vp->vp_integer = t->default_eap_type; pairadd(&fake->config_items, vp); } } else { /* * Don't reject the request outright, * as it's permitted to do EAP without * user-name. */ RDEBUG2W("No EAP-Identity found to start EAP conversation."); } } /* else there WAS a t->username */ if (t->username) { vp = paircopy(t->username); pairadd(&fake->packet->vps, vp); fake->username = pairfind(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY); } } /* else the request ALREADY had a User-Name */ /* * Add the State attribute, too, if it exists. */ if (t->state) { vp = paircopy(t->state); if (vp) pairadd(&fake->packet->vps, vp); } /* * If this is set, we copy SOME of the request attributes * from outside of the tunnel to inside of the tunnel. * * We copy ONLY those attributes which do NOT already * exist in the tunneled request. */ if (t->copy_request_to_tunnel) { VALUE_PAIR *copy; for (vp = request->packet->vps; vp != NULL; vp = vp->next) { /* * The attribute is a server-side thingy, * don't copy it. */ if ((vp->da->attr > 255) && (vp->da->vendor == 0)) { continue; } /* * The outside attribute is already in the * tunnel, don't copy it. * * This works for BOTH attributes which * are originally in the tunneled request, * AND attributes which are copied there * from below. */ if (pairfind(fake->packet->vps, vp->da->attr, vp->da->vendor, TAG_ANY)) { continue; } /* * Some attributes are handled specially. */ switch (vp->da->attr) { /* * NEVER copy Message-Authenticator, * EAP-Message, or State. They're * only for outside of the tunnel. */ case PW_USER_NAME: case PW_USER_PASSWORD: case PW_CHAP_PASSWORD: case PW_CHAP_CHALLENGE: case PW_PROXY_STATE: case PW_MESSAGE_AUTHENTICATOR: case PW_EAP_MESSAGE: case PW_STATE: continue; break; /* * By default, copy it over. */ default: break; } /* * Don't copy from the head, we've already * checked it. */ copy = paircopy2(vp, vp->da->attr, vp->da->vendor, TAG_ANY); pairadd(&fake->packet->vps, copy); } } if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) { fake->server = vp->vp_strvalue; } else if (t->virtual_server) { fake->server = t->virtual_server; } /* else fake->server == request->server */ if ((debug_flag > 0) && fr_log_fp) { RDEBUG("Sending tunneled request"); debug_pair_list(fake->packet->vps); fprintf(fr_log_fp, "server %s {\n", (fake->server == NULL) ? "" : fake->server); } /* * Call authentication recursively, which will * do PAP, CHAP, MS-CHAP, etc. */ rad_virtual_server(fake); /* * Note that we don't do *anything* with the reply * attributes. */ if ((debug_flag > 0) && fr_log_fp) { fprintf(fr_log_fp, "} # server %s\n", (fake->server == NULL) ? "" : fake->server); RDEBUG("Got tunneled reply code %d", fake->reply->code); debug_pair_list(fake->reply->vps); } /* * Decide what to do with the reply. */ switch (fake->reply->code) { case 0: /* No reply code, must be proxied... */ #ifdef WITH_PROXY vp = pairfind(fake->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY); if (vp) { eap_tunnel_data_t *tunnel; RDEBUG("Tunneled authentication will be proxied to %s", vp->vp_strvalue); /* * Tell the original request that it's going * to be proxied. */ pairmove2(&(request->config_items), &(fake->config_items), PW_PROXY_TO_REALM, 0, TAG_ANY); /* * Seed the proxy packet with the * tunneled request. */ rad_assert(request->proxy == NULL); request->proxy = fake->packet; memset(&request->proxy->src_ipaddr, 0, sizeof(request->proxy->src_ipaddr)); memset(&request->proxy->src_ipaddr, 0, sizeof(request->proxy->src_ipaddr)); request->proxy->src_port = 0; request->proxy->dst_port = 0; fake->packet = NULL; rad_free(&fake->reply); fake->reply = NULL; /* * Set up the callbacks for the tunnel */ tunnel = rad_malloc(sizeof(*tunnel)); memset(tunnel, 0, sizeof(*tunnel)); tunnel->tls_session = tls_session; tunnel->callback = eapttls_postproxy; /* * Associate the callback with the request. */ rcode = request_data_add(request, request->proxy, REQUEST_DATA_EAP_TUNNEL_CALLBACK, tunnel, free); rad_assert(rcode == 0); /* * rlm_eap.c has taken care of associating * the handler with the fake request. * * So we associate the fake request with * this request. */ rcode = request_data_add(request, request->proxy, REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK, fake, my_request_free); rad_assert(rcode == 0); fake = NULL; /* * Didn't authenticate the packet, but * we're proxying it. */ rcode = PW_STATUS_CLIENT; } else #endif /* WITH_PROXY */ { RDEBUG("No tunneled reply was found for request %d , and the request was not proxied: rejecting the user.", request->number); rcode = PW_AUTHENTICATION_REJECT; } break; default: /* * Returns RLM_MODULE_FOO, and we want to return * PW_FOO */ rcode = process_reply(handler, tls_session, request, fake->reply); switch (rcode) { case RLM_MODULE_REJECT: rcode = PW_AUTHENTICATION_REJECT; break; case RLM_MODULE_HANDLED: rcode = PW_ACCESS_CHALLENGE; break; case RLM_MODULE_OK: rcode = PW_AUTHENTICATION_ACK; break; default: rcode = PW_AUTHENTICATION_REJECT; break; } break; } request_free(&fake); return rcode; }
/* * Copy a pairlist. */ VALUE_PAIR *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *vp) { return paircopy2(ctx, vp, 0, 0, TAG_ANY); }
/* * Copy a pairlist. */ VALUE_PAIR *paircopy(VALUE_PAIR *vp) { return paircopy2(vp, 0, 0, TAG_ANY); }
static int respond_eap_md5(RADIUS_PACKET *req, RADIUS_PACKET *rep,const char* pwd) { VALUE_PAIR *vp, *id, *state; size_t valuesize; uint8_t identifier; uint8_t *value; FR_MD5_CTX context; uint8_t response[16]; cleanresp(rep); if ((state = paircopy2(req->vps, PW_STATE)) == NULL) { fr_strerror_printf("radeapclient: no state attribute found"); return 0; } if ((id = paircopy2(req->vps, ATTRIBUTE_EAP_ID)) == NULL) { fr_strerror_printf("radeapclient: no EAP-ID attribute found"); return 0; } identifier = id->vp_integer; if ((vp = pairfind(req->vps, ATTRIBUTE_EAP_BASE+PW_EAP_MD5)) == NULL) { fr_strerror_printf("radeapclient: no EAP-MD5 attribute found"); return 0; } /* got the details of the MD5 challenge */ valuesize = vp->vp_octets[0]; value = &vp->vp_octets[1]; /* sanitize items */ if(valuesize > vp->length) { fr_strerror_printf("radeapclient: md5 valuesize if too big (%u > %u)\n", (unsigned int) valuesize, (unsigned int) vp->length); return 0; } /* now do the CHAP operation ourself, rather than build the * buffer. We could also call rad_chap_encode, but it wants * a CHAP-Challenge, which we don't want to bother with. */ fr_MD5Init(&context); fr_MD5Update(&context, &identifier, 1); fr_MD5Update(&context, (uint8_t *) pwd, strlen(pwd)); fr_MD5Update(&context, value, valuesize); fr_MD5Final(response, &context); vp = paircreate(ATTRIBUTE_EAP_BASE+PW_EAP_MD5, PW_TYPE_OCTETS); vp->vp_octets[0]=16; memcpy(&vp->vp_strvalue[1], response, 16); vp->length = 17; pairreplace(&(rep->vps), vp); pairreplace(&(rep->vps), id); /* copy the state object in */ pairreplace(&(rep->vps), state); return 1; }
main(int argc, char *argv[]) { int filedone; RADIUS_PACKET *req,*req2; VALUE_PAIR *vp, *vpkey, *vpextra; extern unsigned int sha1_data_problems; req = NULL; req2 = NULL; filedone = 0; if(argc>1) { sha1_data_problems = 1; } if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { librad_perror("radclient"); return 1; } if ((req = rad_alloc(1)) == NULL) { librad_perror("radclient"); exit(1); } if ((req2 = rad_alloc(1)) == NULL) { librad_perror("radclient"); exit(1); } while(!filedone) { if(req->vps) pairfree(&req->vps); if(req2->vps) pairfree(&req2->vps); if ((req->vps = readvp2(stdin, &filedone, "eapsimlib:")) == NULL) { break; } printf("\nRead:\n"); vp_printlist(stdout, req->vps); map_eapsim_types(req); map_eap_types(req); printf("Mapped to:\n"); vp_printlist(stdout, req->vps); /* find the EAP-Message, copy it to req2 */ vp = paircopy2(req->vps, PW_EAP_MESSAGE); if(vp == NULL) continue; pairadd(&req2->vps, vp); /* only call unmap for sim types here */ unmap_eap_types(req2); unmap_eapsim_types(req2); printf("Unmapped to:\n"); vp_printlist(stdout, req2->vps); vp = pairfind(req2->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC); vpkey = pairfind(req->vps, ATTRIBUTE_EAP_SIM_KEY); vpextra = pairfind(req->vps, ATTRIBUTE_EAP_SIM_EXTRA); if(vp != NULL && vpkey != NULL && vpextra!=NULL) { uint8_t calcmac[16]; /* find the EAP-Message, copy it to req2 */ memset(calcmac, 0, sizeof(calcmac)); printf("Confirming MAC..."); if(eapsim_checkmac(req2->vps, vpkey->strvalue, vpextra->strvalue, vpextra->length, calcmac)) { printf("succeed\n"); } else { int i, j; printf("calculated MAC ("); for (i = 0; i < 20; i++) { if(j==4) { printf("_"); j=0; } j++; printf("%02x", calcmac[i]); } printf(" did not match\n"); } } fflush(stdout); } }
/* * Copy a pairlist. */ VALUE_PAIR *paircopy(VALUE_PAIR *vp) { return paircopy2(vp, -1); }
main(int argc, char *argv[]) { int filedone; RADIUS_PACKET *req,*req2; VALUE_PAIR *vp, *vpkey, *vpextra; extern unsigned int sha1_data_problems; req = NULL; req2 = NULL; filedone = 0; if(argc>1) { sha1_data_problems = 1; } if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { ERROR("%s", fr_strerror()); return 1; } req = rad_alloc(NULL, true) if (!req) { ERROR("%s", fr_strerror()); exit(1); } req2 = rad_alloc(NULL, true); if (!req2) { ERROR("%s", fr_strerror()); exit(1); } while(!filedone) { if (req->vps) pairfree(&req->vps); if (req2->vps) pairfree(&req2->vps); if (readvp2(&req->vps, NULL, stdin, &filedone) < 0) { ERROR("%s", fr_strerror()); break; } if (fr_debug_flag > 1) { DEBUG("Read:"); vp_printlist(stdout, req->vps); } map_eapsim_types(req); map_eap_methods(req); if (fr_debug_flag > 1) { DEBUG("Mapped to:"); vp_printlist(stdout, req->vps); } /* find the EAP-Message, copy it to req2 */ vp = paircopy2(NULL, req->vps, PW_EAP_MESSAGE, 0, TAG_ANY); if(!vp) continue; pairadd(&req2->vps, vp); /* only call unmap for sim types here */ unmap_eap_methods(req2); unmap_eapsim_types(req2); if (fr_debug_flag > 1) { DEBUG("Unmapped to:"); vp_printlist(stdout, req2->vps); } vp = pairfind(req2->vps, PW_EAP_SIM_MAC, 0, TAG_ANY); vpkey = pairfind(req->vps, PW_EAP_SIM_KEY, 0, TAG_ANY); vpextra = pairfind(req->vps, PW_EAP_SIM_EXTRA, 0, TAG_ANY); if(vp != NULL && vpkey != NULL && vpextra!=NULL) { uint8_t calcmac[16]; /* find the EAP-Message, copy it to req2 */ memset(calcmac, 0, sizeof(calcmac)); DEBUG("Confirming MAC..."); if(eapsim_checkmac(req2->vps, vpkey->vp_strvalue, vpextra->vp_strvalue, vpextra->length, calcmac)) { DEBUG("succeed"); } else { int i, j; DEBUG("calculated MAC ("); for (i = 0; i < 20; i++) { if(j==4) { DEBUG("_"); j=0; } j++; DEBUG("%02x", calcmac[i]); } DEBUG("did not match"); } } fflush(stdout); } }