"Fossies" - the Fresh Open Source Software Archive

Member "putty-0.73/unix/procnet.c" (22 Sep 2019, 6862 Bytes) of package /linux/misc/putty-0.73.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "procnet.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Locally authenticate a TCP socket via /proc/net.
    3  *
    4  * Obviously, if a TCP connection comes from a different host, there's
    5  * no way to find out the identity of the thing at the other end (or
    6  * even really to assign that concept a meaning) except by the usual
    7  * method of speaking a protocol over the socket itself which involves
    8  * some form of (preferably cryptographic) authentication exchange.
    9  *
   10  * But if the connection comes from localhost, then on at least some
   11  * operating systems, you can do better. On Linux, /proc/net/tcp and
   12  * /proc/net/tcp6 list the full set of active TCP connection
   13  * endpoints, and they list an owning uid for each one. So once you've
   14  * accepted a connection to a listening socket and found that the
   15  * other end of it is a localhost address, you can look up the _other_
   16  * endpoint in the right one of those files, and find out which uid
   17  * owns it.
   18  */
   19 
   20 #include <ctype.h>
   21 #include <stddef.h>
   22 #include <stdio.h>
   23 
   24 #include <unistd.h>
   25 #include <sys/types.h>
   26 #include <sys/socket.h>
   27 #include <arpa/inet.h>
   28 #include <netinet/in.h>
   29 
   30 #include "misc.h"
   31 
   32 static ptrlen get_space_separated_field(ptrlen *string)
   33 {
   34     const char *p = string->ptr, *end = p + string->len;
   35 
   36     while (p < end && isspace((unsigned char)*p))
   37         p++;
   38     if (p == end)
   39         return PTRLEN_LITERAL("");
   40 
   41     const char *start = p;
   42     while (p < end && !isspace((unsigned char)*p))
   43         p++;
   44     *string = make_ptrlen(p, end - p);
   45     return make_ptrlen(start, p - start);
   46 }
   47 
   48 enum { GOT_LOCAL_UID = 1, GOT_REMOTE_UID = 2 };
   49 
   50 /*
   51  * Open a file formatted like /proc/net/tcp{,6}, and search it for
   52  * both ends of a particular connection.
   53  *
   54  * The operands 'local' and 'remote' give the expected string
   55  * representations of the local and remote addresses of the connection
   56  * we're looking for.
   57  *
   58  * Return value is the bitwise OR of 1 if we found the local end of
   59  * the connection and 2 if we found the remote. Each output uid_t
   60  * parameter is filled in iff the corresponding bit is set in the
   61  * return value.
   62  */
   63 static int lookup_uids_in_procnet_file(
   64     const char *path, ptrlen local, ptrlen remote,
   65     uid_t *local_uid, uid_t *remote_uid)
   66 {
   67     FILE *fp = NULL;
   68     int toret = 0;
   69     ptrlen line, field;
   70 
   71     enum { GF_LOCAL = 1, GF_REMOTE = 2, GF_UID = 4 };
   72 
   73     fp = fopen(path, "r");
   74     if (!fp)
   75         goto out;
   76 
   77     /* Expected indices of fields in /proc/net/tcp* */
   78     const int LOCAL_ADDR_INDEX = 1;
   79     const int REMOTE_ADDR_INDEX = 2;
   80     const int UID_INDEX = 7;
   81 
   82     for (char *linez; (linez = chomp(fgetline(fp))) != NULL ;) {
   83         line = ptrlen_from_asciz(linez);
   84         int gotfields = 0;
   85         ptrlen local_addr = PTRLEN_LITERAL("");
   86         ptrlen remote_addr = PTRLEN_LITERAL("");
   87         long uid = -1;
   88 
   89         for (int i = 0; (field = get_space_separated_field(&line)).len != 0;
   90              i++) {
   91 
   92             if (i == LOCAL_ADDR_INDEX) {
   93                 gotfields |= GF_LOCAL;
   94                 local_addr = field;
   95             } else if (i == REMOTE_ADDR_INDEX) {
   96                 gotfields |= GF_REMOTE;
   97                 remote_addr = field;
   98             } else if (i == UID_INDEX) {
   99                 uid = 0;
  100                 for (const char *p = field.ptr, *end = p + field.len;
  101                      p < end; p++) {
  102                     if (!isdigit((unsigned char)*p)) {
  103                         uid = -1;
  104                         break;
  105                     }
  106                     int dval = *p - '0';
  107                     if (uid > LONG_MAX/10) {
  108                         uid = -1;
  109                         break;
  110                     }
  111                     uid *= 10;
  112                     if (uid > LONG_MAX - dval) {
  113                         uid = -1;
  114                         break;
  115                     }
  116                     uid += dval;
  117                 }
  118 
  119                 gotfields |= GF_UID;
  120             }
  121         }
  122 
  123         if (gotfields == (GF_LOCAL | GF_REMOTE | GF_UID)) {
  124             if (ptrlen_eq_ptrlen(local_addr, local) &&
  125                 ptrlen_eq_ptrlen(remote_addr, remote)) {
  126                 *local_uid = uid;
  127                 toret |= GOT_LOCAL_UID;
  128             }
  129             if (ptrlen_eq_ptrlen(local_addr, remote) &&
  130                 ptrlen_eq_ptrlen(remote_addr, local)) {
  131                 *remote_uid = uid;
  132                 toret |= GOT_REMOTE_UID;
  133             }
  134         }
  135 
  136         sfree(linez);
  137     }
  138 
  139     fclose(fp);
  140     fp = NULL;
  141 
  142   out:
  143     if (fp)
  144         fclose(fp);
  145     return toret;
  146 }
  147 
  148 static const char *procnet_path(int family)
  149 {
  150     switch (family) {
  151       case AF_INET: return "/proc/net/tcp";
  152       case AF_INET6: return "/proc/net/tcp6";
  153       default: return NULL;
  154     }
  155 }
  156 
  157 static char *format_sockaddr(const void *addr, int family)
  158 {
  159     if (family == AF_INET) {
  160         const struct sockaddr_in *a = (const struct sockaddr_in *)addr;
  161         assert(a->sin_family == family);
  162         /* Linux /proc/net formats the IP address native-endian, so we
  163          * don't use ntohl */
  164         return dupprintf("%08X:%04X", a->sin_addr.s_addr, ntohs(a->sin_port));
  165     } else if (family == AF_INET6) {
  166         struct sockaddr_in6 *a = (struct sockaddr_in6 *)addr;
  167         assert(a->sin6_family == family);
  168 
  169         strbuf *sb = strbuf_new();
  170 
  171         const uint32_t *addrwords = (const uint32_t *)a->sin6_addr.s6_addr;
  172         for (int i = 0; i < 4; i++)
  173             strbuf_catf(sb, "%08X", addrwords[i]);
  174         strbuf_catf(sb, ":%04X", ntohs(a->sin6_port));
  175 
  176         return strbuf_to_str(sb);
  177     } else {
  178         return NULL;
  179     }
  180 }
  181 
  182 bool socket_peer_is_same_user(int fd)
  183 {
  184     struct sockaddr_storage addr;
  185     socklen_t addrlen;
  186     int family;
  187     bool toret = false;
  188     char *local = NULL, *remote = NULL;
  189     const char *path;
  190 
  191     addrlen = sizeof(addr);
  192     if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
  193         goto out;
  194     family = addr.ss_family;
  195     if ((path = procnet_path(family)) == NULL)
  196         goto out;
  197     local = format_sockaddr(&addr, family);
  198     if (!local)
  199         goto out;
  200 
  201     addrlen = sizeof(addr);
  202     if (getpeername(fd, (struct sockaddr *)&addr, &addrlen) != 0)
  203         goto out;
  204     if (addr.ss_family != family)
  205         goto out;
  206     remote = format_sockaddr(&addr, family);
  207     if (!remote)
  208         goto out;
  209 
  210     ptrlen locpl = ptrlen_from_asciz(local);
  211     ptrlen rempl = ptrlen_from_asciz(remote);
  212 
  213     /*
  214      * Check that _both_ end of the socket are the uid we expect, as a
  215      * sanity check on the /proc/net file being reasonable at all.
  216      */
  217     uid_t our_uid = getuid();
  218     uid_t local_uid = -1, remote_uid = -1;
  219     int got = lookup_uids_in_procnet_file(
  220         path, locpl, rempl, &local_uid, &remote_uid);
  221     if (got == (GOT_LOCAL_UID | GOT_REMOTE_UID) &&
  222         local_uid == our_uid && remote_uid == our_uid)
  223         toret = true;
  224 
  225   out:
  226     sfree(local);
  227     sfree(remote);
  228     return toret;
  229 }