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