X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmain%2Flisten.c;h=b6e2b4dbcd237b62f8b0630455123853aa0b9ab4;hb=1147ab4f2843e6deee5fd07f2b716de7cbb1675b;hp=05098db68283351a1a916c864c53a8556f9207c7;hpb=539fc567a4eb4854e60f1c3669d178d5a474d49c;p=freeradius.git diff --git a/src/main/listen.c b/src/main/listen.c index 05098db..b6e2b4d 100644 --- a/src/main/listen.c +++ b/src/main/listen.c @@ -25,14 +25,18 @@ RCSID("$Id$") #include -#include #include #include #include +#include #include #include +#ifdef WITH_UDPFROMTO +#include +#endif + #ifdef HAVE_SYS_RESOURCE_H #include #endif @@ -46,6 +50,19 @@ RCSID("$Id$") #endif +void print_packet(RADIUS_PACKET *packet) +{ + char src[256], dst[256]; + + ip_ntoh(&packet->src_ipaddr, src, sizeof(src)); + ip_ntoh(&packet->dst_ipaddr, dst, sizeof(dst)); + + fprintf(stderr, "ID %d: %s %d -> %s %d\n", packet->id, + src, packet->src_port, dst, packet->dst_port); + +} + + /* * We'll use this below. */ @@ -62,38 +79,201 @@ typedef struct rad_listen_master_t { rad_listen_decode_t decode; } rad_listen_master_t; -typedef struct listen_socket_t { - /* - * For normal sockets. - */ - fr_ipaddr_t ipaddr; - int port; - RADCLIENT_LIST *clients; -} listen_socket_t; +static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type); /* * Find a per-socket client. */ RADCLIENT *client_listener_find(const rad_listen_t *listener, - const fr_ipaddr_t *ipaddr) + const fr_ipaddr_t *ipaddr, int src_port) { - const RADCLIENT_LIST *clients; +#ifdef WITH_DYNAMIC_CLIENTS + int rcode; + REQUEST *request; + RADCLIENT *created; +#endif + time_t now; + RADCLIENT *client; + RADCLIENT_LIST *clients; + listen_socket_t *sock; rad_assert(listener != NULL); rad_assert(ipaddr != NULL); - rad_assert((listener->type == RAD_LISTEN_AUTH) || - (listener->type == RAD_LISTEN_ACCT) || - (listener->type == RAD_LISTEN_VQP)); - - clients = ((listen_socket_t *)listener->data)->clients; + sock = listener->data; + clients = sock->clients; /* * This HAS to have been initialized previously. */ rad_assert(clients != NULL); - return client_find(clients, ipaddr); + client = client_find(clients, ipaddr,sock->proto); + if (!client) { + char name[256], buffer[128]; + +#ifdef WITH_DYNAMIC_CLIENTS + unknown: /* used only for dynamic clients */ +#endif + + /* + * DoS attack quenching, but only in daemon mode. + * If they're running in debug mode, show them + * every packet. + */ + if (debug_flag == 0) { + static time_t last_printed = 0; + + now = time(NULL); + if (last_printed == now) return NULL; + + last_printed = now; + } + + listener->print(listener, name, sizeof(name)); + + radlog(L_ERR, "Ignoring request to %s from unknown client %s port %d" +#ifdef WITH_TCP + " proto %s" +#endif + , name, inet_ntop(ipaddr->af, &ipaddr->ipaddr, + buffer, sizeof(buffer)), src_port +#ifdef WITH_TCP + , (sock->proto == IPPROTO_UDP) ? "udp" : "tcp" +#endif + ); + return NULL; + } + +#ifndef WITH_DYNAMIC_CLIENTS + return client; /* return the found client. */ +#else + + /* + * No server defined, and it's not dynamic. Return it. + */ + if (!client->client_server && !client->dynamic) return client; + + now = time(NULL); + + /* + * It's a dynamically generated client, check it. + */ + if (client->dynamic && (src_port != 0)) { + /* + * Lives forever. Return it. + */ + if (client->lifetime == 0) return client; + + /* + * Rate-limit the deletion of known clients. + * This makes them last a little longer, but + * prevents the server from melting down if (say) + * 10k clients all expire at once. + */ + if (now == client->last_new_client) return client; + + /* + * It's not dead yet. Return it. + */ + if ((client->created + client->lifetime) > now) return client; + + /* + * This really puts them onto a queue for later + * deletion. + */ + client_delete(clients, client); + + /* + * Go find the enclosing network again. + */ + client = client_find(clients, ipaddr, sock->proto); + + /* + * WTF? + */ + if (!client) goto unknown; + if (!client->client_server) goto unknown; + + /* + * At this point, 'client' is the enclosing + * network that configures where dynamic clients + * can be defined. + */ + rad_assert(client->dynamic == 0); + + } else if (!client->dynamic && client->rate_limit) { + /* + * The IP is unknown, so we've found an enclosing + * network. Enable DoS protection. We only + * allow one new client per second. Known + * clients aren't subject to this restriction. + */ + if (now == client->last_new_client) goto unknown; + } + + client->last_new_client = now; + + request = request_alloc(); + if (!request) goto unknown; + + request->listener = listener; + request->client = client; + request->packet = rad_recv(listener->fd, 0x02); /* MSG_PEEK */ + if (!request->packet) { /* badly formed, etc */ + request_free(&request); + goto unknown; + } + request->reply = rad_alloc_reply(request->packet); + if (!request->reply) { + request_free(&request); + goto unknown; + } + request->packet->timestamp = request->timestamp; + request->number = 0; + request->priority = listener->type; + request->server = client->client_server; + request->root = &mainconfig; + + /* + * Run a fake request through the given virtual server. + * Look for FreeRADIUS-Client-IP-Address + * FreeRADIUS-Client-Secret + * ... + * + * and create the RADCLIENT structure from that. + */ + DEBUG("server %s {", request->server); + + rcode = module_authorize(0, request); + + DEBUG("} # server %s", request->server); + + if (rcode != RLM_MODULE_OK) { + request_free(&request); + goto unknown; + } + + /* + * If the client was updated by rlm_dynamic_clients, + * don't create the client from attribute-value pairs. + */ + if (request->client == client) { + created = client_create(clients, request); + } else { + created = request->client; + + /* + * This frees the client if it isn't valid. + */ + if (!client_validate(clients, client, created)) goto unknown; + } + request_free(&request); + + if (!created) goto unknown; + + return created; +#endif } static int listen_bind(rad_listen_t *this); @@ -110,8 +290,11 @@ static int rad_status_server(REQUEST *request) DICT_VALUE *dval; switch (request->listener->type) { +#ifdef WITH_STATS + case RAD_LISTEN_NONE: +#endif case RAD_LISTEN_AUTH: - dval = dict_valbyname(PW_AUTZ_TYPE, "Status-Server"); + dval = dict_valbyname(PW_AUTZ_TYPE, 0, "Status-Server"); if (dval) { rcode = module_authorize(dval->value, request); } else { @@ -136,8 +319,9 @@ static int rad_status_server(REQUEST *request) } break; +#ifdef WITH_ACCOUNTING case RAD_LISTEN_ACCT: - dval = dict_valbyname(PW_ACCT_TYPE, "Status-Server"); + dval = dict_valbyname(PW_ACCT_TYPE, 0, "Status-Server"); if (dval) { rcode = module_accounting(dval->value, request); } else { @@ -155,40 +339,315 @@ static int rad_status_server(REQUEST *request) break; } break; +#endif + +#ifdef WITH_COA + /* + * This is a vendor extension. Suggested by Glen + * Zorn in IETF 72, and rejected by the rest of + * the WG. We like it, so it goes in here. + */ + case RAD_LISTEN_COA: + dval = dict_valbyname(PW_RECV_COA_TYPE, 0, "Status-Server"); + if (dval) { + rcode = module_recv_coa(dval->value, request); + } else { + rcode = RLM_MODULE_OK; + } + + switch (rcode) { + case RLM_MODULE_OK: + case RLM_MODULE_UPDATED: + request->reply->code = PW_COA_ACK; + break; + + default: + request->reply->code = 0; /* don't reply */ + break; + } + break; +#endif + + default: + return 0; + } + +#ifdef WITH_STATS + /* + * Full statistics are available only on a statistics + * socket. + */ + if (request->listener->type == RAD_LISTEN_NONE) { + request_stats_reply(request); + } +#endif + + return 0; +} + +#ifdef WITH_TCP +static int auth_tcp_recv(rad_listen_t *listener, + RAD_REQUEST_FUNP *pfun, REQUEST **prequest) +{ + int rcode; + RADIUS_PACKET *packet; + RAD_REQUEST_FUNP fun = NULL; + listen_socket_t *sock = listener->data; + RADCLIENT *client = sock->client; + + /* + * Allocate a packet for partial reads. + */ + if (!sock->packet) { + sock->packet = rad_alloc(0); + if (!sock->packet) return 0; + + sock->packet->sockfd = listener->fd; + sock->packet->src_ipaddr = sock->other_ipaddr; + sock->packet->src_port = sock->other_port; + sock->packet->dst_ipaddr = sock->my_ipaddr; + sock->packet->dst_port = sock->my_port; + } + + /* + * Grab the packet currently being processed. + */ + packet = sock->packet; + + rcode = fr_tcp_read_packet(packet, 0); + + /* + * Still only a partial packet. Put it back, and return, + * so that we'll read more data when it's ready. + */ + if (rcode == 0) { + return 0; + } + + if (rcode == -1) { /* error reading packet */ + char buffer[256]; + + radlog(L_ERR, "Invalid packet from %s port %d: closing socket", + ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)), + packet->src_port); + } + + if (rcode < 0) { /* error or connection reset */ + listener->status = RAD_LISTEN_STATUS_REMOVE_FD; + + /* + * Decrement the number of connections. + */ + if (sock->parent->num_connections > 0) { + sock->parent->num_connections--; + } + if (sock->client->num_connections > 0) { + sock->client->num_connections--; + } + + /* + * Tell the event handler that an FD has disappeared. + */ + DEBUG("Client has closed connection"); + event_new_fd(listener); + + /* + * Do NOT free the listener here. It's in use by + * a request, and will need to hang around until + * all of the requests are done. + * + * It is instead free'd in remove_from_request_hash() + */ + return 0; + } + + RAD_STATS_TYPE_INC(listener, total_requests); + + /* + * Some sanity checks, based on the packet code. + */ + switch(packet->code) { + case PW_AUTHENTICATION_REQUEST: + RAD_STATS_CLIENT_INC(listener, client, total_requests); + fun = rad_authenticate; + break; + + case PW_STATUS_SERVER: + if (!mainconfig.status_server) { + RAD_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped); + DEBUG("WARNING: Ignoring Status-Server request due to security configuration"); + rad_free(&sock->packet); + return 0; + } + fun = rad_status_server; + break; default: + RAD_STATS_INC(radius_auth_stats.total_unknown_types); + RAD_STATS_CLIENT_INC(listener, client, total_unknown_types); + + DEBUG("Invalid packet code %d sent to authentication port from client %s port %d : IGNORED", + packet->code, client->shortname, packet->src_port); + rad_free(&sock->packet); + return 0; + } /* switch over packet types */ + + if (!received_request(listener, packet, prequest, sock->client)) { + RAD_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, sock->client, total_packets_dropped); + rad_free(&sock->packet); + return 0; + } + + *pfun = fun; + sock->packet = NULL; /* we have no need for more partial reads */ + return 1; +} + +static int auth_tcp_accept(rad_listen_t *listener, + UNUSED RAD_REQUEST_FUNP *pfun, + UNUSED REQUEST **prequest) +{ + int newfd, src_port; + rad_listen_t *this; + socklen_t salen; + struct sockaddr_storage src; + listen_socket_t *sock; + fr_ipaddr_t src_ipaddr; + RADCLIENT *client; + + salen = sizeof(src); + + DEBUG2(" ... new connection request on TCP socket."); + + newfd = accept(listener->fd, (struct sockaddr *) &src, &salen); + if (newfd < 0) { + /* + * Non-blocking sockets must handle this. + */ + if (errno == EWOULDBLOCK) { + return 0; + } + + DEBUG2(" ... failed to accept connection."); + return -1; + } + + if (!fr_sockaddr2ipaddr(&src, salen, &src_ipaddr, &src_port)) { + DEBUG2(" ... unknown address family."); + return 0; + } + + /* + * Enforce client IP address checks on accept, not on + * every packet. + */ + if ((client = client_listener_find(listener, + &src_ipaddr, src_port)) == NULL) { + close(newfd); + RAD_STATS_TYPE_INC(listener, total_invalid_requests); + return 0; + } + + /* + * Enforce max_connectionsx on client && listen section. + */ + if ((client->max_connections != 0) && + (client->max_connections == client->num_connections)) { + /* + * FIXME: Print client IP/port, and server IP/port. + */ + radlog(L_INFO, "Ignoring new connection due to client max_connections (%d)", client->max_connections); + close(newfd); + return 0; + } + + sock = listener->data; + if ((sock->max_connections != 0) && + (sock->max_connections == sock->num_connections)) { + /* + * FIXME: Print client IP/port, and server IP/port. + */ + radlog(L_INFO, "Ignoring new connection due to socket max_connections"); + close(newfd); return 0; } + client->num_connections++; + sock->num_connections++; + + /* + * Add the new listener. + */ + this = listen_alloc(listener->type); + if (!this) return -1; + + /* + * Copy everything, including the pointer to the socket + * information. + */ + sock = this->data; + memcpy(this->data, listener->data, sizeof(*sock)); + memcpy(this, listener, sizeof(*this)); + this->next = NULL; + this->data = sock; /* fix it back */ + + sock->parent = listener->data; + sock->other_ipaddr = src_ipaddr; + sock->other_port = src_port; + sock->client = client; + + this->fd = newfd; + this->status = RAD_LISTEN_STATUS_INIT; + this->recv = auth_tcp_recv; + + /* + * FIXME: set O_NONBLOCK on the accept'd fd. + * See djb's portability rants for details. + */ + + /* + * Tell the event loop that we have a new FD. + * This can be called from a child thread... + */ + event_new_fd(this); return 0; } +#endif -static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize) +/* + * This function is stupid and complicated. + */ +static int socket_print(const rad_listen_t *this, char *buffer, size_t bufsize) { + size_t len; listen_socket_t *sock = this->data; const char *name; - char ip_buf[256]; - - if ((sock->ipaddr.af == AF_INET) && - (sock->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) { - strcpy(ip_buf, "*"); - } else { - ip_ntoh(&sock->ipaddr, ip_buf, sizeof(ip_buf)); - } switch (this->type) { +#ifdef WITH_STATS + case RAD_LISTEN_NONE: /* what a hack... */ + name = "status"; + break; +#endif + case RAD_LISTEN_AUTH: name = "authentication"; break; +#ifdef WITH_ACCOUNTING case RAD_LISTEN_ACCT: name = "accounting"; break; +#endif +#ifdef WITH_PROXY case RAD_LISTEN_PROXY: name = "proxy"; break; +#endif #ifdef WITH_VMPS case RAD_LISTEN_VQP: @@ -196,27 +655,144 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize) break; #endif +#ifdef WITH_DHCP + case RAD_LISTEN_DHCP: + name = "dhcp"; + break; +#endif + +#ifdef WITH_COA + case RAD_LISTEN_COA: + name = "coa"; + break; +#endif + default: name = "??"; break; } - if (!this->server) { - return snprintf(buffer, bufsize, "%s address %s port %d", - name, ip_buf, sock->port); - } +#define FORWARD len = strlen(buffer); if (len >= (bufsize + 1)) return 0;buffer += len;bufsize -= len +#define ADDSTRING(_x) strlcpy(buffer, _x, bufsize);FORWARD - return snprintf(buffer, bufsize, "%s address %s port %d as server %s", - name, ip_buf, sock->port, this->server); -} + ADDSTRING(name); + if (sock->interface) { + ADDSTRING(" interface "); + ADDSTRING(sock->interface); + } -/* - * Parse an authentication or accounting socket. - */ -static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) -{ - int rcode; +#ifdef WITH_TCP + if (this->recv == auth_tcp_accept) { + ADDSTRING(" proto tcp"); + } +#endif + +#ifdef WITH_TCP + /* + * TCP sockets get printed a little differently, to make + * it clear what's going on. + */ + if (sock->client) { + ADDSTRING(" from client ("); + ip_ntoh(&sock->other_ipaddr, buffer, bufsize); + FORWARD; + + ADDSTRING(", "); + snprintf(buffer, bufsize, "%d", sock->other_port); + FORWARD; + ADDSTRING(") -> ("); + + if ((sock->my_ipaddr.af == AF_INET) && + (sock->my_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) { + strlcpy(buffer, "*", bufsize); + } else { + ip_ntoh(&sock->my_ipaddr, buffer, bufsize); + } + FORWARD; + + ADDSTRING(", "); + snprintf(buffer, bufsize, "%d", sock->my_port); + FORWARD; + + if (this->server) { + ADDSTRING(", virtual-server="); + ADDSTRING(this->server); + } + + ADDSTRING(")"); + + return 1; + } + +#ifdef WITH_PROXY + /* + * Maybe it's a socket that we opened to a home server. + */ + if ((sock->proto == IPPROTO_TCP) && + (this->type == RAD_LISTEN_PROXY)) { + ADDSTRING(" ("); + ip_ntoh(&sock->my_ipaddr, buffer, bufsize); + FORWARD; + + ADDSTRING(", "); + snprintf(buffer, bufsize, "%d", sock->my_port); + FORWARD; + ADDSTRING(") -> home_server ("); + + if ((sock->other_ipaddr.af == AF_INET) && + (sock->other_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) { + strlcpy(buffer, "*", bufsize); + } else { + ip_ntoh(&sock->other_ipaddr, buffer, bufsize); + } + FORWARD; + + ADDSTRING(", "); + snprintf(buffer, bufsize, "%d", sock->other_port); + FORWARD; + + ADDSTRING(")"); + + return 1; + } +#endif /* WITH_PROXY */ +#endif /* WITH_TCP */ + + ADDSTRING(" address "); + + if ((sock->my_ipaddr.af == AF_INET) && + (sock->my_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) { + strlcpy(buffer, "*", bufsize); + } else { + ip_ntoh(&sock->my_ipaddr, buffer, bufsize); + } + FORWARD; + + ADDSTRING(" port "); + snprintf(buffer, bufsize, "%d", sock->my_port); + FORWARD; + + if (this->server) { + ADDSTRING(" as server "); + ADDSTRING(this->server); + } + +#undef ADDSTRING +#undef FORWARD + + return 1; +} + +extern int check_config; /* radiusd.c */ + + +/* + * Parse an authentication or accounting socket. + */ +static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) +{ + int rcode; int listen_port; fr_ipaddr_t ipaddr; listen_socket_t *sock = this->data; @@ -251,19 +827,74 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) &listen_port, "0"); if (rcode < 0) return -1; - sock->ipaddr = ipaddr; - sock->port = listen_port; + if ((listen_port < 0) || (listen_port > 65535)) { + cf_log_err(cf_sectiontoitem(cs), + "Invalid value for \"port\""); + return -1; + } - /* - * And bind it to the port. - */ - if (listen_bind(this) < 0) { - char buffer[128]; + sock->proto = IPPROTO_UDP; + + if (cf_pair_find(cs, "proto")) { +#ifndef WITH_TCP cf_log_err(cf_sectiontoitem(cs), - "Error binding to port for %s port %d", - ip_ntoh(&sock->ipaddr, buffer, sizeof(buffer)), - sock->port); + "System does not support the TCP protocol. Delete this line from the configuration file."); return -1; +#else + char *proto = NULL; + + + rcode = cf_item_parse(cs, "proto", PW_TYPE_STRING_PTR, + &proto, "udp"); + if (rcode < 0) return -1; + + if (strcmp(proto, "udp") == 0) { + sock->proto = IPPROTO_UDP; + + } else if (strcmp(proto, "tcp") == 0) { + sock->proto = IPPROTO_TCP; + + rcode = cf_item_parse(cs, "max_connections", PW_TYPE_INTEGER, + &sock->max_connections, "64"); + if (rcode < 0) return -1; + + } else { + cf_log_err(cf_sectiontoitem(cs), + "Unknown proto name \"%s\"", proto); + free(proto); + return -1; + } + free(proto); + + /* + * TCP requires a destination IP for sockets. + * UDP doesn't, so it's allowed. + */ +#ifdef WITH_PROXY + if ((this->type == RAD_LISTEN_PROXY) && + (sock->proto != IPPROTO_UDP)) { + cf_log_err(cf_sectiontoitem(cs), + "Proxy listeners can only listen on proto = udp"); + return -1; + } +#endif /* WITH_PROXY */ +#endif /* WITH_TCP */ + } + + sock->my_ipaddr = ipaddr; + sock->my_port = listen_port; + + if (check_config) { + if (home_server_find(&sock->my_ipaddr, sock->my_port, sock->proto)) { + char buffer[128]; + + DEBUG("ERROR: We have been asked to listen on %s port %d, which is also listed as a home server. This can create a proxy loop.", + ip_ntoh(&sock->my_ipaddr, buffer, sizeof(buffer)), + sock->my_port); + return -1; + } + + return 0; /* don't do anything */ } /* @@ -271,14 +902,8 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) * else don't. */ if (cf_pair_find(cs, "interface")) { -#ifndef SO_BINDTODEVICE - cf_log_err(cf_sectiontoitem(cs), - "System does not support binding to interfaces. Delete this line from the configuration file."); - return -1; -#else const char *value; CONF_PAIR *cp = cf_pair_find(cs, "interface"); - struct ifreq ifreq; rad_assert(cp != NULL); value = cf_pair_value(cp); @@ -287,24 +912,69 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) "No interface name given"); return -1; } + sock->interface = value; + } - strcpy(ifreq.ifr_name, value); +#ifdef WITH_DHCP + /* + * If we can do broadcasts.. + */ + if (cf_pair_find(cs, "broadcast")) { +#ifndef SO_BROADCAST + cf_log_err(cf_sectiontoitem(cs), + "System does not support broadcast sockets. Delete this line from the configuration file."); + return -1; +#else + const char *value; + CONF_PAIR *cp = cf_pair_find(cs, "broadcast"); - if (setsockopt(this->fd, SOL_SOCKET, SO_BINDTODEVICE, - (char *)&ifreq, sizeof(ifreq)) < 0) { + if (this->type != RAD_LISTEN_DHCP) { + cf_log_err(cf_pairtoitem(cp), + "Broadcast can only be set for DHCP listeners. Delete this line from the configuration file."); + return -1; + } + + rad_assert(cp != NULL); + value = cf_pair_value(cp); + if (!value) { cf_log_err(cf_sectiontoitem(cs), - "Failed binding to interface %s: %s", - value, strerror(errno)); + "No broadcast value given"); return -1; - } /* else it worked. */ + } + + /* + * Hack... whatever happened to cf_section_parse? + */ + sock->broadcast = (strcmp(value, "yes") == 0); #endif } +#endif + + /* + * And bind it to the port. + */ + if (listen_bind(this) < 0) { + char buffer[128]; + cf_log_err(cf_sectiontoitem(cs), + "Error binding to port for %s port %d", + ip_ntoh(&sock->my_ipaddr, buffer, sizeof(buffer)), + sock->my_port); + return -1; + } + +#ifdef WITH_PROXY + /* + * Proxy sockets don't have clients. + */ + if (this->type == RAD_LISTEN_PROXY) return 0; +#endif /* * The more specific configurations are preferred to more * generic ones. */ client_cs = NULL; + parentcs = cf_top_section(cs); rcode = cf_item_parse(cs, "clients", PW_TYPE_STRING_PTR, §ion_name, NULL); if (rcode < 0) return -1; /* bad string */ @@ -312,7 +982,12 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) /* * Explicit list given: use it. */ - client_cs = cf_section_find(section_name); + client_cs = cf_section_sub_find_name2(parentcs, + "clients", + section_name); + if (!client_cs) { + client_cs = cf_section_find(section_name); + } if (!client_cs) { cf_log_err(cf_sectiontoitem(cs), "Failed to find clients %s {...}", @@ -323,7 +998,6 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) free(section_name); } /* else there was no "clients = " entry. */ - parentcs = cf_top_section(cs); if (!client_cs) { CONF_SECTION *server_cs; @@ -348,10 +1022,20 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) sock->clients = clients_parse_section(client_cs); if (!sock->clients) { cf_log_err(cf_sectiontoitem(cs), - "Failed to find any clients for this listen section"); + "Failed to load clients for this listen section"); return -1; } +#ifdef WITH_TCP + if (sock->proto == IPPROTO_TCP) { + /* + * Re-write the listener receive function to + * allow us to accept the socket. + */ + this->recv = auth_tcp_accept; + } +#endif + return 0; } @@ -368,6 +1052,7 @@ static int auth_socket_send(rad_listen_t *listener, REQUEST *request) } +#ifdef WITH_ACCOUNTING /* * Send an accounting response packet (or not) */ @@ -387,25 +1072,435 @@ static int acct_socket_send(rad_listen_t *listener, REQUEST *request) return rad_send(request->reply, request->packet, request->client->secret); } +#endif + +#ifdef WITH_PROXY +/* + * 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) +{ + rad_assert(request->proxy_listener == listener); + rad_assert(listener->send == proxy_socket_send); + + return rad_send(request->proxy, request->packet, + request->home_server->secret); +} +#endif + +#ifdef WITH_STATS +/* + * 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 int stats_socket_recv(rad_listen_t *listener, + RAD_REQUEST_FUNP *pfun, REQUEST **prequest) +{ + ssize_t rcode; + int code, src_port; + RADIUS_PACKET *packet; + RADCLIENT *client; + fr_ipaddr_t src_ipaddr; + + rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code); + if (rcode < 0) return 0; + + RAD_STATS_TYPE_INC(listener, total_requests); + + if (rcode < 20) { /* AUTH_HDR_LEN */ + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + return 0; + } + + if ((client = client_listener_find(listener, + &src_ipaddr, src_port)) == NULL) { + rad_recv_discard(listener->fd); + RAD_STATS_TYPE_INC(listener, total_invalid_requests); + return 0; + } + + /* + * We only understand Status-Server on this socket. + */ + if (code != PW_STATUS_SERVER) { + DEBUG("Ignoring packet code %d sent to Status-Server port", + code); + rad_recv_discard(listener->fd); + RAD_STATS_TYPE_INC(listener, total_unknown_types); + RAD_STATS_CLIENT_INC(listener, client, total_unknown_types); + return 0; + } + + /* + * Now that we've sanity checked everything, receive the + * packet. + */ + packet = rad_recv(listener->fd, 1); /* require message authenticator */ + if (!packet) { + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + DEBUG("%s", fr_strerror()); + return 0; + } + + if (!received_request(listener, packet, prequest, client)) { + RAD_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped); + rad_free(&packet); + return 0; + } + + *pfun = rad_status_server; + return 1; +} +#endif + + +/* + * 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 int auth_socket_recv(rad_listen_t *listener, + RAD_REQUEST_FUNP *pfun, REQUEST **prequest) +{ + ssize_t rcode; + int code, src_port; + RADIUS_PACKET *packet; + RAD_REQUEST_FUNP fun = NULL; + RADCLIENT *client; + fr_ipaddr_t src_ipaddr; + + rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code); + if (rcode < 0) return 0; + + RAD_STATS_TYPE_INC(listener, total_requests); + + if (rcode < 20) { /* AUTH_HDR_LEN */ + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + return 0; + } + + if ((client = client_listener_find(listener, + &src_ipaddr, src_port)) == NULL) { + rad_recv_discard(listener->fd); + RAD_STATS_TYPE_INC(listener, total_invalid_requests); + return 0; + } + + /* + * Some sanity checks, based on the packet code. + */ + switch(code) { + case PW_AUTHENTICATION_REQUEST: + RAD_STATS_CLIENT_INC(listener, client, total_requests); + fun = rad_authenticate; + break; + + case PW_STATUS_SERVER: + if (!mainconfig.status_server) { + rad_recv_discard(listener->fd); + RAD_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped); + DEBUG("WARNING: Ignoring Status-Server request due to security configuration"); + return 0; + } + fun = rad_status_server; + break; + + default: + rad_recv_discard(listener->fd); + RAD_STATS_INC(radius_auth_stats.total_unknown_types); + RAD_STATS_CLIENT_INC(listener, client, total_unknown_types); + + DEBUG("Invalid packet code %d sent to authentication port from client %s port %d : IGNORED", + code, client->shortname, src_port); + return 0; + break; + } /* switch over packet types */ + + /* + * Now that we've sanity checked everything, receive the + * packet. + */ + packet = rad_recv(listener->fd, client->message_authenticator); + if (!packet) { + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + DEBUG("%s", fr_strerror()); + return 0; + } + + if (!received_request(listener, packet, prequest, client)) { + RAD_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped); + rad_free(&packet); + return 0; + } + + *pfun = fun; + return 1; +} + + +#ifdef WITH_ACCOUNTING +/* + * Receive packets from an accounting socket + */ +static int acct_socket_recv(rad_listen_t *listener, + RAD_REQUEST_FUNP *pfun, REQUEST **prequest) +{ + ssize_t rcode; + int code, src_port; + RADIUS_PACKET *packet; + RAD_REQUEST_FUNP fun = NULL; + RADCLIENT *client; + fr_ipaddr_t src_ipaddr; + + rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code); + if (rcode < 0) return 0; + + RAD_STATS_TYPE_INC(listener, total_requests); + + if (rcode < 20) { /* AUTH_HDR_LEN */ + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + return 0; + } + + if ((client = client_listener_find(listener, + &src_ipaddr, src_port)) == NULL) { + rad_recv_discard(listener->fd); + RAD_STATS_TYPE_INC(listener, total_invalid_requests); + return 0; + } + + /* + * Some sanity checks, based on the packet code. + */ + switch(code) { + case PW_ACCOUNTING_REQUEST: + RAD_STATS_CLIENT_INC(listener, client, total_requests); + fun = rad_accounting; + break; + + case PW_STATUS_SERVER: + if (!mainconfig.status_server) { + rad_recv_discard(listener->fd); + RAD_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, client, total_unknown_types); + + DEBUG("WARNING: Ignoring Status-Server request due to security configuration"); + return 0; + } + fun = rad_status_server; + break; + + default: + rad_recv_discard(listener->fd); + RAD_STATS_TYPE_INC(listener, total_unknown_types); + RAD_STATS_CLIENT_INC(listener, client, total_unknown_types); + + DEBUG("Invalid packet code %d sent to a accounting port from client %s port %d : IGNORED", + code, client->shortname, src_port); + return 0; + } /* switch over packet types */ + + /* + * Now that we've sanity checked everything, receive the + * packet. + */ + packet = rad_recv(listener->fd, 0); + if (!packet) { + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + radlog(L_ERR, "%s", fr_strerror()); + return 0; + } + + /* + * There can be no duplicate accounting packets. + */ + if (!received_request(listener, packet, prequest, client)) { + RAD_STATS_TYPE_INC(listener, total_packets_dropped); + RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped); + rad_free(&packet); + return 0; + } + + *pfun = fun; + return 1; +} +#endif -/* - * 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; +#ifdef WITH_COA +/* + * For now, all CoA requests are *only* originated, and not + * proxied. So all of the necessary work is done in the + * post-proxy section, which is automatically handled by event.c. + * As a result, we don't have to do anything here. + */ +static int rad_coa_reply(REQUEST *request) +{ + VALUE_PAIR *s1, *s2; + + /* + * Inform the user about RFC requirements. + */ + s1 = pairfind(request->proxy->vps, PW_STATE, 0); + if (s1) { + s2 = pairfind(request->proxy_reply->vps, PW_STATE, 0); + + if (!s2) { + DEBUG("WARNING: Client was sent State in CoA, and did not respond with State."); + + } else if ((s1->length != s2->length) || + (memcmp(s1->vp_octets, s2->vp_octets, + s1->length) != 0)) { + DEBUG("WARNING: Client was sent State in CoA, and did not respond with the same State."); + } + } + + return RLM_MODULE_OK; +} + +/* + * Receive a CoA packet. + */ +static int rad_coa_recv(REQUEST *request) +{ + int rcode = RLM_MODULE_OK; + int ack, nak; + VALUE_PAIR *vp; + + /* + * Get the correct response + */ + switch (request->packet->code) { + case PW_COA_REQUEST: + ack = PW_COA_ACK; + nak = PW_COA_NAK; + break; + + case PW_DISCONNECT_REQUEST: + ack = PW_DISCONNECT_ACK; + nak = PW_DISCONNECT_NAK; + break; + + default: /* shouldn't happen */ + return RLM_MODULE_FAIL; + } + +#ifdef WITH_PROXY +#define WAS_PROXIED (request->proxy) +#else +#define WAS_PROXIED (0) +#endif + + if (!WAS_PROXIED) { + /* + * RFC 5176 Section 3.3. If we have a CoA-Request + * with Service-Type = Authorize-Only, it MUST + * have a State attribute in it. + */ + vp = pairfind(request->packet->vps, PW_SERVICE_TYPE, 0); + if (request->packet->code == PW_COA_REQUEST) { + if (vp && (vp->vp_integer == 17)) { + vp = pairfind(request->packet->vps, PW_STATE, 0); + if (!vp || (vp->length == 0)) { + RDEBUG("ERROR: CoA-Request with Service-Type = Authorize-Only MUST contain a State attribute"); + request->reply->code = PW_COA_NAK; + return RLM_MODULE_FAIL; + } + } + } else if (vp) { + /* + * RFC 5176, Section 3.2. + */ + RDEBUG("ERROR: Disconnect-Request MUST NOT contain a Service-Type attribute"); + request->reply->code = PW_DISCONNECT_NAK; + return RLM_MODULE_FAIL; + } + + rcode = module_recv_coa(0, request); + switch (rcode) { + case RLM_MODULE_FAIL: + case RLM_MODULE_INVALID: + case RLM_MODULE_REJECT: + case RLM_MODULE_USERLOCK: + default: + request->reply->code = nak; + break; + + case RLM_MODULE_HANDLED: + return rcode; + + case RLM_MODULE_NOOP: + case RLM_MODULE_NOTFOUND: + case RLM_MODULE_OK: + case RLM_MODULE_UPDATED: + request->reply->code = ack; + break; + } + } else { + /* + * Start the reply code with the proxy reply + * code. + */ + request->reply->code = request->proxy_reply->code; + } + + /* + * Copy State from the request to the reply. + * See RFC 5176 Section 3.3. + */ + vp = paircopy2(request->packet->vps, PW_STATE, 0); + if (vp) pairadd(&request->reply->vps, vp); - rad_assert(request->proxy_listener == listener); - rad_assert(listener->send == proxy_socket_send); + /* + * We may want to over-ride the reply. + */ + rcode = module_send_coa(0, request); + switch (rcode) { + /* + * We need to send CoA-NAK back if Service-Type + * is Authorize-Only. Rely on the user's policy + * to do that. We're not a real NAS, so this + * restriction doesn't (ahem) apply to us. + */ + case RLM_MODULE_FAIL: + case RLM_MODULE_INVALID: + case RLM_MODULE_REJECT: + case RLM_MODULE_USERLOCK: + default: + /* + * Over-ride an ACK with a NAK + */ + request->reply->code = nak; + break; + + case RLM_MODULE_HANDLED: + return rcode; + + case RLM_MODULE_NOOP: + case RLM_MODULE_NOTFOUND: + case RLM_MODULE_OK: + case RLM_MODULE_UPDATED: + /* + * Do NOT over-ride a previously set value. + * Otherwise an "ok" here will re-write a + * NAK to an ACK. + */ + if (request->reply->code == 0) { + request->reply->code = ack; + } + break; - request->proxy->src_ipaddr = sock->ipaddr; - request->proxy->src_port = sock->port; + } - return rad_send(request->proxy, request->packet, - request->home_server->secret); + return RLM_MODULE_OK; } @@ -415,47 +1510,30 @@ static int proxy_socket_send(rad_listen_t *listener, REQUEST *request) * It takes packets, not requests. It sees if the packet looks * OK. If so, it does a number of sanity checks on it. */ -static int auth_socket_recv(rad_listen_t *listener, +static int coa_socket_recv(rad_listen_t *listener, RAD_REQUEST_FUNP *pfun, REQUEST **prequest) { ssize_t rcode; int code, src_port; RADIUS_PACKET *packet; RAD_REQUEST_FUNP fun = NULL; - char buffer[128]; RADCLIENT *client; fr_ipaddr_t src_ipaddr; rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code); if (rcode < 0) return 0; - RAD_SNMP_TYPE_INC(listener, total_requests); + RAD_STATS_TYPE_INC(listener, total_requests); if (rcode < 20) { /* AUTH_HDR_LEN */ - RAD_SNMP_TYPE_INC(listener, total_malformed_requests); + RAD_STATS_TYPE_INC(listener, total_malformed_requests); return 0; } if ((client = client_listener_find(listener, - &src_ipaddr)) == NULL) { + &src_ipaddr, src_port)) == NULL) { rad_recv_discard(listener->fd); - RAD_SNMP_TYPE_INC(listener, total_invalid_requests); - - if (debug_flag > 0) { - char name[1024]; - - listener->print(listener, name, sizeof(name)); - - /* - * This is debugging rather than logging, so that - * DoS attacks don't affect us. - */ - DEBUG("Ignoring request to %s from unknown client %s port %d", - name, - inet_ntop(src_ipaddr.af, &src_ipaddr.ipaddr, - buffer, sizeof(buffer)), src_port); - } - + RAD_STATS_TYPE_INC(listener, total_invalid_requests); return 0; } @@ -463,28 +1541,14 @@ static int auth_socket_recv(rad_listen_t *listener, * Some sanity checks, based on the packet code. */ switch(code) { - case PW_AUTHENTICATION_REQUEST: - RAD_SNMP_CLIENT_INC(listener, client, requests); - fun = rad_authenticate; - break; - - case PW_STATUS_SERVER: - if (!mainconfig.status_server) { - rad_recv_discard(listener->fd); - RAD_SNMP_TYPE_INC(listener, total_packets_dropped); - RAD_SNMP_CLIENT_INC(listener, client, packets_dropped); - DEBUG("WARNING: Ignoring Status-Server request due to security configuration"); - return 0; - } - fun = rad_status_server; + case PW_COA_REQUEST: + case PW_DISCONNECT_REQUEST: + fun = rad_coa_recv; break; default: rad_recv_discard(listener->fd); - RAD_SNMP_INC(rad_snmp.auth.total_unknown_types); - RAD_SNMP_CLIENT_INC(listener, client, unknown_types); - - DEBUG("Invalid packet code %d sent to authentication port from client %s port %d : IGNORED", + DEBUG("Invalid packet code %d sent to coa port from client %s port %d : IGNORED", code, client->shortname, src_port); return 0; break; @@ -496,14 +1560,12 @@ static int auth_socket_recv(rad_listen_t *listener, */ packet = rad_recv(listener->fd, client->message_authenticator); if (!packet) { - RAD_SNMP_TYPE_INC(listener, total_malformed_requests); - DEBUG("%s", librad_errstr); + RAD_STATS_TYPE_INC(listener, total_malformed_requests); + DEBUG("%s", fr_strerror()); return 0; } if (!received_request(listener, packet, prequest, client)) { - RAD_SNMP_TYPE_INC(listener, total_packets_dropped); - RAD_SNMP_CLIENT_INC(listener, client, packets_dropped); rad_free(&packet); return 0; } @@ -511,126 +1573,105 @@ static int auth_socket_recv(rad_listen_t *listener, *pfun = fun; return 1; } +#endif - +#ifdef WITH_PROXY /* - * Receive packets from an accounting socket + * Recieve packets from a proxy socket. */ -static int acct_socket_recv(rad_listen_t *listener, - RAD_REQUEST_FUNP *pfun, REQUEST **prequest) +static int proxy_socket_recv(rad_listen_t *listener, + RAD_REQUEST_FUNP *pfun, REQUEST **prequest) { - ssize_t rcode; - int code, src_port; + REQUEST *request; RADIUS_PACKET *packet; RAD_REQUEST_FUNP fun = NULL; char buffer[128]; - RADCLIENT *client; - fr_ipaddr_t src_ipaddr; - - rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code); - if (rcode < 0) return 0; - - RAD_SNMP_TYPE_INC(listener, total_requests); - - if (rcode < 20) { /* AUTH_HDR_LEN */ - RAD_SNMP_TYPE_INC(listener, total_malformed_requests); - return 0; - } - - if ((client = client_listener_find(listener, - &src_ipaddr)) == NULL) { - rad_recv_discard(listener->fd); - RAD_SNMP_TYPE_INC(listener, total_invalid_requests); - - /* - * This is debugging rather than logging, so that - * DoS attacks don't affect us. - */ - if (debug_flag > 0) { - char name[1024]; - - listener->print(listener, name, sizeof(name)); - - DEBUG("Ignoring request to %s from unknown client %s port %d", - name, - inet_ntop(src_ipaddr.af, &src_ipaddr.ipaddr, - buffer, sizeof(buffer)), src_port); - } + packet = rad_recv(listener->fd, 0); + if (!packet) { + radlog(L_ERR, "%s", fr_strerror()); return 0; } /* - * Some sanity checks, based on the packet code. + * FIXME: Client MIB updates? */ - switch(code) { - case PW_ACCOUNTING_REQUEST: - RAD_SNMP_CLIENT_INC(listener, client, requests); - fun = rad_accounting; + switch(packet->code) { + case PW_AUTHENTICATION_ACK: + case PW_ACCESS_CHALLENGE: + case PW_AUTHENTICATION_REJECT: + fun = rad_authenticate; break; - case PW_STATUS_SERVER: - if (!mainconfig.status_server) { - rad_recv_discard(listener->fd); - RAD_SNMP_TYPE_INC(listener, total_packets_dropped); - RAD_SNMP_CLIENT_INC(listener, client, unknown_types); +#ifdef WITH_ACCOUNTING + case PW_ACCOUNTING_RESPONSE: + fun = rad_accounting; + break; +#endif - DEBUG("WARNING: Ignoring Status-Server request due to security configuration"); - return 0; - } - fun = rad_status_server; +#ifdef WITH_COA + case PW_DISCONNECT_ACK: + case PW_DISCONNECT_NAK: + case PW_COA_ACK: + case PW_COA_NAK: + fun = rad_coa_reply; break; +#endif default: - rad_recv_discard(listener->fd); - RAD_SNMP_TYPE_INC(listener, total_unknown_types); - RAD_SNMP_CLIENT_INC(listener, client, unknown_types); - - DEBUG("Invalid packet code %d sent to a accounting port from client %s port %d : IGNORED", - code, client->shortname, src_port); + /* + * FIXME: Update MIB for packet types? + */ + radlog(L_ERR, "Invalid packet code %d sent to a proxy port " + "from home server %s port %d - ID %d : IGNORED", + packet->code, + ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)), + packet->src_port, packet->id); + rad_free(&packet); return 0; - } /* switch over packet types */ + } - /* - * Now that we've sanity checked everything, receive the - * packet. - */ - packet = rad_recv(listener->fd, 0); - if (!packet) { - RAD_SNMP_TYPE_INC(listener, total_malformed_requests); - radlog(L_ERR, "%s", librad_errstr); + request = received_proxy_response(packet); + if (!request) { + rad_free(&packet); return 0; } +#ifdef WITH_COA /* - * There can be no duplicate accounting packets. + * Distinguish proxied CoA requests from ones we + * originate. */ - if (!received_request(listener, packet, prequest, client)) { - RAD_SNMP_TYPE_INC(listener, total_packets_dropped); - RAD_SNMP_CLIENT_INC(listener, client, packets_dropped); - rad_free(&packet); - return 0; + if ((fun == rad_coa_reply) && + (request->packet->code == request->proxy->code)) { + fun = rad_coa_recv; } +#endif + rad_assert(fun != NULL); *pfun = fun; + *prequest = request; + return 1; } - +#ifdef WITH_TCP /* * Recieve packets from a proxy socket. */ -static int proxy_socket_recv(rad_listen_t *listener, - RAD_REQUEST_FUNP *pfun, REQUEST **prequest) +static int proxy_socket_tcp_recv(rad_listen_t *listener, + RAD_REQUEST_FUNP *pfun, REQUEST **prequest) { REQUEST *request; RADIUS_PACKET *packet; RAD_REQUEST_FUNP fun = NULL; + listen_socket_t *sock = listener->data; char buffer[128]; - packet = rad_recv(listener->fd, 0); + packet = fr_tcp_recv(listener->fd, 0); if (!packet) { - radlog(L_ERR, "%s", librad_errstr); + listener->status = RAD_LISTEN_STATUS_REMOVE_FD; + event_new_fd(listener); return 0; } @@ -644,9 +1685,11 @@ static int proxy_socket_recv(rad_listen_t *listener, fun = rad_authenticate; break; +#ifdef WITH_ACCOUNTING case PW_ACCOUNTING_RESPONSE: fun = rad_accounting; break; +#endif default: /* @@ -661,17 +1704,33 @@ static int proxy_socket_recv(rad_listen_t *listener, return 0; } + packet->src_ipaddr = sock->other_ipaddr; + packet->src_port = sock->other_port; + packet->dst_ipaddr = sock->my_ipaddr; + packet->dst_port = sock->my_port; + + /* + * FIXME: Have it return an indication of packets that + * are OK to ignore (dups, too late), versus ones that + * aren't OK to ignore (unknown response, spoofed, etc.) + * + * Close the socket on bad packets... + */ request = received_proxy_response(packet); if (!request) { return 0; } rad_assert(fun != NULL); + sock->opened = sock->last_packet = request->timestamp; + *pfun = fun; *prequest = request; return 1; } +#endif +#endif static int client_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request) @@ -698,6 +1757,7 @@ static int client_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request) request->client->secret); } +#ifdef WITH_PROXY static int proxy_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request) { rad_encode(request->proxy, NULL, request->home_server->secret); @@ -709,84 +1769,85 @@ static int proxy_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request) static int proxy_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request) { - if (rad_verify(request->proxy_reply, request->proxy, - request->home_server->secret) < 0) { - return -1; - } + /* + * rad_verify is run in event.c, received_proxy_response() + */ return rad_decode(request->proxy_reply, request->proxy, request->home_server->secret); } +#endif +#include "dhcpd.c" -#ifdef WITH_SNMP -static int radius_snmp_recv(rad_listen_t *listener, - UNUSED RAD_REQUEST_FUNP *pfun, - UNUSED REQUEST **prequest) -{ - if ((rad_snmp.smux_fd >= 0) && - (rad_snmp.smux_event == SMUX_READ)) { - smux_read(); - } - - /* - * If we've got to re-connect, then do so now, - * before calling select again. - */ - if (rad_snmp.smux_event == SMUX_CONNECT) { - smux_connect(); - } +#include "command.c" +static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = { +#ifdef WITH_STATS + { common_socket_parse, NULL, + stats_socket_recv, auth_socket_send, + socket_print, client_socket_encode, client_socket_decode }, +#else /* - * Reset this every time, as the smux connect may have - * opened a new socket. + * This always gets defined. */ - listener->fd = rad_snmp.smux_fd; - - return 0; -} - - -static int radius_snmp_print(UNUSED rad_listen_t *this, char *buffer, size_t bufsize) -{ - return snprintf(buffer, bufsize, "SMUX with OID .1.3.6.1.4.1.11344.1.1.1"); -} - -#endif - -static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = { { NULL, NULL, NULL, NULL, NULL, NULL, NULL}, /* RAD_LISTEN_NONE */ +#endif +#ifdef WITH_PROXY /* proxying */ - { NULL, NULL, + { common_socket_parse, NULL, proxy_socket_recv, proxy_socket_send, socket_print, proxy_socket_encode, proxy_socket_decode }, +#endif /* authentication */ { common_socket_parse, NULL, auth_socket_recv, auth_socket_send, socket_print, client_socket_encode, client_socket_decode }, +#ifdef WITH_ACCOUNTING /* accounting */ { common_socket_parse, NULL, acct_socket_recv, acct_socket_send, socket_print, client_socket_encode, client_socket_decode}, +#endif +#ifdef WITH_DETAIL /* detail */ { detail_parse, detail_free, detail_recv, detail_send, detail_print, detail_encode, detail_decode }, +#endif #ifdef WITH_VMPS /* vlan query protocol */ { common_socket_parse, NULL, vqp_socket_recv, vqp_socket_send, socket_print, vqp_socket_encode, vqp_socket_decode }, -#else - { NULL, NULL, NULL, NULL, NULL, NULL, NULL}, #endif - { NULL, NULL, NULL, NULL, NULL, NULL, NULL} /* RAD_LISTEN_SNMP */ +#ifdef WITH_DHCP + /* dhcp query protocol */ + { dhcp_socket_parse, NULL, + dhcp_socket_recv, dhcp_socket_send, + socket_print, dhcp_socket_encode, dhcp_socket_decode }, +#endif + +#ifdef WITH_COMMAND_SOCKET + /* TCP command socket */ + { command_socket_parse, command_socket_free, + command_domain_accept, command_domain_send, + command_socket_print, command_socket_encode, command_socket_decode }, +#endif + +#ifdef WITH_COA + /* Change of Authorization */ + { common_socket_parse, NULL, + coa_socket_recv, auth_socket_send, /* CoA packets are same as auth */ + socket_print, client_socket_encode, client_socket_decode }, +#endif + }; @@ -796,134 +1857,324 @@ static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = { */ static int listen_bind(rad_listen_t *this) { - rad_listen_t **last; + int rcode; + struct sockaddr_storage salocal; + socklen_t salen; listen_socket_t *sock = this->data; +#ifndef WITH_TCP +#define proto_for_port "udp" +#define sock_type SOCK_DGRAM +#else + const char *proto_for_port = "udp"; + int sock_type = SOCK_DGRAM; + + if (sock->proto == IPPROTO_TCP) { +#ifdef WITH_VMPS + if (this->type == RAD_LISTEN_VQP) { + radlog(L_ERR, "VQP does not support TCP transport"); + return -1; + } +#endif + + proto_for_port = "tcp"; + sock_type = SOCK_STREAM; + } +#endif /* * If the port is zero, then it means the appropriate * thing from /etc/services. */ - if (sock->port == 0) { + if (sock->my_port == 0) { struct servent *svp; switch (this->type) { case RAD_LISTEN_AUTH: - svp = getservbyname ("radius", "udp"); + svp = getservbyname ("radius", proto_for_port); if (svp != NULL) { - sock->port = ntohs(svp->s_port); + sock->my_port = ntohs(svp->s_port); } else { - sock->port = PW_AUTH_UDP_PORT; + sock->my_port = PW_AUTH_UDP_PORT; } break; +#ifdef WITH_ACCOUNTING case RAD_LISTEN_ACCT: - svp = getservbyname ("radacct", "udp"); + svp = getservbyname ("radacct", proto_for_port); if (svp != NULL) { - sock->port = ntohs(svp->s_port); + sock->my_port = ntohs(svp->s_port); } else { - sock->port = PW_ACCT_UDP_PORT; + sock->my_port = PW_ACCT_UDP_PORT; } break; +#endif + +#ifdef WITH_PROXY + case RAD_LISTEN_PROXY: + /* leave it at zero */ + break; +#endif #ifdef WITH_VMPS case RAD_LISTEN_VQP: - sock->port = 1589; + sock->my_port = 1589; + break; +#endif + +#ifdef WITH_COA + case RAD_LISTEN_COA: + svp = getservbyname ("radius-dynauth", "udp"); + if (svp != NULL) { + sock->my_port = ntohs(svp->s_port); + } else { + sock->my_port = PW_COA_UDP_PORT; + } break; #endif default: - radlog(L_ERR, "ERROR: Non-fatal internal sanity check failed in bind."); + DEBUG("WARNING: Internal sanity check failed in binding to socket. Ignoring problem."); + return -1; + } + } + + /* + * Copy fr_socket() here, as we may need to bind to a device. + */ + this->fd = socket(sock->my_ipaddr.af, sock_type, 0); + if (this->fd < 0) { + radlog(L_ERR, "Failed opening socket: %s", strerror(errno)); + return -1; + } + + /* + * Bind to a device BEFORE touching IP addresses. + */ + if (sock->interface) { +#ifdef SO_BINDTODEVICE + struct ifreq ifreq; + strcpy(ifreq.ifr_name, sock->interface); + + fr_suid_up(); + rcode = setsockopt(this->fd, SOL_SOCKET, SO_BINDTODEVICE, + (char *)&ifreq, sizeof(ifreq)); + fr_suid_down(); + if (rcode < 0) { + close(this->fd); + radlog(L_ERR, "Failed binding to interface %s: %s", + sock->interface, strerror(errno)); + return -1; + } /* else it worked. */ +#else +#ifdef HAVE_STRUCT_SOCKADDR_IN6 +#ifdef HAVE_NET_IF_H + /* + * Odds are that any system supporting "bind to + * device" also supports IPv6, so this next bit + * isn't necessary. But it's here for + * completeness. + * + * If we're doing IPv6, and the scope hasn't yet + * been defined, set the scope to the scope of + * the interface. + */ + if (sock->my_ipaddr.af == AF_INET6) { + if (sock->my_ipaddr.scope == 0) { + sock->my_ipaddr.scope = if_nametoindex(sock->interface); + if (sock->my_ipaddr.scope == 0) { + close(this->fd); + radlog(L_ERR, "Failed finding interface %s: %s", + sock->interface, strerror(errno)); + return -1; + } + } /* else scope was defined: we're OK. */ + } else +#endif +#endif + /* + * IPv4: no link local addresses, + * and no bind to device. + */ + { + close(this->fd); + radlog(L_ERR, "Failed binding to interface %s: \"bind to device\" is unsupported", sock->interface); + return -1; + } +#endif + } + +#ifdef WITH_TCP + if (sock->proto == IPPROTO_TCP) { + int on = 1; + + if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { + close(this->fd); + radlog(L_ERR, "Failed to reuse address: %s", strerror(errno)); return -1; } } +#endif + +#if defined(WITH_TCP) && defined(WITH_UDPFROMTO) + else /* UDP sockets get UDPfromto */ +#endif + +#ifdef WITH_UDPFROMTO + /* + * Initialize udpfromto for all sockets. + */ + if (udpfromto_init(this->fd) != 0) { + close(this->fd); + return -1; + } +#endif /* - * Find it in the old list, AFTER updating the port. If - * it's there, use that, rather than creating a new - * socket. This allows HUP's to re-use the old sockets, - * which means that packets waiting in the socket queue - * don't get lost. + * Set up sockaddr stuff. */ - for (last = &mainconfig.listen; - *last != NULL; - last = &((*last)->next)) { - listen_socket_t *other; - - if (this->type != (*last)->type) continue; - - if ((this->type == RAD_LISTEN_DETAIL) || - (this->type == RAD_LISTEN_SNMP)) continue; + if (!fr_ipaddr2sockaddr(&sock->my_ipaddr, sock->my_port, &salocal, &salen)) { + close(this->fd); + return -1; + } + +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + if (sock->my_ipaddr.af == AF_INET6) { + /* + * Listening on '::' does NOT get you IPv4 to + * IPv6 mapping. You've got to listen on an IPv4 + * address, too. This makes the rest of the server + * design a little simpler. + */ +#ifdef IPV6_V6ONLY + + if (IN6_IS_ADDR_UNSPECIFIED(&sock->my_ipaddr.ipaddr.ip6addr)) { + int on = 1; + + setsockopt(this->fd, IPPROTO_IPV6, IPV6_V6ONLY, + (char *)&on, sizeof(on)); + } +#endif /* IPV6_V6ONLY */ + } +#endif /* HAVE_STRUCT_SOCKADDR_IN6 */ - other = (listen_socket_t *)((*last)->data); + if (sock->my_ipaddr.af == AF_INET) { + UNUSED int flag; + +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) + /* + * Disable PMTU discovery. On Linux, this + * also makes sure that the "don't fragment" + * flag is zero. + */ + flag = IP_PMTUDISC_DONT; + setsockopt(this->fd, IPPROTO_IP, IP_MTU_DISCOVER, + &flag, sizeof(flag)); +#endif - if ((sock->port == other->port) && - (sock->ipaddr.af == other->ipaddr.af) && - (fr_ipaddr_cmp(&sock->ipaddr, &other->ipaddr) == 0)) { - this->fd = (*last)->fd; - (*last)->fd = -1; - return 0; - } +#if defined(IP_DONTFRAG) + /* + * Ensure that the "don't fragment" flag is zero. + */ + flag = 0; + setsockopt(this->fd, IPPROTO_IP, IP_DONTFRAG, + &flag, sizeof(flag)); +#endif } - this->fd = fr_socket(&sock->ipaddr, sock->port); - if (this->fd < 0) { - radlog(L_ERR, "ERROR: Failed to open socket: %s", - librad_errstr); - return -1; +#ifdef WITH_DHCP +#ifdef SO_BROADCAST + if (sock->broadcast) { + int on = 1; + + if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { + radlog(L_ERR, "Can't set broadcast option: %s\n", + strerror(errno)); + return -1; + } } +#endif +#endif /* - * FreeBSD jail issues. We bind to 0.0.0.0, but the - * kernel instead binds us to a 1.2.3.4. If this - * happens, notice, and remember our real IP. + * May be binding to priviledged ports. */ - { - struct sockaddr_storage src; - socklen_t sizeof_src = sizeof(src); + if (sock->my_port != 0) { +#ifdef SO_REUSEADDR + int on = 1; - memset(&src, 0, sizeof_src); - if (getsockname(this->fd, (struct sockaddr *) &src, - &sizeof_src) < 0) { + if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { + radlog(L_ERR, "Can't set re-use address option: %s\n", + strerror(errno)); return -1; } +#endif - if (src.ss_family == AF_INET) { - struct sockaddr_in *s4; + fr_suid_up(); + rcode = bind(this->fd, (struct sockaddr *) &salocal, salen); + fr_suid_down(); + if (rcode < 0) { + char buffer[256]; + close(this->fd); - s4 = (struct sockaddr_in *)&src; - sock->ipaddr.ipaddr.ip4addr = s4->sin_addr; + this->print(this, buffer, sizeof(buffer)); + radlog(L_ERR, "Failed binding to %s: %s\n", + buffer, strerror(errno)); + return -1; + } + + /* + * FreeBSD jail issues. We bind to 0.0.0.0, but the + * kernel instead binds us to a 1.2.3.4. If this + * happens, notice, and remember our real IP. + */ + { + struct sockaddr_storage src; + socklen_t sizeof_src = sizeof(src); -#ifdef HAVE_STRUCT_SOCKADDR_IN6 - } else if (src.ss_family == AF_INET6) { - struct sockaddr_in6 *s6; + memset(&src, 0, sizeof_src); + if (getsockname(this->fd, (struct sockaddr *) &src, + &sizeof_src) < 0) { + radlog(L_ERR, "Failed getting socket name: %s", + strerror(errno)); + return -1; + } - s6 = (struct sockaddr_in6 *)&src; - sock->ipaddr.ipaddr.ip6addr = s6->sin6_addr; -#endif - } else { - return -1; + if (!fr_sockaddr2ipaddr(&src, sizeof_src, + &sock->my_ipaddr, &sock->my_port)) { + radlog(L_ERR, "Socket has unsupported address family"); + return -1; + } } } -#ifdef O_NONBLOCK - { - int flags; - - if ((flags = fcntl(this->fd, F_GETFL, NULL)) < 0) { - radlog(L_ERR, "Failure getting socket flags: %s)\n", - strerror(errno)); - return -1; - } - - flags |= O_NONBLOCK; - if( fcntl(this->fd, F_SETFL, flags) < 0) { - radlog(L_ERR, "Failure setting socket flags: %s)\n", - strerror(errno)); +#ifdef WITH_TCP + if (sock->proto == IPPROTO_TCP) { + if (listen(this->fd, 8) < 0) { + close(this->fd); + radlog(L_ERR, "Failed in listen(): %s", strerror(errno)); return -1; } - } + } else #endif + if (fr_nonblock(this->fd) < 0) { + close(this->fd); + radlog(L_ERR, "Failed setting non-blocking on socket: %s", + strerror(errno)); + return -1; + } + + /* + * Mostly for proxy sockets. + */ + sock->other_ipaddr.af = sock->my_ipaddr.af; + +/* + * Don't screw up other people. + */ +#undef proto_for_port +#undef sock_type + return 0; } @@ -946,17 +2197,45 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type) this->decode = master_listen[this->type].decode; switch (type) { +#ifdef WITH_STATS + case RAD_LISTEN_NONE: +#endif case RAD_LISTEN_AUTH: +#ifdef WITH_ACCOUNTING case RAD_LISTEN_ACCT: +#endif +#ifdef WITH_PROXY case RAD_LISTEN_PROXY: +#endif +#ifdef WITH_VMPS case RAD_LISTEN_VQP: +#endif +#ifdef WITH_COA + case RAD_LISTEN_COA: +#endif this->data = rad_malloc(sizeof(listen_socket_t)); memset(this->data, 0, sizeof(listen_socket_t)); break; +#ifdef WITH_DHCP + case RAD_LISTEN_DHCP: + this->data = rad_malloc(sizeof(dhcp_socket_t)); + memset(this->data, 0, sizeof(dhcp_socket_t)); + break; +#endif + +#ifdef WITH_DETAIL case RAD_LISTEN_DETAIL: this->data = NULL; break; +#endif + +#ifdef WITH_COMMAND_SOCKET + case RAD_LISTEN_COMMAND: + this->data = rad_malloc(sizeof(fr_command_socket_t)); + memset(this->data, 0, sizeof(fr_command_socket_t)); + break; +#endif default: rad_assert("Unsupported option!" == NULL); @@ -966,84 +2245,126 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type) return this; } - +#ifdef WITH_PROXY /* * Externally visible function for creating a new proxy LISTENER. * - * For now, don't take ipaddr or port. - * * Not thread-safe, but all calls to it are protected by the - * proxy mutex in request_list.c + * proxy mutex in event.c */ -rad_listen_t *proxy_new_listener() +int proxy_new_listener(home_server *home, int src_port) { - int last_proxy_port, port; - rad_listen_t *this, *tmp, **last; - listen_socket_t *sock, *old; + rad_listen_t *this; + listen_socket_t *sock; + + if (!home) return 0; + + if ((home->max_connections > 0) && + (home->num_connections >= home->max_connections)) { + DEBUG("WARNING: Home server has too many open connections (%d)", + home->max_connections); + return 0; + } this = listen_alloc(RAD_LISTEN_PROXY); - /* - * Find an existing proxy socket to copy. - * - * FIXME: Make it per-realm, or per-home server! - */ - last_proxy_port = 0; - old = NULL; - last = &mainconfig.listen; - for (tmp = mainconfig.listen; tmp != NULL; tmp = tmp->next) { - if (tmp->type == RAD_LISTEN_PROXY) { - sock = tmp->data; - if (sock->port > last_proxy_port) { - last_proxy_port = sock->port + 1; - } - if (!old) old = sock; - } + sock = this->data; + sock->other_ipaddr = home->ipaddr; + sock->other_port = home->port; + sock->home = home; - last = &(tmp->next); - } + sock->my_ipaddr = home->src_ipaddr; + sock->my_port = src_port; + sock->proto = home->proto; + +#ifdef WITH_TCP + sock->last_packet = time(NULL); + + if (home->proto == IPPROTO_TCP) { + this->recv = proxy_socket_tcp_recv; + + /* + * FIXME: connect() is blocking! + * We do this with the proxy mutex locked, which may + * cause large delays! + * + * http://www.developerweb.net/forum/showthread.php?p=13486 + */ + this->fd = fr_tcp_client_socket(&home->src_ipaddr, + &home->ipaddr, home->port); + } else +#endif + this->fd = fr_socket(&home->src_ipaddr, src_port); - if (!old) { + if (this->fd < 0) { + DEBUG("Failed opening client socket: %s", fr_strerror()); listen_free(&this); - return NULL; /* This is a serious error. */ + return 0; } /* - * FIXME: find a new IP address to listen on? - * - * This could likely be done in the "home server" - * configuration, to have per-home-server source IP's. + * Figure out which port we were bound to. */ - sock = this->data; - memcpy(&sock->ipaddr, &old->ipaddr, sizeof(sock->ipaddr)); + if (sock->my_port == 0) { + struct sockaddr_storage src; + socklen_t sizeof_src = sizeof(src); + + memset(&src, 0, sizeof_src); + if (getsockname(this->fd, (struct sockaddr *) &src, + &sizeof_src) < 0) { + radlog(L_ERR, "Failed getting socket name: %s", + strerror(errno)); + listen_free(&this); + return 0; + } + + if (!fr_sockaddr2ipaddr(&src, sizeof_src, + &sock->my_ipaddr, &sock->my_port)) { + radlog(L_ERR, "Socket has unsupported address family"); + listen_free(&this); + return 0; + } + } /* - * Keep going until we find an unused port. + * Tell the event loop that we have a new FD */ - for (port = last_proxy_port; port < 64000; port++) { - sock->port = port; - if (listen_bind(this) == 0) { - /* - * Add the new listener to the list of - * listeners. - */ - *last = this; - return this; - } + if (!event_new_fd(this)) { + listen_free(&this); + return 0; } - - listen_free(&this); - return NULL; + + return 1; } +#endif static const FR_NAME_NUMBER listen_compare[] = { +#ifdef WITH_STATS + { "status", RAD_LISTEN_NONE }, +#endif { "auth", RAD_LISTEN_AUTH }, +#ifdef WITH_ACCOUNTING { "acct", RAD_LISTEN_ACCT }, +#endif +#ifdef WITH_DETAIL { "detail", RAD_LISTEN_DETAIL }, +#endif +#ifdef WITH_PROXY + { "proxy", RAD_LISTEN_PROXY }, +#endif #ifdef WITH_VMPS { "vmps", RAD_LISTEN_VQP }, #endif +#ifdef WITH_DHCP + { "dhcp", RAD_LISTEN_DHCP }, +#endif +#ifdef WITH_COMMAND_SOCKET + { "control", RAD_LISTEN_COMMAND }, +#endif +#ifdef WITH_COA + { "coa", RAD_LISTEN_COA }, +#endif { NULL, 0 }, }; @@ -1068,9 +2389,8 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) return NULL; } - type = fr_str2int(listen_compare, listen_type, - RAD_LISTEN_NONE); - if (type == RAD_LISTEN_NONE) { + type = fr_str2int(listen_compare, listen_type, -1); + if (type < 0) { cf_log_err(cf_sectiontoitem(cs), "Invalid type \"%s\" in listen section.", listen_type); @@ -1078,7 +2398,7 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) return NULL; } free(listen_type); - + /* * Allow listen sections in the default config to * refer to a server. @@ -1093,6 +2413,18 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) if (rcode < 0) return NULL; } +#ifdef WITH_PROXY + /* + * We were passed a virtual server, so the caller is + * defining a proxy listener inside of a virtual server. + * This isn't allowed right now. + */ + else if (type == RAD_LISTEN_PROXY) { + radlog(L_ERR, "Error: listen type \"proxy\" Cannot appear in a virtual server section"); + return NULL; + } +#endif + /* * Set up cross-type data. */ @@ -1113,6 +2445,28 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) return this; } +#ifdef WITH_PROXY +static int is_loopback(const fr_ipaddr_t *ipaddr) +{ + /* + * We shouldn't proxy on loopback. + */ + if ((ipaddr->af == AF_INET) && + (ipaddr->ipaddr.ip4addr.s_addr == htonl(INADDR_LOOPBACK))) { + return 1; + } + +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + if ((ipaddr->af == AF_INET6) && + (IN6_IS_ADDR_LINKLOCAL(&ipaddr->ipaddr.ip6addr))) { + return 1; + } +#endif + + return 0; +} +#endif + /* * Generate a list of listeners. Takes an input list of * listeners, too, so we don't close sockets with waiting packets. @@ -1121,11 +2475,14 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) { int override = FALSE; int rcode; - CONF_SECTION *cs; + CONF_SECTION *cs = NULL; rad_listen_t **last; rad_listen_t *this; fr_ipaddr_t server_ipaddr; int auth_port = 0; +#ifdef WITH_PROXY + int defined_proxy = 0; +#endif /* * We shouldn't be called with a pre-existing list. @@ -1167,7 +2524,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) listen_socket_t *sock; server_ipaddr.af = AF_INET; - radlog(L_INFO, "WARNING: The directive 'bind_adress' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'."); + radlog(L_INFO, "WARNING: The directive 'bind_address' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'."); bind_it: #ifdef WITH_VMPS @@ -1180,23 +2537,24 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) sock = this->data; - sock->ipaddr = server_ipaddr; - sock->port = auth_port; + sock->my_ipaddr = server_ipaddr; + sock->my_port = auth_port; sock->clients = clients_parse_section(config); if (!sock->clients) { cf_log_err(cf_sectiontoitem(config), "Failed to find any clients for this listen section"); + listen_free(&this); return -1; } if (listen_bind(this) < 0) { - listen_free(&this); listen_free(head); - radlog(L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->port); + radlog(L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->my_port); + listen_free(&this); return -1; } - auth_port = sock->port; /* may have been updated in listen_bind */ + auth_port = sock->my_port; /* may have been updated in listen_bind */ if (override) { cs = cf_section_sub_find_name2(config, "server", mainconfig.name); @@ -1210,9 +2568,10 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) /* * No acct for vmpsd */ - if (strcmp(progname, "vmpsd") == 0) goto do_proxy; + if (strcmp(progname, "vmpsd") == 0) goto add_sockets; #endif +#ifdef WITH_ACCOUNTING /* * Open Accounting Socket. * @@ -1228,8 +2587,8 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) * The accounting port is always the * authentication port + 1 */ - sock->ipaddr = server_ipaddr; - sock->port = auth_port + 1; + sock->my_ipaddr = server_ipaddr; + sock->my_port = auth_port + 1; sock->clients = clients_parse_section(config); if (!sock->clients) { @@ -1241,7 +2600,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) if (listen_bind(this) < 0) { listen_free(&this); listen_free(head); - radlog(L_ERR, "There appears to be another RADIUS server running on the accounting port %d", sock->port); + radlog(L_ERR, "There appears to be another RADIUS server running on the accounting port %d", sock->my_port); return -1; } @@ -1253,7 +2612,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) *last = this; last = &(this->next); - +#endif } else if (mainconfig.port > 0) { /* no bind address, but a port */ radlog(L_ERR, "The command-line says \"-p %d\", but there is no associated IP address to use", mainconfig.port); @@ -1270,7 +2629,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) cs = cf_section_sub_find_name2(config, "server", mainconfig.name); - if (!cs) goto do_proxy; + if (!cs) goto add_sockets; /* * Should really abstract this code... @@ -1283,12 +2642,12 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) listen_free(head); return -1; } - + *last = this; last = &(this->next); } /* loop over "listen" directives in server */ - goto do_proxy; + goto add_sockets; } /* @@ -1332,47 +2691,48 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) } /* loop over "listen" directives in virtual servers */ } /* loop over virtual servers */ +add_sockets: + /* + * No sockets to receive packets, this is an error. + * proxying is pointless. + */ + if (!*head) { + radlog(L_ERR, "The server is not configured to listen on any ports. Cannot start."); + return -1; + } + + /* + * Print out which sockets we're listening on, and + * add them to the event list. + */ + for (this = *head; this != NULL; this = this->next) { +#ifdef WITH_PROXY + if (this->type == RAD_LISTEN_PROXY) { + defined_proxy = 1; + } + +#endif + event_new_fd(this); + } + /* * If we're proxying requests, open the proxy FD. * Otherwise, don't do anything. */ - do_proxy: - if (mainconfig.proxy_requests == TRUE) { - int port = -1; +#ifdef WITH_PROXY + if ((mainconfig.proxy_requests == TRUE) && + (*head != NULL) && !defined_proxy) { listen_socket_t *sock = NULL; + int port = 0; + home_server home; - /* - * No sockets to receive packets, therefore - * proxying is pointless. - */ - if (!*head) return -1; + memset(&home, 0, sizeof(home)); /* - * If we previously had proxy sockets, copy them - * to the new config. + * */ - if (mainconfig.listen != NULL) { - rad_listen_t *old, *next, **tail; - - tail = &mainconfig.listen; - for (old = mainconfig.listen; - old != NULL; - old = next) { - next = old->next; - - if (old->type != RAD_LISTEN_PROXY) { - tail = &((*tail)->next); - continue; - } - - *last = old; - *tail = next; - old->next = NULL; - last = &(old->next); - } - - goto do_snmp; - } + home.proto = IPPROTO_UDP; + home.src_ipaddr = server_ipaddr; /* * Find the first authentication port, @@ -1381,87 +2741,53 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) for (this = *head; this != NULL; this = this->next) { if (this->type == RAD_LISTEN_AUTH) { sock = this->data; - if (server_ipaddr.af == AF_UNSPEC) { - server_ipaddr = sock->ipaddr; + + if (is_loopback(&sock->my_ipaddr)) continue; + + if (home.src_ipaddr.af == AF_UNSPEC) { + home.src_ipaddr = sock->my_ipaddr; } - port = sock->port + 2; /* skip acct port */ + port = sock->my_port + 2; break; } - if (this->type == RAD_LISTEN_VQP) { +#ifdef WITH_ACCT + if (this->type == RAD_LISTEN_ACCT) { sock = this->data; - if (server_ipaddr.af == AF_UNSPEC) { - server_ipaddr = sock->ipaddr; + + if (is_loopback(&sock->my_ipaddr)) continue; + + if (home.src_ipaddr.af == AF_UNSPEC) { + home.src_ipaddr = sock->my_ipaddr; } - port = sock->port + 1; + port = sock->my_port + 1; break; } +#endif } - if (port < 0) port = 1024 + (fr_rand() & 0x1ff); - /* * Address is still unspecified, use IPv4. */ - if (server_ipaddr.af == AF_UNSPEC) { - server_ipaddr.af = AF_INET; - server_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_ANY); + if (home.src_ipaddr.af == AF_UNSPEC) { + home.src_ipaddr.af = AF_INET; + /* everything else is already set to zero */ } - this = listen_alloc(RAD_LISTEN_PROXY); - sock = this->data; - - /* - * Create the first proxy socket. - */ - sock->ipaddr = server_ipaddr; - - /* - * Try to find a proxy port (value doesn't matter) - */ - for (sock->port = port; - sock->port < 64000; - sock->port++) { - if (listen_bind(this) == 0) { - *last = this; - last = &(this->next); /* just in case */ - break; - } - } + home.ipaddr.af = home.src_ipaddr.af; + /* everything else is already set to zero */ - if (sock->port >= 64000) { + if (!proxy_new_listener(&home, port)) { listen_free(head); - listen_free(&this); - radlog(L_ERR, "Failed to open socket for proxying"); return -1; } } +#endif - do_snmp: -#ifdef WITH_SNMP - if (radius_snmp_init(config)) { - /* - * Forget about the old one. - */ - for (this = mainconfig.listen; - this != NULL; - this = this->next) { - if (this->type != RAD_LISTEN_SNMP) continue; - this->fd = -1; - } - - this = rad_malloc(sizeof(*this)); - memset(this, 0, sizeof(*this)); - - this->type = RAD_LISTEN_SNMP; - this->fd = rad_snmp.smux_fd; - - this->recv = radius_snmp_recv; - this->print = radius_snmp_print; + /* + * Haven't defined any sockets. Die. + */ + if (!*head) return -1; - *last = this; - last = &(this->next); - } -#endif return 0; } @@ -1487,6 +2813,21 @@ void listen_free(rad_listen_t **head) if (master_listen[this->type].free) { master_listen[this->type].free(this); } + +#ifdef WITH_TCP + if ((this->type == RAD_LISTEN_AUTH) +#ifdef WITH_ACCT + || (this->type == RAD_LISTEN_ACCT) +#endif +#ifdef WITH_PROXY + || (this->type == RAD_LISTEN_PROXY) +#endif + ) { + listen_socket_t *sock = this->data; + rad_free(&sock->packet); + } +#endif + free(this->data); free(this); @@ -1495,3 +2836,63 @@ void listen_free(rad_listen_t **head) *head = NULL; } + +#ifdef WITH_STATS +RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr, + int port) +{ + rad_listen_t *this; + + for (this = mainconfig.listen; this != NULL; this = this->next) { + listen_socket_t *sock; + + if ((this->type != RAD_LISTEN_AUTH) +#ifdef WITH_ACCOUNTING + && (this->type != RAD_LISTEN_ACCT) +#endif + ) continue; + + sock = this->data; + + if ((sock->my_port == port) && + (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) == 0)) { + return sock->clients; + } + } + + return NULL; +} +#endif + +rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port) +{ + rad_listen_t *this; + + for (this = mainconfig.listen; this != NULL; this = this->next) { + listen_socket_t *sock; + + /* + * FIXME: For TCP, ignore the *secondary* + * listeners associated with the main socket. + */ + if ((this->type != RAD_LISTEN_AUTH) +#ifdef WITH_ACCOUNTING + && (this->type != RAD_LISTEN_ACCT) +#endif + ) continue; + + sock = this->data; + + if ((sock->my_port == port) && + (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) == 0)) { + return this; + } + + if ((sock->my_port == port) && + fr_inaddr_any(&sock->my_ipaddr)) { + return this; + } + } + + return NULL; +}