Logo Search packages:      
Sourcecode: heimdal version File versions

common.c

/*
 * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
 * (Royal Institute of Technology, Stockholm, Sweden).
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "kx.h"

RCSID("$Id: common.c,v 1.68 2003/04/16 16:45:39 joda Exp $");

char x_socket[MaxPathLen];

u_int32_t display_num;
char display[MaxPathLen];
int display_size = sizeof(display);
char xauthfile[MaxPathLen];
int xauthfile_size = sizeof(xauthfile);
u_char cookie[16];
size_t cookie_len = sizeof(cookie);

#ifndef X_UNIX_PATH
#define X_UNIX_PATH "/tmp/.X11-unix/X"
#endif

#ifndef X_PIPE_PATH
#define X_PIPE_PATH "/tmp/.X11-pipe/X"
#endif

/*
 * Allocate a unix domain socket in `s' for display `dpy' and with
 * filename `pattern'
 *
 * 0 if all is OK
 * -1 if bind failed badly
 * 1 if dpy is already used */

static int
try_socket (struct x_socket *s, int dpy, const char *pattern)
{
    struct sockaddr_un addr;
    int fd;

    fd = socket (AF_UNIX, SOCK_STREAM, 0);
    if (fd < 0)
      err (1, "socket AF_UNIX");
    memset (&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    snprintf (addr.sun_path, sizeof(addr.sun_path), pattern, dpy);
    if(bind(fd,
          (struct sockaddr *)&addr,
          sizeof(addr)) < 0) {
      close (fd);
      if (errno == EADDRINUSE ||
          errno == EACCES  /* Cray return EACCESS */
#ifdef ENOTUNIQ
          || errno == ENOTUNIQ /* bug in Solaris 2.4 */
#endif
          )
          return 1;
      else
          return -1;
    }
    s->fd = fd;
    s->pathname = strdup (addr.sun_path);
    if (s->pathname == NULL)
      errx (1, "strdup: out of memory");
    s->flags = UNIX_SOCKET;
    return 0;
}

#ifdef MAY_HAVE_X11_PIPES
/*
 * Allocate a stream (masqueraded as a named pipe)
 *
 * 0 if all is OK
 * -1 if bind failed badly
 * 1 if dpy is already used
 */

static int
try_pipe (struct x_socket *s, int dpy, const char *pattern)
{
    char path[MAXPATHLEN];
    int ret;
    int fd;
    int pipefd[2];
    
    snprintf (path, sizeof(path), pattern, dpy);
    fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600);
    if (fd < 0) {
      if (errno == EEXIST)
          return 1;
      else
          return -1;
    }

    close (fd);

    ret = pipe (pipefd);
    if (ret < 0)
      err (1, "pipe");

    ret = ioctl (pipefd[1], I_PUSH, "connld");
    if (ret < 0) {
      if(errno == ENOSYS)
          return -1;
      err (1, "ioctl I_PUSH");
    }

    ret = fattach (pipefd[1], path);
    if (ret < 0)
      err (1, "fattach %s", path);

    s->fd  = pipefd[0];
    close (pipefd[1]);
    s->pathname = strdup (path);
    if (s->pathname == NULL)
      errx (1, "strdup: out of memory");
    s->flags = STREAM_PIPE;
    return 0;
}
#endif /* MAY_HAVE_X11_PIPES */

/*
 * Try to create a TCP socket in `s' corresponding to display `dpy'.
 *
 * 0 if all is OK
 * -1 if bind failed badly
 * 1 if dpy is already used
 */

static int
try_tcp (struct x_socket *s, int dpy)
{
    struct sockaddr_in tcpaddr;
    struct in_addr local;
    int one = 1;
    int fd;

    memset(&local, 0, sizeof(local));
    local.s_addr = htonl(INADDR_LOOPBACK);

    fd = socket (AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
      err (1, "socket AF_INET");
#if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
    setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&one,
            sizeof(one));
#endif
    memset (&tcpaddr, 0, sizeof(tcpaddr));
    tcpaddr.sin_family = AF_INET;
    tcpaddr.sin_addr = local;
    tcpaddr.sin_port = htons(6000 + dpy);
    if (bind (fd, (struct sockaddr *)&tcpaddr,
            sizeof(tcpaddr)) < 0) {
      close (fd);
      if (errno == EADDRINUSE)
          return 1;
      else
          return -1;
    }
    s->fd = fd;
    s->pathname = NULL;
    s->flags = TCP;
    return 0;
}

