/* * 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; int pre_proxy_type = 0; VALUE_PAIR *realmpair; VALUE_PAIR *strippedname; VALUE_PAIR *delaypair; VALUE_PAIR *vp; REALM *realm; char *realmname; /* * Not authentication or accounting. Stop it. */ if ((request->packet->code != PW_AUTHENTICATION_REQUEST) && (request->packet->code != PW_ACCOUNTING_REQUEST)) { DEBUG2(" ERROR: Cannot proxy packets of type %d", request->packet->code); 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. */ realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM); if (!realmpair) { /* * Not proxying, 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) { DEBUG2(" ERROR: Failed to find live home server for realm %s", realmname); 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)))) { DEBUG2(" WARNING: Cancelling proxy to Realm %s, as the realm is local.", realm->realm); return RLM_MODULE_NOOP; } /* * Allocate the proxy packet, only if it wasn't already * allocated by a module. This check is mainly to support * the proxying of EAP-TTLS and EAP-PEAP tunneled requests. * * In those cases, the EAP module creates a "fake" * request, and recursively passes it through the * authentication stage of the server. The module then * checks if the request was supposed to be proxied, and * if so, creates a proxy packet from the TUNNELED request, * and not from the EAP request outside of the tunnel. * * The proxy then works like normal, except that the response * packet is "eaten" by the EAP module, and encapsulated into * an EAP packet. */ if (!request->proxy) { /* * Now build a new RADIUS_PACKET. * * 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); } /* * We now massage the attributes to be proxied... */ /* * 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. */ request->proxy->vps = paircopy(request->packet->vps); } /* * Strip the name, if told to. * * Doing it here catches the case of proxied tunneled * requests. */ if (realm->striprealm == TRUE && (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) { /* * 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. */ vp = pairfind(request->proxy->vps, PW_USER_NAME); if (!vp) { vp = paircreate(PW_USER_NAME, PW_TYPE_STRING); if (!vp) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } vp->next = request->proxy->vps; request->proxy->vps = vp; } memcpy(vp->strvalue, strippedname->strvalue, sizeof(vp->strvalue)); vp->length = strippedname->length; /* * Do NOT delete Stripped-User-Name. */ } /* * 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(request->proxy->vps, PW_CHAP_PASSWORD) && pairfind(request->proxy->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(&(request->proxy->vps), vp); } 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; } /* * Add PROXY_STATE attribute, before pre-proxy stage, * so the pre-proxy modules have access to it. * * Note that, at this point, the proxied request HAS NOT * been assigned a RADIUS Id. */ proxy_addinfo(request); /* * Set up for sending the request. */ memcpy(request->proxysecret, realm->secret, sizeof(request->proxysecret)); request->proxy_try_count = mainconfig.proxy_retry_count - 1; request->proxy_next_try = request->timestamp + mainconfig.proxy_retry_delay; delaypair = pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME); request->proxy->timestamp = request->timestamp - (delaypair ? delaypair->lvalue : 0); /* * Do pre-proxying */ vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE); if (vp) { DEBUG2(" Found Pre-Proxy-Type %s", vp->strvalue); pre_proxy_type = vp->lvalue; } rcode = module_pre_proxy(pre_proxy_type, request); /* * Do NOT free request->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)) { request->options |= RAD_REQUEST_OPTION_PROXIED; /* * IF it's a fake request, don't send the proxy * packet. The outer tunnel session will take * care of doing that. */ if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0) { /* * Add the proxied request to the * list of outstanding proxied * requests, BEFORE we send it, so * we have fewer problems with race * conditions when the responses come * back very quickly. */ if (!rl_add_proxy(request)) { DEBUG("ERROR: Failed to proxy request %d", request->number); return RLM_MODULE_FAIL; /* caller doesn't reply */ } rad_send(request->proxy, NULL, (char *)request->proxysecret); } rcode = RLM_MODULE_HANDLED; /* caller doesn't reply */ } else { rcode = RLM_MODULE_FAIL; /* caller doesn't reply */ } return rcode; }
/* * 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; }