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