/*
 * The potential places to create unix sockets.
 */

static char *x_sockets[] = {
X_UNIX_PATH "%u",
"/var/X/.X11-unix/X" "%u",
"/usr/spool/sockets/X11/" "%u",
NULL
};

/*
 * Dito for stream pipes.
 */

#ifdef MAY_HAVE_X11_PIPES
static char *x_pipes[] = {
X_PIPE_PATH "%u",
"/var/X/.X11-pipe/X" "%u",
NULL
};
#endif

/*
 * Create the directory corresponding to dirname of `path' or fail.
 */

static void
try_mkdir (const char *path)
{
    char *dir;
    char *p;
    int oldmask;

    if((dir = strdup (path)) == NULL)
      errx (1, "strdup: out of memory");
    p = strrchr (dir, '/');
    if (p)
      *p = '\0';

    oldmask = umask(0);
    mkdir (dir, 01777);
    umask (oldmask);
    free (dir);
}

/*
 * Allocate a display, returning the number of sockets in `number' and
 * all the corresponding sockets in `sockets'.  If `tcp_socket' is
 * true, also allcoaet a TCP socket.
 *
 * The return value is the display allocated or -1 if an error occurred.
 */

int
get_xsockets (int *number, struct x_socket **sockets, int tcp_socket)
{
     int dpy;
     struct x_socket *s;
     int n;
     int i;

     s = malloc (sizeof(*s) * 5);
     if (s == NULL)
       errx (1, "malloc: out of memory");

     try_mkdir (X_UNIX_PATH);
     try_mkdir (X_PIPE_PATH);

     for(dpy = 4; dpy < 256; ++dpy) {
       char **path;
       int tmp = 0;

       n = 0;
       for (path = x_sockets; *path; ++path) {
           tmp = try_socket (&s[n], dpy, *path);
           if (tmp == -1) {
             if (errno != ENOTDIR && errno != ENOENT)
                 return -1;
           } else if (tmp == 1) {
             while(--n >= 0) {
                 close (s[n].fd);
                 free (s[n].pathname);
             }
             break;
           } else if (tmp == 0)
             ++n;
       }
       if (tmp == 1)
           continue;

#ifdef MAY_HAVE_X11_PIPES
       for (path = x_pipes; *path; ++path) {
           tmp = try_pipe (&s[n], dpy, *path);
           if (tmp == -1) {
             if (errno != ENOTDIR && errno != ENOENT && errno != ENOSYS)
                 return -1;
           } else if (tmp == 1) {
             while (--n >= 0) {
                 close (s[n].fd);
                 free (s[n].pathname);
             }
             break;
           } else if (tmp == 0)
             ++n;
       }

       if (tmp == 1)
           continue;
#endif

       if (tcp_socket) {
           tmp = try_tcp (&s[n], dpy);
           if (tmp == -1)
             return -1;
           else if (tmp == 1) {
             while (--n >= 0) {
                 close (s[n].fd);
                 free (s[n].pathname);
             }
             break;
           } else if (tmp == 0)
             ++n;
       }
       break;
     }
     if (dpy == 256)
       errx (1, "no free x-servers");
     for (i = 0; i < n; ++i)
       if (s[i].flags & LISTENP
           && listen (s[i].fd, SOMAXCONN) < 0)
           err (1, "listen %s", s[i].pathname ? s[i].pathname : "tcp");
     *number = n;
     *sockets = s;
     return dpy;
}

/*
 * Change owner on the `n' sockets in `sockets' to `uid', `gid'.
 * Return 0 is succesful or -1 if an error occurred.
 */

int
chown_xsockets (int n, struct x_socket *sockets, uid_t uid, gid_t gid)
{
    int i;

    for (i = 0; i < n; ++i)
      if (sockets[i].pathname != NULL)
          if (chown (sockets[i].pathname, uid, gid) < 0)
            return -1;
    return 0;
}

/*
 * Connect to local display `dnr' with local transport or TCP.
 * Return a file descriptor.
 */

