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