Xtransutil.c revision 47a4502c
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#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,%p)\n",*familyp,*addrlenp,*addrp);
95
96    switch( *familyp )
97    {
98#if defined(TCPCONN)
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) */
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
173    default:
174	prmsg(1,"ConvertAddress: Unknown family type %d\n",
175	      *familyp);
176	return -1;
177    }
178
179
180    if (*familyp == FamilyLocal)
181    {
182	/*
183	 * In the case of a local connection, we need to get the
184	 * host name for authentication.
185	 */
186
187	char hostnamebuf[256];
188	int len = TRANS(GetHostname) (hostnamebuf, sizeof hostnamebuf);
189
190	if (len > 0) {
191	    if (*addrp && *addrlenp < (len + 1))
192	    {
193		free (*addrp);
194		*addrp = NULL;
195	    }
196	    if (!*addrp)
197		*addrp = malloc (len + 1);
198	    if (*addrp) {
199		strcpy ((char *) *addrp, hostnamebuf);
200		*addrlenp = len;
201	    } else {
202		*addrlenp = 0;
203	    }
204	}
205	else
206	{
207	    if (*addrp)
208		free (*addrp);
209	    *addrp = NULL;
210	    *addrlenp = 0;
211	}
212    }
213
214    return 0;
215}
216
217#endif /* X11_t */
218
219#ifdef ICE_t
220
221/* Needed for _XGethostbyaddr usage in TRANS(GetPeerNetworkId) */
222# if defined(TCPCONN) || defined(UNIXCONN)
223#  define X_INCLUDE_NETDB_H
224#  define XOS_USE_NO_LOCKING
225#  include <X11/Xos_r.h>
226# endif
227
228#include <signal.h>
229
230char *
231TRANS(GetMyNetworkId) (XtransConnInfo ciptr)
232
233{
234    int		family = ciptr->family;
235    char 	*addr = ciptr->addr;
236    char	hostnamebuf[256];
237    char 	*networkId = NULL;
238    const char	*transName = ciptr->transptr->TransName;
239
240    if (gethostname (hostnamebuf, sizeof (hostnamebuf)) < 0)
241    {
242	return (NULL);
243    }
244
245    switch (family)
246    {
247#if defined(UNIXCONN) || defined(LOCALCONN)
248    case AF_UNIX:
249    {
250	struct sockaddr_un *saddr = (struct sockaddr_un *) addr;
251	networkId = malloc (3 + strlen (transName) +
252	    strlen (hostnamebuf) + strlen (saddr->sun_path));
253	if (networkId != NULL)
254	    sprintf (networkId, "%s/%s:%s", transName,
255		     hostnamebuf, saddr->sun_path);
256	break;
257    }
258#endif /* defined(UNIXCONN) || defined(LOCALCONN) */
259
260#if defined(TCPCONN)
261    case AF_INET:
262#if defined(IPv6) && defined(AF_INET6)
263    case AF_INET6:
264#endif
265    {
266	struct sockaddr_in *saddr = (struct sockaddr_in *) addr;
267#if defined(IPv6) && defined(AF_INET6)
268	struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) addr;
269#endif
270	int portnum;
271	char portnumbuf[10];
272
273
274#if defined(IPv6) && defined(AF_INET6)
275	if (family == AF_INET6)
276	    portnum = ntohs (saddr6->sin6_port);
277	else
278#endif
279	    portnum = ntohs (saddr->sin_port);
280
281	snprintf (portnumbuf, sizeof(portnumbuf), "%d", portnum);
282	networkId = malloc (3 + strlen (transName) +
283	    strlen (hostnamebuf) + strlen (portnumbuf));
284	if (networkId != NULL)
285	    sprintf (networkId, "%s/%s:%s", transName, hostnamebuf, portnumbuf);
286	break;
287    }
288#endif /* defined(TCPCONN) */
289
290
291    default:
292	break;
293    }
294
295    return (networkId);
296}
297
298#include <setjmp.h>
299static jmp_buf env;
300
301#ifdef SIGALRM
302static volatile int nameserver_timedout = 0;
303
304static void
305nameserver_lost(int sig _X_UNUSED)
306{
307  nameserver_timedout = 1;
308  longjmp (env, -1);
309  /* NOTREACHED */
310}
311#endif /* SIGALARM */
312
313
314char *
315TRANS(GetPeerNetworkId) (XtransConnInfo ciptr)
316
317{
318    int		family = ciptr->family;
319    char	*peer_addr = ciptr->peeraddr;
320    char	*hostname;
321    char	addrbuf[256];
322    const char	*addr = NULL;
323
324    switch (family)
325    {
326    case AF_UNSPEC:
327#if defined(UNIXCONN) || defined(LOCALCONN)
328    case AF_UNIX:
329    {
330	if (gethostname (addrbuf, sizeof (addrbuf)) == 0)
331	    addr = addrbuf;
332	break;
333    }
334#endif /* defined(UNIXCONN) || defined(LOCALCONN) */
335
336#if defined(TCPCONN)
337    case AF_INET:
338#if defined(IPv6) && defined(AF_INET6)
339    case AF_INET6:
340#endif
341    {
342	struct sockaddr_in *saddr = (struct sockaddr_in *) peer_addr;
343#if defined(IPv6) && defined(AF_INET6)
344	struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) peer_addr;
345#endif
346	char *address;
347	int addresslen;
348#ifdef XTHREADS_NEEDS_BYNAMEPARAMS
349	_Xgethostbynameparams hparams;
350#endif
351	struct hostent * volatile hostp = NULL;
352
353#if defined(IPv6) && defined(AF_INET6)
354	if (family == AF_INET6)
355	{
356	    address = (char *) &saddr6->sin6_addr;
357	    addresslen = sizeof (saddr6->sin6_addr);
358	}
359	else
360#endif
361	{
362	    address = (char *) &saddr->sin_addr;
363	    addresslen = sizeof (saddr->sin_addr);
364	}
365
366#ifdef SIGALRM
367	/*
368	 * gethostbyaddr can take a LONG time if the host does not exist.
369	 * Assume that if it does not respond in NAMESERVER_TIMEOUT seconds
370	 * that something is wrong and do not make the user wait.
371	 * gethostbyaddr will continue after a signal, so we have to
372	 * jump out of it.
373	 */
374
375	nameserver_timedout = 0;
376	signal (SIGALRM, nameserver_lost);
377	alarm (4);
378	if (setjmp(env) == 0) {
379#endif
380	    hostp = _XGethostbyaddr (address, addresslen, family, hparams);
381#ifdef SIGALRM
382	}
383	alarm (0);
384#endif
385	if (hostp != NULL)
386	  addr = hostp->h_name;
387	else
388#if defined(IPv6) && defined(AF_INET6)
389	  addr = inet_ntop (family, address, addrbuf, sizeof (addrbuf));
390#else
391	  addr = inet_ntoa (saddr->sin_addr);
392#endif
393	break;
394    }
395
396#endif /* defined(TCPCONN) */
397
398
399    default:
400	return (NULL);
401    }
402
403
404    hostname = malloc (strlen (ciptr->transptr->TransName) +
405                       (addr ? strlen (addr) : 0) + 2);
406    if (hostname)
407    {
408	strcpy (hostname, ciptr->transptr->TransName);
409	strcat (hostname, "/");
410	if (addr)
411	    strcat (hostname, addr);
412    }
413    return (hostname);
414}
415
416#endif /* ICE_t */
417
418
419#if defined(WIN32) && defined(TCPCONN)
420int
421TRANS(WSAStartup) (void)
422{
423    static WSADATA wsadata;
424
425    prmsg (2,"WSAStartup()\n");
426
427    if (!wsadata.wVersion && WSAStartup(MAKEWORD(2,2), &wsadata))
428        return 1;
429    return 0;
430}
431#endif
432
433#ifdef TRANS_SERVER
434#include <sys/types.h>
435#include <sys/stat.h>
436#include <errno.h>
437
438#if !defined(S_IFLNK) && !defined(S_ISLNK)
439#undef lstat
440#define lstat(a,b) stat(a,b)
441#endif
442
443#define FAIL_IF_NOMODE  1
444#define FAIL_IF_NOT_ROOT 2
445#define WARN_NO_ACCESS 4
446
447/*
448 * We make the assumption that when the 'sticky' (t) bit is requested
449 * it's not save if the directory has non-root ownership or the sticky
450 * bit cannot be set and fail.
451 */
452static int
453trans_mkdir(const char *path, int mode)
454{
455    struct stat buf;
456
457    if (lstat(path, &buf) != 0) {
458	if (errno != ENOENT) {
459	    prmsg(1, "mkdir: ERROR: (l)stat failed for %s (%d)\n",
460		  path, errno);
461	    return -1;
462	}
463	/* Dir doesn't exist. Try to create it */
464
465#if !defined(WIN32) && !defined(__CYGWIN__)
466	/*
467	 * 'sticky' bit requested: assume application makes
468	 * certain security implications. If effective user ID
469	 * is != 0: fail as we may not be able to meet them.
470	 */
471	if (geteuid() != 0) {
472	    if (mode & 01000) {
473		prmsg(1, "mkdir: ERROR: euid != 0,"
474		      "directory %s will not be created.\n",
475		      path);
476#ifdef FAIL_HARD
477		return -1;
478#endif
479	    } else {
480		prmsg(1, "mkdir: Cannot create %s with root ownership\n",
481		      path);
482	    }
483	}
484#endif
485
486#ifndef WIN32
487	if (mkdir(path, mode) == 0) {
488	    if (chmod(path, mode)) {
489		prmsg(1, "mkdir: ERROR: Mode of %s should be set to %04o\n",
490		      path, mode);
491#ifdef FAIL_HARD
492		return -1;
493#endif
494	    }
495#else
496	if (mkdir(path) == 0) {
497#endif
498	} else {
499	    prmsg(1, "mkdir: ERROR: Cannot create %s\n",
500		  path);
501	    return -1;
502	}
503
504	return 0;
505
506    } else {
507	if (S_ISDIR(buf.st_mode)) {
508	    int updateOwner = 0;
509	    int updateMode = 0;
510	    int updatedOwner = 0;
511	    int updatedMode = 0;
512	    int status = 0;
513	    /* Check if the directory's ownership is OK. */
514	    if (buf.st_uid != 0)
515		updateOwner = 1;
516
517	    /*
518	     * Check if the directory's mode is OK.  An exact match isn't
519	     * required, just a mode that isn't more permissive than the
520	     * one requested.
521	     */
522	    if ((~mode) & 0077 & buf.st_mode)
523		updateMode = 1;
524
525	    /*
526	     * If the directory is not writeable not everybody may
527	     * be able to create sockets. Therefore warn if mode
528	     * cannot be fixed.
529	     */
530	    if ((~buf.st_mode) & 0022 & mode) {
531		updateMode = 1;
532		status |= WARN_NO_ACCESS;
533	    }
534
535	    /*
536	     * If 'sticky' bit is requested fail if owner isn't root
537	     * as we assume the caller makes certain security implications
538	     */
539	    if (mode & 01000) {
540		status |= FAIL_IF_NOT_ROOT;
541		if (!(buf.st_mode & 01000)) {
542		    status |= FAIL_IF_NOMODE;
543		    updateMode = 1;
544		}
545	    }
546
547#ifdef HAS_FCHOWN
548	    /*
549	     * If fchown(2) and fchmod(2) are available, try to correct the
550	     * directory's owner and mode.  Otherwise it isn't safe to attempt
551	     * to do this.
552	     */
553	    if (updateMode || updateOwner) {
554		int fd = -1;
555		struct stat fbuf;
556		if ((fd = open(path, O_RDONLY)) != -1) {
557		    if (fstat(fd, &fbuf) == -1) {
558			prmsg(1, "mkdir: ERROR: fstat failed for %s (%d)\n",
559			      path, errno);
560			close(fd);
561			return -1;
562		    }
563		    /*
564		     * Verify that we've opened the same directory as was
565		     * checked above.
566		     */
567		    if (!S_ISDIR(fbuf.st_mode) ||
568			buf.st_dev != fbuf.st_dev ||
569			buf.st_ino != fbuf.st_ino) {
570			prmsg(1, "mkdir: ERROR: inode for %s changed\n",
571			      path);
572			close(fd);
573			return -1;
574		    }
575		    if (updateOwner && fchown(fd, 0, 0) == 0)
576			updatedOwner = 1;
577		    if (updateMode && fchmod(fd, mode) == 0)
578			updatedMode = 1;
579		    close(fd);
580		}
581	    }
582#endif
583
584	    if (updateOwner && !updatedOwner) {
585#ifdef FAIL_HARD
586		if (status & FAIL_IF_NOT_ROOT) {
587		    prmsg(1, "mkdir: ERROR: Owner of %s must be set to root\n",
588			  path);
589		    return -1;
590		}
591#endif
592#if !defined(__APPLE_CC__) && !defined(__CYGWIN__)
593		prmsg(1, "mkdir: Owner of %s should be set to root\n",
594		      path);
595#endif
596	    }
597
598	    if (updateMode && !updatedMode) {
599#ifdef FAIL_HARD
600		if (status & FAIL_IF_NOMODE) {
601		    prmsg(1, "mkdir: ERROR: Mode of %s must be set to %04o\n",
602			  path, mode);
603		    return -1;
604		}
605#endif
606		prmsg(1, "mkdir: Mode of %s should be set to %04o\n",
607		      path, mode);
608		if (status & WARN_NO_ACCESS) {
609		    prmsg(1, "mkdir: this may cause subsequent errors\n");
610		}
611	    }
612	    return 0;
613	}
614	return -1;
615    }
616
617    /* In all other cases, fail */
618    return -1;
619}
620
621#endif /* TRANS_SERVER */
622