Diff for /embedaddon/rsync/clientname.c between versions 1.1.1.3 and 1.1.1.4

version 1.1.1.3, 2016/11/01 09:54:32 version 1.1.1.4, 2021/03/17 00:32:36
Line 3 Line 3
  *   *
  * Copyright (C) 1992-2001 Andrew Tridgell <tridge@samba.org>   * Copyright (C) 1992-2001 Andrew Tridgell <tridge@samba.org>
  * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>   * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
 * Copyright (C) 2002-2015 Wayne Davison * Copyright (C) 2002-2020 Wayne Davison
  *   *
  * This program is free software; you can redistribute it and/or modify   * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by   * it under the terms of the GNU General Public License as published by
Line 27 Line 27
  */   */
   
 #include "rsync.h"  #include "rsync.h"
   #include "itypes.h"
   
   extern int am_daemon;
   
 static const char default_name[] = "UNKNOWN";  static const char default_name[] = "UNKNOWN";
extern int am_server;static const char proxyv2sig[] = "\r\n\r\n\0\r\nQUIT\n";
   
   static char ipaddr_buf[100];
   
/**#define PROXY_V2_SIG_SIZE ((int)sizeof proxyv2sig - 1)
 * Return the IP addr of the client as a string#define PROXY_V2_HEADER_SIZE (PROXY_V2_SIG_SIZE + 1 + 1 + 2)
 **/
 #define CMD_LOCAL 0
 #define CMD_PROXY 1
 
 #define PROXY_FAM_TCPv4 0x11
 #define PROXY_FAM_TCPv6 0x21
 
 #define GET_SOCKADDR_FAMILY(ss) ((struct sockaddr*)ss)->sa_family
 
 static void client_sockaddr(int fd, struct sockaddr_storage *ss, socklen_t *ss_len);
 static int check_name(const char *ipaddr, const struct sockaddr_storage *ss, char *name_buf, size_t name_buf_size);
 static int valid_ipaddr(const char *s);
 
 /* Return the IP addr of the client as a string. */
 char *client_addr(int fd)  char *client_addr(int fd)
 {  {
         static char addr_buf[100];  
         static int initialised;  
         struct sockaddr_storage ss;          struct sockaddr_storage ss;
         socklen_t length = sizeof ss;          socklen_t length = sizeof ss;
   
        if (initialised)        if (*ipaddr_buf)
                return addr_buf;                return ipaddr_buf;
   
        initialised = 1;        if (am_daemon < 0) {     /* daemon over --rsh mode */
 
        if (am_server) {     /* daemon over --rsh mode */ 
                 char *env_str;                  char *env_str;
                strlcpy(addr_buf, "0.0.0.0", sizeof addr_buf);                strlcpy(ipaddr_buf, "0.0.0.0", sizeof ipaddr_buf);
                 if ((env_str = getenv("REMOTE_HOST")) != NULL                  if ((env_str = getenv("REMOTE_HOST")) != NULL
                  || (env_str = getenv("SSH_CONNECTION")) != NULL                   || (env_str = getenv("SSH_CONNECTION")) != NULL
                  || (env_str = getenv("SSH_CLIENT")) != NULL                   || (env_str = getenv("SSH_CLIENT")) != NULL
                  || (env_str = getenv("SSH2_CLIENT")) != NULL) {                   || (env_str = getenv("SSH2_CLIENT")) != NULL) {
                         char *p;                          char *p;
                        strlcpy(addr_buf, env_str, sizeof addr_buf);                        strlcpy(ipaddr_buf, env_str, sizeof ipaddr_buf);
                         /* Truncate the value to just the IP address. */                          /* Truncate the value to just the IP address. */
                        if ((p = strchr(addr_buf, ' ')) != NULL)                        if ((p = strchr(ipaddr_buf, ' ')) != NULL)
                                 *p = '\0';                                  *p = '\0';
                 }                  }
        } else {                if (valid_ipaddr(ipaddr_buf))
                client_sockaddr(fd, &ss, &length);                        return ipaddr_buf;
                getnameinfo((struct sockaddr *)&ss, length, 
                            addr_buf, sizeof addr_buf, NULL, 0, NI_NUMERICHOST); 
         }          }
   
        return addr_buf;        client_sockaddr(fd, &ss, &length);
}        getnameinfo((struct sockaddr *)&ss, length, ipaddr_buf, sizeof ipaddr_buf, NULL, 0, NI_NUMERICHOST);
   
        return ipaddr_buf;
static int get_sockaddr_family(const struct sockaddr_storage *ss) 
{ 
        return ((struct sockaddr *) ss)->sa_family; 
 }  }
   
   
