static size_t rand_xlat(void *instance, REQUEST *request, char *fmt, char *out, size_t outlen, RADIUS_ESCAPE_STRING func) { int64_t result; rlm_expr_t *inst = instance; char buffer[256]; inst = inst; /* -Wunused */ /* * Do an xlat on the provided string (nice recursive operation). */ if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) { radlog(L_ERR, "rlm_expr: xlat failed."); return 0; } result = atoi(buffer); /* * Too small or too big. */ if (result <= 0) return 0; if (result >= (1 << 30)) result = (1 << 30); result *= fr_rand(); /* 0..2^32-1 */ result >>= 32; snprintf(out, outlen, "%ld", (long int) result); return strlen(out); }
/** * @brief Generate a random integer value * */ static size_t rand_xlat(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { int64_t result; char buffer[256]; /* * Do an xlat on the provided string (nice recursive operation). */ if (!radius_xlat(buffer, sizeof(buffer), fmt, request, NULL, NULL)) { RDEBUGE("xlat failed."); *out = '\0'; return 0; } result = atoi(buffer); /* * Too small or too big. */ if (result <= 0) return 0; if (result >= (1 << 30)) result = (1 << 30); result *= fr_rand(); /* 0..2^32-1 */ result >>= 32; snprintf(out, outlen, "%ld", (long int) result); return strlen(out); }
/** Generate some random bytes * * @param rnd_data Buffer to write bytes to. * @param len Number of bytes to write. */ void otp_get_random(uint8_t *rnd_data, size_t len) { size_t bytes_read = 0; size_t bytes_left; int n; while (bytes_read < len) { bytes_left = len - bytes_read; uint32_t r = fr_rand(); n = sizeof(r) < bytes_left ? sizeof(r) : bytes_left; memcpy(rnd_data + bytes_read, &r, n); bytes_read += n; } }
/** * @brief Generate a random integer value * */ static size_t rand_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt, char *out, size_t outlen) { int64_t result; result = atoi(fmt); /* * Too small or too big. */ if (result <= 0) return 0; if (result >= (1 << 30)) result = (1 << 30); result *= fr_rand(); /* 0..2^32-1 */ result >>= 32; snprintf(out, outlen, "%ld", (long int) result); return strlen(out); }
static int mschapv1_encode(RADIUS_PACKET *packet, VALUE_PAIR **request, char const *password) { unsigned int i; uint8_t *p; VALUE_PAIR *challenge, *reply; uint8_t nthash[16]; fr_pair_delete_by_num(&packet->vps, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY); fr_pair_delete_by_num(&packet->vps, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY); challenge = fr_pair_afrom_num(packet, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT); if (!challenge) { return 0; } fr_pair_add(request, challenge); challenge->vp_length = 8; challenge->vp_octets = p = talloc_array(challenge, uint8_t, challenge->vp_length); for (i = 0; i < challenge->vp_length; i++) { p[i] = fr_rand(); } reply = fr_pair_afrom_num(packet, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT); if (!reply) { return 0; } fr_pair_add(request, reply); reply->vp_length = 50; reply->vp_octets = p = talloc_array(reply, uint8_t, reply->vp_length); memset(p, 0, reply->vp_length); p[1] = 0x01; /* NT hash */ if (mschap_ntpwdhash(nthash, password) < 0) { return 0; } smbdes_mschap(nthash, challenge->vp_octets, p + 26); return 1; }
static int mschapv1_encode(RADIUS_PACKET *packet, VALUE_PAIR **request, char const *password) { unsigned int i; uint8_t *p; VALUE_PAIR *challenge, *response; uint8_t nthash[16]; challenge = paircreate(packet, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT); if (!challenge) { fprintf(stderr, "GOT IT %d!\n", __LINE__); return 0; } pairadd(request, challenge); challenge->length = 8; challenge->vp_octets = p = talloc_array(challenge, uint8_t, challenge->length); for (i = 0; i < challenge->length; i++) { p[i] = fr_rand(); } response = paircreate(packet, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT); if (!response) { fprintf(stderr, "GOT IT %d!\n", __LINE__); return 0; } pairadd(request, response); response->length = 50; response->vp_octets = p = talloc_array(response, uint8_t, response->length); memset(p, 0, response->length); p[1] = 0x01; /* NT hash */ mschap_ntpwdhash(nthash, password); smbdes_mschap(nthash, challenge->vp_octets, p + 26); return 1; }
static int mschapv1_encode(VALUE_PAIR **request, const char *password) { unsigned int i; VALUE_PAIR *challenge, *response; uint8_t nthash[16]; challenge = paircreate(PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, PW_TYPE_OCTETS); if (!challenge) { fprintf(stderr, "GOT IT %d!\n", __LINE__); return 0; } pairadd(request, challenge); challenge->length = 8; for (i = 0; i < challenge->length; i++) { challenge->vp_octets[i] = fr_rand(); } response = paircreate(PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, PW_TYPE_OCTETS); if (!response) { fprintf(stderr, "GOT IT %d!\n", __LINE__); return 0; } pairadd(request, response); response->length = 50; memset(response->vp_octets, 0, response->length); response->vp_octets[1] = 0x01; /* NT hash */ mschap_ntpwdhash(nthash, password); smbdes_mschap(nthash, challenge->vp_octets, response->vp_octets + 26); return 1; }
/* * 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 const *versions; uint16_t 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 const *) 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 const *)(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(rep, ATTRIBUTE_EAP_SIM_SUBTYPE, 0); newvp->vp_integer = eapsim_start; pairreplace(&(rep->vps), newvp); /* insert selected version into response. */ { uint16_t no_versions; no_versions = htons(selectedversion); newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE + PW_EAP_SIM_SELECTED_VERSION, 0); pairmemcpy(newvp, (uint8_t *) &no_versions, 2); pairreplace(&(rep->vps), newvp); /* record the selected version */ memcpy(eapsim_mk.versionselect, &no_versions, 2); } vp = newvp = NULL; { uint32_t nonce[4]; uint8_t *p; /* * insert a nonce_mt that we make up. */ nonce[0]=fr_rand(); nonce[1]=fr_rand(); nonce[2]=fr_rand(); nonce[3]=fr_rand(); newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT, 0); p = talloc_zero_array(newvp, uint8_t, 18); /* 18 = 16 bytes of nonce + padding */ memcpy(&p[2], nonce, 16); pairmemsteal(newvp, p); pairreplace(&(rep->vps), newvp); /* also keep a copy of the nonce! */ memcpy(eapsim_mk.nonce_mt, nonce, 16); } { uint16_t idlen; uint8_t *p; uint16_t no_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(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_IDENTITY, 0); idlen = strlen(vp->vp_strvalue); p = talloc_zero_array(newvp, uint8_t, idlen + 2); no_idlen = htons(idlen); memcpy(p, &no_idlen, 2); memcpy(p + 2, vp->vp_strvalue, idlen); pairmemsteal(newvp, p); pairreplace(&(rep->vps), newvp); /* record it */ memcpy(eapsim_mk.identity, vp->vp_strvalue, idlen); eapsim_mk.identitylen = idlen; } return 1; }
/* * Write accounting information to this modules database. */ static int replicate_packet(void *instance, REQUEST *request) { int rcode = RLM_MODULE_NOOP; VALUE_PAIR *vp, *last; home_server *home; REALM *realm; home_pool_t *pool; RADIUS_PACKET *packet = NULL; instance = instance; /* -Wunused */ last = request->config_items; /* * Send as many packets as necessary to different * destinations. */ while (1) { vp = pairfind(last, PW_REPLICATE_TO_REALM, 0); if (!vp) break; last = vp->next; realm = realm_find2(vp->vp_strvalue); if (!realm) { RDEBUG2("ERROR: Cannot Replicate to unknown realm %s", realm); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: RDEBUG2("ERROR: Cannot replicate unknown packet code %d", request->packet->code); cleanup(packet); return RLM_MODULE_FAIL; case PW_AUTHENTICATION_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_COA_REQUEST: case PW_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RDEBUG2(" WARNING: Cancelling replication to Realm %s, as the realm is local.", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { RDEBUG2("ERROR: Failed to find live home server for realm %s", realm->name); continue; } if (!packet) { packet = rad_alloc(1); if (!packet) return RLM_MODULE_FAIL; packet->sockfd = -1; packet->code = request->packet->code; packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { RDEBUG("ERROR: Failed opening socket: %s", fr_strerror()); cleanup(packet); return RLM_MODULE_FAIL; } packet->vps = paircopy(request->packet->vps); if (!packet->vps) { RDEBUG("ERROR: Out of memory!"); cleanup(packet); return RLM_MODULE_FAIL; } /* * For CHAP, create the CHAP-Challenge if * it doesn't exist. */ if ((request->packet->code == PW_AUTHENTICATION_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0) == NULL)) { vp = radius_paircreate(request, &packet->vps, PW_CHAP_CHALLENGE, 0, PW_TYPE_OCTETS); vp->length = AUTH_VECTOR_LEN; memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN); } } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; free(packet->data); packet->data = NULL; packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating packet to Realm %s", realm->name); if (rad_send(packet, NULL, home->secret) < 0) { RDEBUG("ERROR: Failed replicating packet: %s", fr_strerror()); cleanup(packet); return RLM_MODULE_FAIL; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } cleanup(packet); return rcode; }
static int mod_session_init (void *instance, eap_handler_t *handler) { pwd_session_t *session; eap_pwd_t *inst = (eap_pwd_t *)instance; VALUE_PAIR *vp; pwd_id_packet_t *packet; if (!inst || !handler) { ERROR("rlm_eap_pwd: Initiate, NULL data provided"); return 0; } /* * make sure the server's been configured properly */ if (!inst->server_id) { ERROR("rlm_eap_pwd: Server ID is not configured"); return 0; } switch (inst->group) { case 19: case 20: case 21: case 25: case 26: break; default: ERROR("rlm_eap_pwd: Group is not supported"); return 0; } if ((session = talloc_zero(handler, pwd_session_t)) == NULL) return 0; talloc_set_destructor(session, _free_pwd_session); /* * set things up so they can be free'd reliably */ session->group_num = inst->group; session->private_value = NULL; session->peer_scalar = NULL; session->my_scalar = NULL; session->k = NULL; session->my_element = NULL; session->peer_element = NULL; session->group = NULL; session->pwe = NULL; session->order = NULL; session->prime = NULL; /* * The admin can dynamically change the MTU. */ session->mtu = inst->fragment_size; vp = fr_pair_find_by_num(handler->request->packet->vps, PW_FRAMED_MTU, 0, TAG_ANY); /* * session->mtu is *our* MTU. We need to subtract off the EAP * overhead. * * 9 = 4 (EAPOL header) + 4 (EAP header) + 1 (EAP type) * * The fragmentation code deals with the included length * so we don't need to subtract that here. */ if (vp && (vp->vp_integer > 100) && (vp->vp_integer < session->mtu)) { session->mtu = vp->vp_integer - 9; } session->state = PWD_STATE_ID_REQ; session->in = NULL; session->out_pos = 0; handler->opaque = session; /* * construct an EAP-pwd-ID/Request */ session->out_len = sizeof(pwd_id_packet_t) + strlen(inst->server_id); if ((session->out = talloc_zero_array(session, uint8_t, session->out_len)) == NULL) { return 0; } packet = (pwd_id_packet_t *)session->out; packet->group_num = htons(session->group_num); packet->random_function = EAP_PWD_DEF_RAND_FUN; packet->prf = EAP_PWD_DEF_PRF; session->token = fr_rand(); memcpy(packet->token, (char *)&session->token, 4); packet->prep = EAP_PWD_PREP_NONE; memcpy(packet->identity, inst->server_id, session->out_len - sizeof(pwd_id_packet_t) ); handler->stage = PROCESS; return send_pwd_request(session, handler->eap_ds); }
/* * mschap_authenticate() - authenticate user based on given * attributes and configuration. * We will try to find out password in configuration * or in configured passwd file. * If one is found we will check paraneters given by NAS. * * If PW_SMB_ACCOUNT_CTRL is not set to ACB_PWNOTREQ we must have * one of: * PAP: PW_USER_PASSWORD or * MS-CHAP: PW_MSCHAP_CHALLENGE and PW_MSCHAP_RESPONSE or * MS-CHAP2: PW_MSCHAP_CHALLENGE and PW_MSCHAP2_RESPONSE * In case of password mismatch or locked account we MAY return * PW_MSCHAP_ERROR for MS-CHAP or MS-CHAP v2 * If MS-CHAP2 succeeds we MUST return * PW_MSCHAP2_SUCCESS */ static int mschap_authenticate(void * instance, REQUEST *request) { #define inst ((rlm_mschap_t *)instance) VALUE_PAIR *challenge = NULL; VALUE_PAIR *response = NULL; VALUE_PAIR *password = NULL; VALUE_PAIR *lm_password, *nt_password, *smb_ctrl; VALUE_PAIR *username; uint8_t nthashhash[16]; char msch2resp[42]; char *username_string; int chap = 0; int do_ntlm_auth; /* * If we have ntlm_auth configured, use it unless told * otherwise */ do_ntlm_auth = (inst->ntlm_auth != NULL); /* * If we have an ntlm_auth configuration, then we may * want to suppress it. */ if (do_ntlm_auth) { VALUE_PAIR *vp = pairfind(request->config_items, PW_MS_CHAP_USE_NTLM_AUTH); if (vp) do_ntlm_auth = vp->vp_integer; } /* * Find the SMB-Account-Ctrl attribute, or the * SMB-Account-Ctrl-Text attribute. */ smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL); if (!smb_ctrl) { password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL_TEXT); if (password) { smb_ctrl = radius_pairmake(request, &request->config_items, "SMB-Account-CTRL", "0", T_OP_SET); if (smb_ctrl) { smb_ctrl->vp_integer = pdb_decode_acct_ctrl(password->vp_strvalue); } } } /* * We're configured to do MS-CHAP authentication. * and account control information exists. Enforce it. */ if (smb_ctrl) { /* * Password is not required. */ if ((smb_ctrl->vp_integer & ACB_PWNOTREQ) != 0) { RDEBUG2("SMB-Account-Ctrl says no password is required."); return RLM_MODULE_OK; } } /* * Decide how to get the passwords. */ password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD); /* * We need an LM-Password. */ lm_password = pairfind(request->config_items, PW_LM_PASSWORD); if (lm_password) { /* * Allow raw octets. */ if ((lm_password->length == 16) || ((lm_password->length == 32) && (fr_hex2bin(lm_password->vp_strvalue, lm_password->vp_octets, 16) == 16))) { RDEBUG2("Found LM-Password"); lm_password->length = 16; } else { radlog_request(L_ERR, 0, request, "Invalid LM-Password"); lm_password = NULL; } } else if (!password) { if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create LM-Password."); } else { /* there is a configured Cleartext-Password */ lm_password = radius_pairmake(request, &request->config_items, "LM-Password", "", T_OP_EQ); if (!lm_password) { radlog_request(L_ERR, 0, request, "No memory"); } else { smbdes_lmpwdhash(password->vp_strvalue, lm_password->vp_octets); lm_password->length = 16; } } /* * We need an NT-Password. */ nt_password = pairfind(request->config_items, PW_NT_PASSWORD); if (nt_password) { if ((nt_password->length == 16) || ((nt_password->length == 32) && (fr_hex2bin(nt_password->vp_strvalue, nt_password->vp_octets, 16) == 16))) { RDEBUG2("Found NT-Password"); nt_password->length = 16; } else { radlog_request(L_ERR, 0, request, "Invalid NT-Password"); nt_password = NULL; } } else if (!password) { if (!do_ntlm_auth) RDEBUG2("No Cleartext-Password configured. Cannot create NT-Password."); } else { /* there is a configured Cleartext-Password */ nt_password = radius_pairmake(request, &request->config_items, "NT-Password", "", T_OP_EQ); if (!nt_password) { radlog_request(L_ERR, 0, request, "No memory"); return RLM_MODULE_FAIL; } else { mschap_ntpwdhash(nt_password->vp_octets, password->vp_strvalue); nt_password->length = 16; } } challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE); if (!challenge) { RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!"); return RLM_MODULE_REJECT; } /* * We also require an MS-CHAP-Response. */ response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE); /* * MS-CHAP-Response, means MS-CHAPv1 */ if (response) { int offset; /* * MS-CHAPv1 challenges are 8 octets. */ if (challenge->length < 8) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format."); return RLM_MODULE_INVALID; } /* * Responses are 50 octets. */ if (response->length < 50) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return RLM_MODULE_INVALID; } /* * We are doing MS-CHAP. Calculate the MS-CHAP * response */ if (response->vp_octets[1] & 0x01) { RDEBUG2("Client is using MS-CHAPv1 with NT-Password"); password = nt_password; offset = 26; } else { RDEBUG2("Client is using MS-CHAPv1 with LM-Password"); password = lm_password; offset = 2; } /* * Do the MS-CHAP authentication. */ if (do_mschap(inst, request, password, challenge->vp_octets, response->vp_octets + offset, nthashhash, do_ntlm_auth) < 0) { RDEBUG2("MS-CHAP-Response is incorrect."); goto do_error; } chap = 1; } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE)) != NULL) { uint8_t mschapv1_challenge[16]; VALUE_PAIR *name_attr, *response_name; /* * MS-CHAPv2 challenges are 16 octets. */ if (challenge->length < 16) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Challenge has the wrong format."); return RLM_MODULE_INVALID; } /* * Responses are 50 octets. */ if (response->length < 50) { radlog_request(L_AUTH, 0, request, "MS-CHAP-Response has the wrong format."); return RLM_MODULE_INVALID; } /* * We also require a User-Name */ username = pairfind(request->packet->vps, PW_USER_NAME); if (!username) { radlog_request(L_AUTH, 0, request, "We require a User-Name for MS-CHAPv2"); return RLM_MODULE_INVALID; } /* * Check for MS-CHAP-User-Name and if found, use it * to construct the MSCHAPv1 challenge. This is * set by rlm_eap_mschap to the MS-CHAP Response * packet Name field. * * We prefer this to the User-Name in the * packet. */ response_name = pairfind(request->packet->vps, PW_MS_CHAP_USER_NAME); if (response_name) { name_attr = response_name; } else { name_attr = username; } /* * with_ntdomain_hack moved here, too. */ if ((username_string = strchr(name_attr->vp_strvalue, '\\')) != NULL) { if (inst->with_ntdomain_hack) { username_string++; } else { RDEBUG2("NT Domain delimeter found, should we have enabled with_ntdomain_hack?"); username_string = name_attr->vp_strvalue; } } else { username_string = name_attr->vp_strvalue; } if (response_name && ((username->length != response_name->length) || (strncasecmp(username->vp_strvalue, response_name->vp_strvalue, username->length) != 0))) { RDEBUG("ERROR: User-Name (%s) is not the same as MS-CHAP Name (%s) from EAP-MSCHAPv2", username->vp_strvalue, response_name->vp_strvalue); return RLM_MODULE_REJECT; } /* * The old "mschapv2" function has been moved to * here. * * MS-CHAPv2 takes some additional data to create an * MS-CHAPv1 challenge, and then does MS-CHAPv1. */ RDEBUG2("Creating challenge hash with username: %s", username_string); mschap_challenge_hash(response->vp_octets + 2, /* peer challenge */ challenge->vp_octets, /* our challenge */ username_string, /* user name */ mschapv1_challenge); /* resulting challenge */ RDEBUG2("Client is using MS-CHAPv2 for %s, we need NT-Password", username_string); #ifdef __APPLE__ if (inst->open_directory) { return do_od_mschap(request, response, challenge, username_string); } #endif if (do_mschap(inst, request, nt_password, mschapv1_challenge, response->vp_octets + 26, nthashhash, do_ntlm_auth) < 0) { int i; char buffer[128]; RDEBUG2("FAILED: MS-CHAP2-Response is incorrect"); do_error: snprintf(buffer, sizeof(buffer), "E=691 R=%d", inst->allow_retry); if (inst->retry_msg) { snprintf(buffer + 9, sizeof(buffer) - 9, " C="); for (i = 0; i < 16; i++) { snprintf(buffer + 12 + i*2, sizeof(buffer) - 12 - i*2, "%02x", fr_rand() & 0xff); } snprintf(buffer + 44, sizeof(buffer) - 44, " V=3 M=%s", inst->retry_msg); } mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", buffer, strlen(buffer)); return RLM_MODULE_REJECT; } mschap_auth_response(username_string, /* without the domain */ nthashhash, /* nt-hash-hash */ response->vp_octets + 26, /* peer response */ response->vp_octets + 2, /* peer challenge */ challenge->vp_octets, /* our challenge */ msch2resp); /* calculated MPPE key */ mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP2-Success", msch2resp, 42); chap = 2; } else { /* Neither CHAPv1 or CHAPv2 response: die */ RDEBUG("ERROR: You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!"); return RLM_MODULE_INVALID; } /* * We have a CHAP response, but the account may be * disabled. Reject the user with the same error code * we use when their password is invalid. */ if (smb_ctrl) { /* * Account is disabled. * * They're found, but they don't exist, so we * return 'not found'. */ if (((smb_ctrl->vp_integer & ACB_DISABLED) != 0) || ((smb_ctrl->vp_integer & (ACB_NORMAL|ACB_WSTRUST)) == 0)) { RDEBUG2("SMB-Account-Ctrl says that the account is disabled, or is not a normal or workstatin trust account."); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=691 R=1", 9); return RLM_MODULE_NOTFOUND; } /* * User is locked out. */ if ((smb_ctrl->vp_integer & ACB_AUTOLOCK) != 0) { RDEBUG2("SMB-Account-Ctrl says that the account is locked out."); mschap_add_reply(request, &request->reply->vps, *response->vp_octets, "MS-CHAP-Error", "E=647 R=0", 9); return RLM_MODULE_USERLOCK; } } /* now create MPPE attributes */ if (inst->use_mppe) { uint8_t mppe_sendkey[34]; uint8_t mppe_recvkey[34]; if (chap == 1){ RDEBUG2("adding MS-CHAPv1 MPPE keys"); memset(mppe_sendkey, 0, 32); if (lm_password) { memcpy(mppe_sendkey, lm_password->vp_octets, 8); } /* * According to RFC 2548 we * should send NT hash. But in * practice it doesn't work. * Instead, we should send nthashhash * * This is an error on RFC 2548. */ /* * do_mschap cares to zero nthashhash if NT hash * is not available. */ memcpy(mppe_sendkey + 8, nthashhash, 16); mppe_add_reply(request, "MS-CHAP-MPPE-Keys", mppe_sendkey, 32); } else if (chap == 2) { RDEBUG2("adding MS-CHAPv2 MPPE keys"); mppe_chap2_gen_keys128(nthashhash, response->vp_octets + 26, mppe_sendkey, mppe_recvkey); mppe_add_reply(request, "MS-MPPE-Recv-Key", mppe_recvkey, 16); mppe_add_reply(request, "MS-MPPE-Send-Key", mppe_sendkey, 16); } radius_pairmake(request, &request->reply->vps, "MS-MPPE-Encryption-Policy", (inst->require_encryption)? "0x00000002":"0x00000001", T_OP_EQ); radius_pairmake(request, &request->reply->vps, "MS-MPPE-Encryption-Types", (inst->require_strong)? "0x00000004":"0x00000006", T_OP_EQ); } /* else we weren't asked to use MPPE */ return RLM_MODULE_OK; #undef inst }
/** Authenticate a previously sent challenge * */ static rlm_rcode_t mod_process(UNUSED void *instance, eap_session_t *eap_session) { REQUEST *request = eap_session->request; eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t); fr_sim_decode_ctx_t ctx = { .keys = &eap_sim_session->keys, }; VALUE_PAIR *subtype_vp, *from_peer, *vp; fr_cursor_t cursor; eap_sim_subtype_t subtype; int ret; /* * VPS is the data from the client */ from_peer = eap_session->request->packet->vps; fr_cursor_init(&cursor, &request->packet->vps); fr_cursor_tail(&cursor); ret = fr_sim_decode(eap_session->request, &cursor, dict_eap_sim, eap_session->this_round->response->type.data, eap_session->this_round->response->type.length, &ctx); /* * RFC 4186 says we *MUST* notify, not just * send an EAP-Failure in this case where * we cannot decode an EAP-AKA packet. */ if (ret < 0) { RPEDEBUG2("Failed decoding EAP-SIM attributes"); eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION); return RLM_MODULE_HANDLED; /* We need to process more packets */ } vp = fr_cursor_current(&cursor); if (vp && RDEBUG_ENABLED2) { RDEBUG2("Decoded EAP-SIM attributes"); log_request_pair_list(L_DBG_LVL_2, request, vp, NULL); } subtype_vp = fr_pair_find_by_da(from_peer, attr_eap_sim_subtype, TAG_ANY); if (!subtype_vp) { REDEBUG("Missing EAP-SIM-Subtype"); eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION); return RLM_MODULE_HANDLED; /* We need to process more packets */ } subtype = subtype_vp->vp_uint16; switch (eap_sim_session->state) { /* * Response to our advertised versions and request for an ID * This is very similar to Identity negotiation in EAP-AKA['] */ case EAP_SIM_SERVER_START: switch (subtype) { case EAP_SIM_START: if (process_eap_sim_start(eap_session, from_peer) == 0) return RLM_MODULE_HANDLED; eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION); return RLM_MODULE_HANDLED; /* We need to process more packets */ /* * Case 1 where we're allowed to send an EAP-Failure * * This can happen in the case of a conservative * peer, where it refuses to provide the permanent * identity. */ case EAP_SIM_CLIENT_ERROR: { char buff[20]; vp = fr_pair_find_by_da(from_peer, attr_eap_sim_client_error_code, TAG_ANY); if (!vp) { REDEBUG("EAP-SIM Peer rejected SIM-Start (%s) with client-error message but " "has not supplied a client error code", fr_int2str(sim_id_request_table, eap_sim_session->id_req, "<INVALID>")); } else { REDEBUG("Client rejected SIM-Start (%s) with error: %s (%i)", fr_int2str(sim_id_request_table, eap_sim_session->id_req, "<INVALID>"), fr_pair_value_enum(vp, buff), vp->vp_uint16); } eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE); return RLM_MODULE_REJECT; } case EAP_SIM_NOTIFICATION: notification: { char buff[20]; vp = fr_pair_afrom_da(from_peer, attr_eap_sim_notification); if (!vp) { REDEBUG2("Received SIM-Notification with no notification code"); eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION); return RLM_MODULE_HANDLED; /* We need to process more packets */ } /* * Case 2 where we're allowed to send an EAP-Failure */ if (!(vp->vp_uint16 & 0x8000)) { REDEBUG2("SIM-Notification %s (%i) indicates failure", fr_pair_value_enum(vp, buff), vp->vp_uint16); eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE); return RLM_MODULE_REJECT; } /* * ...if it's not a failure, then re-enter the * current state. */ REDEBUG2("Got SIM-Notification %s (%i)", fr_pair_value_enum(vp, buff), vp->vp_uint16); eap_sim_state_enter(eap_session, eap_sim_session->state); return RLM_MODULE_HANDLED; default: unexpected_subtype: /* * RFC 4186 says we *MUST* notify, not just * send an EAP-Failure in this case. */ REDEBUG("Unexpected subtype %pV", &subtype_vp->data); eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION); return RLM_MODULE_HANDLED; /* We need to process more packets */ } } /* * Process the response to our previous challenge. */ case EAP_SIM_SERVER_CHALLENGE: switch (subtype) { /* * A response to our EAP-Sim/Request/Challenge! */ case EAP_SIM_CHALLENGE: switch (process_eap_sim_challenge(eap_session, from_peer)) { case 1: return RLM_MODULE_HANDLED; case 0: return RLM_MODULE_OK; case -1: eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION); return RLM_MODULE_HANDLED; /* We need to process more packets */ } case EAP_SIM_CLIENT_ERROR: { char buff[20]; vp = fr_pair_find_by_da(from_peer, attr_eap_sim_client_error_code, TAG_ANY); if (!vp) { REDEBUG("EAP-SIM Peer rejected SIM-Challenge with client-error message but " "has not supplied a client error code"); } else { REDEBUG("Client rejected SIM-Challenge with error: %s (%i)", fr_pair_value_enum(vp, buff), vp->vp_uint16); } eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE); return RLM_MODULE_REJECT; } case EAP_SIM_NOTIFICATION: goto notification; default: goto unexpected_subtype; } /* * Peer acked our failure */ case EAP_SIM_SERVER_FAILURE_NOTIFICATION: switch (subtype) { case EAP_SIM_NOTIFICATION: RDEBUG2("SIM-Notification ACKed, sending EAP-Failure"); eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE); return RLM_MODULE_REJECT; default: goto unexpected_subtype; } /* * Something bad happened... */ default: rad_assert(0); eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION); return RLM_MODULE_HANDLED; /* We need to process more packets */ } } /* * Initiate the EAP-SIM session by starting the state machine * and initiating the state. */ static rlm_rcode_t mod_session_init(void *instance, eap_session_t *eap_session) { REQUEST *request = eap_session->request; eap_sim_session_t *eap_sim_session; rlm_eap_sim_t *inst = instance; fr_sim_id_type_t type; fr_sim_method_hint_t method; MEM(eap_sim_session = talloc_zero(eap_session, eap_sim_session_t)); eap_session->opaque = eap_sim_session; /* * Set default configuration, we may allow these * to be toggled by attributes later. */ eap_sim_session->send_result_ind = inst->protected_success; eap_sim_session->id_req = SIM_ANY_ID_REQ; /* Set the default */ /* * This value doesn't have be strong, but it is * good if it is different now and then. */ eap_sim_session->sim_id = (fr_rand() & 0xff); /* * Save the keying material, because it could change on a subsequent retrieval. */ RDEBUG2("New EAP-SIM session"); /* * Process the identity that we received in the * EAP-Identity-Response and use it to determine * the initial request we send to the Supplicant. */ if (fr_sim_id_type(&type, &method, eap_session->identity, talloc_array_length(eap_session->identity) - 1) < 0) { RPWDEBUG2("Failed parsing identity, continuing anyway"); } switch (method) { default: RWDEBUG("EAP-Identity-Response hints that EAP-%s should be started, but we're attempting EAP-SIM", fr_int2str(sim_id_method_hint_table, method, "<INVALID>")); break; case SIM_METHOD_HINT_SIM: case SIM_METHOD_HINT_UNKNOWN: break; } eap_session->process = mod_process; /* * Figure out what type of identity we have * and use it to determine the initial * request we send. */ switch (type) { /* * These types need to be transformed into something * usable before we can do anything. */ case SIM_ID_TYPE_UNKNOWN: case SIM_ID_TYPE_PSEUDONYM: case SIM_ID_TYPE_FASTAUTH: /* * Permanent ID means we can just send the challenge */ case SIM_ID_TYPE_PERMANENT: eap_sim_session->keys.identity_len = talloc_array_length(eap_session->identity) - 1; MEM(eap_sim_session->keys.identity = talloc_memdup(eap_sim_session, eap_session->identity, eap_sim_session->keys.identity_len)); eap_sim_state_enter(eap_session, EAP_SIM_SERVER_START); return RLM_MODULE_HANDLED; } return RLM_MODULE_HANDLED; }
static int eap_sim_get_challenge(eap_handler_t *handler, VALUE_PAIR *vps, int idx, eap_sim_state_t *ess) { REQUEST *request = handler->request; VALUE_PAIR *vp, *ki, *algo_version; rad_assert(idx >= 0 && idx < 3); /* * Generate a new RAND value, and derive Kc and SRES from Ki */ ki = pairfind(vps, PW_EAP_SIM_KI, 0, TAG_ANY); if (ki) { int i; /* * Check to see if have a Ki for the IMSI, this allows us to generate the rest * of the triplets. */ algo_version = pairfind(vps, PW_EAP_SIM_ALGO_VERSION, 0, TAG_ANY); if (!algo_version) { REDEBUG("Found Ki, but missing EAP-Sim-Algo-Version"); return 0; } for (i = 0; i < EAPSIM_RAND_SIZE; i++) { ess->keys.rand[idx][i] = fr_rand(); } switch (algo_version->vp_integer) { case 1: comp128v1(ess->keys.sres[idx], ess->keys.Kc[idx], ki->vp_octets, ess->keys.rand[idx]); break; case 2: comp128v23(ess->keys.sres[idx], ess->keys.Kc[idx], ki->vp_octets, ess->keys.rand[idx], true); break; case 3: comp128v23(ess->keys.sres[idx], ess->keys.Kc[idx], ki->vp_octets, ess->keys.rand[idx], false); break; case 4: REDEBUG("Comp128-4 algorithm is not supported as details have not yet been published. " "If you have details of this algorithm please contact the FreeRADIUS " "maintainers"); return 0; default: REDEBUG("Unknown/unsupported algorithm Comp128-%i", algo_version->vp_integer); } if (RDEBUG_ENABLED2) { char buffer[33]; /* 32 hexits (16 bytes) + 1 */ char *p; RDEBUG2("Generated following triplets for round %i:", idx); p = buffer; for (i = 0; i < EAPSIM_RAND_SIZE; i++) { p += sprintf(p, "%02x", ess->keys.rand[idx][i]); } RDEBUG2("\tRAND : 0x%s", buffer); p = buffer; for (i = 0; i < EAPSIM_SRES_SIZE; i++) { p += sprintf(p, "%02x", ess->keys.sres[idx][i]); } RDEBUG2("\tSRES : 0x%s", buffer); p = buffer; for (i = 0; i < EAPSIM_KC_SIZE; i++) { p += sprintf(p, "%02x", ess->keys.Kc[idx][i]); } RDEBUG2("\tKc : 0x%s", buffer); } return 1; } /* * Use known RAND, SRES, and Kc values, these may of been pulled in from an AuC, * or created by sending challenges to the SIM directly. */ vp = pairfind(vps, PW_EAP_SIM_RAND1 + idx, 0, TAG_ANY); if (!vp) { /* bad, we can't find stuff! */ REDEBUG("control:EAP-SIM-RAND%i not found", idx + 1); return 0; } if (vp->length != EAPSIM_RAND_SIZE) { REDEBUG("control:EAP-SIM-RAND%i is not " STRINGIFY(EAPSIM_RAND_SIZE) " bytes, got %zu bytes", idx + 1, vp->length); return 0; } memcpy(ess->keys.rand[idx], vp->vp_octets, EAPSIM_RAND_SIZE); vp = pairfind(vps, PW_EAP_SIM_SRES1 + idx, 0, TAG_ANY); if (!vp) { /* bad, we can't find stuff! */ REDEBUG("control:EAP-SIM-SRES%i not found", idx + 1); return 0; } if (vp->length != EAPSIM_SRES_SIZE) { REDEBUG("control:EAP-SIM-SRES%i is not " STRINGIFY(EAPSIM_SRES_SIZE) " bytes, got %zu bytes", idx + 1, vp->length); return 0; } memcpy(ess->keys.sres[idx], vp->vp_octets, EAPSIM_SRES_SIZE); vp = pairfind(vps, PW_EAP_SIM_KC1 + idx, 0, TAG_ANY); if (!vp) { /* bad, we can't find stuff! */ REDEBUG("control:EAP-SIM-Kc%i not found", idx + 1); return 0; } if (vp->length != EAPSIM_KC_SIZE) { REDEBUG("control:EAP-SIM-Kc%i is not 8 bytes, got %zu bytes", idx + 1, vp->length); return 0; } memcpy(ess->keys.Kc[idx], vp->vp_octets, EAPSIM_KC_SIZE); if (vp->length != EAPSIM_KC_SIZE) { REDEBUG("control:EAP-SIM-Kc%i is not " STRINGIFY(EAPSIM_KC_SIZE) " bytes, got %zu bytes", idx + 1, vp->length); return 0; } memcpy(ess->keys.Kc[idx], vp->vp_strvalue, EAPSIM_KC_SIZE); return 1; }
/** Copy packet to multiple servers * * Create a duplicate of the packet and send it to a list of realms * defined by the presence of the Replicate-To-Realm VP in the control * list of the current request. * * This is pretty hacky and is 100% fire and forget. If you're looking * to forward authentication requests to multiple realms and process * the responses, this function will not allow you to do that. * * @param[in] instance of this module. * @param[in] request The current request. * @param[in] list of attributes to copy to the duplicate packet. * @param[in] code to write into the code field of the duplicate packet. * @return RCODE fail on error, invalid if list does not exist, noop if no replications succeeded, else ok. */ static rlm_rcode_t replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, PW_CODE code) { int rcode = RLM_MODULE_NOOP; bool pass1 = true; vp_cursor_t cursor; VALUE_PAIR *vp; RADIUS_PACKET *packet = NULL; rcode = rlm_replicate_alloc(&packet, request, list, code); if (rcode != RLM_MODULE_OK) { return rcode; } /* * Send as many packets as necessary to different destinations. */ fr_cursor_init(&cursor, &request->config_items); while ((vp = fr_cursor_next_by_num(&cursor, PW_REPLICATE_TO_REALM, 0, TAG_ANY))) { home_server_t *home; REALM *realm; home_pool_t *pool; realm = realm_find2(vp->vp_strvalue); if (!realm) { REDEBUG2("Cannot Replicate to unknown realm \"%s\"", vp->vp_strvalue); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: REDEBUG2("Cannot replicate unknown packet code %d", request->packet->code); rcode = RLM_MODULE_FAIL; goto done; case PW_CODE_ACCESS_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_CODE_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_CODE_COA_REQUEST: case PW_CODE_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RWDEBUG2("Cancelling replication to Realm %s, as the realm is local", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { REDEBUG2("Failed to find live home server for realm %s", realm->name); continue; } /* * For replication to multiple servers we re-use the packet * we built here. */ if (pass1) { packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { REDEBUG("Failed opening socket: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } pass1 = false; } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; TALLOC_FREE(packet->data); packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "<INVALID>"), realm->name); if (rad_send(packet, NULL, home->secret) < 0) { REDEBUG("Failed replicating packet: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } done: talloc_free(packet); return rcode; }
/* * Initiate the EAP-MSCHAPV2 session by sending a challenge to the peer. */ static int mschapv2_initiate(void *type_data, EAP_HANDLER *handler) { int i; VALUE_PAIR *challenge; mschapv2_opaque_t *data; type_data = type_data; /* -Wunused */ challenge = pairmake("MS-CHAP-Challenge", "0x00", T_OP_EQ); if (!challenge) { radlog(L_ERR, "rlm_eap_mschapv2: out of memory"); return 0; } /* * Get a random challenge. */ challenge->length = MSCHAPV2_CHALLENGE_LEN; for (i = 0; i < MSCHAPV2_CHALLENGE_LEN; i++) { challenge->vp_strvalue[i] = fr_rand(); } DEBUG2("rlm_eap_mschapv2: Issuing Challenge"); /* * Keep track of the challenge. */ data = malloc(sizeof(mschapv2_opaque_t)); rad_assert(data != NULL); /* * We're at the stage where we're challenging the user. */ data->code = PW_EAP_MSCHAPV2_CHALLENGE; memcpy(data->challenge, challenge->vp_strvalue, MSCHAPV2_CHALLENGE_LEN); data->mppe_keys = NULL; data->reply = NULL; handler->opaque = data; handler->free_opaque = free_data; /* * Compose the EAP-MSCHAPV2 packet out of the data structure, * and free it. */ eapmschapv2_compose(handler, challenge); pairfree(&challenge); #ifdef WITH_PROXY /* * The EAP session doesn't have enough information to * proxy the "inside EAP" protocol. Disable EAP proxying. */ handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; #endif /* * We don't need to authorize the user at this point. * * We also don't need to keep the challenge, as it's * stored in 'handler->eap_ds', which will be given back * to us... */ handler->stage = AUTHENTICATE; return 1; }
/* * Initiate the EAP-MD5 session by sending a challenge to the peer. */ static int md5_initiate(void *type_data, EAP_HANDLER *handler) { int i; MD5_PACKET *reply; type_data = type_data; /* -Wunused */ /* * Allocate an EAP-MD5 packet. */ reply = eapmd5_alloc(); if (reply == NULL) { radlog(L_ERR, "rlm_eap_md5: out of memory"); return 0; } /* * Fill it with data. */ reply->code = PW_MD5_CHALLENGE; reply->length = 1 + MD5_CHALLENGE_LEN; /* one byte of value size */ reply->value_size = MD5_CHALLENGE_LEN; /* * Allocate user data. */ reply->value = malloc(reply->value_size); if (reply->value == NULL) { radlog(L_ERR, "rlm_eap_md5: out of memory"); eapmd5_free(&reply); return 0; } /* * Get a random challenge. */ for (i = 0; i < reply->value_size; i++) { reply->value[i] = fr_rand(); } DEBUG2("rlm_eap_md5: Issuing Challenge"); /* * Keep track of the challenge. */ handler->opaque = malloc(reply->value_size); rad_assert(handler->opaque != NULL); memcpy(handler->opaque, reply->value, reply->value_size); handler->free_opaque = free; /* * Compose the EAP-MD5 packet out of the data structure, * and free it. */ eapmd5_compose(handler->eap_ds, reply); /* * We don't need to authorize the user at this point. * * We also don't need to keep the challenge, as it's * stored in 'handler->eap_ds', which will be given back * to us... */ handler->stage = AUTHENTICATE; return 1; }
/** Send a message on the "best" channel. * * @param nr the network * @param cd the message we've received */ static bool fr_network_send_request(fr_network_t *nr, fr_channel_data_t *cd) { fr_network_worker_t *worker; fr_channel_data_t *reply; (void) talloc_get_type_abort(nr, fr_network_t); if (nr->num_workers == 1) { worker = nr->workers[0]; } else { uint32_t one, two; if (nr->num_workers == 2) { one = 0; two = 1; } else { one = fr_rand() % nr->num_workers; do { two = fr_rand() % nr->num_workers; } while (two == one); } if (nr->workers[one]->cpu_time < nr->workers[two]->cpu_time) { worker = nr->workers[one]; } else { worker = nr->workers[two]; } } (void) talloc_get_type_abort(worker, fr_network_worker_t); /* * Send the message to the channel. If we fail, drop the * packet. The only reason for failure is that the * worker isn't servicing it's input queue. When that * happens, we have no idea what to do, and the whole * thing falls over. */ if (fr_channel_send_request(worker->channel, cd, &reply) < 0) { worker->stats.dropped++; return false; } worker->stats.in++; /* * We're projecting that the worker will use more CPU * time to process this request. The CPU time will be * updated with a more accurate number when we receive a * reply from this channel. */ worker->cpu_time += worker->predicted; /* * If we have a reply, push it onto our local queue, and * poll for more replies. */ if (reply) fr_network_drain_input(nr, worker->channel, reply); return true; }
/* * Send one packet. */ static int send_one_packet(rc_request_t *request) { assert(request->done == false); /* * 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) { ERROR("Can't open new socket: %s", strerror(errno)); exit(1); } if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, &server_ipaddr, server_port, NULL)) { ERROR("Can't add new socket"); 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) { bool already_hex = false; /* * 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 = true; 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 { DEBUG("WARNING: No password in the request"); } } 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); REDEBUG("No reply from server for ID %d socket %d", 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 = true; } stats.lost++; return -1; } /* * We are trying later. */ request->timestamp = now; request->tries++; } /* * Send the packet. */ if (rad_send(request->packet, NULL, secret) < 0) { REDEBUG("Failed to send packet for ID %d", request->packet->id); } return 0; }
/** * @brief Generate a string of random chars * * Build strings of random chars, useful for generating tokens and passcodes * Format similar to String::Random. */ static size_t randstr_xlat(UNUSED void *instance, REQUEST *request, char *fmt, char *out, size_t outlen, RADIUS_ESCAPE_STRING func) { char *p; char buffer[1024]; unsigned int result; size_t freespace = outlen; size_t len; if (outlen <= 1) return 0; /* * Do an xlat on the provided string (nice recursive operation). */ len = radius_xlat(buffer, sizeof(buffer), fmt, request, func); if (!len) { radlog(L_ERR, "rlm_expr: xlat failed."); *out = '\0'; return 0; } p = buffer; while ((len-- > 0) && (--freespace > 0)) { result = fr_rand(); switch (*p) { /* * Lowercase letters */ case 'c': *out++ = 'a' + (result % 26); break; /* * Uppercase letters */ case 'C': *out++ = 'A' + (result % 26); break; /* * Numbers */ case 'n': *out++ = '0' + (result % 10); break; /* * Alpha numeric */ case 'a': *out++ = randstr_salt[result % (sizeof(randstr_salt) - 3)]; break; /* * Punctuation */ case '!': *out++ = randstr_punc[result % (sizeof(randstr_punc) - 1)]; break; /* * Alpa numeric + punctuation */ case '.': *out++ = '!' + (result % 95); break; /* * Alpha numeric + salt chars './' */ case 's': *out++ = randstr_salt[result % (sizeof(randstr_salt) - 1)]; break; /* * Binary data as hexits (we don't really support * non printable chars). */ case 'h': if (freespace < 2) break; snprintf(out, 3, "%02x", result % 256); /* Already decremented */ freespace -= 1; out += 2; break; default: radlog(L_ERR, "rlm_expr: invalid character class '%c'", *p); return 0; break; } p++; } *out++ = '\0'; return outlen - freespace; }
static int vector_umts_from_ki(eap_session_t *eap_session, VALUE_PAIR *vps, fr_sim_keys_t *keys) { REQUEST *request = eap_session->request; VALUE_PAIR *ki_vp, *amf_vp, *sqn_vp, *version_vp; uint64_t sqn; uint8_t amf_buff[MILENAGE_AMF_SIZE] = { 0x00, 0x00 }; uint8_t opc_buff[MILENAGE_OPC_SIZE]; uint8_t const *opc_p; uint32_t version = 4; int i; ki_vp = fr_pair_find_by_da(vps, attr_sim_ki, TAG_ANY); if (!ki_vp) { RDEBUG3("No &control:%s found, not generating quintuplets locally", attr_sim_ki->name); return 1; } else if (ki_vp->vp_length != MILENAGE_KI_SIZE) { REDEBUG("&control:%s has incorrect length, expected %u bytes got %zu bytes", attr_sim_ki->name, MILENAGE_KI_SIZE, ki_vp->vp_length); return -1; } if (vector_opc_from_op(request, &opc_p, opc_buff, vps, ki_vp->vp_octets) < 0) return -1; amf_vp = fr_pair_find_by_da(vps, attr_sim_amf, TAG_ANY); if (amf_vp) { if (amf_vp->vp_length != sizeof(amf_buff)) { REDEBUG("&control:%s has incorrect length, expected %u bytes got %zu bytes", attr_sim_amf->name, MILENAGE_AMF_SIZE, amf_vp->vp_length); return -1; } memcpy(amf_buff, amf_vp->vp_octets, sizeof(amf_buff)); } sqn_vp = fr_pair_find_by_da(vps, attr_sim_sqn, TAG_ANY); sqn = sqn_vp ? sqn_vp->vp_uint64 : 2; /* * We default to milenage */ version_vp = fr_pair_find_by_da(vps, attr_sim_algo_version, TAG_ANY); if (version_vp) version = version_vp->vp_uint32; for (i = 0; i < SIM_VECTOR_UMTS_RAND_SIZE; i += sizeof(uint32_t)) { uint32_t rand = fr_rand(); memcpy(&keys->umts.vector.rand[i], &rand, sizeof(rand)); } switch (version) { case 4: { uint8_t sqn_buff[MILENAGE_SQN_SIZE]; keys->sqn = sqn_vp ? sqn_vp->vp_uint64 : 0; uint48_to_buff(sqn_buff, keys->sqn); RDEBUG3("Milenage inputs"); RINDENT(); /* * Don't change colon indent, matches other messages later... */ RHEXDUMP_INLINE(L_DBG_LVL_3, ki_vp->vp_octets, MILENAGE_KI_SIZE, "Ki :"); RHEXDUMP_INLINE(L_DBG_LVL_3, opc_p, MILENAGE_OPC_SIZE, "OPc :"); RHEXDUMP_INLINE(L_DBG_LVL_3, sqn_buff, MILENAGE_SQN_SIZE, "SQN :"); RHEXDUMP_INLINE(L_DBG_LVL_3, amf_buff, MILENAGE_AMF_SIZE, "AMF :"); REXDENT(); if (milenage_umts_generate(keys->umts.vector.autn, keys->umts.vector.ik, keys->umts.vector.ck, keys->umts.vector.ak, keys->umts.vector.xres, opc_p, amf_buff, ki_vp->vp_octets, sqn, keys->umts.vector.rand) < 0) { RPEDEBUG2("Failed deriving UMTS Quintuplet"); return -1; } keys->umts.vector.xres_len = 8; } return 0; case 1: case 2: case 3: REDEBUG("Only Milenage can be used to generate quintuplets"); return -1; default: REDEBUG("Unknown/unsupported algorithm %i", version); return -1; } }
static int vector_gsm_from_ki(eap_session_t *eap_session, VALUE_PAIR *vps, int idx, fr_sim_keys_t *keys) { REQUEST *request = eap_session->request; VALUE_PAIR *ki_vp, *version_vp; uint8_t opc_buff[MILENAGE_OPC_SIZE]; uint8_t const *opc_p; uint32_t version; int i; /* * Generate a new RAND value, and derive Kc and SRES from Ki */ ki_vp = fr_pair_find_by_da(vps, attr_sim_ki, TAG_ANY); if (!ki_vp) { RDEBUG3("No &control:%sfound, not generating triplets locally", attr_sim_ki->name); return 1; } else if (ki_vp->vp_length != MILENAGE_KI_SIZE) { REDEBUG("&control:%s has incorrect length, expected 16 bytes got %zu bytes", attr_sim_ki->name, ki_vp->vp_length); return -1; } /* * Check to see if have a Ki for the IMSI, this allows us to generate the rest * of the triplets. */ version_vp = fr_pair_find_by_da(vps, attr_sim_algo_version, TAG_ANY); if (!version_vp) { if (vector_opc_from_op(request, &opc_p, opc_buff, vps, ki_vp->vp_octets) < 0) return -1; version = opc_p ? 4 : 3; /* * Version we explicitly specified, see if we can find the prerequisite * attributes. */ } else { version = version_vp->vp_uint32; if (version == 4) { if (vector_opc_from_op(request, &opc_p, opc_buff, vps, ki_vp->vp_octets) < 0) return -1; if (!opc_p) { RPEDEBUG2("No &control:%s or &control:%s found, " "can't run Milenage (COMP128-4)", attr_sim_op->name, attr_sim_opc->name); return -1; } } } for (i = 0; i < SIM_VECTOR_GSM_RAND_SIZE; i += sizeof(uint32_t)) { uint32_t rand = fr_rand(); memcpy(&keys->gsm.vector[idx].rand[i], &rand, sizeof(rand)); } switch (version) { case 1: comp128v1(keys->gsm.vector[idx].sres, keys->gsm.vector[idx].kc, ki_vp->vp_octets, keys->gsm.vector[idx].rand); break; case 2: comp128v23(keys->gsm.vector[idx].sres, keys->gsm.vector[idx].kc, ki_vp->vp_octets, keys->gsm.vector[idx].rand, true); break; case 3: comp128v23(keys->gsm.vector[idx].sres, keys->gsm.vector[idx].kc, ki_vp->vp_octets, keys->gsm.vector[idx].rand, false); break; case 4: if (milenage_gsm_generate(keys->gsm.vector[idx].sres, keys->gsm.vector[idx].kc, opc_p, ki_vp->vp_octets, keys->gsm.vector[idx].rand) < 0) { RPEDEBUG2("Failed deriving GSM triplet"); return -1; } return 0; default: REDEBUG("Unknown/unsupported algorithm %i", version); return -1; } return 0; }
/* * Send one packet. */ static int send_one_packet(rc_request_t *request) { assert(request->done == false); /* * 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_socket_client_tcp(NULL, &request->packet->dst_ipaddr, request->packet->dst_port, false); } else #endif mysockfd = fr_socket(&client_ipaddr, 0); if (mysockfd < 0) { ERROR("Failed opening socket"); exit(1); } if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, &request->packet->dst_ipaddr, request->packet->dst_port, NULL)) { ERROR("Can't add new socket"); 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) { VALUE_PAIR *vp; if ((vp = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) { fr_pair_value_strcpy(vp, request->password->vp_strvalue); } else if ((vp = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) { uint8_t buffer[17]; rad_chap_encode(request->packet, buffer, fr_rand() & 0xff, request->password); fr_pair_value_memcpy(vp, buffer, 17); } else if (fr_pair_find_by_num(request->packet->vps, PW_MS_CHAP_PASSWORD, 0, TAG_ANY) != NULL) { mschapv1_encode(request->packet, &request->packet->vps, request->password->vp_strvalue); } else { DEBUG("WARNING: No password in the request"); } } request->timestamp = time(NULL); request->tries = 1; request->resend++; } 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); REDEBUG("No reply from server for ID %d socket %d", 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 = true; } stats.lost++; return -1; } /* * We are trying later. */ request->timestamp = now; request->tries++; } /* * Send the packet. */ if (rad_send(request->packet, NULL, secret) < 0) { REDEBUG("Failed to send packet for ID %d", request->packet->id); deallocate_id(request); request->done = true; return -1; } fr_packet_header_print(fr_log_fp, request->packet, false); if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, request->packet->vps); return 0; }
/* * Send one packet. */ static int send_one_packet(radclient_t *radclient) { assert(radclient->done == 0); /* * Remember when we have to wake up, to re-send the * request, of we didn't receive a response. */ if ((sleep_time == -1) || (sleep_time > (int) timeout)) { sleep_time = (int) timeout; } /* * Haven't sent the packet yet. Initialize it. */ if (radclient->request->id == -1) { int i, rcode; assert(radclient->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: radclient->request->src_ipaddr.af = server_ipaddr.af; rcode = fr_packet_list_id_alloc(pl, ipproto, radclient->request, NULL); if (rcode < 0) { 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) { fprintf(stderr, "radclient: Can't open new socket\n"); 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; } if (rcode == 0) { done = 0; sleep_time = 0; return 0; } assert(radclient->request->id != -1); assert(radclient->request->data == NULL); for (i = 0; i < 4; i++) { ((uint32_t *) radclient->request->vector)[i] = fr_rand(); } /* * Update the password, so it can be encrypted with the * new authentication vector. */ if (radclient->password[0] != '\0') { VALUE_PAIR *vp; if ((vp = pairfind(radclient->request->vps, PW_USER_PASSWORD, 0)) != NULL) { strlcpy(vp->vp_strvalue, radclient->password, sizeof(vp->vp_strvalue)); vp->length = strlen(vp->vp_strvalue); } else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD, 0)) != 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) { strlcpy(vp->vp_strvalue, radclient->password, sizeof(vp->vp_strvalue)); vp->length = strlen(vp->vp_strvalue); rad_chap_encode(radclient->request, vp->vp_octets, fr_rand() & 0xff, vp); vp->length = 17; } } else if (pairfind(radclient->request->vps, PW_MSCHAP_PASSWORD, 0) != NULL) { mschapv1_encode(&radclient->request->vps, radclient->password); } else if (fr_debug_flag) { printf("WARNING: No password in the request\n"); } } radclient->timestamp = time(NULL); radclient->tries = 1; radclient->resend++; /* * Duplicate found. Serious error! */ if (!fr_packet_list_insert(pl, &radclient->request)) { assert(0 == 1); } #ifdef WITH_TCP /* * WTF? */ if (client_port == 0) { client_ipaddr = radclient->request->src_ipaddr; client_port = radclient->request->src_port; } #endif } else { /* radclient->request->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 - radclient->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 - radclient->timestamp))) { sleep_time = now - radclient->timestamp; } return 0; } /* * We're not trying later, maybe the packet is done. */ if (radclient->tries == retries) { assert(radclient->request->id >= 0); /* * Delete the request from the tree of * outstanding requests. */ fr_packet_list_yank(pl, radclient->request); fprintf(stderr, "radclient: no response from server for ID %d socket %d\n", radclient->request->id, radclient->request->sockfd); deallocate_id(radclient); /* * Normally we mark it "done" when we've received * the response, but this is a special case. */ if (radclient->resend == resend_count) { radclient->done = 1; } totallost++; return -1; } /* * We are trying later. */ radclient->timestamp = now; radclient->tries++; } /* * Send the packet. */ if (rad_send(radclient->request, NULL, secret) < 0) { fprintf(stderr, "radclient: Failed to send packet for ID %d: %s\n", radclient->request->id, fr_strerror()); } if (fr_debug_flag > 2) print_hex(radclient->request); return 0; }
/* * read the config section and load all the eap authentication types present. */ static int eap_instantiate(CONF_SECTION *cs, void **instance) { int i, num_types; int has_tls, do_tls; rlm_eap_t *inst; CONF_SECTION *scs; inst = (rlm_eap_t *) malloc(sizeof(*inst)); if (!inst) { return -1; } memset(inst, 0, sizeof(*inst)); if (cf_section_parse(cs, inst, module_config) < 0) { eap_detach(inst); return -1; } /* * Create our own random pool. */ for (i = 0; i < 256; i++) { inst->rand_pool.randrsl[i] = fr_rand(); } fr_randinit(&inst->rand_pool, 1); /* * List of sessions are set to NULL by the memset * of 'inst', above. */ /* * Lookup sessions in the tree. We don't free them in * the tree, as that's taken care of elsewhere... */ inst->session_tree = rbtree_create(eap_handler_cmp, NULL, 0); if (!inst->session_tree) { radlog(L_ERR|L_CONS, "rlm_eap2: Cannot initialize tree"); eap_detach(inst); return -1; } /* * This registers ALL available methods. * * FIXME: we probably want to selectively register * some methods. */ if (eap_server_register_methods() < 0) { eap_detach(inst); return -1; } /* Load all the configured EAP-Types */ num_types = 0; has_tls = do_tls = 0; for (scs=cf_subsection_find_next(cs, NULL, NULL); scs != NULL; scs=cf_subsection_find_next(cs, scs, NULL)) { const char *auth_type; char buffer[64], *p; auth_type = cf_section_name1(scs); if (!auth_type) continue; if (num_types >= EAP_MAX_METHODS) { radlog(L_INFO, "WARNING: Ignoring EAP type %s: too many types defined", auth_type); continue; } /* * Hostapd doesn't do case-insensitive comparisons. * So we mash everything to uppercase for it. */ strlcpy(buffer, auth_type, sizeof(buffer)); for (p = buffer; *p; p++) { if (!islower((int)*p)) continue; *p = toupper((int)*p); } inst->methods[num_types] = eap_server_get_type(buffer, &inst->vendors[num_types]); if (inst->methods[num_types] == EAP_TYPE_NONE) { radlog(L_ERR|L_CONS, "rlm_eap2: Unknown EAP type %s", auth_type); eap_detach(inst); return -1; } switch (inst->methods[num_types]) { case EAP_TYPE_TLS: has_tls = TRUE; /* FALL-THROUGH */ case EAP_TYPE_TTLS: case EAP_TYPE_PEAP: case EAP_TYPE_FAST: do_tls = TRUE; break; default: break; } num_types++; /* successfully loaded one more types */ } inst->num_types = num_types; if (do_tls && !has_tls) { radlog(L_ERR|L_CONS, "rlm_eap2: TLS has not been configured. Cannot do methods that need TLS."); eap_detach(inst); return -1; } if (do_tls) { /* * Initialize TLS. */ if (eap_example_server_init_tls(inst) < 0) { radlog(L_ERR|L_CONS, "rlm_eap2: Cannot initialize TLS"); eap_detach(inst); return -1; } } pthread_mutex_init(&(inst->session_mutex), NULL); *instance = inst; return 0; }
/* * read the config section and load all the eap authentication types present. */ static int mod_instantiate(CONF_SECTION *cs, void *instance) { int i, ret; eap_type_t method; int num_methods; CONF_SECTION *scs; rlm_eap_t *inst = instance; /* * Create our own random pool. */ for (i = 0; i < 256; i++) { inst->rand_pool.randrsl[i] = fr_rand(); } fr_randinit(&inst->rand_pool, 1); inst->rand_pool.randcnt = 0; inst->xlat_name = cf_section_name2(cs); if (!inst->xlat_name) inst->xlat_name = "EAP"; /* Load all the configured EAP-Types */ num_methods = 0; for(scs = cf_subsection_find_next(cs, NULL, NULL); scs != NULL; scs = cf_subsection_find_next(cs, scs, NULL)) { const char *name; name = cf_section_name1(scs); if (!name) continue; if (!strcmp(name, TLS_CONFIG_SECTION)) continue; method = eap_name2type(name); if (method == PW_EAP_INVALID) { cf_log_err_cs(cs, "Unknown EAP method %s", name); return -1; } if ((method < PW_EAP_MD5) || (method >= PW_EAP_MAX_TYPES)) { cf_log_err_cs(cs, "Invalid EAP method %s (unsupported)", name); return -1; } #if !defined(HAVE_OPENSSL_SSL_H) || !defined(HAVE_LIBSSL) /* * This allows the default configuration to be * shipped with EAP-TLS, etc. enabled. If the * system doesn't have OpenSSL, they will be * ignored. * * If the system does have OpenSSL, then this * code will not be used. The administrator will * then have to delete the tls, * etc. configurations from eap.conf in order to * have EAP without the TLS types. */ if ((method == PW_EAP_TLS) || (method == PW_EAP_TTLS) || (method == PW_EAP_PEAP)) { DEBUG2("rlm_eap (%s): Ignoring EAP method %s because we do not have OpenSSL support", inst->xlat_name, name); continue; } #endif /* * Load the type. */ ret = eap_module_load(inst, &inst->methods[method], method, scs); (void) talloc_get_type_abort(inst->methods[method], eap_module_t); if (ret < 0) { (void) talloc_steal(inst, inst->methods[method]); return -1; } (void) talloc_steal(inst, inst->methods[method]); num_methods++; /* successfully loaded one more methods */ } if (num_methods == 0) { cf_log_err_cs(cs, "No EAP method configured, module cannot do anything"); return -1; } /* * Ensure that the default EAP type is loaded. */ method = eap_name2type(inst->default_method_name); if (method == PW_EAP_INVALID) { cf_log_err_cs(cs, "Unknown default EAP method '%s'", inst->default_method_name); return -1; } if (!inst->methods[method]) { cf_log_err_cs(cs, "No such sub-type for default EAP method %s", inst->default_method_name); return -1; } inst->default_method = method; /* save the numerical method */ /* * List of sessions are set to NULL by the memset * of 'inst', above. */ /* * Lookup sessions in the tree. We don't free them in * the tree, as that's taken care of elsewhere... */ inst->session_tree = rbtree_create(eap_handler_cmp, NULL, 0); if (!inst->session_tree) { radlog(L_ERR, "rlm_eap (%s): Cannot initialize tree", inst->xlat_name); return -1; } if (fr_debug_flag) { inst->handler_tree = rbtree_create(eap_handler_ptr_cmp, NULL, 0); if (!inst->handler_tree) { radlog(L_ERR, "rlm_eap (%s): Cannot initialize tree", inst->xlat_name); return -1; } #ifdef HAVE_PTHREAD_H if (pthread_mutex_init(&(inst->handler_mutex), NULL) < 0) { radlog(L_ERR, "rlm_eap (%s): Failed initializing mutex: %s", inst->xlat_name, strerror(errno)); return -1; } #endif } #ifdef HAVE_PTHREAD_H if (pthread_mutex_init(&(inst->session_mutex), NULL) < 0) { radlog(L_ERR, "rlm_eap (%s): Failed initializing mutex: %s", inst->xlat_name, strerror(errno)); return -1; } #endif return 0; }
/* * read the config section and load all the eap authentication types present. */ static int eap_instantiate(CONF_SECTION *cs, void **instance) { int i, eap_type; int num_types; CONF_SECTION *scs; rlm_eap_t *inst; inst = (rlm_eap_t *) malloc(sizeof(*inst)); if (!inst) { return -1; } memset(inst, 0, sizeof(*inst)); if (cf_section_parse(cs, inst, module_config) < 0) { eap_detach(inst); return -1; } /* * Create our own random pool. */ for (i = 0; i < 256; i++) { inst->rand_pool.randrsl[i] = fr_rand(); } fr_randinit(&inst->rand_pool, 1); inst->rand_pool.randcnt = 0; inst->xlat_name = cf_section_name2(cs); if (!inst->xlat_name) inst->xlat_name = "EAP"; /* Load all the configured EAP-Types */ num_types = 0; for(scs=cf_subsection_find_next(cs, NULL, NULL); scs != NULL; scs=cf_subsection_find_next(cs, scs, NULL)) { const char *auth_type; auth_type = cf_section_name1(scs); if (!auth_type) continue; if (!strcmp(auth_type, TLS_CONFIG_SECTION)) continue; eap_type = eaptype_name2type(auth_type); if (eap_type < 0) { radlog(L_ERR, "rlm_eap: Unknown EAP type %s", auth_type); eap_detach(inst); return -1; } #ifndef HAVE_OPENSSL_SSL_H /* * This allows the default configuration to be * shipped with EAP-TLS, etc. enabled. If the * system doesn't have OpenSSL, they will be * ignored. * * If the system does have OpenSSL, then this * code will not be used. The administrator will * then have to delete the tls, * etc. configurations from eap.conf in order to * have EAP without the TLS types. */ if ((eap_type == PW_EAP_TLS) || (eap_type == PW_EAP_TTLS) || (eap_type == PW_EAP_PEAP)) { DEBUG2("Ignoring EAP-Type/%s because we do not have OpenSSL support.", auth_type); continue; } #endif /* * Load the type. */ if (eaptype_load(&inst->types[eap_type], eap_type, scs) < 0) { eap_detach(inst); return -1; } num_types++; /* successfully loaded one more types */ } if (num_types == 0) { radlog(L_ERR|L_CONS, "rlm_eap: No EAP type configured, module cannot do anything."); eap_detach(inst); return -1; } /* * Ensure that the default EAP type is loaded. */ eap_type = eaptype_name2type(inst->default_eap_type_name); if (eap_type < 0) { radlog(L_ERR|L_CONS, "rlm_eap: Unknown default EAP type %s", inst->default_eap_type_name); eap_detach(inst); return -1; } if (inst->types[eap_type] == NULL) { radlog(L_ERR|L_CONS, "rlm_eap: No such sub-type for default EAP type %s", inst->default_eap_type_name); eap_detach(inst); return -1; } inst->default_eap_type = eap_type; /* save the numerical type */ /* * List of sessions are set to NULL by the memset * of 'inst', above. */ /* * Lookup sessions in the tree. We don't free them in * the tree, as that's taken care of elsewhere... */ inst->session_tree = rbtree_create(eap_handler_cmp, NULL, 0); if (!inst->session_tree) { radlog(L_ERR|L_CONS, "rlm_eap: Cannot initialize tree"); eap_detach(inst); return -1; } if (fr_debug_flag) { inst->handler_tree = rbtree_create(eap_handler_ptr_cmp, NULL, 0); if (!inst->handler_tree) { radlog(L_ERR|L_CONS, "rlm_eap: Cannot initialize tree"); eap_detach(inst); return -1; } #ifdef HAVE_PTHREAD_H if (pthread_mutex_init(&(inst->handler_mutex), NULL) < 0) { radlog(L_ERR|L_CONS, "rlm_eap: Failed initializing mutex: %s", strerror(errno)); eap_detach(inst); return -1; } #endif } #ifdef HAVE_PTHREAD_H if (pthread_mutex_init(&(inst->session_mutex), NULL) < 0) { radlog(L_ERR|L_CONS, "rlm_eap: Failed initializing mutex: %s", strerror(errno)); eap_detach(inst); return -1; } #endif *instance = inst; 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, false); if (!request->packet) { ERROR("No memory"); talloc_free(request); return NULL; } request->reply = rad_alloc(request, false); if (!request->reply) { ERROR("No memory"); talloc_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_RUNNING; request->handle = NULL; request->server = talloc_typed_strdup(request, "default"); request->root = &main_config; /* * Read packet from fp */ if (readvp2(request->packet, &request->packet->vps, fp, &filedone) < 0) { fr_perror("unittest"); talloc_free(request); return NULL; } /* * Set the defaults for IPs, etc. */ request->packet->code = PW_CODE_ACCESS_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, *q; 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; da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0); rad_assert(da != NULL); vp->da = da; /* * Re-do pairmemsteal ourselves, * because we play games with * vp->da, and pairmemsteal goes * to GREAT lengths to sanitize * and fix and change and * double-check the various * fields. */ memcpy(&q, &vp->vp_octets, sizeof(q)); talloc_free(q); vp->vp_octets = talloc_steal(vp, p); vp->type = VT_DATA; VERIFY_VP(vp); } 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)); fr_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_ACCESS_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->log.lvl = debug_flag; request->log.func = 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; }
/* * Initiate the EAP-MD5 session by sending a challenge to the peer. */ static int mod_session_init(UNUSED void *instance, eap_handler_t *handler) { int i; MD5_PACKET *reply; REQUEST *request = handler->request; /* * Allocate an EAP-MD5 packet. */ reply = talloc(handler, MD5_PACKET); if (!reply) { return 0; } /* * Fill it with data. */ reply->code = PW_MD5_CHALLENGE; reply->length = 1 + MD5_CHALLENGE_LEN; /* one byte of value size */ reply->value_size = MD5_CHALLENGE_LEN; /* * Allocate user data. */ reply->value = talloc_array(reply, uint8_t, reply->value_size); if (!reply->value) { talloc_free(reply); return 0; } /* * Get a random challenge. */ for (i = 0; i < reply->value_size; i++) { reply->value[i] = fr_rand(); } RDEBUG2("Issuing MD5 Challenge"); /* * Keep track of the challenge. */ handler->opaque = talloc_array(handler, uint8_t, reply->value_size); rad_assert(handler->opaque != NULL); memcpy(handler->opaque, reply->value, reply->value_size); handler->free_opaque = NULL; /* * Compose the EAP-MD5 packet out of the data structure, * and free it. */ eapmd5_compose(handler->eap_ds, reply); /* * We don't need to authorize the user at this point. * * We also don't need to keep the challenge, as it's * stored in 'handler->eap_ds', which will be given back * to us... */ handler->stage = PROCESS; return 1; }
/** Generate a string of random chars * * Build strings of random chars, useful for generating tokens and passcodes * Format similar to String::Random. */ static ssize_t randstr_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt, char *out, size_t outlen) { char const *p; unsigned int result; size_t freespace = outlen; if (outlen <= 1) return 0; *out = '\0'; p = fmt; while (*p && (--freespace > 0)) { result = fr_rand(); switch (*p) { /* * Lowercase letters */ case 'c': *out++ = 'a' + (result % 26); break; /* * Uppercase letters */ case 'C': *out++ = 'A' + (result % 26); break; /* * Numbers */ case 'n': *out++ = '0' + (result % 10); break; /* * Alpha numeric */ case 'a': *out++ = randstr_salt[result % (sizeof(randstr_salt) - 3)]; break; /* * Punctuation */ case '!': *out++ = randstr_punc[result % (sizeof(randstr_punc) - 1)]; break; /* * Alpa numeric + punctuation */ case '.': *out++ = '!' + (result % 95); break; /* * Alpha numeric + salt chars './' */ case 's': *out++ = randstr_salt[result % (sizeof(randstr_salt) - 1)]; break; /* * Chars suitable for One Time Password tokens. * Alpha numeric with easily confused char pairs removed. */ case 'o': *out++ = randstr_otp[result % (sizeof(randstr_otp) - 1)]; break; /* * Binary data as hexits (we don't really support * non printable chars). */ case 'h': if (freespace < 2) { break; } snprintf(out, 3, "%02x", result % 256); /* Already decremented */ freespace -= 1; out += 2; break; /* * Binary data with uppercase hexits */ case 'H': if (freespace < 2) { break; } snprintf(out, 3, "%02X", result % 256); /* Already decremented */ freespace -= 1; out += 2; break; default: ERROR("rlm_expr: invalid character class '%c'", *p); return -1; } p++; } *out++ = '\0'; return outlen - freespace; }
/** Copy packet to multiple servers * * Create a duplicate of the packet and send it to a list of realms * defined by the presence of the Replicate-To-Realm VP in the control * list of the current request. * * This is pretty hacky and is 100% fire and forget. If you're looking * to forward authentication requests to multiple realms and process * the responses, this function will not allow you to do that. * * @param[in] instance of this module. * @param[in] request The current request. * @param[in] list of attributes to copy to the duplicate packet. * @param[in] code to write into the code field of the duplicate packet. * @return RCODE fail on error, invalid if list does not exist, noop if no * replications succeeded, else ok. */ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, unsigned int code) { int rcode = RLM_MODULE_NOOP; VALUE_PAIR *vp, **vps, *last; home_server *home; REALM *realm; home_pool_t *pool; RADIUS_PACKET *packet = NULL; last = request->config_items; /* * Send as many packets as necessary to different * destinations. */ while (1) { vp = pairfind(last, PW_REPLICATE_TO_REALM, 0, TAG_ANY); if (!vp) break; last = vp->next; realm = realm_find2(vp->vp_strvalue); if (!realm) { RDEBUG2E("Cannot Replicate to unknown realm %s", realm); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: RDEBUG2E("Cannot replicate unknown packet code %d", request->packet->code); cleanup(packet); return RLM_MODULE_FAIL; case PW_AUTHENTICATION_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_COA_REQUEST: case PW_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RDEBUG2W("Cancelling replication to Realm %s, as the realm is local.", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { RDEBUG2E("Failed to find live home server for realm %s", realm->name); continue; } /* * For replication to multiple servers we re-use the packet * we built here. */ if (!packet) { packet = rad_alloc(NULL, 1); if (!packet) return RLM_MODULE_FAIL; packet->sockfd = -1; packet->code = code; packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { RDEBUGE("Failed opening socket: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } vps = radius_list(request, list); if (!vps) { RDEBUGW("List '%s' doesn't exist for " "this packet", fr_int2str(pair_lists, list, "?unknown?")); rcode = RLM_MODULE_INVALID; goto done; } /* * Don't assume the list actually contains any * attributes. */ if (*vps) { packet->vps = paircopy(packet, *vps); if (!packet->vps) { rcode = RLM_MODULE_FAIL; goto done; } } /* * For CHAP, create the CHAP-Challenge if * it doesn't exist. */ if ((code == PW_AUTHENTICATION_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) { vp = radius_paircreate(request, &packet->vps, PW_CHAP_CHALLENGE, 0); vp->length = AUTH_VECTOR_LEN; memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN); } } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; free(packet->data); packet->data = NULL; packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "¿unknown?"),realm->name); if (rad_send(packet, NULL, home->secret) < 0) { RDEBUGE("Failed replicating packet: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } done: cleanup(packet); return rcode; }