Clean up loopback / inaddr_any checks
[freeradius.git] / src / main / listen.c
index e86da73..e82d585 100644 (file)
@@ -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;
 }
 
@@ -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) {
@@ -789,6 +782,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.
@@ -885,16 +880,24 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        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 */
+       }
+
        /*
         *      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,7 +909,6 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                        return -1;
                }
                sock->interface = value;
-#endif
        }
 
 #ifdef WITH_DHCP
@@ -1343,9 +1345,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.");
@@ -1399,10 +1401,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;
@@ -1450,7 +1452,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);
 
        /*
@@ -1830,7 +1832,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
@@ -1917,12 +1919,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;
                }
        }
@@ -1936,11 +1943,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);
 
@@ -1954,8 +1961,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) {
@@ -2399,6 +2441,26 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server)
        return this;
 }
 
+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;
+}
+
 /*
  *     Generate a list of listeners.  Takes an input list of
  *     listeners, too, so we don't close sockets with waiting packets.
@@ -2625,6 +2687,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.
         */
@@ -2664,6 +2735,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;
                                }
@@ -2673,6 +2747,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;
                                }
@@ -2800,18 +2877,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))) {
+                   fr_inaddr_any(&sock->my_ipaddr)) {
                        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))) {
-                       return this;
-               }
-#endif
        }
 
        return NULL;