Annotation of embedaddon/rsync/clientname.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Functions for looking up the remote name or addr of a socket.
        !             3:  *
        !             4:  * Copyright (C) 1992-2001 Andrew Tridgell <tridge@samba.org>
        !             5:  * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
        !             6:  * Copyright (C) 2002-2009 Wayne Davison
        !             7:  *
        !             8:  * This program is free software; you can redistribute it and/or modify
        !             9:  * it under the terms of the GNU General Public License as published by
        !            10:  * the Free Software Foundation; either version 3 of the License, or
        !            11:  * (at your option) any later version.
        !            12:  *
        !            13:  * This program is distributed in the hope that it will be useful,
        !            14:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            15:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            16:  * GNU General Public License for more details.
        !            17:  *
        !            18:  * You should have received a copy of the GNU General Public License along
        !            19:  * with this program; if not, visit the http://fsf.org website.
        !            20:  */
        !            21: 
        !            22: /*
        !            23:  * This file is now converted to use the new-style getaddrinfo()
        !            24:  * interface, which supports IPv6 but is also supported on recent
        !            25:  * IPv4-only machines.  On systems that don't have that interface, we
        !            26:  * emulate it using the KAME implementation.
        !            27:  */
        !            28: 
        !            29: #include "rsync.h"
        !            30: 
        !            31: static const char default_name[] = "UNKNOWN";
        !            32: extern int am_server;
        !            33: 
        !            34: 
        !            35: /**
        !            36:  * Return the IP addr of the client as a string
        !            37:  **/
        !            38: char *client_addr(int fd)
        !            39: {
        !            40:        static char addr_buf[100];
        !            41:        static int initialised;
        !            42:        struct sockaddr_storage ss;
        !            43:        socklen_t length = sizeof ss;
        !            44:        char *ssh_info, *p;
        !            45: 
        !            46:        if (initialised)
        !            47:                return addr_buf;
        !            48: 
        !            49:        initialised = 1;
        !            50: 
        !            51:        if (am_server) {        /* daemon over --rsh mode */
        !            52:                strlcpy(addr_buf, "0.0.0.0", sizeof addr_buf);
        !            53:                if ((ssh_info = getenv("SSH_CONNECTION")) != NULL
        !            54:                    || (ssh_info = getenv("SSH_CLIENT")) != NULL
        !            55:                    || (ssh_info = getenv("SSH2_CLIENT")) != NULL) {
        !            56:                        strlcpy(addr_buf, ssh_info, sizeof addr_buf);
        !            57:                        /* Truncate the value to just the IP address. */
        !            58:                        if ((p = strchr(addr_buf, ' ')) != NULL)
        !            59:                                *p = '\0';
        !            60:                }
        !            61:        } else {
        !            62:                client_sockaddr(fd, &ss, &length);
        !            63:                getnameinfo((struct sockaddr *)&ss, length,
        !            64:                            addr_buf, sizeof addr_buf, NULL, 0, NI_NUMERICHOST);
        !            65:        }
        !            66: 
        !            67:        return addr_buf;
        !            68: }
        !            69: 
        !            70: 
        !            71: static int get_sockaddr_family(const struct sockaddr_storage *ss)
        !            72: {
        !            73:        return ((struct sockaddr *) ss)->sa_family;
        !            74: }
        !            75: 
        !            76: 
        !            77: /**
        !            78:  * Return the DNS name of the client.
        !            79:  *
        !            80:  * The name is statically cached so that repeated lookups are quick,
        !            81:  * so there is a limit of one lookup per customer.
        !            82:  *
        !            83:  * If anything goes wrong, including the name->addr->name check, then
        !            84:  * we just use "UNKNOWN", so you can use that value in hosts allow
        !            85:  * lines.
        !            86:  *
        !            87:  * After translation from sockaddr to name we do a forward lookup to
        !            88:  * make sure nobody is spoofing PTR records.
        !            89:  **/
        !            90: char *client_name(int fd)
        !            91: {
        !            92:        static char name_buf[100];
        !            93:        static char port_buf[100];
        !            94:        static int initialised;
        !            95:        struct sockaddr_storage ss;
        !            96:        socklen_t ss_len;
        !            97: 
        !            98:        if (initialised)
        !            99:                return name_buf;
        !           100: 
        !           101:        strlcpy(name_buf, default_name, sizeof name_buf);
        !           102:        initialised = 1;
        !           103: 
        !           104:        memset(&ss, 0, sizeof ss);
        !           105: 
        !           106:        if (am_server) {        /* daemon over --rsh mode */
        !           107:                char *addr = client_addr(fd);
        !           108:                struct addrinfo hint, *answer;
        !           109:                int err;
        !           110: 
        !           111:                if (strcmp(addr, "0.0.0.0") == 0)
        !           112:                        return name_buf;
        !           113: 
        !           114:                memset(&hint, 0, sizeof hint);
        !           115: 
        !           116: #ifdef AI_NUMERICHOST
        !           117:                hint.ai_flags = AI_NUMERICHOST;
        !           118: #endif
        !           119:                hint.ai_socktype = SOCK_STREAM;
        !           120: 
        !           121:                if ((err = getaddrinfo(addr, NULL, &hint, &answer)) != 0) {
        !           122:                        rprintf(FLOG, "malformed address %s: %s\n",
        !           123:                                addr, gai_strerror(err));
        !           124:                        return name_buf;
        !           125:                }
        !           126: 
        !           127:                switch (answer->ai_family) {
        !           128:                case AF_INET:
        !           129:                        ss_len = sizeof (struct sockaddr_in);
        !           130:                        memcpy(&ss, answer->ai_addr, ss_len);
        !           131:                        break;
        !           132: #ifdef INET6
        !           133:                case AF_INET6:
        !           134:                        ss_len = sizeof (struct sockaddr_in6);
        !           135:                        memcpy(&ss, answer->ai_addr, ss_len);
        !           136:                        break;
        !           137: #endif
        !           138:                default:
        !           139:                        exit_cleanup(RERR_SOCKETIO);
        !           140:                }
        !           141:                freeaddrinfo(answer);
        !           142:        } else {
        !           143:                ss_len = sizeof ss;
        !           144:                client_sockaddr(fd, &ss, &ss_len);
        !           145:        }
        !           146: 
        !           147:        if (lookup_name(fd, &ss, ss_len, name_buf, sizeof name_buf,
        !           148:                        port_buf, sizeof port_buf) == 0)
        !           149:                check_name(fd, &ss, name_buf, sizeof name_buf);
        !           150: 
        !           151:        return name_buf;
        !           152: }
        !           153: 
        !           154: 
        !           155: 
        !           156: /**
        !           157:  * Get the sockaddr for the client.
        !           158:  *
        !           159:  * If it comes in as an ipv4 address mapped into IPv6 format then we
        !           160:  * convert it back to a regular IPv4.
        !           161:  **/
        !           162: void client_sockaddr(int fd,
        !           163:                     struct sockaddr_storage *ss,
        !           164:                     socklen_t *ss_len)
        !           165: {
        !           166:        memset(ss, 0, sizeof *ss);
        !           167: 
        !           168:        if (getpeername(fd, (struct sockaddr *) ss, ss_len)) {
        !           169:                /* FIXME: Can we really not continue? */
        !           170:                rsyserr(FLOG, errno, "getpeername on fd%d failed", fd);
        !           171:                exit_cleanup(RERR_SOCKETIO);
        !           172:        }
        !           173: 
        !           174: #ifdef INET6
        !           175:        if (get_sockaddr_family(ss) == AF_INET6 &&
        !           176:            IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)ss)->sin6_addr)) {
        !           177:                /* OK, so ss is in the IPv6 family, but it is really
        !           178:                 * an IPv4 address: something like
        !           179:                 * "::ffff:10.130.1.2".  If we use it as-is, then the
        !           180:                 * reverse lookup might fail or perhaps something else
        !           181:                 * bad might happen.  So instead we convert it to an
        !           182:                 * equivalent address in the IPv4 address family.  */
        !           183:                struct sockaddr_in6 sin6;
        !           184:                struct sockaddr_in *sin;
        !           185: 
        !           186:                memcpy(&sin6, ss, sizeof sin6);
        !           187:                sin = (struct sockaddr_in *)ss;
        !           188:                memset(sin, 0, sizeof *sin);
        !           189:                sin->sin_family = AF_INET;
        !           190:                *ss_len = sizeof (struct sockaddr_in);
        !           191: #ifdef HAVE_SOCKADDR_IN_LEN
        !           192:                sin->sin_len = *ss_len;
        !           193: #endif
        !           194:                sin->sin_port = sin6.sin6_port;
        !           195: 
        !           196:                /* There is a macro to extract the mapped part
        !           197:                 * (IN6_V4MAPPED_TO_SINADDR ?), but it does not seem
        !           198:                 * to be present in the Linux headers. */
        !           199:                memcpy(&sin->sin_addr, &sin6.sin6_addr.s6_addr[12],
        !           200:                    sizeof sin->sin_addr);
        !           201:        }
        !           202: #endif
        !           203: }
        !           204: 
        !           205: 
        !           206: /**
        !           207:  * Look up a name from @p ss into @p name_buf.
        !           208:  *
        !           209:  * @param fd file descriptor for client socket.
        !           210:  **/
        !           211: int lookup_name(int fd, const struct sockaddr_storage *ss,
        !           212:                socklen_t ss_len,
        !           213:                char *name_buf, size_t name_buf_size,
        !           214:                char *port_buf, size_t port_buf_size)
        !           215: {
        !           216:        int name_err;
        !           217: 
        !           218:        /* reverse lookup */
        !           219:        name_err = getnameinfo((struct sockaddr *) ss, ss_len,
        !           220:                               name_buf, name_buf_size,
        !           221:                               port_buf, port_buf_size,
        !           222:                               NI_NAMEREQD | NI_NUMERICSERV);
        !           223:        if (name_err != 0) {
        !           224:                strlcpy(name_buf, default_name, name_buf_size);
        !           225:                rprintf(FLOG, "name lookup failed for %s: %s\n",
        !           226:                        client_addr(fd), gai_strerror(name_err));
        !           227:                return name_err;
        !           228:        }
        !           229: 
        !           230:        return 0;
        !           231: }
        !           232: 
        !           233: 
        !           234: 
        !           235: /**
        !           236:  * Compare an addrinfo from the resolver to a sockinfo.
        !           237:  *
        !           238:  * Like strcmp, returns 0 for identical.
        !           239:  **/
        !           240: int compare_addrinfo_sockaddr(const struct addrinfo *ai,
        !           241:                              const struct sockaddr_storage *ss)
        !           242: {
        !           243:        int ss_family = get_sockaddr_family(ss);
        !           244:        const char fn[] = "compare_addrinfo_sockaddr";
        !           245: 
        !           246:        if (ai->ai_family != ss_family) {
        !           247:                rprintf(FLOG, "%s: response family %d != %d\n",
        !           248:                        fn, ai->ai_family, ss_family);
        !           249:                return 1;
        !           250:        }
        !           251: 
        !           252:        /* The comparison method depends on the particular AF. */
        !           253:        if (ss_family == AF_INET) {
        !           254:                const struct sockaddr_in *sin1, *sin2;
        !           255: 
        !           256:                sin1 = (const struct sockaddr_in *) ss;
        !           257:                sin2 = (const struct sockaddr_in *) ai->ai_addr;
        !           258: 
        !           259:                return memcmp(&sin1->sin_addr, &sin2->sin_addr,
        !           260:                              sizeof sin1->sin_addr);
        !           261:        }
        !           262: 
        !           263: #ifdef INET6
        !           264:        if (ss_family == AF_INET6) {
        !           265:                const struct sockaddr_in6 *sin1, *sin2;
        !           266: 
        !           267:                sin1 = (const struct sockaddr_in6 *) ss;
        !           268:                sin2 = (const struct sockaddr_in6 *) ai->ai_addr;
        !           269: 
        !           270:                if (ai->ai_addrlen < sizeof (struct sockaddr_in6)) {
        !           271:                        rprintf(FLOG, "%s: too short sockaddr_in6; length=%d\n",
        !           272:                                fn, (int)ai->ai_addrlen);
        !           273:                        return 1;
        !           274:                }
        !           275: 
        !           276:                if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
        !           277:                           sizeof sin1->sin6_addr))
        !           278:                        return 1;
        !           279: 
        !           280: #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
        !           281:                if (sin1->sin6_scope_id != sin2->sin6_scope_id)
        !           282:                        return 1;
        !           283: #endif
        !           284:                return 0;
        !           285:        }
        !           286: #endif /* INET6 */
        !           287: 
        !           288:        /* don't know */
        !           289:        return 1;
        !           290: }
        !           291: 
        !           292: 
        !           293: /**
        !           294:  * Do a forward lookup on @p name_buf and make sure it corresponds to
        !           295:  * @p ss -- otherwise we may be being spoofed.  If we suspect we are,
        !           296:  * then we don't abort the connection but just emit a warning, and
        !           297:  * change @p name_buf to be "UNKNOWN".
        !           298:  *
        !           299:  * We don't do anything with the service when checking the name,
        !           300:  * because it doesn't seem that it could be spoofed in any way, and
        !           301:  * getaddrinfo on random service names seems to cause problems on AIX.
        !           302:  **/
        !           303: int check_name(int fd,
        !           304:               const struct sockaddr_storage *ss,
        !           305:               char *name_buf, size_t name_buf_size)
        !           306: {
        !           307:        struct addrinfo hints, *res, *res0;
        !           308:        int error;
        !           309:        int ss_family = get_sockaddr_family(ss);
        !           310: 
        !           311:        memset(&hints, 0, sizeof hints);
        !           312:        hints.ai_family = ss_family;
        !           313:        hints.ai_flags = AI_CANONNAME;
        !           314:        hints.ai_socktype = SOCK_STREAM;
        !           315:        error = getaddrinfo(name_buf, NULL, &hints, &res0);
        !           316:        if (error) {
        !           317:                rprintf(FLOG, "forward name lookup for %s failed: %s\n",
        !           318:                        name_buf, gai_strerror(error));
        !           319:                strlcpy(name_buf, default_name, name_buf_size);
        !           320:                return error;
        !           321:        }
        !           322: 
        !           323:        /* Given all these results, we expect that one of them will be
        !           324:         * the same as ss.  The comparison is a bit complicated. */
        !           325:        for (res = res0; res; res = res->ai_next) {
        !           326:                if (!compare_addrinfo_sockaddr(res, ss))
        !           327:                        break;  /* OK, identical */
        !           328:        }
        !           329: 
        !           330:        if (!res0) {
        !           331:                /* We hit the end of the list without finding an
        !           332:                 * address that was the same as ss. */
        !           333:                rprintf(FLOG, "no known address for \"%s\": "
        !           334:                        "spoofed address?\n", name_buf);
        !           335:                strlcpy(name_buf, default_name, name_buf_size);
        !           336:        } else if (res == NULL) {
        !           337:                /* We hit the end of the list without finding an
        !           338:                 * address that was the same as ss. */
        !           339:                rprintf(FLOG, "%s is not a known address for \"%s\": "
        !           340:                        "spoofed address?\n", client_addr(fd), name_buf);
        !           341:                strlcpy(name_buf, default_name, name_buf_size);
        !           342:        }
        !           343: 
        !           344:        freeaddrinfo(res0);
        !           345:        return 0;
        !           346: }

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