int
connect_local_xsocket (unsigned dnr)
{
     int fd;
     char **path;

     for (path = x_sockets; *path; ++path) {
       struct sockaddr_un addr;

       fd = socket (AF_UNIX, SOCK_STREAM, 0);
       if (fd < 0)
           break;
       memset (&addr, 0, sizeof(addr));
       addr.sun_family = AF_UNIX;
       snprintf (addr.sun_path, sizeof(addr.sun_path), *path, dnr);
       if (connect (fd, (struct sockaddr *)&addr, sizeof(addr)) == 0)
           return fd;
       close(fd);
     }
     {
       struct sockaddr_in addr;

       fd = socket(AF_INET, SOCK_STREAM, 0);
       if (fd < 0)
           err (1, "socket AF_INET");
       memset (&addr, 0, sizeof(addr));
       addr.sin_family = AF_INET;
       addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
       addr.sin_port = htons(6000 + dnr);
       if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0)
           return fd;
       close(fd);
     }
     err (1, "connecting to local display %u", dnr);
}

/*
 * Create a cookie file with a random cookie for the localhost.  The
 * file name will be stored in `xauthfile' (but not larger than
 * `xauthfile_size'), and the cookie returned in `cookie', `cookie_sz'.
 * Return 0 if succesful, or errno.
 */

int
create_and_write_cookie (char *xauthfile,
                   size_t xauthfile_size,
                   u_char *cookie,
                   size_t cookie_sz)
{
     Xauth auth;
     char tmp[64];
     int fd;
     FILE *f;
     char hostname[MaxHostNameLen];
     int saved_errno;

     gethostname (hostname, sizeof(hostname));
     
     auth.family = FamilyLocal;
     auth.address = hostname;
     auth.address_length = strlen(auth.address);
     snprintf (tmp, sizeof(tmp), "%d", display_num);
     auth.number_length = strlen(tmp);
     auth.number = tmp;
     auth.name = COOKIE_TYPE;
     auth.name_length = strlen(auth.name);
     auth.data_length = cookie_sz;
     auth.data = (char*)cookie;
#ifdef KRB5
     krb5_generate_random_block (cookie, cookie_sz);
#else
     krb_generate_random_block (cookie, cookie_sz);
#endif

     strlcpy(xauthfile, "/tmp/AXXXXXX", xauthfile_size);
     fd = mkstemp(xauthfile);
     if(fd < 0) {
       saved_errno = errno;
       syslog(LOG_ERR, "create_and_write_cookie: mkstemp: %m");
         return saved_errno;
     }
     f = fdopen(fd, "r+");
     if(f == NULL){
       saved_errno = errno;
       close(fd);
       return errno;
     }
     if(XauWriteAuth(f, &auth) == 0) {
       saved_errno = errno;
       fclose(f);
       return saved_errno;
     }

     /*
      * I would like to write a cookie for localhost:n here, but some
      * stupid code in libX11 will not look for cookies of that type,
      * so we are forced to use FamilyWild instead.
      */

     auth.family  = FamilyWild;
     auth.address_length = 0;

     if (XauWriteAuth(f, &auth) == 0) {
       saved_errno = errno;
       fclose (f);
       return saved_errno;
     }

     if(fclose(f))
       return errno;
     return 0;
}

/*
 * Verify and remove cookies.  Read and parse a X-connection from
 * `fd'. Check the cookie used is the same as in `cookie'.  Remove the
 * cookie and copy the rest of it to `sock'.
 * Expect cookies iff cookiesp.
 * Return 0 iff ok.
 *
 * The protocol is as follows:
 *
 * C->S:    [Bl]                    1
 *          unused                        1
 *          protocol major version        2
 *          protocol minor version        2
 *          length of auth protocol name(n)     2
 *          length of auth protocol data  2
 *          unused                        2
 *          authorization protocol name   n
 *          pad                     pad(n)
 *          authorization protocol data   d
 *          pad                     pad(d)
 *
 * S->C:    Failed
 *          0                       1
 *          length of reason        1
 *          protocol major version        2
 *          protocol minor version        2
 *          length in 4 bytes unit of
 *          additional data (n+p)/4       2
 *          reason                        n
 *          unused                        p = pad(n)
 */

