/** Compare two pair lists except for the password information. * * For every element in "check" at least one matching copy must be present * in "reply". * * @param req Current request * @param request request valuepairs * @param check check/control valuepairs * @param[in,out] reply reply value pairs * * @return 0 on match. */ int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply) { VALUE_PAIR *check_item; VALUE_PAIR *auth_item; int result = 0; int compare; int other; for (check_item = check; check_item != NULL; check_item = check_item->next) { /* * If the user is setting a configuration value, * then don't bother comparing it to any attributes * sent to us by the user. It ALWAYS matches. */ if ((check_item->operator == T_OP_SET) || (check_item->operator == T_OP_ADD)) { continue; } switch (check_item->attribute) { /* * Attributes we skip during comparison. * These are "server" check items. */ case PW_CRYPT_PASSWORD: case PW_AUTH_TYPE: case PW_AUTZ_TYPE: case PW_ACCT_TYPE: case PW_SESSION_TYPE: case PW_STRIP_USER_NAME: continue; break; /* * IF the password attribute exists, THEN * we can do comparisons against it. If not, * then the request did NOT contain a * User-Password attribute, so we CANNOT do * comparisons against it. * * This hack makes CHAP-Password work.. */ case PW_USER_PASSWORD: if (check_item->operator == T_OP_CMP_EQ) { DEBUG("WARNING: Found User-Password == \"...\"."); DEBUG("WARNING: Are you sure you don't mean Cleartext-Password?"); DEBUG("WARNING: See \"man rlm_pap\" for more information."); } if (pairfind(request, PW_USER_PASSWORD, 0) == NULL) { continue; } break; } /* * See if this item is present in the request. */ other = otherattr(check_item->attribute); auth_item = request; try_again: if (other >= 0) { while (auth_item != NULL) { if ((auth_item->attribute == (unsigned int) other) || (other == 0)) { break; } auth_item = auth_item->next; } } /* * Not found, it's not a match. */ if (auth_item == NULL) { /* * Didn't find it. If we were *trying* * to not find it, then we succeeded. */ if (check_item->operator == T_OP_CMP_FALSE) { continue; } else { return -1; } } /* * Else we found it, but we were trying to not * find it, so we failed. */ if (check_item->operator == T_OP_CMP_FALSE) { return -1; } /* * We've got to xlat the string before doing * the comparison. */ if (check_item->flags.do_xlat) { int rcode; char buffer[sizeof(check_item->vp_strvalue)]; check_item->flags.do_xlat = 0; rcode = radius_xlat(buffer, sizeof(buffer), check_item->vp_strvalue, req, NULL, NULL); /* * Parse the string into a new value. */ pairparsevalue(check_item, buffer); } /* * OK it is present now compare them. */ compare = radius_callback_compare(req, auth_item, check_item, check, reply); switch (check_item->operator) { case T_OP_EQ: default: radlog(L_INFO, "Invalid operator for item %s: " "reverting to '=='", check_item->name); case T_OP_CMP_TRUE: case T_OP_CMP_FALSE: case T_OP_CMP_EQ: if (compare != 0) result = -1; break; case T_OP_NE: if (compare == 0) result = -1; break; case T_OP_LT: if (compare >= 0) result = -1; break; case T_OP_GT: if (compare <= 0) result = -1; break; case T_OP_LE: if (compare > 0) result = -1; break; case T_OP_GE: if (compare < 0) result = -1; break; #ifdef HAVE_REGEX_H case T_OP_REG_EQ: case T_OP_REG_NE: if (compare != 0) result = -1; break; #endif } /* switch over the operator of the check item */ /* * This attribute didn't match, but maybe there's * another of the same attribute, which DOES match. */ if ((result != 0) && (other >= 0)) { auth_item = auth_item->next; result = 0; goto try_again; } } /* for every entry in the check item list */ return result; }
/* * EAP authorization DEPENDS on other rlm authorizations, * to check for user existance & get their configured values. * It Handles EAP-START Messages, User-Name initilization. */ static int eap_authorize(void *instance, REQUEST *request) { rlm_eap_t *inst; int status; VALUE_PAIR *vp; inst = (rlm_eap_t *)instance; #ifdef WITH_PROXY /* * We don't do authorization again, once we've seen the * proxy reply (or the proxied packet) */ if (request->proxy != NULL) return RLM_MODULE_NOOP; #endif /* * For EAP_START, send Access-Challenge with EAP Identity * request. even when we have to proxy this request * * RFC 2869, Section 2.3.1 notes that the "domain" of the * user, (i.e. where to proxy him) comes from the EAP-Identity, * so we CANNOT proxy the user, until we know his identity. * * We therefore send an EAP Identity request. */ status = eap_start(inst, request); switch(status) { case EAP_NOOP: return RLM_MODULE_NOOP; case EAP_FAIL: return RLM_MODULE_FAIL; case EAP_FOUND: return RLM_MODULE_HANDLED; case EAP_OK: case EAP_NOTFOUND: default: break; } /* * RFC 2869, Section 2.3.1. If a NAS sends an EAP-Identity, * it MUST copy the identity into the User-Name attribute. * * But we don't worry about that too much. We depend on * each EAP sub-module to look for handler->request->username, * and to get excited if it doesn't appear. */ vp = pairfind(request->config_items, PW_AUTH_TYPE, 0); if ((!vp) || (vp->vp_integer != PW_AUTHTYPE_REJECT)) { vp = pairmake("Auth-Type", inst->xlat_name, T_OP_EQ); if (!vp) { RDEBUG2("Failed to create Auth-Type %s: %s\n", inst->xlat_name, fr_strerror()); return RLM_MODULE_FAIL; } pairadd(&request->config_items, vp); } else { RDEBUG2("WARNING: Auth-Type already set. Not setting to EAP"); } if (status == EAP_OK) return RLM_MODULE_OK; return RLM_MODULE_UPDATED; }
/* * Move attributes from one list to the other * if not already present. */ void pairmove(VALUE_PAIR **to, VALUE_PAIR **from) { VALUE_PAIR **tailto, *i, *j, *next; VALUE_PAIR *tailfrom = NULL; VALUE_PAIR *found; int has_password = 0; /* * First, see if there are any passwords here, and * point "tailto" to the end of the "to" list. */ tailto = to; for(i = *to; i; i = i->next) { if (i->attribute == PW_USER_PASSWORD || i->attribute == PW_CRYPT_PASSWORD) has_password = 1; tailto = &i->next; } /* * Loop over the "from" list. */ for(i = *from; i; i = next) { next = i->next; /* * If there was a password in the "to" list, * do not move any other password from the * "from" to the "to" list. */ if (has_password && (i->attribute == PW_USER_PASSWORD || i->attribute == PW_CRYPT_PASSWORD)) { tailfrom = i; continue; } switch (i->operator) { /* * These are COMPARISON attributes * from a check list, and are not * supposed to be copied! */ case T_OP_NE: case T_OP_GE: case T_OP_GT: case T_OP_LE: case T_OP_LT: case T_OP_CMP_TRUE: case T_OP_CMP_FALSE: case T_OP_CMP_EQ: case T_OP_REG_EQ: case T_OP_REG_NE: tailfrom = i; continue; default: break; } /* * If the attribute is already present in "to", * do not move it from "from" to "to". We make * an exception for "Hint" which can appear multiple * times, and we never move "Fall-Through". */ if (i->attribute == PW_FALL_THROUGH || (i->attribute != PW_HINT && i->attribute != PW_FRAMED_ROUTE)) { found = pairfind(*to, i->attribute, i->vendor); switch (i->operator) { /* * If matching attributes are found, * delete them. */ case T_OP_SUB: /* -= */ if (found) { if (!i->vp_strvalue[0] || (strcmp((char *)found->vp_strvalue, (char *)i->vp_strvalue) == 0)){ pairdelete(to, found->attribute, found->vendor); /* * 'tailto' may have been * deleted... */ tailto = to; for(j = *to; j; j = j->next) { tailto = &j->next; } } } tailfrom = i; continue; break; case T_OP_EQ: /* = */ /* * FIXME: Tunnel attributes with * different tags are different * attributes. */ if (found) { tailfrom = i; continue; /* with the loop */ } break; /* * If a similar attribute is found, * replace it with the new one. Otherwise, * add the new one to the list. */ case T_OP_SET: /* := */ if (found) { VALUE_PAIR *mynext = found->next; /* * Do NOT call pairdelete() * here, due to issues with * re-writing "request->username". * * Everybody calls pairmove, * and expects it to work. * We can't update request->username * here, so instead we over-write * the vp that it's pointing to. */ memcpy(found, i, sizeof(*found)); found->next = mynext; pairdelete(&found->next, found->attribute, found->vendor); /* * 'tailto' may have been * deleted... */ for(j = found; j; j = j->next) { tailto = &j->next; } continue; } break; /* * Add the new element to the list, even * if similar ones already exist. */ default: case T_OP_ADD: /* += */ break; } } if (tailfrom) tailfrom->next = next; else *from = next; /* * If ALL of the 'to' attributes have been deleted, * then ensure that the 'tail' is updated to point * to the head. */ if (!*to) { tailto = to; } *tailto = i; if (i) { i->next = NULL; tailto = &i->next; } } }
static int sendrecv_eap(RADIUS_PACKET *rep) { RADIUS_PACKET *req = NULL; VALUE_PAIR *vp, *vpnext; int tried_eap_md5 = 0; /* * Keep a copy of the the User-Password attribute. */ if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) != NULL) { strlcpy(password, (char *)vp->vp_strvalue, sizeof(vp->vp_strvalue)); } else if ((vp = pairfind(rep->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) { strlcpy(password, (char *)vp->vp_strvalue, sizeof(vp->vp_strvalue)); /* * Otherwise keep a copy of the CHAP-Password attribute. */ } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) { strlcpy(password, (char *)vp->vp_strvalue, sizeof(vp->vp_strvalue)); } else { *password = '******'; } again: rep->id++; /* * if there are EAP types, encode them into an EAP-Message * */ map_eap_methods(rep); /* * Fix up Digest-Attributes issues */ for (vp = rep->vps; vp != NULL; vp = vp->next) { switch (vp->da->attribute) { default: break; case PW_DIGEST_REALM: case PW_DIGEST_NONCE: case PW_DIGEST_METHOD: case PW_DIGEST_URI: case PW_DIGEST_QOP: case PW_DIGEST_ALGORITHM: case PW_DIGEST_BODY_DIGEST: case PW_DIGEST_CNONCE: case PW_DIGEST_NONCE_COUNT: case PW_DIGEST_USER_NAME: /* overlapping! */ memmove(&vp->vp_strvalue[2], &vp->vp_octets[0], vp->length); vp->vp_octets[0] = vp->da->attribute - PW_DIGEST_REALM + 1; vp->length += 2; vp->vp_octets[1] = vp->length; vp->da->attribute = PW_DIGEST_ATTRIBUTES; break; } } /* * If we've already sent a packet, free up the old * one, and ensure that the next packet has a unique * ID and authentication vector. */ if (rep->data) { talloc_free(rep->data); rep->data = NULL; } fr_md5_calc(rep->vector, rep->vector, sizeof(rep->vector)); if (*password != '\0') { if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) != NULL) { pairstrcpy(vp, password); } else if ((vp = pairfind(rep->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) { pairstrcpy(vp, password); } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) { pairstrcpy(vp, password); rad_chap_encode(rep, vp->vp_octets, rep->id, vp); vp->length = 17; } } /* there WAS a password */ /* send the response, wait for the next request */ send_packet(rep, &req); /* okay got back the packet, go and decode the EAP-Message. */ unmap_eap_methods(req); debug_packet(req, R_RECV); /* now look for the code type. */ for (vp = req->vps; vp != NULL; vp = vpnext) { vpnext = vp->next; switch (vp->da->attribute) { default: break; case ATTRIBUTE_EAP_BASE+PW_EAP_MD5: if(respond_eap_md5(req, rep) && tried_eap_md5 < 3) { tried_eap_md5++; goto again; } break; case ATTRIBUTE_EAP_BASE+PW_EAP_SIM: if(respond_eap_sim(req, rep)) { goto again; } break; } } return 1; }
int od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR * usernamepair) { tDirStatus status = eDSNoErr; tDirReference dsRef = 0; tDirNodeReference userNodeRef = 0; tDataBuffer *tDataBuff = NULL; tDataBuffer *pStepBuff = NULL; tDataNode *pAuthType = NULL; unsigned long uiCurr = 0; unsigned long uiLen = 0; char *username_string = NULL; char *shortUserName = NULL; VALUE_PAIR *response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE); #ifndef NDEBUG int t; #endif username_string = (char *) malloc(usernamepair->length + 1); if (username_string == NULL) return RLM_MODULE_FAIL; strlcpy(username_string, (char *)usernamepair->vp_strvalue, usernamepair->length + 1); status = dsOpenDirService(&dsRef); if (status != eDSNoErr) { free(username_string); radlog(L_ERR,"rlm_mschap: od_mschap_auth(): dsOpenDirService = %d", status); return RLM_MODULE_FAIL; } status = getUserNodeRef(username_string, &shortUserName, &userNodeRef, dsRef); if(status != RLM_MODULE_OK) { DEBUG2("rlm_osx_od: ds_mschap_auth: getUserNodeRef failed"); if (username_string != NULL) free(username_string); if (dsRef != 0) dsCloseDirService(dsRef); return status; } /* We got a node; fill the stepBuffer kDSStdAuthMSCHAP2 MS-CHAPv2 authentication method. The Open Directory plug-in generates the reply data for the client. The input buffer format consists of a four byte length specifying the length of the user name that follows, the user name, a four byte value specifying the length of the server challenge that follows, the server challenge, a four byte value specifying the length of the peer challenge that follows, the peer challenge, a four byte value specifying the length of the client's digest that follows, and the client's digest. The output buffer consists of a four byte value specifying the length of the return digest for the client's challenge. r = FillAuthBuff(pAuthBuff, 5, strlen(inName), inName, // Directory Services long or short name strlen(schal), schal, // server challenge strlen(peerchal), peerchal, // client challenge strlen(p24), p24, // P24 NT-Response 4, "User"); // must match the username that was used for the hash inName = username_string schal = challenge->vp_strvalue peerchal = response->vp_strvalue + 2 (16 octets) p24 = response->vp_strvalue + 26 (24 octets) */ pStepBuff = dsDataBufferAllocate(dsRef, 4096); tDataBuff = dsDataBufferAllocate(dsRef, 4096); pAuthType = dsDataNodeAllocateString(dsRef, kDSStdAuthMSCHAP2); uiCurr = 0; DEBUG2(" rlm_mschap:username_string = %s, shortUserName=%s (length = %lu)\n", username_string, shortUserName, strlen(shortUserName)); /* User name length + username */ uiLen = strlen(shortUserName); memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(size_t)); uiCurr += sizeof(size_t); memcpy(&(tDataBuff->fBufferData[uiCurr]), shortUserName, uiLen); uiCurr += uiLen; #ifndef NDEBUG DEBUG2(" rlm_mschap: stepbuf server challenge:\t"); for (t = 0; t < challenge->length; t++) { fprintf(stderr, "%02x", challenge->vp_strvalue[t]); } fprintf(stderr, "\n"); #endif /* server challenge (ie. my (freeRADIUS) challenge) */ uiLen = 16; memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(size_t)); uiCurr += sizeof(size_t); memcpy(&(tDataBuff->fBufferData[uiCurr]), &(challenge->vp_strvalue[0]), uiLen); uiCurr += uiLen; #ifndef NDEBUG DEBUG2(" rlm_mschap: stepbuf peer challenge:\t\t"); for (t = 2; t < 18; t++) { fprintf(stderr, "%02x", response->vp_strvalue[t]); } fprintf(stderr, "\n"); #endif /* peer challenge (ie. the client-generated response) */ uiLen = 16; memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(size_t)); uiCurr += sizeof(size_t); memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[2]), uiLen); uiCurr += uiLen; #ifndef NDEBUG DEBUG2(" rlm_mschap stepbuf p24:\t\t"); for (t = 26; t < 50; t++) { fprintf(stderr, "%02x", response->vp_strvalue[t]); } fprintf(stderr, "\n"); #endif /* p24 (ie. second part of client-generated response) */ uiLen = 24; /* strlen(&(response->vp_strvalue[26])); may contain NULL byte in the middle. */ memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(size_t)); uiCurr += sizeof(size_t); memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[26]), uiLen); uiCurr += uiLen; /* Client generated use name (short name?) */ uiLen = strlen(username_string); memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(size_t)); uiCurr += sizeof(size_t); memcpy(&(tDataBuff->fBufferData[uiCurr]), username_string, uiLen); uiCurr += uiLen; tDataBuff->fBufferLength = uiCurr; status = dsDoDirNodeAuth(userNodeRef, pAuthType, 1, tDataBuff, pStepBuff, NULL); if (status == eDSNoErr) { if (pStepBuff->fBufferLength > 4) { unsigned long len; memcpy(&len, pStepBuff->fBufferData, 4); if (len == 40) { char mschap_reply[41] = { '\0' }; pStepBuff->fBufferData[len+4] = '\0'; mschap_reply[0] = 'S'; mschap_reply[1] = '='; memcpy(&(mschap_reply[2]), &(pStepBuff->fBufferData[4]), len); mschap_add_reply(request, &request->reply->vps, *response->vp_strvalue, "MS-CHAP2-Success", mschap_reply, len+2); DEBUG2("rlm_mschap: dsDoDirNodeAuth returns stepbuff: %s (len=%ld)\n", mschap_reply, len); } } } /* clean up */ if (username_string != NULL) free(username_string); if (shortUserName != NULL) free(shortUserName); if (tDataBuff != NULL) dsDataBufferDeAllocate(dsRef, tDataBuff); if (pStepBuff != NULL) dsDataBufferDeAllocate(dsRef, pStepBuff); if (pAuthType != NULL) dsDataNodeDeAllocate(dsRef, pAuthType); if (userNodeRef != 0) dsCloseDirNode(userNodeRef); if (dsRef != 0) dsCloseDirService(dsRef); if (status != eDSNoErr) { errno = EACCES; radlog(L_ERR, "rlm_mschap: authentication failed %d", status); /* <-- returns -14091 (eDSAuthMethodNotSupported) -14090 */ return RLM_MODULE_REJECT; } return RLM_MODULE_OK; }
/* * 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, 0, TAG_ANY)) == NULL) { fprintf(stderr, "illegal start message has no VERSION_LIST\n"); return 0; } versions = (uint16_t *)vp->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: %u\n", (unsigned int) 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 %u bytes\n", versioncount, (unsigned int) 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, 0, TAG_ANY); fullauthidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ, 0, TAG_ANY); permanentidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_PERMANENT_ID_REQ, 0, TAG_ANY); if(!fullauthidreq_vp || 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, 0, PW_TYPE_INTEGER); newvp->vp_integer = eapsim_start; pairreplace(&(rep->vps), newvp); /* insert selected version into response. */ newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_SELECTED_VERSION, 0, PW_TYPE_OCTETS); versions = (uint16_t *)newvp->vp_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, 0, PW_TYPE_OCTETS); newvp->vp_octets[0]=0; newvp->vp_octets[1]=0; newvp->length = 18; /* 16 bytes of nonce + padding */ nonce[0]=fr_rand(); nonce[1]=fr_rand(); nonce[2]=fr_rand(); nonce[3]=fr_rand(); memcpy(&newvp->vp_octets[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, 0, TAG_ANY); if(!vp) { 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, 0, PW_TYPE_OCTETS); idlen = strlen(vp->vp_strvalue); pidlen = (uint16_t *)newvp->vp_strvalue; *pidlen = htons(idlen); newvp->length = idlen + 2; memcpy(&newvp->vp_strvalue[2], vp->vp_strvalue, idlen); pairreplace(&(rep->vps), newvp); /* record it */ memcpy(eapsim_mk.identity, vp->vp_strvalue, idlen); eapsim_mk.identitylen = idlen; } return 1; }
/* * this code runs the EAP-SIM client state machine. * the *request* is from the server. * the *reponse* is to the server. * */ static int respond_eap_sim(RADIUS_PACKET *req, RADIUS_PACKET *resp) { enum eapsim_clientstates state, newstate; enum eapsim_subtype subtype; VALUE_PAIR *vp, *statevp, *radstate, *eapid; char statenamebuf[32], subtypenamebuf[32]; if ((radstate = paircopy2(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL) { return 0; } if ((eapid = paircopy2(NULL, req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY)) == NULL) { return 0; } /* first, dig up the state from the request packet, setting * outselves to be in EAP-SIM-Start state if there is none. */ if((statevp = pairfind(resp->vps, ATTRIBUTE_EAP_SIM_STATE, 0, TAG_ANY)) == NULL) { /* must be initial request */ statevp = paircreate(ATTRIBUTE_EAP_SIM_STATE, 0, PW_TYPE_INTEGER); statevp->vp_integer = eapsim_client_init; pairreplace(&(resp->vps), statevp); } state = statevp->vp_integer; /* * map the attributes, and authenticate them. */ unmap_eapsim_types(req); if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_SUBTYPE, 0, TAG_ANY)) == NULL) { return 0; } subtype = vp->vp_integer; /* * look for the appropriate state, and process incoming message */ switch(state) { case eapsim_client_init: switch(subtype) { case eapsim_start: newstate = process_eap_start(req, resp); break; case eapsim_challenge: case eapsim_notification: case eapsim_reauth: default: fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n", sim_state2name(state, statenamebuf, sizeof(statenamebuf)), sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf))); /* invalid state, drop message */ return 0; } break; case eapsim_client_start: switch(subtype) { case eapsim_start: /* NOT SURE ABOUT THIS ONE, retransmit, I guess */ newstate = process_eap_start(req, resp); break; case eapsim_challenge: newstate = process_eap_challenge(req, resp); break; default: fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n", sim_state2name(state, statenamebuf, sizeof(statenamebuf)), sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf))); /* invalid state, drop message */ return 0; } break; default: fprintf(stderr, "radeapclient: sim in illegal state %s\n", sim_state2name(state, statenamebuf, sizeof(statenamebuf))); return 0; } /* copy the eap state object in */ pairreplace(&(resp->vps), eapid); /* update stete info, and send new packet */ map_eapsim_types(resp); /* copy the radius state object in */ pairreplace(&(resp->vps), radstate); statevp->vp_integer = newstate; return 1; }
static rlm_rcode_t mod_authorize(void *instance, REQUEST * request) { int rcode = RLM_MODULE_NOTFOUND; rlm_sql_t *inst = instance; rlm_sql_handle_t *handle; VALUE_PAIR *check_tmp = NULL; VALUE_PAIR *reply_tmp = NULL; VALUE_PAIR *user_profile = NULL; int dofallthrough = 1; int rows; char *expanded = NULL; /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, request, NULL) < 0) { return RLM_MODULE_FAIL; } /* * Reserve a socket * * After this point use goto error or goto release to cleanup socket temporary pairlists and * temporary attributes. */ handle = sql_get_socket(inst); if (!handle) goto error; /* * Query the check table to find any conditions associated with this user/realm/whatever... */ if (inst->config->authorize_check_query && *inst->config->authorize_check_query) { if (radius_axlat(&expanded, request, inst->config->authorize_check_query, sql_escape_func, inst) < 0) { REDEBUG("Error generating query"); goto error; } rows = sql_getvpdata(inst, &handle, request, &check_tmp, expanded); if (rows < 0) { REDEBUG("SQL query error"); goto error; } TALLOC_FREE(expanded); /* * Only do this if *some* check pairs were returned */ if ((rows > 0) && (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0)) { RDEBUG2("User found in radcheck table"); radius_xlat_move(request, &request->config_items, &check_tmp); rcode = RLM_MODULE_OK; } /* * We only process reply table items if check conditions were verified */ else { goto skipreply; } } if (inst->config->authorize_reply_query && *inst->config->authorize_reply_query) { /* * Now get the reply pairs since the paircompare matched */ if (radius_axlat(&expanded, request, inst->config->authorize_reply_query, sql_escape_func, inst) < 0) { REDEBUG("Error generating query"); goto error; } rows = sql_getvpdata(inst, &handle, request->reply, &reply_tmp, expanded); if (rows < 0) { REDEBUG("SQL query error"); goto error; } TALLOC_FREE(expanded); if (rows > 0) { if (!inst->config->read_groups) { dofallthrough = fallthrough(reply_tmp); } RDEBUG2("User found in radreply table"); radius_xlat_move(request, &request->reply->vps, &reply_tmp); rcode = RLM_MODULE_OK; } } skipreply: /* * Clear out the pairlists */ pairfree(&check_tmp); pairfree(&reply_tmp); /* * dofallthrough is set to 1 by default so that if the user information * is not found, we will still process groups. If the user information, * however, *is* found, Fall-Through must be set in order to process * the groups as well. */ if (dofallthrough) { rcode = rlm_sql_process_groups(inst, request, handle, &dofallthrough); if (rcode != RLM_MODULE_OK) { goto release; } } /* * Repeat the above process with the default profile or User-Profile */ if (dofallthrough) { /* * Check for a default_profile or for a User-Profile. */ user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY); char const *profile = user_profile ? user_profile->vp_strvalue : inst->config->default_profile; if (!profile || !*profile) { goto release; } RDEBUG("Checking profile %s", profile); if (sql_set_user(inst, request, profile) < 0) { REDEBUG("Error setting profile"); goto error; } rcode = rlm_sql_process_groups(inst, request, handle, &dofallthrough); if (rcode != RLM_MODULE_OK) { REDEBUG("Error processing profile groups"); goto release; } } goto release; error: rcode = RLM_MODULE_FAIL; release: TALLOC_FREE(expanded); sql_release_socket(inst, handle); pairfree(&check_tmp); pairfree(&reply_tmp); return rcode; }
/* * Initialize a radclient data structure and add it to * the global linked list. */ static int radclient_init(TALLOC_CTX *ctx, char const *filename) { FILE *fp; vp_cursor_t cursor; VALUE_PAIR *vp; rc_request_t *request; int filedone = 0; int request_number = 1; assert(filename != NULL); /* * Determine where to read the VP's from. */ if (strcmp(filename, "-") != 0) { fp = fopen(filename, "r"); if (!fp) { fprintf(stderr, "radclient: Error opening %s: %s\n", filename, strerror(errno)); return 0; } } else { fp = stdin; } /* * Loop until the file is done. */ do { /* * Allocate it. */ request = talloc_zero(ctx, rc_request_t); if (!request) { goto oom; } talloc_set_destructor(request, _rc_request_free); request->packet = rad_alloc(request, 1); if (!request->packet) { goto oom; } #ifdef WITH_TCP request->packet->src_ipaddr = client_ipaddr; request->packet->src_port = client_port; request->packet->dst_ipaddr = server_ipaddr; request->packet->dst_port = server_port; #endif request->filename = filename; request->packet->id = -1; /* allocate when sending */ request->request_number = request_number++; /* * Read the VP's. */ request->packet->vps = readvp2(request, fp, &filedone, "radclient:"); if (!request->packet->vps) { talloc_free(request); if (fp != stdin) { fclose(fp); } return 1; } /* * Keep a copy of the the User-Password attribute. */ if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) { strlcpy(request->password, vp->vp_strvalue, sizeof(request->password)); /* * Otherwise keep a copy of the CHAP-Password attribute. */ } else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) { strlcpy(request->password, vp->vp_strvalue, sizeof(request->password)); } else if ((vp = pairfind(request->packet->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY)) != NULL) { strlcpy(request->password, vp->vp_strvalue, sizeof(request->password)); } else { request->password[0] = '\0'; } /* * Fix up Digest-Attributes issues */ for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) { /* * Double quoted strings get marked up as xlat expansions, * but we don't support that in request. */ if (vp->type == VT_XLAT) { vp->vp_strvalue = vp->value.xlat; vp->value.xlat = NULL; vp->type = VT_DATA; } if (!vp->da->vendor) switch (vp->da->attr) { default: break; /* * Allow it to set the packet type in * the attributes read from the file. */ case PW_PACKET_TYPE: request->packet->code = vp->vp_integer; break; case PW_PACKET_DST_PORT: request->packet->dst_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_DST_IP_ADDRESS: request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; break; case PW_PACKET_DST_IPV6_ADDRESS: request->packet->dst_ipaddr.af = AF_INET6; request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; break; case PW_PACKET_SRC_PORT: request->packet->src_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_SRC_IP_ADDRESS: request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; break; case PW_PACKET_SRC_IPV6_ADDRESS: request->packet->src_ipaddr.af = AF_INET6; request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; break; case PW_DIGEST_REALM: case PW_DIGEST_NONCE: case PW_DIGEST_METHOD: case PW_DIGEST_URI: case PW_DIGEST_QOP: case PW_DIGEST_ALGORITHM: case PW_DIGEST_BODY_DIGEST: case PW_DIGEST_CNONCE: case PW_DIGEST_NONCE_COUNT: case PW_DIGEST_USER_NAME: /* overlapping! */ { DICT_ATTR const *da; uint8_t *p; p = talloc_array(vp, uint8_t, vp->length + 2); memcpy(p + 2, vp->vp_octets, vp->length); p[0] = vp->da->attr - PW_DIGEST_REALM + 1; vp->length += 2; p[1] = vp->length; pairmemsteal(vp, p); da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0); if (!da) { goto oom; } vp->da = da; } break; } } /* loop over the VP's we read in */ /* * Add it to the tail of the list. */ if (!request_head) { assert(rc_request_tail == NULL); request_head = request; request->prev = NULL; } else { assert(rc_request_tail->next == NULL); rc_request_tail->next = request; request->prev = rc_request_tail; } rc_request_tail = request; request->next = NULL; } while (!filedone); /* loop until the file is done. */ if (fp != stdin) fclose(fp); /* * And we're done. */ return 1; oom: fprintf(stderr, "radclient: Out of memory\n"); talloc_free(request); if (fp != stdin) fclose(fp); return 0; }
static REQUEST *request_setup(FILE *fp) { VALUE_PAIR *vp; REQUEST *request; vp_cursor_t cursor; /* * Create and initialize the new request. */ request = request_alloc(NULL); request->packet = rad_alloc(request, 0); if (!request->packet) { ERROR("No memory"); request_free(&request); return NULL; } request->reply = rad_alloc(request, 0); if (!request->reply) { ERROR("No memory"); request_free(&request); return NULL; } request->listener = listen_alloc(request); request->client = client_alloc(request); request->number = 0; request->master_state = REQUEST_ACTIVE; request->child_state = REQUEST_ACTIVE; request->handle = NULL; request->server = talloc_strdup(request, "default"); request->root = &mainconfig; /* * Read packet from fp */ request->packet->vps = readvp2(request->packet, fp, &filedone, "radiusd:"); if (!request->packet->vps) { talloc_free(request); return NULL; } /* * Set the defaults for IPs, etc. */ request->packet->code = PW_CODE_AUTHENTICATION_REQUEST; request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK); request->packet->src_port = 18120; request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK); request->packet->dst_port = 1812; /* * Copied from radclient */ #if 1 /* * Fix up Digest-Attributes issues */ for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) { /* * Double quoted strings get marked up as xlat expansions, * but we don't support that here. */ if (vp->type == VT_XLAT) { vp->vp_strvalue = vp->value.xlat; vp->value.xlat = NULL; vp->type = VT_DATA; } if (!vp->da->vendor) switch (vp->da->attr) { default: break; /* * Allow it to set the packet type in * the attributes read from the file. */ case PW_PACKET_TYPE: request->packet->code = vp->vp_integer; break; case PW_PACKET_DST_PORT: request->packet->dst_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_DST_IP_ADDRESS: request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; break; case PW_PACKET_DST_IPV6_ADDRESS: request->packet->dst_ipaddr.af = AF_INET6; request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; break; case PW_PACKET_SRC_PORT: request->packet->src_port = (vp->vp_integer & 0xffff); break; case PW_PACKET_SRC_IP_ADDRESS: request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; break; case PW_PACKET_SRC_IPV6_ADDRESS: request->packet->src_ipaddr.af = AF_INET6; request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; break; case PW_CHAP_PASSWORD: { int i, already_hex = 0; /* * If it's 17 octets, it *might* be already encoded. * Or, it might just be a 17-character password (maybe UTF-8) * Check it for non-printable characters. The odds of ALL * of the characters being 32..255 is (1-7/8)^17, or (1/8)^17, * or 1/(2^51), which is pretty much zero. */ if (vp->length == 17) { for (i = 0; i < 17; i++) { if (vp->vp_octets[i] < 32) { already_hex = 1; break; } } } /* * Allow the user to specify ASCII or hex CHAP-Password */ if (!already_hex) { uint8_t *p; size_t len, len2; len = len2 = vp->length; if (len2 < 17) len2 = 17; p = talloc_zero_array(vp, uint8_t, len2); memcpy(p, vp->vp_strvalue, len); rad_chap_encode(request->packet, p, fr_rand() & 0xff, vp); vp->vp_octets = p; vp->length = 17; } } break; case PW_DIGEST_REALM: case PW_DIGEST_NONCE: case PW_DIGEST_METHOD: case PW_DIGEST_URI: case PW_DIGEST_QOP: case PW_DIGEST_ALGORITHM: case PW_DIGEST_BODY_DIGEST: case PW_DIGEST_CNONCE: case PW_DIGEST_NONCE_COUNT: case PW_DIGEST_USER_NAME: /* overlapping! */ { DICT_ATTR const *da; uint8_t *p; p = talloc_array(vp, uint8_t, vp->length + 2); memcpy(p + 2, vp->vp_octets, vp->length); p[0] = vp->da->attr - PW_DIGEST_REALM + 1; vp->length += 2; p[1] = vp->length; pairmemsteal(vp, p); da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0); rad_assert(da != NULL); vp->da = da; } break; } } /* loop over the VP's we read in */ #endif if (debug_flag) { for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) { /* * Take this opportunity to verify all the VALUE_PAIRs are still valid. */ if (!talloc_get_type(vp, VALUE_PAIR)) { ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp)); log_talloc_report(vp); rad_assert(0); } vp_print(fr_log_fp, vp); } fflush(fr_log_fp); } /* * FIXME: set IPs, etc. */ request->packet->code = PW_CODE_AUTHENTICATION_REQUEST; request->packet->src_ipaddr.af = AF_INET; request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK); request->packet->src_port = 18120; request->packet->dst_ipaddr.af = AF_INET; request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK); request->packet->dst_port = 1812; /* * Build the reply template from the request. */ request->reply->sockfd = request->packet->sockfd; request->reply->dst_ipaddr = request->packet->src_ipaddr; request->reply->src_ipaddr = request->packet->dst_ipaddr; request->reply->dst_port = request->packet->src_port; request->reply->src_port = request->packet->dst_port; request->reply->id = request->packet->id; request->reply->code = 0; /* UNKNOWN code */ memcpy(request->reply->vector, request->packet->vector, sizeof(request->reply->vector)); request->reply->vps = NULL; request->reply->data = NULL; request->reply->data_len = 0; /* * Debugging */ request->options = debug_flag; request->radlog = vradlog_request; request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY); request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY); return request; }
static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request) { rlm_rcode_t rcode = RLM_MODULE_OK; rlm_sql_handle_t *handle = NULL; rlm_sql_t *inst = instance; rlm_sql_row_t row; int check = 0; uint32_t ipno = 0; char const *call_num = NULL; VALUE_PAIR *vp; int ret; uint32_t nas_addr = 0; int nas_port = 0; char *expanded = NULL; /* If simul_count_query is not defined, we don't do any checking */ if (!inst->config->simul_count_query || (inst->config->simul_count_query[0] == '\0')) { return RLM_MODULE_NOOP; } if((!request->username) || (request->username->length == '\0')) { REDEBUG("Zero Length username not permitted"); return RLM_MODULE_INVALID; } if(sql_set_user(inst, request, NULL) < 0) { return RLM_MODULE_FAIL; } if (radius_axlat(&expanded, request, inst->config->simul_count_query, sql_escape_func, inst) < 0) { return RLM_MODULE_FAIL; } /* initialize the sql socket */ handle = sql_get_socket(inst); if (!handle) { talloc_free(expanded); return RLM_MODULE_FAIL; } if (rlm_sql_select_query(&handle, inst, expanded)) { rcode = RLM_MODULE_FAIL; goto finish; } ret = rlm_sql_fetch_row(&handle, inst); if (ret != 0) { rcode = RLM_MODULE_FAIL; goto finish; } row = handle->row; if (!row) { rcode = RLM_MODULE_FAIL; goto finish; } request->simul_count = atoi(row[0]); (inst->module->sql_finish_select_query)(handle, inst->config); TALLOC_FREE(expanded); if(request->simul_count < request->simul_max) { rcode = RLM_MODULE_OK; goto finish; } /* * Looks like too many sessions, so let's start verifying * them, unless told to rely on count query only. */ if (!inst->config->simul_verify_query || (inst->config->simul_verify_query[0] == '\0')) { rcode = RLM_MODULE_OK; goto finish; } if (radius_axlat(&expanded, request, inst->config->simul_verify_query, sql_escape_func, inst) < 0) { rcode = RLM_MODULE_FAIL; goto finish; } if(rlm_sql_select_query(&handle, inst, expanded)) { goto finish; } /* * Setup some stuff, like for MPP detection. */ request->simul_count = 0; if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) { ipno = vp->vp_ipaddr; } if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) { call_num = vp->vp_strvalue; } while (rlm_sql_fetch_row(&handle, inst) == 0) { row = handle->row; if (!row) { break; } if (!row[2]){ RDEBUG("Cannot zap stale entry. No username present in entry.", inst->config->xlat_name); rcode = RLM_MODULE_FAIL; goto finish; } if (!row[1]){ RDEBUG("Cannot zap stale entry. No session id in entry.", inst->config->xlat_name); rcode = RLM_MODULE_FAIL; goto finish; } if (row[3]) { nas_addr = inet_addr(row[3]); } if (row[4]) { nas_port = atoi(row[4]); } check = rad_check_ts(nas_addr, nas_port, row[2], row[1]); if (check == 0) { /* * Stale record - zap it. */ if (inst->config->deletestalesessions == true) { uint32_t framed_addr = 0; char proto = 0; int sess_time = 0; if (row[5]) framed_addr = inet_addr(row[5]); if (row[7]){ if (strcmp(row[7], "PPP") == 0) proto = 'P'; else if (strcmp(row[7], "SLIP") == 0) proto = 'S'; } if (row[8]) sess_time = atoi(row[8]); session_zap(request, nas_addr, nas_port, row[2], row[1], framed_addr, proto, sess_time); } } else if (check == 1) { /* * User is still logged in. */ ++request->simul_count; /* * Does it look like a MPP attempt? */ if (row[5] && ipno && inet_addr(row[5]) == ipno) { request->simul_mpp = 2; } else if (row[6] && call_num && !strncmp(row[6],call_num,16)) { request->simul_mpp = 2; } } else { /* * Failed to check the terminal server for * duplicate logins: return an error. */ REDEBUG("Failed to check the terminal server for user '%s'.", row[2]); rcode = RLM_MODULE_FAIL; goto finish; } } finish: (inst->module->sql_finish_select_query)(handle, inst->config); sql_release_socket(inst, handle); talloc_free(expanded); /* * The Auth module apparently looks at request->simul_count, * not the return value of this module when deciding to deny * a call for too many sessions. */ return rcode; }
void request_stats_reply(REQUEST *request) { VALUE_PAIR *flag, *vp; /* * Statistics are available ONLY on a "status" port. */ rad_assert(request->packet->code == PW_CODE_STATUS_SERVER); rad_assert(request->listener->type == RAD_LISTEN_NONE); flag = pairfind(request->packet->vps, 127, VENDORPEC_FREERADIUS, TAG_ANY); if (!flag || (flag->vp_integer == 0)) return; /* * Authentication. */ if (((flag->vp_integer & 0x01) != 0) && ((flag->vp_integer & 0xc0) == 0)) { request_stats_addvp(request, authvp, &radius_auth_stats); } #ifdef WITH_ACCOUNTING /* * Accounting */ if (((flag->vp_integer & 0x02) != 0) && ((flag->vp_integer & 0xc0) == 0)) { request_stats_addvp(request, acctvp, &radius_acct_stats); } #endif #ifdef WITH_PROXY /* * Proxied authentication requests. */ if (((flag->vp_integer & 0x04) != 0) && ((flag->vp_integer & 0x20) == 0)) { request_stats_addvp(request, proxy_authvp, &proxy_auth_stats); } #ifdef WITH_ACCOUNTING /* * Proxied accounting requests. */ if (((flag->vp_integer & 0x08) != 0) && ((flag->vp_integer & 0x20) == 0)) { request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats); } #endif #endif /* * Internal server statistics */ if ((flag->vp_integer & 0x10) != 0) { vp = radius_paircreate(request, &request->reply->vps, 176, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = start_time.tv_sec; vp = radius_paircreate(request, &request->reply->vps, 177, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = hup_time.tv_sec; #ifdef HAVE_PTHREAD_H int i, array[RAD_LISTEN_MAX], pps[2]; thread_pool_queue_stats(array, pps); for (i = 0; i <= 4; i++) { vp = radius_paircreate(request, &request->reply->vps, 162 + i, VENDORPEC_FREERADIUS); if (!vp) continue; vp->vp_integer = array[i]; } for (i = 0; i < 2; i++) { vp = radius_paircreate(request, &request->reply->vps, 181 + i, VENDORPEC_FREERADIUS); if (!vp) continue; vp->vp_integer = pps[i]; } #endif } /* * For a particular client. */ if ((flag->vp_integer & 0x20) != 0) { fr_ipaddr_t ipaddr; VALUE_PAIR *server_ip, *server_port = NULL; RADCLIENT *client = NULL; RADCLIENT_LIST *cl = NULL; /* * See if we need to look up the client by server * socket. */ server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY); if (server_ip) { server_port = pairfind(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY); if (server_port) { ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr; cl = listener_find_client_list(&ipaddr, server_port->vp_integer); /* * Not found: don't do anything */ if (!cl) return; } } vp = pairfind(request->packet->vps, 167, VENDORPEC_FREERADIUS, TAG_ANY); if (vp) { memset(&ipaddr, 0, sizeof(ipaddr)); ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; client = client_find(cl, &ipaddr, IPPROTO_UDP); #ifdef WITH_TCP if (!client) { client = client_find(cl, &ipaddr, IPPROTO_TCP); } #endif /* * Else look it up by number. */ } else if ((vp = pairfind(request->packet->vps, 168, VENDORPEC_FREERADIUS, TAG_ANY)) != NULL) { client = client_findbynumber(cl, vp->vp_integer); } if (client) { /* * If found, echo it back, along with * the requested statistics. */ pairadd(&request->reply->vps, paircopyvp(request->reply, vp)); /* * When retrieving client by number, also * echo back it's IP address. */ if ((vp->da->type == PW_TYPE_INTEGER) && (client->ipaddr.af == AF_INET)) { vp = radius_paircreate(request, &request->reply->vps, 167, VENDORPEC_FREERADIUS); if (vp) { vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr; } if (client->prefix != 32) { vp = radius_paircreate(request, &request->reply->vps, 169, VENDORPEC_FREERADIUS); if (vp) { vp->vp_integer = client->prefix; } } } if (server_ip) { pairadd(&request->reply->vps, paircopyvp(request->reply, server_ip)); } if (server_port) { pairadd(&request->reply->vps, paircopyvp(request->reply, server_port)); } if ((flag->vp_integer & 0x01) != 0) { request_stats_addvp(request, client_authvp, &client->auth); } #ifdef WITH_ACCOUNTING if ((flag->vp_integer & 0x01) != 0) { request_stats_addvp(request, client_acctvp, &client->acct); } #endif } /* else client wasn't found, don't echo it back */ } /* * For a particular "listen" socket. */ if (((flag->vp_integer & 0x40) != 0) && ((flag->vp_integer & 0x03) != 0)) { rad_listen_t *this; VALUE_PAIR *server_ip, *server_port; fr_ipaddr_t ipaddr; /* * See if we need to look up the server by socket * socket. */ server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY); if (!server_ip) return; server_port = pairfind(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY); if (!server_port) return; ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr; this = listener_find_byipaddr(&ipaddr, server_port->vp_integer, IPPROTO_UDP); /* * Not found: don't do anything */ if (!this) return; pairadd(&request->reply->vps, paircopyvp(request->reply, server_ip)); pairadd(&request->reply->vps, paircopyvp(request->reply, server_port)); if (((flag->vp_integer & 0x01) != 0) && ((request->listener->type == RAD_LISTEN_AUTH) || (request->listener->type == RAD_LISTEN_NONE))) { request_stats_addvp(request, authvp, &this->stats); } #ifdef WITH_ACCOUNTING if (((flag->vp_integer & 0x02) != 0) && ((request->listener->type == RAD_LISTEN_ACCT) || (request->listener->type == RAD_LISTEN_NONE))) { request_stats_addvp(request, acctvp, &this->stats); } #endif } #ifdef WITH_PROXY /* * Home servers. */ if (((flag->vp_integer & 0x80) != 0) && ((flag->vp_integer & 0x03) != 0)) { home_server *home; VALUE_PAIR *server_ip, *server_port; fr_ipaddr_t ipaddr; /* * See if we need to look up the server by socket * socket. */ server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY); if (!server_ip) return; server_port = pairfind(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY); if (!server_port) return; #ifndef NDEBUG memset(&ipaddr, 0, sizeof(ipaddr)); #endif ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr; home = home_server_find(&ipaddr, server_port->vp_integer, IPPROTO_UDP); /* * Not found: don't do anything */ if (!home) return; pairadd(&request->reply->vps, paircopyvp(request->reply, server_ip)); pairadd(&request->reply->vps, paircopyvp(request->reply, server_port)); vp = radius_paircreate(request, &request->reply->vps, 172, VENDORPEC_FREERADIUS); if (vp) vp->vp_integer = home->currently_outstanding; vp = radius_paircreate(request, &request->reply->vps, 173, VENDORPEC_FREERADIUS); if (vp) vp->vp_integer = home->state; if ((home->state == HOME_STATE_ALIVE) && (home->revive_time.tv_sec != 0)) { vp = radius_paircreate(request, &request->reply->vps, 175, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = home->revive_time.tv_sec; } if ((home->state == HOME_STATE_ALIVE) && (home->ema.window > 0)) { vp = radius_paircreate(request, &request->reply->vps, 178, VENDORPEC_FREERADIUS); if (vp) vp->vp_integer = home->ema.window; vp = radius_paircreate(request, &request->reply->vps, 179, VENDORPEC_FREERADIUS); if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE; vp = radius_paircreate(request, &request->reply->vps, 180, VENDORPEC_FREERADIUS); if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE; } if (home->state == HOME_STATE_IS_DEAD) { vp = radius_paircreate(request, &request->reply->vps, 174, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period; } /* * Show more information... * * FIXME: do this for clients, too! */ vp = radius_paircreate(request, &request->reply->vps, 184, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = home->last_packet_recv; vp = radius_paircreate(request, &request->reply->vps, 185, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = home->last_packet_sent; if (((flag->vp_integer & 0x01) != 0) && (home->type == HOME_TYPE_AUTH)) { request_stats_addvp(request, proxy_authvp, &home->stats); } #ifdef WITH_ACCOUNTING if (((flag->vp_integer & 0x02) != 0) && (home->type == HOME_TYPE_ACCT)) { request_stats_addvp(request, proxy_acctvp, &home->stats); } #endif } #endif /* WITH_PROXY */ }
static int do_attr_rewrite(void *instance, REQUEST *request) { rlm_attr_rewrite_t *data = (rlm_attr_rewrite_t *) instance; int ret = RLM_MODULE_NOOP; VALUE_PAIR *attr_vp = NULL; VALUE_PAIR *tmp = NULL; regex_t preg; regmatch_t pmatch[9]; int cflags = 0; int err = 0; char done_xlat = 0; unsigned int len = 0; char err_msg[MAX_STRING_LEN]; unsigned int i = 0; unsigned int j = 0; unsigned int counter = 0; char new_str[MAX_STRING_LEN]; char *ptr, *ptr2; char search_STR[MAX_STRING_LEN]; char replace_STR[MAX_STRING_LEN]; if ((attr_vp = pairfind(request->config_items, PW_REWRITE_RULE, 0, TAG_ANY)) != NULL){ if (data->name == NULL || strcmp(data->name,attr_vp->vp_strvalue)) return RLM_MODULE_NOOP; } if (data->new_attr){ /* new_attribute = yes */ if (!radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL, NULL)) { DEBUG2("%s: xlat on replace string failed.", data->name); return ret; } attr_vp = pairmake(data->attribute,replace_STR,0); if (attr_vp == NULL){ DEBUG2("%s: Could not add new attribute %s with value '%s'", data->name, data->attribute,replace_STR); return ret; } switch(data->searchin){ case RLM_REGEX_INPACKET: pairadd(&request->packet->vps,attr_vp); break; case RLM_REGEX_INCONFIG: pairadd(&request->config_items,attr_vp); break; case RLM_REGEX_INREPLY: pairadd(&request->reply->vps,attr_vp); break; #ifdef WITH_PROXY case RLM_REGEX_INPROXY: if (!request->proxy) { pairbasicfree(attr_vp); return RLM_MODULE_NOOP; } pairadd(&request->proxy->vps, attr_vp); break; case RLM_REGEX_INPROXYREPLY: if (!request->proxy_reply) { pairbasicfree(attr_vp); return RLM_MODULE_NOOP; } pairadd(&request->proxy_reply->vps, attr_vp); break; #endif default: radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", data->name); data->searchin = RLM_REGEX_INPACKET; pairadd(&request->packet->vps,attr_vp); break; } DEBUG2("%s: Added attribute %s with value '%s'", data->name,data->attribute,replace_STR); ret = RLM_MODULE_OK; } else { int replace_len = 0; /* new_attribute = no */ switch (data->searchin) { case RLM_REGEX_INPACKET: if (!data->da->vendor && (data->da->attr == PW_USER_NAME)) attr_vp = request->username; else if (!data->da->vendor && (data->da->attr == PW_USER_PASSWORD)) attr_vp = request->password; else tmp = request->packet->vps; break; case RLM_REGEX_INCONFIG: tmp = request->config_items; break; case RLM_REGEX_INREPLY: tmp = request->reply->vps; break; #ifdef WITH_PROXY case RLM_REGEX_INPROXYREPLY: if (!request->proxy_reply) return RLM_MODULE_NOOP; tmp = request->proxy_reply->vps; break; case RLM_REGEX_INPROXY: if (!request->proxy) return RLM_MODULE_NOOP; tmp = request->proxy->vps; break; #endif default: radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", data->name); data->searchin = RLM_REGEX_INPACKET; attr_vp = pairfind(request->packet->vps, data->da->attr, data->da->vendor, TAG_ANY); break; } do_again: if (tmp != NULL) attr_vp = pairfind(tmp, data->da->attr, data->da->vendor, TAG_ANY); if (attr_vp == NULL) { DEBUG2("%s: Could not find value pair for attribute %s", data->name,data->attribute); return ret; } if (attr_vp->vp_strvalue == NULL || attr_vp->length == 0){ DEBUG2("%s: Attribute %s string value NULL or of zero length", data->name,data->attribute); return ret; } cflags |= REG_EXTENDED; if (data->nocase) cflags |= REG_ICASE; if (!radius_xlat(search_STR, sizeof(search_STR), data->search, request, NULL, NULL) && data->search_len != 0) { DEBUG2("%s: xlat on search string failed.", data->name); return ret; } if ((err = regcomp(&preg,search_STR,cflags))) { regerror(err, &preg, err_msg, MAX_STRING_LEN); DEBUG2("%s: regcomp() returned error: %s", data->name,err_msg); return ret; } if ((attr_vp->type == PW_TYPE_IPADDR) && (attr_vp->vp_strvalue[0] == '\0')) { inet_ntop(AF_INET, &(attr_vp->vp_ipaddr), attr_vp->vp_strvalue, sizeof(attr_vp->vp_strvalue)); } ptr = new_str; ptr2 = attr_vp->vp_strvalue; counter = 0; for ( i = 0 ;i < (unsigned)data->num_matches; i++) { err = regexec(&preg, ptr2, REQUEST_MAX_REGEX, pmatch, 0); if (err == REG_NOMATCH) { if (i == 0) { DEBUG2("%s: Does not match: %s = %s", data->name, data->attribute, attr_vp->vp_strvalue); regfree(&preg); goto to_do_again; } else break; } if (err != 0) { regfree(&preg); radlog(L_ERR, "%s: match failure for attribute %s with value '%s'", data->name, data->attribute, attr_vp->vp_strvalue); return ret; } if (pmatch[0].rm_so == -1) break; len = pmatch[0].rm_so; if (data->append) { len = len + (pmatch[0].rm_eo - pmatch[0].rm_so); } counter += len; if (counter >= MAX_STRING_LEN) { regfree(&preg); DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name, data->attribute, attr_vp->vp_strvalue); return ret; } memcpy(ptr, ptr2,len); ptr += len; *ptr = '\0'; ptr2 += pmatch[0].rm_eo; if (i == 0){ /* * We only run on the first match, sorry */ for(j = 0; j <= REQUEST_MAX_REGEX; j++){ char *p; char buffer[sizeof(attr_vp->vp_strvalue)]; /* * Stolen from src/main/valuepair.c, paircompare() */ /* * Delete old matches if the corresponding match does not * exist in the current regex */ if (pmatch[j].rm_so == -1){ p = request_data_get(request,request,REQUEST_DATA_REGEX | j); if (p){ free(p); continue; } break; } memcpy(buffer, attr_vp->vp_strvalue + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so); buffer[pmatch[j].rm_eo - pmatch[j].rm_so] = '\0'; p = strdup(buffer); request_data_add(request,request,REQUEST_DATA_REGEX | j,p,free); } } if (!done_xlat){ if (data->replace_len != 0 && radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL, NULL) == 0) { DEBUG2("%s: xlat on replace string failed.", data->name); return ret; } replace_len = (data->replace_len != 0) ? strlen(replace_STR) : 0; done_xlat = 1; } counter += replace_len; if (counter >= MAX_STRING_LEN) { regfree(&preg); DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name, data->attribute, attr_vp->vp_strvalue); return ret; } if (replace_len){ memcpy(ptr, replace_STR, replace_len); ptr += replace_len; *ptr = '\0'; } } regfree(&preg); len = strlen(ptr2) + 1; /* We add the ending NULL */ counter += len; if (counter >= MAX_STRING_LEN){ DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name, data->attribute, attr_vp->vp_strvalue); return ret; } memcpy(ptr, ptr2, len); ptr[len] = '\0'; DEBUG2("%s: Changed value for attribute %s from '%s' to '%s'", data->name, data->attribute, attr_vp->vp_strvalue, new_str); if (pairparsevalue(attr_vp, new_str) == NULL) { DEBUG2("%s: Could not write value '%s' into attribute %s: %s", data->name, new_str, data->attribute, fr_strerror()); return ret; } to_do_again: ret = RLM_MODULE_OK; if (tmp != NULL){ tmp = attr_vp->next; if (tmp != NULL) goto do_again; } } return ret; }
/** Move pairs, replacing/over-writing them, and doing xlat. * * Move attributes from one list to the other if not already present. */ void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from) { VALUE_PAIR **tailto, *i, *j, *next; VALUE_PAIR *tailfrom = NULL; VALUE_PAIR *found; /* * Point "tailto" to the end of the "to" list. */ tailto = to; for (i = *to; i; i = i->next) { tailto = &i->next; } /* * Loop over the "from" list. */ for (i = *from; i; i = next) { next = i->next; /* * Don't move 'fallthrough' over. */ if (i->attribute == PW_FALL_THROUGH) { tailfrom = i; continue; } /* * We've got to xlat the string before moving * it over. */ if (i->flags.do_xlat) { int rcode; char buffer[sizeof(i->vp_strvalue)]; i->flags.do_xlat = 0; rcode = radius_xlat(buffer, sizeof(buffer), i->vp_strvalue, req, NULL, NULL); /* * Parse the string into a new value. */ pairparsevalue(i, buffer); } found = pairfind(*to, i->attribute, i->vendor); switch (i->operator) { /* * If a similar attribute is found, * delete it. */ case T_OP_SUB: /* -= */ if (found) { if (!i->vp_strvalue[0] || (strcmp((char *)found->vp_strvalue, (char *)i->vp_strvalue) == 0)) { pairdelete(to, found->attribute, found->vendor, found->flags.tag); /* * 'tailto' may have been * deleted... */ tailto = to; for (j = *to; j; j = j->next) { tailto = &j->next; } } } tailfrom = i; continue; break; /* * Add it, if it's not already there. */ case T_OP_EQ: /* = */ if (found) { tailfrom = i; continue; /* with the loop */ } break; /* * If a similar attribute is found, * replace it with the new one. Otherwise, * add the new one to the list. */ case T_OP_SET: /* := */ if (found) { VALUE_PAIR *vp; vp = found->next; memcpy(found, i, sizeof(*found)); found->next = vp; tailfrom = i; continue; } break; /* * FIXME: Add support for <=, >=, <, > * * which will mean (for integers) * 'make the attribute the smaller, etc' */ /* * Add the new element to the list, even * if similar ones already exist. */ default: case T_OP_ADD: /* += */ break; } if (tailfrom) { tailfrom->next = next; } else { *from = next; } /* * If ALL of the 'to' attributes have been deleted, * then ensure that the 'tail' is updated to point * to the head. */ if (!*to) { tailto = to; } *tailto = i; if (i) { i->next = NULL; tailto = &i->next; } } /* loop over the 'from' list */ }
/* * given a radius request with some attributes in the EAP range, build * them all into a single EAP-Message body. * * Note that this function will build multiple EAP-Message bodies * if there are multiple eligible EAP-types. This is incorrect, as the * recipient will in fact concatenate them. * * XXX - we could break the loop once we process one type. Maybe this * just deserves an assert? * */ static void map_eap_methods(RADIUS_PACKET *req) { VALUE_PAIR *vp, *vpnext; int id, eapcode; eap_packet_t ep; int eap_method; vp = pairfind(req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY); if(!vp) { id = ((int)getpid() & 0xff); } else { id = vp->vp_integer; } vp = pairfind(req->vps, ATTRIBUTE_EAP_CODE, 0, TAG_ANY); if(!vp) { eapcode = PW_EAP_REQUEST; } else { eapcode = vp->vp_integer; } for(vp = req->vps; vp != NULL; vp = vpnext) { /* save it in case it changes! */ vpnext = vp->next; if(vp->da->attribute >= ATTRIBUTE_EAP_BASE && vp->da->attribute < ATTRIBUTE_EAP_BASE+256) { break; } } if(!vp) { return; } eap_method = vp->da->attribute - ATTRIBUTE_EAP_BASE; switch(eap_method) { case PW_EAP_IDENTITY: case PW_EAP_NOTIFICATION: case PW_EAP_NAK: case PW_EAP_MD5: case PW_EAP_OTP: case PW_EAP_GTC: case PW_EAP_TLS: case PW_EAP_LEAP: case PW_EAP_TTLS: case PW_EAP_PEAP: default: /* * no known special handling, it is just encoded as an * EAP-message with the given type. */ /* nuke any existing EAP-Messages */ pairdelete(&req->vps, PW_EAP_MESSAGE, 0, TAG_ANY); memset(&ep, 0, sizeof(ep)); ep.code = eapcode; ep.id = id; ep.type.num = eap_method; ep.type.length = vp->length; ep.type.data = vp->vp_octets; /* no need for copy */ eap_basic_compose(req, &ep); } }
/* * Send one packet. */ static int send_one_packet(rc_request_t *request) { assert(request->done == 0); /* * Remember when we have to wake up, to re-send the * request, of we didn't receive a reply. */ if ((sleep_time == -1) || (sleep_time > (int) timeout)) { sleep_time = (int) timeout; } /* * Haven't sent the packet yet. Initialize it. */ if (request->packet->id == -1) { int i; bool rcode; assert(request->reply == NULL); /* * Didn't find a free packet ID, we're not done, * we don't sleep, and we stop trying to process * this packet. */ retry: request->packet->src_ipaddr.af = server_ipaddr.af; rcode = fr_packet_list_id_alloc(pl, ipproto, &request->packet, NULL); if (!rcode) { int mysockfd; #ifdef WITH_TCP if (proto) { mysockfd = fr_tcp_client_socket(NULL, &server_ipaddr, server_port); } else #endif mysockfd = fr_socket(&client_ipaddr, 0); if (mysockfd < 0) { fprintf(stderr, "radclient: Can't open new socket: %s\n", strerror(errno)); exit(1); } if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, &server_ipaddr, server_port, NULL)) { fprintf(stderr, "radclient: Can't add new socket\n"); exit(1); } goto retry; } assert(request->packet->id != -1); assert(request->packet->data == NULL); for (i = 0; i < 4; i++) { ((uint32_t *) request->packet->vector)[i] = fr_rand(); } /* * Update the password, so it can be encrypted with the * new authentication vector. */ if (request->password[0] != '\0') { VALUE_PAIR *vp; if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) { pairstrcpy(vp, request->password); } else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) { int already_hex = 0; /* * If it's 17 octets, it *might* be already encoded. * Or, it might just be a 17-character password (maybe UTF-8) * Check it for non-printable characters. The odds of ALL * of the characters being 32..255 is (1-7/8)^17, or (1/8)^17, * or 1/(2^51), which is pretty much zero. */ if (vp->length == 17) { for (i = 0; i < 17; i++) { if (vp->vp_octets[i] < 32) { already_hex = 1; break; } } } /* * Allow the user to specify ASCII or hex CHAP-Password */ if (!already_hex) { uint8_t *p; size_t len, len2; len = len2 = strlen(request->password); if (len2 < 17) len2 = 17; p = talloc_zero_array(vp, uint8_t, len2); memcpy(p, request->password, len); rad_chap_encode(request->packet, p, fr_rand() & 0xff, vp); vp->vp_octets = p; vp->length = 17; } } else if (pairfind(request->packet->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY) != NULL) { mschapv1_encode(request->packet, &request->packet->vps, request->password); } else if (fr_debug_flag) { printf("WARNING: No password in the request\n"); } } request->timestamp = time(NULL); request->tries = 1; request->resend++; #ifdef WITH_TCP /* * WTF? */ if (client_port == 0) { client_ipaddr = request->packet->src_ipaddr; client_port = request->packet->src_port; } #endif } else { /* request->packet->id >= 0 */ time_t now = time(NULL); /* * FIXME: Accounting packets are never retried! * The Acct-Delay-Time attribute is updated to * reflect the delay, and the packet is re-sent * from scratch! */ /* * Not time for a retry, do so. */ if ((now - request->timestamp) < timeout) { /* * When we walk over the tree sending * packets, we update the minimum time * required to sleep. */ if ((sleep_time == -1) || (sleep_time > (now - request->timestamp))) { sleep_time = now - request->timestamp; } return 0; } /* * We're not trying later, maybe the packet is done. */ if (request->tries == retries) { assert(request->packet->id >= 0); /* * Delete the request from the tree of * outstanding requests. */ fr_packet_list_yank(pl, request->packet); fprintf(stderr, "radclient: no reply from server for ID %d socket %d\n", request->packet->id, request->packet->sockfd); deallocate_id(request); /* * Normally we mark it "done" when we've received * the reply, but this is a special case. */ if (request->resend == resend_count) { request->done = 1; } totallost++; return -1; } /* * We are trying later. */ request->timestamp = now; request->tries++; } /* * Send the packet. */ if (rad_send(request->packet, NULL, secret) < 0) { fprintf(stderr, "radclient: Failed to send packet for ID %d: %s\n", request->packet->id, fr_strerror()); } if (fr_debug_flag > 2) print_hex(request->packet); return 0; }
main(int argc, char *argv[]) { int filedone; RADIUS_PACKET *req,*req2; VALUE_PAIR *vp, *vpkey, *vpextra; extern unsigned int sha1_data_problems; req = NULL; req2 = NULL; filedone = 0; if(argc>1) { sha1_data_problems = 1; } if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radclient"); return 1; } req = rad_alloc(NULL, 1) if (!req) { fr_perror("radclient"); exit(1); } req2 = rad_alloc(NULL, 1); if (!req2) { fr_perror("radclient"); exit(1); } while(!filedone) { if(req->vps) pairfree(&req->vps); if(req2->vps) pairfree(&req2->vps); if ((req->vps = readvp2(stdin, &filedone, "eapsimlib:")) == NULL) { break; } if (fr_debug_flag > 1) { printf("\nRead:\n"); vp_printlist(stdout, req->vps); } map_eapsim_types(req); map_eap_methods(req); if (fr_debug_flag > 1) { printf("Mapped to:\n"); vp_printlist(stdout, req->vps); } /* find the EAP-Message, copy it to req2 */ vp = paircopy2(NULL, req->vps, PW_EAP_MESSAGE, 0, TAG_ANY); if(!vp) continue; pairadd(&req2->vps, vp); /* only call unmap for sim types here */ unmap_eap_methods(req2); unmap_eapsim_types(req2); if (fr_debug_flag > 1) { printf("Unmapped to:\n"); vp_printlist(stdout, req2->vps); } vp = pairfind(req2->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0, TAG_ANY); vpkey = pairfind(req->vps, ATTRIBUTE_EAP_SIM_KEY, 0, TAG_ANY); vpextra = pairfind(req->vps, ATTRIBUTE_EAP_SIM_EXTRA, 0, TAG_ANY); if(vp != NULL && vpkey != NULL && vpextra!=NULL) { uint8_t calcmac[16]; /* find the EAP-Message, copy it to req2 */ memset(calcmac, 0, sizeof(calcmac)); printf("Confirming MAC..."); if(eapsim_checkmac(req2->vps, vpkey->vp_strvalue, vpextra->vp_strvalue, vpextra->length, calcmac)) { printf("succeed\n"); } else { int i, j; printf("calculated MAC ("); for (i = 0; i < 20; i++) { if(j==4) { printf("_"); j=0; } j++; printf("%02x", calcmac[i]); } printf(" did not match\n"); } } fflush(stdout); } }
/* * Generate the keys after the user has been authenticated. */ static rlm_rcode_t wimax_postauth(void *instance, REQUEST *request) { rlm_wimax_t *inst = instance; VALUE_PAIR *msk, *emsk, *vp; VALUE_PAIR *mn_nai, *ip, *fa_rk; HMAC_CTX hmac; unsigned int rk1_len, rk2_len, rk_len; uint32_t mip_spi; uint8_t usage_data[24]; uint8_t mip_rk_1[EVP_MAX_MD_SIZE], mip_rk_2[EVP_MAX_MD_SIZE]; uint8_t mip_rk[2 * EVP_MAX_MD_SIZE]; msk = pairfind(request->reply->vps, 1129, 0, TAG_ANY); emsk = pairfind(request->reply->vps, 1130, 0, TAG_ANY); if (!msk || !emsk) { RDEBUG("No EAP-MSK or EAP-EMSK. Cannot create WiMAX keys."); return RLM_MODULE_NOOP; } /* * If we delete the MS-MPPE-*-Key attributes, then add in * the WiMAX-MSK so that the client has a key available. */ if (inst->delete_mppe_keys) { pairdelete(&request->reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY); pairdelete(&request->reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY); vp = radius_pairmake(request, &request->reply->vps, "WiMAX-MSK", "0x00", T_OP_EQ); if (vp) { memcpy(vp->vp_octets, msk->vp_octets, msk->length); vp->length = msk->length; } } /* * Initialize usage data. */ memcpy(usage_data, "*****@*****.**", 21); /* with trailing \0 */ usage_data[21] = 0x02; usage_data[22] = 0x00; usage_data[23] = 0x01; /* * MIP-RK-1 = HMAC-SSHA256(EMSK, usage-data | 0x01) */ HMAC_CTX_init(&hmac); HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL); HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data)); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * MIP-RK-2 = HMAC-SSHA256(EMSK, MIP-RK-1 | usage-data | 0x01) */ HMAC_Init_ex(&hmac, emsk->vp_octets, emsk->length, EVP_sha256(), NULL); HMAC_Update(&hmac, (const uint8_t *) &mip_rk_1, rk1_len); HMAC_Update(&hmac, &usage_data[0], sizeof(usage_data)); HMAC_Final(&hmac, &mip_rk_2[0], &rk2_len); memcpy(mip_rk, mip_rk_1, rk1_len); memcpy(mip_rk + rk1_len, mip_rk_2, rk2_len); rk_len = rk1_len + rk2_len; /* * MIP-SPI = HMAC-SSHA256(MIP-RK, "SPI CMIP PMIP"); */ HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha256(), NULL); HMAC_Update(&hmac, (const uint8_t *) "SPI CMIP PMIP", 12); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * Take the 4 most significant octets. * If less than 256, add 256. */ mip_spi = ((mip_rk_1[0] << 24) | (mip_rk_1[1] << 16) | (mip_rk_1[2] << 8) | mip_rk_1[3]); if (mip_spi < 256) mip_spi += 256; if (debug_flag) { int len = rk_len; char buffer[512]; if (len > 128) len = 128; /* buffer size */ fr_bin2hex(mip_rk, buffer, len); radlog_request(L_DBG, 0, request, "MIP-RK = 0x%s", buffer); radlog_request(L_DBG, 0, request, "MIP-SPI = %08x", ntohl(mip_spi)); } /* * FIXME: Perform SPI collision prevention */ /* * Calculate mobility keys */ mn_nai = pairfind(request->packet->vps, 1900, 0, TAG_ANY); if (!mn_nai) mn_nai = pairfind(request->reply->vps, 1900, 0, TAG_ANY); if (!mn_nai) { RDEBUGW("WiMAX-MN-NAI was not found in the request or in the reply."); RDEBUGW("We cannot calculate MN-HA keys."); } /* * WiMAX-IP-Technology */ vp = NULL; if (mn_nai) vp = pairfind(request->reply->vps, 23, VENDORPEC_WIMAX, TAG_ANY); if (!vp) { RDEBUGW("WiMAX-IP-Technology not found in reply."); RDEBUGW("Not calculating MN-HA keys"); } if (vp) switch (vp->vp_integer) { case 2: /* PMIP4 */ /* * Look for WiMAX-hHA-IP-MIP4 */ ip = pairfind(request->reply->vps, 6, VENDORPEC_WIMAX, TAG_ANY); if (!ip) { RDEBUGW("WiMAX-hHA-IP-MIP4 not found. Cannot calculate MN-HA-PMIP4 key"); break; } /* * MN-HA-PMIP4 = * H(MIP-RK, "PMIP4 MN HA" | HA-IPv4 | MN-NAI); */ HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "PMIP4 MN HA", 11); HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4); HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * Put MN-HA-PMIP4 into WiMAX-MN-hHA-MIP4-Key */ vp = pairfind(request->reply->vps, 10, VENDORPEC_WIMAX, TAG_ANY); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, 10, VENDORPEC_WIMAX); } if (!vp) { RDEBUGW("Failed creating WiMAX-MN-hHA-MIP4-Key"); break; } memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len); vp->length = rk1_len; /* * Put MN-HA-PMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI */ vp = pairfind(request->reply->vps, 11, VENDORPEC_WIMAX, TAG_ANY); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, 11, VENDORPEC_WIMAX); } if (!vp) { RDEBUGW("Failed creating WiMAX-MN-hHA-MIP4-SPI"); break; } vp->vp_integer = mip_spi + 1; break; case 3: /* CMIP4 */ /* * Look for WiMAX-hHA-IP-MIP4 */ ip = pairfind(request->reply->vps, 6, VENDORPEC_WIMAX, TAG_ANY); if (!ip) { RDEBUGW("WiMAX-hHA-IP-MIP4 not found. Cannot calculate MN-HA-CMIP4 key"); break; } /* * MN-HA-CMIP4 = * H(MIP-RK, "CMIP4 MN HA" | HA-IPv4 | MN-NAI); */ HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "CMIP4 MN HA", 11); HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipaddr, 4); HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * Put MN-HA-CMIP4 into WiMAX-MN-hHA-MIP4-Key */ vp = pairfind(request->reply->vps, 10, VENDORPEC_WIMAX, TAG_ANY); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, 10, VENDORPEC_WIMAX); } if (!vp) { RDEBUGW("Failed creating WiMAX-MN-hHA-MIP4-Key"); break; } memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len); vp->length = rk1_len; /* * Put MN-HA-CMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI */ vp = pairfind(request->reply->vps, 11, VENDORPEC_WIMAX, TAG_ANY); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, 11, VENDORPEC_WIMAX); } if (!vp) { RDEBUGW("Failed creating WiMAX-MN-hHA-MIP4-SPI"); break; } vp->vp_integer = mip_spi; break; case 4: /* CMIP6 */ /* * Look for WiMAX-hHA-IP-MIP6 */ ip = pairfind(request->reply->vps, 7, VENDORPEC_WIMAX, TAG_ANY); if (!ip) { RDEBUGW("WiMAX-hHA-IP-MIP6 not found. Cannot calculate MN-HA-CMIP6 key"); break; } /* * MN-HA-CMIP6 = * H(MIP-RK, "CMIP6 MN HA" | HA-IPv6 | MN-NAI); */ HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "CMIP6 MN HA", 11); HMAC_Update(&hmac, (const uint8_t *) &ip->vp_ipv6addr, 16); HMAC_Update(&hmac, (const uint8_t *) &mn_nai->vp_strvalue, mn_nai->length); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); /* * Put MN-HA-CMIP6 into WiMAX-MN-hHA-MIP6-Key */ vp = pairfind(request->reply->vps, 12, VENDORPEC_WIMAX, TAG_ANY); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, 12, VENDORPEC_WIMAX); } if (!vp) { RDEBUGW("Failed creating WiMAX-MN-hHA-MIP6-Key"); break; } memcpy(vp->vp_octets, &mip_rk_1[0], rk1_len); vp->length = rk1_len; /* * Put MN-HA-CMIP6-SPI into WiMAX-MN-hHA-MIP6-SPI */ vp = pairfind(request->reply->vps, 13, VENDORPEC_WIMAX, TAG_ANY); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, 13, VENDORPEC_WIMAX); } if (!vp) { RDEBUGW("Failed creating WiMAX-MN-hHA-MIP6-SPI"); break; } vp->vp_integer = mip_spi + 2; break; default: break; /* do nothing */ } /* * Generate FA-RK, if requested. * * FA-RK= H(MIP-RK, "FA-RK") */ fa_rk = pairfind(request->reply->vps, 14, VENDORPEC_WIMAX, TAG_ANY); if (fa_rk && (fa_rk->length <= 1)) { HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL); HMAC_Update(&hmac, (const uint8_t *) "FA-RK", 5); HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len); memcpy(fa_rk->vp_octets, &mip_rk_1[0], rk1_len); fa_rk->length = rk1_len; } /* * Create FA-RK-SPI, which is really SPI-CMIP4, which is * really MIP-SPI. Clear? Of course. This is WiMAX. */ if (fa_rk) { vp = pairfind(request->reply->vps, 61, VENDORPEC_WIMAX, TAG_ANY); if (!vp) { vp = radius_paircreate(request, &request->reply->vps, 61, VENDORPEC_WIMAX); } if (!vp) { RDEBUGW("Failed creating WiMAX-FA-RK-SPI"); } else { vp->vp_integer = mip_spi; } } /* * Give additional information about requests && responses * * WiMAX-RRQ-MN-HA-SPI */ vp = pairfind(request->packet->vps, 20, VENDORPEC_WIMAX, TAG_ANY); if (vp) { RDEBUG("Client requested MN-HA key: Should use SPI to look up key from storage."); if (!mn_nai) { RDEBUGW("MN-NAI was not found!"); } /* * WiMAX-RRQ-HA-IP */ if (!pairfind(request->packet->vps, 18, VENDORPEC_WIMAX, TAG_ANY)) { RDEBUGW("HA-IP was not found!"); } /* * WiMAX-HA-RK-Key-Requested */ vp = pairfind(request->packet->vps, 58, VENDORPEC_WIMAX, TAG_ANY); if (vp && (vp->vp_integer == 1)) { RDEBUG("Client requested HA-RK: Should use IP to look it up from storage."); } } /* * Wipe the context of all sensitive information. */ HMAC_CTX_cleanup(&hmac); return RLM_MODULE_UPDATED; }
/* * we got an EAP-Request/Sim/Challenge message in a legal state. * * use the RAND challenge to produce the SRES result, and then * use that to generate a new MAC. * * for the moment, we ignore the RANDs, then just plug in the SRES * values. * */ static int process_eap_challenge(RADIUS_PACKET *req, RADIUS_PACKET *rep) { VALUE_PAIR *newvp; VALUE_PAIR *mac, *randvp; VALUE_PAIR *sres1,*sres2,*sres3; VALUE_PAIR *Kc1, *Kc2, *Kc3; uint8_t calcmac[20]; /* look for the AT_MAC and the challenge data */ mac = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0, TAG_ANY); randvp= pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_RAND, 0, TAG_ANY); if(!mac || !randvp) { fprintf(stderr, "radeapclient: challenge message needs to contain RAND and MAC\n"); return 0; } /* * compare RAND with randX, to verify this is the right response * to this challenge. */ { VALUE_PAIR *randcfgvp[3]; uint8_t *randcfg[3]; randcfg[0] = &randvp->vp_octets[2]; randcfg[1] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE]; randcfg[2] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE*2]; randcfgvp[0] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND1, 0, TAG_ANY); randcfgvp[1] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND2, 0, TAG_ANY); randcfgvp[2] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND3, 0, TAG_ANY); if(!randcfgvp[0] || !randcfgvp[1] || !randcfgvp[2]) { fprintf(stderr, "radeapclient: needs to have rand1, 2 and 3 set.\n"); return 0; } if(memcmp(randcfg[0], randcfgvp[0]->vp_octets, EAPSIM_RAND_SIZE)!=0 || memcmp(randcfg[1], randcfgvp[1]->vp_octets, EAPSIM_RAND_SIZE)!=0 || memcmp(randcfg[2], randcfgvp[2]->vp_octets, EAPSIM_RAND_SIZE)!=0) { int rnum,i,j; fprintf(stderr, "radeapclient: one of rand 1,2,3 didn't match\n"); for(rnum = 0; rnum < 3; rnum++) { fprintf(stderr, "received rand %d: ", rnum); j=0; for (i = 0; i < EAPSIM_RAND_SIZE; i++) { if(j==4) { printf("_"); j=0; } j++; fprintf(stderr, "%02x", randcfg[rnum][i]); } fprintf(stderr, "\nconfigured rand %d: ", rnum); j=0; for (i = 0; i < EAPSIM_RAND_SIZE; i++) { if(j==4) { printf("_"); j=0; } j++; fprintf(stderr, "%02x", randcfgvp[rnum]->vp_octets[i]); } fprintf(stderr, "\n"); } return 0; } } /* * now dig up the sres values from the response packet, * which were put there when we read things in. * * Really, they should be calculated from the RAND! * */ sres1 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES1, 0, TAG_ANY); sres2 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES2, 0, TAG_ANY); sres3 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES3, 0, TAG_ANY); if(!sres1 || !sres2 || !sres3) { fprintf(stderr, "radeapclient: needs to have sres1, 2 and 3 set.\n"); return 0; } memcpy(eapsim_mk.sres[0], sres1->vp_strvalue, sizeof(eapsim_mk.sres[0])); memcpy(eapsim_mk.sres[1], sres2->vp_strvalue, sizeof(eapsim_mk.sres[1])); memcpy(eapsim_mk.sres[2], sres3->vp_strvalue, sizeof(eapsim_mk.sres[2])); Kc1 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC1, 0, TAG_ANY); Kc2 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC2, 0, TAG_ANY); Kc3 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC3, 0, TAG_ANY); if(!Kc1 || !Kc2 || !Kc3) { fprintf(stderr, "radeapclient: needs to have Kc1, 2 and 3 set.\n"); return 0; } memcpy(eapsim_mk.Kc[0], Kc1->vp_strvalue, sizeof(eapsim_mk.Kc[0])); memcpy(eapsim_mk.Kc[1], Kc2->vp_strvalue, sizeof(eapsim_mk.Kc[1])); memcpy(eapsim_mk.Kc[2], Kc3->vp_strvalue, sizeof(eapsim_mk.Kc[2])); /* all set, calculate keys */ eapsim_calculate_keys(&eapsim_mk); if(debug_flag) { eapsim_dump_mk(&eapsim_mk); } /* verify the MAC, now that we have all the keys. */ if(eapsim_checkmac(NULL, req->vps, eapsim_mk.K_aut, eapsim_mk.nonce_mt, sizeof(eapsim_mk.nonce_mt), calcmac)) { printf("MAC check succeed\n"); } else { int i, j; j=0; printf("calculated MAC ("); for (i = 0; i < 20; i++) { if(j==4) { printf("_"); j=0; } j++; printf("%02x", calcmac[i]); } printf(" did not match\n"); return 0; } /* form new response clear of any EAP stuff */ cleanresp(rep); /* mark the subtype as being EAP-SIM/Response/Start */ newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, 0, PW_TYPE_INTEGER); newvp->vp_integer = eapsim_challenge; pairreplace(&(rep->vps), newvp); /* * fill the SIM_MAC with a field that will in fact get appended * to the packet before the MAC is calculated */ newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0, PW_TYPE_OCTETS); memcpy(newvp->vp_strvalue+EAPSIM_SRES_SIZE*0, sres1->vp_strvalue, EAPSIM_SRES_SIZE); memcpy(newvp->vp_strvalue+EAPSIM_SRES_SIZE*1, sres2->vp_strvalue, EAPSIM_SRES_SIZE); memcpy(newvp->vp_strvalue+EAPSIM_SRES_SIZE*2, sres3->vp_strvalue, EAPSIM_SRES_SIZE); newvp->length = EAPSIM_SRES_SIZE*3; pairreplace(&(rep->vps), newvp); newvp = paircreate(ATTRIBUTE_EAP_SIM_KEY, 0, PW_TYPE_OCTETS); memcpy(newvp->vp_strvalue, eapsim_mk.K_aut, EAPSIM_AUTH_SIZE); newvp->length = EAPSIM_AUTH_SIZE; pairreplace(&(rep->vps), newvp); return 1; }
/* * rad_accounting: call modules. * * The return value of this function isn't actually used right now, so * it's not entirely clear if it is returning the right things. --Pac. */ int rad_accounting(REQUEST *request) { int result = RLM_MODULE_OK; #ifdef WITH_PROXY #define WAS_PROXIED (request->proxy) #else #define WAS_PROXIED (0) #endif /* * Run the modules only once, before proxying. */ if (!WAS_PROXIED) { VALUE_PAIR *vp; int acct_type = 0; result = module_preacct(request); switch (result) { /* * The module has a number of OK return codes. */ case RLM_MODULE_NOOP: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: break; /* * The module handled the request, stop here. */ case RLM_MODULE_HANDLED: return result; /* * The module failed, or said the request is * invalid, therefore we stop here. */ case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_NOTFOUND: case RLM_MODULE_REJECT: case RLM_MODULE_USERLOCK: default: return result; } /* * Do the data storage before proxying. This is to ensure * that we log the packet, even if the proxy never does. */ vp = pairfind(request->config_items, PW_ACCT_TYPE, 0, TAG_ANY); if (vp) { acct_type = vp->vp_integer; DEBUG2(" Found Acct-Type %s", dict_valnamebyattr(PW_ACCT_TYPE, 0, acct_type)); } result = module_accounting(acct_type, request); switch (result) { /* * In case the accounting module returns FAIL, * it's still useful to send the data to the * proxy. */ case RLM_MODULE_FAIL: case RLM_MODULE_NOOP: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: break; /* * The module handled the request, don't reply. */ case RLM_MODULE_HANDLED: return result; /* * Neither proxy, nor reply to invalid requests. */ case RLM_MODULE_INVALID: case RLM_MODULE_NOTFOUND: case RLM_MODULE_REJECT: case RLM_MODULE_USERLOCK: default: return result; } /* * Maybe one of the preacct modules has decided * that a proxy should be used. */ if ((vp = pairfind(request->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY))) { REALM *realm; /* * Check whether Proxy-To-Realm is * a LOCAL realm. */ realm = realm_find2(vp->vp_strvalue); if (realm && !realm->acct_pool) { DEBUG("rad_accounting: Cancelling proxy to realm %s, as it is a LOCAL realm.", realm->name); pairdelete(&request->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY); } else { /* * Don't reply to the NAS now because * we have to send the proxied packet * before that. */ return result; } } } /* * We get here IF we're not proxying, OR if we've * received the accounting reply from the end server, * THEN we can reply to the NAS. * If the accounting module returns NOOP, the data * storage did not succeed, so radiusd should not send * Accounting-Response. */ switch (result) { /* * Send back an ACK to the NAS. */ case RLM_MODULE_OK: case RLM_MODULE_UPDATED: request->reply->code = PW_ACCOUNTING_RESPONSE; break; /* * Failed to log or to proxy the accounting data, * therefore don't reply to the NAS. */ case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: case RLM_MODULE_REJECT: case RLM_MODULE_USERLOCK: default: break; } return result; }
static int respond_eap_md5(RADIUS_PACKET *req, RADIUS_PACKET *rep) { VALUE_PAIR *vp, *id, *state; size_t valuesize, namesize; uint8_t identifier; uint8_t *value; uint8_t *name; FR_MD5_CTX context; uint8_t response[16]; cleanresp(rep); if ((state = paircopy2(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL) { fprintf(stderr, "radeapclient: no state attribute found\n"); return 0; } if ((id = paircopy2(NULL, req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY)) == NULL) { fprintf(stderr, "radeapclient: no EAP-ID attribute found\n"); return 0; } identifier = id->vp_integer; if ((vp = pairfind(req->vps, ATTRIBUTE_EAP_BASE+PW_EAP_MD5, 0, TAG_ANY)) == NULL) { fprintf(stderr, "radeapclient: no EAP-MD5 attribute found\n"); return 0; } /* got the details of the MD5 challenge */ valuesize = vp->vp_octets[0]; value = &vp->vp_octets[1]; name = &vp->vp_octets[valuesize+1]; namesize = vp->length - (valuesize + 1); /* sanitize items */ if(valuesize > vp->length) { fprintf(stderr, "radeapclient: md5 valuesize if too big (%u > %u)\n", (unsigned int) valuesize, (unsigned int) vp->length); return 0; } /* now do the CHAP operation ourself, rather than build the * buffer. We could also call rad_chap_encode, but it wants * a CHAP-Challenge, which we don't want to bother with. */ fr_MD5Init(&context); fr_MD5Update(&context, &identifier, 1); fr_MD5Update(&context, (uint8_t *) password, strlen(password)); fr_MD5Update(&context, value, valuesize); fr_MD5Final(response, &context); vp = paircreate(ATTRIBUTE_EAP_BASE+PW_EAP_MD5, 0, PW_TYPE_OCTETS); vp->vp_octets[0]=16; memcpy(&vp->vp_strvalue[1], response, 16); vp->length = 17; pairreplace(&(rep->vps), vp); pairreplace(&(rep->vps), id); /* copy the state object in */ pairreplace(&(rep->vps), state); return 1; }
int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original) { int i, code, length; VALUE_PAIR *vp; uint8_t *ptr; VALUE_PAIR *vps[VQP_MAX_ATTRIBUTES]; if (!packet) { fr_strerror_printf("Failed encoding VQP"); return -1; } if (packet->data) return 0; vp = pairfind(packet->vps, PW_VQP_PACKET_TYPE); if (!vp) { fr_strerror_printf("Failed to find VQP-Packet-Type in response packet"); return -1; } code = vp->lvalue; if ((code < 1) || (code > 4)) { fr_strerror_printf("Invalid value %d for VQP-Packet-Type", code); return -1; } length = VQP_HDR_LEN; memset(vps, 0, sizeof(vps)); vp = pairfind(packet->vps, PW_VQP_ERROR_CODE); /* * FIXME: Map attributes from calling-station-Id, etc. * * Maybe do this via rlm_vqp? That's probably the * best place to add the code... */ /* * No error: encode attributes. */ if (!vp) for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) { if (!contents[code][i]) break; vps[i] = pairfind(packet->vps, contents[code][i] | 0x2000); /* * FIXME: Print the name... */ if (!vps[i]) { fr_strerror_printf("Failed to find VQP attribute %02x", contents[code][i]); return -1; } length += 6; length += vps[i]->length; } packet->data = malloc(length); if (!packet->data) { fr_strerror_printf("No memory"); return -1; } packet->data_len = length; ptr = packet->data; ptr[0] = VQP_VERSION; ptr[1] = code; if (!vp) { ptr[2] = 0; } else { ptr[2] = vp->lvalue & 0xff; return 0; } /* * The number of attributes is hard-coded. */ if ((code == 1) || (code == 3)) { uint32_t sequence; ptr[3] = VQP_MAX_ATTRIBUTES; sequence = htonl(packet->id); memcpy(ptr + 4, &sequence, 4); } else { if (!original) { fr_strerror_printf("Cannot send VQP response without request"); return -1; } /* * Packet Sequence Number */ memcpy(ptr + 4, original->data + 4, 4); ptr[3] = 2; } ptr += 8; /* * Encode the VP's. */ for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) { if (!vps[i]) break; if ((ptr - packet->data) >= packet->data_len) break; vp = vps[i]; debug_pair(vp); /* * Type. Note that we look at only the lower 8 * bits, as the upper 8 bits have been hacked. * See also dictionary.vqp */ ptr[0] = 0; ptr[1] = 0; ptr[2] = 0x0c; ptr[3] = vp->attribute & 0xff; /* Length */ ptr[4] = 0; ptr[5] = vp->length & 0xff; ptr += 6; /* Data */ switch (vp->type) { case PW_TYPE_IPADDR: memcpy(ptr, &vp->vp_ipaddr, 4); break; default: case PW_TYPE_OCTETS: case PW_TYPE_STRING: memcpy(ptr, vp->vp_octets, vp->length); break; } ptr += vp->length; } return 0; }
/* * member of the radius group? */ static int od_authorize(UNUSED void *instance, REQUEST *request) { char *name = NULL; struct passwd *userdata = NULL; struct group *groupdata = NULL; int ismember = 0; RADCLIENT *rad_client = NULL; uuid_t uuid; uuid_t guid_sacl; uuid_t guid_nasgroup; int err; char host_ipaddr[128] = {0}; if (!request || !request->username) { RDEBUG("OpenDirectory requires a User-Name attribute."); return RLM_MODULE_NOOP; } /* resolve SACL */ uuid_clear(guid_sacl); groupdata = getgrnam(kRadiusSACLName); if (groupdata != NULL) { err = mbr_gid_to_uuid(groupdata->gr_gid, guid_sacl); if (err != 0) { radlog(L_ERR, "rlm_opendirectory: The group \"%s\" does not have a GUID.", kRadiusSACLName); return RLM_MODULE_FAIL; } } else { RDEBUG("The SACL group \"%s\" does not exist on this system.", kRadiusSACLName); } /* resolve client access list */ uuid_clear(guid_nasgroup); rad_client = request->client; #if 0 if (rad_client->community[0] != '\0' ) { /* * The "community" can be a GUID (Globally Unique ID) or * a group name */ if (uuid_parse(rad_client->community, guid_nasgroup) != 0) { /* attempt to resolve the name */ groupdata = getgrnam(rad_client->community); if (groupdata == NULL) { radlog(L_AUTH, "rlm_opendirectory: The group \"%s\" does not exist on this system.", rad_client->community); return RLM_MODULE_FAIL; } err = mbr_gid_to_uuid(groupdata->gr_gid, guid_nasgroup); if (err != 0) { radlog(L_AUTH, "rlm_opendirectory: The group \"%s\" does not have a GUID.", rad_client->community); return RLM_MODULE_FAIL; } } } else #endif { if (rad_client == NULL) { RDEBUG("The client record could not be found for host %s.", ip_ntoh(&request->packet->src_ipaddr, host_ipaddr, sizeof(host_ipaddr))); } else { RDEBUG("The host %s does not have an access group.", ip_ntoh(&request->packet->src_ipaddr, host_ipaddr, sizeof(host_ipaddr))); } } if (uuid_is_null(guid_sacl) && uuid_is_null(guid_nasgroup)) { RDEBUG("no access control groups, all users allowed."); if (pairfind(request->config_items, PW_AUTH_TYPE, 0) == NULL) { pairadd(&request->config_items, pairmake("Auth-Type", kAuthType, T_OP_EQ)); RDEBUG("Setting Auth-Type = %s", kAuthType); } return RLM_MODULE_OK; } /* resolve user */ uuid_clear(uuid); name = (char *)request->username->vp_strvalue; rad_assert(name != NULL); userdata = getpwnam(name); if (userdata != NULL) { err = mbr_uid_to_uuid(userdata->pw_uid, uuid); if (err != 0) uuid_clear(uuid); } if (uuid_is_null(uuid)) { radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", "Could not get the user's uuid", T_OP_EQ); return RLM_MODULE_NOTFOUND; } if (!uuid_is_null(guid_sacl)) { err = mbr_check_service_membership(uuid, kRadiusServiceName, &ismember); if (err != 0) { radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", "Failed to check group membership", T_OP_EQ); return RLM_MODULE_FAIL; } if (ismember == 0) { radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", "User is not authorized", T_OP_EQ); return RLM_MODULE_USERLOCK; } } if (!uuid_is_null(guid_nasgroup)) { err = mbr_check_membership_refresh(uuid, guid_nasgroup, &ismember); if (err != 0) { radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", "Failed to check group membership", T_OP_EQ); return RLM_MODULE_FAIL; } if (ismember == 0) { radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", "User is not authorized", T_OP_EQ); return RLM_MODULE_USERLOCK; } } if (pairfind(request->config_items, PW_AUTH_TYPE, 0) == NULL) { pairadd(&request->config_items, pairmake("Auth-Type", kAuthType, T_OP_EQ)); RDEBUG("Setting Auth-Type = %s", kAuthType); } return RLM_MODULE_OK; }
/* * Find a a previous EAP-Request sent by us, which matches * the current EAP-Response. * * Then, release the handle from the list, and return it to * the caller. * * Also since we fill the eap_ds with the present EAP-Response we * got to free the prev_eapds & move the eap_ds to prev_eapds */ static EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request) { int i; VALUE_PAIR *state; rbnode_t *node; EAP_HANDLER *handler, myHandler; /* * We key the sessions off of the 'state' attribute, so it * must exist. */ state = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY); if (!state || (state->length != EAP_STATE_LEN)) { return NULL; } myHandler.src_ipaddr = request->packet->src_ipaddr; memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state)); /* * Playing with a data structure shared among threads * means that we need a lock, to avoid conflict. */ pthread_mutex_lock(&(inst->session_mutex)); /* * Check the first few handlers in the list, and delete * them if they're too old. We don't need to check them * all, as incoming requests will quickly cause older * handlers to be deleted. * */ for (i = 0; i < 2; i++) { handler = inst->session_head; if (handler && ((request->timestamp - handler->timestamp) > inst->timer_limit)) { node = rbtree_find(inst->session_tree, handler); rad_assert(node != NULL); rbtree_delete(inst->session_tree, node); /* * handler == inst->session_head */ inst->session_head = handler->next; if (handler->next) { handler->next->prev = NULL; } else { inst->session_head = NULL; } eap_handler_free(handler); } } handler = NULL; node = rbtree_find(inst->session_tree, &myHandler); if (node) { handler = rbtree_node2data(inst->session_tree, node); /* * Delete old handler from the tree. */ rbtree_delete(inst->session_tree, node); /* * And unsplice it from the linked list. */ if (handler->prev) { handler->prev->next = handler->next; } else { inst->session_head = handler->next; } if (handler->next) { handler->next->prev = handler->prev; } else { inst->session_tail = handler->prev; } handler->prev = handler->next = NULL; } pthread_mutex_unlock(&(inst->session_mutex)); /* * Not found. */ if (!node) { RDEBUG2("Request not found in the list"); return NULL; } /* * Found, but state verification failed. */ if (!handler) { radlog(L_ERR, "rlm_eap2: State verification failed."); return NULL; } RDEBUG2("Request found, released from the list"); return handler; }
/* * For backwards compatibility. */ static int eap_authenticate(void *instance, REQUEST *request) { rlm_eap_t *inst; EAP_HANDLER *handler; eap_packet_t *eap_packet; int rcode; inst = (rlm_eap_t *) instance; if (!pairfind(request->packet->vps, PW_EAP_MESSAGE, 0)) { RDEBUG("ERROR: You set 'Auth-Type = EAP' for a request that does not contain an EAP-Message attribute!"); return RLM_MODULE_INVALID; } /* * Get the eap packet to start with */ eap_packet = eap_vp2packet(request->packet->vps); if (eap_packet == NULL) { radlog_request(L_ERR, 0, request, "Malformed EAP Message"); return RLM_MODULE_FAIL; } /* * Create the eap handler. The eap_packet will end up being * "swallowed" into the handler, so we can't access it after * this call. */ handler = eap_handler(inst, &eap_packet, request); if (handler == NULL) { RDEBUG2("Failed in handler"); return RLM_MODULE_INVALID; } /* * Select the appropriate eap_type or default to the * configured one */ rcode = eaptype_select(inst, handler); /* * If it failed, die. */ if (rcode == EAP_INVALID) { eap_fail(handler); eap_handler_free(inst, handler); RDEBUG2("Failed in EAP select"); return RLM_MODULE_INVALID; } #ifdef WITH_PROXY /* * If we're doing horrible tunneling work, remember it. */ if ((request->options & RAD_REQUEST_OPTION_PROXY_EAP) != 0) { RDEBUG2(" Not-EAP proxy set. Not composing EAP"); /* * Add the handle to the proxied list, so that we * can retrieve it in the post-proxy stage, and * send a response. */ handler->inst_holder = inst; rcode = request_data_add(request, inst, REQUEST_DATA_EAP_HANDLER, handler, (void *) eap_opaque_free); rad_assert(rcode == 0); return RLM_MODULE_HANDLED; } #endif #ifdef WITH_PROXY /* * Maybe the request was marked to be proxied. If so, * proxy it. */ if (request->proxy != NULL) { VALUE_PAIR *vp = NULL; rad_assert(request->proxy_reply == NULL); /* * Add the handle to the proxied list, so that we * can retrieve it in the post-proxy stage, and * send a response. */ handler->inst_holder = inst; rcode = request_data_add(request, inst, REQUEST_DATA_EAP_HANDLER, handler, (void *) eap_opaque_free); rad_assert(rcode == 0); /* * Some simple sanity checks. These should really * be handled by the radius library... */ vp = pairfind(request->proxy->vps, PW_EAP_MESSAGE, 0); if (vp) { vp = pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0); if (!vp) { vp = pairmake("Message-Authenticator", "0x00", T_OP_EQ); rad_assert(vp != NULL); pairadd(&(request->proxy->vps), vp); } } /* * Delete the "proxied to" attribute, as it's * set to 127.0.0.1 for tunneled requests, and * we don't want to tell the world that... */ pairdelete(&request->proxy->vps, PW_FREERADIUS_PROXIED_TO, VENDORPEC_FREERADIUS); RDEBUG2(" Tunneled session will be proxied. Not doing EAP."); return RLM_MODULE_HANDLED; } #endif /* * We are done, wrap the EAP-request in RADIUS to send * with all other required radius attributes */ rcode = eap_compose(handler); /* * Add to the list only if it is EAP-Request, OR if * it's LEAP, and a response. */ if (((handler->eap_ds->request->code == PW_EAP_REQUEST) && (handler->eap_ds->request->type.type >= PW_EAP_MD5)) || /* * LEAP is a little different. At Stage 4, * it sends an EAP-Success message, but we still * need to keep the State attribute & session * data structure around for the AP Challenge. * * At stage 6, LEAP sends an EAP-Response, which * isn't put into the list. */ ((handler->eap_ds->response->code == PW_EAP_RESPONSE) && (handler->eap_ds->response->type.type == PW_EAP_LEAP) && (handler->eap_ds->request->code == PW_EAP_SUCCESS) && (handler->eap_ds->request->type.type == 0))) { /* * Return FAIL if we can't remember the handler. * This is actually disallowed by the * specification, as unexpected FAILs could have * been forged. However, we want to signal to * everyone else involved that we are * intentionally failing the session, as opposed * to accidentally failing it. */ if (!eaplist_add(inst, handler)) { eap_fail(handler); eap_handler_free(inst, handler); return RLM_MODULE_FAIL; } } else { RDEBUG2("Freeing handler"); /* handler is not required any more, free it now */ eap_handler_free(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) { VALUE_PAIR *vp; /* * Doesn't exist, add it in. */ vp = pairfind(request->reply->vps, PW_USER_NAME, 0); if (!vp) { vp = pairmake("User-Name", "", T_OP_EQ); strlcpy(vp->vp_strvalue, request->username->vp_strvalue, sizeof(vp->vp_strvalue)); vp->length = request->username->length; 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++; } } return rcode; }
/* * Handles multiple EAP-Message attrs * ie concatenates all to get the complete EAP packet. * * NOTE: Sometimes Framed-MTU might contain the length of EAP-Message, * refer fragmentation in rfc2869. */ static int eap_vp2data(VALUE_PAIR *vps, void **data, int *data_len) { VALUE_PAIR *first, *vp; unsigned char *ptr; uint16_t len; int total_len; /* * Get only EAP-Message attribute list */ first = pairfind(vps, PW_EAP_MESSAGE, 0, TAG_ANY); if (first == NULL) { radlog(L_ERR, "rlm_eap2: EAP-Message not found"); return -1; } /* * Sanity check the length before doing anything. */ if (first->length < 4) { radlog(L_ERR, "rlm_eap2: EAP packet is too short."); return -1; } /* * Get the Actual length from the EAP packet * First EAP-Message contains the EAP packet header */ memcpy(&len, first->vp_strvalue + 2, sizeof(len)); len = ntohs(len); /* * Take out even more weird things. */ if (len < 4) { radlog(L_ERR, "rlm_eap2: EAP packet has invalid length."); return -1; } /* * Sanity check the length, BEFORE malloc'ing memory. */ total_len = 0; for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE, 0, TAG_ANY)) { total_len += vp->length; if (total_len > len) { radlog(L_ERR, "rlm_eap2: Malformed EAP packet. Length in packet header does not match actual length"); return -1; } } /* * If the length is SMALLER, die, too. */ if (total_len < len) { radlog(L_ERR, "rlm_eap2: Malformed EAP packet. Length in packet header does not match actual length"); return -1; } /* * Now that we know the lengths are OK, allocate memory. */ *data = malloc(len); if (!*data) { radlog(L_ERR, "rlm_eap2: out of memory"); return -1; } *data_len = len; /* * Copy the data from EAP-Message's over to our EAP packet. */ ptr = *data; /* RADIUS ensures order of attrs, so just concatenate all */ for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE, 0, TAG_ANY)) { memcpy(ptr, vp->vp_strvalue, vp->length); ptr += vp->length; } return 0; }
/* * If we're proxying EAP, then there may be magic we need * to do. */ static int eap_post_proxy(void *inst, REQUEST *request) { size_t i; size_t len; VALUE_PAIR *vp; EAP_HANDLER *handler; /* * Just in case the admin lists EAP in post-proxy-type Fail. */ if (!request->proxy_reply) return RLM_MODULE_NOOP; /* * If there was a handler associated with this request, * then it's a tunneled request which was proxied... */ handler = request_data_get(request, inst, REQUEST_DATA_EAP_HANDLER); if (handler != NULL) { int rcode; eap_tunnel_data_t *data; /* * Grab the tunnel callbacks from the request. */ data = (eap_tunnel_data_t *) request_data_get(request, request->proxy, REQUEST_DATA_EAP_TUNNEL_CALLBACK); if (!data) { radlog_request(L_ERR, 0, request, "Failed to retrieve callback for tunneled session!"); eap_handler_free(inst, handler); return RLM_MODULE_FAIL; } /* * Do the callback... */ RDEBUG2("Doing post-proxy callback"); rcode = data->callback(handler, data->tls_session); free(data); if (rcode == 0) { RDEBUG2("Failed in post-proxy callback"); eap_fail(handler); eap_handler_free(inst, handler); return RLM_MODULE_REJECT; } /* * We are done, wrap the EAP-request in RADIUS to send * with all other required radius attributes */ eap_compose(handler); /* * Add to the list only if it is EAP-Request, OR if * it's LEAP, and a response. */ if ((handler->eap_ds->request->code == PW_EAP_REQUEST) && (handler->eap_ds->request->type.type >= PW_EAP_MD5)) { if (!eaplist_add(inst, handler)) { eap_fail(handler); eap_handler_free(inst, handler); return RLM_MODULE_FAIL; } } else { /* couldn't have been LEAP, there's no tunnel */ RDEBUG2("Freeing handler"); /* handler is not required any more, free it now */ eap_handler_free(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); if (!vp) { vp = pairmake("User-Name", request->username->vp_strvalue, T_OP_EQ); rad_assert(vp != NULL); pairadd(&(request->reply->vps), vp); } } return RLM_MODULE_OK; } else { RDEBUG2("No pre-existing handler found"); } /* * There may be more than one Cisco-AVPair. * Ensure we find the one with the LEAP attribute. */ vp = request->proxy_reply->vps; for (;;) { /* * Hmm... there's got to be a better way to * discover codes for vendor attributes. * * This is vendor Cisco (9), Cisco-AVPair * attribute (1) */ vp = pairfind(vp, 1, 9); if (!vp) { return RLM_MODULE_NOOP; } /* * If it's "leap:session-key", then stop. * * The format is VERY specific! */ if (strncasecmp(vp->vp_strvalue, "leap:session-key=", 17) == 0) { break; } /* * Not this AV-pair. Go to the next one. */ vp = vp->next; } /* * The format is very specific. */ if (vp->length != 17 + 34) { RDEBUG2("Cisco-AVPair with leap:session-key has incorrect length %d: Expected %d", vp->length, 17 + 34); return RLM_MODULE_NOOP; } /* * Decrypt the session key, using the proxy data. */ i = 34; /* starts off with 34 octets */ len = rad_tunnel_pwdecode(vp->vp_octets + 17, &i, request->home_server->secret, request->proxy->vector); /* * FIXME: Assert that i == 16. */ /* * Encrypt the session key again, using the request data. */ rad_tunnel_pwencode(vp->vp_strvalue + 17, &len, request->client->secret, request->packet->vector); return RLM_MODULE_UPDATED; }
/* * 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; }
/* * Call the function_name inside the module * Store all vps in hashes %RAD_CHECK %RAD_REPLY %RAD_REQUEST * */ static int rlmperl_call(void *instance, REQUEST *request, char *function_name) { PERL_INST *inst = instance; VALUE_PAIR *vp; int exitstatus=0, count; STRLEN n_a; HV *rad_reply_hv; HV *rad_check_hv; HV *rad_config_hv; HV *rad_request_hv; HV *rad_request_proxy_hv; HV *rad_request_proxy_reply_hv; #ifdef USE_ITHREADS PerlInterpreter *interp; interp = rlm_perl_clone(inst->perl); { dTHXa(interp); PERL_SET_CONTEXT(interp); } #else PERL_SET_CONTEXT(inst->perl); #endif { dSP; ENTER; SAVETMPS; /* * Radius has told us to call this function, but none * is defined. */ if (!function_name) { return RLM_MODULE_FAIL; } rad_reply_hv = get_hv("RAD_REPLY",1); rad_check_hv = get_hv("RAD_CHECK",1); rad_config_hv = get_hv("RAD_CONFIG",1); rad_request_hv = get_hv("RAD_REQUEST",1); rad_request_proxy_hv = get_hv("RAD_REQUEST_PROXY",1); rad_request_proxy_reply_hv = get_hv("RAD_REQUEST_PROXY_REPLY",1); perl_store_vps(request->reply->vps, rad_reply_hv); perl_store_vps(request->config_items, rad_check_hv); perl_store_vps(request->packet->vps, rad_request_hv); perl_store_vps(request->config_items, rad_config_hv); if (request->proxy != NULL) { perl_store_vps(request->proxy->vps, rad_request_proxy_hv); } else { hv_undef(rad_request_proxy_hv); } if (request->proxy_reply !=NULL) { perl_store_vps(request->proxy_reply->vps, rad_request_proxy_reply_hv); } else { hv_undef(rad_request_proxy_reply_hv); } PUSHMARK(SP); /* * This way %RAD_xx can be pushed onto stack as sub parameters. * XPUSHs( newRV_noinc((SV *)rad_request_hv) ); * XPUSHs( newRV_noinc((SV *)rad_reply_hv) ); * XPUSHs( newRV_noinc((SV *)rad_check_hv) ); * PUTBACK; */ count = call_pv(function_name, G_SCALAR | G_EVAL | G_NOARGS); SPAGAIN; if (SvTRUE(ERRSV)) { radlog(L_ERR, "rlm_perl: perl_embed:: module = %s , func = %s exit status= %s\n", inst->module, function_name, SvPV(ERRSV,n_a)); POPs; } if (count == 1) { exitstatus = POPi; if (exitstatus >= 100 || exitstatus < 0) { exitstatus = RLM_MODULE_FAIL; } } PUTBACK; FREETMPS; LEAVE; vp = NULL; if ((get_hv_content(rad_request_hv, &vp)) > 0 ) { pairfree(&request->packet->vps); request->packet->vps = vp; vp = NULL; /* * Update cached copies */ request->username = pairfind(request->packet->vps, PW_USER_NAME); request->password = pairfind(request->packet->vps, PW_USER_PASSWORD); if (!request->password) request->password = pairfind(request->packet->vps, PW_CHAP_PASSWORD); } if ((get_hv_content(rad_reply_hv, &vp)) > 0 ) { pairfree(&request->reply->vps); request->reply->vps = vp; vp = NULL; } if ((get_hv_content(rad_check_hv, &vp)) > 0 ) { pairfree(&request->config_items); request->config_items = vp; vp = NULL; } if (request->proxy && (get_hv_content(rad_request_proxy_hv, &vp) > 0)) { pairfree(&request->proxy->vps); request->proxy->vps = vp; vp = NULL; } if (request->proxy_reply && (get_hv_content(rad_request_proxy_reply_hv, &vp) > 0)) { pairfree(&request->proxy_reply->vps); request->proxy_reply->vps = vp; vp = NULL; } } return exitstatus; }
static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void * instance, REQUEST *request) { VALUE_PAIR *authtype, *challenge, *response, *password; uint8_t buffer[64]; password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY); if(!password) { AUTH("rlm_cram: Cleartext-Password is required for authentication"); return RLM_MODULE_INVALID; } authtype = pairfind(request->packet->vps, SM_AUTHTYPE, VENDORPEC_SM, TAG_ANY); if(!authtype) { AUTH("rlm_cram: Required attribute Sandy-Mail-Authtype missed"); return RLM_MODULE_INVALID; } challenge = pairfind(request->packet->vps, SM_CHALLENGE, VENDORPEC_SM, TAG_ANY); if(!challenge) { AUTH("rlm_cram: Required attribute Sandy-Mail-Challenge missed"); return RLM_MODULE_INVALID; } response = pairfind(request->packet->vps, SM_RESPONSE, VENDORPEC_SM, TAG_ANY); if(!response) { AUTH("rlm_cram: Required attribute Sandy-Mail-Response missed"); return RLM_MODULE_INVALID; } switch(authtype->vp_integer){ case 2: /* CRAM-MD5 */ if(challenge->length < 5 || response->length != 16) { AUTH("rlm_cram: invalid MD5 challenge/response length"); return RLM_MODULE_INVALID; } calc_md5_digest(buffer, challenge->vp_octets, challenge->length, password->vp_strvalue); if(!memcmp(buffer, response->vp_octets, 16)) return RLM_MODULE_OK; break; case 3: /* APOP */ if(challenge->length < 5 || response->length != 16) { AUTH("rlm_cram: invalid APOP challenge/response length"); return RLM_MODULE_INVALID; } calc_apop_digest(buffer, challenge->vp_octets, challenge->length, password->vp_strvalue); if(!memcmp(buffer, response->vp_octets, 16)) return RLM_MODULE_OK; break; case 8: /* CRAM-MD4 */ if(challenge->length < 5 || response->length != 16) { AUTH("rlm_cram: invalid MD4 challenge/response length"); return RLM_MODULE_INVALID; } calc_md4_digest(buffer, challenge->vp_octets, challenge->length, password->vp_strvalue); if(!memcmp(buffer, response->vp_octets, 16)) return RLM_MODULE_OK; break; case 9: /* CRAM-SHA1 */ if(challenge->length < 5 || response->length != 20) { AUTH("rlm_cram: invalid MD4 challenge/response length"); return RLM_MODULE_INVALID; } calc_sha1_digest(buffer, challenge->vp_octets, challenge->length, password->vp_strvalue); if(!memcmp(buffer, response->vp_octets, 20)) return RLM_MODULE_OK; break; default: AUTH("rlm_cram: unsupported Sandy-Mail-Authtype"); return RLM_MODULE_INVALID; } return RLM_MODULE_NOTFOUND; }