Xtransutil.c revision b53e5eea
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 Independant 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#ifdef X11_t
66
67/*
68 * These values come from X.h and Xauth.h, and MUST match them. Some
69 * of these values are also defined by the ChangeHost protocol message.
70 */
71
72#define FamilyInternet		0	/* IPv4 */
73#define FamilyDECnet		1
74#define FamilyChaos		2
75#define FamilyInternet6		6
76#define FamilyAmoeba		33
77#define FamilyLocalHost		252
78#define FamilyKrb5Principal	253
79#define FamilyNetname		254
80#define FamilyLocal		256
81#define FamilyWild		65535
82
83/*
84 * TRANS(ConvertAddress) converts a sockaddr based address to an
85 * X authorization based address. Some of this is defined as part of
86 * the ChangeHost protocol. The rest is just done in a consistent manner.
87 */
88
89int
90TRANS(ConvertAddress)(int *familyp, int *addrlenp, Xtransaddr **addrp)
91
92{
93
94    PRMSG(2,"ConvertAddress(%d,%d,%x)\n",*familyp,*addrlenp,*addrp);
95
96    switch( *familyp )
97    {
98#if defined(TCPCONN) || defined(STREAMSCONN)
99    case AF_INET:
100    {
101	/*
102	 * Check for the BSD hack localhost address 127.0.0.1.
103	 * In this case, we are really FamilyLocal.
104	 */
105
106	struct sockaddr_in saddr;
107	int len = sizeof(saddr.sin_addr.s_addr);
108	char *cp = (char *) &saddr.sin_addr.s_addr;
109
110	memcpy (&saddr, *addrp, sizeof (struct sockaddr_in));
111
112	if ((len == 4) && (cp[0] == 127) && (cp[1] == 0) &&
113	    (cp[2] == 0) && (cp[3] == 1))
114	{
115	    *familyp=FamilyLocal;
116	}
117	else
118	{
119	    *familyp=FamilyInternet;
120	    *addrlenp=len;
121	    memcpy(*addrp,&saddr.sin_addr,len);
122	}
123	break;
124    }
125
126#if defined(IPv6) && defined(AF_INET6)
127    case AF_INET6:
128    {
129	struct sockaddr_in6 saddr6;
130
131	memcpy (&saddr6, *addrp, sizeof (struct sockaddr_in6));
132
133	if (IN6_IS_ADDR_LOOPBACK(&saddr6.sin6_addr))
134	{
135	    *familyp=FamilyLocal;
136	}
137	else if (IN6_IS_ADDR_V4MAPPED(&(saddr6.sin6_addr))) {
138	    char *cp = (char *) &saddr6.sin6_addr.s6_addr[12];
139
140	    if ((cp[0] == 127) && (cp[1] == 0) &&
141	      (cp[2] == 0) && (cp[3] == 1))
142	    {
143		*familyp=FamilyLocal;
144	    }
145	    else
146	    {
147		*familyp=FamilyInternet;
148		*addrlenp = sizeof (struct in_addr);
149		memcpy(*addrp,cp,*addrlenp);
150	    }
151	}
152	else
153	{
154	    *familyp=FamilyInternet6;
155	    *addrlenp=sizeof(saddr6.sin6_addr);
156	    memcpy(*addrp,&saddr6.sin6_addr,sizeof(saddr6.sin6_addr));
157	}
158	break;
159    }
160#endif /* IPv6 */
161#endif /* defined(TCPCONN) || defined(STREAMSCONN) */
162
163
164#if defined(UNIXCONN) || defined(LOCALCONN)
165    case AF_UNIX:
166    {
167	*familyp=FamilyLocal;
168	break;
169    }
170#endif /* defined(UNIXCONN) || defined(LOCALCONN) */
171
172#if (defined(__SCO__) || defined(__UNIXWARE__)) && defined(LOCALCONN)
173    case 0:
174    {
175	*familyp=FamilyLocal;
176	break;
177    }
178#endif
179
180    default:
181	PRMSG(1,"ConvertAddress: Unknown family type %d\n",
182	      *familyp, 0,0 );
183	return -1;
184    }
185
186
187    if (*familyp == FamilyLocal)
188    {
189	/*
190	 * In the case of a local connection, we need to get the
191	 * host name for authentication.
192	 */
193
194	char hostnamebuf[256];
195	int len = TRANS(GetHostname) (hostnamebuf, sizeof hostnamebuf);
196
197	if (len > 0) {
198	    if (*addrp && *addrlenp < (len + 1))
199	    {
200		xfree ((char *) *addrp);
201		*addrp = NULL;
202	    }
203	    if (!*addrp)
204		*addrp = (Xtransaddr *) xalloc (len + 1);
205	    if (*addrp) {
206		strcpy ((char *) *addrp, hostnamebuf);
207		*addrlenp = len;
208	    } else {
209		*addrlenp = 0;
210	    }
211	}
212	else
213	{
214	    if (*addrp)
215		xfree ((char *) *addrp);
216	    *addrp = NULL;
217	    *addrlenp = 0;
218	}
219    }
220
221    return 0;
222}
223
224#endif /* X11_t */
225
226#ifdef ICE_t
227
228/* Needed for _XGethostbyaddr usage in TRANS(GetPeerNetworkId) */
229# if defined(TCPCONN) || defined(UNIXCONN)
230#  define X_INCLUDE_NETDB_H
231#  define XOS_USE_NO_LOCKING
232#  include <X11/Xos_r.h>
233# endif
234
235#include <signal.h>
236
237char *
238TRANS(GetMyNetworkId) (XtransConnInfo ciptr)
239
240{
241    int		family = ciptr->family;
242    char 	*addr = ciptr->addr;
243    char	hostnamebuf[256];
244    char 	*networkId = NULL;
245    char	*transName = ciptr->transptr->TransName;
246
247    if (gethostname (hostnamebuf, sizeof (hostnamebuf)) < 0)
248    {
249	return (NULL);
250    }
251
252    switch (family)
253    {
254#if defined(UNIXCONN) || defined(STREAMSCONN) || defined(LOCALCONN)
255    case AF_UNIX:
256    {
257	struct sockaddr_un *saddr = (struct sockaddr_un *) addr;
258	networkId = (char *) xalloc (3 + strlen (transName) +
259	    strlen (hostnamebuf) + strlen (saddr->sun_path));
260	sprintf (networkId, "%s/%s:%s", transName,
261	    hostnamebuf, saddr->sun_path);
262	break;
263    }
264#endif /* defined(UNIXCONN) || defined(STREAMSCONN) || defined(LOCALCONN) */
265
266#if defined(TCPCONN) || defined(STREAMSCONN)
267    case AF_INET:
268#if defined(IPv6) && defined(AF_INET6)
269    case AF_INET6:
270#endif
271    {
272	struct sockaddr_in *saddr = (struct sockaddr_in *) addr;
273#if defined(IPv6) && defined(AF_INET6)
274	struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) addr;
275#endif
276	int portnum;
277	char portnumbuf[10];
278
279
280#if defined(IPv6) && defined(AF_INET6)
281	if (family == AF_INET6)
282	    portnum = ntohs (saddr6->sin6_port);
283	else
284#endif
285	    portnum = ntohs (saddr->sin_port);
286
287	snprintf (portnumbuf, sizeof(portnumbuf), "%d", portnum);
288	networkId = (char *) xalloc (3 + strlen (transName) +
289	    strlen (hostnamebuf) + strlen (portnumbuf));
290	sprintf (networkId, "%s/%s:%s", transName, hostnamebuf, portnumbuf);
291	break;
292    }
293#endif /* defined(TCPCONN) || defined(STREAMSCONN) */
294
295
296    default:
297	break;
298    }
299
300    return (networkId);
301}
302
303#include <setjmp.h>
304static jmp_buf env;
305
306#ifdef SIGALRM
307static volatile int nameserver_timedout = 0;
308
309static
310#ifdef RETSIGTYPE /* set by autoconf AC_TYPE_SIGNAL */
311RETSIGTYPE
312#else /* Imake */
313#ifdef SIGNALRETURNSINT
314int
315#else
316void
317#endif
318#endif
319nameserver_lost(int sig)
320{
321  nameserver_timedout = 1;
322  longjmp (env, -1);
323  /* NOTREACHED */
324#ifdef SIGNALRETURNSINT
325  return -1;				/* for picky compilers */
326#endif
327}
328#endif /* SIGALARM */
329
330
331char *
332TRANS(GetPeerNetworkId) (XtransConnInfo ciptr)
333
334{
335    int		family = ciptr->family;
336    char	*peer_addr = ciptr->peeraddr;
337    char	*hostname;
338    char	addrbuf[256];
339    const char	*addr = NULL;
340
341    switch (family)
342    {
343    case AF_UNSPEC:
344#if defined(UNIXCONN) || defined(STREAMSCONN) || defined(LOCALCONN)
345    case AF_UNIX:
346    {
347	if (gethostname (addrbuf, sizeof (addrbuf)) == 0)
348	    addr = addrbuf;
349	break;
350    }
351#endif /* defined(UNIXCONN) || defined(STREAMSCONN) || defined(LOCALCONN) */
352
353#if defined(TCPCONN) || defined(STREAMSCONN)
354    case AF_INET:
355#if defined(IPv6) && defined(AF_INET6)
356    case AF_INET6:
357#endif
358    {
359	struct sockaddr_in *saddr = (struct sockaddr_in *) peer_addr;
360#if defined(IPv6) && defined(AF_INET6)
361	struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) peer_addr;
362#endif
363	char *address;
364	int addresslen;
365#ifdef XTHREADS_NEEDS_BYNAMEPARAMS
366	_Xgethostbynameparams hparams;
367#endif
368	struct hostent * volatile hostp = NULL;
369
370#if defined(IPv6) && defined(AF_INET6)
371	if (family == AF_INET6)
372	{
373	    address = (char *) &saddr6->sin6_addr;
374	    addresslen = sizeof (saddr6->sin6_addr);
375	}
376	else
377#endif
378	{
379	    address = (char *) &saddr->sin_addr;
380	    addresslen = sizeof (saddr->sin_addr);
381	}
382
383#ifdef SIGALRM
384	/*
385	 * gethostbyaddr can take a LONG time if the host does not exist.
386	 * Assume that if it does not respond in NAMESERVER_TIMEOUT seconds
387	 * that something is wrong and do not make the user wait.
388	 * gethostbyaddr will continue after a signal, so we have to
389	 * jump out of it.
390	 */
391
392	nameserver_timedout = 0;
393	signal (SIGALRM, nameserver_lost);
394	alarm (4);
395	if (setjmp(env) == 0) {
396#endif
397	    hostp = _XGethostbyaddr (address, addresslen, family, hparams);
398#ifdef SIGALRM
399	}
400	alarm (0);
401#endif
402	if (hostp != NULL)
403	  addr = hostp->h_name;
404	else
405#if defined(IPv6) && defined(AF_INET6)
406	  addr = inet_ntop (family, address, addrbuf, sizeof (addrbuf));
407#else
408	  addr = inet_ntoa (saddr->sin_addr);
409#endif
410	break;
411    }
412
413#endif /* defined(TCPCONN) || defined(STREAMSCONN) */
414
415
416    default:
417	return (NULL);
418    }
419
420
421    hostname = (char *) xalloc (
422	strlen (ciptr->transptr->TransName) + strlen (addr) + 2);
423    strcpy (hostname, ciptr->transptr->TransName);
424    strcat (hostname, "/");
425    if (addr)
426	strcat (hostname, addr);
427
428    return (hostname);
429}
430
431#endif /* ICE_t */
432
433
434#if defined(WIN32) && defined(TCPCONN)
435int
436TRANS(WSAStartup) (void)
437{
438    static WSADATA wsadata;
439
440    PRMSG (2,"WSAStartup()\n", 0, 0, 0);
441
442    if (!wsadata.wVersion && WSAStartup(MAKEWORD(2,2), &wsadata))
443        return 1;
444    return 0;
445}
446#endif
447
448#include <ctype.h>
449
450static int
451is_numeric (const char *str)
452{
453    int i;
454
455    for (i = 0; i < (int) strlen (str); i++)
456	if (!isdigit (str[i]))
457	    return (0);
458
459    return (1);
460}
461
462#ifdef TRANS_SERVER
463#include <sys/types.h>
464#include <sys/stat.h>
465#include <errno.h>
466
467#if !defined(S_IFLNK) && !defined(S_ISLNK)
468#undef lstat
469#define lstat(a,b) stat(a,b)
470#endif
471
472#define FAIL_IF_NOMODE  1
473#define FAIL_IF_NOT_ROOT 2
474#define WARN_NO_ACCESS 4
475
476/*
477 * We make the assumption that when the 'sticky' (t) bit is requested
478 * it's not save if the directory has non-root ownership or the sticky
479 * bit cannot be set and fail.
480 */
481static int
482trans_mkdir(const char *path, int mode)
483{
484    struct stat buf;
485
486    if (lstat(path, &buf) != 0) {
487	if (errno != ENOENT) {
488	    PRMSG(1, "mkdir: ERROR: (l)stat failed for %s (%d)\n",
489		  path, errno, 0);
490	    return -1;
491	}
492	/* Dir doesn't exist. Try to create it */
493
494#if !defined(WIN32) && !defined(__CYGWIN__)
495	/*
496	 * 'sticky' bit requested: assume application makes
497	 * certain security implications. If effective user ID
498	 * is != 0: fail as we may not be able to meet them.
499	 */
500	if (geteuid() != 0) {
501	    if (mode & 01000) {
502		PRMSG(1, "mkdir: ERROR: euid != 0,"
503		      "directory %s will not be created.\n",
504		      path, 0, 0);
505#ifdef FAIL_HARD
506		return -1;
507#endif
508	    } else {
509		PRMSG(1, "mkdir: Cannot create %s with root ownership\n",
510		      path, 0, 0);
511	    }
512	}
513#endif
514
515#ifndef WIN32
516	if (mkdir(path, mode) == 0) {
517	    if (chmod(path, mode)) {
518		PRMSG(1, "mkdir: ERROR: Mode of %s should be set to %04o\n",
519		      path, mode, 0);
520#ifdef FAIL_HARD
521		return -1;
522#endif
523	    }
524#else
525	if (mkdir(path) == 0) {
526#endif
527	} else {
528	    PRMSG(1, "mkdir: ERROR: Cannot create %s\n",
529		  path, 0, 0);
530	    return -1;
531	}
532
533	return 0;
534
535    } else {
536	if (S_ISDIR(buf.st_mode)) {
537	    int updateOwner = 0;
538	    int updateMode = 0;
539	    int updatedOwner = 0;
540	    int updatedMode = 0;
541	    int status = 0;
542	    /* Check if the directory's ownership is OK. */
543	    if (buf.st_uid != 0)
544		updateOwner = 1;
545
546	    /*
547	     * Check if the directory's mode is OK.  An exact match isn't
548	     * required, just a mode that isn't more permissive than the
549	     * one requested.
550	     */
551	    if ((~mode) & 0077 & buf.st_mode)
552		updateMode = 1;
553
554	    /*
555	     * If the directory is not writeable not everybody may
556	     * be able to create sockets. Therefore warn if mode
557	     * cannot be fixed.
558	     */
559	    if ((~buf.st_mode) & 0022 & mode) {
560		updateMode = 1;
561		status |= WARN_NO_ACCESS;
562	    }
563
564	    /*
565	     * If 'sticky' bit is requested fail if owner isn't root
566	     * as we assume the caller makes certain security implications
567	     */
568	    if (mode & 01000) {
569		status |= FAIL_IF_NOT_ROOT;
570		if (!(buf.st_mode & 01000)) {
571		    status |= FAIL_IF_NOMODE;
572		    updateMode = 1;
573		}
574	    }
575
576#ifdef HAS_FCHOWN
577	    /*
578	     * If fchown(2) and fchmod(2) are available, try to correct the
579	     * directory's owner and mode.  Otherwise it isn't safe to attempt
580	     * to do this.
581	     */
582	    if (updateMode || updateOwner) {
583		int fd = -1;
584		struct stat fbuf;
585		if ((fd = open(path, O_RDONLY)) != -1) {
586		    if (fstat(fd, &fbuf) == -1) {
587			PRMSG(1, "mkdir: ERROR: fstat failed for %s (%d)\n",
588			      path, errno, 0);
589			return -1;
590		    }
591		    /*
592		     * Verify that we've opened the same directory as was
593		     * checked above.
594		     */
595		    if (!S_ISDIR(fbuf.st_mode) ||
596			buf.st_dev != fbuf.st_dev ||
597			buf.st_ino != fbuf.st_ino) {
598			PRMSG(1, "mkdir: ERROR: inode for %s changed\n",
599			      path, 0, 0);
600			return -1;
601		    }
602		    if (updateOwner && fchown(fd, 0, 0) == 0)
603			updatedOwner = 1;
604		    if (updateMode && fchmod(fd, mode) == 0)
605			updatedMode = 1;
606		    close(fd);
607		}
608	    }
609#endif
610
611	    if (updateOwner && !updatedOwner) {
612#ifdef FAIL_HARD
613		if (status & FAIL_IF_NOT_ROOT) {
614		    PRMSG(1, "mkdir: ERROR: Owner of %s must be set to root\n",
615			  path, 0, 0);
616		    return -1;
617		}
618#endif
619#if !defined(__APPLE_CC__) && !defined(__CYGWIN__)
620	  	PRMSG(1, "mkdir: Owner of %s should be set to root\n",
621		      path, 0, 0);
622#endif
623	    }
624
625	    if (updateMode && !updatedMode) {
626#ifdef FAIL_HARD
627		if (status & FAIL_IF_NOMODE) {
628		    PRMSG(1, "mkdir: ERROR: Mode of %s must be set to %04o\n",
629			  path, mode, 0);
630		    return -1;
631		}
632#endif
633	  	PRMSG(1, "mkdir: Mode of %s should be set to %04o\n",
634		      path, mode, 0);
635		if (status & WARN_NO_ACCESS) {
636		    PRMSG(1, "mkdir: this may cause subsequent errors\n",
637			  0, 0, 0);
638		}
639	    }
640	    return 0;
641	}
642	return -1;
643    }
644
645    /* In all other cases, fail */
646    return -1;
647}
648
649#endif /* TRANS_SERVER */
650