- {
- fprintf(stderr, "%s: Error: Unknown syslog facility: %s\n",
- progname, s);
- exit(1);
- }
-
- /* this should never be reached */
- return LOG_DAEMON;
-}
-
-
-/*
- * Check if an incoming request is "ok"
- *
- * It takes packets, not requests. It sees if the packet looks
- * OK. If so, it does a number of sanity checks on it.
- */
-static RAD_REQUEST_FUNP packet_ok(RADIUS_PACKET *packet,
- rad_listen_t *listener)
-{
- REQUEST *curreq;
- RAD_REQUEST_FUNP fun = NULL;
-
- /*
- * Some sanity checks, based on the packet code.
- */
- switch(packet->code) {
- case PW_AUTHENTICATION_REQUEST:
- /*
- * Check for requests sent to the wrong
- * port, and ignore them, if so.
- */
- if (listener->type != RAD_LISTEN_AUTH) {
- RAD_SNMP_INC(rad_snmp.auth.total_packets_dropped);
- radlog(L_ERR, "Authentication-Request sent to a non-authentication port from "
- "client %s:%d - ID %d : IGNORED",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- }
- fun = rad_authenticate;
- break;
-
- case PW_ACCOUNTING_REQUEST:
- /*
- * Check for requests sent to the wrong
- * port, and ignore them, if so.
- */
- if (listener->type != RAD_LISTEN_ACCT) {
- RAD_SNMP_INC(rad_snmp.acct.total_packets_dropped);
- radlog(L_ERR, "Accounting-Request packet sent to a non-accounting port from "
- "client %s:%d - ID %d : IGNORED",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- }
- fun = rad_accounting;
- break;
-
- case PW_AUTHENTICATION_ACK:
- case PW_ACCESS_CHALLENGE:
- case PW_AUTHENTICATION_REJECT:
- /*
- * Replies NOT sent to the proxy port get
- * an error message logged, and the
- * packet is dropped.
- */
- if (listener->type != RAD_LISTEN_PROXY) {
- RAD_SNMP_INC(rad_snmp.auth.total_packets_dropped);
- radlog(L_ERR, "Authentication reply packet code %d sent to a non-proxy reply port from "
- "client %s:%d - ID %d : IGNORED",
- packet->code,
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- }
- fun = rad_authenticate;
- break;
-
- case PW_ACCOUNTING_RESPONSE:
- /*
- * Replies NOT sent to the proxy port get
- * an error message logged, and the
- * packet is dropped.
- */
- if (listener->type != RAD_LISTEN_PROXY) {
- RAD_SNMP_INC(rad_snmp.acct.total_packets_dropped);
- radlog(L_ERR, "Accounting reply packet code %d sent to a non-proxy reply port from "
- "client %s:%d - ID %d : IGNORED",
- packet->code,
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return 0;
- }
- fun = rad_accounting;
- break;
-
- case PW_STATUS_SERVER:
- if (!mainconfig.status_server) {
- DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
- return NULL;
- }
- fun = rad_status_server;
- break;
-
- case PW_PASSWORD_REQUEST:
- RAD_SNMP_INC(rad_snmp.auth.total_unknown_types);
-
- /*
- * We don't support this anymore.
- */
- radlog(L_ERR, "Deprecated password change request from client %s:%d - ID %d : IGNORED",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- break;
-
- default:
- RAD_SNMP_INC(rad_snmp.auth.total_unknown_types);
-
- radlog(L_ERR, "Unknown packet code %d from client %s:%d "
- "- ID %d : IGNORED", packet->code,
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- break;
-
- } /* switch over packet types */
-
- /*
- * Don't handle proxy replies here. They need to
- * return the *old* request, so we can re-process it.
- */
- if (listener->type == RAD_LISTEN_PROXY) {
- return fun;
- }
-
- /*
- * If there is no existing request of id, code, etc.,
- * then we can return, and let it be processed.
- */
- if ((curreq = rl_find(packet)) == NULL) {
- /*
- * Count the total number of requests, to see if
- * there are too many. If so, return with an
- * error.
- */
- if (mainconfig.max_requests) {
- int request_count = rl_num_requests();
-
- /*
- * This is a new request. Let's see if
- * it makes us go over our configured
- * bounds.
- */
- if (request_count > mainconfig.max_requests) {
- radlog(L_ERR, "Dropping request (%d is too many): "
- "from client %s:%d - ID: %d", request_count,
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- radlog(L_INFO, "WARNING: Please check the radiusd.conf file.\n"
- "\tThe value for 'max_requests' is probably set too low.\n");
- return NULL;
- } /* else there were a small number of requests */
- } /* else there was no configured limit for requests */
-
- /*
- * FIXME: Add checks for system load. If the
- * system is busy, start dropping requests...
- *
- * We can probably keep some statistics
- * ourselves... if there are more requests
- * coming in than we can handle, start dropping
- * some.
- */
-
- return fun;
- }
-
- /*
- * "fake" requests MUST NEVER be in the request list.
- *
- * They're used internally in the server. Any reply
- * is a reply to the local server, and any proxied packet
- * gets sent outside of the tunnel.
- */
- rad_assert((curreq->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0);
-
- /*
- * The current request isn't finished, which
- * means that the NAS sent us a new packet, while
- * we are still processing the old request.
- */
- if (!curreq->finished) {
- /*
- * If the authentication vectors are identical,
- * then the NAS is re-transmitting it, trying to
- * kick us into responding to the request.
- */
- if (memcmp(curreq->packet->vector, packet->vector,
- sizeof(packet->vector)) == 0) {
- RAD_SNMP_INC(rad_snmp.auth.total_dup_requests);
-
- /*
- * It's not finished because the request
- * was proxied, but there was no reply
- * from the home server.
- */
- if (curreq->proxy && !curreq->proxy_reply) {
- /*
- * We're taking care of sending
- * duplicate proxied packets, so
- * we ignore any duplicate
- * requests from the NAS.
- *
- * FIXME: Make it ALWAYS synchronous!
- */
- if (!mainconfig.proxy_synchronous) {
- RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
-
- DEBUG2("Ignoring duplicate packet from client "
- "%s:%d - ID: %d, due to outstanding proxied request %d.",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id,
- curreq->number);
- return NULL;
-
- /*
- * We ARE proxying the request,
- * and we have NOT received a
- * proxy reply yet, and we ARE
- * doing synchronous proxying.
- *
- * In that case, go kick
- * the home RADIUS server
- * again.
- */
- } else {
- char buffer[64];
-
- DEBUG2("Sending duplicate proxied request to home server %s:%d - ID: %d",
- ip_ntoa(buffer, curreq->proxy->dst_ipaddr),
- curreq->proxy->dst_port,
-
- curreq->proxy->id);
- }
- curreq->proxy_next_try = time_now + mainconfig.proxy_retry_delay;
- rad_send(curreq->proxy, curreq->packet,
- curreq->proxysecret);
- return NULL;
- } /* else the packet was not proxied */
-
- /*
- * Someone's still working on it, so we
- * ignore the duplicate request.
- */
- radlog(L_ERR, "Discarding duplicate request from "
- "client %s:%d - ID: %d due to unfinished request %d",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id,
- curreq->number);
- return NULL;
- } /* else the authentication vectors were different */
-
- /*
- * The authentication vectors are different, so
- * the NAS has given up on us, as we've taken too
- * long to process the request. This is a
- * SERIOUS problem!
- */
- RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
-
- radlog(L_ERR, "Dropping conflicting packet from "
- "client %s:%d - ID: %d due to unfinished request %d",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id,
- curreq->number);
- return NULL;
- }
-
- /*
- * The old request is finished. We now check the
- * authentication vectors. If the client has sent us a
- * request with identical code && ID, but different
- * vector, then they MUST have gotten our response, so we
- * can delete the original request, and process the new
- * one.
- *
- * If the vectors are the same, then it's a duplicate
- * request, and we can send a duplicate reply.
- */
- if (memcmp(curreq->packet->vector, packet->vector,
- sizeof(packet->vector)) == 0) {
- RAD_SNMP_INC(rad_snmp.auth.total_dup_requests);
-
- /*
- * If the packet has been delayed, then silently
- * send a response, and clear the delayed flag.
- *
- * Note that this means if the NAS kicks us while
- * we're delaying a reject, then the reject may
- * be sent sooner than otherwise.
- *
- * This COULD be construed as a bug. Maybe what
- * we want to do is to ignore the duplicate
- * packet, and send the reject later.
- */
- if (curreq->options & RAD_REQUEST_OPTION_DELAYED_REJECT) {
- curreq->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT;
- rad_send(curreq->reply, curreq->packet, curreq->secret);
- return NULL;
- }
-
- /*
- * Maybe we've saved a reply packet. If so,
- * re-send it. Otherwise, just complain.
- */
- if (curreq->reply->code != 0) {
- DEBUG2("Sending duplicate reply "
- "to client %s:%d - ID: %d",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- rad_send(curreq->reply, curreq->packet, curreq->secret);
- return NULL;
- }
-
- /*
- * Else we never sent a reply to the NAS,
- * as we decided somehow we didn't like the request.
- *
- * This shouldn't happen, in general...
- */
- DEBUG2("Discarding duplicate request from client %s:%d - ID: %d",
- client_name(packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- } /* else the vectors were different, so we discard the old request. */
-
- /*
- * 'packet' has the same source IP, source port, code,
- * and Id as 'curreq', but a different authentication
- * vector. We can therefore delete 'curreq', as we were
- * only keeping it around to send out duplicate replies,
- * if the first reply got lost in the network.
- */
- rl_delete(curreq);
-
- /*
- * The request is OK. We can process it...
- *
- * Don't bother checking the maximum nubmer of requests
- * here. we've just deleted one, so we KNOW we're under
- * the limit if we add one more.
- */
- return fun;
-}
-
-
-/*
- * Do a proxy check of the REQUEST list when using the new proxy code.
- */
-static REQUEST *proxy_ok(RADIUS_PACKET *packet)
-{
- REALM *cl;
- REQUEST *oldreq;
- char buffer[32];
-
- /*
- * Find the original request in the request list
- */
- oldreq = rl_find_proxy(packet);
-
- /*
- * If we haven't found the original request which was
- * sent, to get this reply. Complain, and discard this
- * request, as there's no way for us to send it to a NAS.
- */
- if (!oldreq) {
- radlog(L_PROXY, "No outstanding request was found for proxy reply from home server %s:%d - ID %d",
- ip_ntoa(buffer, packet->src_ipaddr),
- packet->src_port, packet->id);
- return NULL;
- }
-
- /*
- * The proxy reply has arrived too late, as the original
- * (old) request has timed out, been rejected, and marked
- * as finished. The client has already received a
- * response, so there is nothing that can be done. Delete
- * the tardy reply from the home server, and return NULL.
- */
- if ((oldreq->reply->code != 0) ||
- (oldreq->finished)) {
- radlog(L_ERR, "Reply from home server %s:%d - ID: %d arrived too late for request %d. Try increasing 'retry_delay' or 'max_request_time'",
- ip_ntoa(buffer, packet->src_ipaddr),
- packet->src_port, packet->id,
- oldreq->number);
- return NULL;
- }
-
- /*
- * If there is already a reply, maybe this one is a
- * duplicate?
- */
- if (oldreq->proxy_reply) {
- if (memcmp(oldreq->proxy_reply->vector,
- packet->vector,
- sizeof(oldreq->proxy_reply->vector)) == 0) {
- radlog(L_ERR, "Discarding duplicate reply from home server %s:%d - ID: %d for request %d",
- ip_ntoa(buffer, packet->src_ipaddr),
- packet->src_port, packet->id,
- oldreq->number);
- } else {
- /*
- * ? The home server gave us a new *
- * proxy reply, which doesn't match * the
- * old one. Delete it
- ! */
- DEBUG2("Ignoring conflicting proxy reply");
- }
-
- /*
- * We've already received a reply, so
- * we discard this one, as we don't want
- * to do duplicate work.
- */
- return NULL;
- } /* else there wasn't a proxy reply yet, so we can process it */
-
- /*
- * Refresh the old request, and update it with the proxy
- * reply.
- *
- * ? Can we delete the proxy request here? * Is there
- * any more need for it?
- *
- * FIXME: we probably shouldn't be updating the time
- * stamp here.
- */
- oldreq->timestamp = time_now;
- oldreq->proxy_reply = packet;
-
- /*
- * Now that we've verified the packet IS actually
- * from that realm, and not forged, we can go mark the
- * realms for this home server as active.
- *
- * If we had done this check in the 'find realm by IP address'
- * function, then an attacker could force us to use a home
- * server which was inactive, by forging reply packets
- * which didn't match any request. We would think that
- * the reply meant the home server was active, would
- * re-activate the realms, and THEN bounce the packet
- * as garbage.
- */
- for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
- if (oldreq->proxy_reply->src_ipaddr == cl->ipaddr) {
- if (oldreq->proxy_reply->src_port == cl->auth_port) {
- cl->active = TRUE;
- cl->last_reply = oldreq->timestamp;
- } else if (oldreq->proxy_reply->src_port == cl->acct_port) {
- cl->acct_active = TRUE;
- cl->last_reply = oldreq->timestamp;
- }
- }
- }
-
- return oldreq;
-}
-
-/*
- * Do more checks, this time on the REQUEST data structure.
- *
- * The main purpose of this code is to handle proxied requests.
- */
-static REQUEST *request_ok(RADIUS_PACKET *packet, uint8_t *secret,
- rad_listen_t *listener)
-{
- REQUEST *request = NULL;
-
- /*
- * If the request has come in on the proxy FD, then
- * it's a proxy reply, so pass it through the code which
- * tries to find the original request, which we should
- * process, rather than processing the reply as a "new"
- * request.
- */
- if (listener->type == RAD_LISTEN_PROXY) {
- /*
- * Find the old request, based on the current
- * packet.
- */
- request = proxy_ok(packet);
- if (!request) {
- return NULL;
- }
- rad_assert(request->magic == REQUEST_MAGIC);
-
- /*
- * We must have passed through the code below
- * for the original request, which adds the
- * reply packet to it.
- */
- rad_assert(request->reply != NULL);
-
- } else { /* remember the new request */
- /*
- * A unique per-request counter.
- */
- static int request_num_counter = 0;
-
- request = request_alloc(); /* never fails */
- request->packet = packet;
- request->number = request_num_counter++;
- strNcpy(request->secret, (char *)secret,
- sizeof(request->secret));
-
- /*
- * Remember the request.
- */
- rl_add(request);
-
- /*
- * ADD IN "server identifier" from "listen"
- * directive!
- */
-
- /*
- * The request passes many of our sanity checks.
- * From here on in, if anything goes wrong, we
- * send a reject message, instead of dropping the
- * packet.
- *
- * Build the reply template from the request
- * template.
- */
- rad_assert(request->reply == NULL);
- if ((request->reply = rad_alloc(0)) == NULL) {
- radlog(L_ERR, "No memory");
- exit(1);
- }
- request->reply->sockfd = request->packet->sockfd;
- request->reply->dst_ipaddr = request->packet->src_ipaddr;
- request->reply->src_ipaddr = request->packet->dst_ipaddr;
- request->reply->dst_port = request->packet->src_port;
- request->reply->src_port = request->packet->dst_port;
- request->reply->id = request->packet->id;
- request->reply->code = 0; /* UNKNOWN code */
- memcpy(request->reply->vector, request->packet->vector,
- sizeof(request->reply->vector));
- request->reply->vps = NULL;
- request->reply->data = NULL;
- request->reply->data_len = 0;
- }
-
- return request;
-}
-