int
verify_and_remove_cookies (int fd, int sock, int cookiesp)
{
     u_char beg[12];
     int bigendianp;
     unsigned n, d, npad, dpad;
     char *protocol_name, *protocol_data;
     u_char zeros[6] = {0, 0, 0, 0, 0, 0};
     u_char refused[20] = {0, 10, 
                     0, 0, /* protocol major version  */
                     0, 0, /* protocol minor version */
                     0, 0, /* length of additional data / 4 */
                     'b', 'a', 'd', ' ', 'c', 'o', 'o', 'k', 'i', 'e',
                     0, 0};

     if (net_read (fd, beg, sizeof(beg)) != sizeof(beg))
        return 1;
     if (net_write (sock, beg, 6) != 6)
        return 1;
     bigendianp = beg[0] == 'B';
     if (bigendianp) {
        n = (beg[6] << 8) | beg[7];
        d = (beg[8] << 8) | beg[9];
     } else {
        n = (beg[7] << 8) | beg[6];
        d = (beg[9] << 8) | beg[8];
     }
     npad = (4 - (n % 4)) % 4;
     dpad = (4 - (d % 4)) % 4;
     protocol_name = malloc(n + npad);
     if (n + npad != 0 && protocol_name == NULL)
       return 1;
     protocol_data = malloc(d + dpad);
     if (d + dpad != 0 && protocol_data == NULL) {
       free (protocol_name);
       return 1;
     }
     if (net_read (fd, protocol_name, n + npad) != n + npad)
       goto fail;
     if (net_read (fd, protocol_data, d + dpad) != d + dpad)
       goto fail;
     if (cookiesp) {
       if (strncmp (protocol_name, COOKIE_TYPE, strlen(COOKIE_TYPE)) != 0)
           goto refused;
       if (d != cookie_len ||
           memcmp (protocol_data, cookie, cookie_len) != 0)
           goto refused;
     }
     free (protocol_name);
     free (protocol_data);
     if (net_write (sock, zeros, 6) != 6)
        return 1;
     return 0;
refused:
     refused[2] = beg[2];
     refused[3] = beg[3];
     refused[4] = beg[4];
     refused[5] = beg[5];
     if (bigendianp)
       refused[7] = 3;
     else
       refused[6] = 3;

     net_write (fd, refused, sizeof(refused));
fail:
     free (protocol_name);
     free (protocol_data);
     return 1;
}

/* 
 * Return 0 iff `cookie' is compatible with the cookie for the
 * localhost with name given in `ai' (or `hostname') and display
 * number in `disp_nr'.
 */

static int
match_local_auth (Xauth* auth,
              struct addrinfo *ai, const char *hostname, int disp_nr)
{
    int auth_disp;
    char *tmp_disp;
    struct addrinfo *a;
    
    tmp_disp = malloc(auth->number_length + 1);
    if (tmp_disp == NULL)
      return -1;
    memcpy(tmp_disp, auth->number, auth->number_length);
    tmp_disp[auth->number_length] = '\0';
    auth_disp = atoi(tmp_disp);
    free (tmp_disp);
    if (auth_disp != disp_nr)
      return 1;
    for (a = ai; a != NULL; a = a->ai_next) {
      if ((auth->family == FamilyLocal
           || auth->family == FamilyWild)
          && a->ai_canonname != NULL
          && strncmp (auth->address,
                  a->ai_canonname,
                  auth->address_length) == 0)
          return 0;
    }
    if (hostname != NULL
      && (auth->family    == FamilyLocal
          || auth->family == FamilyWild)
      && strncmp (auth->address, hostname, auth->address_length) == 0)
      return 0;
    return 1;
}

/*
 * Find `our' cookie from the cookie file `f' and return it or NULL.
 */

static Xauth*
find_auth_cookie (FILE *f)
{
    Xauth *ret = NULL;
    char local_hostname[MaxHostNameLen];
    char *display = getenv("DISPLAY");
    char d[MaxHostNameLen + 4];
    char *colon;
    struct addrinfo *ai;
    struct addrinfo hints;
    int disp;
    int error;

    if(display == NULL)
      display = ":0";
    strlcpy(d, display, sizeof(d));
    display = d;
    colon = strchr (display, ':');
    if (colon == NULL)
      disp = 0;
    else {
      *colon = '\0';
      disp = atoi (colon + 1);
    }
    if (strcmp (display, "") == 0
      || strncmp (display, "unix", 4) == 0
      || strncmp (display, "localhost", 9) == 0) {
      gethostname (local_hostname, sizeof(local_hostname));
      display = local_hostname;
    }
    memset (&hints, 0, sizeof(hints));
    hints.ai_flags    = AI_CANONNAME;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    error = getaddrinfo (display, NULL, &hints, &ai);
    if (error)
      ai = NULL;

    for (; (ret = XauReadAuth (f)) != NULL; XauDisposeAuth(ret)) {
      if (match_local_auth (ret, ai, display, disp) == 0) {
          if (ai != NULL)
            freeaddrinfo (ai);
          return ret;
      }
    }
    if (ai != NULL)
      freeaddrinfo (ai);
    return NULL;
}

