diff options
Diffstat (limited to 'crypto/heimdal/appl/kx/common.c')
-rw-r--r-- | crypto/heimdal/appl/kx/common.c | 794 |
1 files changed, 794 insertions, 0 deletions
diff --git a/crypto/heimdal/appl/kx/common.c b/crypto/heimdal/appl/kx/common.c new file mode 100644 index 0000000..0d23169 --- /dev/null +++ b/crypto/heimdal/appl/kx/common.c @@ -0,0 +1,794 @@ +/* + * 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.62 2001/02/15 04:20:51 assar 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. + * Return a file descriptor. + */ + +int +connect_local_xsocket (unsigned dnr) +{ + int fd; + struct sockaddr_un addr; + char **path; + + for (path = x_sockets; *path; ++path) { + 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), *path, dnr); + if (connect (fd, (struct sockaddr *)&addr, sizeof(addr)) == 0) + return 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]; + struct in_addr loopback; + int saved_errno; + + gethostname (hostname, sizeof(hostname)); + loopback.s_addr = htonl(INADDR_LOOPBACK); + + 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 HAVE_OPENSSL_DES_H + krb5_generate_random_block (cookie, cookie_sz); +#else + des_rand_data (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 0 /* XXX */ + auth.address = (char *)&loopback; + auth.address_length = sizeof(loopback); +#endif + + 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 = strndup (auth->number, auth->number_length); + if (tmp_disp == NULL) + return -1; + 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_in addr) +{ + char data[40]; + socklen_t len = sizeof(data); + + return 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 + ; +} + +/* + * 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 */ |