Line 89  static int get_sockaddr_family(const struct sockaddr_s Line 97  static int get_sockaddr_family(const struct sockaddr_s
  * After translation from sockaddr to name we do a forward lookup to   * After translation from sockaddr to name we do a forward lookup to
  * make sure nobody is spoofing PTR records.   * make sure nobody is spoofing PTR records.
  **/   **/
char *client_name(int fd)char *client_name(const char *ipaddr)
 {  {
         static char name_buf[100];          static char name_buf[100];
        static char port_buf[100];        char port_buf[100];
        static int initialised; 
         struct sockaddr_storage ss;          struct sockaddr_storage ss;
         socklen_t ss_len;          socklen_t ss_len;
           struct addrinfo hint, *answer;
           int err;
   
        if (initialised)        if (*name_buf)
                 return name_buf;                  return name_buf;
   
         strlcpy(name_buf, default_name, sizeof name_buf);          strlcpy(name_buf, default_name, sizeof name_buf);
         initialised = 1;  
   
           if (strcmp(ipaddr, "0.0.0.0") == 0)
                   return name_buf;
   
         memset(&ss, 0, sizeof ss);          memset(&ss, 0, sizeof ss);
           memset(&hint, 0, sizeof hint);
   
         if (am_server) {        /* daemon over --rsh mode */  
                 char *addr = client_addr(fd);  
                 struct addrinfo hint, *answer;  
                 int err;  
   
                 if (strcmp(addr, "0.0.0.0") == 0)  
                         return name_buf;  
   
                 memset(&hint, 0, sizeof hint);  
   
 #ifdef AI_NUMERICHOST  #ifdef AI_NUMERICHOST
                hint.ai_flags = AI_NUMERICHOST;        hint.ai_flags = AI_NUMERICHOST;
 #endif  #endif
                hint.ai_socktype = SOCK_STREAM;        hint.ai_socktype = SOCK_STREAM;
   
                if ((err = getaddrinfo(addr, NULL, &hint, &answer)) != 0) {        if ((err = getaddrinfo(ipaddr, NULL, &hint, &answer)) != 0) {
                        rprintf(FLOG, "malformed address %s: %s\n",                rprintf(FLOG, "malformed address %s: %s\n", ipaddr, gai_strerror(err));
                                addr, gai_strerror(err));                return name_buf;
                        return name_buf;        }
                } 
   
                switch (answer->ai_family) {        switch (answer->ai_family) {
                case AF_INET:        case AF_INET:
                        ss_len = sizeof (struct sockaddr_in);                ss_len = sizeof (struct sockaddr_in);
                        memcpy(&ss, answer->ai_addr, ss_len);                memcpy(&ss, answer->ai_addr, ss_len);
                        break;                break;
 #ifdef INET6  #ifdef INET6
                case AF_INET6:        case AF_INET6:
                        ss_len = sizeof (struct sockaddr_in6);                ss_len = sizeof (struct sockaddr_in6);
                        memcpy(&ss, answer->ai_addr, ss_len);                memcpy(&ss, answer->ai_addr, ss_len);
                        break;                break;
 #endif  #endif
                default:        default:
                        exit_cleanup(RERR_SOCKETIO);                NOISY_DEATH("Unknown ai_family value");
                } 
                freeaddrinfo(answer); 
        } else { 
                ss_len = sizeof ss; 
                client_sockaddr(fd, &ss, &ss_len); 
         }          }
           freeaddrinfo(answer);
   
        if (lookup_name(fd, &ss, ss_len, name_buf, sizeof name_buf,        /* reverse lookup */
                        port_buf, sizeof port_buf) == 0)        err = getnameinfo((struct sockaddr*)&ss, ss_len, name_buf, sizeof name_buf,
                check_name(fd, &ss, name_buf, sizeof name_buf);                          port_buf, sizeof port_buf, NI_NAMEREQD | NI_NUMERICSERV);
         if (err) {
                 strlcpy(name_buf, default_name, sizeof name_buf);
                 rprintf(FLOG, "name lookup failed for %s: %s\n", ipaddr, gai_strerror(err));
         } else
                 check_name(ipaddr, &ss, name_buf, sizeof name_buf);
   
         return name_buf;          return name_buf;
 }  }
   
   
   /* Try to read a proxy protocol header (V1 or V2). Returns 1 on success or 0 on failure. */
   int read_proxy_protocol_header(int fd)
   {
           union {
                   struct {
                           char line[108];
                   } v1;
                   struct {
                           char sig[PROXY_V2_SIG_SIZE];
                           char ver_cmd;
                           char fam;
                           char len[2];
                           union {
                                   struct {
                                           char src_addr[4];
                                           char dst_addr[4];
                                           char src_port[2];
                                           char dst_port[2];
                                   } ip4;
                                   struct {
                                           char src_addr[16];
                                           char dst_addr[16];
                                           char src_port[2];
                                           char dst_port[2];
                                   } ip6;
                                   struct {
                                           char src_addr[108];
                                           char dst_addr[108];
                                   } unx;
                           } addr;
                   } v2;
           } hdr;
   
           read_buf(fd, (char*)&hdr, PROXY_V2_SIG_SIZE);
   
           if (memcmp(hdr.v2.sig, proxyv2sig, PROXY_V2_SIG_SIZE) == 0) { /* Proxy V2 */
                   int ver, cmd, size;
   
                   read_buf(fd, (char*)&hdr + PROXY_V2_SIG_SIZE, PROXY_V2_HEADER_SIZE - PROXY_V2_SIG_SIZE);
   
                   ver = (hdr.v2.ver_cmd & 0xf0) >> 4;
                   cmd = (hdr.v2.ver_cmd & 0x0f);
                   size = (hdr.v2.len[0] << 8) + hdr.v2.len[1];
   
                   if (ver != 2 || size + PROXY_V2_HEADER_SIZE > (int)sizeof hdr)
                           return 0;
   
                   /* Grab all the remaining data in the binary request. */
                   read_buf(fd, (char*)&hdr + PROXY_V2_HEADER_SIZE, size);
   
                   switch (cmd) {
                   case CMD_PROXY:
                           switch (hdr.v2.fam) {
                           case PROXY_FAM_TCPv4:
                                   if (size != sizeof hdr.v2.addr.ip4)
                                           return 0;
                                   inet_ntop(AF_INET, hdr.v2.addr.ip4.src_addr, ipaddr_buf, sizeof ipaddr_buf);
                                   return valid_ipaddr(ipaddr_buf);
                           case PROXY_FAM_TCPv6:
                                   if (size != sizeof hdr.v2.addr.ip6)
                                           return 0;
                                   inet_ntop(AF_INET6, hdr.v2.addr.ip6.src_addr, ipaddr_buf, sizeof ipaddr_buf);
                                   return valid_ipaddr(ipaddr_buf);
                           default:
                                   break;
                           }
                           /* For an unsupported protocol we'll ignore the proxy data (leaving ipaddr_buf unset)
                            * and accept the connection, which will get handled as a normal socket addr. */
                           return 1;
                   case CMD_LOCAL:
                           return 1;
                   default:
                           break;
                   }
   
                   return 0;
           }
   
           if (memcmp(hdr.v1.line, "PROXY", 5) == 0) { /* Proxy V1 */
                   char *endc, *sp, *p = hdr.v1.line + PROXY_V2_SIG_SIZE;
                   int port_chk;
   
                   *p = '\0';
                   if (!strchr(hdr.v1.line, '\n')) {
                           while (1) {
                                   read_buf(fd, p, 1);
                                   if (*p++ == '\n')
                                           break;
                                   if (p - hdr.v1.line >= (int)sizeof hdr.v1.line - 1)
                                           return 0;
                           }
                           *p = '\0';
                   }
   
                   endc = strchr(hdr.v1.line, '\r');
                   if (!endc || endc[1] != '\n' || endc[2])
                           return 0;
                   *endc = '\0';
   
                   p = hdr.v1.line + 5;
   
                   if (!isSpace(p++))
                           return 0;
                   if (strncmp(p, "TCP4", 4) == 0)
                           p += 4;
                   else if (strncmp(p, "TCP6", 4) == 0)
                           p += 4;
                   else if (strncmp(p, "UNKNOWN", 7) == 0)
                           return 1;
                   else
                           return 0;
   
                   if (!isSpace(p++))
                           return 0;
   
                   if ((sp = strchr(p, ' ')) == NULL)
                           return 0;
                   *sp = '\0';
                   if (!valid_ipaddr(p))
                           return 0;
                   strlcpy(ipaddr_buf, p, sizeof ipaddr_buf); /* It will always fit when valid. */
   
                   p = sp + 1;
                   if ((sp = strchr(p, ' ')) == NULL)
                           return 0;
                   *sp = '\0';
                   if (!valid_ipaddr(p))
                           return 0;
                   /* Ignore destination address. */
   
                   p = sp + 1;
                   if ((sp = strchr(p, ' ')) == NULL)
                           return 0;
                   *sp = '\0';
                   port_chk = strtol(p, &endc, 10);
                   if (*endc || port_chk == 0)
                           return 0;
                   /* Ignore source port. */
   
                   p = sp + 1;
                   port_chk = strtol(p, &endc, 10);
                   if (*endc || port_chk == 0)
                           return 0;
                   /* Ignore destination port. */
   
                   return 1;
           }
   
           return 0;
   }
   
   
 /**  /**
  * Get the sockaddr for the client.   * Get the sockaddr for the client.
  *   *
  * If it comes in as an ipv4 address mapped into IPv6 format then we   * If it comes in as an ipv4 address mapped into IPv6 format then we
  * convert it back to a regular IPv4.   * convert it back to a regular IPv4.
  **/   **/
void client_sockaddr(int fd,static void client_sockaddr(int fd, struct sockaddr_storage *ss, socklen_t *ss_len)
                     struct sockaddr_storage *ss, 
                     socklen_t *ss_len) 
 {  {
         memset(ss, 0, sizeof *ss);          memset(ss, 0, sizeof *ss);
   
Line 174  void client_sockaddr(int fd, Line 325  void client_sockaddr(int fd,
         }          }
   
 #ifdef INET6  #ifdef INET6
        if (get_sockaddr_family(ss) == AF_INET6 &&        if (GET_SOCKADDR_FAMILY(ss) == AF_INET6
            IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)ss)->sin6_addr)) {         && IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)ss)->sin6_addr)) {
                 /* OK, so ss is in the IPv6 family, but it is really                  /* OK, so ss is in the IPv6 family, but it is really
                  * an IPv4 address: something like                   * an IPv4 address: something like
                  * "::ffff:10.130.1.2".  If we use it as-is, then the                   * "::ffff:10.130.1.2".  If we use it as-is, then the
Line 198  void client_sockaddr(int fd, Line 349  void client_sockaddr(int fd,
                 /* There is a macro to extract the mapped part                  /* There is a macro to extract the mapped part
                  * (IN6_V4MAPPED_TO_SINADDR ?), but it does not seem                   * (IN6_V4MAPPED_TO_SINADDR ?), but it does not seem
                  * to be present in the Linux headers. */                   * to be present in the Linux headers. */
                memcpy(&sin->sin_addr, &sin6.sin6_addr.s6_addr[12],                memcpy(&sin->sin_addr, &sin6.sin6_addr.s6_addr[12], sizeof sin->sin_addr);
                    sizeof sin->sin_addr); 
         }          }
 #endif  #endif
 }  }
   
   
 /**  /**
  * Look up a name from @p ss into @p name_buf.  
  *  
  * @param fd file descriptor for client socket.  
  **/  
 int lookup_name(int fd, const struct sockaddr_storage *ss,  
                 socklen_t ss_len,  
                 char *name_buf, size_t name_buf_size,  
                 char *port_buf, size_t port_buf_size)  
 {  
         int name_err;  
   
         /* reverse lookup */  
         name_err = getnameinfo((struct sockaddr *) ss, ss_len,  
                                name_buf, name_buf_size,  
                                port_buf, port_buf_size,  
                                NI_NAMEREQD | NI_NUMERICSERV);  
         if (name_err != 0) {  
                 strlcpy(name_buf, default_name, name_buf_size);  
                 rprintf(FLOG, "name lookup failed for %s: %s\n",  
                         client_addr(fd), gai_strerror(name_err));  
                 return name_err;  
         }  
   
         return 0;  
 }  
   
   
   
 /**  
  * Compare an addrinfo from the resolver to a sockinfo.   * Compare an addrinfo from the resolver to a sockinfo.
  *   *
  * Like strcmp, returns 0 for identical.   * Like strcmp, returns 0 for identical.
  **/   **/
