/* * Merge a cached entry into a REQUEST. */ static void cache_merge(rlm_cache_t *inst, REQUEST *request, rlm_cache_entry_t *c) { VALUE_PAIR *vp; rad_assert(request != NULL); rad_assert(c != NULL); if (c->control) { vp = paircopy(c->control); pairmove(&request->config_items, &vp); pairfree(&vp); } if (c->request && request->packet) { vp = paircopy(c->request); pairmove(&request->packet->vps, &vp); pairfree(&vp); } if (c->reply && request->reply) { vp = paircopy(c->reply); pairmove(&request->reply->vps, &vp); pairfree(&vp); } if (inst->stats) { vp = paircreate(PW_CACHE_ENTRY_HITS, 0, PW_TYPE_INTEGER); rad_assert(vp != NULL); vp->vp_integer = c->hits; pairadd(&request->packet->vps, vp); } }
/* * Merge a cached entry into a REQUEST. */ static void cache_merge(rlm_cache_t *inst, REQUEST *request, rlm_cache_entry_t *c) { VALUE_PAIR *vp; rad_assert(request != NULL); rad_assert(c != NULL); vp = pairfind(request->config_items, inst->cache_merge->attr, inst->cache_merge->vendor, TAG_ANY); if (vp && (vp->vp_integer == 0)) { RDEBUG2("Told not to merge entry into request"); return; } if (c->control) { RDEBUG2("Merging cached control list:"); rdebug_pair_list(2, request, c->control); vp = paircopy(c->control); pairmove(&request->config_items, &vp); pairfree(&vp); } if (c->request && request->packet) { RDEBUG2("Merging cached request list:"); rdebug_pair_list(2, request, c->request); vp = paircopy(c->request); pairmove(&request->packet->vps, &vp); pairfree(&vp); } if (c->reply && request->reply) { RDEBUG2("Merging cached reply list:"); rdebug_pair_list(2, request, c->reply); vp = paircopy(c->reply); pairmove(&request->reply->vps, &vp); pairfree(&vp); } if (inst->stats) { vp = pairalloc(inst->cache_entry_hits); rad_assert(vp != NULL); vp->vp_integer = c->hits; pairadd(&request->packet->vps, vp); } }
/* * Add hints to the info sent by the terminal server * based on the pattern of the username, and other attributes. */ static int hints_setup(PAIR_LIST *hints, REQUEST *request) { char const *name; VALUE_PAIR *add; VALUE_PAIR *tmp; PAIR_LIST *i; VALUE_PAIR *request_pairs; int updated = 0, ft; request_pairs = request->packet->vps; if (!hints || !request_pairs) return RLM_MODULE_NOOP; /* * Check for valid input, zero length names not permitted */ name = (tmp = pairfind(request_pairs, PW_USER_NAME, 0, TAG_ANY)) ? tmp->vp_strvalue : NULL; if (!name || name[0] == 0) { /* * No name, nothing to do. */ return RLM_MODULE_NOOP; } for (i = hints; i; i = i->next) { /* * Use "paircompare", which is a little more general... */ if (((strcmp(i->name, "DEFAULT") == 0) || (strcmp(i->name, name) == 0)) && (paircompare(request, request_pairs, i->check, NULL) == 0)) { RDEBUG2(" hints: Matched %s at %d", i->name, i->lineno); /* * Now add all attributes to the request list, * except PW_STRIP_USER_NAME and PW_FALL_THROUGH * and xlat them. */ add = paircopy(request->packet, i->reply); ft = fallthrough(add); pairdelete(&add, PW_STRIP_USER_NAME, 0, TAG_ANY); pairdelete(&add, PW_FALL_THROUGH, 0, TAG_ANY); radius_xlat_move(request, &request->packet->vps, &add); pairfree(&add); updated = 1; if (!ft) { break; } } } if (updated == 0) { return RLM_MODULE_NOOP; } return RLM_MODULE_UPDATED; }
/* * Merge a cached entry into a REQUEST. */ static void cache_merge(rlm_cache_t *inst, REQUEST *request, rlm_cache_entry_t *c) { VALUE_PAIR *vp; rad_assert(request != NULL); rad_assert(c != NULL); vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY); if (vp && (vp->vp_integer == 0)) { RDEBUG2("Told not to merge entry into request"); return; } if (c->control) { RDEBUG2("Merging cached control list:"); rdebug_pair_list(2, request, c->control); pairadd(&request->config_items, paircopy(request, c->control)); } if (c->packet && request->packet) { RDEBUG2("Merging cached request list:"); rdebug_pair_list(2, request, c->packet); pairadd(&request->packet->vps, paircopy(request->packet, c->packet)); } if (c->reply && request->reply) { RDEBUG2("Merging cached reply list:"); rdebug_pair_list(2, request, c->reply); pairadd(&request->reply->vps, paircopy(request->reply, c->reply)); } if (inst->stats) { vp = paircreate(request->packet, PW_CACHE_ENTRY_HITS, 0); rad_assert(vp != NULL); vp->vp_integer = c->hits; pairadd(&request->packet->vps, vp); } }
/* * Initialize the reply with the request. */ static int packet_authorize(void *instance, REQUEST *request) { VALUE_PAIR *vps; instance = instance; /* -Wunused */ vps = paircopy(request->packet->vps); pairadd(&(request->reply->vps), vps); return RLM_MODULE_UPDATED; }
/** Allocate a request packet * * This is done once per request with the same packet being sent to multiple realms. */ static rlm_rcode_t rlm_replicate_alloc(RADIUS_PACKET **out, REQUEST *request, pair_lists_t list, PW_CODE code) { rlm_rcode_t rcode = RLM_MODULE_OK; RADIUS_PACKET *packet = NULL; VALUE_PAIR *vp, **vps; *out = NULL; packet = rad_alloc(request, 1); if (!packet) { return RLM_MODULE_FAIL; } packet->code = code; /* * Figure out which list in the request were replicating */ vps = radius_list(request, list); if (!vps) { RWDEBUG("List '%s' doesn't exist for this packet", fr_int2str(pair_lists, list, "<INVALID>")); rcode = RLM_MODULE_INVALID; goto error; } /* * Don't assume the list actually contains any attributes. */ if (*vps) { packet->vps = paircopy(packet, *vps); if (!packet->vps) { rcode = RLM_MODULE_FAIL; goto error; } } /* * For CHAP, create the CHAP-Challenge if it doesn't exist. */ if ((code == PW_CODE_ACCESS_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) { vp = radius_paircreate(packet, &packet->vps, PW_CHAP_CHALLENGE, 0); pairmemcpy(vp, request->packet->vector, AUTH_VECTOR_LEN); } *out = packet; return rcode; error: talloc_free(packet); return rcode; }
/* * Merge a cached entry into a REQUEST. */ static void CC_HINT(nonnull) cache_merge(rlm_cache_t *inst, REQUEST *request, rlm_cache_entry_t *c) { VALUE_PAIR *vp; vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY); if (vp && (vp->vp_integer == 0)) { RDEBUG2("Told not to merge entry into request"); return; } RDEBUG2("Merging cache entry into request"); if (c->control) { rdebug_pair_list(L_DBG_LVL_2, request, c->control, "&control:"); radius_pairmove(request, &request->config_items, paircopy(request, c->control), false); } if (c->packet && request->packet) { rdebug_pair_list(L_DBG_LVL_2, request, c->packet, "&request:"); radius_pairmove(request, &request->packet->vps, paircopy(request->packet, c->packet), false); } if (c->reply && request->reply) { rdebug_pair_list(L_DBG_LVL_2, request, c->reply, "&reply:"); radius_pairmove(request, &request->reply->vps, paircopy(request->reply, c->reply), false); } if (inst->stats) { rad_assert(request->packet != NULL); vp = pairfind(request->packet->vps, PW_CACHE_ENTRY_HITS, 0, TAG_ANY); if (!vp) { vp = paircreate(request->packet, PW_CACHE_ENTRY_HITS, 0); rad_assert(vp != NULL); pairadd(&request->packet->vps, vp); } vp->vp_integer = c->hits; } }
/* * 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; } }
/* * Do authentication, by letting EAP-TLS do most of the work. */ static int CC_HINT(nonnull) mod_authenticate(void *type_arg, eap_handler_t *handler) { fr_tls_status_t status; tls_session_t *tls_session = (tls_session_t *) handler->opaque; REQUEST *request = handler->request; rlm_eap_tls_t *inst; inst = type_arg; RDEBUG2("Authenticate"); status = eaptls_process(handler); RDEBUG2("eaptls_process returned %d\n", status); switch (status) { /* * EAP-TLS handshake was successful, return an * EAP-TLS-Success packet here. * * If a virtual server was configured, check that * it accepts the certificates, too. */ case FR_TLS_SUCCESS: if (inst->virtual_server) { VALUE_PAIR *vp; REQUEST *fake; /* create a fake request */ fake = request_alloc_fake(request); rad_assert(!fake->packet->vps); fake->packet->vps = paircopy(fake->packet, request->packet->vps); /* set the virtual server to use */ if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) { fake->server = vp->vp_strvalue; } else { fake->server = inst->virtual_server; } RDEBUG("Processing EAP-TLS Certificate check:"); debug_pair_list(fake->packet->vps); RDEBUG("server %s {", fake->server); rad_virtual_server(fake); RDEBUG("} # server %s", fake->server); /* copy the reply vps back to our reply */ pairfilter(request->reply, &request->reply->vps, &fake->reply->vps, 0, 0, TAG_ANY); /* reject if virtual server didn't return accept */ if (fake->reply->code != PW_CODE_AUTHENTICATION_ACK) { RDEBUG2("Certificates were rejected by the virtual server"); request_free(&fake); eaptls_fail(handler, 0); return 0; } request_free(&fake); /* success */ } break; /* * The TLS code is still working on the TLS * exchange, and it's a valid TLS request. * do nothing. */ case FR_TLS_HANDLED: return 1; /* * Handshake is done, proceed with decoding tunneled * data. */ case FR_TLS_OK: RDEBUG2("Received unexpected tunneled data after successful handshake"); #ifndef NDEBUG if ((debug_flag > 2) && fr_log_fp) { unsigned int i; unsigned int data_len; unsigned char buffer[1024]; data_len = (tls_session->record_minus)(&tls_session->dirty_in, buffer, sizeof(buffer)); DEBUG(" Tunneled data (%u bytes)", data_len); for (i = 0; i < data_len; i++) { if ((i & 0x0f) == 0x00) fprintf(fr_log_fp, " %x: ", i); if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n"); fprintf(fr_log_fp, "%02x ", buffer[i]); } fprintf(fr_log_fp, "\n"); } #endif eaptls_fail(handler, 0); return 0; break; /* * Anything else: fail. * * Also, remove the session from the cache so that * the client can't re-use it. */ default: tls_fail(tls_session); return 0; } /* * Success: Automatically return MPPE keys. */ return eaptls_success(handler, 0); }
static int dhcp_process(REQUEST *request) { int rcode; VALUE_PAIR *vp; vp = pairfind(request->packet->vps, 53, DHCP_MAGIC_VENDOR); /* DHCP-Message-Type */ if (vp) { DICT_VALUE *dv = dict_valbyattr(53, DHCP_MAGIC_VENDOR, vp->vp_integer); DEBUG("Trying sub-section dhcp %s {...}", dv->name ? dv->name : "<unknown>"); rcode = module_post_auth(vp->vp_integer, request); } else { DEBUG("DHCP: Failed to find DHCP-Message-Type in packet!"); rcode = RLM_MODULE_FAIL; } /* * For messages from a client, look for Relay attribute, * and forward it if necessary. */ vp = NULL; if (request->packet->data[0] == 1) { vp = pairfind(request->config_items, 270, DHCP_MAGIC_VENDOR); } if (vp) { VALUE_PAIR *giaddr; /* * Find the original giaddr. * FIXME: Maybe look in the original packet? * * It's invalid to have giaddr=0 AND a relay option */ giaddr = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR); if (giaddr && (giaddr->vp_ipaddr == htonl(INADDR_ANY))) { if (pairfind(request->packet->vps, 82, DHCP_MAGIC_VENDOR)) { RDEBUG("DHCP: Received packet with giaddr = 0 and containing relay option: Discarding packet"); return 1; } } if (request->packet->data[3] > 10) { RDEBUG("DHCP: Number of hops is greater than 10: not relaying"); return 1; } /* * Forward a reply... */ pairfree(&request->reply->vps); request->reply->vps = paircopy(request->packet->vps); request->reply->code = request->packet->code; request->reply->id = request->packet->id; request->reply->src_ipaddr = request->packet->dst_ipaddr; request->reply->src_port = request->packet->dst_port; request->reply->dst_ipaddr.af = AF_INET; request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; /* * Don't change the destination port. It's the * server port. */ /* * Hop count goes up. */ vp = pairfind(request->reply->vps, 259, DHCP_MAGIC_VENDOR); if (vp) vp->vp_integer++; return 1; } /* * Responses from a server. Handle them differently. */ if (request->packet->data[0] == 2) { pairfree(&request->reply->vps); request->reply->vps = paircopy(request->packet->vps); request->reply->code = request->packet->code; request->reply->id = request->packet->id; /* * Delete any existing giaddr. If we received a * message from the server, then we're NOT the * server. So we must be the destination of the * giaddr field. */ pairdelete(&request->packet->vps, 266, DHCP_MAGIC_VENDOR); /* * Search for client IP address. */ vp = pairfind(request->packet->vps, 264, DHCP_MAGIC_VENDOR); if (!vp) { request->reply->code = 0; RDEBUG("DHCP: No YIAddr in the reply. Discarding packet"); return 1; } /* * FROM us, TO the client's IP, OUR port + 1. */ request->reply->src_ipaddr = request->packet->dst_ipaddr; request->reply->src_port = request->packet->dst_port; request->reply->dst_ipaddr.af = AF_INET; request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; request->reply->dst_port = request->packet->dst_port + 1; /* * Hop count goes down. */ vp = pairfind(request->reply->vps, 259, DHCP_MAGIC_VENDOR); if (vp && (vp->vp_integer > 0)) vp->vp_integer--; /* * FIXME: Keep original somewhere? If the * broadcast flags are set, use them here? */ return 1; } vp = pairfind(request->reply->vps, 53, DHCP_MAGIC_VENDOR); /* DHCP-Message-Type */ if (vp) { request->reply->code = vp->vp_integer; if ((request->reply->code != 0) && (request->reply->code < PW_DHCP_OFFSET)) { request->reply->code += PW_DHCP_OFFSET; } } else switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: if (request->packet->code == PW_DHCP_DISCOVER) { request->reply->code = PW_DHCP_OFFER; break; } else if (request->packet->code == PW_DHCP_REQUEST) { request->reply->code = PW_DHCP_ACK; break; } request->reply->code = PW_DHCP_NAK; break; default: case RLM_MODULE_REJECT: case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: if (request->packet->code == PW_DHCP_DISCOVER) { request->reply->code = 0; /* ignore the packet */ } else { request->reply->code = PW_DHCP_NAK; } break; case RLM_MODULE_HANDLED: break; } /* * Releases don't get replies. */ if (request->packet->code == PW_DHCP_RELEASE) { request->reply->code = 0; } return 1; }
/* * Process an EAP request */ fr_tls_status_t eaptls_process(eap_handler_t *handler) { tls_session_t *tls_session = (tls_session_t *) handler->opaque; EAPTLS_PACKET *tlspacket; fr_tls_status_t status; REQUEST *request = handler->request; if (!request) return FR_TLS_FAIL; RDEBUG2("processing EAP-TLS"); SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, request); if (handler->certs) pairadd(&request->packet->vps, paircopy(request->packet, handler->certs)); /* This case is when SSL generates Alert then we * send that alert to the client and then send the EAP-Failure */ status = eaptls_verify(handler); RDEBUG2("eaptls_verify returned %d\n", status); switch (status) { default: case FR_TLS_INVALID: case FR_TLS_FAIL: /* * Success means that we're done the initial * handshake. For TTLS, this means send stuff * back to the client, and the client sends us * more tunneled data. */ case FR_TLS_SUCCESS: goto done; /* * Normal TLS request, continue with the "get rest * of fragments" phase. */ case FR_TLS_REQUEST: eaptls_request(handler->eap_ds, tls_session); status = FR_TLS_HANDLED; goto done; /* * The handshake is done, and we're in the "tunnel * data" phase. */ case FR_TLS_OK: RDEBUG2("Done initial handshake"); /* * Get the rest of the fragments. */ case FR_TLS_FIRST_FRAGMENT: case FR_TLS_MORE_FRAGMENTS: case FR_TLS_LENGTH_INCLUDED: case FR_TLS_MORE_FRAGMENTS_WITH_LENGTH: break; } /* * Extract the TLS packet from the buffer. */ if ((tlspacket = eaptls_extract(request, handler->eap_ds, status)) == NULL) { status = FR_TLS_FAIL; goto done; } /* * Get the session struct from the handler * * update the dirty_in buffer * * NOTE: This buffer will contain partial data when M bit is set. * * CAUTION while reinitializing this buffer, it should be * reinitialized only when this M bit is NOT set. */ if (tlspacket->dlen != (tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) { talloc_free(tlspacket); RDEBUG("Exceeded maximum record size"); status =FR_TLS_FAIL; goto done; } /* * No longer needed. */ talloc_free(tlspacket); /* * SSL initalization is done. Return. * * The TLS data will be in the tls_session structure. */ if (SSL_is_init_finished(tls_session->ssl)) { /* * The initialization may be finished, but if * there more fragments coming, then send ACK, * and get the caller to continue the * conversation. */ if ((status == FR_TLS_MORE_FRAGMENTS) || (status == FR_TLS_MORE_FRAGMENTS_WITH_LENGTH) || (status == FR_TLS_FIRST_FRAGMENT)) { /* * Send the ACK. */ eaptls_send_ack(handler->eap_ds, tls_session->peap_flag); RDEBUG2("Init is done, but tunneled data is fragmented"); status = FR_TLS_HANDLED; goto done; } status = tls_application_data(tls_session, request); goto done; } /* * Continue the handshake. */ status = eaptls_operation(status, handler); done: SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, NULL); return status; }
/* * Relay the request to a remote server. * Returns: * * RLM_MODULE_FAIL: we don't reply, caller returns without replying * RLM_MODULE_NOOP: caller falls through to normal processing * RLM_MODULE_HANDLED : we reply, caller returns without replying */ int proxy_send(REQUEST *request) { int rcode; int pre_proxy_type = 0; VALUE_PAIR *realmpair; VALUE_PAIR *strippedname; VALUE_PAIR *delaypair; VALUE_PAIR *vp; REALM *realm; char *realmname; /* * Not authentication or accounting. Stop it. */ if ((request->packet->code != PW_AUTHENTICATION_REQUEST) && (request->packet->code != PW_ACCOUNTING_REQUEST)) { DEBUG2(" ERROR: Cannot proxy packets of type %d", request->packet->code); return RLM_MODULE_FAIL; } /* * The timestamp is used below to figure the * next_try. The request needs to "hang around" until * either the other server sends a reply or the retry * count has been exceeded. Until then, it should not * be eligible for the time-based cleanup. --Pac. */ realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM); if (!realmpair) { /* * Not proxying, so we can exit from the proxy * code. */ return RLM_MODULE_NOOP; } /* * If the server has already decided to reject the request, * then don't try to proxy it. */ if (request->reply->code == PW_AUTHENTICATION_REJECT) { DEBUG2("Cancelling proxy as request was already rejected"); return RLM_MODULE_REJECT; } if (((vp = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL) && (vp->lvalue == PW_AUTHTYPE_REJECT)) { DEBUG2("Cancelling proxy as request was already rejected"); return RLM_MODULE_REJECT; } /* * Length == 0 means it exists, but there's no realm. * Don't proxy it. */ if (realmpair->length == 0) { return RLM_MODULE_NOOP; } realmname = (char *)realmpair->strvalue; /* * Look for the realm, using the load balancing * version of realm find. */ realm = proxy_realm_ldb(request, realmname, (request->packet->code == PW_ACCOUNTING_REQUEST)); if (realm == NULL) { DEBUG2(" ERROR: Failed to find live home server for realm %s", realmname); return RLM_MODULE_FAIL; } /* * Remember that we sent the request to a Realm. */ pairadd(&request->packet->vps, pairmake("Realm", realm->realm, T_OP_EQ)); /* * Access-Request: look for LOCAL realm. * Accounting-Request: look for LOCAL realm. */ if (((request->packet->code == PW_AUTHENTICATION_REQUEST) && (realm->ipaddr == htonl(INADDR_NONE))) || ((request->packet->code == PW_ACCOUNTING_REQUEST) && (realm->acct_ipaddr == htonl(INADDR_NONE)))) { DEBUG2(" WARNING: Cancelling proxy to Realm %s, as the realm is local.", realm->realm); return RLM_MODULE_NOOP; } /* * Allocate the proxy packet, only if it wasn't already * allocated by a module. This check is mainly to support * the proxying of EAP-TTLS and EAP-PEAP tunneled requests. * * In those cases, the EAP module creates a "fake" * request, and recursively passes it through the * authentication stage of the server. The module then * checks if the request was supposed to be proxied, and * if so, creates a proxy packet from the TUNNELED request, * and not from the EAP request outside of the tunnel. * * The proxy then works like normal, except that the response * packet is "eaten" by the EAP module, and encapsulated into * an EAP packet. */ if (!request->proxy) { /* * Now build a new RADIUS_PACKET. * * FIXME: it could be that the id wraps around * too fast if we have a lot of requests, it * might be better to keep a seperate ID value * per remote server. * * OTOH the remote radius server should be smart * enough to compare _both_ ID and vector. * Right? */ if ((request->proxy = rad_alloc(TRUE)) == NULL) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } /* * We now massage the attributes to be proxied... */ /* * Copy the request, then look up name and * plain-text password in the copy. * * Note that the User-Name attribute is the * *original* as sent over by the client. The * Stripped-User-Name attribute is the one hacked * through the 'hints' file. */ request->proxy->vps = paircopy(request->packet->vps); } /* * Strip the name, if told to. * * Doing it here catches the case of proxied tunneled * requests. */ if (realm->striprealm == TRUE && (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) { /* * If there's a Stripped-User-Name attribute in * the request, then use THAT as the User-Name * for the proxied request, instead of the * original name. * * This is done by making a copy of the * Stripped-User-Name attribute, turning it into * a User-Name attribute, deleting the * Stripped-User-Name and User-Name attributes * from the vps list, and making the new * User-Name the head of the vps list. */ vp = pairfind(request->proxy->vps, PW_USER_NAME); if (!vp) { vp = paircreate(PW_USER_NAME, PW_TYPE_STRING); if (!vp) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } vp->next = request->proxy->vps; request->proxy->vps = vp; } memcpy(vp->strvalue, strippedname->strvalue, sizeof(vp->strvalue)); vp->length = strippedname->length; /* * Do NOT delete Stripped-User-Name. */ } /* * If there is no PW_CHAP_CHALLENGE attribute but * there is a PW_CHAP_PASSWORD we need to add it * since we can't use the request authenticator * anymore - we changed it. */ if (pairfind(request->proxy->vps, PW_CHAP_PASSWORD) && pairfind(request->proxy->vps, PW_CHAP_CHALLENGE) == NULL) { vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING); if (!vp) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } vp->length = AUTH_VECTOR_LEN; memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN); pairadd(&(request->proxy->vps), vp); } request->proxy->code = request->packet->code; if (request->packet->code == PW_AUTHENTICATION_REQUEST) { request->proxy->dst_port = realm->auth_port; request->proxy->dst_ipaddr = realm->ipaddr; } else if (request->packet->code == PW_ACCOUNTING_REQUEST) { request->proxy->dst_port = realm->acct_port; request->proxy->dst_ipaddr = realm->acct_ipaddr; } /* * Add PROXY_STATE attribute, before pre-proxy stage, * so the pre-proxy modules have access to it. * * Note that, at this point, the proxied request HAS NOT * been assigned a RADIUS Id. */ proxy_addinfo(request); /* * Set up for sending the request. */ memcpy(request->proxysecret, realm->secret, sizeof(request->proxysecret)); request->proxy_try_count = mainconfig.proxy_retry_count - 1; request->proxy_next_try = request->timestamp + mainconfig.proxy_retry_delay; delaypair = pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME); request->proxy->timestamp = request->timestamp - (delaypair ? delaypair->lvalue : 0); /* * Do pre-proxying */ vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE); if (vp) { DEBUG2(" Found Pre-Proxy-Type %s", vp->strvalue); pre_proxy_type = vp->lvalue; } rcode = module_pre_proxy(pre_proxy_type, request); /* * Do NOT free request->proxy->vps, the pairs are needed * for the retries! --Pac. */ /* * Delay sending the proxy packet until after we've * done the work above, playing with the request. * * After this point, it becomes dangerous to play * with the request data structure, as the reply MAY * come in and get processed before we're done with it here. * * Only proxy the packet if the pre-proxy code succeeded. */ if ((rcode == RLM_MODULE_OK) || (rcode == RLM_MODULE_NOOP) || (rcode == RLM_MODULE_UPDATED)) { request->options |= RAD_REQUEST_OPTION_PROXIED; /* * IF it's a fake request, don't send the proxy * packet. The outer tunnel session will take * care of doing that. */ if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0) { /* * Add the proxied request to the * list of outstanding proxied * requests, BEFORE we send it, so * we have fewer problems with race * conditions when the responses come * back very quickly. */ if (!rl_add_proxy(request)) { DEBUG("ERROR: Failed to proxy request %d", request->number); return RLM_MODULE_FAIL; /* caller doesn't reply */ } rad_send(request->proxy, NULL, (char *)request->proxysecret); } rcode = RLM_MODULE_HANDLED; /* caller doesn't reply */ } else { rcode = RLM_MODULE_FAIL; /* caller doesn't reply */ } return rcode; }
/* * Write accounting information to this modules database. */ static int replicate_packet(void *instance, REQUEST *request) { int rcode = RLM_MODULE_NOOP; VALUE_PAIR *vp, *last; home_server *home; REALM *realm; home_pool_t *pool; RADIUS_PACKET *packet = NULL; instance = instance; /* -Wunused */ last = request->config_items; /* * Send as many packets as necessary to different * destinations. */ while (1) { vp = pairfind(last, PW_REPLICATE_TO_REALM, 0); if (!vp) break; last = vp->next; realm = realm_find2(vp->vp_strvalue); if (!realm) { RDEBUG2("ERROR: Cannot Replicate to unknown realm %s", realm); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: RDEBUG2("ERROR: Cannot replicate unknown packet code %d", request->packet->code); cleanup(packet); return RLM_MODULE_FAIL; case PW_AUTHENTICATION_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_COA_REQUEST: case PW_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RDEBUG2(" WARNING: Cancelling replication to Realm %s, as the realm is local.", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { RDEBUG2("ERROR: Failed to find live home server for realm %s", realm->name); continue; } if (!packet) { packet = rad_alloc(1); if (!packet) return RLM_MODULE_FAIL; packet->sockfd = -1; packet->code = request->packet->code; packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { RDEBUG("ERROR: Failed opening socket: %s", fr_strerror()); cleanup(packet); return RLM_MODULE_FAIL; } packet->vps = paircopy(request->packet->vps); if (!packet->vps) { RDEBUG("ERROR: Out of memory!"); cleanup(packet); return RLM_MODULE_FAIL; } /* * For CHAP, create the CHAP-Challenge if * it doesn't exist. */ if ((request->packet->code == PW_AUTHENTICATION_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0) == NULL)) { vp = radius_paircreate(request, &packet->vps, PW_CHAP_CHALLENGE, 0, PW_TYPE_OCTETS); vp->length = AUTH_VECTOR_LEN; memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN); } } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; free(packet->data); packet->data = NULL; packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating packet to Realm %s", realm->name); if (rad_send(packet, NULL, home->secret) < 0) { RDEBUG("ERROR: Failed replicating packet: %s", fr_strerror()); cleanup(packet); return RLM_MODULE_FAIL; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } cleanup(packet); return rcode; }
/* * 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; } }
/* * Relay the request to a remote server. * Returns: * * RLM_MODULE_FAIL: we don't reply, caller returns without replying * RLM_MODULE_NOOP: caller falls through to normal processing * RLM_MODULE_HANDLED : we reply, caller returns without replying */ int proxy_send(REQUEST *request) { int rcode; VALUE_PAIR *proxypair; VALUE_PAIR *replicatepair; VALUE_PAIR *realmpair; VALUE_PAIR *namepair; VALUE_PAIR *strippednamepair; VALUE_PAIR *delaypair; VALUE_PAIR *vp, *vps; REALM *realm; char *realmname; int replicating; /* * Not authentication or accounting. Stop it. */ if ((request->packet->code != PW_AUTHENTICATION_REQUEST) && (request->packet->code != PW_ACCOUNTING_REQUEST)) { return RLM_MODULE_FAIL; } /* * The timestamp is used below to figure the * next_try. The request needs to "hang around" until * either the other server sends a reply or the retry * count has been exceeded. Until then, it should not * be eligible for the time-based cleanup. --Pac. */ /* Look for proxy/replicate signs */ /* FIXME - What to do if multiple Proxy-To/Replicate-To attrs are * set... Log an error? Actually replicate to multiple places? That * would be cool. For now though, I'll just take the first one and * ignore the rest. */ proxypair = pairfind(request->config_items, PW_PROXY_TO_REALM); replicatepair = pairfind(request->config_items, PW_REPLICATE_TO_REALM); if (proxypair) { realmpair = proxypair; replicating = 0; } else if (replicatepair) { realmpair = replicatepair; replicating = 1; } else { /* * Neither proxy or replicate attributes are set, * so we can exit from the proxy code. */ return RLM_MODULE_NOOP; } /* * If the server has already decided to reject the request, * then don't try to proxy it. */ if (request->reply->code == PW_AUTHENTICATION_REJECT) { DEBUG2("Cancelling proxy as request was already rejected"); return RLM_MODULE_REJECT; } if (((vp = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL) && (vp->lvalue == PW_AUTHTYPE_REJECT)) { DEBUG2("Cancelling proxy as request was already rejected"); return RLM_MODULE_REJECT; } /* * Length == 0 means it exists, but there's no realm. * Don't proxy it. */ if (realmpair->length == 0) { return RLM_MODULE_NOOP; } realmname = (char *)realmpair->strvalue; /* * Look for the realm, using the load balancing * version of realm find. */ realm = proxy_realm_ldb(request, realmname, (request->packet->code == PW_ACCOUNTING_REQUEST)); if (realm == NULL) { return RLM_MODULE_FAIL; } /* * Remember that we sent the request to a Realm. */ pairadd(&request->packet->vps, pairmake("Realm", realm->realm, T_OP_EQ)); /* * Access-Request: look for LOCAL realm. * Accounting-Request: look for LOCAL realm. */ if (((request->packet->code == PW_AUTHENTICATION_REQUEST) && (realm->ipaddr == htonl(INADDR_NONE))) || ((request->packet->code == PW_ACCOUNTING_REQUEST) && (realm->acct_ipaddr == htonl(INADDR_NONE)))) { return RLM_MODULE_NOOP; } /* * Copy the request, then look up * name and plain-text password in the copy. * * Note that the User-Name attribute is the *original* * as sent over by the client. The Stripped-User-Name * attribute is the one hacked through the 'hints' file. */ vps = paircopy(request->packet->vps); namepair = pairfind(vps, PW_USER_NAME); strippednamepair = pairfind(vps, PW_STRIPPED_USER_NAME); /* * If there's a Stripped-User-Name attribute in the * request, then use THAT as the User-Name for the * proxied request, instead of the original name. * * This is done by making a copy of the Stripped-User-Name * attribute, turning it into a User-Name attribute, * deleting the Stripped-User-Name and User-Name attributes * from the vps list, and making the new User-Name * the head of the vps list. */ if (strippednamepair) { vp = paircreate(PW_USER_NAME, PW_TYPE_STRING); if (!vp) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } memcpy(vp->strvalue, strippednamepair->strvalue, sizeof(vp->strvalue)); vp->length = strippednamepair->length; pairdelete(&vps, PW_USER_NAME); pairdelete(&vps, PW_STRIPPED_USER_NAME); vp->next = vps; namepair = vp; vps = vp; } /* * Now build a new RADIUS_PACKET and send it. * * FIXME: it could be that the id wraps around too fast if * we have a lot of requests, it might be better to keep * a seperate ID value per remote server. * * OTOH the remote radius server should be smart enough to * compare _both_ ID and vector. Right ? */ if ((request->proxy = rad_alloc(TRUE)) == NULL) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } /* * Proxied requests get sent out the proxy FD ONLY. */ request->proxy->sockfd = proxyfd; request->proxy->code = request->packet->code; if (request->packet->code == PW_AUTHENTICATION_REQUEST) { request->proxy->dst_port = realm->auth_port; request->proxy->dst_ipaddr = realm->ipaddr; } else if (request->packet->code == PW_ACCOUNTING_REQUEST) { request->proxy->dst_port = realm->acct_port; request->proxy->dst_ipaddr = realm->acct_ipaddr; } rad_assert(request->proxy->vps == NULL); request->proxy->vps = vps; /* * Add the request to the list of outstanding requests. * Note that request->proxy->id is a 16 bits value, * while rad_send sends only the 8 least significant * bits of that same value. */ request->proxy->id = (proxy_id++) & 0xff; proxy_id &= 0xffff; /* * Add PROXY_STATE attribute. */ proxy_addinfo(request); /* * If there is no PW_CHAP_CHALLENGE attribute but there * is a PW_CHAP_PASSWORD we need to add it since we can't * use the request authenticator anymore - we changed it. */ if (pairfind(vps, PW_CHAP_PASSWORD) && pairfind(vps, PW_CHAP_CHALLENGE) == NULL) { vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING); if (!vp) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } vp->length = AUTH_VECTOR_LEN; memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN); pairadd(&vps, vp); } /* * Set up for sending the request. */ memcpy(request->proxysecret, realm->secret, sizeof(request->proxysecret)); request->proxy_is_replicate = replicating; request->proxy_try_count = mainconfig.proxy_retry_count - 1; request->proxy_next_try = request->timestamp + mainconfig.proxy_retry_delay; delaypair = pairfind(vps, PW_ACCT_DELAY_TIME); request->proxy->timestamp = request->timestamp - (delaypair ? delaypair->lvalue : 0); /* * Do pre-proxying */ rcode = module_pre_proxy(request); /* * Do NOT free proxy->vps, the pairs are needed for the * retries! --Pac. */ /* * Delay sending the proxy packet until after we've * done the work above, playing with the request. * * After this point, it becomes dangerous to play * with the request data structure, as the reply MAY * come in and get processed before we're done with it here. * * Only proxy the packet if the pre-proxy code succeeded. */ if ((rcode == RLM_MODULE_OK) || (rcode == RLM_MODULE_NOOP) || (rcode == RLM_MODULE_UPDATED)) { rad_send(request->proxy, NULL, (char *)request->proxysecret); if (!replicating) { rcode = RLM_MODULE_HANDLED; /* caller doesn't reply */ } else { rcode = RLM_MODULE_NOOP; /* caller replies */ } } else { rcode = RLM_MODULE_FAIL; /* caller doesn't reply */ } return rcode; }
/** Copy packet to multiple servers * * Create a duplicate of the packet and send it to a list of realms * defined by the presence of the Replicate-To-Realm VP in the control * list of the current request. * * This is pretty hacky and is 100% fire and forget. If you're looking * to forward authentication requests to multiple realms and process * the responses, this function will not allow you to do that. * * @param[in] instance of this module. * @param[in] request The current request. * @param[in] list of attributes to copy to the duplicate packet. * @param[in] code to write into the code field of the duplicate packet. * @return RCODE fail on error, invalid if list does not exist, noop if no * replications succeeded, else ok. */ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, unsigned int code) { int rcode = RLM_MODULE_NOOP; VALUE_PAIR *vp, **vps, *last; home_server *home; REALM *realm; home_pool_t *pool; RADIUS_PACKET *packet = NULL; last = request->config_items; /* * Send as many packets as necessary to different * destinations. */ while (1) { vp = pairfind(last, PW_REPLICATE_TO_REALM, 0, TAG_ANY); if (!vp) break; last = vp->next; realm = realm_find2(vp->vp_strvalue); if (!realm) { RDEBUG2E("Cannot Replicate to unknown realm %s", realm); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: RDEBUG2E("Cannot replicate unknown packet code %d", request->packet->code); cleanup(packet); return RLM_MODULE_FAIL; case PW_AUTHENTICATION_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_COA_REQUEST: case PW_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RDEBUG2W("Cancelling replication to Realm %s, as the realm is local.", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { RDEBUG2E("Failed to find live home server for realm %s", realm->name); continue; } /* * For replication to multiple servers we re-use the packet * we built here. */ if (!packet) { packet = rad_alloc(NULL, 1); if (!packet) return RLM_MODULE_FAIL; packet->sockfd = -1; packet->code = code; packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { RDEBUGE("Failed opening socket: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } vps = radius_list(request, list); if (!vps) { RDEBUGW("List '%s' doesn't exist for " "this packet", fr_int2str(pair_lists, list, "?unknown?")); rcode = RLM_MODULE_INVALID; goto done; } /* * Don't assume the list actually contains any * attributes. */ if (*vps) { packet->vps = paircopy(packet, *vps); if (!packet->vps) { rcode = RLM_MODULE_FAIL; goto done; } } /* * For CHAP, create the CHAP-Challenge if * it doesn't exist. */ if ((code == PW_AUTHENTICATION_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) { vp = radius_paircreate(request, &packet->vps, PW_CHAP_CHALLENGE, 0); vp->length = AUTH_VECTOR_LEN; memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN); } } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; free(packet->data); packet->data = NULL; packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "¿unknown?"),realm->name); if (rad_send(packet, NULL, home->secret) < 0) { RDEBUGE("Failed replicating packet: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } done: cleanup(packet); return rcode; }
/* * Process an EAP request */ eaptls_status_t eaptls_process(EAP_HANDLER *handler) { tls_session_t *tls_session = (tls_session_t *) handler->opaque; EAPTLS_PACKET *tlspacket; eaptls_status_t status; REQUEST *request = handler->request; RDEBUG2("processing EAP-TLS"); if (handler->certs) pairadd(&request->packet->vps, paircopy(handler->certs)); /* This case is when SSL generates Alert then we * send that alert to the client and then send the EAP-Failure */ status = eaptls_verify(handler); RDEBUG2("eaptls_verify returned %d\n", status); switch (status) { default: case EAPTLS_INVALID: case EAPTLS_FAIL: /* * Success means that we're done the initial * handshake. For TTLS, this means send stuff * back to the client, and the client sends us * more tunneled data. */ case EAPTLS_SUCCESS: return status; break; /* * Normal TLS request, continue with the "get rest * of fragments" phase. */ case EAPTLS_REQUEST: eaptls_request(handler->eap_ds, tls_session); return EAPTLS_HANDLED; break; /* * The handshake is done, and we're in the "tunnel * data" phase. */ case EAPTLS_OK: RDEBUG2("Done initial handshake"); /* * Get the rest of the fragments. */ case EAPTLS_FIRST_FRAGMENT: case EAPTLS_MORE_FRAGMENTS: case EAPTLS_LENGTH_INCLUDED: case EAPTLS_MORE_FRAGMENTS_WITH_LENGTH: break; } /* * Extract the TLS packet from the buffer. */ if ((tlspacket = eaptls_extract(request, handler->eap_ds, status)) == NULL) return EAPTLS_FAIL; /* * Get the session struct from the handler * * update the dirty_in buffer * * NOTE: This buffer will contain partial data when M bit is set. * * CAUTION while reinitializing this buffer, it should be * reinitialized only when this M bit is NOT set. */ if (tlspacket->dlen != (tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) { eaptls_free(&tlspacket); RDEBUG("Exceeded maximum record size"); return EAPTLS_FAIL; } /* * No longer needed. */ eaptls_free(&tlspacket); /* * SSL initalization is done. Return. * * The TLS data will be in the tls_session structure. */ if (SSL_is_init_finished(tls_session->ssl)) { int err; /* * The initialization may be finished, but if * there more fragments coming, then send ACK, * and get the caller to continue the * conversation. */ if ((status == EAPTLS_MORE_FRAGMENTS) || (status == EAPTLS_MORE_FRAGMENTS_WITH_LENGTH) || (status == EAPTLS_FIRST_FRAGMENT)) { /* * Send the ACK. */ eaptls_send_ack(handler->eap_ds, tls_session->peap_flag); RDEBUG2("Init is done, but tunneled data is fragmented"); return EAPTLS_HANDLED; } /* * Decrypt the complete record. */ BIO_write(tls_session->into_ssl, tls_session->dirty_in.data, tls_session->dirty_in.used); /* * Clear the dirty buffer now that we are done with it * and init the clean_out buffer to store decrypted data */ (tls_session->record_init)(&tls_session->dirty_in); (tls_session->record_init)(&tls_session->clean_out); /* * Read (and decrypt) the tunneled data from the * SSL session, and put it into the decrypted * data buffer. */ err = SSL_read(tls_session->ssl, tls_session->clean_out.data, sizeof(tls_session->clean_out.data)); if (err < 0) { RDEBUG("SSL_read Error"); switch (SSL_get_error(tls_session->ssl, err)) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: RDEBUG("Error in fragmentation logic"); break; default: /* * FIXME: Call int_ssl_check? */ break; } return EAPTLS_FAIL; } if (err == 0) { RDEBUG("WARNING: No data inside of the tunnel."); } /* * Passed all checks, successfully decrypted data */ tls_session->clean_out.used = err; return EAPTLS_OK; } /* * Continue the handshake. */ return eaptls_operation(status, handler); }
/* * 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; }
/** 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; }
/* * Do post-proxy processing, * 0 = fail * 1 = OK. * * Called from rlm_eap.c, eap_postproxy(). */ static int mschap_postproxy(EAP_HANDLER *handler, void *tunnel_data) { VALUE_PAIR *response = NULL; mschapv2_opaque_t *data; data = (mschapv2_opaque_t *) handler->opaque; rad_assert(data != NULL); tunnel_data = tunnel_data; /* -Wunused */ DEBUG2(" rlm_eap_mschapv2: Passing reply from proxy back into the tunnel %p %d.", handler->request, handler->request->reply->code); /* * There is only a limited number of possibilities. */ switch (handler->request->reply->code) { case PW_AUTHENTICATION_ACK: DEBUG(" rlm_eap_mschapv2: Proxied authentication succeeded."); /* * Move the attribute, so it doesn't go into * the reply. */ pairmove2(&response, &handler->request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY); break; default: case PW_AUTHENTICATION_REJECT: DEBUG(" rlm_eap_mschapv2: Proxied authentication did not succeed."); return 0; } /* * No response, die. */ if (!response) { radlog(L_ERR, "rlm_eap_mschapv2: Proxied reply contained no MS-CHAPv2-Success or MS-CHAP-Error"); return 0; } /* * Done doing EAP proxy stuff. */ handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; eapmschapv2_compose(handler, response); data->code = PW_EAP_MSCHAPV2_SUCCESS; /* * Delete MPPE keys & encryption policy * * FIXME: Use intelligent names... */ fix_mppe_keys(handler, data); /* * save any other attributes for re-use in the final * access-accept e.g. vlan, etc. This lets the PEAP * use_tunneled_reply code work */ data->reply = paircopy(handler->request->reply->vps); /* * And we need to challenge the user, not ack/reject them, * so we re-write the ACK to a challenge. Yuck. */ handler->request->reply->code = PW_ACCESS_CHALLENGE; pairfree(&response); 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(TALLOC_CTX *ctx, VALUE_PAIR *vps, HV *rad_hv) { VALUE_PAIR *head, *sublist; AV *av; char const *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. This is a horrible hack to * get around various other stupidity. */ head = paircopy(ctx, vps); while (head) { vp_cursor_t cursor; /* * Tagged attributes are added to the hash with name * <attribute>:<tag>, others just use the normal attribute * name as the key. */ if (head->da->flags.has_tag && (head->tag != 0)) { snprintf(namebuf, sizeof(namebuf), "%s:%d", head->da->name, head->tag); name = namebuf; } else { name = head->da->name; } /* * Create a new list with all the attributes like this one * which are in the same tag group. */ sublist = NULL; pairfilter(ctx, &sublist, &head, head->da->attr, head->da->vendor, head->tag); fr_cursor_init(&cursor, &sublist); /* * Attribute has multiple values */ if (fr_cursor_next(&cursor)) { VALUE_PAIR *vp; av = newAV(); for (vp = fr_cursor_first(&cursor); vp; vp = fr_cursor_next(&cursor)) { len = vp_prints_value(buffer, sizeof(buffer), vp, 0); 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), sublist, 0); (void)hv_store(rad_hv, name, strlen(name), newSVpv(buffer, len), 0); } pairfree(&sublist); } rad_assert(!head); }
/** 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; }
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; }
/* * Common code called by everything below. */ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *filename, fr_hash_table_t *ht, VALUE_PAIR *request_pairs, VALUE_PAIR **reply_pairs) { char const *name, *match; VALUE_PAIR *check_tmp; VALUE_PAIR *reply_tmp; const PAIR_LIST *user_pl, *default_pl; int found = 0; PAIR_LIST my_pl; char buffer[256]; if (!inst->key) { VALUE_PAIR *namepair; namepair = request->username; name = namepair ? namepair->vp_strvalue : "NONE"; } else { int len; len = radius_xlat(buffer, sizeof(buffer), request, inst->key, NULL, NULL); if (len < 0) { return RLM_MODULE_FAIL; } name = len ? buffer : "NONE"; } if (!ht) return RLM_MODULE_NOOP; my_pl.name = name; user_pl = fr_hash_table_finddata(ht, &my_pl); my_pl.name = "DEFAULT"; default_pl = fr_hash_table_finddata(ht, &my_pl); /* * Find the entry for the user. */ while (user_pl || default_pl) { const PAIR_LIST *pl; if (!default_pl && user_pl) { pl = user_pl; match = name; user_pl = user_pl->next; } else if (!user_pl && default_pl) { pl = default_pl; match = "DEFAULT"; default_pl = default_pl->next; } else if (user_pl->order < default_pl->order) { pl = user_pl; match = name; user_pl = user_pl->next; } else { pl = default_pl; match = "DEFAULT"; default_pl = default_pl->next; } if (paircompare(request, request_pairs, pl->check, reply_pairs) == 0) { RDEBUG2("%s: Matched entry %s at line %d", filename, match, pl->lineno); found = 1; check_tmp = paircopy(request, pl->check); /* ctx may be reply or proxy */ reply_tmp = paircopy(request, pl->reply); radius_xlat_move(request, reply_pairs, &reply_tmp); pairmove(request, &request->config_items, &check_tmp); pairfree(&reply_tmp); pairfree(&check_tmp); /* * Fallthrough? */ if (!fallthrough(pl->reply)) break; } } /* * Remove server internal parameters. */ pairdelete(reply_pairs, PW_FALL_THROUGH, 0, TAG_ANY); /* * See if we succeeded. */ if (!found) return RLM_MODULE_NOOP; /* on to the next module */ return RLM_MODULE_OK; }
/** Copy pairs matching a VPT in the current request * * @param ctx to allocate new VALUE_PAIRs under. * @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 tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR **vps, *vp; REQUEST *current = request; vp_cursor_t from, to; rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST)); if (out) *out = NULL; if (radius_request(¤t, vpt->tmpl_request) < 0) { return -3; } vps = radius_list(request, vpt->tmpl_list); if (!vps) { return -2; } switch (vpt->type) { /* * May not be found, but it *is* a known name. */ case TMPL_TYPE_ATTR: { int num; (void) fr_cursor_init(&to, out); (void) fr_cursor_init(&from, vps); vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag); if (!vp) return -1; switch (vpt->tmpl_num) { /* Copy all pairs of this type (and tag) */ case NUM_ALL: do { VERIFY_VP(vp); vp = paircopyvp(ctx, vp); if (!vp) { pairfree(out); return -4; } fr_cursor_insert(&to, vp); } while ((vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag))); break; /* Specific attribute number */ default: for (num = vpt->tmpl_num; num && vp; num--, vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag)) { VERIFY_VP(vp); } if (!vp) return -1; /* FALL-THROUGH */ /* Just copy the first pair */ case NUM_ANY: vp = paircopyvp(ctx, vp); if (!vp) { pairfree(out); return -4; } fr_cursor_insert(&to, vp); } } break; case TMPL_TYPE_LIST: vp = paircopy(ctx, *vps); if (!vp) return 0; fr_cursor_merge(&to, vp); break; default: rad_assert(0); } return 0; }