/* $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> #include <unistd.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; #if defined(LOCALCONN) || defined(TCPCONN) Bool reset_hostname = False; /* Reset hostname? */ #endif #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 if (!phostname) reset_hostname = True; 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) { #ifdef HAVE_LAUNCHD if (!phostname || phostname[0]=='/') { #else if (!phostname) { #endif #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) 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; 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 */ #if defined(LOCALCONN) || defined(TCPCONN) /* * If we computed the host name, get rid of it so that * XDisplayString() and XDisplayName() agree. */ if (reset_hostname) { Xfree (phostname); phostname = NULL; } #endif 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; #ifdef HAVE_LAUNCHD if (phostname && strlen(phostname) > 11 && !strncmp(phostname, "/tmp/launch", 11)) sprintf (*fullnamep, "%s%s%d", (phostname ? phostname : ""), (dnet ? "::" : ":"), idisplay); else #endif 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; reset_hostname = True; 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((char*)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); }