xcb_auth.c revision aa30ed02
1/* Copyright (C) 2001-2004 Bart Massey and Jamey Sharp. 2 * 3 * Permission is hereby granted, free of charge, to any person obtaining a 4 * copy of this software and associated documentation files (the "Software"), 5 * to deal in the Software without restriction, including without limitation 6 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 * and/or sell copies of the Software, and to permit persons to whom the 8 * Software is furnished to do so, subject to the following conditions: 9 * 10 * The above copyright notice and this permission notice shall be included in 11 * all copies or substantial portions of the Software. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 17 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 * 20 * Except as contained in this notice, the names of the authors or their 21 * institutions shall not be used in advertising or otherwise to promote the 22 * sale, use or other dealings in this Software without prior written 23 * authorization from the authors. 24 */ 25 26/* Authorization systems for the X protocol. */ 27 28#ifdef HAVE_CONFIG_H 29#include "config.h" 30#endif 31 32#include <assert.h> 33#include <X11/Xauth.h> 34#include <sys/param.h> 35#include <unistd.h> 36#include <stdlib.h> 37#include <time.h> 38 39#ifdef __INTERIX 40/* _don't_ ask. interix has INADDR_LOOPBACK in here. */ 41#include <rpc/types.h> 42#endif 43 44#ifdef _WIN32 45#ifdef HASXDMAUTH 46/* We must include the wrapped windows.h before any system header which includes 47 it unwrapped, to avoid conflicts with types defined in X headers */ 48#include <X11/Xwindows.h> 49#endif 50#include "xcb_windefs.h" 51#else 52#include <arpa/inet.h> 53#include <sys/socket.h> 54#include <netinet/in.h> 55#include <sys/un.h> 56#endif /* _WIN32 */ 57 58#include "xcb.h" 59#include "xcbint.h" 60 61#ifdef HASXDMAUTH 62#include <X11/Xdmcp.h> 63#endif 64 65enum auth_protos { 66#ifdef HASXDMAUTH 67 AUTH_XA1, 68#endif 69 AUTH_MC1, 70 N_AUTH_PROTOS 71}; 72 73#define AUTH_PROTO_XDM_AUTHORIZATION "XDM-AUTHORIZATION-1" 74#define AUTH_PROTO_MIT_MAGIC_COOKIE "MIT-MAGIC-COOKIE-1" 75 76static char *authnames[N_AUTH_PROTOS] = { 77#ifdef HASXDMAUTH 78 AUTH_PROTO_XDM_AUTHORIZATION, 79#endif 80 AUTH_PROTO_MIT_MAGIC_COOKIE, 81}; 82 83static int authnameslen[N_AUTH_PROTOS] = { 84#ifdef HASXDMAUTH 85 sizeof(AUTH_PROTO_XDM_AUTHORIZATION) - 1, 86#endif 87 sizeof(AUTH_PROTO_MIT_MAGIC_COOKIE) - 1, 88}; 89 90static size_t memdup(char **dst, void *src, size_t len) 91{ 92 if(len) 93 *dst = malloc(len); 94 else 95 *dst = 0; 96 if(!*dst) 97 return 0; 98 memcpy(*dst, src, len); 99 return len; 100} 101 102static int authname_match(enum auth_protos kind, char *name, size_t namelen) 103{ 104 if(authnameslen[kind] != namelen) 105 return 0; 106 if(memcmp(authnames[kind], name, namelen)) 107 return 0; 108 return 1; 109} 110 111#define SIN6_ADDR(s) (&((struct sockaddr_in6 *)s)->sin6_addr) 112 113static Xauth *get_authptr(struct sockaddr *sockname, int display) 114{ 115 char *addr = 0; 116 int addrlen = 0; 117 unsigned short family; 118 char hostnamebuf[256]; /* big enough for max hostname */ 119 char dispbuf[40]; /* big enough to hold more than 2^64 base 10 */ 120 int dispbuflen; 121 122 family = FamilyLocal; /* 256 */ 123 switch(sockname->sa_family) 124 { 125#ifdef AF_INET6 126 case AF_INET6: 127 addr = (char *) SIN6_ADDR(sockname); 128 addrlen = sizeof(*SIN6_ADDR(sockname)); 129 if(!IN6_IS_ADDR_V4MAPPED(SIN6_ADDR(sockname))) 130 { 131 if(!IN6_IS_ADDR_LOOPBACK(SIN6_ADDR(sockname))) 132 family = XCB_FAMILY_INTERNET_6; 133 break; 134 } 135 addr += 12; 136 /* if v4-mapped, fall through. */ 137#endif 138 case AF_INET: 139 if(!addr) 140 addr = (char *) &((struct sockaddr_in *)sockname)->sin_addr; 141 addrlen = sizeof(((struct sockaddr_in *)sockname)->sin_addr); 142 if(*(in_addr_t *) addr != htonl(INADDR_LOOPBACK)) 143 family = XCB_FAMILY_INTERNET; 144 break; 145 case AF_UNIX: 146 break; 147 default: 148 return 0; /* cannot authenticate this family */ 149 } 150 151 dispbuflen = snprintf(dispbuf, sizeof(dispbuf), "%d", display); 152 if(dispbuflen < 0) 153 return 0; 154 /* snprintf may have truncate our text */ 155 dispbuflen = MIN(dispbuflen, sizeof(dispbuf) - 1); 156 157 if (family == FamilyLocal) { 158 if (gethostname(hostnamebuf, sizeof(hostnamebuf)) == -1) 159 return 0; /* do not know own hostname */ 160 addr = hostnamebuf; 161 addrlen = strlen(addr); 162 } 163 164 return XauGetBestAuthByAddr (family, 165 (unsigned short) addrlen, addr, 166 (unsigned short) dispbuflen, dispbuf, 167 N_AUTH_PROTOS, authnames, authnameslen); 168} 169 170#ifdef HASXDMAUTH 171static int next_nonce(void) 172{ 173 static int nonce = 0; 174 static pthread_mutex_t nonce_mutex = PTHREAD_MUTEX_INITIALIZER; 175 int ret; 176 pthread_mutex_lock(&nonce_mutex); 177 ret = nonce++; 178 pthread_mutex_unlock(&nonce_mutex); 179 return ret; 180} 181 182static void do_append(char *buf, int *idxp, void *val, size_t valsize) { 183 memcpy(buf + *idxp, val, valsize); 184 *idxp += valsize; 185} 186#endif 187 188static int compute_auth(xcb_auth_info_t *info, Xauth *authptr, struct sockaddr *sockname) 189{ 190 if (authname_match(AUTH_MC1, authptr->name, authptr->name_length)) { 191 info->datalen = memdup(&info->data, authptr->data, authptr->data_length); 192 if(!info->datalen) 193 return 0; 194 return 1; 195 } 196#ifdef HASXDMAUTH 197#define APPEND(buf,idx,val) do_append((buf),&(idx),&(val),sizeof(val)) 198 if (authname_match(AUTH_XA1, authptr->name, authptr->name_length)) { 199 int j; 200 201 info->data = malloc(192 / 8); 202 if(!info->data) 203 return 0; 204 205 for (j = 0; j < 8; j++) 206 info->data[j] = authptr->data[j]; 207 switch(sockname->sa_family) { 208 case AF_INET: 209 /*block*/ { 210 struct sockaddr_in *si = (struct sockaddr_in *) sockname; 211 APPEND(info->data, j, si->sin_addr.s_addr); 212 APPEND(info->data, j, si->sin_port); 213 } 214 break; 215#ifdef AF_INET6 216 case AF_INET6: 217 /*block*/ { 218 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *) sockname; 219 if(IN6_IS_ADDR_V4MAPPED(SIN6_ADDR(sockname))) 220 { 221 do_append(info->data, &j, &si6->sin6_addr.s6_addr[12], 4); 222 APPEND(info->data, j, si6->sin6_port); 223 } 224 else 225 { 226 /* XDM-AUTHORIZATION-1 does not handle IPv6 correctly. Do the 227 same thing Xlib does: use all zeroes for the 4-byte address 228 and 2-byte port number. */ 229 uint32_t fakeaddr = 0; 230 uint16_t fakeport = 0; 231 APPEND(info->data, j, fakeaddr); 232 APPEND(info->data, j, fakeport); 233 } 234 } 235 break; 236#endif 237 case AF_UNIX: 238 /*block*/ { 239 uint32_t fakeaddr = htonl(0xffffffff - next_nonce()); 240 uint16_t fakeport = htons(getpid()); 241 APPEND(info->data, j, fakeaddr); 242 APPEND(info->data, j, fakeport); 243 } 244 break; 245 default: 246 free(info->data); 247 return 0; /* do not know how to build this */ 248 } 249 { 250 uint32_t now = htonl(time(0)); 251 APPEND(info->data, j, now); 252 } 253 assert(j <= 192 / 8); 254 while (j < 192 / 8) 255 info->data[j++] = 0; 256 info->datalen = j; 257 XdmcpWrap ((unsigned char *) info->data, (unsigned char *) authptr->data + 8, (unsigned char *) info->data, info->datalen); 258 return 1; 259 } 260#undef APPEND 261#endif 262 263 return 0; /* Unknown authorization type */ 264} 265 266/* `sockaddr_un.sun_path' typical size usually ranges between 92 and 108 */ 267#define INITIAL_SOCKNAME_SLACK 108 268 269/* Return a dynamically allocated socket address structure according 270 to the value returned by either getpeername() or getsockname() 271 (according to POSIX, applications should not assume a particular 272 length for `sockaddr_un.sun_path') */ 273static struct sockaddr *get_peer_sock_name(int (*socket_func)(int, 274 struct sockaddr *, 275 socklen_t *), 276 int fd) 277{ 278 socklen_t socknamelen = sizeof(struct sockaddr) + INITIAL_SOCKNAME_SLACK; 279 socklen_t actual_socknamelen = socknamelen; 280 struct sockaddr *sockname = malloc(socknamelen); 281 282 if (sockname == NULL) 283 return NULL; 284 285 /* Both getpeername() and getsockname() truncates sockname if 286 there is not enough space and set the required length in 287 actual_socknamelen */ 288 if (socket_func(fd, sockname, &actual_socknamelen) == -1) 289 goto sock_or_realloc_error; 290 291 if (actual_socknamelen > socknamelen) 292 { 293 struct sockaddr *new_sockname = NULL; 294 socknamelen = actual_socknamelen; 295 296 if ((new_sockname = realloc(sockname, actual_socknamelen)) == NULL) 297 goto sock_or_realloc_error; 298 299 sockname = new_sockname; 300 301 if (socket_func(fd, sockname, &actual_socknamelen) == -1 || 302 actual_socknamelen > socknamelen) 303 goto sock_or_realloc_error; 304 } 305 306 return sockname; 307 308 sock_or_realloc_error: 309 free(sockname); 310 return NULL; 311} 312 313int _xcb_get_auth_info(int fd, xcb_auth_info_t *info, int display) 314{ 315 /* code adapted from Xlib/ConnDis.c, xtrans/Xtranssocket.c, 316 xtrans/Xtransutils.c */ 317 struct sockaddr *sockname = NULL; 318 int gotsockname = 0; 319 Xauth *authptr = 0; 320 int ret = 1; 321 322 /* Some systems like hpux or Hurd do not expose peer names 323 * for UNIX Domain Sockets, but this is irrelevant, 324 * since compute_auth() ignores the peer name in this 325 * case anyway.*/ 326 if ((sockname = get_peer_sock_name(getpeername, fd)) == NULL) 327 { 328 if ((sockname = get_peer_sock_name(getsockname, fd)) == NULL) 329 return 0; /* can only authenticate sockets */ 330 if (sockname->sa_family != AF_UNIX) 331 { 332 free(sockname); 333 return 0; /* except for AF_UNIX, sockets should have peernames */ 334 } 335 gotsockname = 1; 336 } 337 338 authptr = get_authptr(sockname, display); 339 if (authptr == 0) 340 { 341 free(sockname); 342 return 0; /* cannot find good auth data */ 343 } 344 345 info->namelen = memdup(&info->name, authptr->name, authptr->name_length); 346 if (!info->namelen) 347 goto no_auth; /* out of memory */ 348 349 if (!gotsockname) 350 { 351 free(sockname); 352 353 if ((sockname = get_peer_sock_name(getsockname, fd)) == NULL) 354 { 355 free(info->name); 356 goto no_auth; /* can only authenticate sockets */ 357 } 358 } 359 360 ret = compute_auth(info, authptr, sockname); 361 if(!ret) 362 { 363 free(info->name); 364 goto no_auth; /* cannot build auth record */ 365 } 366 367 free(sockname); 368 sockname = NULL; 369 370 XauDisposeAuth(authptr); 371 return ret; 372 373 no_auth: 374 free(sockname); 375 376 info->name = 0; 377 info->namelen = 0; 378 XauDisposeAuth(authptr); 379 return 0; 380} 381