static int eap_sim_sendstart(eap_handler_t *handler) { VALUE_PAIR **vps, *newvp; uint16_t words[3]; eap_sim_state_t *ess; RADIUS_PACKET *packet; uint8_t *p; rad_assert(handler->request != NULL); rad_assert(handler->request->reply); ess = (eap_sim_state_t *)handler->opaque; /* these are the outgoing attributes */ packet = handler->request->reply; vps = &packet->vps; rad_assert(vps != NULL); /* * Add appropriate TLVs for the EAP things we wish to send. */ /* the version list. We support only version 1. */ words[0] = htons(sizeof(words[1])); words[1] = htons(EAP_SIM_VERSION); words[2] = 0; newvp = paircreate(packet, PW_EAP_SIM_VERSION_LIST, 0); pairmemcpy(newvp, (uint8_t const *) words, sizeof(words)); pairadd(vps, newvp); /* set the EAP_ID - new value */ newvp = paircreate(packet, PW_EAP_ID, 0); newvp->vp_integer = ess->sim_id++; pairreplace(vps, newvp); /* record it in the ess */ ess->keys.versionlistlen = 2; memcpy(ess->keys.versionlist, words + 1, ess->keys.versionlistlen); /* the ANY_ID attribute. We do not support re-auth or pseudonym */ newvp = paircreate(packet, PW_EAP_SIM_FULLAUTH_ID_REQ, 0); newvp->length = 2; newvp->vp_octets = p = talloc_array(newvp, uint8_t, 2); p[0] = 0; p[0] = 1; pairadd(vps, newvp); /* the SUBTYPE, set to start. */ newvp = paircreate(packet, PW_EAP_SIM_SUBTYPE, 0); newvp->vp_integer = EAPSIM_START; pairreplace(vps, newvp); return 1; }
static int eap_sim_sendstart(EAP_HANDLER *handler) { VALUE_PAIR **vps, *newvp; uint16_t *words; struct eap_sim_server_state *ess; rad_assert(handler->request != NULL); rad_assert(handler->request->reply); ess = (struct eap_sim_server_state *)handler->opaque; /* these are the outgoing attributes */ vps = &handler->request->reply->vps; rad_assert(vps != NULL); /* * add appropriate TLVs for the EAP things we wish to send. */ /* the version list. We support only version 1. */ newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST, 0, PW_TYPE_OCTETS); words = (uint16_t *)newvp->vp_strvalue; newvp->length = 3*sizeof(uint16_t); words[0] = htons(1*sizeof(uint16_t)); words[1] = htons(EAP_SIM_VERSION); words[2] = 0; pairadd(vps, newvp); /* set the EAP_ID - new value */ newvp = paircreate(ATTRIBUTE_EAP_ID, 0, PW_TYPE_INTEGER); newvp->vp_integer = ess->sim_id++; pairreplace(vps, newvp); /* record it in the ess */ ess->keys.versionlistlen = 2; memcpy(ess->keys.versionlist, words+1, ess->keys.versionlistlen); /* the ANY_ID attribute. We do not support re-auth or pseudonym */ newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ, 0, PW_TYPE_OCTETS); newvp->length = 2; newvp->vp_strvalue[0]=0; newvp->vp_strvalue[0]=1; pairadd(vps, newvp); /* the SUBTYPE, set to start. */ newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, 0, PW_TYPE_INTEGER); newvp->vp_integer = eapsim_start; pairreplace(vps, newvp); return 1; }
/** Add a module failure message VALUE_PAIR to the request */ void module_failure_msg(REQUEST *request, char const *fmt, ...) { va_list ap; char *p; VALUE_PAIR *vp; if (!fmt || !request->packet) { va_start(ap, fmt); va_end(ap); return; } va_start(ap, fmt); vp = paircreate(request->packet, PW_MODULE_FAILURE_MESSAGE, 0); if (!vp) { va_end(ap); return; } p = talloc_vasprintf(vp, fmt, ap); if (request->module && *request->module) { pairsprintf(vp, "%s: %s", request->module, p); } else { pairsprintf(vp, "%s", p); } talloc_free(p); pairadd(&request->packet->vps, vp); }
VALUE_PAIR *eap_packet2vp(RADIUS_PACKET *packet, const eap_packet_raw_t *eap) { int total, size; const uint8_t *ptr; VALUE_PAIR *head = NULL; VALUE_PAIR **tail = &head; VALUE_PAIR *vp; total = eap->length[0] * 256 + eap->length[1]; ptr = (const uint8_t *) eap; do { size = total; if (size > 253) size = 253; vp = paircreate(packet, PW_EAP_MESSAGE, 0); if (!vp) { pairfree(&head); return NULL; } memcpy(vp->vp_octets, ptr, size); vp->length = size; *tail = vp; tail = &(vp->next); ptr += size; total -= size; } while (total > 0); return head; }
VALUE_PAIR *eap_packet2vp(const eap_packet_t *packet) { int total, size; const uint8_t *ptr; VALUE_PAIR *head = NULL; VALUE_PAIR **tail = &head; VALUE_PAIR *vp; total = packet->length[0] * 256 + packet->length[1]; ptr = (const uint8_t *) packet; do { size = total; if (size > 253) size = 253; vp = paircreate(PW_EAP_MESSAGE, PW_TYPE_OCTETS); if (!vp) { pairfree(&head); return NULL; } memcpy(vp->vp_octets, ptr, size); vp->length = size; *tail = vp; tail = &(vp->next); ptr += size; total -= size; } while (total > 0); return head; }
/* * 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); } }
static int eap_req2vp(EAP_HANDLER *handler) { int encoded, total, size; const uint8_t *ptr; VALUE_PAIR *head = NULL; VALUE_PAIR **tail = &head; VALUE_PAIR *vp; ptr = wpabuf_head(handler->server_ctx.eap_if->eapReqData); encoded = total = wpabuf_len(handler->server_ctx.eap_if->eapReqData); do { size = total; if (size > 253) size = 253; vp = paircreate(PW_EAP_MESSAGE, PW_TYPE_OCTETS); if (!vp) { pairfree(&head); return -1; } memcpy(vp->vp_octets, ptr, size); vp->length = size; *tail = vp; tail = &(vp->next); ptr += size; total -= size; } while (total > 0); pairdelete(&handler->request->reply->vps, PW_EAP_MESSAGE, TAG_ANY); pairadd(&handler->request->reply->vps, head); return encoded; }
/* * compose EAP reply packet in EAP-Message attr of RADIUS. If * EAP exceeds 253, frame it in multiple EAP-Message attrs. */ int eap_basic_compose(RADIUS_PACKET *packet, eap_packet_t *reply) { VALUE_PAIR *vp; eap_packet_raw_t *eap_packet; int rcode; if (eap_wireformat(reply) == EAP_INVALID) { return RLM_MODULE_INVALID; } eap_packet = (eap_packet_raw_t *)reply->packet; pairdelete(&(packet->vps), PW_EAP_MESSAGE, 0, TAG_ANY); vp = eap_packet2vp(packet, eap_packet); if (!vp) return RLM_MODULE_INVALID; pairadd(&(packet->vps), vp); /* * EAP-Message is always associated with * Message-Authenticator but not vice-versa. * * Don't add a Message-Authenticator if it's already * there. */ vp = pairfind(packet->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY); if (!vp) { vp = paircreate(packet, PW_MESSAGE_AUTHENTICATOR, 0); vp->vp_length = AUTH_VECTOR_LEN; vp->vp_octets = talloc_zero_array(vp, uint8_t, vp->vp_length); pairadd(&(packet->vps), vp); } /* Set request reply code, but only if it's not already set. */ rcode = RLM_MODULE_OK; if (!packet->code) switch (reply->code) { case PW_EAP_RESPONSE: case PW_EAP_SUCCESS: packet->code = PW_CODE_ACCESS_ACCEPT; rcode = RLM_MODULE_HANDLED; break; case PW_EAP_FAILURE: packet->code = PW_CODE_ACCESS_REJECT; rcode = RLM_MODULE_REJECT; break; case PW_EAP_REQUEST: packet->code = PW_CODE_ACCESS_CHALLENGE; rcode = RLM_MODULE_HANDLED; break; default: /* Should never enter here */ ERROR("rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code); packet->code = PW_CODE_ACCESS_REJECT; break; } return rcode; }
static rlm_rcode_t mod_post_auth(UNUSED void * instance, REQUEST *request) { #ifdef WITH_DHCP int rcode; VALUE_PAIR *vp; vp = pairfind(request->packet->vps, 43, DHCP_MAGIC_VENDOR, TAG_ANY); if (vp) { /* * vendor-specific options contain * * vendor opt 220/0xdc - SoH payload, or null byte to probe, or string * "NAP" to indicate server-side support for SoH in OFFERs * * vendor opt 222/0xde - SoH correlation ID as utf-16 string, yuck... */ uint8_t vopt, vlen, *data; data = vp->vp_octets; while (data < vp->vp_octets + vp->length) { vopt = *data++; vlen = *data++; switch (vopt) { case 220: if (vlen <= 1) { RDEBUG("SoH adding NAP marker to DHCP reply"); /* client probe; send "NAP" in the reply */ vp = paircreate(request->reply, 43, DHCP_MAGIC_VENDOR); vp->vp_octets[0] = 220; vp->vp_octets[1] = 3; vp->vp_octets[4] = 'N'; vp->vp_octets[3] = 'A'; vp->vp_octets[2] = 'P'; vp->length = 5; pairadd(&request->reply->vps, vp); } else { RDEBUG("SoH decoding NAP from DHCP request"); /* SoH payload */ rcode = soh_verify(request, data, vlen); if (rcode < 0) { return RLM_MODULE_FAIL; } } break; default: /* nothing to do */ break; } data += vlen; } return RLM_MODULE_OK; } #endif return RLM_MODULE_NOOP; }
/* * compose EAP reply packet in EAP-Message attr of RADIUS. If * EAP exceeds 253, frame it in multiple EAP-Message attrs. */ int eap_basic_compose(RADIUS_PACKET *packet, EAP_PACKET *reply) { VALUE_PAIR *vp; eap_packet_t *eap_packet; int rcode; if (eap_wireformat(reply) == EAP_INVALID) { return RLM_MODULE_INVALID; } eap_packet = (eap_packet_t *)reply->packet; pairdelete(&(packet->vps), PW_EAP_MESSAGE); vp = eap_packet2vp(eap_packet); if (!vp) return RLM_MODULE_INVALID; pairadd(&(packet->vps), vp); /* * EAP-Message is always associated with * Message-Authenticator but not vice-versa. * * Don't add a Message-Authenticator if it's already * there. */ vp = pairfind(packet->vps, PW_MESSAGE_AUTHENTICATOR); if (!vp) { vp = paircreate(PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS); memset(vp->vp_strvalue, 0, AUTH_VECTOR_LEN); vp->length = AUTH_VECTOR_LEN; pairadd(&(packet->vps), vp); } /* Set request reply code, but only if it's not already set. */ rcode = RLM_MODULE_OK; if (!packet->code) switch(reply->code) { case PW_EAP_RESPONSE: case PW_EAP_SUCCESS: packet->code = PW_AUTHENTICATION_ACK; rcode = RLM_MODULE_HANDLED; break; case PW_EAP_FAILURE: packet->code = PW_AUTHENTICATION_REJECT; rcode = RLM_MODULE_REJECT; break; case PW_EAP_REQUEST: packet->code = PW_ACCESS_CHALLENGE; rcode = RLM_MODULE_HANDLED; break; default: /* Should never enter here */ packet->code = PW_AUTHENTICATION_REJECT; break; } return rcode; }
static int mschapv1_encode(RADIUS_PACKET *packet, VALUE_PAIR **request, char const *password) { unsigned int i; uint8_t *p; VALUE_PAIR *challenge, *response; uint8_t nthash[16]; challenge = paircreate(packet, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT); if (!challenge) { fprintf(stderr, "GOT IT %d!\n", __LINE__); return 0; } pairadd(request, challenge); challenge->length = 8; challenge->vp_octets = p = talloc_array(challenge, uint8_t, challenge->length); for (i = 0; i < challenge->length; i++) { p[i] = fr_rand(); } response = paircreate(packet, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT); if (!response) { fprintf(stderr, "GOT IT %d!\n", __LINE__); return 0; } pairadd(request, response); response->length = 50; response->vp_octets = p = talloc_array(response, uint8_t, response->length); memset(p, 0, response->length); p[1] = 0x01; /* NT hash */ mschap_ntpwdhash(nthash, password); smbdes_mschap(nthash, challenge->vp_octets, p + 26); return 1; }
/* * 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); 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 = paircreate(PW_CACHE_ENTRY_HITS, 0, PW_TYPE_INTEGER); rad_assert(vp != NULL); vp->vp_integer = c->hits; pairadd(&request->packet->vps, vp); } }
/* * Add a proxy-pair to the end of the request. */ static void proxy_addinfo(REQUEST *request) { VALUE_PAIR *proxy_pair; proxy_pair = paircreate(PW_PROXY_STATE, PW_TYPE_STRING); if (proxy_pair == NULL) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } sprintf((char *)proxy_pair->strvalue, "%d", request->packet->id); proxy_pair->length = strlen((char *)proxy_pair->strvalue); pairadd(&request->proxy->vps, proxy_pair); }
static int mschapv1_encode(VALUE_PAIR **request, const char *password) { unsigned int i; VALUE_PAIR *challenge, *response; uint8_t nthash[16]; challenge = paircreate(PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, PW_TYPE_OCTETS); if (!challenge) { fprintf(stderr, "GOT IT %d!\n", __LINE__); return 0; } pairadd(request, challenge); challenge->length = 8; for (i = 0; i < challenge->length; i++) { challenge->vp_octets[i] = fr_rand(); } response = paircreate(PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, PW_TYPE_OCTETS); if (!response) { fprintf(stderr, "GOT IT %d!\n", __LINE__); return 0; } pairadd(request, response); response->length = 50; memset(response->vp_octets, 0, response->length); response->vp_octets[1] = 0x01; /* NT hash */ mschap_ntpwdhash(nthash, password); smbdes_mschap(nthash, challenge->vp_octets, response->vp_octets + 26); return 1; }
/** Create a pair and add it to a particular list of VPs * * Note that this function ALWAYS returns. If we're OOM, then it causes the * server to exit! */ VALUE_PAIR *radius_paircreate(UNUSED REQUEST *request, VALUE_PAIR **vps, unsigned int attribute, unsigned int vendor, int type) { VALUE_PAIR *vp; vp = paircreate(attribute, vendor, type); if (!vp) { radlog(L_ERR, "No memory!"); rad_assert("No memory" == NULL); _exit(1); } if (vps) pairadd(vps, vp); return vp; }
/** Create a VALUE_PAIR and add it to a list of VALUE_PAIR s * * @note This function ALWAYS returns. If we're OOM, then it causes the * @note server to exit, so you don't need to check the return value. * * @param[in] ctx Context to allocate VALUE_PAIRs in. * @param[out] vps List to add new VALUE_PAIR to, if NULL will just * return VALUE_PAIR. * @param[in] attribute number. * @param[in] vendor number. * @return a new VLAUE_PAIR or causes server to exit on error. */ VALUE_PAIR *radius_paircreate(TALLOC_CTX *ctx, VALUE_PAIR **vps, unsigned int attribute, unsigned int vendor) { VALUE_PAIR *vp; vp = paircreate(ctx, attribute, vendor); if (!vp) { ERROR("No memory!"); rad_assert("No memory" == NULL); fr_exit_now(1); } if (vps) pairadd(vps, vp); return vp; }
static void cache_merge(rlm_cache_t *inst, REQUEST *request, rlm_cache_entry_t *c) { VALUE_PAIR *vp; vp_map_t *map; vp = pairfind(request->config, 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"); RINDENT(); for (map = c->maps; map; map = map->next) { /* * The only reason that the application of a map entry * can fail, is if the destination list or request * isn't valid. For now we don't consider this fatal * and continue merging the rest of the maps. */ if (map_to_request(request, map, map_to_vp, NULL) < 0) { char buffer[1024]; map_prints(buffer, sizeof(buffer), map); REXDENT(); RDEBUG("Skipping %s", buffer); RINDENT(); continue; } } REXDENT(); 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; } }
/* * Before sending an Access-Reject, call the modules in the * Post-Auth-Type REJECT stanza. */ static int rad_postauth_reject(REQUEST *request) { int result; VALUE_PAIR *tmp; DICT_VALUE *dval; dval = dict_valbyname(PW_POST_AUTH_TYPE, "REJECT"); if (dval) { /* Overwrite the Post-Auth-Type with the value REJECT */ pairdelete(&request->config_items, PW_POST_AUTH_TYPE); tmp = paircreate(PW_POST_AUTH_TYPE, PW_TYPE_INTEGER); tmp->lvalue = dval->value; pairadd(&request->config_items, tmp); result = rad_postauth(request); } else { /* No REJECT stanza */ result = RLM_MODULE_OK; } return result; }
/* * 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; } if (c->control) { RDEBUG2("Merging cached control list"); rdebug_pair_list(L_DBG_LVL_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(L_DBG_LVL_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(L_DBG_LVL_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); } }
/* * Copy the specified attribute to the specified list */ static void mypairappend(VALUE_PAIR *item, VALUE_PAIR **to) { VALUE_PAIR *tmp; tmp = paircreate(item->attribute, item->type); if( tmp == NULL ) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } switch (tmp->type) { case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: case PW_TYPE_DATE: tmp->lvalue = item->lvalue; break; default: memcpy((char *)tmp->strvalue, (char *)item->strvalue, item->length); tmp->length = item->length; break; } pairadd(to, tmp); }
/* * this code sends the success message. * * the only work to be done is the add the appropriate SEND/RECV * radius attributes derived from the MSK. * */ static int eap_sim_sendsuccess(EAP_HANDLER *handler) { unsigned char *p; struct eap_sim_server_state *ess; VALUE_PAIR **outvps; VALUE_PAIR *newvp; /* outvps is the data to the client. */ outvps= &handler->request->reply->vps; ess = (struct eap_sim_server_state *)handler->opaque; /* set the EAP_ID - new value */ newvp = paircreate(ATTRIBUTE_EAP_ID, 0, PW_TYPE_INTEGER); newvp->vp_integer = ess->sim_id++; pairreplace(outvps, newvp); p = ess->keys.msk; add_reply(outvps, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN); p += EAPTLS_MPPE_KEY_LEN; add_reply(outvps, "MS-MPPE-Send-Key", p, EAPTLS_MPPE_KEY_LEN); return 1; }
/* * this code sends the success message. * * the only work to be done is the add the appropriate SEND/RECV * radius attributes derived from the MSK. * */ static int eap_sim_sendsuccess(eap_handler_t *handler) { unsigned char *p; struct eap_sim_server_state *ess; VALUE_PAIR *vp; RADIUS_PACKET *packet; /* outvps is the data to the client. */ packet = handler->request->reply; ess = (struct eap_sim_server_state *)handler->opaque; /* set the EAP_ID - new value */ vp = paircreate(packet, ATTRIBUTE_EAP_ID, 0); vp->vp_integer = ess->sim_id++; pairreplace(&handler->request->reply->vps, vp); p = ess->keys.msk; eap_add_reply(handler->request, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN); p += EAPTLS_MPPE_KEY_LEN; eap_add_reply(handler->request, "MS-MPPE-Send-Key", p, EAPTLS_MPPE_KEY_LEN); return 1; }
/* * Actually generates EAP-Session-Id, which is an internal server * attribute. Not all systems want to send EAP-Key-Nam */ void eaptls_gen_eap_key(RADIUS_PACKET *packet, SSL *s, uint32_t header) { VALUE_PAIR *vp; uint8_t *p; if (!s->s3) { EDEBUG("No SSLv3 information"); return; } vp = paircreate(packet, PW_EAP_SESSION_ID, 0); if (!vp) return; vp->length = 1 + 2 * SSL3_RANDOM_SIZE; p = talloc_array(vp, uint8_t, vp->length); p[0] = header & 0xff; memcpy(p + 1, s->s3->client_random, SSL3_RANDOM_SIZE); memcpy(p + 1 + SSL3_RANDOM_SIZE, s->s3->server_random, SSL3_RANDOM_SIZE); vp->vp_octets = p; pairadd(&packet->vps, vp); }
VALUE_PAIR *eap_packet2vp(RADIUS_PACKET *packet, eap_packet_raw_t const *eap) { int total, size; uint8_t const *ptr; VALUE_PAIR *head = NULL; VALUE_PAIR *vp; vp_cursor_t out; total = eap->length[0] * 256 + eap->length[1]; if (total == 0) { DEBUG("Asked to encode empty EAP-Message!"); return NULL; } ptr = (uint8_t const *) eap; fr_cursor_init(&out, &head); do { size = total; if (size > 253) size = 253; vp = paircreate(packet, PW_EAP_MESSAGE, 0); if (!vp) { pairfree(&head); return NULL; } pairmemcpy(vp, ptr, size); fr_cursor_insert(&out, vp); ptr += size; total -= size; } while (total > 0); return head; }
/** Add a module failure message VALUE_PAIR to the request */ void vmodule_failure_msg(REQUEST *request, char const *fmt, va_list ap) { char *p; VALUE_PAIR *vp; va_list aq; if (!fmt || !request->packet) { return; } vp = paircreate(request->packet, PW_MODULE_FAILURE_MESSAGE, 0); if (!vp) { return; } /* * If we don't copy the original ap we get a segfault from vasprintf. This is apparently * due to ap sometimes being implemented with a stack offset which is invalidated if * ap is passed into another function. See here: * http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html * * I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when * running unit tests which generate errors under CI. */ va_copy(aq, ap); p = talloc_vasprintf(vp, fmt, aq); talloc_set_type(p, char); va_end(aq); if (request->module && *request->module) { pairsprintf(vp, "%s: %s", request->module, p); } else { pairsprintf(vp, "%s", p); } talloc_free(p); pairadd(&request->packet->vps, vp); }
/* * Do EAP. */ static rlm_rcode_t eap_authenticate(void *instance, REQUEST *request) { rlm_eap_t *inst; EAP_HANDLER *handler; void *data; int data_len; rlm_rcode_t rcode; VALUE_PAIR *vp; inst = (rlm_eap_t *) instance; vp = pairfind(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY); if (!vp) { RDEBUG("No EAP-Message. Not doing EAP."); return RLM_MODULE_FAIL; } /* * Get the eap packet to start with */ data = NULL; data_len = 0; if (eap_vp2data(request->packet->vps, &data, &data_len) < 0) { radlog(L_ERR, "rlm_eap2: Malformed EAP Message"); return RLM_MODULE_FAIL; } vp = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY); if (vp) { handler = eaplist_find(inst, request); if (!handler) { RDEBUG("No handler found"); return RLM_MODULE_FAIL; } } else { handler = malloc(sizeof(*handler)); if (!handler) return RLM_MODULE_FAIL; memset(handler, 0, sizeof(*handler)); handler->inst = inst; handler->eap_cb.get_eap_user = server_get_eap_user; handler->eap_cb.get_eap_req_id_text = server_get_eap_req_id_text; handler->eap_conf.eap_server = 1; handler->eap_conf.ssl_ctx = inst->tls_ctx; /* * Copy EAP-FAST parameters. */ handler->eap_conf.pac_opaque_encr_key = inst->pac_opaque_encr_key; handler->eap_conf.eap_fast_a_id = inst->eap_fast_a_id; handler->eap_conf.eap_fast_a_id_len = strlen(inst->eap_fast_a_id); handler->eap_conf.eap_fast_a_id_info = inst->eap_fast_a_id_info; handler->eap_conf.eap_fast_prov = inst->eap_fast_prov; handler->eap_conf.pac_key_lifetime = inst->pac_key_lifetime; handler->eap_conf.pac_key_refresh_time = inst->pac_key_refresh_time; handler->eap_conf.backend_auth = inst->backend_auth; handler->server_ctx.eap = eap_server_sm_init(handler, &handler->eap_cb, &handler->eap_conf); if (handler->server_ctx.eap == NULL) { free(handler); return RLM_MODULE_FAIL; } handler->server_ctx.eap_if = eap_get_interface(handler->server_ctx.eap); /* Enable "port" and request EAP to start authentication. */ handler->server_ctx.eap_if->portEnabled = TRUE; handler->server_ctx.eap_if->eapRestart = TRUE; } handler->request = request; wpabuf_free(handler->server_ctx.eap_if->eapRespData); handler->server_ctx.eap_if->eapRespData = wpabuf_alloc_copy(data, data_len); if (handler->server_ctx.eap_if->eapRespData) { handler->server_ctx.eap_if->eapResp = TRUE; } if (eap_example_server_step(handler) < 0) { RDEBUG("Failed in EAP library"); goto fail; } if (handler->server_ctx.eap_if->eapSuccess) { request->reply->code = PW_AUTHENTICATION_ACK; rcode = RLM_MODULE_OK; } else if (handler->server_ctx.eap_if->eapFail) { fail: request->reply->code = PW_AUTHENTICATION_REJECT; rcode = RLM_MODULE_REJECT; } else { request->reply->code = PW_ACCESS_CHALLENGE; rcode = RLM_MODULE_HANDLED; } if (handler->server_ctx.eap_if->eapFail || handler->server_ctx.eap_if->eapSuccess) { RDEBUG2("Freeing handler"); /* handler is not required any more, free it now */ eap_handler_free(handler); handler = NULL; } else { eaplist_add(inst, handler); } /* * If it's an Access-Accept, RFC 2869, Section 2.3.1 * says that we MUST include a User-Name attribute in the * Access-Accept. */ if ((request->reply->code == PW_AUTHENTICATION_ACK) && request->username) { /* * Doesn't exist, add it in. */ vp = pairfind(request->reply->vps, PW_USER_NAME, 0, TAG_ANY); if (!vp) { vp = pairmake("User-Name", request->username->vp_strvalue, T_OP_EQ); rad_assert(vp != NULL); pairadd(&(request->reply->vps), vp); } /* * Cisco AP1230 has a bug and needs a zero * terminated string in Access-Accept. */ if ((inst->cisco_accounting_username_bug) && (vp->length < (int) sizeof(vp->vp_strvalue))) { vp->vp_strvalue[vp->length] = '\0'; vp->length++; } } vp = pairfind(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY); if (!vp) { vp = paircreate(PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS); memset(vp->vp_strvalue, 0, AUTH_VECTOR_LEN); vp->length = AUTH_VECTOR_LEN; pairadd(&(request->reply->vps), vp); } return rcode; }
/* * Find the named user in this modules database. Create the set * of attribute-value pairs to check and reply with for this user * from the database. The authentication code only needs to check * the password, the rest is done here. */ static int sqlcounter_authorize(void *instance, REQUEST *request) { rlm_sqlcounter_t *data = (rlm_sqlcounter_t *) instance; int ret=RLM_MODULE_NOOP; int counter=0; int res=0; DICT_ATTR *dattr; VALUE_PAIR *key_vp, *check_vp; VALUE_PAIR *reply_item; char msg[128]; char querystr[MAX_QUERY_LEN]; char responsestr[MAX_QUERY_LEN]; /* quiet the compiler */ instance = instance; request = request; /* * Before doing anything else, see if we have to reset * the counters. */ if (data->reset_time && (data->reset_time <= request->timestamp)) { /* * Re-set the next time and prev_time for this counters range */ data->last_reset = data->reset_time; find_next_reset(data,request->timestamp); } /* * Look for the key. User-Name is special. It means * The REAL username, after stripping. */ DEBUG2("rlm_sqlcounter: Entering module authorize code"); key_vp = (data->key_attr == PW_USER_NAME) ? request->username : pairfind(request->packet->vps, data->key_attr); if (key_vp == NULL) { DEBUG2("rlm_sqlcounter: Could not find Key value pair"); return ret; } /* * Look for the check item */ if ((dattr = dict_attrbyname(data->check_name)) == NULL) { return ret; } /* DEBUG2("rlm_sqlcounter: Found Check item attribute %d", dattr->attr); */ if ((check_vp= pairfind(request->config_items, dattr->attr)) == NULL) { DEBUG2("rlm_sqlcounter: Could not find Check item value pair"); return ret; } /* first, expand %k, %b and %e in query */ sqlcounter_expand(querystr, MAX_QUERY_LEN, data->query, instance); /* second, xlat any request attribs in query */ radius_xlat(responsestr, MAX_QUERY_LEN, querystr, request, sql_escape_func); /* third, wrap query with sql module & expand */ snprintf(querystr, sizeof(querystr), "%%{%%S:%s}", responsestr); sqlcounter_expand(responsestr, MAX_QUERY_LEN, querystr, instance); /* Finally, xlat resulting SQL query */ radius_xlat(querystr, MAX_QUERY_LEN, responsestr, request, sql_escape_func); counter = atoi(querystr); /* * Check if check item > counter */ res=check_vp->lvalue - counter; if (res > 0) { DEBUG2("rlm_sqlcounter: (Check item - counter) is greater than zero"); /* * We are assuming that simultaneous-use=1. But * even if that does not happen then our user * could login at max for 2*max-usage-time Is * that acceptable? */ /* * User is allowed, but set Session-Timeout. * Stolen from main/auth.c */ /* * If we are near a reset then add the next * limit, so that the user will not need to * login again */ if (data->reset_time && ( res >= (data->reset_time - request->timestamp))) { res = data->reset_time - request->timestamp; res += check_vp->lvalue; } if ((reply_item = pairfind(request->reply->vps, PW_SESSION_TIMEOUT)) != NULL) { if (reply_item->lvalue > res) reply_item->lvalue = res; } else { if ((reply_item = paircreate(PW_SESSION_TIMEOUT, PW_TYPE_INTEGER)) == NULL) { radlog(L_ERR|L_CONS, "no memory"); return RLM_MODULE_NOOP; } reply_item->lvalue = res; pairadd(&request->reply->vps, reply_item); } ret=RLM_MODULE_OK; DEBUG2("rlm_sqlcounter: Authorized user %s, check_item=%d, counter=%d", key_vp->strvalue,check_vp->lvalue,counter); DEBUG2("rlm_sqlcounter: Sent Reply-Item for user %s, Type=Session-Timeout, value=%d", key_vp->strvalue,reply_item->lvalue); } else{ char module_fmsg[MAX_STRING_LEN]; VALUE_PAIR *module_fmsg_vp; DEBUG2("rlm_sqlcounter: (Check item - counter) is less than zero"); /* * User is denied access, send back a reply message */ snprintf(msg, sizeof(msg), "Your maximum %s usage time has been reached", data->reset); reply_item=pairmake("Reply-Message", msg, T_OP_EQ); pairadd(&request->reply->vps, reply_item); snprintf(module_fmsg, sizeof(module_fmsg), "rlm_sqlcounter: Maximum %s usage time reached", data->reset); module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ); pairadd(&request->packet->vps, module_fmsg_vp); ret=RLM_MODULE_REJECT; DEBUG2("rlm_sqlcounter: Rejected user %s, check_item=%d, counter=%d", key_vp->strvalue,check_vp->lvalue,counter); } return ret; }
/* * Create a VALUE_PAIR from an ASCII attribute and value, * where the attribute name is in the form: * * Attr-%d * Vendor-%d-Attr-%d * VendorName-Attr-%d */ static VALUE_PAIR *pairmake_any(const char *attribute, const char *value, int operator) { int attr, vendor; size_t size; const char *p = attribute; char *q; VALUE_PAIR *vp; /* * Unknown attributes MUST be of type 'octets' */ if (value && (strncasecmp(value, "0x", 2) != 0)) { fr_strerror_printf("Unknown attribute \"%s\" requires a hex string, not \"%s\"", attribute, value); return NULL; } vendor = 0; /* * Pull off vendor prefix first. */ if (strncasecmp(p, "Attr-", 5) != 0) { if (strncasecmp(p, "Vendor-", 7) == 0) { vendor = (int) strtol(p + 7, &q, 10); if ((vendor == 0) || (vendor > 65535)) { fr_strerror_printf("Invalid vendor value in attribute name \"%s\"", attribute); return NULL; } p = q; } else { /* must be vendor name */ char buffer[256]; q = strchr(p, '-'); if (!q) { fr_strerror_printf("Invalid vendor name in attribute name \"%s\"", attribute); return NULL; } if ((size_t) (q - p) >= sizeof(buffer)) { fr_strerror_printf("Vendor name too long in attribute name \"%s\"", attribute); return NULL; } memcpy(buffer, p, (q - p)); buffer[q - p] = '\0'; vendor = dict_vendorbyname(buffer); if (!vendor) { fr_strerror_printf("Unknown vendor name in attribute name \"%s\"", attribute); return NULL; } p = q; } if (*p != '-') { fr_strerror_printf("Invalid text following vendor definition in attribute name \"%s\"", attribute); return NULL; } p++; } /* * Attr-%d */ if (strncasecmp(p, "Attr-", 5) != 0) { fr_strerror_printf("Invalid format in attribute name \"%s\"", attribute); return NULL; } attr = strtol(p + 5, &q, 10); /* * Invalid, or trailing text after number. */ if ((attr == 0) || *q) { fr_strerror_printf("Invalid value in attribute name \"%s\"", attribute); return NULL; } /* * Double-check the size of attr. */ if (vendor) { DICT_VENDOR *dv = dict_vendorbyvalue(vendor); if (!dv) { if (attr > 255) { attr_error: fr_strerror_printf("Invalid attribute number in attribute name \"%s\"", attribute); return NULL; } } else switch (dv->type) { case 1: if (attr > 255) goto attr_error; break; case 2: if (attr > 65535) goto attr_error; break; case 4: /* Internal limitations! */ if (attr > 65535) goto attr_error; break; default: fr_strerror_printf("Internal sanity check failed"); return NULL; } } attr |= vendor << 16; /* * We've now parsed the attribute properly, Let's create * it. This next stop also looks the attribute up in the * dictionary, and creates the appropriate type for it. */ if ((vp = paircreate(attr, PW_TYPE_OCTETS)) == NULL) { fr_strerror_printf("out of memory"); return NULL; } vp->operator = (operator == 0) ? T_OP_EQ : operator; if (!value) return vp; size = strlen(value + 2); /* * We may be reading something like Attr-5. i.e. * who-ever wrote the text didn't understand it, but we * do. */ switch (vp->type) { default: if (size == (vp->length * 2)) break; vp->type = PW_TYPE_OCTETS; /* FALL-THROUGH */ case PW_TYPE_OCTETS: case PW_TYPE_ABINARY: vp->length = size >> 1; if (vp->length > sizeof(vp->vp_octets)) { vp->length = sizeof(vp->vp_octets); } break; case PW_TYPE_STRING: vp->length = size >> 1; memset(&vp->vp_strvalue, 0, sizeof(vp->vp_strvalue)); if (vp->length >= sizeof(vp->vp_strvalue)) { vp->length = sizeof(vp->vp_strvalue) - 1; } break; } if (fr_hex2bin(value + 2, vp->vp_octets, size) != vp->length) { fr_strerror_printf("Invalid hex string"); free(vp); return NULL; } /* * Move contents around based on type. This is * to work around the historical use of "lvalue". */ switch (vp->type) { case PW_TYPE_DATE: case PW_TYPE_IPADDR: case PW_TYPE_INTEGER: memcpy(&vp->lvalue, vp->vp_octets, sizeof(vp->lvalue)); vp->vp_strvalue[0] = '\0'; break; default: break; } return vp; }
/** Send the challenge itself * * Challenges will come from one of three places eventually: * * 1 from attributes like PW_EAP_SIM_RANDx * (these might be retrived from a database) * * 2 from internally implemented SIM authenticators * (a simple one based upon XOR will be provided) * * 3 from some kind of SS7 interface. * * For now, they only come from attributes. * It might be that the best way to do 2/3 will be with a different * module to generate/calculate things. * */ static int eap_sim_sendchallenge(eap_handler_t *handler) { REQUEST *request = handler->request; eap_sim_state_t *ess; VALUE_PAIR **invps, **outvps, *newvp; RADIUS_PACKET *packet; uint8_t *p; ess = (eap_sim_state_t *)handler->opaque; rad_assert(handler->request != NULL); rad_assert(handler->request->reply); /* * Invps is the data from the client but this is for non-protocol data here. * We should already have consumed any client originated data. */ invps = &handler->request->packet->vps; /* * Outvps is the data to the client */ packet = handler->request->reply; outvps = &packet->vps; if (RDEBUG_ENABLED2) { RDEBUG2("EAP-SIM decoded packet:"); debug_pair_list(*invps); } /* * Okay, we got the challenges! Put them into an attribute. */ newvp = paircreate(packet, PW_EAP_SIM_RAND, 0); newvp->length = 2 + (EAPSIM_RAND_SIZE * 3); newvp->vp_octets = p = talloc_array(newvp, uint8_t, newvp->length); memset(p, 0, 2); /* clear reserved bytes */ p += 2; memcpy(p, ess->keys.rand[0], EAPSIM_RAND_SIZE); p += EAPSIM_RAND_SIZE; memcpy(p, ess->keys.rand[1], EAPSIM_RAND_SIZE); p += EAPSIM_RAND_SIZE; memcpy(p, ess->keys.rand[2], EAPSIM_RAND_SIZE); pairadd(outvps, newvp); /* * Set the EAP_ID - new value */ newvp = paircreate(packet, PW_EAP_ID, 0); newvp->vp_integer = ess->sim_id++; pairreplace(outvps, newvp); /* * Make a copy of the identity */ ess->keys.identitylen = strlen(handler->identity); memcpy(ess->keys.identity, handler->identity, ess->keys.identitylen); /* * Use the SIM identity, if available */ newvp = pairfind(*invps, PW_EAP_SIM_IDENTITY, 0, TAG_ANY); if (newvp && newvp->length > 2) { uint16_t len; memcpy(&len, newvp->vp_octets, sizeof(uint16_t)); len = ntohs(len); if (len <= newvp->length - 2 && len <= MAX_STRING_LEN) { ess->keys.identitylen = len; memcpy(ess->keys.identity, newvp->vp_octets + 2, ess->keys.identitylen); } } /* * All set, calculate keys! */ eapsim_calculate_keys(&ess->keys); #ifdef EAP_SIM_DEBUG_PRF eapsim_dump_mk(&ess->keys); #endif /* * Need to include an AT_MAC attribute so that it will get * calculated. The NONCE_MT and the MAC are both 16 bytes, so * We store the NONCE_MT in the MAC for the encoder, which * will pull it out before it does the operation. */ newvp = paircreate(packet, PW_EAP_SIM_MAC, 0); pairmemcpy(newvp, ess->keys.nonce_mt, 16); pairreplace(outvps, newvp); newvp = paircreate(packet, PW_EAP_SIM_KEY, 0); pairmemcpy(newvp, ess->keys.K_aut, 16); pairreplace(outvps, newvp); /* the SUBTYPE, set to challenge. */ newvp = paircreate(packet, PW_EAP_SIM_SUBTYPE, 0); newvp->vp_integer = EAPSIM_CHALLENGE; pairreplace(outvps, newvp); return 1; }
/* * Authenticate the user via one of any well-known password. */ static int securid_authenticate(void *instance, REQUEST *request) { int rcode; rlm_securid_t *inst = instance; VALUE_PAIR *module_fmsg_vp; VALUE_PAIR *vp; char buffer[MAX_STRING_LEN]=""; const char *username=NULL, *password=NULL; char module_fmsg[MAX_STRING_LEN]=""; /* * We can only authenticate user requests which HAVE * a User-Name attribute. */ if (!request->username) { radlog(L_AUTH, "rlm_securid: Attribute \"User-Name\" is required for authentication."); return RLM_MODULE_INVALID; } if (!request->password) { radlog_request(L_AUTH, 0, request, "Attribute \"Password\" is required for authentication."); return RLM_MODULE_INVALID; } /* * Clear-text passwords are the only ones we support. */ if (request->password->attribute != PW_USER_PASSWORD) { radlog_request(L_AUTH, 0, request, "Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->name); return RLM_MODULE_INVALID; } /* * The user MUST supply a non-zero-length password. */ if (request->password->length == 0) { snprintf(module_fmsg,sizeof(module_fmsg),"rlm_securid: empty password supplied"); module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ); pairadd(&request->packet->vps, module_fmsg_vp); return RLM_MODULE_INVALID; } /* * shortcuts */ username = request->username->vp_strvalue; password = request->password->vp_strvalue; RDEBUG("User [%s] login attempt with password [%s]", username, password); rcode = securidAuth(inst, request, username, password, buffer, sizeof(buffer)); switch (rcode) { case RC_SECURID_AUTH_SUCCESS: rcode = RLM_MODULE_OK; break; case RC_SECURID_AUTH_CHALLENGE: /* reply with Access-challenge message code (11) */ /* Generate Prompt attribute */ vp = paircreate(PW_PROMPT, 0, PW_TYPE_INTEGER); rad_assert(vp != NULL); vp->vp_integer = 0; /* no echo */ pairadd(&request->reply->vps, vp); /* Mark the packet as a Acceess-Challenge Packet */ request->reply->code = PW_ACCESS_CHALLENGE; RDEBUG("Sending Access-Challenge."); rcode = RLM_MODULE_HANDLED; break; case RC_SECURID_AUTH_FAILURE: case RC_SECURID_AUTH_ACCESS_DENIED_FAILURE: case RC_SECURID_AUTH_INVALID_SERVER_FAILURE: default: rcode = RLM_MODULE_REJECT; break; } if (*buffer) { /* Generate Reply-Message attribute with reply message data */ vp = pairmake("Reply-Message", buffer, T_OP_EQ); /* make sure message ends with '\0' */ if (vp->length < (int) sizeof(vp->vp_strvalue)) { vp->vp_strvalue[vp->length] = '\0'; vp->length++; } pairadd(&request->reply->vps,vp); } return rcode; }