xcb_auth.c revision 602e473d
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#include <assert.h> 29#include <X11/Xauth.h> 30#include <sys/socket.h> 31#include <netinet/in.h> 32#include <sys/un.h> 33#include <sys/param.h> 34#include <unistd.h> 35#include <stdlib.h> 36 37#include "xcb.h" 38#include "xcbint.h" 39 40#ifdef HASXDMAUTH 41#include <X11/Xdmcp.h> 42#endif 43 44enum auth_protos { 45#ifdef HASXDMAUTH 46 AUTH_XA1, 47#endif 48 AUTH_MC1, 49 N_AUTH_PROTOS 50}; 51 52#define AUTH_PROTO_XDM_AUTHORIZATION "XDM-AUTHORIZATION-1" 53#define AUTH_PROTO_MIT_MAGIC_COOKIE "MIT-MAGIC-COOKIE-1" 54 55static char *authnames[N_AUTH_PROTOS] = { 56#ifdef HASXDMAUTH 57 AUTH_PROTO_XDM_AUTHORIZATION, 58#endif 59 AUTH_PROTO_MIT_MAGIC_COOKIE, 60}; 61 62static int authnameslen[N_AUTH_PROTOS] = { 63#ifdef HASXDMAUTH 64 sizeof(AUTH_PROTO_XDM_AUTHORIZATION) - 1, 65#endif 66 sizeof(AUTH_PROTO_MIT_MAGIC_COOKIE) - 1, 67}; 68 69static size_t memdup(char **dst, void *src, size_t len) 70{ 71 if(len) 72 *dst = malloc(len); 73 else 74 *dst = 0; 75 if(!*dst) 76 return 0; 77 memcpy(*dst, src, len); 78 return len; 79} 80 81static int authname_match(enum auth_protos kind, char *name, size_t namelen) 82{ 83 if(authnameslen[kind] != namelen) 84 return 0; 85 if(memcmp(authnames[kind], name, namelen)) 86 return 0; 87 return 1; 88} 89 90#define SIN6_ADDR(s) (&((struct sockaddr_in6 *)s)->sin6_addr) 91 92static Xauth *get_authptr(struct sockaddr *sockname, unsigned int socknamelen, 93 int display) 94{ 95 char *addr = 0; 96 int addrlen = 0; 97 unsigned short family; 98 char hostnamebuf[256]; /* big enough for max hostname */ 99 char dispbuf[40]; /* big enough to hold more than 2^64 base 10 */ 100 int dispbuflen; 101 102 family = FamilyLocal; /* 256 */ 103 switch(sockname->sa_family) 104 { 105#ifdef AF_INET6 106 case AF_INET6: 107 addr = (char *) SIN6_ADDR(sockname); 108 addrlen = sizeof(*SIN6_ADDR(sockname)); 109 if(!IN6_IS_ADDR_V4MAPPED(SIN6_ADDR(sockname))) 110 { 111 if(!IN6_IS_ADDR_LOOPBACK(SIN6_ADDR(sockname))) 112 family = XCB_FAMILY_INTERNET_6; 113 break; 114 } 115 addr += 12; 116 /* if v4-mapped, fall through. */ 117#endif 118 case AF_INET: 119 if(!addr) 120 addr = (char *) &((struct sockaddr_in *)sockname)->sin_addr; 121 addrlen = sizeof(((struct sockaddr_in *)sockname)->sin_addr); 122 if(*(in_addr_t *) addr != htonl(INADDR_LOOPBACK)) 123 family = XCB_FAMILY_INTERNET; 124 break; 125 case AF_UNIX: 126 break; 127 default: 128 return 0; /* cannot authenticate this family */ 129 } 130 131 dispbuflen = snprintf(dispbuf, sizeof(dispbuf), "%d", display); 132 if(dispbuflen < 0) 133 return 0; 134 /* snprintf may have truncate our text */ 135 dispbuflen = MIN(dispbuflen, sizeof(dispbuf) - 1); 136 137 if (family == FamilyLocal) { 138 if (gethostname(hostnamebuf, sizeof(hostnamebuf)) == -1) 139 return 0; /* do not know own hostname */ 140 addr = hostnamebuf; 141 addrlen = strlen(addr); 142 } 143 144 return XauGetBestAuthByAddr (family, 145 (unsigned short) addrlen, addr, 146 (unsigned short) dispbuflen, dispbuf, 147 N_AUTH_PROTOS, authnames, authnameslen); 148} 149 150#ifdef HASXDMAUTH 151static int next_nonce(void) 152{ 153 static int nonce = 0; 154 static pthread_mutex_t nonce_mutex = PTHREAD_MUTEX_INITIALIZER; 155 int ret; 156 pthread_mutex_lock(&nonce_mutex); 157 ret = nonce++; 158 pthread_mutex_unlock(&nonce_mutex); 159 return ret; 160} 161 162static void do_append(char *buf, int *idxp, void *val, size_t valsize) { 163 memcpy(buf + *idxp, val, valsize); 164 *idxp += valsize; 165} 166#endif 167 168static int compute_auth(xcb_auth_info_t *info, Xauth *authptr, struct sockaddr *sockname) 169{ 170 if (authname_match(AUTH_MC1, authptr->name, authptr->name_length)) { 171 info->datalen = memdup(&info->data, authptr->data, authptr->data_length); 172 if(!info->datalen) 173 return 0; 174 return 1; 175 } 176#ifdef HASXDMAUTH 177#define APPEND(buf,idx,val) do_append((buf),&(idx),&(val),sizeof(val)) 178 if (authname_match(AUTH_XA1, authptr->name, authptr->name_length)) { 179 int j; 180 181 info->data = malloc(192 / 8); 182 if(!info->data) 183 return 0; 184 185 for (j = 0; j < 8; j++) 186 info->data[j] = authptr->data[j]; 187 switch(sockname->sa_family) { 188 case AF_INET: 189 /*block*/ { 190 struct sockaddr_in *si = (struct sockaddr_in *) sockname; 191 APPEND(info->data, j, si->sin_addr.s_addr); 192 APPEND(info->data, j, si->sin_port); 193 } 194 break; 195#ifdef AF_INET6 196 case AF_INET6: 197 /*block*/ { 198 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *) sockname; 199 if(IN6_IS_ADDR_V4MAPPED(SIN6_ADDR(sockname))) 200 { 201 do_append(info->data, &j, &si6->sin6_addr.s6_addr[12], 4); 202 APPEND(info->data, j, si6->sin6_port); 203 } 204 else 205 { 206 /* XDM-AUTHORIZATION-1 does not handle IPv6 correctly. Do the 207 same thing Xlib does: use all zeroes for the 4-byte address 208 and 2-byte port number. */ 209 uint32_t fakeaddr = 0; 210 uint16_t fakeport = 0; 211 APPEND(info->data, j, fakeaddr); 212 APPEND(info->data, j, fakeport); 213 } 214 } 215 break; 216#endif 217 case AF_UNIX: 218 /*block*/ { 219 uint32_t fakeaddr = htonl(0xffffffff - next_nonce()); 220 uint16_t fakeport = htons(getpid()); 221 APPEND(info->data, j, fakeaddr); 222 APPEND(info->data, j, fakeport); 223 } 224 break; 225 default: 226 free(info->data); 227 return 0; /* do not know how to build this */ 228 } 229 { 230 uint32_t now = htonl(time(0)); 231 APPEND(info->data, j, now); 232 } 233 assert(j <= 192 / 8); 234 while (j < 192 / 8) 235 info->data[j++] = 0; 236 info->datalen = j; 237 XdmcpWrap ((unsigned char *) info->data, (unsigned char *) authptr->data + 8, (unsigned char *) info->data, info->datalen); 238 return 1; 239 } 240#undef APPEND 241#endif 242 243 return 0; /* Unknown authorization type */ 244} 245 246int _xcb_get_auth_info(int fd, xcb_auth_info_t *info, int display) 247{ 248 /* code adapted from Xlib/ConnDis.c, xtrans/Xtranssocket.c, 249 xtrans/Xtransutils.c */ 250 char sockbuf[sizeof(struct sockaddr) + MAXPATHLEN]; 251 unsigned int socknamelen = sizeof(sockbuf); /* need extra space */ 252 struct sockaddr *sockname = (struct sockaddr *) &sockbuf; 253 int gotsockname = 0; 254 Xauth *authptr = 0; 255 int ret = 1; 256 257 /* Some systems like hpux or Hurd do not expose peer names 258 * for UNIX Domain Sockets, but this is irrelevant, 259 * since compute_auth() ignores the peer name in this 260 * case anyway.*/ 261 if (getpeername(fd, sockname, &socknamelen) == -1) 262 { 263 if (getsockname(fd, sockname, &socknamelen) == -1) 264 return 0; /* can only authenticate sockets */ 265 if (sockname->sa_family != AF_UNIX) 266 return 0; /* except for AF_UNIX, sockets should have peernames */ 267 gotsockname = 1; 268 } 269 270 authptr = get_authptr(sockname, socknamelen, display); 271 if (authptr == 0) 272 return 0; /* cannot find good auth data */ 273 274 info->namelen = memdup(&info->name, authptr->name, authptr->name_length); 275 if (!info->namelen) 276 goto no_auth; /* out of memory */ 277 278 if (!gotsockname && getsockname(fd, sockname, &socknamelen) == -1) 279 { 280 free(info->name); 281 goto no_auth; /* can only authenticate sockets */ 282 } 283 284 ret = compute_auth(info, authptr, sockname); 285 if(!ret) 286 { 287 free(info->name); 288 goto no_auth; /* cannot build auth record */ 289 } 290 291 XauDisposeAuth(authptr); 292 return ret; 293 294 no_auth: 295 info->name = 0; 296 info->namelen = 0; 297 XauDisposeAuth(authptr); 298 return 0; 299} 300