int rad_check_ts(UNUSED uint32_t nasaddr, UNUSED unsigned int portnum, UNUSED const char *user, UNUSED const char *session_id) { radlog(L_ERR, "Simultaneous-Use is not supported"); return 2; }
/* * given a radius request with many attribues in the EAP-SIM range, build * them all into a single EAP-SIM body. * */ int map_eapsim_basictypes(RADIUS_PACKET *r, EAP_PACKET *ep) { VALUE_PAIR *vp; int encoded_size; uint8_t *encodedmsg, *attr; unsigned int id, eapcode; unsigned char *macspace, *append; int appendlen; unsigned char subtype; macspace = NULL; append = NULL; appendlen = 0; /* * encodedmsg is now an EAP-SIM message. * it might be too big for putting into an EAP-Type-SIM * */ vp = pairfind(r->vps, ATTRIBUTE_EAP_SIM_SUBTYPE, 0); if(vp == NULL) { subtype = eapsim_start; } else { subtype = vp->vp_integer; } vp = pairfind(r->vps, ATTRIBUTE_EAP_ID, 0); if(vp == NULL) { id = ((int)getpid() & 0xff); } else { id = vp->vp_integer; } vp = pairfind(r->vps, ATTRIBUTE_EAP_CODE, 0); if(vp == NULL) { eapcode = PW_EAP_REQUEST; } else { eapcode = vp->vp_integer; } /* * take a walk through the attribute list to see how much space * that we need to encode all of this. */ encoded_size = 0; for(vp = r->vps; vp != NULL; vp = vp->next) { int roundedlen; int vplen; if(vp->attribute < ATTRIBUTE_EAP_SIM_BASE || vp->attribute >= ATTRIBUTE_EAP_SIM_BASE+256) { continue; } vplen = vp->length; /* * the AT_MAC attribute is a bit different, when we get to this * attribute, we pull the contents out, save it for later * processing, set the size to 16 bytes (plus 2 bytes padding). * * At this point, we only care about the size. */ if(vp->attribute == ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC) { vplen = 18; } /* round up to next multiple of 4, after taking in * account the type and length bytes */ roundedlen = (vplen + 2 + 3) & ~3; encoded_size += roundedlen; } if (ep->code != PW_EAP_SUCCESS) ep->code = eapcode; ep->id = (id & 0xff); ep->type.type = PW_EAP_SIM; /* * if no attributes were found, do very little. * */ if(encoded_size == 0) { encodedmsg = malloc(3); /* FIX: could be NULL */ encodedmsg[0]=subtype; encodedmsg[1]=0; encodedmsg[2]=0; ep->type.length = 3; ep->type.data = encodedmsg; return 0; } /* * figured out the length, so malloc some space for the results. * * Note that we do not bother going through an "EAP" stage, which * is a bit strange compared to the unmap, which expects to see * an EAP-SIM virtual attributes. * * EAP is 1-code, 1-identifier, 2-length, 1-type = 5 overhead. * * SIM code adds a subtype, and 2 bytes of reserved = 3. * */ /* malloc space for it */ encoded_size += 3; encodedmsg = malloc(encoded_size); if (encodedmsg == NULL) { radlog(L_ERR, "eapsim: out of memory allocating %d bytes", encoded_size+5); return 0; } memset(encodedmsg, 0, encoded_size); /* * now walk the attributes again, sticking them in. * * we go three bytes into the encoded message, because there are two * bytes of reserved, and we will fill the "subtype" in later. * */ attr = encodedmsg+3; for(vp = r->vps; vp != NULL; vp = vp->next) { int roundedlen; if(vp->attribute < ATTRIBUTE_EAP_SIM_BASE || vp->attribute >= ATTRIBUTE_EAP_SIM_BASE+256) { continue; } /* * the AT_MAC attribute is a bit different, when we get to this * attribute, we pull the contents out, save it for later * processing, set the size to 16 bytes (plus 2 bytes padding). * * At this point, we put in zeros, and remember where the * sixteen bytes go. */ if(vp->attribute == ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC) { roundedlen = 20; memset(&attr[2], 0, 18); macspace = &attr[4]; append = vp->vp_octets; appendlen = vp->length; } else { roundedlen = (vp->length + 2 + 3) & ~3; memset(attr, 0, roundedlen); memcpy(&attr[2], vp->vp_strvalue, vp->length); } attr[0] = vp->attribute - ATTRIBUTE_EAP_SIM_BASE; attr[1] = roundedlen >> 2; attr += roundedlen; } encodedmsg[0] = subtype; ep->type.length = encoded_size; ep->type.data = encodedmsg; /* * if macspace was set and we have a key, * then we should calculate the HMAC-SHA1 of the resulting EAP-SIM * packet, appended with the value of append. */ vp = pairfind(r->vps, ATTRIBUTE_EAP_SIM_KEY, 0); if(macspace != NULL && vp != NULL) { unsigned char *buffer; eap_packet_t *hdr; uint16_t hmaclen, total_length = 0; unsigned char sha1digest[20]; total_length = EAP_HEADER_LEN + 1 + encoded_size; hmaclen = total_length + appendlen; buffer = (unsigned char *)malloc(hmaclen); hdr = (eap_packet_t *)buffer; if (!hdr) { radlog(L_ERR, "rlm_eap: out of memory"); free(encodedmsg); return 0; } hdr->code = eapcode & 0xFF; hdr->id = (id & 0xFF); total_length = htons(total_length); memcpy(hdr->length, &total_length, sizeof(total_length)); hdr->data[0] = PW_EAP_SIM; /* copy the data */ memcpy(&hdr->data[1], encodedmsg, encoded_size); /* copy the nonce */ memcpy(&hdr->data[encoded_size+1], append, appendlen); /* HMAC it! */ fr_hmac_sha1(buffer, hmaclen, vp->vp_octets, vp->length, sha1digest); /* done with the buffer, free it */ free(buffer); /* now copy the digest to where it belongs in the AT_MAC */ /* note that it is truncated to 128-bits */ memcpy(macspace, sha1digest, 16); } /* if we had an AT_MAC and no key, then fail */ if(macspace != NULL && vp == NULL) { if(encodedmsg != NULL) free(encodedmsg); return 0; } return 1; }
/* * 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; }
static int getUserNodeRef(char* inUserName, char **outUserName, tDirNodeReference* userNodeRef, tDirReference dsRef) { tDataBuffer *tDataBuff = NULL; tDirNodeReference nodeRef = 0; long status = eDSNoErr; tContextData context = 0; uint32_t nodeCount = 0; uint32_t attrIndex = 0; tDataList *nodeName = NULL; tAttributeEntryPtr pAttrEntry = NULL; tDataList *pRecName = NULL; tDataList *pRecType = NULL; tDataList *pAttrType = NULL; uint32_t recCount = 0; tRecordEntry *pRecEntry = NULL; tAttributeListRef attrListRef = 0; char *pUserLocation = NULL; tAttributeValueListRef valueRef = 0; tAttributeValueEntry *pValueEntry = NULL; tDataList *pUserNode = NULL; int result = RLM_MODULE_FAIL; if (inUserName == NULL) { radlog(L_ERR, "rlm_mschap: getUserNodeRef(): no username"); return RLM_MODULE_FAIL; } tDataBuff = dsDataBufferAllocate(dsRef, 4096); if (tDataBuff == NULL) { radlog(L_ERR, "rlm_mschap: getUserNodeRef(): dsDataBufferAllocate() status = %ld", status); return RLM_MODULE_FAIL; } do { /* find on search node */ status = dsFindDirNodes(dsRef, tDataBuff, NULL, eDSAuthenticationSearchNodeName, &nodeCount, &context); if (status != eDSNoErr) { radlog(L_ERR,"rlm_mschap: getUserNodeRef(): no node found? status = %ld", status); result = RLM_MODULE_FAIL; break; } if (nodeCount < 1) { radlog(L_ERR,"rlm_mschap: getUserNodeRef(): nodeCount < 1, status = %ld", status); result = RLM_MODULE_FAIL; break; } status = dsGetDirNodeName(dsRef, tDataBuff, 1, &nodeName); if (status != eDSNoErr) { radlog(L_ERR,"rlm_mschap: getUserNodeRef(): dsGetDirNodeName() status = %ld", status); result = RLM_MODULE_FAIL; break; } status = dsOpenDirNode(dsRef, nodeName, &nodeRef); dsDataListDeallocate(dsRef, nodeName); free(nodeName); nodeName = NULL; if (status != eDSNoErr) { radlog(L_ERR,"rlm_mschap: getUserNodeRef(): dsOpenDirNode() status = %ld", status); result = RLM_MODULE_FAIL; break; } pRecName = dsBuildListFromStrings(dsRef, inUserName, NULL); pRecType = dsBuildListFromStrings(dsRef, kDSStdRecordTypeUsers, NULL); pAttrType = dsBuildListFromStrings(dsRef, kDSNAttrMetaNodeLocation, kDSNAttrRecordName, NULL); recCount = 1; status = dsGetRecordList(nodeRef, tDataBuff, pRecName, eDSExact, pRecType, pAttrType, 0, &recCount, &context); if (status != eDSNoErr || recCount == 0) { radlog(L_ERR,"rlm_mschap: getUserNodeRef(): dsGetRecordList() status = %ld, recCount=%u", status, recCount); result = RLM_MODULE_FAIL; break; } status = dsGetRecordEntry(nodeRef, tDataBuff, 1, &attrListRef, &pRecEntry); if (status != eDSNoErr) { radlog(L_ERR,"rlm_mschap: getUserNodeRef(): dsGetRecordEntry() status = %ld", status); result = RLM_MODULE_FAIL; break; } for (attrIndex = 1; (attrIndex <= pRecEntry->fRecordAttributeCount) && (status == eDSNoErr); attrIndex++) { status = dsGetAttributeEntry(nodeRef, tDataBuff, attrListRef, attrIndex, &valueRef, &pAttrEntry); if (status == eDSNoErr && pAttrEntry != NULL) { if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation) == 0) { status = dsGetAttributeValue(nodeRef, tDataBuff, 1, valueRef, &pValueEntry); if (status == eDSNoErr && pValueEntry != NULL) { pUserLocation = (char *) calloc(pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof(char)); memcpy(pUserLocation, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength); } } else if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrRecordName) == 0) { status = dsGetAttributeValue(nodeRef, tDataBuff, 1, valueRef, &pValueEntry); if (status == eDSNoErr && pValueEntry != NULL) { *outUserName = (char *) malloc(pValueEntry->fAttributeValueData.fBufferLength + 1); bzero(*outUserName,pValueEntry->fAttributeValueData.fBufferLength + 1); memcpy(*outUserName, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength); } } if (pValueEntry != NULL) { dsDeallocAttributeValueEntry(dsRef, pValueEntry); pValueEntry = NULL; } dsDeallocAttributeEntry(dsRef, pAttrEntry); pAttrEntry = NULL; dsCloseAttributeValueList(valueRef); valueRef = 0; } } /* OpenDirectory doesn't support mschapv2 authentication against * Active Directory. AD users need to be authenticated using the * normal freeradius AD path (i.e. ntlm_auth). */ if (strncmp(pUserLocation, kActiveDirLoc, strlen(kActiveDirLoc)) == 0) { DEBUG2("[mschap] OpenDirectory authentication returning noop. OD doesn't support MSCHAPv2 for ActiveDirectory users."); result = RLM_MODULE_NOOP; break; } pUserNode = dsBuildFromPath(dsRef, pUserLocation, "/"); if (pUserNode == NULL) { radlog(L_ERR,"rlm_mschap: getUserNodeRef(): dsBuildFromPath() returned NULL"); result = RLM_MODULE_FAIL; break; } status = dsOpenDirNode(dsRef, pUserNode, userNodeRef); dsDataListDeallocate(dsRef, pUserNode); free(pUserNode); if (status != eDSNoErr) { radlog(L_ERR,"rlm_mschap: getUserNodeRef(): dsOpenDirNode() status = %ld", status); result = RLM_MODULE_FAIL; break; } result = RLM_MODULE_OK; } while (0); if (pRecEntry != NULL) dsDeallocRecordEntry(dsRef, pRecEntry); if (tDataBuff != NULL) dsDataBufferDeAllocate(dsRef, tDataBuff); if (pUserLocation != NULL) free(pUserLocation); if (pRecName != NULL) { dsDataListDeallocate(dsRef, pRecName); free(pRecName); } if (pRecType != NULL) { dsDataListDeallocate(dsRef, pRecType); free(pRecType); } if (pAttrType != NULL) { dsDataListDeallocate(dsRef, pAttrType); free(pAttrType); } if (nodeRef != 0) dsCloseDirNode(nodeRef); return result; }
static int eap_pwd_authenticate (void *arg, EAP_HANDLER *handler) { pwd_session_t *pwd_session; pwd_hdr *hdr; pwd_id_packet *id; EAP_PACKET *response; REQUEST *request, *fake; VALUE_PAIR *pw, **outvps, *vp; EAP_DS *eap_ds; int len, ret = 0; eap_pwd_t *inst = (eap_pwd_t *)arg; uint16_t offset; uint8_t exch, *buf, *ptr, msk[MSK_EMSK_LEN], emsk[MSK_EMSK_LEN]; uint8_t peer_confirm[SHA256_DIGEST_LENGTH]; BIGNUM *x = NULL, *y = NULL; if ((handler == NULL) || ((eap_ds = handler->eap_ds) == NULL) || (inst == NULL)) { return 0; } pwd_session = (pwd_session_t *)handler->opaque; request = handler->request; response = handler->eap_ds->response; hdr = (pwd_hdr *)response->type.data; buf = hdr->data; len = response->type.length - sizeof(pwd_hdr); /* * see if we're fragmenting, if so continue until we're done */ if (pwd_session->out_buf_pos) { if (len) { RDEBUG2("pwd got something more than an ACK for a fragment"); } return send_pwd_request(pwd_session, eap_ds); } /* * the first fragment will have a total length, make a * buffer to hold all the fragments */ if (EAP_PWD_GET_LENGTH_BIT(hdr)) { if (pwd_session->in_buf) { RDEBUG2("pwd already alloced buffer for fragments"); return 0; } pwd_session->in_buf_len = ntohs(buf[0] * 256 | buf[1]); if ((pwd_session->in_buf = malloc(pwd_session->in_buf_len)) == NULL) { RDEBUG2("pwd cannot malloc %d buffer to hold fragments", pwd_session->in_buf_len); return 0; } memset(pwd_session->in_buf, 0, pwd_session->in_buf_len); pwd_session->in_buf_pos = 0; buf += sizeof(uint16_t); len -= sizeof(uint16_t); } /* * all fragments, including the 1st will have the M(ore) bit set, * buffer those fragments! */ if (EAP_PWD_GET_MORE_BIT(hdr)) { rad_assert(pwd_session->in_buf != NULL); if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) { RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent."); return 0; } memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len); pwd_session->in_buf_pos += len; /* * send back an ACK for this fragment */ exch = EAP_PWD_GET_EXCHANGE(hdr); eap_ds->request->code = PW_EAP_REQUEST; eap_ds->request->type.type = PW_EAP_PWD; eap_ds->request->type.length = sizeof(pwd_hdr); if ((eap_ds->request->type.data = malloc(sizeof(pwd_hdr))) == NULL) { radlog(L_ERR, "rlm_eap_pwd: fragment ACK, out of memory"); return 0; } hdr = (pwd_hdr *)eap_ds->request->type.data; EAP_PWD_SET_EXCHANGE(hdr, exch); return 1; } if (pwd_session->in_buf) { /* * the last fragment... */ if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) { RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent."); return 0; } memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len); buf = pwd_session->in_buf; len = pwd_session->in_buf_len; } switch (pwd_session->state) { case PWD_STATE_ID_REQ: if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_ID) { RDEBUG2("pwd exchange is incorrect: not ID"); return 0; } id = (pwd_id_packet *)buf; if ((id->prf != EAP_PWD_DEF_PRF) || (id->random_function != EAP_PWD_DEF_RAND_FUN) || (id->prep != EAP_PWD_PREP_NONE) || (memcmp(id->token, (char *)&pwd_session->token, 4)) || (id->group_num != ntohs(pwd_session->group_num))) { RDEBUG2("pwd id response is invalid"); return 0; } /* * we've agreed on the ciphersuite, record it... */ ptr = (uint8_t *)&pwd_session->ciphersuite; memcpy(ptr, (char *)&id->group_num, sizeof(uint16_t)); ptr += sizeof(uint16_t); *ptr = EAP_PWD_DEF_RAND_FUN; ptr += sizeof(uint8_t); *ptr = EAP_PWD_DEF_PRF; pwd_session->peer_id_len = len - sizeof(pwd_id_packet); if (pwd_session->peer_id_len >= sizeof(pwd_session->peer_id)) { RDEBUG2("pwd id response is malformed"); return 0; } memcpy(pwd_session->peer_id, id->identity, pwd_session->peer_id_len); pwd_session->peer_id[pwd_session->peer_id_len] = '\0'; /* * make fake request to get the password for the usable ID */ if ((fake = request_alloc_fake(handler->request)) == NULL) { RDEBUG("pwd unable to create fake request!"); return 0; } if ((fake->username = pairmake("User-Name", "", T_OP_EQ)) == NULL) { RDEBUG("pwd unanable to create value pair for username!"); request_free(&fake); return 0; } memcpy(fake->username->vp_strvalue, pwd_session->peer_id, pwd_session->peer_id_len); fake->username->length = pwd_session->peer_id_len; fake->username->vp_strvalue[fake->username->length] = 0; if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) { fake->server = vp->vp_strvalue; } else if (inst->conf->virtual_server) { fake->server = inst->conf->virtual_server; } /* else fake->server == request->server */ if ((debug_flag > 0) && fr_log_fp) { RDEBUG("Sending tunneled request"); debug_pair_list(fake->packet->vps); fprintf(fr_log_fp, "server %s {\n", (fake->server == NULL) ? "" : fake->server); } /* * Call authorization recursively, which will * get the password. */ module_authorize(0, fake); /* * Note that we don't do *anything* with the reply * attributes. */ if ((debug_flag > 0) && fr_log_fp) { fprintf(fr_log_fp, "} # server %s\n", (fake->server == NULL) ? "" : fake->server); RDEBUG("Got tunneled reply code %d", fake->reply->code); debug_pair_list(fake->reply->vps); } if ((pw = pairfind(fake->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) == NULL) { DEBUG2("failed to find password for %s to do pwd authentication", pwd_session->peer_id); request_free(&fake); return 0; } if (compute_password_element(pwd_session, pwd_session->group_num, pw->data.strvalue, strlen(pw->data.strvalue), inst->conf->server_id, strlen(inst->conf->server_id), pwd_session->peer_id, strlen(pwd_session->peer_id), &pwd_session->token)) { DEBUG2("failed to obtain password element :-("); request_free(&fake); return 0; } request_free(&fake); /* * compute our scalar and element */ if (compute_scalar_element(pwd_session, inst->bnctx)) { DEBUG2("failed to compute server's scalar and element"); return 0; } if (((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { DEBUG2("server point allocation failed"); return 0; } /* * element is a point, get both coordinates: x and y */ if (!EC_POINT_get_affine_coordinates_GFp(pwd_session->group, pwd_session->my_element, x, y, inst->bnctx)) { DEBUG2("server point assignment failed"); BN_free(x); BN_free(y); return 0; } /* * construct request */ pwd_session->out_buf_len = BN_num_bytes(pwd_session->order) + (2 * BN_num_bytes(pwd_session->prime)); if ((pwd_session->out_buf = malloc(pwd_session->out_buf_len)) == NULL) { radlog(L_ERR, "rlm_eap_pwd: out of memory to send commit"); return 0; } memset(pwd_session->out_buf, 0, pwd_session->out_buf_len); ptr = pwd_session->out_buf; offset = BN_num_bytes(pwd_session->prime) - BN_num_bytes(x); BN_bn2bin(x, ptr + offset); ptr += BN_num_bytes(pwd_session->prime); offset = BN_num_bytes(pwd_session->prime) - BN_num_bytes(y); BN_bn2bin(y, ptr + offset); ptr += BN_num_bytes(pwd_session->prime); offset = BN_num_bytes(pwd_session->order) - BN_num_bytes(pwd_session->my_scalar); BN_bn2bin(pwd_session->my_scalar, ptr + offset); pwd_session->state = PWD_STATE_COMMIT; ret = send_pwd_request(pwd_session, eap_ds); break; case PWD_STATE_COMMIT: if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_COMMIT) { RDEBUG2("pwd exchange is incorrect: not commit!"); return 0; } /* * process the peer's commit and generate the shared key, k */ if (process_peer_commit(pwd_session, buf, inst->bnctx)) { RDEBUG2("failed to process peer's commit"); return 0; } /* * compute our confirm blob */ if (compute_server_confirm(pwd_session, pwd_session->my_confirm, inst->bnctx)) { radlog(L_ERR, "rlm_eap_pwd: failed to compute confirm!"); return 0; } /* * construct a response...which is just our confirm blob */ pwd_session->out_buf_len = SHA256_DIGEST_LENGTH; if ((pwd_session->out_buf = malloc(pwd_session->out_buf_len)) == NULL) { radlog(L_ERR, "rlm_eap_pwd: out of memory to send confirm"); return 0; } memset(pwd_session->out_buf, 0, pwd_session->out_buf_len); memcpy(pwd_session->out_buf, pwd_session->my_confirm, SHA256_DIGEST_LENGTH); pwd_session->state = PWD_STATE_CONFIRM; ret = send_pwd_request(pwd_session, eap_ds); break; case PWD_STATE_CONFIRM: if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_CONFIRM) { RDEBUG2("pwd exchange is incorrect: not commit!"); return 0; } if (compute_peer_confirm(pwd_session, peer_confirm, inst->bnctx)) { RDEBUG2("pwd exchange cannot compute peer's confirm"); return 0; } if (memcmp(peer_confirm, buf, SHA256_DIGEST_LENGTH)) { RDEBUG2("pwd exchange fails: peer confirm is incorrect!"); return 0; } if (compute_keys(pwd_session, peer_confirm, msk, emsk)) { RDEBUG2("pwd exchange cannot generate (E)MSK!"); return 0; } eap_ds->request->code = PW_EAP_SUCCESS; /* * return the MSK (in halves) */ outvps = &handler->request->reply->vps; add_reply(outvps, "MS-MPPE-Recv-Key", msk, MPPE_KEY_LEN); add_reply(outvps, "MS-MPPE-Send-Key", msk+MPPE_KEY_LEN, MPPE_KEY_LEN); ret = 1; break; default: RDEBUG2("unknown PWD state"); return 0; } /* * we processed the buffered fragments, get rid of them */ if (pwd_session->in_buf) { free(pwd_session->in_buf); pwd_session->in_buf = NULL; } return ret; }
static int attr_rewrite_instantiate(CONF_SECTION *conf, void **instance) { rlm_attr_rewrite_t *data; DICT_ATTR *dattr; /* * Set up a storage area for instance data */ data = rad_malloc(sizeof(*data)); if (!data) { return -1; } memset(data, 0, sizeof(*data)); /* * If the configuration parameters can't be parsed, then * fail. */ if (cf_section_parse(conf, data, module_config) < 0) { free(data); return -1; } /* * Discover the attribute number of the key. */ if (data->attribute == NULL) { radlog(L_ERR, "rlm_attr_rewrite: 'attribute' must be set."); return -1; } if (data->search == NULL || data->replace == NULL) { radlog(L_ERR, "rlm_attr_rewrite: search/replace strings must be set."); return -1; } data->search_len = strlen(data->search); data->replace_len = strlen(data->replace); if (data->replace_len == 0 && data->new_attr){ radlog(L_ERR, "rlm_attr_rewrite: replace string must not be zero length in order to create new attribute."); return -1; } if (data->num_matches < 1 || data->num_matches > MAX_STRING_LEN) { radlog(L_ERR, "rlm_attr_rewrite: Illegal range for match number."); return -1; } if (data->searchin_str == NULL) { radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet."); data->searchin = RLM_REGEX_INPACKET; } else{ if (strcmp(data->searchin_str, "packet") == 0) data->searchin = RLM_REGEX_INPACKET; else if (strcmp(data->searchin_str, "config") == 0) data->searchin = RLM_REGEX_INCONFIG; else if (strcmp(data->searchin_str, "control") == 0) data->searchin = RLM_REGEX_INCONFIG; else if (strcmp(data->searchin_str, "reply") == 0) data->searchin = RLM_REGEX_INREPLY; else if (strcmp(data->searchin_str, "proxy") == 0) data->searchin = RLM_REGEX_INPROXY; else if (strcmp(data->searchin_str, "proxy_reply") == 0) data->searchin = RLM_REGEX_INPROXYREPLY; else { radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet."); data->searchin = RLM_REGEX_INPACKET; } } dattr = dict_attrbyname(data->attribute); if (dattr == NULL) { radlog(L_ERR, "rlm_attr_rewrite: No such attribute %s", data->attribute); return -1; } data->attr_num = dattr->attr; /* Add the module instance name */ data->name = cf_section_name2(conf); /* may be NULL */ *instance = data; return 0; }
/* * if we have something to log, then we log it * otherwise we return the retcode as soon as possible */ static int do_logging(char *str, int retcode) { if (str && (*str != '\0')) radlog(L_INFO,"%s", str); return retcode; }
static int do_linelog(void *instance, REQUEST *request) { int fd = -1; char buffer[4096]; char *p; char line[1024]; rlm_linelog_t *inst = (rlm_linelog_t*) instance; const char *value = inst->line; if (inst->reference) { CONF_ITEM *ci; CONF_PAIR *cp; radius_xlat(line + 1, sizeof(line) - 2, inst->reference, request, linelog_escape_func); line[0] = '.'; /* force to be in current section */ /* * Don't allow it to go back up */ if (line[1] == '.') goto do_log; ci = cf_reference_item(NULL, inst->cs, line); if (!ci) { RDEBUG2("No such entry \"%s\"", line); return RLM_MODULE_NOOP; } if (!cf_item_is_pair(ci)) { RDEBUG2("Entry \"%s\" is not a variable assignment ", line); goto do_log; } cp = cf_itemtopair(ci); value = cf_pair_value(cp); if (!value) { RDEBUG2("Entry \"%s\" has no value", line); goto do_log; } /* * Value exists, but is empty. Don't log anything. */ if (!*value) return RLM_MODULE_OK; } do_log: /* * FIXME: Check length. */ if (strcmp(inst->filename, "syslog") != 0) { radius_xlat(buffer, sizeof(buffer), inst->filename, request, NULL); /* check path and eventually create subdirs */ p = strrchr(buffer,'/'); if (p) { *p = '\0'; if (rad_mkdir(buffer, 0700) < 0) { radlog_request(L_ERR, 0, request, "rlm_linelog: Failed to create directory %s: %s", buffer, strerror(errno)); return RLM_MODULE_FAIL; } *p = '/'; } fd = open(buffer, O_WRONLY | O_APPEND | O_CREAT, inst->permissions); if (fd == -1) { radlog(L_ERR, "rlm_linelog: Failed to open %s: %s", buffer, strerror(errno)); return RLM_MODULE_FAIL; } } /* * FIXME: Check length. */ radius_xlat(line, sizeof(line) - 1, value, request, linelog_escape_func); if (fd >= 0) { strcat(line, "\n"); write(fd, line, strlen(line)); close(fd); #ifdef HAVE_SYSLOG_H } else { syslog(LOG_INFO, "%s", line); #endif } return RLM_MODULE_OK; }
/* * Read the users, huntgroups or hints file. * Return a PAIR_LIST. */ int pairlist_read(const char *file, PAIR_LIST **list, int complain) { FILE *fp; int mode = FIND_MODE_NAME; char entry[256]; char buffer[8192]; const char *ptr; VALUE_PAIR *check_tmp; VALUE_PAIR *reply_tmp; PAIR_LIST *pl = NULL, *t; PAIR_LIST **last = &pl; int lineno = 0; int old_lineno = 0; FR_TOKEN parsecode; char newfile[8192]; /* * Open the file. The error message should be a little * more useful... */ if ((fp = fopen(file, "r")) == NULL) { if (!complain) return -1; radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s", file, strerror(errno)); return -1; } parsecode = T_EOL; /* * Read the entire file into memory for speed. */ while(fgets(buffer, sizeof(buffer), fp) != NULL) { lineno++; if (!feof(fp) && (strchr(buffer, '\n') == NULL)) { fclose(fp); radlog(L_ERR, "%s[%d]: line too long", file, lineno); pairlist_free(&pl); return -1; } if (buffer[0] == '#' || buffer[0] == '\n') continue; /* * If the line contains nothing but whitespace, * ignore it. */ ptr = buffer; while (isspace((int) *ptr)) ptr++; if (*ptr == '\0') continue; parse_again: if(mode == FIND_MODE_NAME) { /* * Find the entry starting with the users name */ if (isspace((int) buffer[0])) { if (parsecode != T_EOL) { radlog(L_ERR|L_CONS, "%s[%d]: Unexpected trailing comma for entry %s", file, lineno, entry); fclose(fp); return -1; } continue; } ptr = buffer; getword(&ptr, entry, sizeof(entry)); /* * Include another file if we see * $INCLUDE filename */ if (strcasecmp(entry, "$include") == 0) { while(isspace((int) *ptr)) ptr++; /* * If it's an absolute pathname, * then use it verbatim. * * If not, then make the $include * files *relative* to the current * file. */ if (FR_DIR_IS_RELATIVE(ptr)) { char *p; strlcpy(newfile, file, sizeof(newfile)); p = strrchr(newfile, FR_DIR_SEP); if (!p) { p = newfile + strlen(newfile); *p = FR_DIR_SEP; } getword(&ptr, p + 1, sizeof(newfile) - 1 - (p - buffer)); } else { getword(&ptr, newfile, sizeof(newfile)); } t = NULL; if (pairlist_read(newfile, &t, 0) != 0) { pairlist_free(&pl); radlog(L_ERR|L_CONS, "%s[%d]: Could not open included file %s: %s", file, lineno, newfile, strerror(errno)); fclose(fp); return -1; } *last = t; /* * t may be NULL, it may have one * entry, or it may be a linked list * of entries. Go to the end of the * list. */ while (*last) last = &((*last)->next); continue; } /* * Parse the check values */ check_tmp = NULL; reply_tmp = NULL; old_lineno = lineno; parsecode = userparse(ptr, &check_tmp); if (parsecode == T_OP_INVALID) { pairlist_free(&pl); radlog(L_ERR|L_CONS, "%s[%d]: Parse error (check) for entry %s: %s", file, lineno, entry, fr_strerror()); fclose(fp); return -1; } else if (parsecode == T_COMMA) { radlog(L_ERR|L_CONS, "%s[%d]: Unexpected trailing comma in check item list for entry %s", file, lineno, entry); fclose(fp); return -1; } mode = FIND_MODE_REPLY; parsecode = T_COMMA; } else { if(*buffer == ' ' || *buffer == '\t') { if (parsecode != T_COMMA) { radlog(L_ERR|L_CONS, "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s", file, lineno, entry); fclose(fp); return -1; } /* * Parse the reply values */ parsecode = userparse(buffer, &reply_tmp); /* valid tokens are 1 or greater */ if (parsecode < 1) { pairlist_free(&pl); radlog(L_ERR|L_CONS, "%s[%d]: Parse error (reply) for entry %s: %s", file, lineno, entry, fr_strerror()); fclose(fp); return -1; } } else { size_t entry_len; char *q; entry_len = strlen(entry) + 1; /* * Done with this entry... */ q = rad_malloc(sizeof(*t) + entry_len); t = (PAIR_LIST *) q; memset(t, 0, sizeof(*t)); t->check = check_tmp; t->reply = reply_tmp; t->lineno = old_lineno; check_tmp = NULL; reply_tmp = NULL; q += sizeof(*t); memcpy(q, entry, entry_len); t->name = q; *last = t; last = &(t->next); mode = FIND_MODE_NAME; if (buffer[0] != 0) goto parse_again; } } } /* * Make sure that we also read the last line of the file! */ if (mode == FIND_MODE_REPLY) { buffer[0] = 0; goto parse_again; } fclose(fp); *list = pl; return 0; }
/************************************************************************* * * Function: redis_get_socket * * Purpose: Return a REDIS socket from the connection pool * *************************************************************************/ REDISSOCK *redis_get_socket(REDIS_INST *inst) { REDISSOCK *cur, *start; int tried_to_connect = 0; int unconnected = 0; time_t now = time(NULL); /* * Start at the last place we left off. */ start = inst->last_used; if (!start) start = inst->redispool; cur = start; while (cur) { #ifdef HAVE_PTHREAD_H /* * If this socket is in use by another thread, * skip it, and try another socket. * * If it isn't used, then grab it ourselves. */ if (pthread_mutex_trylock(&cur->mutex) != 0) { goto next; } /* else we now have the lock */ #endif /* * If the socket has outlived its lifetime, and * is connected, close it, and mark it as open for * reconnections. */ if (inst->lifetime && (cur->state == sockconnected) && ((cur->connected + inst->lifetime) < now)) { DEBUG2("Closing socket %d as its lifetime has been exceeded", cur->id); redis_close_socket(inst, cur); cur->state = sockunconnected; goto reconnect; } /* * If we have performed too many queries over this * socket, then close it. */ if (inst->max_queries && (cur->state == sockconnected) && (cur->queries >= inst->max_queries)) { DEBUG2("Closing socket %d as its max_queries has been exceeded", cur->id); redis_close_socket(inst, cur); cur->state = sockunconnected; goto reconnect; } /* * If we happen upon an unconnected socket, and * this instance's grace period on * (re)connecting has expired, then try to * connect it. This should be really rare. */ if ((cur->state == sockunconnected) && (now > inst->connect_after)) { reconnect: radlog(L_INFO, "rlm_redis (%s): Trying to (re)connect unconnected handle %d..", inst->xlat_name, cur->id); tried_to_connect++; connect_single_socket(inst, cur); } /* if we still aren't connected, ignore this handle */ if (cur->state == sockunconnected) { DEBUG("rlm_redis (%s): Ignoring unconnected handle %d..", inst->xlat_name, cur->id); unconnected++; #ifdef HAVE_PTHREAD_H pthread_mutex_unlock(&cur->mutex); #endif goto next; } /* should be connected, grab it */ DEBUG("rlm_redis (%s): Reserving redis socket id: %d", inst->xlat_name, cur->id); if (unconnected != 0 || tried_to_connect != 0) { DEBUG("rlm_redis (%s): got socket %d after skipping %d unconnected handles, tried to reconnect %d though", inst->xlat_name, cur->id, unconnected, tried_to_connect); } /* * The socket is returned in the locked * state. * * We also remember where we left off, * so that the next search can start from * here. * * Note that multiple threads MAY over-write * the 'inst->last_used' variable. This is OK, * as it's a pointer only used for reading. */ inst->last_used = cur->next; cur->queries++; return cur; /* move along the list */ next: cur = cur->next; /* * Because we didnt start at the start, once we * hit the end of the linklist, we should go * back to the beginning and work toward the * middle! */ if (!cur) { cur = inst->redispool; } /* * If we're at the socket we started */ if (cur == start) { break; } } /* * Suppress most of the log messages. We don't want to * flood the log with this message for EVERY packet. * Instead, write to the log only once a second or so. * * This code has race conditions when threaded, but the * only result is that a few more messages are logged. */ if (now <= last_logged_failure) return NULL; last_logged_failure = now; /* We get here if every DB handle is unconnected and unconnectABLE */ radlog(L_INFO, "rlm_redis (%s): There are no DB handles to use! skipped %d, tried to connect %d", inst->xlat_name, unconnected, tried_to_connect); return NULL; }
/** 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, TAG_ANY) == 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; }
static int redis_xlat(void *instance, REQUEST *request, char *fmt, char *out, size_t freespace, UNUSED RADIUS_ESCAPE_STRING func) { REDIS_INST *inst = instance; REDISSOCK *dissocket; size_t ret = 0; char *buffer_ptr; char buffer[21]; char querystr[MAX_QUERY_LEN]; if (!radius_xlat(querystr, sizeof(querystr), fmt, request, redis_escape_func)) { radlog(L_ERR, "rlm_redis (%s): xlat failed.", inst->xlat_name); return 0; } if ((dissocket = redis_get_socket(inst)) == NULL) { radlog(L_ERR, "rlm_redis (%s): redis_get_socket() failed", inst->xlat_name); return 0; } /* Query failed for some reason, release socket and return */ if (rlm_redis_query(dissocket, inst, querystr) < 0) { rlm_redis_finish_query(dissocket); redis_release_socket(inst,dissocket); return 0; } switch (dissocket->reply->type) { case REDIS_REPLY_INTEGER: buffer_ptr = buffer; snprintf(buffer_ptr, sizeof(buffer), "%lld", dissocket->reply->integer); ret = strlen(buffer_ptr); break; case REDIS_REPLY_STATUS: case REDIS_REPLY_STRING: buffer_ptr = dissocket->reply->str; ret = dissocket->reply->len; break; default: buffer_ptr = NULL; break; } if ((ret >= freespace) || (buffer_ptr == NULL)) { RDEBUG("rlm_redis (%s): Can't write result, insufficient space or unsupported result\n", inst->xlat_name); rlm_redis_finish_query(dissocket); redis_release_socket(inst,dissocket); return 0; } strlcpy(out,buffer_ptr,freespace); rlm_redis_finish_query(dissocket); redis_release_socket(inst,dissocket); return ret; }
static int mod_instantiate(CONF_SECTION *conf, void *instance) { rlm_krb5_t *inst = instance; krb5_error_code ret; #ifndef HEIMDAL_KRB5 krb5_keytab keytab; char keytab_name[200]; char *princ_name; #endif #ifdef HEIMDAL_KRB5 DEBUG("Using Heimdal Kerberos library"); #else DEBUG("Using MIT Kerberos library"); #endif if (!krb5_is_thread_safe()) { /* * rlm_krb5 was built as threadsafe */ #ifdef KRB5_IS_THREAD_SAFE ERROR("Build time libkrb5 was threadsafe, but run time library claims not to be"); ERROR("Modify runtime linker path (LD_LIBRARY_PATH on most systems), to prefer threadsafe libkrb5"); return -1; /* * rlm_krb5 was not built as threadsafe */ #else radlog(L_WARN, "libkrb5 is not threadsafe, recompile it with thread support enabled (" # ifdef HEIMDAL_KRB5 "--enable-pthread-support" # else "--disable-thread-support=no" # endif ")"); WARN("rlm_krb5 will run in single threaded mode, performance may be degraded"); } else { WARN("Build time libkrb5 was not threadsafe, but run time library claims to be"); WARN("Reconfigure and recompile rlm_krb5 to enable thread support"); #endif } inst->xlat_name = cf_section_name2(conf); if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf); ret = krb5_init_context(&inst->context); if (ret) { ERROR("rlm_krb5 (%s): context initialisation failed: %s", inst->xlat_name, rlm_krb5_error(NULL, ret)); return -1; } /* * Split service principal into service and host components * they're needed to build the server principal in MIT, * and to set the validation service in Heimdal. */ if (inst->service_princ) { size_t len; /* Service principal appears to contain a host component */ inst->hostname = strchr(inst->service_princ, '/'); if (inst->hostname) { len = (inst->hostname - inst->service_princ); inst->hostname++; } else { len = strlen(inst->service_princ); } if (len) { inst->service = talloc_array(inst, char, (len + 1)); strlcpy(inst->service, inst->service_princ, len + 1); } }
/* * Relay the request to a remote server. * Returns: * * RLM_MODULE_FAIL: we don't reply, caller returns without replying * RLM_MODULE_NOOP: caller falls through to normal processing * RLM_MODULE_HANDLED : we reply, caller returns without replying */ int proxy_send(REQUEST *request) { int rcode; VALUE_PAIR *proxypair; VALUE_PAIR *replicatepair; VALUE_PAIR *realmpair; VALUE_PAIR *namepair; VALUE_PAIR *strippednamepair; VALUE_PAIR *delaypair; VALUE_PAIR *vp, *vps; REALM *realm; char *realmname; int replicating; /* * Not authentication or accounting. Stop it. */ if ((request->packet->code != PW_AUTHENTICATION_REQUEST) && (request->packet->code != PW_ACCOUNTING_REQUEST)) { return RLM_MODULE_FAIL; } /* * The timestamp is used below to figure the * next_try. The request needs to "hang around" until * either the other server sends a reply or the retry * count has been exceeded. Until then, it should not * be eligible for the time-based cleanup. --Pac. */ /* Look for proxy/replicate signs */ /* FIXME - What to do if multiple Proxy-To/Replicate-To attrs are * set... Log an error? Actually replicate to multiple places? That * would be cool. For now though, I'll just take the first one and * ignore the rest. */ proxypair = pairfind(request->config_items, PW_PROXY_TO_REALM); replicatepair = pairfind(request->config_items, PW_REPLICATE_TO_REALM); if (proxypair) { realmpair = proxypair; replicating = 0; } else if (replicatepair) { realmpair = replicatepair; replicating = 1; } else { /* * Neither proxy or replicate attributes are set, * so we can exit from the proxy code. */ return RLM_MODULE_NOOP; } /* * If the server has already decided to reject the request, * then don't try to proxy it. */ if (request->reply->code == PW_AUTHENTICATION_REJECT) { DEBUG2("Cancelling proxy as request was already rejected"); return RLM_MODULE_REJECT; } if (((vp = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL) && (vp->lvalue == PW_AUTHTYPE_REJECT)) { DEBUG2("Cancelling proxy as request was already rejected"); return RLM_MODULE_REJECT; } /* * Length == 0 means it exists, but there's no realm. * Don't proxy it. */ if (realmpair->length == 0) { return RLM_MODULE_NOOP; } realmname = (char *)realmpair->strvalue; /* * Look for the realm, using the load balancing * version of realm find. */ realm = proxy_realm_ldb(request, realmname, (request->packet->code == PW_ACCOUNTING_REQUEST)); if (realm == NULL) { return RLM_MODULE_FAIL; } /* * Remember that we sent the request to a Realm. */ pairadd(&request->packet->vps, pairmake("Realm", realm->realm, T_OP_EQ)); /* * Access-Request: look for LOCAL realm. * Accounting-Request: look for LOCAL realm. */ if (((request->packet->code == PW_AUTHENTICATION_REQUEST) && (realm->ipaddr == htonl(INADDR_NONE))) || ((request->packet->code == PW_ACCOUNTING_REQUEST) && (realm->acct_ipaddr == htonl(INADDR_NONE)))) { return RLM_MODULE_NOOP; } /* * Copy the request, then look up * name and plain-text password in the copy. * * Note that the User-Name attribute is the *original* * as sent over by the client. The Stripped-User-Name * attribute is the one hacked through the 'hints' file. */ vps = paircopy(request->packet->vps); namepair = pairfind(vps, PW_USER_NAME); strippednamepair = pairfind(vps, PW_STRIPPED_USER_NAME); /* * If there's a Stripped-User-Name attribute in the * request, then use THAT as the User-Name for the * proxied request, instead of the original name. * * This is done by making a copy of the Stripped-User-Name * attribute, turning it into a User-Name attribute, * deleting the Stripped-User-Name and User-Name attributes * from the vps list, and making the new User-Name * the head of the vps list. */ if (strippednamepair) { vp = paircreate(PW_USER_NAME, PW_TYPE_STRING); if (!vp) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } memcpy(vp->strvalue, strippednamepair->strvalue, sizeof(vp->strvalue)); vp->length = strippednamepair->length; pairdelete(&vps, PW_USER_NAME); pairdelete(&vps, PW_STRIPPED_USER_NAME); vp->next = vps; namepair = vp; vps = vp; } /* * Now build a new RADIUS_PACKET and send it. * * FIXME: it could be that the id wraps around too fast if * we have a lot of requests, it might be better to keep * a seperate ID value per remote server. * * OTOH the remote radius server should be smart enough to * compare _both_ ID and vector. Right ? */ if ((request->proxy = rad_alloc(TRUE)) == NULL) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } /* * Proxied requests get sent out the proxy FD ONLY. */ request->proxy->sockfd = proxyfd; request->proxy->code = request->packet->code; if (request->packet->code == PW_AUTHENTICATION_REQUEST) { request->proxy->dst_port = realm->auth_port; request->proxy->dst_ipaddr = realm->ipaddr; } else if (request->packet->code == PW_ACCOUNTING_REQUEST) { request->proxy->dst_port = realm->acct_port; request->proxy->dst_ipaddr = realm->acct_ipaddr; } rad_assert(request->proxy->vps == NULL); request->proxy->vps = vps; /* * Add the request to the list of outstanding requests. * Note that request->proxy->id is a 16 bits value, * while rad_send sends only the 8 least significant * bits of that same value. */ request->proxy->id = (proxy_id++) & 0xff; proxy_id &= 0xffff; /* * Add PROXY_STATE attribute. */ proxy_addinfo(request); /* * If there is no PW_CHAP_CHALLENGE attribute but there * is a PW_CHAP_PASSWORD we need to add it since we can't * use the request authenticator anymore - we changed it. */ if (pairfind(vps, PW_CHAP_PASSWORD) && pairfind(vps, PW_CHAP_CHALLENGE) == NULL) { vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING); if (!vp) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } vp->length = AUTH_VECTOR_LEN; memcpy(vp->strvalue, request->packet->vector, AUTH_VECTOR_LEN); pairadd(&vps, vp); } /* * Set up for sending the request. */ memcpy(request->proxysecret, realm->secret, sizeof(request->proxysecret)); request->proxy_is_replicate = replicating; request->proxy_try_count = mainconfig.proxy_retry_count - 1; request->proxy_next_try = request->timestamp + mainconfig.proxy_retry_delay; delaypair = pairfind(vps, PW_ACCT_DELAY_TIME); request->proxy->timestamp = request->timestamp - (delaypair ? delaypair->lvalue : 0); /* * Do pre-proxying */ rcode = module_pre_proxy(request); /* * Do NOT free proxy->vps, the pairs are needed for the * retries! --Pac. */ /* * Delay sending the proxy packet until after we've * done the work above, playing with the request. * * After this point, it becomes dangerous to play * with the request data structure, as the reply MAY * come in and get processed before we're done with it here. * * Only proxy the packet if the pre-proxy code succeeded. */ if ((rcode == RLM_MODULE_OK) || (rcode == RLM_MODULE_NOOP) || (rcode == RLM_MODULE_UPDATED)) { rad_send(request->proxy, NULL, (char *)request->proxysecret); if (!replicating) { rcode = RLM_MODULE_HANDLED; /* caller doesn't reply */ } else { rcode = RLM_MODULE_NOOP; /* caller replies */ } } else { rcode = RLM_MODULE_FAIL; /* caller doesn't reply */ } return rcode; }
static int NEVER_RETURNS not_implemented(SQLSOCK * sqlsocket, SQL_CONFIG *config) { radlog(L_ERR, "sql_postgresql: calling unimplemented function"); exit(1); }
/************************************************************************* * * Function: sql_fetch_row * * Purpose: database specific fetch_row. Returns a SQL_ROW struct * with all the data for the query in 'sqlsocket->row'. Returns * 0 on success, -1 on failure, SQL_DOWN if database is down. * *************************************************************************/ static int sql_fetch_row(SQLSOCK * sqlsocket, SQL_CONFIG *config) { int returnCode = -1; rlm_sql_sqlite_sock *sqlite_sock = sqlsocket->conn; const char *blob; int blobLen; int status; int colindex = 0; int colcount = 0; int coltype = 0; int colintvalue = 0; int ret_blob_size = 0; char **rowdata = NULL; const unsigned char *textStr; char intStr[256]; status = sqlite3_step(sqlite_sock->pStmt); radlog(L_DBG, "rlm_sql_sqlite: sqlite3_step = %d\n", status); if (status == SQLITE_DONE) { sql_free_rowdata(sqlsocket, sqlite_sock->columnCount); return 0; } else if (status == SQLITE_ROW) { if (sqlite_sock->columnCount == 0) { sqlite_sock->columnCount = sql_num_fields(sqlsocket, config); } colcount = sqlite_sock->columnCount; if (colcount == 0) return -1; sql_free_rowdata(sqlsocket, colcount); ret_blob_size = sizeof(char *) * (colcount+1); rowdata = (char **)rad_malloc(ret_blob_size); /* Space for pointers */ if (rowdata != NULL) { memset(rowdata, 0, ret_blob_size); /* NULL-pad the pointers */ sqlsocket->row = rowdata; } for (colindex = 0; colindex < colcount; colindex++) { coltype = sqlite3_column_type(sqlite_sock->pStmt, colindex); switch (coltype) { case SQLITE_INTEGER: colintvalue = sqlite3_column_int(sqlite_sock->pStmt, colindex); snprintf(intStr, sizeof(intStr), "%d", colintvalue); rowdata[colindex] = strdup(intStr); break; case SQLITE_TEXT: textStr = sqlite3_column_text(sqlite_sock->pStmt, colindex); if (textStr != NULL) rowdata[colindex] = strdup((const char *)textStr); break; case SQLITE_BLOB: blob = sqlite3_column_blob(sqlite_sock->pStmt, colindex); if (blob != NULL) { blobLen = sqlite3_column_bytes(sqlite_sock->pStmt, colindex); rowdata[colindex] = (char *)rad_malloc(blobLen + 1); if (rowdata[colindex] != NULL) { memcpy(rowdata[colindex], blob, blobLen); } } break; default: break; } } returnCode = 0; } return returnCode; }
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)) != 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)) { 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; 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; 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->attr_num == PW_USER_NAME) attr_vp = request->username; else if (data->attr_num == 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; 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; 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->attr_num); break; } do_again: if (tmp != NULL) attr_vp = pairfind(tmp, data->attr_num); 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) && 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) == 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; }
/************************************************************************* * * Function: sql_userparse * * Purpose: Read entries from the database and fill VALUE_PAIR structures * *************************************************************************/ int sql_userparse(VALUE_PAIR ** first_pair, SQL_ROW row) { VALUE_PAIR *pair; const char *ptr, *value; char buf[MAX_STRING_LEN]; char do_xlat = 0; FR_TOKEN token, operator = T_EOL; /* * Verify the 'Attribute' field */ if (row[2] == NULL || row[2][0] == '\0') { radlog(L_ERR, "rlm_sql: The 'Attribute' field is empty or NULL, skipping the entire row."); return -1; } /* * Verify the 'op' field */ if (row[4] != NULL && row[4][0] != '\0') { ptr = row[4]; operator = gettoken(&ptr, buf, sizeof(buf)); if ((operator < T_OP_ADD) || (operator > T_OP_CMP_EQ)) { radlog(L_ERR, "rlm_sql: Invalid operator \"%s\" for attribute %s", row[4], row[2]); return -1; } } else { /* * Complain about empty or invalid 'op' field */ operator = T_OP_CMP_EQ; radlog(L_ERR, "rlm_sql: The 'op' field for attribute '%s = %s' is NULL, or non-existent.", row[2], row[3]); radlog(L_ERR, "rlm_sql: You MUST FIX THIS if you want the configuration to behave as you expect."); } /* * The 'Value' field may be empty or NULL */ value = row[3]; /* * If we have a new-style quoted string, where the * *entire* string is quoted, do xlat's. */ if (row[3] != NULL && ((row[3][0] == '\'') || (row[3][0] == '`') || (row[3][0] == '"')) && (row[3][0] == row[3][strlen(row[3])-1])) { token = gettoken(&value, buf, sizeof(buf)); switch (token) { /* * Take the unquoted string. */ case T_SINGLE_QUOTED_STRING: case T_DOUBLE_QUOTED_STRING: value = buf; break; /* * Mark the pair to be allocated later. */ case T_BACK_QUOTED_STRING: value = NULL; do_xlat = 1; break; /* * Keep the original string. */ default: value = row[3]; break; } } /* * Create the pair */ if (do_xlat) { pair = pairmake_xlat(row[2], value, operator); } else { pair = pairmake(row[2], value, operator); } if (pair == NULL) { radlog(L_ERR, "rlm_sql: Failed to create the pair: %s", fr_strerror()); return -1; } /* * Add the pair into the packet */ pairadd(first_pair, pair); return 0; }
/* * Do any per-module initialization that is separate to each * configured instance of the module. e.g. set up connections * to external databases, read configuration files, set up * dictionary entries, etc. * * If configuration information is given in the config section * that must be referenced in later calls, store a handle to it * in *instance otherwise put a null pointer there. */ static int sqlippool_instantiate(CONF_SECTION * conf, void ** instance) { module_instance_t *modinst; rlm_sqlippool_t * data; const char * pool_name = NULL; /* * Set up a storage area for instance data */ data = rad_malloc(sizeof(*data)); memset(data, 0, sizeof(*data)); /* * If the configuration parameters can't be parsed, then * fail. */ if (cf_section_parse(conf, data, module_config) < 0) { free(data); return -1; } if (IS_EMPTY(data->sql_instance_name)) { radlog(L_ERR, "rlm_sqlippool: the 'sql-instance-name' variable must be set."); sqlippool_detach(data); return -1; } /* * Check that all the queries are in place */ if (IS_EMPTY(data->allocate_clear)) { radlog(L_ERR, "rlm_sqlippool: the 'allocate-clear' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->allocate_find)) { radlog(L_ERR, "rlm_sqlippool: the 'allocate-find' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->allocate_update)) { radlog(L_ERR, "rlm_sqlippool: the 'allocate-update' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->start_update)) { radlog(L_ERR, "rlm_sqlippool: the 'start-update' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->alive_update)) { radlog(L_ERR, "rlm_sqlippool: the 'alive-update' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->stop_clear)) { radlog(L_ERR, "rlm_sqlippool: the 'stop-clear' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->on_clear)) { radlog(L_ERR, "rlm_sqlippool: the 'on-clear' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->off_clear)) { radlog(L_ERR, "rlm_sqlippool: the 'off-clear' statement must be set."); sqlippool_detach(data); return -1; } pool_name = cf_section_name2(conf); if (pool_name != NULL) data->pool_name = strdup(pool_name); else data->pool_name = strdup("ippool"); modinst = find_module_instance(cf_section_find("modules"), data->sql_instance_name, 1); if (!modinst) { radlog(L_ERR, "sqlippool_instantiate: failed to find sql instance named %s", data->sql_instance_name); sqlippool_detach(data); return -1; } if (strcmp(modinst->entry->name, "rlm_sql") != 0) { radlog(L_ERR, "sqlippool_instantiate: Module \"%s\"" " is not an instance of the rlm_sql module", data->sql_instance_name); sqlippool_detach(data); return -1; } data->sql_inst = (SQL_INST *) modinst->insthandle; *instance = data; return 0; }
/* * Detach a instance give a chance to a module to make some internal setup ... */ static int perl_detach(void *instance) { PERL_INST *inst = (PERL_INST *) instance; int exitstatus=0,count=0; #ifdef USE_ITHREADS POOL_HANDLE *handle, *tmp, *tmp2; MUTEX_LOCK(&inst->perl_pool->mutex); inst->perl_pool->detach = yes; MUTEX_UNLOCK(&inst->perl_pool->mutex); for (handle = inst->perl_pool->head; handle != NULL; handle = handle->next) { radlog(L_DBG,"Detach perl 0x%lx", (unsigned long) handle->clone); /* * Wait until clone becomes idle */ MUTEX_LOCK(&handle->lock); /* * Give a clones chance to run detach function */ { dTHXa(handle->clone); PERL_SET_CONTEXT(handle->clone); { dSP; ENTER; SAVETMPS; PUSHMARK(SP); count = call_pv(inst->func_detach, G_SCALAR | G_EVAL ); SPAGAIN; if (count == 1) { exitstatus = POPi; /* * FIXME: bug in perl * */ if (exitstatus >= 100 || exitstatus < 0) { exitstatus = RLM_MODULE_FAIL; } } PUTBACK; FREETMPS; LEAVE; radlog(L_DBG,"detach at 0x%lx returned status %d", (unsigned long) handle->clone, exitstatus); } } MUTEX_UNLOCK(&handle->lock); } /* * Free handles */ for (tmp = inst->perl_pool->head; tmp !=NULL ; tmp = tmp2) { tmp2 = tmp->next; radlog(L_DBG,"rlm_perl:: Destroy perl"); rlm_perl_destruct(tmp->clone); delete_pool_handle(tmp,inst); } { dTHXa(inst->perl); #endif /* USE_ITHREADS */ PERL_SET_CONTEXT(inst->perl); { dSP; ENTER; SAVETMPS; PUSHMARK(SP); count = call_pv(inst->func_detach, G_SCALAR | G_EVAL ); SPAGAIN; if (count == 1) { exitstatus = POPi; if (exitstatus >= 100 || exitstatus < 0) { exitstatus = RLM_MODULE_FAIL; } } PUTBACK; FREETMPS; LEAVE; } #ifdef USE_ITHREADS } #endif xlat_unregister(inst->xlat_name, perl_xlat); free(inst->xlat_name); if (inst->func_authorize) free(inst->func_authorize); if (inst->func_authenticate) free(inst->func_authenticate); if (inst->func_accounting) free(inst->func_accounting); if (inst->func_preacct) free(inst->func_preacct); if (inst->func_checksimul) free(inst->func_checksimul); if (inst->func_pre_proxy) free(inst->func_pre_proxy); if (inst->func_post_proxy) free(inst->func_post_proxy); if (inst->func_post_auth) free(inst->func_post_auth); if (inst->func_detach) free(inst->func_detach); #ifdef USE_ITHREADS free(inst->perl_pool->head); free(inst->perl_pool->tail); MUTEX_DESTROY(&inst->perl_pool->mutex); free(inst->perl_pool); rlm_perl_destruct(inst->perl); #else perl_destruct(inst->perl); perl_free(inst->perl); #endif free(inst); return exitstatus; }
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; uint32_t uiCurr = 0; uint32_t uiLen = 0; char *username_string = NULL; char *shortUserName = NULL; VALUE_PAIR *response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT); #ifndef NDEBUG unsigned 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) { if (status != RLM_MODULE_NOOP) { RDEBUG2("od_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; RDEBUG2("OD username_string = %s, OD shortUserName=%s (length = %lu)\n", username_string, shortUserName, strlen(shortUserName)); /* User name length + username */ uiLen = (uint32_t)strlen(shortUserName); memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(uiLen)); uiCurr += sizeof(uiLen); memcpy(&(tDataBuff->fBufferData[uiCurr]), shortUserName, uiLen); uiCurr += uiLen; #ifndef NDEBUG RDEBUG2(" 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(uiLen)); uiCurr += sizeof(uiLen); memcpy(&(tDataBuff->fBufferData[uiCurr]), &(challenge->vp_strvalue[0]), uiLen); uiCurr += uiLen; #ifndef NDEBUG RDEBUG2(" 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(uiLen)); uiCurr += sizeof(uiLen); memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[2]), uiLen); uiCurr += uiLen; #ifndef NDEBUG RDEBUG2(" 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(uiLen)); uiCurr += sizeof(uiLen); memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[26]), uiLen); uiCurr += uiLen; /* Client generated use name (short name?) */ uiLen = (uint32_t)strlen(username_string); memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(uiLen)); uiCurr += sizeof(uiLen); 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) { uint32_t len; memcpy(&len, pStepBuff->fBufferData, sizeof(len)); if (len == 40) { char mschap_reply[42] = { '\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); RDEBUG2("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; }
static int pool_release(POOL_HANDLE *handle, PERL_INST *inst) { POOL_HANDLE *tmp, *tmp2; int spare, i, t; time_t now; /* * Lock it */ MUTEX_LOCK(&inst->perl_pool->mutex); /* * If detach is set then just release the mutex */ if (inst->perl_pool->detach == yes ) { handle->status = idle; MUTEX_UNLOCK(&handle->lock); MUTEX_UNLOCK(&inst->perl_pool->mutex); return 0; } MUTEX_UNLOCK(&handle->lock); handle->status = idle; inst->perl_pool->active_clones--; spare = inst->perl_pool->current_clones - inst->perl_pool->active_clones; radlog(L_DBG,"perl_pool total/active/spare [%d/%d/%d]" , inst->perl_pool->current_clones, inst->perl_pool->active_clones, spare); if (spare < inst->perl_pool->min_spare_clones) { t = inst->perl_pool->min_spare_clones - spare; for (i=0;i<t; i++) { if ((tmp = pool_grow(inst)) == NULL) { MUTEX_UNLOCK(&inst->perl_pool->mutex); return -1; } } MUTEX_UNLOCK(&inst->perl_pool->mutex); return 0; } now = time(NULL); if ((now - inst->perl_pool->time_when_last_added) < inst->perl_pool->cleanup_delay) { MUTEX_UNLOCK(&inst->perl_pool->mutex); return 0; } if (spare > inst->perl_pool->max_spare_clones) { spare -= inst->perl_pool->max_spare_clones; for (tmp = inst->perl_pool->head; (tmp !=NULL ) && (spare > 0) ; tmp = tmp2) { tmp2 = tmp->next; if(tmp->status == idle) { rlm_destroy_perl(tmp->clone); delete_pool_handle(tmp,inst); spare--; break; } } } /* * If the clone have reached max_request_per_clone clean it. */ if (inst->perl_pool->max_request_per_clone > 0 ) { if (handle->request_count > inst->perl_pool->max_request_per_clone) { rlm_destroy_perl(handle->clone); delete_pool_handle(handle,inst); } } /* * Hurry Up :) */ MUTEX_UNLOCK(&inst->perl_pool->mutex); return 0; }
static int eap_pwd_initiate (void *type_data, EAP_HANDLER *handler) { pwd_session_t *pwd_session; eap_pwd_t *inst = (eap_pwd_t *)type_data; VALUE_PAIR *vp; pwd_id_packet *pack; if (!inst || !handler) { radlog(L_ERR, "rlm_eap_pwd: initiate, NULL data provided"); return -1; } /* * make sure the server's been configured properly */ if (inst->conf->server_id == NULL) { radlog(L_ERR, "rlm_eap_pwd: server ID is not configured!"); return -1; } switch (inst->conf->group) { case 19: case 20: case 21: case 25: case 26: break; default: radlog(L_ERR, "rlm_eap_pwd: group is not supported!"); return -1; } if ((pwd_session = (pwd_session_t *)malloc(sizeof(*pwd_session))) == NULL) { radlog(L_ERR, "rlm_eap_pwd: initiate, out of memory (1)"); return -1; } /* * set things up so they can be free'd reliably */ pwd_session->group_num = inst->conf->group; pwd_session->private_value = NULL; pwd_session->peer_scalar = NULL; pwd_session->my_scalar = NULL; pwd_session->k = NULL; pwd_session->my_element = NULL; pwd_session->peer_element = NULL; pwd_session->group = NULL; pwd_session->pwe = NULL; pwd_session->order = NULL; pwd_session->prime = NULL; /* * figure out the MTU (basically do what eap-tls does) */ pwd_session->mtu = inst->conf->fragment_size; vp = pairfind(handler->request->packet->vps, PW_FRAMED_MTU, 0, TAG_ANY); if (vp && ((int)(vp->vp_integer - 9) < pwd_session->mtu)) { /* * 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. */ pwd_session->mtu = vp->vp_integer - 9; } pwd_session->state = PWD_STATE_ID_REQ; pwd_session->in_buf = NULL; pwd_session->out_buf_pos = 0; handler->opaque = pwd_session; handler->free_opaque = free_session; /* * construct an EAP-pwd-ID/Request */ pwd_session->out_buf_len = sizeof(pwd_id_packet) + strlen(inst->conf->server_id); if ((pwd_session->out_buf = malloc(pwd_session->out_buf_len)) == NULL) { radlog(L_ERR, "rlm_eap_pwd: initiate, out of memory to send ID request"); return -1; } memset(pwd_session->out_buf, 0, pwd_session->out_buf_len); pack = (pwd_id_packet *)pwd_session->out_buf; pack->group_num = htons(pwd_session->group_num); pack->random_function = EAP_PWD_DEF_RAND_FUN; pack->prf = EAP_PWD_DEF_PRF; pwd_session->token = random(); memcpy(pack->token, (char *)&pwd_session->token, 4); pack->prep = EAP_PWD_PREP_NONE; strcpy(pack->identity, inst->conf->server_id); handler->stage = AUTHENTICATE; return send_pwd_request(pwd_session, handler->eap_ds); }
/* * The xlat function */ static int perl_xlat(void *instance, REQUEST *request, char *fmt, char * out, size_t freespace, RADIUS_ESCAPE_STRING func) { PERL_INST *inst= (PERL_INST *) instance; PerlInterpreter *perl; char params[1024], *ptr, *tmp; int count, ret=0; STRLEN n_a; /* * Do an xlat on the provided string (nice recursive operation). */ if (!radius_xlat(params, sizeof(params), fmt, request, func)) { radlog(L_ERR, "rlm_perl: xlat failed."); return 0; } #ifndef USE_ITHREADS perl = inst->perl; #endif #ifdef USE_ITHREADS POOL_HANDLE *handle; if ((handle = pool_pop(instance)) == NULL) { return 0; } perl = handle->clone; radlog(L_DBG,"Found a interpetator 0x%lx",(unsigned long) perl); { dTHXa(perl); } #endif PERL_SET_CONTEXT(perl); { dSP; ENTER;SAVETMPS; ptr = strtok(params, " "); PUSHMARK(SP); while (ptr != NULL) { XPUSHs(sv_2mortal(newSVpv(ptr,0))); ptr = strtok(NULL, " "); } PUTBACK; count = call_pv(inst->func_xlat, G_SCALAR | G_EVAL); SPAGAIN; if (SvTRUE(ERRSV)) { radlog(L_ERR, "rlm_perl: perl_xlat exit %s\n", SvPV(ERRSV,n_a)); POPs ; } else if (count > 0) { tmp = POPp; ret = strlen(tmp); strncpy(out,tmp,ret); radlog(L_DBG,"rlm_perl: Len is %d , out is %s freespace is %d", ret, out,freespace); } PUTBACK ; FREETMPS ; LEAVE ; } #ifdef USE_ITHREADS pool_release(handle, instance); #endif return ret; }
/* * given a radius request with an EAP-SIM body, decode it into TLV pairs * * return value is TRUE if it succeeded, false if there was something * wrong and the packet should be discarded. * */ int unmap_eapsim_basictypes(RADIUS_PACKET *r, uint8_t *attr, unsigned int attrlen) { VALUE_PAIR *newvp; int eapsim_attribute; unsigned int eapsim_len; int es_attribute_count; es_attribute_count=0; /* big enough to have even a single attribute */ if(attrlen < 5) { radlog(L_ERR, "eap: EAP-Sim attribute too short: %d < 2", attrlen); return 0; } newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, 0, PW_TYPE_INTEGER); if (!newvp) return 0; newvp->vp_integer = attr[0]; newvp->length = 1; pairadd(&(r->vps), newvp); attr += 3; attrlen -= 3; /* now, loop processing each attribute that we find */ while(attrlen > 0) { if(attrlen < 2) { radlog(L_ERR, "eap: EAP-Sim attribute %d too short: %d < 2", es_attribute_count, attrlen); return 0; } eapsim_attribute = attr[0]; eapsim_len = attr[1] * 4; if(eapsim_len > attrlen) { radlog(L_ERR, "eap: EAP-Sim attribute %d (no.%d) has length longer than data (%d > %d)" , eapsim_attribute , es_attribute_count, eapsim_len, attrlen); return 0; } if(eapsim_len > MAX_STRING_LEN) { eapsim_len = MAX_STRING_LEN; } if (eapsim_len < 2) { radlog(L_ERR, "eap: EAP-Sim attribute %d (no.%d) has length too small", eapsim_attribute, es_attribute_count); return 0; } newvp = paircreate(eapsim_attribute+ATTRIBUTE_EAP_SIM_BASE, 0, PW_TYPE_OCTETS); memcpy(newvp->vp_strvalue, &attr[2], eapsim_len-2); newvp->length = eapsim_len-2; pairadd(&(r->vps), newvp); newvp = NULL; /* advance pointers, decrement length */ attr += eapsim_len; attrlen -= eapsim_len; es_attribute_count++; } return 1; }
/* * Do any per-module initialization that is separate to each * configured instance of the module. e.g. set up connections * to external databases, read configuration files, set up * dictionary entries, etc. * * If configuration information is given in the config section * that must be referenced in later calls, store a handle to it * in *instance otherwise put a null pointer there. * * Boyan: * Setup a hashes wich we will use later * parse a module and give him a chance to live * */ static int perl_instantiate(CONF_SECTION *conf, void **instance) { PERL_INST *inst = (PERL_INST *) instance; HV *rad_reply_hv; HV *rad_check_hv; HV *rad_request_hv; HV *rad_request_proxy_hv; HV *rad_request_proxy_reply_hv; AV *end_AV; char *embed[4], *xlat_name; int exitstatus = 0, argc=0; /* * Set up a storage area for instance data */ inst = rad_malloc(sizeof(PERL_INST)); memset(inst, 0, sizeof(PERL_INST)); /* * If the configuration parameters can't be parsed, then * fail. */ if (cf_section_parse(conf, inst, module_config) < 0) { free(inst); return -1; } embed[0] = NULL; if (inst->perl_flags) { embed[1] = inst->perl_flags; embed[2] = inst->module; embed[3] = "0"; argc = 4; } else { embed[1] = inst->module; embed[2] = "0"; argc = 3; } #ifdef USE_ITHREADS inst->perl = interp; if ((inst->perl = perl_alloc()) == NULL) { radlog(L_DBG, "rlm_perl: No memory for allocating new perl !"); return (-1); } perl_construct(inst->perl); PL_perl_destruct_level = 2; { dTHXa(inst->perl); } PERL_SET_CONTEXT(inst->perl); #else if ((inst->perl = perl_alloc()) == NULL) { radlog(L_ERR, "rlm_perl: No memory for allocating new perl !"); return -1; } perl_construct(inst->perl); #endif #if PERL_REVISION >= 5 && PERL_VERSION >=8 PL_exit_flags |= PERL_EXIT_DESTRUCT_END; #endif exitstatus = perl_parse(inst->perl, xs_init, argc, embed, NULL); end_AV = PL_endav; PL_endav = Nullav; if(!exitstatus) { exitstatus = perl_run(inst->perl); } else { radlog(L_ERR,"rlm_perl: perl_parse failed: %s not found or has syntax errors. \n", inst->module); return (-1); } PL_endav = end_AV; newXS("radiusd::radlog",XS_radiusd_radlog, "rlm_perl.c"); rad_reply_hv = newHV(); rad_check_hv = newHV(); rad_request_hv = newHV(); rad_request_proxy_hv = newHV(); rad_request_proxy_reply_hv = newHV(); rad_reply_hv = get_hv("RAD_REPLY",1); rad_check_hv = get_hv("RAD_CHECK",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); xlat_name = cf_section_name2(conf); if (xlat_name == NULL) xlat_name = cf_section_name1(conf); if (xlat_name){ inst->xlat_name = strdup(xlat_name); xlat_register(xlat_name, perl_xlat, inst); } #ifdef USE_ITHREADS if ((init_pool(conf, inst)) == -1) { radlog(L_ERR,"Couldn't init a pool of perl clones. Exiting"); return -1; } #endif *instance = inst; return 0; }
/* * Compose the response. */ static int eapmschapv2_compose(EAP_HANDLER *handler, VALUE_PAIR *reply) { uint8_t *ptr; int16_t length; mschapv2_header_t *hdr; EAP_DS *eap_ds = handler->eap_ds; eap_ds->request->code = PW_EAP_REQUEST; eap_ds->request->type.type = PW_EAP_MSCHAPV2; switch (reply->attribute) { case PW_MSCHAP_CHALLENGE: /* * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Code | Identifier | Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type | OpCode | MS-CHAPv2-ID | MS-Length... * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | MS-Length | Value-Size | Challenge... * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Challenge... * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Name... * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ length = MSCHAPV2_HEADER_LEN + MSCHAPV2_CHALLENGE_LEN + strlen(handler->identity); eap_ds->request->type.data = malloc(length); /* * Allocate room for the EAP-MS-CHAPv2 data. */ if (eap_ds->request->type.data == NULL) { radlog(L_ERR, "rlm_eap_mschapv2: out of memory"); return 0; } eap_ds->request->type.length = length; ptr = eap_ds->request->type.data; hdr = (mschapv2_header_t *) ptr; hdr->opcode = PW_EAP_MSCHAPV2_CHALLENGE; hdr->mschapv2_id = eap_ds->response->id + 1; length = htons(length); memcpy(hdr->ms_length, &length, sizeof(uint16_t)); hdr->value_size = MSCHAPV2_CHALLENGE_LEN; ptr += MSCHAPV2_HEADER_LEN; /* * Copy the Challenge, success, or error over. */ memcpy(ptr, reply->vp_strvalue, reply->length); memcpy((ptr + reply->length), handler->identity, strlen(handler->identity)); break; case PW_MSCHAP2_SUCCESS: /* * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Code | Identifier | Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type | OpCode | MS-CHAPv2-ID | MS-Length... * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | MS-Length | Message... * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ DEBUG2("MSCHAP Success\n"); length = 46; eap_ds->request->type.data = malloc(length); /* * Allocate room for the EAP-MS-CHAPv2 data. */ if (eap_ds->request->type.data == NULL) { radlog(L_ERR, "rlm_eap_mschapv2: out of memory"); return 0; } memset(eap_ds->request->type.data, 0, length); eap_ds->request->type.length = length; eap_ds->request->type.data[0] = PW_EAP_MSCHAPV2_SUCCESS; eap_ds->request->type.data[1] = eap_ds->response->id; length = htons(length); memcpy((eap_ds->request->type.data + 2), &length, sizeof(uint16_t)); memcpy((eap_ds->request->type.data + 4), reply->vp_strvalue + 1, 42); break; case PW_MSCHAP_ERROR: DEBUG2("MSCHAP Failure\n"); length = 4 + reply->length - 1; eap_ds->request->type.data = malloc(length); /* * Allocate room for the EAP-MS-CHAPv2 data. */ if (eap_ds->request->type.data == NULL) { radlog(L_ERR, "rlm_eap_mschapv2: out of memory"); return 0; } memset(eap_ds->request->type.data, 0, length); eap_ds->request->type.length = length; eap_ds->request->type.data[0] = PW_EAP_MSCHAPV2_FAILURE; eap_ds->request->type.data[1] = eap_ds->response->id; length = htons(length); memcpy((eap_ds->request->type.data + 2), &length, sizeof(uint16_t)); /* * Copy the entire failure message. */ memcpy((eap_ds->request->type.data + 4), reply->vp_strvalue + 1, reply->length - 1); break; default: radlog(L_ERR, "rlm_eap_mschapv2: Internal sanity check failed"); return 0; break; } return 1; }
/* * 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_request_hv; HV *rad_request_proxy_hv; HV *rad_request_proxy_reply_hv; #ifdef USE_ITHREADS POOL_HANDLE *handle; if ((handle = pool_pop(instance)) == NULL) { return RLM_MODULE_FAIL; } radlog(L_DBG,"found interpetator at address 0x%lx",(unsigned long) handle->clone); { dTHXa(handle->clone); PERL_SET_CONTEXT(handle->clone); } #else PERL_SET_CONTEXT(inst->perl); radlog(L_DBG,"Using perl at 0x%lx",(unsigned long) 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_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); 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); } vp = NULL; 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; if ((get_hv_content(rad_reply_hv, &vp)) > 0 ) { pairmove(&request->reply->vps, &vp); pairfree(&vp); } if ((get_hv_content(rad_check_hv, &vp)) > 0 ) { pairmove(&request->config_items, &vp); pairfree(&vp); } if ((get_hv_content(rad_request_proxy_reply_hv, &vp)) > 0 && request->proxy_reply != NULL) { pairfree(&request->proxy_reply->vps); pairmove(&request->proxy_reply->vps, &vp); pairfree(&vp); } } #ifdef USE_ITHREADS pool_release(handle,instance); radlog(L_DBG,"Unreserve perl at address 0x%lx", (unsigned long) handle->clone); #endif return exitstatus; }
/* * Do post-proxy processing, * 0 = fail * 1 = OK. * * Called from rlm_eap.c, eap_postproxy(). */ static int mschap_postproxy(EAP_HANDLER *handler, void *tunnel_data) { VALUE_PAIR *response = NULL; mschapv2_opaque_t *data; data = (mschapv2_opaque_t *) handler->opaque; rad_assert(data != NULL); tunnel_data = tunnel_data; /* -Wunused */ DEBUG2(" rlm_eap_mschapv2: Passing reply from proxy back into the tunnel %p %d.", handler->request, handler->request->reply->code); /* * There is only a limited number of possibilities. */ switch (handler->request->reply->code) { case PW_AUTHENTICATION_ACK: DEBUG(" rlm_eap_mschapv2: Proxied authentication succeeded."); /* * Move the attribute, so it doesn't go into * the reply. */ pairmove2(&response, &handler->request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY); break; default: case PW_AUTHENTICATION_REJECT: DEBUG(" rlm_eap_mschapv2: Proxied authentication did not succeed."); return 0; } /* * No response, die. */ if (!response) { radlog(L_ERR, "rlm_eap_mschapv2: Proxied reply contained no MS-CHAPv2-Success or MS-CHAP-Error"); return 0; } /* * Done doing EAP proxy stuff. */ handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP; eapmschapv2_compose(handler, response); data->code = PW_EAP_MSCHAPV2_SUCCESS; /* * Delete MPPE keys & encryption policy * * FIXME: Use intelligent names... */ fix_mppe_keys(handler, data); /* * save any other attributes for re-use in the final * access-accept e.g. vlan, etc. This lets the PEAP * use_tunneled_reply code work */ data->reply = paircopy(handler->request->reply->vps); /* * And we need to challenge the user, not ack/reject them, * so we re-write the ACK to a challenge. Yuck. */ handler->request->reply->code = PW_ACCESS_CHALLENGE; pairfree(&response); return 1; }
/* * Check one terminal server to see if a user is logged in. * * Return values: * 0 The user is off-line. * 1 The user is logged in. * 2 Some error occured. */ int rad_check_ts(uint32_t nasaddr, unsigned int portnum, const char *user, const char *session_id) { pid_t pid, child_pid; int status; char address[16]; char port[11]; RADCLIENT *cl; fr_ipaddr_t ipaddr; ipaddr.af = AF_INET; ipaddr.ipaddr.ip4addr.s_addr = nasaddr; /* * Find NAS type. */ cl = client_find_old(&ipaddr); if (!cl) { /* * Unknown NAS, so trusting radutmp. */ DEBUG2("checkrad: Unknown NAS %s, not checking", ip_ntoa(address, nasaddr)); return 1; } /* * No nastype, or nas type 'other', trust radutmp. */ if (!cl->nastype || (cl->nastype[0] == '\0') || (strcmp(cl->nastype, "other") == 0)) { DEBUG2("checkrad: No NAS type, or type \"other\" not checking"); return 1; } /* * Fork. */ if ((pid = rad_fork()) < 0) { /* do wait for the fork'd result */ radlog(L_ERR, "Accounting: Failed in fork(): Cannot run checkrad\n"); return 2; } if (pid > 0) { child_pid = rad_waitpid(pid, &status); /* * It's taking too long. Stop waiting for it. * * Don't bother to kill it, as we don't care what * happens to it now. */ if (child_pid == 0) { radlog(L_ERR, "Check-TS: timeout waiting for checkrad"); return 2; } if (child_pid < 0) { radlog(L_ERR, "Check-TS: unknown error in waitpid()"); return 2; } return WEXITSTATUS(status); } /* * We don't close fd's 0, 1, and 2. If we're in debugging mode, * then they should go to stdout (etc), along with the other * server log messages. * * If we're not in debugging mode, then the code in radiusd.c * takes care of connecting fd's 0, 1, and 2 to /dev/null. */ closefrom(3); ip_ntoa(address, nasaddr); snprintf(port, 11, "%u", portnum); #ifdef __EMX__ /* OS/2 can't directly execute scripts then we call the command processor to execute checkrad */ execl(getenv("COMSPEC"), "", "/C","checkrad", cl->nastype, address, port, user, session_id, NULL); #else execl(mainconfig.checkrad, "checkrad", cl->nastype, address, port, user, session_id, NULL); #endif radlog(L_ERR, "Check-TS: exec %s: %s", mainconfig.checkrad, strerror(errno)); /* * Exit - 2 means "some error occured". */ exit(2); return 2; }