/* $XConsortium: ConnDis.c,v 11.123 94/05/19 11:00:27 mor Exp $ */
/* $XFree86: xc/lib/X11/ConnDis.c,v 3.6 1994/10/20 06:03:02 dawes Exp $ */
/*
 
Copyright (c) 1989  X Consortium

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

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
X CONSORTIUM 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 X Consortium 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 X Consortium.

*/

/* 
 * This file contains operating system dependencies.
 */

#define NEED_EVENTS

#include <X11/Xlibint.h>
#include "Xlibnet.h"
#include <X11/Xtrans.h>
#include <X11/Xauth.h>
#include <stdio.h>
#include <ctype.h>

#if !defined(WIN32) && !defined(MINIX)
#include <sys/socket.h>
#endif

#ifndef X_CONNECTION_RETRIES		/* number retries on ECONNREFUSED */
#define X_CONNECTION_RETRIES 5
#endif

#ifdef LOCALCONN
#include <sys/utsname.h>
#endif

static void GetAuthorization();

static char *copystring (src, len)
    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]
 *
 * The second colon indicates a DECnet style name.  No hostname is interpretted
 * 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 (display_name, fullnamep, dpynump, screenp,
		      auth_namep, auth_namelenp, auth_datap, auth_datalenp)
    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, *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;			/* required display number */
    int iscreen = 0;			/* optional screen number */
    int (*connfunc)();			/* method to create connection */
    int len;				/* length tmp variable */
    int retry;				/* retry counter */
    char address[128];			/* final address passed to
					   X Transport Interface */
    XtransConnInfo trans_conn = NULL;	/* transport connection object */
    int connect_stat;
#ifdef LOCALCONN
    struct utsname sys;
#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 the required 
     * first colon.
     */
    for (lastp = p; *p && *p != ':'; p++) ;
    if (!*p) return NULL;		/* must have a colon */

    if (p != lastp) {		/* hostname given? */
	phostname = copystring (lastp, p - lastp);
	if (!phostname) goto bad;	/* no memory */
    }
#ifdef LOCALCONN
    /* check if phostname == localnodename */
    if (phostname && uname(&sys) >= 0 &&
	!strncmp(phostname, sys.nodename, strlen(sys.nodename)))
    {
	Xfree (phostname);
	phostname = copystring ("unix", 4);
    }
#endif


    /*
     * Step 2, see if this is a DECnet address by looking for the optional
     * second colon.
     */
    if (p[1] == ':') {			/* then DECnet format */
	dnet = True;
	if( pprotocol ) Xfree(pprotocol); /* override the protocol specified */
	pprotocol = copystring ("dnet", 4);
	p++;
    }

    /*
     * see if we're allowed to have a DECnet address
     */
#ifndef DNETCONN
    if (dnet) goto bad;
#endif

    /*
     * Step 3, 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 4, 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)
    if (!pprotocol) {
	if (!phostname)
#if defined(UNIXCONN) || defined(LOCALCONN)
	    pprotocol = copystring ("local", 5);
	else
#endif
	    pprotocol = copystring ("tcp", 3);
    }
#else
#if defined(AMRPCCONN)
    if (!pprotocol) {
            pprotocol = copystring ("amcon", 5);
    }
#endif
#endif

#if defined(UNIXCONN) || defined(LOCALCONN)
    /*
     * 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

    /*
     * This seems kind of backwards, but we need to put the protocol,
     * host, and port back together to pass to _X11TransOpenCOTSClient().
     */

    sprintf(address,"%s/%s:%s",
	pprotocol ? pprotocol : "",
	phostname ? phostname : "",
	pdpynum );

    /*
     * 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)
	    {
		Xfree (saddr);
		saddr = NULL;
	    }
	    continue;
	    }

	break;
	}

    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);

    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) Xfree (saddr);
    if (pprotocol) Xfree (pprotocol);
    if (phostname) Xfree (phostname);
    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 (display_name, fullnamep, dpynump, screenp,
		      auth_namep, auth_namelenp, auth_datap, auth_datalenp)
    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;
}



static int padlength[4] = {0, 3, 2, 1};	 /* make sure auth is multiple of 4 */

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 = padlength [auth_length & 3];
	if (pad) add_to_iov (padbuf, pad);
    }
    if (auth_strlen > 0) {
	add_to_iov (auth_string, auth_strlen);
	pad = padlength [auth_strlen & 3];
	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
#ifdef X_NOT_STDC_ENV
#define Time_t long
extern Time_t time ();
#else
#include <time.h>
#define Time_t time_t
#endif
#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 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 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(servername, window, cred_out, len)
        char           *servername;
        int             window;
	char	       *cred_out;
        int            *len;
{
        AUTH           *a;
        XDR             xdr;

        a = (AUTH *)authdes_create(servername, window, NULL, NULL);
        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(trans_conn, family, saddr, saddrlen, idisplay,
		 auth_namep, auth_namelenp, auth_datap, auth_datalenp)
    XtransConnInfo trans_conn;
    int family;
    int saddrlen;
    int idisplay;
    char *saddr;
    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
    char xdmcp_data[192/8];
#endif
    char *auth_name;
    int auth_namelen;
    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 = 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 = (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 */
#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;
	    
	    _XLockMutex(_Xglobal_lock);
	    the_addr = unix_addr--;
	    _XUnlockMutex(_Xglobal_lock);
	    the_port = getpid ();

	    xdmcp_data[j++] = (the_addr >> 24) & 0xFF;
	    xdmcp_data[j++] = (the_addr >> 16) & 0xFF;
	    xdmcp_data[j++] = (the_addr >>  8) & 0xFF;
	    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 = rpc_cred;
	    else {
		auth_datalen = 0;
		auth_data = NULL;
	    }
	}
    }
#endif
    if (saddr) Xfree (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);
}
