Files
heimdal/appl/kx/common.c
Assar Westerlund 65423ced9d (match_local_auth): only look for FamilyLocal (and FamilyWild)
cookies.  This will not work when we start talking tcp to the local
X-server but `connect_local_xsocket' and the rest of the code doesn't
handle it anyway and the old code could (and did) pick up the wrong
cookie sometimes.  If we have to match FamilyInternet cookies, the
search order has to be changed anyway


git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@7058 ec53bebd-3082-4978-b11e-865c3cabbd6b
1999-09-29 17:03:21 +00:00

786 lines
18 KiB
C

/*
* Copyright (c) 1995, 1996, 1997, 1998, 1999 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Kungliga Tekniska
* Högskolan and its contributors.
*
* 4. 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$");
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
#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK 0x7f000001
#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;
des_rand_data (cookie, cookie_sz);
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 `disp_he' and display number in
* `disp_nr'.
*/
static int
match_local_auth (Xauth* auth, struct hostent *disp_he, int disp_nr)
{
int auth_disp;
char *tmp_disp;
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;
if (auth->family == FamilyLocal
|| auth->family == FamilyWild) {
int i;
if (strncmp (auth->address,
disp_he->h_name,
auth->address_length) == 0)
return 0;
for (i = 0; disp_he->h_aliases[i] != NULL; ++i)
if (strncmp (auth->address,
disp_he->h_aliases[i],
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 *colon;
struct hostent *display_he;
int disp;
if (display == NULL)
display = ":0";
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;
}
display_he = gethostbyname (display);
if (display_he == NULL) {
warnx ("gethostbyname %s: %s", display, hstrerror(h_errno));
return NULL;
}
for (; (ret = XauReadAuth (f)) != NULL; XauDisposeAuth(ret)) {
if (match_local_auth (ret, display_he, disp) == 0)
return ret;
}
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];
int 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 */