/* * Send an authentication response packet */ static int status_socket_send(rad_listen_t *listener, REQUEST *request) { rad_assert(request->listener == listener); rad_assert(listener->send == status_socket_send); return rad_send(request->reply, request->packet, request->client->secret); }
/* * Send a packet to a home server. * * FIXME: have different code for proxy auth & acct! */ static int proxy_socket_send(rad_listen_t *listener, REQUEST *request) { listen_socket_t *sock = listener->data; rad_assert(request->proxy_listener == listener); rad_assert(listener->send == proxy_socket_send); request->proxy->src_ipaddr = sock->ipaddr; request->proxy->src_port = sock->port; return rad_send(request->proxy, request->packet, request->home_server->secret); }
/* * Send an accounting response packet (or not) */ static int acct_socket_send(rad_listen_t *listener, REQUEST *request) { rad_assert(request->listener == listener); rad_assert(listener->send == acct_socket_send); /* * Accounting reject's are silently dropped. * * We do it here to avoid polluting the rest of the * code with this knowledge */ if (request->reply->code == 0) return 0; return rad_send(request->reply, request->packet, request->client->secret); }
static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep) { int i; struct timeval tv; if (!req || !rep || !*rep) return -1; for (i = 0; i < retries; i++) { fd_set rdfdesc; debug_packet(req, R_SENT); if (rad_send(req, NULL, secret) < 0) { fr_perror("radeapclient"); exit(1); } /* And wait for reply, timing out as necessary */ FD_ZERO(&rdfdesc); FD_SET(req->sockfd, &rdfdesc); tv.tv_sec = (int)timeout; tv.tv_usec = 1000000 * (timeout - (int) timeout); /* Something's wrong if we don't get exactly one fd. */ if (select(req->sockfd + 1, &rdfdesc, NULL, NULL, &tv) != 1) { continue; } *rep = rad_recv(req->sockfd, 0); if (*rep != NULL) { /* * If we get a response from a machine * which we did NOT send a request to, * then complain. */ if (((*rep)->src_ipaddr.af != req->dst_ipaddr.af) || (memcmp(&(*rep)->src_ipaddr.ipaddr, &req->dst_ipaddr.ipaddr, ((req->dst_ipaddr.af == AF_INET ? /* AF_INET6? */ sizeof(req->dst_ipaddr.ipaddr.ip4addr) : /* FIXME: AF_INET6 */ sizeof(req->dst_ipaddr.ipaddr.ip6addr)))) != 0) || ((*rep)->src_port != req->dst_port)) { char src[128], dst[128]; ip_ntoh(&(*rep)->src_ipaddr, src, sizeof(src)); ip_ntoh(&req->dst_ipaddr, dst, sizeof(dst)); fprintf(stderr, "radclient: ERROR: Sent request to host %s port %d, got response from host %s port %d\n!", dst, req->dst_port, src, (*rep)->src_port); exit(1); } break; } else { /* NULL: couldn't receive the packet */ fr_perror("radclient"); exit(1); } } /* No response or no data read (?) */ if (i == retries) { fprintf(stderr, "radclient: no response from server\n"); exit(1); } /* * FIXME: Discard the packet & listen for another. * * Hmm... we should really be using eapol_test, which does * a lot more than radeapclient. */ if (rad_verify(*rep, req, secret) != 0) { fr_perror("rad_verify"); exit(1); } if (rad_decode(*rep, req, secret) != 0) { fr_perror("rad_decode"); exit(1); } /* libradius debug already prints out the value pairs for us */ if (!fr_debug_flag && do_output) { debug_packet(*rep, R_RECV); } if((*rep)->code == PW_CODE_AUTHENTICATION_ACK) { totalapp++; } else if ((*rep)->code == PW_CODE_AUTHENTICATION_REJECT) { totaldeny++; } return 0; }
/* * 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; }
/* * Write accounting information to this modules database. */ static int replicate_packet(void *instance, REQUEST *request) { int rcode = RLM_MODULE_NOOP; VALUE_PAIR *vp, *last; home_server *home; REALM *realm; home_pool_t *pool; RADIUS_PACKET *packet = NULL; instance = instance; /* -Wunused */ last = request->config_items; /* * Send as many packets as necessary to different * destinations. */ while (1) { vp = pairfind(last, PW_REPLICATE_TO_REALM, 0); if (!vp) break; last = vp->next; realm = realm_find2(vp->vp_strvalue); if (!realm) { RDEBUG2("ERROR: Cannot Replicate to unknown realm %s", realm); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: RDEBUG2("ERROR: Cannot replicate unknown packet code %d", request->packet->code); cleanup(packet); return RLM_MODULE_FAIL; case PW_AUTHENTICATION_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_COA_REQUEST: case PW_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RDEBUG2(" WARNING: Cancelling replication to Realm %s, as the realm is local.", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { RDEBUG2("ERROR: Failed to find live home server for realm %s", realm->name); continue; } if (!packet) { packet = rad_alloc(1); if (!packet) return RLM_MODULE_FAIL; packet->sockfd = -1; packet->code = request->packet->code; packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { RDEBUG("ERROR: Failed opening socket: %s", fr_strerror()); cleanup(packet); return RLM_MODULE_FAIL; } packet->vps = paircopy(request->packet->vps); if (!packet->vps) { RDEBUG("ERROR: Out of memory!"); cleanup(packet); return RLM_MODULE_FAIL; } /* * For CHAP, create the CHAP-Challenge if * it doesn't exist. */ if ((request->packet->code == PW_AUTHENTICATION_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0) == NULL)) { vp = radius_paircreate(request, &packet->vps, PW_CHAP_CHALLENGE, 0, PW_TYPE_OCTETS); vp->length = AUTH_VECTOR_LEN; memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN); } } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; free(packet->data); packet->data = NULL; packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating packet to Realm %s", realm->name); if (rad_send(packet, NULL, home->secret) < 0) { RDEBUG("ERROR: Failed replicating packet: %s", fr_strerror()); cleanup(packet); return RLM_MODULE_FAIL; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } cleanup(packet); return rcode; }
/** Copy packet to multiple servers * * Create a duplicate of the packet and send it to a list of realms * defined by the presence of the Replicate-To-Realm VP in the control * list of the current request. * * This is pretty hacky and is 100% fire and forget. If you're looking * to forward authentication requests to multiple realms and process * the responses, this function will not allow you to do that. * * @param[in] instance of this module. * @param[in] request The current request. * @param[in] list of attributes to copy to the duplicate packet. * @param[in] code to write into the code field of the duplicate packet. * @return RCODE fail on error, invalid if list does not exist, noop if no * replications succeeded, else ok. */ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, unsigned int code) { int rcode = RLM_MODULE_NOOP; VALUE_PAIR *vp, **vps, *last; home_server *home; REALM *realm; home_pool_t *pool; RADIUS_PACKET *packet = NULL; last = request->config_items; /* * Send as many packets as necessary to different * destinations. */ while (1) { vp = pairfind(last, PW_REPLICATE_TO_REALM, 0, TAG_ANY); if (!vp) break; last = vp->next; realm = realm_find2(vp->vp_strvalue); if (!realm) { RDEBUG2E("Cannot Replicate to unknown realm %s", realm); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: RDEBUG2E("Cannot replicate unknown packet code %d", request->packet->code); cleanup(packet); return RLM_MODULE_FAIL; case PW_AUTHENTICATION_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_COA_REQUEST: case PW_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RDEBUG2W("Cancelling replication to Realm %s, as the realm is local.", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { RDEBUG2E("Failed to find live home server for realm %s", realm->name); continue; } /* * For replication to multiple servers we re-use the packet * we built here. */ if (!packet) { packet = rad_alloc(NULL, 1); if (!packet) return RLM_MODULE_FAIL; packet->sockfd = -1; packet->code = code; packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { RDEBUGE("Failed opening socket: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } vps = radius_list(request, list); if (!vps) { RDEBUGW("List '%s' doesn't exist for " "this packet", fr_int2str(pair_lists, list, "?unknown?")); rcode = RLM_MODULE_INVALID; goto done; } /* * Don't assume the list actually contains any * attributes. */ if (*vps) { packet->vps = paircopy(packet, *vps); if (!packet->vps) { rcode = RLM_MODULE_FAIL; goto done; } } /* * For CHAP, create the CHAP-Challenge if * it doesn't exist. */ if ((code == PW_AUTHENTICATION_REQUEST) && (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) && (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) { vp = radius_paircreate(request, &packet->vps, PW_CHAP_CHALLENGE, 0); vp->length = AUTH_VECTOR_LEN; memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN); } } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; free(packet->data); packet->data = NULL; packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "¿unknown?"),realm->name); if (rad_send(packet, NULL, home->secret) < 0) { RDEBUGE("Failed replicating packet: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } done: cleanup(packet); return rcode; }
static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep) { int i; struct timeval tv; for (i = 0; i < retries; i++) { fd_set rdfdesc; rad_send(req, NULL, secret); /* And wait for reply, timing out as necessary */ FD_ZERO(&rdfdesc); FD_SET(req->sockfd, &rdfdesc); tv.tv_sec = (int)timeout; tv.tv_usec = 1000000 * (timeout - (int) timeout); /* Something's wrong if we don't get exactly one fd. */ if (select(req->sockfd + 1, &rdfdesc, NULL, NULL, &tv) != 1) { continue; } *rep = rad_recv(req->sockfd); if (*rep != NULL) { /* * If we get a response from a machine * which we did NOT send a request to, * then complain. */ if (((*rep)->src_ipaddr != req->dst_ipaddr) || ((*rep)->src_port != req->dst_port)) { char src[64], dst[64]; ip_ntoa(src, (*rep)->src_ipaddr); ip_ntoa(dst, req->dst_ipaddr); fprintf(stderr, "radclient: ERROR: Sent request to host %s port %d, got response from host %s port %d\n!", dst, req->dst_port, src, (*rep)->src_port); exit(1); } break; } else { /* NULL: couldn't receive the packet */ librad_perror("radclient:"); exit(1); } } /* No response or no data read (?) */ if (i == retries) { fprintf(stderr, "radclient: no response from server\n"); exit(1); } /* * FIXME: Discard the packet & listen for another. * * Hmm... we should really be using eapol_test, which does * a lot more than radeapclient. */ if (rad_verify(*rep, req, secret) != 0) { librad_perror("rad_verify"); exit(1); } if (rad_decode(*rep, req, secret) != 0) { librad_perror("rad_decode"); exit(1); } /* libradius debug already prints out the value pairs for us */ if (!librad_debug && do_output) { printf("Received response ID %d, code %d, length = %d\n", (*rep)->id, (*rep)->code, (*rep)->data_len); vp_printlist(stdout, (*rep)->vps); } if((*rep)->code == PW_AUTHENTICATION_ACK) { totalapp++; } else { totaldeny++; } return 0; }
/* * 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 do_packet(int allports, uint32_t nasaddr, const struct radutmp *u) { int i, retries=5, timeout=3; struct timeval tv; RADIUS_PACKET *req, *rep = NULL; VALUE_PAIR *vp; const char *secret; if ((req = rad_alloc(1)) == NULL) { librad_perror("radzap"); exit(1); } req->id = getpid() & 0xFF; req->code = PW_ACCOUNTING_REQUEST; req->dst_port = acct_port; if(req->dst_port == 0) req->dst_port = getport("radacct"); if(req->dst_port == 0) req->dst_port = PW_ACCT_UDP_PORT; if (radiusip == INADDR_NONE) { req->dst_ipaddr = ip_getaddr("localhost"); } else { req->dst_ipaddr = radiusip; } if(!req->dst_ipaddr) req->dst_ipaddr = 0x7f000001; req->vps = NULL; secret = getsecret(req->dst_ipaddr); if(allports != 0) { INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_ACCOUNTING_OFF); IPPAIR(PW_NAS_IP_ADDRESS, nasaddr); INTPAIR(PW_ACCT_DELAY_TIME, 0); } else { char login[sizeof u->login+1]; char session_id[sizeof u->session_id+1]; strNcpy(login, u->login, sizeof login); strNcpy(session_id, u->session_id, sizeof session_id); INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_STOP); IPPAIR(PW_NAS_IP_ADDRESS, u->nas_address); INTPAIR(PW_ACCT_DELAY_TIME, 0); STRINGPAIR(PW_USER_NAME, login); INTPAIR(PW_NAS_PORT, u->nas_port); STRINGPAIR(PW_ACCT_SESSION_ID, session_id); if(u->proto=='P') { INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER); INTPAIR(PW_FRAMED_PROTOCOL, PW_PPP); } else if(u->proto=='S') { INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER); INTPAIR(PW_FRAMED_PROTOCOL, PW_SLIP); } else { INTPAIR(PW_SERVICE_TYPE, PW_LOGIN_USER); /* A guess, really */ } IPPAIR(PW_FRAMED_IP_ADDRESS, u->framed_address); INTPAIR(PW_ACCT_SESSION_TIME, 0); INTPAIR(PW_ACCT_INPUT_OCTETS, 0); INTPAIR(PW_ACCT_OUTPUT_OCTETS, 0); INTPAIR(PW_ACCT_INPUT_PACKETS, 0); INTPAIR(PW_ACCT_OUTPUT_PACKETS, 0); } if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("radzap: socket: "); exit(1); } for (i = 0; i < retries; i++) { fd_set rdfdesc; rad_send(req, NULL, secret); /* And wait for reply, timing out as necessary */ FD_ZERO(&rdfdesc); FD_SET(req->sockfd, &rdfdesc); tv.tv_sec = (int)timeout; tv.tv_usec = 1000000 * (timeout - (int)timeout); /* Something's wrong if we don't get exactly one fd. */ if (select(req->sockfd + 1, &rdfdesc, NULL, NULL, &tv) != 1) { continue; } rep = rad_recv(req->sockfd); if (rep != NULL) { break; } else { /* NULL: couldn't receive the packet */ librad_perror("radzap:"); exit(1); } } /* No response or no data read (?) */ if (i == retries) { fprintf(stderr, "%s: no response from server\n", progname); exit(1); } if (rad_decode(rep, req, secret) != 0) { librad_perror("rad_decode"); exit(1); } vp_printlist(stdout, rep->vps); return 0; }
/* * Send one packet. */ static int send_one_packet(rc_request_t *request) { assert(request->done == false); /* * Remember when we have to wake up, to re-send the * request, of we didn't receive a reply. */ if ((sleep_time == -1) || (sleep_time > (int) timeout)) { sleep_time = (int) timeout; } /* * Haven't sent the packet yet. Initialize it. */ if (request->packet->id == -1) { int i; bool rcode; assert(request->reply == NULL); /* * Didn't find a free packet ID, we're not done, * we don't sleep, and we stop trying to process * this packet. */ retry: request->packet->src_ipaddr.af = server_ipaddr.af; rcode = fr_packet_list_id_alloc(pl, ipproto, &request->packet, NULL); if (!rcode) { int mysockfd; #ifdef WITH_TCP if (proto) { mysockfd = fr_tcp_client_socket(NULL, &server_ipaddr, server_port); } else #endif mysockfd = fr_socket(&client_ipaddr, 0); if (mysockfd < 0) { ERROR("Can't open new socket: %s", strerror(errno)); exit(1); } if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, &server_ipaddr, server_port, NULL)) { ERROR("Can't add new socket"); exit(1); } goto retry; } assert(request->packet->id != -1); assert(request->packet->data == NULL); for (i = 0; i < 4; i++) { ((uint32_t *) request->packet->vector)[i] = fr_rand(); } /* * Update the password, so it can be encrypted with the * new authentication vector. */ if (request->password[0] != '\0') { VALUE_PAIR *vp; if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) { pairstrcpy(vp, request->password); } else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) { bool already_hex = false; /* * If it's 17 octets, it *might* be already encoded. * Or, it might just be a 17-character password (maybe UTF-8) * Check it for non-printable characters. The odds of ALL * of the characters being 32..255 is (1-7/8)^17, or (1/8)^17, * or 1/(2^51), which is pretty much zero. */ if (vp->length == 17) { for (i = 0; i < 17; i++) { if (vp->vp_octets[i] < 32) { already_hex = true; break; } } } /* * Allow the user to specify ASCII or hex CHAP-Password */ if (!already_hex) { uint8_t *p; size_t len, len2; len = len2 = strlen(request->password); if (len2 < 17) len2 = 17; p = talloc_zero_array(vp, uint8_t, len2); memcpy(p, request->password, len); rad_chap_encode(request->packet, p, fr_rand() & 0xff, vp); vp->vp_octets = p; vp->length = 17; } } else if (pairfind(request->packet->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY) != NULL) { mschapv1_encode(request->packet, &request->packet->vps, request->password); } else { DEBUG("WARNING: No password in the request"); } } request->timestamp = time(NULL); request->tries = 1; request->resend++; #ifdef WITH_TCP /* * WTF? */ if (client_port == 0) { client_ipaddr = request->packet->src_ipaddr; client_port = request->packet->src_port; } #endif } else { /* request->packet->id >= 0 */ time_t now = time(NULL); /* * FIXME: Accounting packets are never retried! * The Acct-Delay-Time attribute is updated to * reflect the delay, and the packet is re-sent * from scratch! */ /* * Not time for a retry, do so. */ if ((now - request->timestamp) < timeout) { /* * When we walk over the tree sending * packets, we update the minimum time * required to sleep. */ if ((sleep_time == -1) || (sleep_time > (now - request->timestamp))) { sleep_time = now - request->timestamp; } return 0; } /* * We're not trying later, maybe the packet is done. */ if (request->tries == retries) { assert(request->packet->id >= 0); /* * Delete the request from the tree of * outstanding requests. */ fr_packet_list_yank(pl, request->packet); REDEBUG("No reply from server for ID %d socket %d", request->packet->id, request->packet->sockfd); deallocate_id(request); /* * Normally we mark it "done" when we've received * the reply, but this is a special case. */ if (request->resend == resend_count) { request->done = true; } stats.lost++; return -1; } /* * We are trying later. */ request->timestamp = now; request->tries++; } /* * Send the packet. */ if (rad_send(request->packet, NULL, secret) < 0) { REDEBUG("Failed to send packet for ID %d", request->packet->id); } return 0; }
/* * Compare two RADIUS_PACKET data structures, based on a number * of criteria. */ static int send_one_packet(radclient_t *radclient) { int i; assert(radclient->done == 0); /* * Remember when we have to wake up, to re-send the * request, of we didn't receive a response. */ if ((sleep_time == -1) || (sleep_time > (int) timeout)) { sleep_time = (int) timeout; } /* * Haven't sent the packet yet. Initialize it. */ if (radclient->request->id == -1) { int found = 0; assert(radclient->reply == NULL); /* * Find a free packet Id */ for (i = 0; i < 256; i++) { if (radius_id[(last_used_id + i) & 0xff] == 0) { last_used_id = (last_used_id + i) & 0xff; radius_id[last_used_id] = 1; radclient->request->id = last_used_id++; found = 1; break; } } /* * Didn't find a free packet ID, we're not done, * we don't sleep, and we stop trying to process * this packet. */ if (!found) { done = 0; sleep_time = 0; return 0; } assert(radclient->request->id != -1); assert(radclient->request->data == NULL); librad_md5_calc(radclient->request->vector, radclient->request->vector, sizeof(radclient->request->vector)); /* * Update the password, so it can be encrypted with the * new authentication vector. */ if (radclient->password[0] != '\0') { VALUE_PAIR *vp; if ((vp = pairfind(radclient->request->vps, PW_PASSWORD)) != NULL) { strNcpy((char *)vp->strvalue, radclient->password, sizeof(vp->strvalue)); vp->length = strlen(vp->strvalue); } else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD)) != NULL) { strNcpy((char *)vp->strvalue, radclient->password, sizeof(vp->strvalue)); vp->length = strlen(vp->strvalue); rad_chap_encode(radclient->request, (char *) vp->strvalue, radclient->request->id, vp); vp->length = 17; } } radclient->timestamp = time(NULL); radclient->tries = 1; radclient->resend++; /* * Duplicate found. Serious error! */ } else /* radclient->request->id >= 0 */ { time_t now = time(NULL); /* * FIXME: Accounting packets are never retried! * The Acct-Delay-Time attribute is updated to * reflect the delay, and the packet is re-sent * from scratch! */ /* * Not time for a retry, do so. */ if ((now - radclient->timestamp) < timeout) { /* * When we walk over the tree sending * packets, we update the minimum time * required to sleep. */ if ((sleep_time == -1) || (sleep_time > (now - radclient->timestamp))) { sleep_time = now - radclient->timestamp; } return 0; } /* * We're not trying later, maybe the packet is done. */ if (radclient->tries == retries) { assert(radclient->request->id >= 0); /* * Delete the request from the tree of * outstanding requests. */ fprintf(stderr, "radclient: no response from server for ID %d\n", radclient->request->id); /* * Normally we mark it "done" when we've received * the response, but this is a special case. */ if (radclient->resend == resend_count) { radclient->done = 1; } totallost++; return -1; } /* * We are trying later. */ radclient->timestamp = now; radclient->tries++; } /* * Send the packet. */ if (rad_send(radclient->request, NULL, secret) < 0) { fprintf(stderr, "radclient: Failed to send packet for ID %d: %s\n", radclient->request->id, librad_errstr); } return 0; }
/*Note: * We just support EAP proxy. * rad_send_eap_response * send a EAP response to radius server */ int rad_send_eap_response(RADIUS_PACKET* rep,const char* secret,const char* pwd) { VALUE_PAIR* vp; int rc; int old_debug_flag; vp = NULL; rc = 0; old_debug_flag = 0; /* * if there are EAP types, encode them into an EAP-Message * */ map_eap_types(rep); /* * Fix up Digest-Attributes issues */ for (vp = rep->vps; vp != NULL; vp = vp->next) { switch (vp->attribute) { default: break; case PW_DIGEST_REALM: case PW_DIGEST_NONCE: case PW_DIGEST_METHOD: case PW_DIGEST_URI: case PW_DIGEST_QOP: case PW_DIGEST_ALGORITHM: case PW_DIGEST_BODY_DIGEST: case PW_DIGEST_CNONCE: case PW_DIGEST_NONCE_COUNT: case PW_DIGEST_USER_NAME: /* overlapping! */ memmove(&vp->vp_strvalue[2], &vp->vp_octets[0], vp->length); vp->vp_octets[0] = vp->attribute - PW_DIGEST_REALM + 1; vp->length += 2; vp->vp_octets[1] = vp->length; vp->attribute = PW_DIGEST_ATTRIBUTES; break; } } fr_md5_calc(rep->vector, rep->vector, sizeof(rep->vector)); if ((pwd != NULL) && (*pwd != '\0')) { if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD)) != NULL) { strncpy((char *)vp->vp_strvalue, pwd, sizeof(vp->vp_strvalue) - 1); vp->length = strlen(pwd); } else if ((vp = pairfind(rep->vps, PW_USER_PASSWORD)) != NULL) { strncpy((char *)vp->vp_strvalue, pwd, sizeof(vp->vp_strvalue) - 1); vp->length = strlen(pwd); } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD)) != NULL) { strncpy((char *)vp->vp_strvalue, pwd, sizeof(vp->vp_strvalue) - 1); vp->length = strlen(pwd); rad_chap_encode(rep, vp->vp_octets, rep->id, vp); vp->length = 17; } } /* there WAS a password */ /* send the response*/ if(fr_debug_flag) { debug_packet(rep,R_SENT); old_debug_flag = fr_debug_flag; fr_debug_flag = 0; /*just turn off the debug-flag to avoid rad_send debug out agin*/ } if(rad_send(rep,NULL,secret) < 0) rc = -1; else rc = 0; if(old_debug_flag) fr_debug_flag = old_debug_flag; return rc; }
/* * Send one packet. */ static int send_one_packet(radclient_t *radclient) { assert(radclient->done == 0); /* * Remember when we have to wake up, to re-send the * request, of we didn't receive a response. */ if ((sleep_time == -1) || (sleep_time > (int) timeout)) { sleep_time = (int) timeout; } /* * Haven't sent the packet yet. Initialize it. */ if (radclient->request->id == -1) { int i, rcode; assert(radclient->reply == NULL); /* * Didn't find a free packet ID, we're not done, * we don't sleep, and we stop trying to process * this packet. */ retry: radclient->request->src_ipaddr.af = server_ipaddr.af; rcode = fr_packet_list_id_alloc(pl, ipproto, radclient->request, NULL); if (rcode < 0) { int mysockfd; #ifdef WITH_TCP if (proto) { mysockfd = fr_tcp_client_socket(NULL, &server_ipaddr, server_port); } else #endif mysockfd = fr_socket(&client_ipaddr, 0); if (!mysockfd) { fprintf(stderr, "radclient: Can't open new socket\n"); exit(1); } if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, &server_ipaddr, server_port, NULL)) { fprintf(stderr, "radclient: Can't add new socket\n"); exit(1); } goto retry; } if (rcode == 0) { done = 0; sleep_time = 0; return 0; } assert(radclient->request->id != -1); assert(radclient->request->data == NULL); for (i = 0; i < 4; i++) { ((uint32_t *) radclient->request->vector)[i] = fr_rand(); } /* * Update the password, so it can be encrypted with the * new authentication vector. */ if (radclient->password[0] != '\0') { VALUE_PAIR *vp; if ((vp = pairfind(radclient->request->vps, PW_USER_PASSWORD, 0)) != NULL) { strlcpy(vp->vp_strvalue, radclient->password, sizeof(vp->vp_strvalue)); vp->length = strlen(vp->vp_strvalue); } else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD, 0)) != NULL) { int already_hex = 0; /* * If it's 17 octets, it *might* be already encoded. * Or, it might just be a 17-character password (maybe UTF-8) * Check it for non-printable characters. The odds of ALL * of the characters being 32..255 is (1-7/8)^17, or (1/8)^17, * or 1/(2^51), which is pretty much zero. */ if (vp->length == 17) { for (i = 0; i < 17; i++) { if (vp->vp_octets[i] < 32) { already_hex = 1; break; } } } /* * Allow the user to specify ASCII or hex CHAP-Password */ if (!already_hex) { strlcpy(vp->vp_strvalue, radclient->password, sizeof(vp->vp_strvalue)); vp->length = strlen(vp->vp_strvalue); rad_chap_encode(radclient->request, vp->vp_octets, fr_rand() & 0xff, vp); vp->length = 17; } } else if (pairfind(radclient->request->vps, PW_MSCHAP_PASSWORD, 0) != NULL) { mschapv1_encode(&radclient->request->vps, radclient->password); } else if (fr_debug_flag) { printf("WARNING: No password in the request\n"); } } radclient->timestamp = time(NULL); radclient->tries = 1; radclient->resend++; /* * Duplicate found. Serious error! */ if (!fr_packet_list_insert(pl, &radclient->request)) { assert(0 == 1); } #ifdef WITH_TCP /* * WTF? */ if (client_port == 0) { client_ipaddr = radclient->request->src_ipaddr; client_port = radclient->request->src_port; } #endif } else { /* radclient->request->id >= 0 */ time_t now = time(NULL); /* * FIXME: Accounting packets are never retried! * The Acct-Delay-Time attribute is updated to * reflect the delay, and the packet is re-sent * from scratch! */ /* * Not time for a retry, do so. */ if ((now - radclient->timestamp) < timeout) { /* * When we walk over the tree sending * packets, we update the minimum time * required to sleep. */ if ((sleep_time == -1) || (sleep_time > (now - radclient->timestamp))) { sleep_time = now - radclient->timestamp; } return 0; } /* * We're not trying later, maybe the packet is done. */ if (radclient->tries == retries) { assert(radclient->request->id >= 0); /* * Delete the request from the tree of * outstanding requests. */ fr_packet_list_yank(pl, radclient->request); fprintf(stderr, "radclient: no response from server for ID %d socket %d\n", radclient->request->id, radclient->request->sockfd); deallocate_id(radclient); /* * Normally we mark it "done" when we've received * the response, but this is a special case. */ if (radclient->resend == resend_count) { radclient->done = 1; } totallost++; return -1; } /* * We are trying later. */ radclient->timestamp = now; radclient->tries++; } /* * Send the packet. */ if (rad_send(radclient->request, NULL, secret) < 0) { fprintf(stderr, "radclient: Failed to send packet for ID %d: %s\n", radclient->request->id, fr_strerror()); } if (fr_debug_flag > 2) print_hex(radclient->request); return 0; }
/** Copy packet to multiple servers * * Create a duplicate of the packet and send it to a list of realms * defined by the presence of the Replicate-To-Realm VP in the control * list of the current request. * * This is pretty hacky and is 100% fire and forget. If you're looking * to forward authentication requests to multiple realms and process * the responses, this function will not allow you to do that. * * @param[in] instance of this module. * @param[in] request The current request. * @param[in] list of attributes to copy to the duplicate packet. * @param[in] code to write into the code field of the duplicate packet. * @return RCODE fail on error, invalid if list does not exist, noop if no replications succeeded, else ok. */ static rlm_rcode_t replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, PW_CODE code) { int rcode = RLM_MODULE_NOOP; bool pass1 = true; vp_cursor_t cursor; VALUE_PAIR *vp; RADIUS_PACKET *packet = NULL; rcode = rlm_replicate_alloc(&packet, request, list, code); if (rcode != RLM_MODULE_OK) { return rcode; } /* * Send as many packets as necessary to different destinations. */ fr_cursor_init(&cursor, &request->config_items); while ((vp = fr_cursor_next_by_num(&cursor, PW_REPLICATE_TO_REALM, 0, TAG_ANY))) { home_server_t *home; REALM *realm; home_pool_t *pool; realm = realm_find2(vp->vp_strvalue); if (!realm) { REDEBUG2("Cannot Replicate to unknown realm \"%s\"", vp->vp_strvalue); continue; } /* * We shouldn't really do this on every loop. */ switch (request->packet->code) { default: REDEBUG2("Cannot replicate unknown packet code %d", request->packet->code); rcode = RLM_MODULE_FAIL; goto done; case PW_CODE_ACCESS_REQUEST: pool = realm->auth_pool; break; #ifdef WITH_ACCOUNTING case PW_CODE_ACCOUNTING_REQUEST: pool = realm->acct_pool; break; #endif #ifdef WITH_COA case PW_CODE_COA_REQUEST: case PW_CODE_DISCONNECT_REQUEST: pool = realm->acct_pool; break; #endif } if (!pool) { RWDEBUG2("Cancelling replication to Realm %s, as the realm is local", realm->name); continue; } home = home_server_ldb(realm->name, pool, request); if (!home) { REDEBUG2("Failed to find live home server for realm %s", realm->name); continue; } /* * For replication to multiple servers we re-use the packet * we built here. */ if (pass1) { packet->id = fr_rand() & 0xff; packet->sockfd = fr_socket(&home->src_ipaddr, 0); if (packet->sockfd < 0) { REDEBUG("Failed opening socket: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } pass1 = false; } else { size_t i; for (i = 0; i < sizeof(packet->vector); i++) { packet->vector[i] = fr_rand() & 0xff; } packet->id++; TALLOC_FREE(packet->data); packet->data_len = 0; } /* * (Re)-Write these. */ packet->dst_ipaddr = home->ipaddr; packet->dst_port = home->port; memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr)); packet->src_port = 0; /* * Encode, sign and then send the packet. */ RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "<INVALID>"), realm->name); if (rad_send(packet, NULL, home->secret) < 0) { REDEBUG("Failed replicating packet: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto done; } /* * We've sent it to at least one destination. */ rcode = RLM_MODULE_OK; } done: talloc_free(packet); return rcode; }
/* * Send one packet. */ static int send_one_packet(rc_request_t *request) { assert(request->done == false); /* * Remember when we have to wake up, to re-send the * request, of we didn't receive a reply. */ if ((sleep_time == -1) || (sleep_time > (int) timeout)) sleep_time = (int) timeout; /* * Haven't sent the packet yet. Initialize it. */ if (request->packet->id == -1) { int i; bool rcode; assert(request->reply == NULL); /* * Didn't find a free packet ID, we're not done, * we don't sleep, and we stop trying to process * this packet. */ retry: request->packet->src_ipaddr.af = server_ipaddr.af; rcode = fr_packet_list_id_alloc(pl, ipproto, &request->packet, NULL); if (!rcode) { int mysockfd; #ifdef WITH_TCP if (proto) { mysockfd = fr_socket_client_tcp(NULL, &request->packet->dst_ipaddr, request->packet->dst_port, false); } else #endif mysockfd = fr_socket(&client_ipaddr, 0); if (mysockfd < 0) { ERROR("Failed opening socket"); exit(1); } if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, &request->packet->dst_ipaddr, request->packet->dst_port, NULL)) { ERROR("Can't add new socket"); exit(1); } goto retry; } assert(request->packet->id != -1); assert(request->packet->data == NULL); for (i = 0; i < 4; i++) { ((uint32_t *) request->packet->vector)[i] = fr_rand(); } /* * Update the password, so it can be encrypted with the * new authentication vector. */ if (request->password) { VALUE_PAIR *vp; if ((vp = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) { fr_pair_value_strcpy(vp, request->password->vp_strvalue); } else if ((vp = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) { uint8_t buffer[17]; rad_chap_encode(request->packet, buffer, fr_rand() & 0xff, request->password); fr_pair_value_memcpy(vp, buffer, 17); } else if (fr_pair_find_by_num(request->packet->vps, PW_MS_CHAP_PASSWORD, 0, TAG_ANY) != NULL) { mschapv1_encode(request->packet, &request->packet->vps, request->password->vp_strvalue); } else { DEBUG("WARNING: No password in the request"); } } request->timestamp = time(NULL); request->tries = 1; request->resend++; } else { /* request->packet->id >= 0 */ time_t now = time(NULL); /* * FIXME: Accounting packets are never retried! * The Acct-Delay-Time attribute is updated to * reflect the delay, and the packet is re-sent * from scratch! */ /* * Not time for a retry, do so. */ if ((now - request->timestamp) < timeout) { /* * When we walk over the tree sending * packets, we update the minimum time * required to sleep. */ if ((sleep_time == -1) || (sleep_time > (now - request->timestamp))) { sleep_time = now - request->timestamp; } return 0; } /* * We're not trying later, maybe the packet is done. */ if (request->tries == retries) { assert(request->packet->id >= 0); /* * Delete the request from the tree of * outstanding requests. */ fr_packet_list_yank(pl, request->packet); REDEBUG("No reply from server for ID %d socket %d", request->packet->id, request->packet->sockfd); deallocate_id(request); /* * Normally we mark it "done" when we've received * the reply, but this is a special case. */ if (request->resend == resend_count) { request->done = true; } stats.lost++; return -1; } /* * We are trying later. */ request->timestamp = now; request->tries++; } /* * Send the packet. */ if (rad_send(request->packet, NULL, secret) < 0) { REDEBUG("Failed to send packet for ID %d", request->packet->id); deallocate_id(request); request->done = true; return -1; } fr_packet_header_print(fr_log_fp, request->packet, false); if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, request->packet->vps); return 0; }
/* * Refresh a request, by using proxy_retry_delay, cleanup_delay, * max_request_time, etc. * * When walking over the request list, all of the per-request * magic is done here. */ static int refresh_request(REQUEST *request, void *data) { rl_walk_t *info = (rl_walk_t *) data; time_t difference; child_pid_t child_pid; rad_assert(request->magic == REQUEST_MAGIC); /* * If the request is marked as a delayed reject, AND it's * time to send the reject, then do so now. */ if (request->finished && ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) != 0)) { rad_assert(request->child_pid == NO_SUCH_CHILD_PID); difference = info->now - request->timestamp; if (difference >= (time_t) mainconfig.reject_delay) { /* * Clear the 'delayed reject' bit, so that we * don't do this again. */ request->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT; rad_send(request->reply, request->packet, request->secret); } } /* * If the request has finished processing, AND it's child has * been cleaned up, AND it's time to clean up the request, * OR, it's an accounting request. THEN, go delete it. * * If this is a request which had the "don't cache" option * set, then delete it immediately, as it CANNOT have a * duplicate. */ if (request->finished && ((request->timestamp + mainconfig.cleanup_delay <= info->now) || ((request->options & RAD_REQUEST_OPTION_DONT_CACHE) != 0))) { rad_assert(request->child_pid == NO_SUCH_CHILD_PID); /* * Request completed, delete it, and unlink it * from the currently 'alive' list of requests. */ DEBUG2("Cleaning up request %d ID %d with timestamp %08lx", request->number, request->packet->id, (unsigned long) request->timestamp); /* * Delete the request. */ rl_delete(request); return RL_WALK_CONTINUE; } /* * Maybe the child process handling the request has hung: * kill it, and continue. */ if ((request->timestamp + mainconfig.max_request_time) <= info->now) { int number; child_pid = request->child_pid; number = request->number; /* * There MUST be a RAD_PACKET reply. */ rad_assert(request->reply != NULL); /* * If we've tried to proxy the request, and * the proxy server hasn't responded, then * we send a REJECT back to the caller. * * For safety, we assert that there is no child * handling the request. If the assertion fails, * it means that we've sent a proxied request to * the home server, and the child thread is still * sitting on the request! */ if (request->proxy && !request->proxy_reply) { rad_assert(request->child_pid == NO_SUCH_CHILD_PID); radlog(L_ERR, "Rejecting request %d due to lack of any response from home server %s:%d", request->number, client_name(request->packet->src_ipaddr), request->packet->src_port); request_reject(request); request->finished = TRUE; return RL_WALK_CONTINUE; } if (mainconfig.kill_unresponsive_children) { if (child_pid != NO_SUCH_CHILD_PID) { /* * This request seems to have hung * - kill it */ #ifdef HAVE_PTHREAD_H radlog(L_ERR, "Killing unresponsive thread for request %d", request->number); pthread_cancel(child_pid); #endif } /* else no proxy reply, quietly fail */ /* * Maybe we haven't killed it. In that * case, print a warning. */ } else if ((child_pid != NO_SUCH_CHILD_PID) && ((request->options & RAD_REQUEST_OPTION_LOGGED_CHILD) == 0)) { radlog(L_ERR, "WARNING: Unresponsive child (id %lu) for request %d", (unsigned long)child_pid, number); /* * Set the option that we've sent a log message, * so that we don't send more than one message * per request. */ request->options |= RAD_REQUEST_OPTION_LOGGED_CHILD; } /* * Send a reject message for the request, mark it * finished, and forget about the child. */ request_reject(request); request->child_pid = NO_SUCH_CHILD_PID; if (mainconfig.kill_unresponsive_children) request->finished = TRUE; return RL_WALK_CONTINUE; } /* the request has been in the queue for too long */ /* * If the request is still being processed, then due to the * above check, it's still within it's time limit. In that * case, don't do anything. */ if (request->child_pid != NO_SUCH_CHILD_PID) { return RL_WALK_CONTINUE; } /* * The request is finished. */ if (request->finished) goto setup_timeout; /* * We're not proxying requests at all. */ if (!mainconfig.proxy_requests) goto setup_timeout; /* * We're proxying synchronously, so we don't retry it here. * Some other code takes care of retrying the proxy requests. */ if (mainconfig.proxy_synchronous) goto setup_timeout; /* * The proxy retry delay is zero, meaning don't retry. */ if (mainconfig.proxy_retry_delay == 0) goto setup_timeout; /* * There is no proxied request for this packet, so there's * no proxy retries. */ if (!request->proxy) goto setup_timeout; /* * We've already seen the proxy reply, so we don't need * to send another proxy request. */ if (request->proxy_reply) goto setup_timeout; /* * It's not yet time to re-send this proxied request. */ if (request->proxy_next_try > info->now) goto setup_timeout; /* * If the proxy retry count is zero, then * we've sent the last try, and have NOT received * a reply from the end server. In that case, * we don't bother trying again, but just mark * the request as finished, and go to the next one. */ if (request->proxy_try_count == 0) { rad_assert(request->child_pid == NO_SUCH_CHILD_PID); request_reject(request); realm_disable(request->proxy->dst_ipaddr,request->proxy->dst_port); request->finished = TRUE; goto setup_timeout; } /* * We're trying one more time, so count down * the tries, and set the next try time. */ request->proxy_try_count--; request->proxy_next_try = info->now + mainconfig.proxy_retry_delay; /* Fix up Acct-Delay-Time */ if (request->proxy->code == PW_ACCOUNTING_REQUEST) { VALUE_PAIR *delaypair; delaypair = pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME); if (!delaypair) { delaypair = paircreate(PW_ACCT_DELAY_TIME, PW_TYPE_INTEGER); if (!delaypair) { radlog(L_ERR|L_CONS, "no memory"); exit(1); } pairadd(&request->proxy->vps, delaypair); } delaypair->lvalue = info->now - request->proxy->timestamp; /* Must recompile the valuepairs to wire format */ free(request->proxy->data); request->proxy->data = NULL; } /* proxy accounting request */ /* * Assert that we have NOT seen the proxy reply yet. * * If we HAVE seen it, then we SHOULD NOT be bugging the * home server! */ rad_assert(request->proxy_reply == NULL); /* * Send the proxy packet. */ request->proxy_outstanding++; rad_send(request->proxy, NULL, request->proxysecret); setup_timeout: /* * Don't do more long-term checks, if we've got to wake * up now. */ if (info->smallest == 0) { return RL_WALK_CONTINUE; } /* * The request is finished. Wake up when it's time to * clean it up. */ if (request->finished) { difference = (request->timestamp + mainconfig.cleanup_delay) - info->now; /* * If the request is marked up to be rejected later, * then wake up later. */ if ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) != 0) { if (difference >= (time_t) mainconfig.reject_delay) { difference = (time_t) mainconfig.reject_delay; } } } else if (request->proxy && !request->proxy_reply) { /* * The request is NOT finished, but there is an * outstanding proxy request, with no matching * proxy reply. * * Wake up when it's time to re-send * the proxy request. * * But in synchronous proxy, we don't retry but we update * the next retry time as NAS has not resent the request * in the given retry window. */ if (mainconfig.proxy_synchronous) { /* * If the retry_delay * count has passed, * then mark the realm dead. */ if (info->now > (request->timestamp + (mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count))) { rad_assert(request->child_pid == NO_SUCH_CHILD_PID); request_reject(request); realm_disable(request->proxy->dst_ipaddr, request->proxy->dst_port); request->finished = TRUE; goto setup_timeout; } request->proxy_next_try = info->now + mainconfig.proxy_retry_delay; } difference = request->proxy_next_try - info->now; } else { /* * The request is NOT finished. * * Wake up when it's time to kill the errant * thread/process. */ difference = (request->timestamp + mainconfig.max_request_time) - info->now; } /* * If the server is CPU starved, then we CAN miss a time * for servicing requests. In which case the 'difference' * value will be negative. select() doesn't like that, * so we fix it. */ if (difference < 0) { difference = 0; } /* * Update the 'smallest' time. */ if ((info->smallest < 0) || (difference < info->smallest)) { info->smallest = difference; } return RL_WALK_CONTINUE; }