]> jfr.im git - solanum.git/blobdiff - ircd/s_serv.c
free server_p->certfp, allocated in newconf.c
[solanum.git] / ircd / s_serv.c
index 208a6fe16ef0c7366ac95bad017f235153c926ce..0025622d704e04af82d08dc5b469f6495bffd773 100644 (file)
@@ -31,7 +31,6 @@
 #include "s_serv.h"
 #include "class.h"
 #include "client.h"
-#include "common.h"
 #include "hash.h"
 #include "match.h"
 #include "ircd.h"
 #include "capability.h"
 #include "s_assert.h"
 
-#ifndef INADDR_NONE
-#define INADDR_NONE ((unsigned int) 0xffffffff)
-#endif
-
 int MaxConnectionCount = 1;
 int MaxClientCount = 1;
 int refresh_user_links = 0;
@@ -152,6 +147,7 @@ init_builtin_capabs(void)
 
 static CNCB serv_connect_callback;
 static CNCB serv_connect_ssl_callback;
+static SSL_OPEN_CB serv_connect_ssl_open_callback;
 
 /*
  * hunt_server - Do the basic thing in delivering the message (command)
@@ -348,6 +344,9 @@ check_server(const char *name, struct Client *client_p)
        rb_dlink_node *ptr;
        int error = -1;
        const char *encr;
+       bool name_matched = false;
+       bool host_matched = false;
+       bool certfp_failed = false;
 
        s_assert(NULL != client_p);
        if(client_p == NULL)
@@ -361,6 +360,8 @@ check_server(const char *name, struct Client *client_p)
 
        RB_DLINK_FOREACH(ptr, server_conf_list.head)
        {
+               struct rb_sockaddr_storage client_addr;
+
                tmp_p = ptr->data;
 
                if(ServerConfIllegal(tmp_p))
@@ -369,14 +370,23 @@ check_server(const char *name, struct Client *client_p)
                if(!match(tmp_p->name, name))
                        continue;
 
-               error = -3;
+               name_matched = true;
 
-               /* XXX: Fix me for IPv6 */
-               /* XXX sockhost is the IPv4 ip as a string */
-               if(match(tmp_p->host, client_p->host) ||
-                  match(tmp_p->host, client_p->sockhost))
+               if(rb_inet_pton_sock(client_p->sockhost, (struct sockaddr *)&client_addr) <= 0)
+                       SET_SS_FAMILY(&client_addr, AF_UNSPEC);
+
+               if((tmp_p->connect_host && match(tmp_p->connect_host, client_p->host))
+                       || (GET_SS_FAMILY(&client_addr) == GET_SS_FAMILY(&tmp_p->connect4)
+                               && comp_with_mask_sock((struct sockaddr *)&client_addr,
+                                       (struct sockaddr *)&tmp_p->connect4, 32))
+#ifdef RB_IPV6
+                       || (GET_SS_FAMILY(&client_addr) == GET_SS_FAMILY(&tmp_p->connect6)
+                               && comp_with_mask_sock((struct sockaddr *)&client_addr,
+                                       (struct sockaddr *)&tmp_p->connect6, 128))
+#endif
+                       )
                {
-                       error = -2;
+                       host_matched = true;
 
                        if(tmp_p->passwd)
                        {
@@ -398,8 +408,10 @@ check_server(const char *name, struct Client *client_p)
 
                        if(tmp_p->certfp)
                        {
-                               if(!client_p->certfp || strcasecmp(tmp_p->certfp, client_p->certfp) != 0)
+                               if(!client_p->certfp || rb_strcasecmp(tmp_p->certfp, client_p->certfp) != 0) {
+                                       certfp_failed = true;
                                        continue;
+                               }
                        }
 
                        server_p = tmp_p;
@@ -408,7 +420,17 @@ check_server(const char *name, struct Client *client_p)
        }
 
        if(server_p == NULL)
+       {
+               /* return the most specific error */
+               if(certfp_failed)
+                       error = -6;
+               else if(host_matched)
+                       error = -2;
+               else if(name_matched)
+                       error = -3;
+
                return error;
+       }
 
        if(ServerConfSSL(server_p) && client_p->localClient->ssl_ctl == NULL)
        {
@@ -595,6 +617,9 @@ burst_TS6(struct Client *client_p)
                if(!IsPerson(target_p))
                        continue;
 
+               if(MyClient(target_p->from) && target_p->localClient->att_sconf != NULL && ServerConfNoExport(target_p->localClient->att_sconf))
+                       continue;
+
                send_umode(NULL, target_p, 0, ubuf);
                if(!*ubuf)
                {
@@ -797,19 +822,6 @@ server_estab(struct Client *client_p)
        /* Its got identd , since its a server */
        SetGotId(client_p);
 
-       /* If there is something in the serv_list, it might be this
-        * connecting server..
-        */
-       if(!ServerInfo.hub && serv_list.head)
-       {
-               if(client_p != serv_list.head->data || serv_list.head->next)
-               {
-                       ServerStats.is_ref++;
-                       sendto_one(client_p, "ERROR :I'm a leaf not a hub");
-                       return exit_client(client_p, client_p, client_p, "I'm a leaf");
-               }
-       }
-
        if(IsUnknown(client_p))
        {
                /* the server may be linking based on certificate fingerprint now. --nenolod */
@@ -817,7 +829,7 @@ server_estab(struct Client *client_p)
                           EmptyString(server_p->spasswd) ? "*" : server_p->spasswd, TS_CURRENT, me.id);
 
                /* pass info to new server */
-               send_capabilities(client_p, default_server_capabs
+               send_capabilities(client_p, default_server_capabs | CAP_MASK
                                  | (ServerConfCompressed(server_p) ? CAP_ZIP_SUPPORTED : 0)
                                  | (ServerConfTb(server_p) ? CAP_TB : 0));
 
@@ -901,6 +913,9 @@ server_estab(struct Client *client_p)
                if(target_p == client_p)
                        continue;
 
+               if(target_p->localClient->att_sconf != NULL && ServerConfNoExport(target_p->localClient->att_sconf))
+                       continue;
+
                if(has_id(target_p) && has_id(client_p))
                {
                        sendto_one(target_p, ":%s SID %s 2 %s :%s%s",
@@ -949,6 +964,10 @@ server_estab(struct Client *client_p)
                if(IsMe(target_p) || target_p->from == client_p)
                        continue;
 
+               /* don't distribute downstream leaves of servers that are no-export */
+               if(MyClient(target_p->from) && target_p->from->localClient->att_sconf != NULL && ServerConfNoExport(target_p->from->localClient->att_sconf))
+                       continue;
+
                /* presumption, if target has an id, so does its uplink */
                if(has_id(client_p) && has_id(target_p))
                        sendto_one(client_p, ":%s SID %s %d %s :%s%s",
@@ -1009,7 +1028,8 @@ int
 serv_connect(struct server_conf *server_p, struct Client *by)
 {
        struct Client *client_p;
-       struct rb_sockaddr_storage myipnum;
+       struct rb_sockaddr_storage sa_connect;
+       struct rb_sockaddr_storage sa_bind;
        char note[HOSTLEN + 10];
        rb_fde_t *F;
 
@@ -1017,8 +1037,42 @@ serv_connect(struct server_conf *server_p, struct Client *by)
        if(server_p == NULL)
                return 0;
 
+       SET_SS_FAMILY(&sa_connect, AF_UNSPEC);
+       SET_SS_FAMILY(&sa_bind, AF_UNSPEC);
+
+#ifdef RB_IPV6
+       if(server_p->aftype != AF_UNSPEC
+               && GET_SS_FAMILY(&server_p->connect4) == AF_INET
+               && GET_SS_FAMILY(&server_p->connect6) == AF_INET6)
+       {
+               if(rand() % 2 == 0)
+               {
+                       sa_connect = server_p->connect4;
+                       sa_bind = server_p->bind4;
+               }
+               else
+               {
+                       sa_connect = server_p->connect6;
+                       sa_bind = server_p->bind6;
+               }
+       }
+       else if(server_p->aftype == AF_INET || GET_SS_FAMILY(&server_p->connect4) == AF_INET)
+#endif
+       {
+               sa_connect = server_p->connect4;
+               sa_bind = server_p->bind4;
+       }
+#ifdef RB_IPV6
+       else if(server_p->aftype == AF_INET6 || GET_SS_FAMILY(&server_p->connect6) == AF_INET6)
+       {
+               sa_connect = server_p->connect6;
+               sa_bind = server_p->bind6;
+       }
+#endif
+
        /* log */
-       rb_inet_ntop_sock((struct sockaddr *)&server_p->my_ipnum, buf, sizeof(buf));
+       buf[0] = 0;
+       rb_inet_ntop_sock((struct sockaddr *)&sa_connect, buf, sizeof(buf));
        ilog(L_SERVER, "Connect to *[%s] @%s", server_p->name, buf);
 
        /*
@@ -1036,7 +1090,12 @@ serv_connect(struct server_conf *server_p, struct Client *by)
        }
 
        /* create a socket for the server connection */
-       if((F = rb_socket(GET_SS_FAMILY(&server_p->my_ipnum), SOCK_STREAM, 0, NULL)) == NULL)
+       if(GET_SS_FAMILY(&sa_connect) == AF_UNSPEC)
+       {
+               ilog_error("unspecified socket address family");
+               return 0;
+       }
+       else if((F = rb_socket(GET_SS_FAMILY(&sa_connect), SOCK_STREAM, 0, NULL)) == NULL)
        {
                ilog_error("opening a stream socket");
                return 0;
@@ -1051,16 +1110,14 @@ serv_connect(struct server_conf *server_p, struct Client *by)
 
        /* Copy in the server, hostname, fd */
        rb_strlcpy(client_p->name, server_p->name, sizeof(client_p->name));
-       rb_strlcpy(client_p->host, server_p->host, sizeof(client_p->host));
+       if(server_p->connect_host)
+               rb_strlcpy(client_p->host, server_p->connect_host, sizeof(client_p->host));
+       else
+               rb_strlcpy(client_p->host, buf, sizeof(client_p->host));
        rb_strlcpy(client_p->sockhost, buf, sizeof(client_p->sockhost));
        client_p->localClient->F = F;
        /* shove the port number into the sockaddr */
-#ifdef RB_IPV6
-       if(GET_SS_FAMILY(&server_p->my_ipnum) == AF_INET6)
-               ((struct sockaddr_in6 *)&server_p->my_ipnum)->sin6_port = htons(server_p->port);
-       else
-#endif
-               ((struct sockaddr_in *)&server_p->my_ipnum)->sin_port = htons(server_p->port);
+       SET_SS_PORT(&sa_connect, htons(server_p->port));
 
        /*
         * Set up the initial server evilness, ripped straight from
@@ -1088,65 +1145,28 @@ serv_connect(struct server_conf *server_p, struct Client *by)
         */
        make_server(client_p);
        if(by && IsClient(by))
-               strcpy(client_p->serv->by, by->name);
+               rb_strlcpy(client_p->serv->by, by->name, sizeof(client_p->serv->by));
        else
                strcpy(client_p->serv->by, "AutoConn.");
 
        SetConnecting(client_p);
        rb_dlinkAddTail(client_p, &client_p->node, &global_client_list);
 
-       if(ServerConfVhosted(server_p))
+       if(GET_SS_FAMILY(&sa_bind) == AF_UNSPEC)
        {
-               memcpy(&myipnum, &server_p->my_ipnum, sizeof(myipnum));
-               ((struct sockaddr_in *)&myipnum)->sin_port = 0;
-               SET_SS_FAMILY(&myipnum, GET_SS_FAMILY(&server_p->my_ipnum));
-
-       }
-       else if(GET_SS_FAMILY(&server_p->my_ipnum) == AF_INET && ServerInfo.specific_ipv4_vhost)
-       {
-               memcpy(&myipnum, &ServerInfo.ip, sizeof(myipnum));
-               ((struct sockaddr_in *)&myipnum)->sin_port = 0;
-               SET_SS_FAMILY(&myipnum, AF_INET);
-               SET_SS_LEN(&myipnum, sizeof(struct sockaddr_in));
-       }
-
+               if(GET_SS_FAMILY(&sa_connect) == GET_SS_FAMILY(&ServerInfo.bind4))
+                       sa_bind = ServerInfo.bind4;
 #ifdef RB_IPV6
-       else if((GET_SS_FAMILY(&server_p->my_ipnum) == AF_INET6) && ServerInfo.specific_ipv6_vhost)
-       {
-               memcpy(&myipnum, &ServerInfo.ip6, sizeof(myipnum));
-               ((struct sockaddr_in6 *)&myipnum)->sin6_port = 0;
-               SET_SS_FAMILY(&myipnum, AF_INET6);
-               SET_SS_LEN(&myipnum, sizeof(struct sockaddr_in6));
-       }
+               if(GET_SS_FAMILY(&sa_connect) == GET_SS_FAMILY(&ServerInfo.bind6))
+                       sa_bind = ServerInfo.bind6;
 #endif
-       else
-       {
-               if(ServerConfSSL(server_p))
-               {
-                       rb_connect_tcp(client_p->localClient->F,
-                                      (struct sockaddr *)&server_p->my_ipnum, NULL, 0,
-                                      serv_connect_ssl_callback, client_p,
-                                      ConfigFileEntry.connect_timeout);
-               }
-               else
-                       rb_connect_tcp(client_p->localClient->F,
-                                      (struct sockaddr *)&server_p->my_ipnum, NULL, 0,
-                                      serv_connect_callback, client_p,
-                                      ConfigFileEntry.connect_timeout);
-
-               return 1;
        }
-       if(ServerConfSSL(server_p))
-               rb_connect_tcp(client_p->localClient->F, (struct sockaddr *)&server_p->my_ipnum,
-                              (struct sockaddr *)&myipnum,
-                              GET_SS_LEN(&myipnum), serv_connect_ssl_callback, client_p,
-                              ConfigFileEntry.connect_timeout);
-       else
-               rb_connect_tcp(client_p->localClient->F, (struct sockaddr *)&server_p->my_ipnum,
-                              (struct sockaddr *)&myipnum,
-                              GET_SS_LEN(&myipnum), serv_connect_callback, client_p,
-                              ConfigFileEntry.connect_timeout);
 
+       rb_connect_tcp(client_p->localClient->F,
+               (struct sockaddr *)&sa_connect,
+               GET_SS_FAMILY(&sa_bind) == AF_UNSPEC ? NULL : (struct sockaddr *)&sa_bind,
+               ServerConfSSL(server_p) ? serv_connect_ssl_callback : serv_connect_callback,
+               client_p, ConfigFileEntry.connect_timeout);
        return 1;
 }
 
@@ -1170,15 +1190,22 @@ serv_connect_ssl_callback(rb_fde_t *F, int status, void *data)
 
        }
        client_p->localClient->F = xF[0];
+       client_p->localClient->ssl_callback = serv_connect_ssl_open_callback;
 
-       client_p->localClient->ssl_ctl = start_ssld_connect(F, xF[1], rb_get_fd(xF[0]));
+       client_p->localClient->ssl_ctl = start_ssld_connect(F, xF[1], connid_get(client_p));
        if(!client_p->localClient->ssl_ctl)
        {
                serv_connect_callback(client_p->localClient->F, RB_ERROR, data);
                return;
        }
        SetSSL(client_p);
-       serv_connect_callback(client_p->localClient->F, RB_OK, client_p);
+}
+
+static int
+serv_connect_ssl_open_callback(struct Client *client_p, int status)
+{
+       serv_connect_callback(client_p->localClient->F, status, client_p);
+       return 1; /* suppress default exit_client handler for status != RB_OK */
 }
 
 /*
@@ -1222,7 +1249,7 @@ serv_connect_callback(rb_fde_t *F, int status, void *data)
                /* COMM_ERR_TIMEOUT wont have an errno associated with it,
                 * the others will.. --fl
                 */
-               if(status == RB_ERR_TIMEOUT)
+               if(status == RB_ERR_TIMEOUT || status == RB_ERROR_SSL)
                {
                        sendto_realops_snomask(SNO_GENERAL, is_remote_connect(client_p) ? L_NETWIDE : L_ALL,
                                        "Error connecting to %s[%s]: %s",
@@ -1260,6 +1287,18 @@ serv_connect_callback(rb_fde_t *F, int status, void *data)
                return;
        }
 
+       if(server_p->certfp && (!client_p->certfp || rb_strcasecmp(server_p->certfp, client_p->certfp) != 0))
+       {
+               sendto_realops_snomask(SNO_GENERAL, is_remote_connect(client_p) ? L_NETWIDE : L_ALL,
+                    "Connection to %s has invalid certificate fingerprint %s",
+                    client_p->name, client_p->certfp);
+               ilog(L_SERVER, "Access denied, invalid certificate fingerprint %s from %s",
+                    client_p->certfp, log_client_name(client_p, SHOW_IP));
+
+               exit_client(client_p, client_p, &me, "Invalid fingerprint.");
+               return;
+       }
+
        /* Next, send the initial handshake */
        SetHandshake(client_p);
 
@@ -1268,7 +1307,7 @@ serv_connect_callback(rb_fde_t *F, int status, void *data)
                   EmptyString(server_p->spasswd) ? "*" : server_p->spasswd, TS_CURRENT, me.id);
 
        /* pass my info to the new server */
-       send_capabilities(client_p, default_server_capabs
+       send_capabilities(client_p, default_server_capabs | CAP_MASK
                          | (ServerConfCompressed(server_p) ? CAP_ZIP_SUPPORTED : 0)
                          | (ServerConfTb(server_p) ? CAP_TB : 0));