Xtransutil.c revision a1910677
1/*
2
3Copyright 1993, 1994, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27 * Copyright 1993, 1994 NCR Corporation - Dayton, Ohio, USA
28 *
29 * All Rights Reserved
30 *
31 * Permission to use, copy, modify, and distribute this software and its
32 * documentation for any purpose and without fee is hereby granted, provided
33 * that the above copyright notice appear in all copies and that both that
34 * copyright notice and this permission notice appear in supporting
35 * documentation, and that the name NCR not be used in advertising
36 * or publicity pertaining to distribution of the software without specific,
37 * written prior permission.  NCR makes no representations about the
38 * suitability of this software for any purpose.  It is provided "as is"
39 * without express or implied warranty.
40 *
41 * NCRS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
42 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
43 * NO EVENT SHALL NCR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
44 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
45 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
46 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
47 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
48 */
49
50/*
51 * These are some utility functions created for convenience or to provide
52 * an interface that is similar to an existing interface. These are built
53 * only using the Transport Independent API, and have no knowledge of
54 * the internal implementation.
55 */
56
57#ifdef XTHREADS
58#include <X11/Xthreads.h>
59#endif
60#ifdef WIN32
61#include <X11/Xlibint.h>
62#include <X11/Xwinsock.h>
63#endif
64
65#if defined(IPv6) && !defined(AF_INET6)
66#error "Cannot build IPv6 support without AF_INET6"
67#endif
68
69/* Temporary workaround for consumers whose configure scripts were
70   generated with pre-1.6 versions of xtrans.m4 */
71#if defined(IPv6) && !defined(HAVE_INET_NTOP)
72#define HAVE_INET_NTOP
73#endif
74
75#ifdef X11_t
76
77/*
78 * These values come from X.h and Xauth.h, and MUST match them. Some
79 * of these values are also defined by the ChangeHost protocol message.
80 */
81
82#define FamilyInternet		0	/* IPv4 */
83#define FamilyDECnet		1
84#define FamilyChaos		2
85#define FamilyInternet6		6
86#define FamilyAmoeba		33
87#define FamilyLocalHost		252
88#define FamilyKrb5Principal	253
89#define FamilyNetname		254
90#define FamilyLocal		256
91#define FamilyWild		65535
92
93/*
94 * TRANS(ConvertAddress) converts a sockaddr based address to an
95 * X authorization based address. Some of this is defined as part of
96 * the ChangeHost protocol. The rest is just done in a consistent manner.
97 */
98
99int
100TRANS(ConvertAddress)(int *familyp, int *addrlenp, Xtransaddr **addrp)
101
102{
103
104    prmsg(2,"ConvertAddress(%d,%d,%p)\n",*familyp,*addrlenp,*addrp);
105
106    switch( *familyp )
107    {
108#if defined(TCPCONN)
109    case AF_INET:
110    {
111	/*
112	 * Check for the BSD hack localhost address 127.0.0.1.
113	 * In this case, we are really FamilyLocal.
114	 */
115
116	struct sockaddr_in saddr;
117	int len = sizeof(saddr.sin_addr.s_addr);
118	char *cp = (char *) &saddr.sin_addr.s_addr;
119
120	memcpy (&saddr, *addrp, sizeof (struct sockaddr_in));
121
122	if ((len == 4) && (cp[0] == 127) && (cp[1] == 0) &&
123	    (cp[2] == 0) && (cp[3] == 1))
124	{
125	    *familyp=FamilyLocal;
126	}
127	else
128	{
129	    *familyp=FamilyInternet;
130	    *addrlenp=len;
131	    memcpy(*addrp,&saddr.sin_addr,len);
132	}
133	break;
134    }
135
136#ifdef IPv6
137    case AF_INET6:
138    {
139	struct sockaddr_in6 saddr6;
140
141	memcpy (&saddr6, *addrp, sizeof (struct sockaddr_in6));
142
143	if (IN6_IS_ADDR_LOOPBACK(&saddr6.sin6_addr))
144	{
145	    *familyp=FamilyLocal;
146	}
147	else if (IN6_IS_ADDR_V4MAPPED(&(saddr6.sin6_addr))) {
148	    char *cp = (char *) &saddr6.sin6_addr.s6_addr[12];
149
150	    if ((cp[0] == 127) && (cp[1] == 0) &&
151	      (cp[2] == 0) && (cp[3] == 1))
152	    {
153		*familyp=FamilyLocal;
154	    }
155	    else
156	    {
157		*familyp=FamilyInternet;
158		*addrlenp = sizeof (struct in_addr);
159		memcpy(*addrp,cp,*addrlenp);
160	    }
161	}
162	else
163	{
164	    *familyp=FamilyInternet6;
165	    *addrlenp=sizeof(saddr6.sin6_addr);
166	    memcpy(*addrp,&saddr6.sin6_addr,sizeof(saddr6.sin6_addr));
167	}
168	break;
169    }
170#endif /* IPv6 */
171#endif /* defined(TCPCONN) */
172
173
174#if defined(UNIXCONN) || defined(LOCALCONN)
175    case AF_UNIX:
176    {
177	*familyp=FamilyLocal;
178	break;
179    }
180#endif /* defined(UNIXCONN) || defined(LOCALCONN) */
181
182
183    default:
184	prmsg(1,"ConvertAddress: Unknown family type %d\n",
185	      *familyp);
186	return -1;
187    }
188
189
190    if (*familyp == FamilyLocal)
191    {
192	/*
193	 * In the case of a local connection, we need to get the
194	 * host name for authentication.
195	 */
196
197	char hostnamebuf[256];
198	int len = TRANS(GetHostname) (hostnamebuf, sizeof hostnamebuf);
199
200	if (len > 0) {
201	    if (*addrp && *addrlenp < (len + 1))
202	    {
203		free (*addrp);
204		*addrp = NULL;
205	    }
206	    if (!*addrp)
207		*addrp = malloc (len + 1);
208	    if (*addrp) {
209		strcpy ((char *) *addrp, hostnamebuf);
210		*addrlenp = len;
211	    } else {
212		*addrlenp = 0;
213	    }
214	}
215	else
216	{
217	    if (*addrp)
218		free (*addrp);
219	    *addrp = NULL;
220	    *addrlenp = 0;
221	}
222    }
223
224    return 0;
225}
226
227#endif /* X11_t */
228
229#ifdef ICE_t
230
231/* Needed for _XGethostbyaddr usage in TRANS(GetPeerNetworkId) */
232# if defined(TCPCONN) || defined(UNIXCONN)
233#  define X_INCLUDE_NETDB_H
234#  define XOS_USE_NO_LOCKING
235#  include <X11/Xos_r.h>
236# endif
237
238#include <signal.h>
239
240char *
241TRANS(GetMyNetworkId) (XtransConnInfo ciptr)
242
243{
244    int		family = ciptr->family;
245    char 	*addr = ciptr->addr;
246    char	hostnamebuf[256];
247    char 	*networkId = NULL;
248    const char	*transName = ciptr->transptr->TransName;
249
250    if (gethostname (hostnamebuf, sizeof (hostnamebuf)) < 0)
251    {
252	return (NULL);
253    }
254
255    switch (family)
256    {
257#if defined(UNIXCONN) || defined(LOCALCONN)
258    case AF_UNIX:
259    {
260	struct sockaddr_un *saddr = (struct sockaddr_un *) addr;
261	networkId = malloc (3 + strlen (transName) +
262	    strlen (hostnamebuf) + strlen (saddr->sun_path));
263	if (networkId != NULL)
264	    sprintf (networkId, "%s/%s:%s", transName,
265		     hostnamebuf, saddr->sun_path);
266	break;
267    }
268#endif /* defined(UNIXCONN) || defined(LOCALCONN) */
269
270#if defined(TCPCONN)
271    case AF_INET:
272#ifdef IPv6
273    case AF_INET6:
274#endif
275    {
276	struct sockaddr_in *saddr = (struct sockaddr_in *) addr;
277#ifdef IPv6
278	struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) addr;
279#endif
280	int portnum;
281	char portnumbuf[10];
282
283
284#ifdef IPv6
285	if (family == AF_INET6)
286	    portnum = ntohs (saddr6->sin6_port);
287	else
288#endif
289	    portnum = ntohs (saddr->sin_port);
290
291	snprintf (portnumbuf, sizeof(portnumbuf), "%d", portnum);
292	networkId = malloc (3 + strlen (transName) +
293	    strlen (hostnamebuf) + strlen (portnumbuf));
294	if (networkId != NULL)
295	    sprintf (networkId, "%s/%s:%s", transName, hostnamebuf, portnumbuf);
296	break;
297    }
298#endif /* defined(TCPCONN) */
299
300
301    default:
302	break;
303    }
304
305    return (networkId);
306}
307
308#include <setjmp.h>
309static jmp_buf env;
310
311#ifdef SIGALRM
312static volatile int nameserver_timedout = 0;
313
314static void
315nameserver_lost(int sig _X_UNUSED)
316{
317  nameserver_timedout = 1;
318  longjmp (env, -1);
319  /* NOTREACHED */
320}
321#endif /* SIGALARM */
322
323
324char *
325TRANS(GetPeerNetworkId) (XtransConnInfo ciptr)
326
327{
328    int		family = ciptr->family;
329    char	*peer_addr = ciptr->peeraddr;
330    char	*hostname;
331    char	addrbuf[256];
332    const char	*addr = NULL;
333
334    switch (family)
335    {
336    case AF_UNSPEC:
337#if defined(UNIXCONN) || defined(LOCALCONN)
338    case AF_UNIX:
339    {
340	if (gethostname (addrbuf, sizeof (addrbuf)) == 0)
341	    addr = addrbuf;
342	break;
343    }
344#endif /* defined(UNIXCONN) || defined(LOCALCONN) */
345
346#if defined(TCPCONN)
347    case AF_INET:
348#ifdef IPv6
349    case AF_INET6:
350#endif
351    {
352	struct sockaddr_in *saddr = (struct sockaddr_in *) peer_addr;
353#ifdef IPv6
354	struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) peer_addr;
355#endif
356	char *address;
357	int addresslen;
358#ifdef XTHREADS_NEEDS_BYNAMEPARAMS
359	_Xgethostbynameparams hparams;
360#endif
361	struct hostent * volatile hostp = NULL;
362
363#ifdef IPv6
364	if (family == AF_INET6)
365	{
366	    address = (char *) &saddr6->sin6_addr;
367	    addresslen = sizeof (saddr6->sin6_addr);
368	}
369	else
370#endif
371	{
372	    address = (char *) &saddr->sin_addr;
373	    addresslen = sizeof (saddr->sin_addr);
374	}
375
376#ifdef SIGALRM
377	/*
378	 * gethostbyaddr can take a LONG time if the host does not exist.
379	 * Assume that if it does not respond in NAMESERVER_TIMEOUT seconds
380	 * that something is wrong and do not make the user wait.
381	 * gethostbyaddr will continue after a signal, so we have to
382	 * jump out of it.
383	 */
384
385	nameserver_timedout = 0;
386	signal (SIGALRM, nameserver_lost);
387	alarm (4);
388	if (setjmp(env) == 0) {
389#endif
390	    hostp = _XGethostbyaddr (address, addresslen, family, hparams);
391#ifdef SIGALRM
392	}
393	alarm (0);
394#endif
395	if (hostp != NULL)
396	  addr = hostp->h_name;
397	else
398#ifdef HAVE_INET_NTOP
399	  addr = inet_ntop (family, address, addrbuf, sizeof (addrbuf));
400#else
401	  addr = inet_ntoa (saddr->sin_addr);
402#endif
403	break;
404    }
405
406#endif /* defined(TCPCONN) */
407
408
409    default:
410	return (NULL);
411    }
412
413
414    hostname = malloc (strlen (ciptr->transptr->TransName) +
415                       (addr ? strlen (addr) : 0) + 2);
416    if (hostname)
417    {
418	strcpy (hostname, ciptr->transptr->TransName);
419	strcat (hostname, "/");
420	if (addr)
421	    strcat (hostname, addr);
422    }
423    return (hostname);
424}
425
426#endif /* ICE_t */
427
428
429#if defined(WIN32) && defined(TCPCONN)
430int
431TRANS(WSAStartup) (void)
432{
433    static WSADATA wsadata;
434
435    prmsg (2,"WSAStartup()\n");
436
437    if (!wsadata.wVersion && WSAStartup(MAKEWORD(2,2), &wsadata))
438        return 1;
439    return 0;
440}
441#endif
442
443#ifdef TRANS_SERVER
444#include <sys/types.h>
445#include <sys/stat.h>
446#include <errno.h>
447
448#if !defined(S_IFLNK) && !defined(S_ISLNK)
449#undef lstat
450#define lstat(a,b) stat(a,b)
451#endif
452
453#define FAIL_IF_NOMODE  1
454#define FAIL_IF_NOT_ROOT 2
455#define WARN_NO_ACCESS 4
456
457/*
458 * We make the assumption that when the 'sticky' (t) bit is requested
459 * it's not save if the directory has non-root ownership or the sticky
460 * bit cannot be set and fail.
461 */
462static int
463trans_mkdir(const char *path, int mode)
464{
465    struct stat buf;
466
467    if (lstat(path, &buf) != 0) {
468	if (errno != ENOENT) {
469	    prmsg(1, "mkdir: ERROR: (l)stat failed for %s (%d)\n",
470		  path, errno);
471	    return -1;
472	}
473	/* Dir doesn't exist. Try to create it */
474
475#if !defined(WIN32) && !defined(__CYGWIN__)
476	/*
477	 * 'sticky' bit requested: assume application makes
478	 * certain security implications. If effective user ID
479	 * is != 0: fail as we may not be able to meet them.
480	 */
481	if (geteuid() != 0) {
482	    if (mode & 01000) {
483		prmsg(1, "mkdir: ERROR: euid != 0,"
484		      "directory %s will not be created.\n",
485		      path);
486#ifdef FAIL_HARD
487		return -1;
488#endif
489	    } else {
490		prmsg(1, "mkdir: Cannot create %s with root ownership\n",
491		      path);
492	    }
493	}
494#endif
495
496#ifndef WIN32
497	if (mkdir(path, mode) == 0) {
498	    if (chmod(path, mode)) {
499		prmsg(1, "mkdir: ERROR: Mode of %s should be set to %04o\n",
500		      path, mode);
501#ifdef FAIL_HARD
502		return -1;
503#endif
504	    }
505#else
506	if (mkdir(path) == 0) {
507#endif
508	} else {
509	    prmsg(1, "mkdir: ERROR: Cannot create %s\n",
510		  path);
511	    return -1;
512	}
513
514	return 0;
515
516    } else {
517	if (S_ISDIR(buf.st_mode)) {
518	    int updateOwner = 0;
519	    int updateMode = 0;
520	    int updatedOwner = 0;
521	    int updatedMode = 0;
522	    int status = 0;
523	    /* Check if the directory's ownership is OK. */
524	    if (buf.st_uid != 0)
525		updateOwner = 1;
526
527	    /*
528	     * Check if the directory's mode is OK.  An exact match isn't
529	     * required, just a mode that isn't more permissive than the
530	     * one requested.
531	     */
532	    if ((~mode) & 0077 & buf.st_mode)
533		updateMode = 1;
534
535	    /*
536	     * If the directory is not writeable not everybody may
537	     * be able to create sockets. Therefore warn if mode
538	     * cannot be fixed.
539	     */
540	    if ((~buf.st_mode) & 0022 & mode) {
541		updateMode = 1;
542		status |= WARN_NO_ACCESS;
543	    }
544
545	    /*
546	     * If 'sticky' bit is requested fail if owner isn't root
547	     * as we assume the caller makes certain security implications
548	     */
549	    if (mode & 01000) {
550		status |= FAIL_IF_NOT_ROOT;
551		if (!(buf.st_mode & 01000)) {
552		    status |= FAIL_IF_NOMODE;
553		    updateMode = 1;
554		}
555	    }
556
557#ifdef HAS_FCHOWN
558	    /*
559	     * If fchown(2) and fchmod(2) are available, try to correct the
560	     * directory's owner and mode.  Otherwise it isn't safe to attempt
561	     * to do this.
562	     */
563	    if (updateMode || updateOwner) {
564		int fd = -1;
565		struct stat fbuf;
566		if ((fd = open(path, O_RDONLY)) != -1) {
567		    if (fstat(fd, &fbuf) == -1) {
568			prmsg(1, "mkdir: ERROR: fstat failed for %s (%d)\n",
569			      path, errno);
570			close(fd);
571			return -1;
572		    }
573		    /*
574		     * Verify that we've opened the same directory as was
575		     * checked above.
576		     */
577		    if (!S_ISDIR(fbuf.st_mode) ||
578			buf.st_dev != fbuf.st_dev ||
579			buf.st_ino != fbuf.st_ino) {
580			prmsg(1, "mkdir: ERROR: inode for %s changed\n",
581			      path);
582			close(fd);
583			return -1;
584		    }
585		    if (updateOwner && fchown(fd, 0, 0) == 0)
586			updatedOwner = 1;
587		    if (updateMode && fchmod(fd, mode) == 0)
588			updatedMode = 1;
589		    close(fd);
590		}
591	    }
592#endif
593
594	    if (updateOwner && !updatedOwner) {
595#ifdef FAIL_HARD
596		if (status & FAIL_IF_NOT_ROOT) {
597		    prmsg(1, "mkdir: ERROR: Owner of %s must be set to root\n",
598			  path);
599		    return -1;
600		}
601#endif
602#if !defined(__APPLE_CC__) && !defined(__CYGWIN__)
603		prmsg(1, "mkdir: Owner of %s should be set to root\n",
604		      path);
605#endif
606	    }
607
608	    if (updateMode && !updatedMode) {
609#ifdef FAIL_HARD
610		if (status & FAIL_IF_NOMODE) {
611		    prmsg(1, "mkdir: ERROR: Mode of %s must be set to %04o\n",
612			  path, mode);
613		    return -1;
614		}
615#endif
616		prmsg(1, "mkdir: Mode of %s should be set to %04o\n",
617		      path, mode);
618		if (status & WARN_NO_ACCESS) {
619		    prmsg(1, "mkdir: this may cause subsequent errors\n");
620		}
621	    }
622	    return 0;
623	}
624	return -1;
625    }
626
627    /* In all other cases, fail */
628    return -1;
629}
630
631#endif /* TRANS_SERVER */
632