xorg-libx11/src/ConnDis.c
Alan Coopersmith abcc7e1865 When opening display, if LOCALCONN fails, fall back to UNIXCONN, then TCPCONN
Port to X11R7 of Sun bug fix 4061225 by Alex Chen for X11R6 - when failing to
connect on a named pipe, try a Unix socket first, to better support people who
replace their X servers with ones that don't support named pipe transport.
<http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=4061225>
2006-08-23 18:49:30 -07:00

1252 lines
32 KiB
C

/* $XdotOrg: lib/X11/src/ConnDis.c,v 1.10 2005-07-03 07:00:55 daniels Exp $ */
/* $Xorg: ConnDis.c,v 1.8 2001/02/09 02:03:31 xorgcvs Exp $ */
/*
Copyright 1989, 1998 The Open Group
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of The Open Group shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from The Open Group.
*/
/* $XFree86: xc/lib/X11/ConnDis.c,v 3.28 2003/12/02 23:33:17 herrb Exp $ */
/*
* This file contains operating system dependencies.
*/
#define NEED_EVENTS
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <X11/Xlibint.h>
#include <X11/Xtrans/Xtrans.h>
#include <X11/Xauth.h>
#include <X11/Xdmcp.h>
#include <stdio.h>
#include <ctype.h>
#if !defined(WIN32)
#ifndef Lynx
#include <sys/socket.h>
#else
#include <socket.h>
#endif
#else
#include <X11/Xwindows.h>
#endif
#ifndef X_CONNECTION_RETRIES /* number retries on ECONNREFUSED */
#define X_CONNECTION_RETRIES 5
#endif
#ifdef LOCALCONN
#include <sys/utsname.h>
#endif
#include "Xintconn.h"
/* prototypes */
static void GetAuthorization(
XtransConnInfo trans_conn,
int family,
char *saddr,
int saddrlen,
int idisplay,
char **auth_namep,
int *auth_namelenp,
char **auth_datap,
int *auth_datalenp);
/* functions */
static char *copystring (char *src, int len)
{
char *dst = Xmalloc (len + 1);
if (dst) {
strncpy (dst, src, len);
dst[len] = '\0';
}
return dst;
}
/*
* Attempts to connect to server, given display name. Returns file descriptor
* (network socket) or -1 if connection fails. Display names may be of the
* following format:
*
* [protocol/] [hostname] : [:] displaynumber [.screennumber]
*
* A string with exactly two colons seperating hostname from the display
* indicates a DECnet style name. Colons in the hostname may occur if an
* IPv6 numeric address is used as the hostname. An IPv6 numeric address
* may also end in a double colon, so three colons in a row indicates an
* IPv6 address ending in :: followed by :display. To make it easier for
* people to read, an IPv6 numeric address hostname may be surrounded by
* [ ] in a similar fashion to the IPv6 numeric address URL syntax defined
* by IETF RFC 2732.
*
* If no hostname and no protocol is specified, the string is interpreted
* as the most efficient local connection to a server on the same machine.
* This is usually:
*
* o shared memory
* o local stream
* o UNIX domain socket
* o TCP to local host
*
* This function will eventually call the X Transport Interface functions
* which expects the hostname in the format:
*
* [protocol/] [hostname] : [:] displaynumber
*
*/
XtransConnInfo
_X11TransConnectDisplay (
char *display_name,
char **fullnamep, /* RETURN */
int *dpynump, /* RETURN */
int *screenp, /* RETURN */
char **auth_namep, /* RETURN */
int *auth_namelenp, /* RETURN */
char **auth_datap, /* RETURN */
int *auth_datalenp) /* RETURN */
{
int family;
int saddrlen;
Xtransaddr *saddr;
char *lastp, *lastc, *p; /* char pointers */
char *pprotocol = NULL; /* start of protocol name */
char *phostname = NULL; /* start of host of display */
char *pdpynum = NULL; /* start of dpynum of display */
char *pscrnum = NULL; /* start of screen of display */
Bool dnet = False; /* if true, then DECnet format */
int idisplay = 0; /* required display number */
int iscreen = 0; /* optional screen number */
/* int (*connfunc)(); */ /* method to create connection */
int len, hostlen; /* length tmp variable */
int retry; /* retry counter */
char addrbuf[128]; /* final address passed to
X Transport Interface */
char* address = addrbuf;
XtransConnInfo trans_conn = NULL; /* transport connection object */
int connect_stat;
#ifdef LOCALCONN
struct utsname sys;
# ifdef UNIXCONN
Bool try_unix_socket = False; /* Try unix if local fails */
# endif
#endif
#ifdef TCPCONN
char *tcphostname = NULL; /* A place to save hostname pointer */
#endif
p = display_name;
saddrlen = 0; /* set so that we can clear later */
saddr = NULL;
/*
* Step 0, find the protocol. This is delimited by the optional
* slash ('/').
*/
for (lastp = p; *p && *p != ':' && *p != '/'; p++) ;
if (!*p) return NULL; /* must have a colon */
if (p != lastp && *p != ':') { /* protocol given? */
pprotocol = copystring (lastp, p - lastp);
if (!pprotocol) goto bad; /* no memory */
p++; /* skip the '/' */
} else
p = display_name; /* reset the pointer in
case no protocol was given */
/*
* Step 1, find the hostname. This is delimited by either one colon,
* or two colons in the case of DECnet (DECnet Phase V allows a single
* colon in the hostname). (See note above regarding IPv6 numeric
* addresses with triple colons or [] brackets.)
*/
lastp = p;
lastc = NULL;
for (; *p; p++)
if (*p == ':')
lastc = p;
if (!lastc) return NULL; /* must have a colon */
if ((lastp != lastc) && (*(lastc - 1) == ':')
#if defined(IPv6) && defined(AF_INET6)
&& ( ((lastc - 1) == lastp) || (*(lastc - 2) != ':'))
#endif
) {
/* DECnet display specified */
#ifndef DNETCONN
goto bad;
#else
dnet = True;
/* override the protocol specified */
if (pprotocol)
Xfree (pprotocol);
pprotocol = copystring ("dnet", 4);
hostlen = lastc - 1 - lastp;
#endif
}
else
hostlen = lastc - lastp;
if (hostlen > 0) { /* hostname given? */
phostname = copystring (lastp, hostlen);
if (!phostname) goto bad; /* no memory */
}
p = lastc;
#ifdef LOCALCONN
/* check if phostname == localnodename AND protocol not specified */
if (!pprotocol && (!phostname || (phostname && uname(&sys) >= 0 &&
!strncmp(phostname, sys.nodename,
(strlen(sys.nodename) < strlen(phostname) ?
strlen(phostname) : strlen(sys.nodename))))))
{
/*
* We'll first attempt to connect using the local transport. If
* that fails, we'll try again using the Unix socket transport. If
* this fails (which is the case if sshd X protocol forwarding is
* being used), retry using tcp and this hostname.
*/
#ifdef UNIXCONN
try_unix_socket = True;
#endif
#ifdef TCPCONN
if (phostname)
tcphostname = copystring(phostname, strlen(phostname));
else
tcphostname = copystring("localhost", 9);
#endif
Xfree (phostname);
phostname = copystring ("unix", 4);
}
#endif
/*
* Step 2, find the display number. This field is required and is
* delimited either by a nul or a period, depending on whether or not
* a screen number is present.
*/
for (lastp = ++p; *p && isascii(*p) && isdigit(*p); p++) ;
if ((p == lastp) || /* required field */
(*p != '\0' && *p != '.') || /* invalid non-digit terminator */
!(pdpynum = copystring (lastp, p - lastp))) /* no memory */
goto bad;
idisplay = atoi (pdpynum);
/*
* Step 3, find the screen number. This field is optional. It is
* present only if the display number was followed by a period (which
* we've already verified is the only non-nul character).
*/
if (*p) {
for (lastp = ++p; *p && isascii(*p) && isdigit (*p); p++) ;
if (p != lastp) {
if (*p || /* non-digits */
!(pscrnum = copystring (lastp, p - lastp))) /* no memory */
goto bad;
iscreen = atoi (lastp);
}
}
/*
* At this point, we know the following information:
*
* pprotocol protocol string or NULL
* phostname hostname string or NULL
* idisplay display number
* iscreen screen number
* dnet DECnet boolean
*
* We can now decide which transport to use based on the ConnectionFlags
* build parameter the hostname string. If phostname is NULL or equals
* the string "local", then choose the best transport. If phostname
* is "unix", then choose BSD UNIX domain sockets (if configured).
*/
#if defined(TCPCONN) || defined(UNIXCONN) || defined(LOCALCONN) || defined(MNX_TCPCONN) || defined(OS2PIPECONN)
if (!pprotocol) {
if (!phostname) {
#if defined(UNIXCONN) || defined(LOCALCONN) || defined(OS2PIPECONN)
pprotocol = copystring ("local", 5);
#if defined(TCPCONN)
tcphostname = copystring("localhost", 9);
#endif
}
else
{
#endif
pprotocol = copystring ("tcp", 3);
}
}
#endif
#if defined(UNIXCONN) || defined(LOCALCONN) || defined(OS2PIPECONN)
/*
* Now that the defaults have been established, see if we have any
* special names that we have to override:
*
* :N => if UNIXCONN then unix-domain-socket
* ::N => if UNIXCONN then unix-domain-socket
* unix:N => if UNIXCONN then unix-domain-socket
*
* Note that if UNIXCONN isn't defined, then we can use the default
* transport connection function set above.
*/
if (!phostname) {
#ifdef apollo
; /* Unix domain sockets are *really* bad on apollos */
#else
if( pprotocol ) Xfree(pprotocol);
pprotocol = copystring ("local", 5);
#endif
}
else if (strcmp (phostname, "unix") == 0) {
if( pprotocol ) Xfree(pprotocol);
pprotocol = copystring ("local", 5);
}
#endif
#if defined(TCPCONN)
connect:
#endif
/*
* This seems kind of backwards, but we need to put the protocol,
* host, and port back together to pass to _X11TransOpenCOTSClient().
*/
{
int olen = 3 + (pprotocol ? strlen(pprotocol) : 0) +
(phostname ? strlen(phostname) : 0) +
(pdpynum ? strlen(pdpynum) : 0);
if (olen > sizeof addrbuf) address = Xmalloc (olen);
}
if (!address) goto bad;
sprintf(address,"%s/%s:%d",
pprotocol ? pprotocol : "",
phostname ? phostname : "",
idisplay );
/*
* Make the connection, also need to get the auth address info for
* the connection. Do retries in case server host has hit its
* backlog (which, unfortunately, isn't distinguishable from there not
* being a server listening at all, which is why we have to not retry
* too many times).
*/
for(retry=X_CONNECTION_RETRIES; retry>=0; retry-- )
{
if ( (trans_conn = _X11TransOpenCOTSClient(address)) == NULL )
{
break;
}
if ((connect_stat = _X11TransConnect(trans_conn,address)) < 0 )
{
_X11TransClose(trans_conn);
trans_conn = NULL;
if (connect_stat == TRANS_TRY_CONNECT_AGAIN)
{
sleep(1);
continue;
}
else
break;
}
_X11TransGetPeerAddr(trans_conn, &family, &saddrlen, &saddr);
/*
* The family is given in a socket format (ie AF_INET). This
* will convert it to the format used by the authorization and
* X protocol (ie FamilyInternet).
*/
if( _X11TransConvertAddress(&family, &saddrlen, &saddr) < 0 )
{
_X11TransClose(trans_conn);
trans_conn = NULL;
sleep(1);
if (saddr)
{
free ((char *) saddr);
saddr = NULL;
}
continue;
}
break;
}
if (address != addrbuf) Xfree (address);
address = addrbuf;
if( trans_conn == NULL )
goto bad;
/*
* Set close-on-exec so that programs that fork() doesn't get confused.
*/
_X11TransSetOption(trans_conn,TRANS_CLOSEONEXEC,1);
/*
* Build the expanded display name:
*
* [host] : [:] dpy . scr \0
*/
len = ((phostname ? strlen(phostname) : 0) + 1 + (dnet ? 1 : 0) +
strlen(pdpynum) + 1 + (pscrnum ? strlen(pscrnum) : 1) + 1);
*fullnamep = (char *) Xmalloc (len);
if (!*fullnamep) goto bad;
sprintf (*fullnamep, "%s%s%d.%d",
(phostname ? phostname : ""),
(dnet ? "::" : ":"),
idisplay, iscreen);
*dpynump = idisplay;
*screenp = iscreen;
if (pprotocol) Xfree (pprotocol);
if (phostname) Xfree (phostname);
if (pdpynum) Xfree (pdpynum);
if (pscrnum) Xfree (pscrnum);
#ifdef TCPCONN
if (tcphostname) Xfree (tcphostname);
#endif
GetAuthorization(trans_conn, family, (char *) saddr, saddrlen, idisplay,
auth_namep, auth_namelenp, auth_datap, auth_datalenp);
return trans_conn;
/*
* error return; make sure everything is cleaned up.
*/
bad:
if (trans_conn) (void)_X11TransClose(trans_conn);
if (saddr) free ((char *) saddr);
if (pprotocol) Xfree (pprotocol);
if (phostname) Xfree (phostname);
if (address && address != addrbuf) { Xfree(address); address = addrbuf; }
#if defined(LOCALCONN) && defined(UNIXCONN)
if (try_unix_socket) {
pprotocol = copystring ("unix", 4);
phostname = NULL;
try_unix_socket = False; /* Do this only once */
goto connect;
}
#endif
#if defined(TCPCONN)
if (tcphostname) {
pprotocol = copystring("tcp", 3);
phostname = tcphostname;
tcphostname = NULL;
goto connect;
}
#endif
if (pdpynum) Xfree (pdpynum);
if (pscrnum) Xfree (pscrnum);
return NULL;
}
/*
* This is gross, but we need it for compatiblity.
* The test suite relies on the following interface.
*
*/
int _XConnectDisplay (
char *display_name,
char **fullnamep, /* RETURN */
int *dpynump, /* RETURN */
int *screenp, /* RETURN */
char **auth_namep, /* RETURN */
int *auth_namelenp, /* RETURN */
char **auth_datap, /* RETURN */
int *auth_datalenp) /* RETURN */
{
XtransConnInfo trans_conn;
trans_conn = _X11TransConnectDisplay (
display_name, fullnamep, dpynump, screenp,
auth_namep, auth_namelenp, auth_datap, auth_datalenp);
if (trans_conn)
{
int fd = _X11TransGetConnectionNumber (trans_conn);
_X11TransFreeConnInfo (trans_conn);
return (fd);
}
else
return (-1);
}
/*****************************************************************************
* *
* Connection Utility Routines *
* *
*****************************************************************************/
/*
* Disconnect from server.
*/
int _XDisconnectDisplay (trans_conn)
XtransConnInfo trans_conn;
{
_X11TransDisconnect(trans_conn);
_X11TransClose(trans_conn);
return 0;
}
Bool
_XSendClientPrefix (dpy, client, auth_proto, auth_string, prefix)
Display *dpy;
xConnClientPrefix *client; /* contains count for auth_* */
char *auth_proto, *auth_string; /* NOT null-terminated */
xConnSetupPrefix *prefix; /* prefix information */
{
int auth_length = client->nbytesAuthProto;
int auth_strlen = client->nbytesAuthString;
static char padbuf[3]; /* for padding to 4x bytes */
int pad;
struct iovec iovarray[5], *iov = iovarray;
int niov = 0;
int len = 0;
#define add_to_iov(b,l) \
{ iov->iov_base = (b); iov->iov_len = (l); iov++, niov++; len += (l); }
add_to_iov ((caddr_t) client, SIZEOF(xConnClientPrefix));
/*
* write authorization protocol name and data
*/
if (auth_length > 0) {
add_to_iov (auth_proto, auth_length);
pad = -auth_length & 3; /* pad auth_length to a multiple of 4 */
if (pad) add_to_iov (padbuf, pad);
}
if (auth_strlen > 0) {
add_to_iov (auth_string, auth_strlen);
pad = -auth_strlen & 3; /* pad auth_strlen to a multiple of 4 */
if (pad) add_to_iov (padbuf, pad);
}
#undef add_to_iov
len -= _X11TransWritev (dpy->trans_conn, iovarray, niov);
/*
* Set the connection non-blocking since we use select() to block.
*/
_X11TransSetOption(dpy->trans_conn, TRANS_NONBLOCKING, 1);
if (len != 0)
return -1;
#ifdef K5AUTH
if (auth_length == 14 &&
!strncmp(auth_proto, "MIT-KERBEROS-5", 14))
{
return k5_clientauth(dpy, prefix);
} else
#endif
return 0;
}
#ifdef STREAMSCONN
#ifdef SVR4
#include <tiuser.h>
#else
#undef HASXDMAUTH
#endif
#endif
#ifdef SECURE_RPC
#include <rpc/rpc.h>
#ifdef ultrix
#include <time.h>
#include <rpc/auth_des.h>
#endif
#endif
#ifdef HASXDMAUTH
#include <time.h>
#define Time_t time_t
#endif
/*
* First, a routine for setting authorization data
*/
static int xauth_namelen = 0;
static char *xauth_name = NULL; /* NULL means use default mechanism */
static int xauth_datalen = 0;
static char *xauth_data = NULL; /* NULL means get default data */
/*
* This is a list of the authorization names which Xlib currently supports.
* Xau will choose the file entry which matches the earliest entry in this
* array, allowing us to prioritize these in terms of the most secure first
*/
static char *default_xauth_names[] = {
#ifdef K5AUTH
"MIT-KERBEROS-5",
#endif
#ifdef SECURE_RPC
"SUN-DES-1",
#endif
#ifdef HASXDMAUTH
"XDM-AUTHORIZATION-1",
#endif
"MIT-MAGIC-COOKIE-1"
};
static _Xconst int default_xauth_lengths[] = {
#ifdef K5AUTH
14, /* strlen ("MIT-KERBEROS-5") */
#endif
#ifdef SECURE_RPC
9, /* strlen ("SUN-DES-1") */
#endif
#ifdef HASXDMAUTH
19, /* strlen ("XDM-AUTHORIZATION-1") */
#endif
18 /* strlen ("MIT-MAGIC-COOKIE-1") */
};
#define NUM_DEFAULT_AUTH (sizeof (default_xauth_names) / sizeof (default_xauth_names[0]))
static char **xauth_names = default_xauth_names;
static _Xconst int *xauth_lengths = default_xauth_lengths;
static int xauth_names_length = NUM_DEFAULT_AUTH;
void XSetAuthorization (name, namelen, data, datalen)
int namelen, datalen; /* lengths of name and data */
char *name, *data; /* NULL or arbitrary array of bytes */
{
char *tmpname, *tmpdata;
_XLockMutex(_Xglobal_lock);
if (xauth_name) Xfree (xauth_name); /* free any existing data */
if (xauth_data) Xfree (xauth_data);
xauth_name = xauth_data = NULL; /* mark it no longer valid */
xauth_namelen = xauth_datalen = 0;
_XUnlockMutex(_Xglobal_lock);
if (namelen < 0) namelen = 0; /* check for bogus inputs */
if (datalen < 0) datalen = 0; /* maybe should return? */
if (namelen > 0) { /* try to allocate space */
tmpname = Xmalloc ((unsigned) namelen);
if (!tmpname) return;
memcpy (tmpname, name, namelen);
} else {
tmpname = NULL;
}
if (datalen > 0) {
tmpdata = Xmalloc ((unsigned) datalen);
if (!tmpdata) {
if (tmpname) (void) Xfree (tmpname);
return;
}
memcpy (tmpdata, data, datalen);
} else {
tmpdata = NULL;
}
_XLockMutex(_Xglobal_lock);
xauth_name = tmpname; /* and store the suckers */
xauth_namelen = namelen;
if (tmpname)
{
xauth_names = &xauth_name;
xauth_lengths = &xauth_namelen;
xauth_names_length = 1;
}
else
{
xauth_names = default_xauth_names;
xauth_lengths = default_xauth_lengths;
xauth_names_length = NUM_DEFAULT_AUTH;
}
xauth_data = tmpdata;
xauth_datalen = datalen;
_XUnlockMutex(_Xglobal_lock);
return;
}
#ifdef SECURE_RPC
/*
* Create a credential that we can send to the X server.
*/
static int
auth_ezencode(
char *servername,
int window,
char *cred_out,
int *len)
{
AUTH *a;
XDR xdr;
#if defined(SVR4) && defined(sun)
a = authdes_seccreate(servername, window, NULL, NULL);
#else
a = (AUTH *)authdes_create(servername, window, NULL, NULL);
#endif
if (a == (AUTH *)NULL) {
perror("auth_create");
return 0;
}
xdrmem_create(&xdr, cred_out, *len, XDR_ENCODE);
if (AUTH_MARSHALL(a, &xdr) == FALSE) {
perror("auth_marshall");
AUTH_DESTROY(a);
return 0;
}
*len = xdr_getpos(&xdr);
AUTH_DESTROY(a);
return 1;
}
#endif
#ifdef K5AUTH
#include <com_err.h>
extern krb5_flags krb5_kdc_default_options;
/*
* k5_clientauth
*
* Returns non-zero if the setup prefix has been read,
* so we can tell XOpenDisplay to not bother looking for it by
* itself.
*/
static int k5_clientauth(dpy, sprefix)
Display *dpy;
xConnSetupPrefix *sprefix;
{
krb5_error_code retval;
xReq prefix;
char *buf;
CARD16 plen, tlen;
krb5_data kbuf;
krb5_ccache cc;
krb5_creds creds;
krb5_principal cprinc, sprinc;
krb5_ap_rep_enc_part *repl;
krb5_init_ets();
/*
* stage 0: get encoded principal and tgt from server
*/
_XRead(dpy, (char *)&prefix, sz_xReq);
if (prefix.reqType != 2 && prefix.reqType != 3)
/* not an auth packet... so deal */
if (prefix.reqType == 0 || prefix.reqType == 1)
{
memcpy((char *)sprefix, (char *)&prefix, sz_xReq);
_XRead(dpy, (char *)sprefix + sz_xReq,
sz_xConnSetupPrefix - sz_xReq); /* ewww... gross */
return 1;
}
else
{
fprintf(stderr,
"Xlib: Krb5 stage 0: got illegal connection setup success code %d\n",
prefix.reqType);
return -1;
}
if (prefix.data != 0)
{
fprintf(stderr, "Xlib: got out of sequence (%d) packet in Krb5 auth\n",
prefix.data);
return -1;
}
buf = (char *)malloc((prefix.length << 2) - sz_xReq);
if (buf == NULL) /* malloc failed. Run away! */
{
fprintf(stderr, "Xlib: malloc bombed in Krb5 auth\n");
return -1;
}
tlen = (prefix.length << 2) - sz_xReq;
_XRead(dpy, buf, tlen);
if (prefix.reqType == 2 && tlen < 6)
{
fprintf(stderr, "Xlib: Krb5 stage 0 reply from server too short\n");
free(buf);
return -1;
}
if (prefix.reqType == 2)
{
plen = *(CARD16 *)buf;
kbuf.data = buf + 2;
kbuf.length = (plen > tlen) ? tlen : plen;
}
else
{
kbuf.data = buf;
kbuf.length = tlen;
}
if (XauKrb5Decode(kbuf, &sprinc))
{
free(buf);
fprintf(stderr, "Xlib: XauKrb5Decode bombed\n");
return -1;
}
if (prefix.reqType == 3) /* do some special stuff here */
{
char *sname, *hostname = NULL;
sname = (char *)malloc(krb5_princ_component(sprinc, 0)->length + 1);
if (sname == NULL)
{
free(buf);
krb5_free_principal(sprinc);
fprintf(stderr, "Xlib: malloc bombed in Krb5 auth\n");
return -1;
}
memcpy(sname, krb5_princ_component(sprinc, 0)->data,
krb5_princ_component(sprinc, 0)->length);
sname[krb5_princ_component(sprinc, 0)->length] = '\0';
krb5_free_principal(sprinc);
if (dpy->display_name[0] != ':') /* hunt for a hostname */
{
char *t;
if ((hostname = (char *)malloc(strlen(dpy->display_name)))
== NULL)
{
free(buf);
free(sname);
fprintf(stderr, "Xlib: malloc bombed in Krb5 auth\n");
return -1;
}
strcpy(hostname, dpy->display_name);
t = strchr(hostname, ':');
if (t == NULL)
{
free(buf);
free(sname);
free(hostname);
fprintf(stderr,
"Xlib: shouldn't get here! malformed display name.");
return -1;
}
if ((t - hostname + 1 < strlen(hostname)) && t[1] == ':')
t++;
*t = '\0'; /* truncate the dpy number out */
}
retval = krb5_sname_to_principal(hostname, sname,
KRB5_NT_SRV_HST, &sprinc);
free(sname);
if (hostname)
free(hostname);
if (retval)
{
free(buf);
fprintf(stderr, "Xlib: krb5_sname_to_principal failed: %s\n",
error_message(retval));
return -1;
}
}
if (retval = krb5_cc_default(&cc))
{
free(buf);
krb5_free_principal(sprinc);
fprintf(stderr, "Xlib: krb5_cc_default failed: %s\n",
error_message(retval));
return -1;
}
if (retval = krb5_cc_get_principal(cc, &cprinc))
{
free(buf);
krb5_free_principal(sprinc);
fprintf(stderr, "Xlib: cannot get Kerberos principal from \"%s\": %s\n",
krb5_cc_default_name(), error_message(retval));
return -1;
}
bzero((char *)&creds, sizeof(creds));
creds.server = sprinc;
creds.client = cprinc;
if (prefix.reqType == 2)
{
creds.second_ticket.length = tlen - plen - 2;
creds.second_ticket.data = buf + 2 + plen;
retval = krb5_get_credentials(KRB5_GC_USER_USER |
krb5_kdc_default_options,
cc, &creds);
}
else
retval = krb5_get_credentials(krb5_kdc_default_options,
cc, &creds);
if (retval)
{
free(buf);
krb5_free_cred_contents(&creds);
fprintf(stderr, "Xlib: cannot get Kerberos credentials: %s\n",
error_message(retval));
return -1;
}
/*
* now format the ap_req to send to the server
*/
if (prefix.reqType == 2)
retval = krb5_mk_req_extended(AP_OPTS_USE_SESSION_KEY |
AP_OPTS_MUTUAL_REQUIRED, NULL,
0, 0, NULL, cc,
&creds, NULL, &kbuf);
else
retval = krb5_mk_req_extended(AP_OPTS_MUTUAL_REQUIRED, NULL,
0, 0, NULL, cc, &creds, NULL,
&kbuf);
free(buf);
if (retval) /* Some manner of Kerberos lossage */
{
krb5_free_cred_contents(&creds);
fprintf(stderr, "Xlib: krb5_mk_req_extended failed: %s\n",
error_message(retval));
return -1;
}
prefix.reqType = 1;
prefix.data = 0;
prefix.length = (kbuf.length + sz_xReq + 3) >> 2;
/*
* stage 1: send ap_req to server
*/
_XSend(dpy, (char *)&prefix, sz_xReq);
_XSend(dpy, (char *)kbuf.data, kbuf.length);
free(kbuf.data);
/*
* stage 2: get ap_rep from server to mutually authenticate
*/
_XRead(dpy, (char *)&prefix, sz_xReq);
if (prefix.reqType != 2)
if (prefix.reqType == 0 || prefix.reqType == 1)
{
memcpy((char *)sprefix, (char *)&prefix, sz_xReq);
_XRead(dpy, (char *)sprefix + sz_xReq,
sz_xConnSetupPrefix - sz_xReq);
return 1;
}
else
{
fprintf(stderr,
"Xlib: Krb5 stage 2: got illegal connection setup success code %d\n",
prefix.reqType);
return -1;
}
if (prefix.data != 2)
return -1;
kbuf.length = (prefix.length << 2) - sz_xReq;
kbuf.data = (char *)malloc(kbuf.length);
if (kbuf.data == NULL)
{
fprintf(stderr, "Xlib: malloc bombed in Krb5 auth\n");
return -1;
}
_XRead(dpy, (char *)kbuf.data, kbuf.length);
retval = krb5_rd_rep(&kbuf, &creds.keyblock, &repl);
if (retval)
{
free(kbuf.data);
fprintf(stderr, "Xlib: krb5_rd_rep failed: %s\n",
error_message(retval));
return -1;
}
free(kbuf.data);
/*
* stage 3: send a short ack to the server and return
*/
prefix.reqType = 3;
prefix.data = 0;
prefix.length = sz_xReq >> 2;
_XSend(dpy, (char *)&prefix, sz_xReq);
return 0;
}
#endif /* K5AUTH */
static void
GetAuthorization(
XtransConnInfo trans_conn,
int family,
char *saddr,
int saddrlen,
int idisplay,
char **auth_namep, /* RETURN */
int *auth_namelenp, /* RETURN */
char **auth_datap, /* RETURN */
int *auth_datalenp) /* RETURN */
{
#ifdef SECURE_RPC
char rpc_cred[MAX_AUTH_BYTES];
#endif
#ifdef HASXDMAUTH
unsigned char xdmcp_data[192/8];
#endif
char *auth_name;
int auth_namelen;
unsigned char *auth_data;
int auth_datalen;
Xauth *authptr = NULL;
/*
* Look up the authorization protocol name and data if necessary.
*/
if (xauth_name && xauth_data) {
auth_namelen = xauth_namelen;
auth_name = xauth_name;
auth_datalen = xauth_datalen;
auth_data = (unsigned char *) xauth_data;
} else {
char dpynumbuf[40]; /* big enough to hold 2^64 and more */
(void) sprintf (dpynumbuf, "%d", idisplay);
authptr = XauGetBestAuthByAddr ((unsigned short) family,
(unsigned short) saddrlen,
saddr,
(unsigned short) strlen (dpynumbuf),
dpynumbuf,
xauth_names_length,
xauth_names,
xauth_lengths);
if (authptr) {
auth_namelen = authptr->name_length;
auth_name = (char *)authptr->name;
auth_datalen = authptr->data_length;
auth_data = (unsigned char *) authptr->data;
} else {
auth_namelen = 0;
auth_name = NULL;
auth_datalen = 0;
auth_data = NULL;
}
}
#ifdef HASXDMAUTH
/*
* build XDM-AUTHORIZATION-1 data
*/
if (auth_namelen == 19 && !strncmp (auth_name, "XDM-AUTHORIZATION-1", 19))
{
int i, j;
Time_t now;
int family, addrlen;
Xtransaddr *addr = NULL;
for (j = 0; j < 8; j++)
xdmcp_data[j] = auth_data[j];
_X11TransGetMyAddr(trans_conn, &family, &addrlen, &addr);
switch( family )
{
#ifdef AF_INET
case AF_INET:
{
/*
* addr will contain a sockaddr_in with all
* of the members already in network byte order.
*/
for(i=4; i<8; i++) /* do sin_addr */
xdmcp_data[j++] = ((char *)addr)[i];
for(i=2; i<4; i++) /* do sin_port */
xdmcp_data[j++] = ((char *)addr)[i];
break;
}
#endif /* AF_INET */
#if defined(IPv6) && defined(AF_INET6)
case AF_INET6:
/* XXX This should probably never happen */
{
unsigned char ipv4mappedprefix[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
/* In the case of v4 mapped addresses send the v4
part of the address - addr is already in network byte order */
if (memcmp(addr+8, ipv4mappedprefix, 12) == 0) {
for (i = 20 ; i < 24; i++)
xdmcp_data[j++] = ((char *)addr)[i];
/* Port number */
for (i=2; i<4; i++)
xdmcp_data[j++] = ((char *)addr)[i];
} else {
/* Fake data to keep the data aligned. Otherwise the
the server will bail about incorrect timing data */
for (i = 0; i < 6; i++) {
xdmcp_data[j++] = 0;
}
}
break;
}
#endif /* AF_INET6 */
#ifdef AF_UNIX
case AF_UNIX:
{
/*
* We don't use the sockaddr_un for this encoding.
* Instead, we create a sockaddr_in filled with
* a decreasing counter for the address, and the
* pid for the port.
*/
static unsigned long unix_addr = 0xFFFFFFFF;
unsigned long the_addr;
unsigned short the_port;
unsigned long the_utime;
struct timeval tp;
X_GETTIMEOFDAY(&tp);
_XLockMutex(_Xglobal_lock);
the_addr = unix_addr--;
_XUnlockMutex(_Xglobal_lock);
the_utime = (unsigned long) tp.tv_usec;
the_port = getpid ();
xdmcp_data[j++] = (the_utime >> 24) & 0xFF;
xdmcp_data[j++] = (the_utime >> 16) & 0xFF;
xdmcp_data[j++] = ((the_utime >> 8) & 0xF0)
| ((the_addr >> 8) & 0x0F);
xdmcp_data[j++] = (the_addr >> 0) & 0xFF;
xdmcp_data[j++] = (the_port >> 8) & 0xFF;
xdmcp_data[j++] = (the_port >> 0) & 0xFF;
break;
}
#endif /* AF_UNIX */
#ifdef AF_DECnet
case AF_DECnet:
/*
* What is the defined encoding for this?
*/
break;
#endif /* AF_DECnet */
default:
/*
* Need to return some kind of errro status here.
* maybe a NULL auth??
*/
break;
} /* switch */
if (addr)
free ((char *) addr);
time (&now);
xdmcp_data[j++] = (now >> 24) & 0xFF;
xdmcp_data[j++] = (now >> 16) & 0xFF;
xdmcp_data[j++] = (now >> 8) & 0xFF;
xdmcp_data[j++] = (now >> 0) & 0xFF;
while (j < 192 / 8)
xdmcp_data[j++] = 0;
_XLockMutex(_Xglobal_lock);
/* this function might use static data, hence the lock around it */
XdmcpWrap (xdmcp_data, auth_data + 8,
xdmcp_data, j);
_XUnlockMutex(_Xglobal_lock);
auth_data = xdmcp_data;
auth_datalen = j;
}
#endif /* HASXDMAUTH */
#ifdef SECURE_RPC
/*
* The SUN-DES-1 authorization protocol uses the
* "secure RPC" mechanism in SunOS 4.0+.
*/
if (auth_namelen == 9 && !strncmp(auth_name, "SUN-DES-1", 9)) {
char servernetname[MAXNETNAMELEN + 1];
/*
* Copy over the server's netname from the authorization
* data field filled in by XauGetAuthByAddr().
*/
if (auth_datalen > MAXNETNAMELEN) {
auth_datalen = 0;
auth_data = NULL;
} else {
memcpy(servernetname, auth_data, auth_datalen);
servernetname[auth_datalen] = '\0';
auth_datalen = sizeof (rpc_cred);
if (auth_ezencode(servernetname, 100, rpc_cred,
&auth_datalen))
auth_data = (unsigned char *) rpc_cred;
else {
auth_datalen = 0;
auth_data = NULL;
}
}
}
#endif
if (saddr) free ((char *) saddr);
if ((*auth_namelenp = auth_namelen))
{
if ((*auth_namep = Xmalloc(auth_namelen)))
memcpy(*auth_namep, auth_name, auth_namelen);
else
*auth_namelenp = 0;
}
else
*auth_namep = NULL;
if ((*auth_datalenp = auth_datalen))
{
if ((*auth_datap = Xmalloc(auth_datalen)))
memcpy(*auth_datap, auth_data, auth_datalen);
else
*auth_datalenp = 0;
}
else
*auth_datap = NULL;
if (authptr) XauDisposeAuth (authptr);
}