int compare_addrinfo_sockaddr(const struct addrinfo *ai,static int compare_addrinfo_sockaddr(const struct addrinfo *ai, const struct sockaddr_storage *ss)
                              const struct sockaddr_storage *ss) 
 {  {
        int ss_family = get_sockaddr_family(ss);        int ss_family = GET_SOCKADDR_FAMILY(ss);
         const char fn[] = "compare_addrinfo_sockaddr";          const char fn[] = "compare_addrinfo_sockaddr";
   
         if (ai->ai_family != ss_family) {          if (ai->ai_family != ss_family) {
Line 258  int compare_addrinfo_sockaddr(const struct addrinfo *a Line 378  int compare_addrinfo_sockaddr(const struct addrinfo *a
                 sin1 = (const struct sockaddr_in *) ss;                  sin1 = (const struct sockaddr_in *) ss;
                 sin2 = (const struct sockaddr_in *) ai->ai_addr;                  sin2 = (const struct sockaddr_in *) ai->ai_addr;
   
                return memcmp(&sin1->sin_addr, &sin2->sin_addr,                return memcmp(&sin1->sin_addr, &sin2->sin_addr, sizeof sin1->sin_addr);
                              sizeof sin1->sin_addr); 
         }          }
   
 #ifdef INET6  #ifdef INET6
Line 269  int compare_addrinfo_sockaddr(const struct addrinfo *a Line 388  int compare_addrinfo_sockaddr(const struct addrinfo *a
                 sin1 = (const struct sockaddr_in6 *) ss;                  sin1 = (const struct sockaddr_in6 *) ss;
                 sin2 = (const struct sockaddr_in6 *) ai->ai_addr;                  sin2 = (const struct sockaddr_in6 *) ai->ai_addr;
   
                if (ai->ai_addrlen < sizeof (struct sockaddr_in6)) {                if (ai->ai_addrlen < (int)sizeof (struct sockaddr_in6)) {
                         rprintf(FLOG, "%s: too short sockaddr_in6; length=%d\n",                          rprintf(FLOG, "%s: too short sockaddr_in6; length=%d\n",
                                 fn, (int)ai->ai_addrlen);                                  fn, (int)ai->ai_addrlen);
                         return 1;                          return 1;
                 }                  }
   
                if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,                if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr, sizeof sin1->sin6_addr))
                           sizeof sin1->sin6_addr)) 
                         return 1;                          return 1;
   
 #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID  #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
Line 302  int compare_addrinfo_sockaddr(const struct addrinfo *a Line 420  int compare_addrinfo_sockaddr(const struct addrinfo *a
  * because it doesn't seem that it could be spoofed in any way, and   * because it doesn't seem that it could be spoofed in any way, and
  * getaddrinfo on random service names seems to cause problems on AIX.   * getaddrinfo on random service names seems to cause problems on AIX.
  **/   **/
int check_name(int fd,static int check_name(const char *ipaddr, const struct sockaddr_storage *ss, char *name_buf, size_t name_buf_size)
               const struct sockaddr_storage *ss, 
               char *name_buf, size_t name_buf_size) 
 {  {
         struct addrinfo hints, *res, *res0;          struct addrinfo hints, *res, *res0;
         int error;          int error;
        int ss_family = get_sockaddr_family(ss);        int ss_family = GET_SOCKADDR_FAMILY(ss);
   
         memset(&hints, 0, sizeof hints);          memset(&hints, 0, sizeof hints);
         hints.ai_family = ss_family;          hints.ai_family = ss_family;
Line 339  int check_name(int fd, Line 455  int check_name(int fd,
                 /* We hit the end of the list without finding an                  /* We hit the end of the list without finding an
                  * address that was the same as ss. */                   * address that was the same as ss. */
                 rprintf(FLOG, "%s is not a known address for \"%s\": "                  rprintf(FLOG, "%s is not a known address for \"%s\": "
                        "spoofed address?\n", client_addr(fd), name_buf);                        "spoofed address?\n", ipaddr, name_buf);
                 strlcpy(name_buf, default_name, name_buf_size);                  strlcpy(name_buf, default_name, name_buf_size);
         }          }
   
         freeaddrinfo(res0);          freeaddrinfo(res0);
         return 0;          return 0;
   }
   
   /* Returns 1 for a valid IPv4 or IPv6 addr, or 0 for a bad one. */
   static int valid_ipaddr(const char *s)
   {
           int i;
   
           if (strchr(s, ':') != NULL) { /* Only IPv6 has a colon. */
                   int count, saw_double_colon = 0;
                   int ipv4_at_end = 0;
   
                   if (*s == ':') { /* A colon at the start must be a :: */
                           if (*++s != ':')
                                   return 0;
                           saw_double_colon = 1;
                           s++;
                   }
   
                   for (count = 0; count < 8; count++) {
                           if (!*s)
                                   return saw_double_colon;
   
                           if (strchr(s, ':') == NULL && strchr(s, '.') != NULL) {
                                   if ((!saw_double_colon && count != 6) || (saw_double_colon && count > 6))
                                           return 0;
                                   ipv4_at_end = 1;
                                   break;
                           }
   
                           if (!isHexDigit(s++)) /* Need 1-4 hex digits */
                                   return 0;
                           if (isHexDigit(s) && isHexDigit(++s) && isHexDigit(++s) && isHexDigit(++s))
                                   return 0;
   
                           if (*s == ':') {
                                   if (!*++s)
                                           return 0;
                                   if (*s == ':') {
                                           if (saw_double_colon)
                                                   return 0;
                                           saw_double_colon = 1;
                                           s++;
                                   }
                           }
                   }
   
                   if (!ipv4_at_end)
                           return !*s;
           }
   
           /* IPv4 */
           for (i = 0; i < 4; i++) {
                   long n;
                   char *end;
   
                   if (i && *s++ != '.')
                           return 0;
                   n = strtol(s, &end, 10);
                   if (n > 255 || n < 0 || end <= s || end > s+3)
                           return 0;
                   s = end;
           }
   
           return !*s;
 }  }

Removed from v.1.1.1.3  
changed lines
  Added in v.1.1.1.4


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>