X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmain%2Flisten.c;h=57c96070d7aaf445815a09e611c6a8d78a84649a;hb=bca85674a83f94b9cfae0ebdc129bbaf645c8f7d;hp=4ecb5f41290a9432df3836e37942f04266b7185a;hpb=4c886e11f1e92f4d7b6f43d81b5ca0dcf57719da;p=freeradius.git diff --git a/src/main/listen.c b/src/main/listen.c index 4ecb5f4..57c9607 100644 --- a/src/main/listen.c +++ b/src/main/listen.c @@ -84,7 +84,7 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type); /* * Find a per-socket client. */ -RADCLIENT *client_listener_find(const rad_listen_t *listener, +RADCLIENT *client_listener_find(rad_listen_t *listener, const fr_ipaddr_t *ipaddr, int src_port) { #ifdef WITH_DYNAMIC_CLIENTS @@ -201,7 +201,8 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener, * can be defined. */ rad_assert(client->dynamic == 0); - } else { + + } else if (!client->dynamic && client->rate_limit) { /* * The IP is unknown, so we've found an enclosing * network. Enable DoS protection. We only @@ -293,7 +294,7 @@ static int rad_status_server(REQUEST *request) 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 { @@ -320,7 +321,7 @@ static int rad_status_server(REQUEST *request) #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 { @@ -347,7 +348,7 @@ static int rad_status_server(REQUEST *request) * the WG. We like it, so it goes in here. */ case RAD_LISTEN_COA: - dval = dict_valbyname(PW_RECV_COA_TYPE, "Status-Server"); + dval = dict_valbyname(PW_RECV_COA_TYPE, 0, "Status-Server"); if (dval) { rcode = module_recv_coa(dval->value, request); } else { @@ -404,13 +405,14 @@ static int auth_tcp_recv(rad_listen_t *listener, 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; - sock->packet = NULL; rcode = fr_tcp_read_packet(packet, 0); @@ -419,7 +421,6 @@ static int auth_tcp_recv(rad_listen_t *listener, * so that we'll read more data when it's ready. */ if (rcode == 0) { - sock->packet = packet; return 0; } @@ -432,8 +433,6 @@ static int auth_tcp_recv(rad_listen_t *listener, } if (rcode < 0) { /* error or connection reset */ - rad_free(&packet); - listener->status = RAD_LISTEN_STATUS_REMOVE_FD; /* @@ -459,7 +458,6 @@ static int auth_tcp_recv(rad_listen_t *listener, * * It is instead free'd in remove_from_request_hash() */ - return 0; } @@ -476,37 +474,34 @@ static int auth_tcp_recv(rad_listen_t *listener, case PW_STATUS_SERVER: if (!mainconfig.status_server) { - rad_free(&packet); 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_free(&packet); 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; - break; } /* 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(&packet); + rad_free(&sock->packet); return 0; } - packet->dst_ipaddr = sock->my_ipaddr; - packet->dst_port = sock->my_port; - *pfun = fun; + sock->packet = NULL; /* we have no need for more partial reads */ return 1; } @@ -625,7 +620,7 @@ static int auth_tcp_accept(rad_listen_t *listener, /* * This function is stupid and complicated. */ -static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize) +static int socket_print(const rad_listen_t *this, char *buffer, size_t bufsize) { size_t len; listen_socket_t *sock = this->data; @@ -682,12 +677,10 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize) ADDSTRING(name); -#ifdef SO_BINDTODEVICE if (sock->interface) { ADDSTRING(" interface "); ADDSTRING(sock->interface); } -#endif #ifdef WITH_TCP if (this->recv == auth_tcp_accept) { @@ -732,6 +725,7 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize) return 1; } +#ifdef WITH_PROXY /* * Maybe it's a socket that we opened to a home server. */ @@ -762,7 +756,8 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize) return 1; } -#endif +#endif /* WITH_PROXY */ +#endif /* WITH_TCP */ ADDSTRING(" address "); @@ -789,6 +784,8 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize) return 1; } +extern int check_config; /* radiusd.c */ + /* * Parse an authentication or accounting socket. @@ -873,28 +870,40 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) * 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 +#endif /* WITH_PROXY */ +#endif /* WITH_TCP */ } sock->my_ipaddr = ipaddr; sock->my_port = listen_port; +#ifdef WITH_PROXY + 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 */ + } +#endif + /* * If we can bind to interfaces, do so, * 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"); @@ -906,8 +915,42 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) return -1; } sock->interface = 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 (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), + "No broadcast value given"); + return -1; + } + + /* + * Hack... whatever happened to cf_section_parse? + */ + sock->broadcast = (strcmp(value, "yes") == 0); #endif } +#endif /* * And bind it to the port. @@ -1308,9 +1351,9 @@ static int rad_coa_reply(REQUEST *request) /* * Inform the user about RFC requirements. */ - s1 = pairfind(request->proxy->vps, PW_STATE); + s1 = pairfind(request->proxy->vps, PW_STATE, 0); if (s1) { - s2 = pairfind(request->proxy_reply->vps, PW_STATE); + 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."); @@ -1364,10 +1407,10 @@ static int rad_coa_recv(REQUEST *request) * with Service-Type = Authorize-Only, it MUST * have a State attribute in it. */ - vp = pairfind(request->packet->vps, PW_SERVICE_TYPE); + 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); + 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; @@ -1415,7 +1458,7 @@ static int rad_coa_recv(REQUEST *request) * Copy State from the request to the reply. * See RFC 5176 Section 3.3. */ - vp = paircopy2(request->packet->vps, PW_STATE); + vp = paircopy2(request->packet->vps, PW_STATE, 0); if (vp) pairadd(&request->reply->vps, vp); /* @@ -1795,7 +1838,7 @@ static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = { #ifdef WITH_COMMAND_SOCKET /* TCP command socket */ - { command_socket_parse, NULL, + { command_socket_parse, command_socket_free, command_domain_accept, command_domain_send, command_socket_print, command_socket_encode, command_socket_decode }, #endif @@ -1882,12 +1925,17 @@ static int listen_bind(rad_listen_t *this) #ifdef WITH_COA case RAD_LISTEN_COA: - sock->my_port = PW_COA_UDP_PORT; + 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; } } @@ -1901,11 +1949,11 @@ static int listen_bind(rad_listen_t *this) return -1; } -#ifdef SO_BINDTODEVICE /* * Bind to a device BEFORE touching IP addresses. */ if (sock->interface) { +#ifdef SO_BINDTODEVICE struct ifreq ifreq; strcpy(ifreq.ifr_name, sock->interface); @@ -1919,8 +1967,43 @@ static int listen_bind(rad_listen_t *this) 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) { @@ -2000,10 +2083,34 @@ static int listen_bind(rad_listen_t *this) #endif } +#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 + /* * May be binding to priviledged ports. */ if (sock->my_port != 0) { +#ifdef SO_REUSEADDR + int on = 1; + + 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 + fr_suid_up(); rcode = bind(this->fd, (struct sockaddr *) &salocal, salen); fr_suid_down(); @@ -2224,7 +2331,10 @@ int proxy_new_listener(home_server *home, int src_port) /* * Tell the event loop that we have a new FD */ - event_new_fd(this); + if (!event_new_fd(this)) { + listen_free(&this); + return 0; + } return 1; } @@ -2311,7 +2421,7 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server) * defining a proxy listener inside of a virtual server. * This isn't allowed right now. */ - else if (this->type == RAD_LISTEN_PROXY) { + else if (type == RAD_LISTEN_PROXY) { radlog(L_ERR, "Error: listen type \"proxy\" Cannot appear in a virtual server section"); return NULL; } @@ -2337,6 +2447,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. @@ -2394,7 +2526,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 @@ -2563,6 +2695,15 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head) 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. */ @@ -2592,10 +2733,7 @@ add_sockets: /* * */ -#ifdef WITH_TCP home.proto = IPPROTO_UDP; -#endif - home.src_ipaddr = server_ipaddr; /* @@ -2605,6 +2743,9 @@ add_sockets: for (this = *head; this != NULL; this = this->next) { if (this->type == RAD_LISTEN_AUTH) { sock = this->data; + + if (is_loopback(&sock->my_ipaddr)) continue; + if (home.src_ipaddr.af == AF_UNSPEC) { home.src_ipaddr = sock->my_ipaddr; } @@ -2614,6 +2755,9 @@ add_sockets: #ifdef WITH_ACCT if (this->type == RAD_LISTEN_ACCT) { sock = this->data; + + if (is_loopback(&sock->my_ipaddr)) continue; + if (home.src_ipaddr.af == AF_UNSPEC) { home.src_ipaddr = sock->my_ipaddr; } @@ -2673,12 +2817,12 @@ void listen_free(rad_listen_t **head) } #ifdef WITH_TCP - if ((this->type == RAD_LISTEN_AUTH) || + if ((this->type == RAD_LISTEN_AUTH) #ifdef WITH_ACCT - (this->type == RAD_LISTEN_ACCT) || + || (this->type == RAD_LISTEN_ACCT) #endif #ifdef WITH_PROXY - (this->type == RAD_LISTEN_PROXY) + || (this->type == RAD_LISTEN_PROXY) #endif ) { listen_socket_t *sock = this->data; @@ -2704,8 +2848,11 @@ RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr, for (this = mainconfig.listen; this != NULL; this = this->next) { listen_socket_t *sock; - if ((this->type != RAD_LISTEN_AUTH) && - (this->type != RAD_LISTEN_ACCT)) continue; + if ((this->type != RAD_LISTEN_AUTH) +#ifdef WITH_ACCOUNTING + && (this->type != RAD_LISTEN_ACCT) +#endif + ) continue; sock = this->data; @@ -2730,8 +2877,11 @@ rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port) * FIXME: For TCP, ignore the *secondary* * listeners associated with the main socket. */ - if ((this->type != RAD_LISTEN_AUTH) && - (this->type != RAD_LISTEN_ACCT)) continue; + if ((this->type != RAD_LISTEN_AUTH) +#ifdef WITH_ACCOUNTING + && (this->type != RAD_LISTEN_ACCT) +#endif + ) continue; sock = this->data; @@ -2741,18 +2891,9 @@ rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port) } if ((sock->my_port == port) && - ((sock->my_ipaddr.af == AF_INET) && - (sock->my_ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY))) { - return this; - } - -#ifdef HAVE_STRUCT_SOCKADDR_IN6 - if ((sock->my_port == port) && - (sock->my_ipaddr.af == AF_INET6) && - (IN6_IS_ADDR_UNSPECIFIED(&sock->my_ipaddr.ipaddr.ip6addr))) { + fr_inaddr_any(&sock->my_ipaddr)) { return this; } -#endif } return NULL;