/*
 * Get rid of the cookie that we were sent and get the correct one
 * from our own cookie file instead.
 */

int
replace_cookie(int xserver, int fd, char *filename, int cookiesp) /* XXX */
{
     u_char beg[12];
     int bigendianp;
     unsigned n, d, npad, dpad;
     FILE *f;
     u_char zeros[6] = {0, 0, 0, 0, 0, 0};

     if (net_read (fd, beg, sizeof(beg)) != sizeof(beg))
        return 1;
     if (net_write (xserver, beg, 6) != 6)
        return 1;
     bigendianp = beg[0] == 'B';
     if (bigendianp) {
        n = (beg[6] << 8) | beg[7];
        d = (beg[8] << 8) | beg[9];
     } else {
        n = (beg[7] << 8) | beg[6];
        d = (beg[9] << 8) | beg[8];
     }
     if (n != 0 || d != 0)
        return 1;
     f = fopen(filename, "r");
     if (f != NULL) {
       Xauth *auth = find_auth_cookie (f);
       u_char len[6] = {0, 0, 0, 0, 0, 0};
       
       fclose (f);

       if (auth != NULL) {
           n = auth->name_length;
           d = auth->data_length;
       } else {
           n = 0;
           d = 0;
       }
       if (bigendianp) {
           len[0] = n >> 8;
           len[1] = n & 0xFF;
           len[2] = d >> 8;
           len[3] = d & 0xFF;
       } else {
           len[0] = n & 0xFF;
           len[1] = n >> 8;
           len[2] = d & 0xFF;
           len[3] = d >> 8;
       }
       if (net_write (xserver, len, 6) != 6) {
           XauDisposeAuth(auth);
           return 1;
       }
       if(n != 0 && net_write (xserver, auth->name, n) != n) {
           XauDisposeAuth(auth);
           return 1;
       }
       npad = (4 - (n % 4)) % 4;
       if (npad && net_write (xserver, zeros, npad) != npad) {
           XauDisposeAuth(auth);
           return 1;
       }
       if (d != 0 && net_write (xserver, auth->data, d) != d) {
           XauDisposeAuth(auth);
           return 1;
       }
       XauDisposeAuth(auth);
       dpad = (4 - (d % 4)) % 4;
       if (dpad && net_write (xserver, zeros, dpad) != dpad)
           return 1;
     } else {
       if(net_write(xserver, zeros, 6) != 6)
           return 1;
     }
     return 0;
}

/*
 * Some simple controls on the address and corresponding socket
 */

int
suspicious_address (int sock, struct sockaddr *addr)
{
    char data[40];
    socklen_t len = sizeof(data);

    switch (addr->sa_family) {
    case AF_INET:
      return ((struct sockaddr_in *)addr)->sin_addr.s_addr != 
          htonl(INADDR_LOOPBACK)
#if defined(IP_OPTIONS) && defined(HAVE_GETSOCKOPT)
          || getsockopt (sock, IPPROTO_IP, IP_OPTIONS, data, &len) < 0
          || len != 0
#endif
          ;
      break;
#ifdef HAVE_IPV6
    case AF_INET6:
      /* XXX check route headers */
      return !IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6*)addr)->sin6_addr);
#endif
    default:
      return 1;
    }
}

/*
 * This really sucks, but these functions are used and if we're not
 * linking against libkrb they don't exist.  Using the heimdal storage
 * functions will not work either cause we do not always link with
 * libkrb5 either.
 */

#ifndef KRB4

int
krb_get_int(void *f, u_int32_t *to, int size, int lsb)
{
    int i;
    unsigned char *from = (unsigned char *)f;

    *to = 0;
    if(lsb){
      for(i = size-1; i >= 0; i--)
          *to = (*to << 8) | from[i];
    }else{
      for(i = 0; i < size; i++)
          *to = (*to << 8) | from[i];
    }
    return size;
}

int
krb_put_int(u_int32_t from, void *to, size_t rem, int size)
{
    int i;
    unsigned char *p = (unsigned char *)to;

    if (rem < size)
      return -1;

    for(i = size - 1; i >= 0; i--){
      p[i] = from & 0xff;
      from >>= 8;
    }
    return size;
}

#endif /* !KRB4 */

Generated by  Doxygen 1.6.0   Back to index