/* * Generate a random value * challenge = MD5(random) */ int eapmd5_challenge(unsigned char *value, int len) { int i; /* * Get real pseudo-random numbers. */ for (i = 0; i < len; i++) { value[i] = lrad_rand(); } radlog(L_INFO, "rlm_eap_md5: Issuing Challenge"); return 1; }
static int call_modloadbalance(int component, modgroup *g, REQUEST *request, int default_result) { int count = 1; modcallable *p, *child = NULL; /* * Catch people who have issues. */ if (!g->children) { DEBUG2(" WARNING! Asked to process empty load-balance group. Returning %s.", lrad_int2str(rcode_table, default_result, "??")); return default_result; } /* * Pick a random child. */ /* Loop over the children */ for(p = g->children; p; p = p->next) { if (!child) { child = p; count = 1; continue; } /* * Keep track of how many load balancing servers * we've gone through. */ count++; /* * See the "camel book" for why this works. * * If (rand(0..n) < 1), pick the current realm. * We add a scale factor of 65536, to avoid * floating point. */ if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) { child = p; } } rad_assert(child != NULL); /* Call the chosen child by recursing into modcall */ return modcall(component, child, request); }
/* * Like realm find, but does load balancing, and we don't * wake up any sleeping realms. Someone should already have * done that. * * It also does NOT do fail-over to default if the realms are dead, * as that decision has already been made. */ static REALM *proxy_realm_ldb(REQUEST *request, const char *realm_name, int accounting) { int redone = 0; REALM *cl, *lb; uint32_t count; redo: lb = NULL; count = 0; for (cl = mainconfig.realms; cl; cl = cl->next) { /* * Wake up any sleeping realm. * * Note that the 'realm find' function will only * wake up the FIRST realm which matches. We've * got to wake up ALL of the matching realms. */ if (cl->wakeup <= request->timestamp) { cl->active = TRUE; } if (cl->acct_wakeup <= request->timestamp) { cl->acct_active = TRUE; } /* * Asked for auth/acct, and the auth/acct server * is not active. Skip it. */ if ((!accounting && !cl->active) || (accounting && !cl->acct_active)) { continue; } /* * The realm name doesn't match, skip it. */ if (strcasecmp(cl->realm, realm_name) != 0) { continue; } /* * Fail-over, pick the first one that matches. */ if ((count == 0) && /* if size > 0, we have round-robin */ (cl->ldflag == 0)) { return cl; } /* * We're doing load-balancing. Pick a random * number, which will be used to determine which * home server is chosen. */ if (!lb) { lb = cl; count = 1; continue; } /* * Keep track of how many load balancing servers * we've gone through. */ count++; /* * See the "camel book" for why this works. * * If (rand(0..n) < 1), pick the current realm. * We add a scale factor of 65536, to avoid * floating point. */ if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) { lb = cl; } } /* loop over the realms */ /* * All are dead, see if we have to wake */ if (!redone && !lb && mainconfig.wake_all_if_all_dead) { for (cl = mainconfig.realms; cl; cl = cl->next) { if(strcasecmp(cl->realm,realm_name) == 0) { if (!accounting && !cl->active) { cl->active = TRUE; } else if (accounting && !cl->acct_active) { cl->acct_active = TRUE; } } } redone = 1; goto redo; } /* * Return the load-balanced realm. */ return lb; }
/* * Like realm find, but does load balancing, and we don't * wake up any sleeping realms. Someone should already have * done that. * * It also does NOT do fail-over to default if the realms are dead, * as that decision has already been made. */ static REALM *proxy_realm_ldb(REQUEST *request, const char *realm_name, int accounting) { REALM *cl; REALM *array[32]; /* maximum number of load balancing realms */ int size; size = 0; for (cl = mainconfig.realms; cl; cl = cl->next) { /* * Wake up any sleeping realm. * * Note that the 'realm find' function will only * wake up the FIRST realm which matches. We've * got to wake up ALL of the matching realms. */ if (cl->wakeup <= request->timestamp) { cl->active = TRUE; } if (cl->acct_wakeup <= request->timestamp) { cl->acct_active = TRUE; } /* * Asked for auth/acct, and the auth/acct server * is not active. Skip it. */ if ((!accounting && !cl->active) || (accounting && !cl->acct_active)) { continue; } /* * The realm name doesn't match, skip it. */ if (strcasecmp(cl->realm, realm_name) != 0) { continue; } /* * Fail-over, pick the first one that matches. */ if ((size == 0) && /* if size > 0, we have round-robin */ (cl->ldflag == 0)) { return cl; } /* * Else we're doing round-robin. */ array[size] = cl; size++; /* * If too many realms are found, then limit * the number to 32. */ if (size >= 32) { break; } } /* * WTF? We didn't find a live realm! * * This means that 'rlm_realm' found a live realm for * us, and then it was marked dead before code execution * got here. The only thing to do is to dump the request. */ if (size == 0) { return NULL; } /* * Pick a random realm. It's not true round-robin, * but over a large number of requests, it statistically * distributes the requests evenly among the realms. */ return array[(int) (lrad_rand() % size)]; }
/* * For more than 2 modules with redundancy + load balancing * across all of them, layering the "redundant" and * "load-balance" groups gets too complicated. As a result, we * implement a special function to do this. */ static int call_modredundantloadbalance(int component, modgroup *g, REQUEST *request, int default_result) { int count = 1; int myresult = default_result; int priority = 0; /* default result has lowest priority */ modcallable *p, *child = NULL; /* * Catch people who have issues. */ if (!g->children) { DEBUG2(" WARNING! Asked to process empty redundant-load-balance group. Returning %s.", lrad_int2str(rcode_table, default_result, "??")); return default_result; } /* * Pick a random child. */ /* Loop over the children */ for(p = g->children; p; p = p->next) { if (!child) { child = p; count = 1; continue; } /* * Keep track of how many load balancing servers * we've gone through. */ count++; /* * See the "camel book" for why this works. * * If (rand(0..n) < 1), pick the current realm. * We add a scale factor of 65536, to avoid * floating point. */ if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) { child = p; } } rad_assert(child != NULL); /* * Call the chosen child, with fail-over to the next one * if it is down. */ p = child; do { /* * Call the chosen entry. If we're done, then * stop. */ if (!call_one(component, p, request, &priority, &myresult)) { break; } /* * Go to the next one, and wrap around to the beginning if * we reach the end. */ p = p->next; if (!p) p = g->children; } while (p != child); /* * And return whatever was decided. */ return myresult; }
/* * Initiate the EAP-MD5 session by sending a challenge to the peer. */ static int md5_initiate(void *type_data, EAP_HANDLER *handler) { int i; MD5_PACKET *reply; type_data = type_data; /* -Wunused */ /* * Allocate an EAP-MD5 packet. */ reply = eapmd5_alloc(); if (reply == NULL) { radlog(L_ERR, "rlm_eap_md5: out of memory"); return 0; } /* * Fill it with data. */ reply->code = PW_MD5_CHALLENGE; reply->length = 1 + MD5_CHALLENGE_LEN; /* one byte of value size */ reply->value_size = MD5_CHALLENGE_LEN; /* * Allocate user data. */ reply->value = malloc(reply->value_size); if (reply->value == NULL) { radlog(L_ERR, "rlm_eap_md5: out of memory"); eapmd5_free(&reply); return 0; } /* * Get a random challenge. */ for (i = 0; i < reply->value_size; i++) { reply->value[i] = lrad_rand(); } radlog(L_INFO, "rlm_eap_md5: Issuing Challenge"); /* * Keep track of the challenge. */ handler->opaque = malloc(reply->value_size); rad_assert(handler->opaque != NULL); memcpy(handler->opaque, reply->value, reply->value_size); handler->free_opaque = free; /* * Compose the EAP-MD5 packet out of the data structure, * and free it. */ eapmd5_compose(handler->eap_ds, reply); /* * We don't need to authorize the user at this point. * * We also don't need to keep the challenge, as it's * stored in 'handler->eap_ds', which will be given back * to us... */ handler->stage = AUTHENTICATE; return 1; }
/* * we got an EAP-Request/Sim/Start message in a legal state. * * pick a supported version, put it into the reply, and insert a nonce. */ static int process_eap_start(RADIUS_PACKET *req, RADIUS_PACKET *rep) { VALUE_PAIR *vp, *newvp; VALUE_PAIR *anyidreq_vp, *fullauthidreq_vp, *permanentidreq_vp; uint16_t *versions, selectedversion; unsigned int i,versioncount; /* form new response clear of any EAP stuff */ cleanresp(rep); if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST)) == NULL) { fprintf(stderr, "illegal start message has no VERSION_LIST\n"); return 0; } versions = (uint16_t *)vp->strvalue; /* verify that the attribute length is big enough for a length field */ if(vp->length < 4) { fprintf(stderr, "start message has illegal VERSION_LIST. Too short: %d\n", vp->length); return 0; } versioncount = ntohs(versions[0])/2; /* verify that the attribute length is big enough for the given number * of versions present. */ if((unsigned)vp->length <= (versioncount*2 + 2)) { fprintf(stderr, "start message is too short. Claimed %d versions does not fit in %d bytes\n", versioncount, vp->length); return 0; } /* * record the versionlist for the MK calculation. */ eapsim_mk.versionlistlen = versioncount*2; memcpy(eapsim_mk.versionlist, (unsigned char *)(versions+1), eapsim_mk.versionlistlen); /* walk the version list, and pick the one we support, which * at present, is 1, EAP_SIM_VERSION. */ selectedversion=0; for(i=0; i < versioncount; i++) { if(ntohs(versions[i+1]) == EAP_SIM_VERSION) { selectedversion=EAP_SIM_VERSION; break; } } if(selectedversion == 0) { fprintf(stderr, "eap-sim start message. No compatible version found. We need %d\n", EAP_SIM_VERSION); for(i=0; i < versioncount; i++) { fprintf(stderr, "\tfound version %d\n", ntohs(versions[i+1])); } } /* * now make sure that we have only FULLAUTH_ID_REQ. * I think that it actually might not matter - we can answer in * anyway we like, but it is illegal to have more than one * present. */ anyidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_ANY_ID_REQ); fullauthidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ); permanentidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_PERMANENT_ID_REQ); if(fullauthidreq_vp == NULL || anyidreq_vp != NULL || permanentidreq_vp != NULL) { fprintf(stderr, "start message has %sanyidreq, %sfullauthid and %spermanentid. Illegal combination.\n", (anyidreq_vp != NULL ? "a " : "no "), (fullauthidreq_vp != NULL ? "a " : "no "), (permanentidreq_vp != NULL ? "a " : "no ")); return 0; } /* okay, we have just any_id_req there, so fill in response */ /* mark the subtype as being EAP-SIM/Response/Start */ newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER); newvp->lvalue = eapsim_start; pairreplace(&(rep->vps), newvp); /* insert selected version into response. */ newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_SELECTED_VERSION, PW_TYPE_OCTETS); versions = (uint16_t *)newvp->strvalue; versions[0] = htons(selectedversion); newvp->length = 2; pairreplace(&(rep->vps), newvp); /* record the selected version */ memcpy(eapsim_mk.versionselect, (unsigned char *)versions, 2); vp = newvp = NULL; { uint32_t nonce[4]; /* * insert a nonce_mt that we make up. */ newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT, PW_TYPE_OCTETS); newvp->strvalue[0]=0; newvp->strvalue[1]=0; newvp->length = 18; /* 16 bytes of nonce + padding */ nonce[0]=lrad_rand(); nonce[1]=lrad_rand(); nonce[2]=lrad_rand(); nonce[3]=lrad_rand(); memcpy(&newvp->strvalue[2], nonce, 16); pairreplace(&(rep->vps), newvp); /* also keep a copy of the nonce! */ memcpy(eapsim_mk.nonce_mt, nonce, 16); } { uint16_t *pidlen, idlen; /* * insert the identity here. */ vp = pairfind(rep->vps, PW_USER_NAME); if(vp == NULL) { fprintf(stderr, "eap-sim: We need to have a User-Name attribute!\n"); return 0; } newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_IDENTITY, PW_TYPE_OCTETS); idlen = strlen(vp->strvalue); pidlen = (uint16_t *)newvp->strvalue; *pidlen = htons(idlen); newvp->length = idlen + 2; memcpy(&newvp->strvalue[2], vp->strvalue, idlen); pairreplace(&(rep->vps), newvp); /* record it */ memcpy(eapsim_mk.identity, vp->strvalue, idlen); eapsim_mk.identitylen = idlen; } return 1; }
/* * Add an entry to the proxy tree. * * This is the ONLY function in this source file which may be called * from a child thread. It therefore needs mutexes... */ int rl_add_proxy(REQUEST *request) { int i, found, proxy; uint32_t mask; proxy_id_t myid, *entry; myid.dst_ipaddr = request->proxy->dst_ipaddr; myid.dst_port = request->proxy->dst_port; /* * Proxied requests get sent out the proxy FD ONLY. * * FIXME: Once we allocate multiple proxy FD's, move this * code to below, so we can have more than 256 requests * outstanding. */ request->proxy_outstanding = 1; pthread_mutex_lock(&proxy_mutex); /* * Assign a proxy ID. */ entry = rbtree_finddata(proxy_id_tree, &myid); if (!entry) { /* allocate it */ entry = rad_malloc(sizeof(*entry) + sizeof(entry->id) * 255); entry->dst_ipaddr = request->proxy->dst_ipaddr; entry->dst_port = request->proxy->dst_port; entry->index = 0; DEBUG3(" proxy: creating %08x:%d", entry->dst_ipaddr, entry->dst_port); /* * Insert the new home server entry into * the tree. * * FIXME: We don't (currently) delete the * entries, so this is technically a * memory leak. */ if (rbtree_insert(proxy_id_tree, entry) == 0) { DEBUG2("ERROR: Failed to insert entry into proxy Id tree"); free(entry); return 0; } /* * Clear out bits in the array which DO have * proxy Fd's associated with them. We do this * by getting the mask of bits which have proxy * fd's... */ mask = 0; for (i = 0; i < 32; i++) { if (proxy_fds[i] != -1) { mask |= (1 << i); } } rad_assert(mask != 0); /* * Set bits here indicate that the Fd is in use. */ entry->mask = mask; mask = ~mask; /* * Set the bits which are unused (and therefore * allocated). The clear bits indicate that the Id * for that FD is unused. */ for (i = 0; i < 256; i++) { entry->id[i] = mask; } } /* else the entry already existed in the proxy Id tree */ retry: /* * Try to find a free Id. */ found = -1; for (i = 0; i < 256; i++) { /* * Some bits are still zero.. */ if (entry->id[(i + entry->index) & 0xff] != (uint32_t) ~0) { found = (i + entry->index) & 0xff; break; } /* * Hmm... do we want to re-use Id's, when we * haven't seen all of the responses? */ } /* * No free Id, try to get a new FD. */ if (found < 0) { /* * First, see if there were FD's recently allocated, * which we don't know about. */ mask = 0; for (i = 0; i < 32; i++) { if (proxy_fds[i] < 0) continue; mask |= (1 << i); } /* * There ARE more FD's than we know about. * Update the masks for Id's, and re-try. */ if (entry->mask != mask) { /* * New mask always has more bits than * the old one, but never fewer bits. */ rad_assert((entry->mask & mask) == entry->mask); /* * Clear the bits we already know about, * and then or in those bits into the * global mask. */ mask ^= entry->mask; entry->mask |= mask; mask = ~mask; /* * Clear the bits in the Id's for the new * FD's. */ for (i = 0; i < 256; i++) { entry->id[i] &= mask; } /* * And try again to allocate an Id. */ goto retry; } /* else no new Fd's were allocated. */ /* * If all Fd's are allocated, die. */ if (~mask == 0) { radlog(L_ERR|L_CONS, "ERROR: More than 8000 proxied requests outstanding for home server %08x:%d", ntohs(entry->dst_ipaddr), entry->dst_port); return 0; } /* * Allocate a new proxy Fd. This function adds it * into the list of listeners. */ proxy = proxy_new_listener(); if (proxy < 0) { DEBUG2("ERROR: Failed to create a new socket for proxying requests."); return 0; } /* * */ found = -1; for (i = 0; i < 32; i++) { /* * Found a free entry. Save the socket, * and remember where we saved it. */ if (proxy_fds[(proxy + i) & 0x1f] == -1) { proxy_fds[(proxy + i) & 0x1f] = proxy; found = (proxy + i) & 0x1f; break; } } rad_assert(found >= 0); /* i.e. the mask had free bits. */ mask = 1 << found; entry->mask |= mask; mask = ~mask; /* * Clear the relevant bits in the mask. */ for (i = 0; i < 256; i++) { entry->id[i] &= mask; } /* * Pick a random Id to start from, as we've * just guaranteed that it's free. */ found = lrad_rand() & 0xff; } /* * Mark next (hopefully unused) entry. */ entry->index = (found + 1) & 0xff; /* * We now have to find WHICH proxy fd to use. */ proxy = -1; for (i = 0; i < 32; i++) { /* * FIXME: pick a random socket to use? */ if ((entry->id[found] & (1 << i)) == 0) { proxy = i; break; } } /* * There was no bit clear, which we had just checked above... */ rad_assert(proxy != -1); /* * Mark the Id as allocated, for thei Fd. */ entry->id[found] |= (1 << proxy); request->proxy->id = found; request->proxy->src_ipaddr = proxy_ipaddr; rad_assert(proxy_fds[proxy] != -1); request->proxy->sockfd = proxy_fds[proxy]; DEBUG3(" proxy: allocating %08x:%d %d", entry->dst_ipaddr, entry->dst_port, request->proxy->id); if (!rbtree_insert(proxy_tree, request)) { DEBUG2("ERROR: Failed to insert entry into proxy tree"); return 0; } pthread_mutex_unlock(&proxy_mutex); return 1; }