Support pre-IPv6 systems (without getaddrinfo)
[profile/ivi/libxcb.git] / src / xcb_auth.c
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/param.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <arpa/inet.h>
34
35 #ifdef __INTERIX
36 /* _don't_ ask. interix has INADDR_LOOPBACK in here. */
37 #include <rpc/types.h>
38 #endif
39
40 #ifdef _WIN32
41 #include "xcb_windefs.h"
42 #else
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <sys/un.h>
46 #endif /* _WIN32 */
47
48 #include "xcb.h"
49 #include "xcbint.h"
50
51 #ifdef HASXDMAUTH
52 #include <X11/Xdmcp.h>
53 #endif
54
55 enum auth_protos {
56 #ifdef HASXDMAUTH
57     AUTH_XA1,
58 #endif
59     AUTH_MC1,
60     N_AUTH_PROTOS
61 };
62
63 #define AUTH_PROTO_XDM_AUTHORIZATION "XDM-AUTHORIZATION-1"
64 #define AUTH_PROTO_MIT_MAGIC_COOKIE "MIT-MAGIC-COOKIE-1"
65
66 static char *authnames[N_AUTH_PROTOS] = {
67 #ifdef HASXDMAUTH
68     AUTH_PROTO_XDM_AUTHORIZATION,
69 #endif
70     AUTH_PROTO_MIT_MAGIC_COOKIE,
71 };
72
73 static int authnameslen[N_AUTH_PROTOS] = {
74 #ifdef HASXDMAUTH
75     sizeof(AUTH_PROTO_XDM_AUTHORIZATION) - 1,
76 #endif
77     sizeof(AUTH_PROTO_MIT_MAGIC_COOKIE) - 1,
78 };
79
80 static size_t memdup(char **dst, void *src, size_t len)
81 {
82     if(len)
83         *dst = malloc(len);
84     else
85         *dst = 0;
86     if(!*dst)
87         return 0;
88     memcpy(*dst, src, len);
89     return len;
90 }
91
92 static int authname_match(enum auth_protos kind, char *name, size_t namelen)
93 {
94     if(authnameslen[kind] != namelen)
95         return 0;
96     if(memcmp(authnames[kind], name, namelen))
97         return 0;
98     return 1;
99 }
100
101 #define SIN6_ADDR(s) (&((struct sockaddr_in6 *)s)->sin6_addr)
102
103 static Xauth *get_authptr(struct sockaddr *sockname, int display)
104 {
105     char *addr = 0;
106     int addrlen = 0;
107     unsigned short family;
108     char hostnamebuf[256];   /* big enough for max hostname */
109     char dispbuf[40];   /* big enough to hold more than 2^64 base 10 */
110     int dispbuflen;
111
112     family = FamilyLocal; /* 256 */
113     switch(sockname->sa_family)
114     {
115 #ifdef AF_INET6
116     case AF_INET6:
117         addr = (char *) SIN6_ADDR(sockname);
118         addrlen = sizeof(*SIN6_ADDR(sockname));
119         if(!IN6_IS_ADDR_V4MAPPED(SIN6_ADDR(sockname)))
120         {
121             if(!IN6_IS_ADDR_LOOPBACK(SIN6_ADDR(sockname)))
122                 family = XCB_FAMILY_INTERNET_6;
123             break;
124         }
125         addr += 12;
126         /* if v4-mapped, fall through. */
127 #endif
128     case AF_INET:
129         if(!addr)
130             addr = (char *) &((struct sockaddr_in *)sockname)->sin_addr;
131         addrlen = sizeof(((struct sockaddr_in *)sockname)->sin_addr);
132         if(*(in_addr_t *) addr != htonl(INADDR_LOOPBACK))
133             family = XCB_FAMILY_INTERNET;
134         break;
135     case AF_UNIX:
136         break;
137     default:
138         return 0;   /* cannot authenticate this family */
139     }
140
141     dispbuflen = snprintf(dispbuf, sizeof(dispbuf), "%d", display);
142     if(dispbuflen < 0)
143         return 0;
144     /* snprintf may have truncate our text */
145     dispbuflen = MIN(dispbuflen, sizeof(dispbuf) - 1);
146
147     if (family == FamilyLocal) {
148         if (gethostname(hostnamebuf, sizeof(hostnamebuf)) == -1)
149             return 0;   /* do not know own hostname */
150         addr = hostnamebuf;
151         addrlen = strlen(addr);
152     }
153
154     return XauGetBestAuthByAddr (family,
155                                  (unsigned short) addrlen, addr,
156                                  (unsigned short) dispbuflen, dispbuf,
157                                  N_AUTH_PROTOS, authnames, authnameslen);
158 }
159
160 #ifdef HASXDMAUTH
161 static int next_nonce(void)
162 {
163     static int nonce = 0;
164     static pthread_mutex_t nonce_mutex = PTHREAD_MUTEX_INITIALIZER;
165     int ret;
166     pthread_mutex_lock(&nonce_mutex);
167     ret = nonce++;
168     pthread_mutex_unlock(&nonce_mutex);
169     return ret;
170 }
171
172 static void do_append(char *buf, int *idxp, void *val, size_t valsize) {
173     memcpy(buf + *idxp, val, valsize);
174     *idxp += valsize;
175 }
176 #endif
177      
178 static int compute_auth(xcb_auth_info_t *info, Xauth *authptr, struct sockaddr *sockname)
179 {
180     if (authname_match(AUTH_MC1, authptr->name, authptr->name_length)) {
181         info->datalen = memdup(&info->data, authptr->data, authptr->data_length);
182         if(!info->datalen)
183             return 0;
184         return 1;
185     }
186 #ifdef HASXDMAUTH
187 #define APPEND(buf,idx,val) do_append((buf),&(idx),&(val),sizeof(val))
188     if (authname_match(AUTH_XA1, authptr->name, authptr->name_length)) {
189         int j;
190
191         info->data = malloc(192 / 8);
192         if(!info->data)
193             return 0;
194
195         for (j = 0; j < 8; j++)
196             info->data[j] = authptr->data[j];
197         switch(sockname->sa_family) {
198         case AF_INET:
199             /*block*/ {
200             struct sockaddr_in *si = (struct sockaddr_in *) sockname;
201             APPEND(info->data, j, si->sin_addr.s_addr);
202             APPEND(info->data, j, si->sin_port);
203         }
204         break;
205 #ifdef AF_INET6
206         case AF_INET6:
207             /*block*/ {
208             struct sockaddr_in6 *si6 = (struct sockaddr_in6 *) sockname;
209             if(IN6_IS_ADDR_V4MAPPED(SIN6_ADDR(sockname)))
210             {
211                 do_append(info->data, &j, &si6->sin6_addr.s6_addr[12], 4);
212                 APPEND(info->data, j, si6->sin6_port);
213             }
214             else
215             {
216                 /* XDM-AUTHORIZATION-1 does not handle IPv6 correctly.  Do the
217                    same thing Xlib does: use all zeroes for the 4-byte address
218                    and 2-byte port number. */
219                 uint32_t fakeaddr = 0;
220                 uint16_t fakeport = 0;
221                 APPEND(info->data, j, fakeaddr);
222                 APPEND(info->data, j, fakeport);
223             }
224         }
225         break;
226 #endif
227         case AF_UNIX:
228             /*block*/ {
229             uint32_t fakeaddr = htonl(0xffffffff - next_nonce());
230             uint16_t fakeport = htons(getpid());
231             APPEND(info->data, j, fakeaddr);
232             APPEND(info->data, j, fakeport);
233         }
234         break;
235         default:
236             free(info->data);
237             return 0;   /* do not know how to build this */
238         }
239         {
240             uint32_t now = htonl(time(0));
241             APPEND(info->data, j, now);
242         }
243         assert(j <= 192 / 8);
244         while (j < 192 / 8)
245             info->data[j++] = 0;
246         info->datalen = j;
247         XdmcpWrap ((unsigned char *) info->data, (unsigned char *) authptr->data + 8, (unsigned char *) info->data, info->datalen);
248         return 1;
249     }
250 #undef APPEND
251 #endif
252
253     return 0;   /* Unknown authorization type */
254 }
255
256 /* `sockaddr_un.sun_path' typical size usually ranges between 92 and 108 */
257 #define INITIAL_SOCKNAME_SLACK 108
258
259 /* Return a dynamically allocated socket address structure according
260    to the value returned by either getpeername() or getsockname()
261    (according to POSIX, applications should not assume a particular
262    length for `sockaddr_un.sun_path') */
263 static struct sockaddr *get_peer_sock_name(int (*socket_func)(int,
264                                                               struct sockaddr *,
265                                                               socklen_t *),
266                                            int fd)
267 {
268     socklen_t socknamelen = sizeof(struct sockaddr) + INITIAL_SOCKNAME_SLACK;
269     socklen_t actual_socknamelen = socknamelen;
270     struct sockaddr *sockname = malloc(socknamelen);
271
272     if (sockname == NULL)
273         return NULL;
274
275     /* Both getpeername() and getsockname() truncates sockname if
276        there is not enough space and set the required length in
277        actual_socknamelen */
278     if (socket_func(fd, sockname, &actual_socknamelen) == -1)
279         goto sock_or_realloc_error;
280
281     if (actual_socknamelen > socknamelen)
282     {
283         struct sockaddr *new_sockname = NULL;
284         socknamelen = actual_socknamelen;
285
286         if ((new_sockname = realloc(sockname, actual_socknamelen)) == NULL)
287             goto sock_or_realloc_error;
288
289         sockname = new_sockname;
290
291         if (socket_func(fd, sockname, &actual_socknamelen) == -1 ||
292             actual_socknamelen > socknamelen)
293             goto sock_or_realloc_error;
294     }
295
296     return sockname;
297
298  sock_or_realloc_error:
299     free(sockname);
300     return NULL;
301 }
302
303 int _xcb_get_auth_info(int fd, xcb_auth_info_t *info, int display)
304 {
305     /* code adapted from Xlib/ConnDis.c, xtrans/Xtranssocket.c,
306        xtrans/Xtransutils.c */
307     struct sockaddr *sockname = NULL;
308     int gotsockname = 0;
309     Xauth *authptr = 0;
310     int ret = 1;
311
312     /* Some systems like hpux or Hurd do not expose peer names
313      * for UNIX Domain Sockets, but this is irrelevant,
314      * since compute_auth() ignores the peer name in this
315      * case anyway.*/
316     if ((sockname = get_peer_sock_name(getpeername, fd)) == NULL)
317     {
318         if ((sockname = get_peer_sock_name(getsockname, fd)) == NULL)
319             return 0;   /* can only authenticate sockets */
320         if (sockname->sa_family != AF_UNIX)
321         {
322             free(sockname);
323             return 0;   /* except for AF_UNIX, sockets should have peernames */
324         }
325         gotsockname = 1;
326     }
327
328     authptr = get_authptr(sockname, display);
329     if (authptr == 0)
330     {
331         free(sockname);
332         return 0;   /* cannot find good auth data */
333     }
334
335     info->namelen = memdup(&info->name, authptr->name, authptr->name_length);
336     if (!info->namelen)
337         goto no_auth;   /* out of memory */
338
339     if (!gotsockname)
340     {
341         free(sockname);
342
343         if ((sockname = get_peer_sock_name(getsockname, fd)) == NULL)
344         {
345             free(info->name);
346             goto no_auth;   /* can only authenticate sockets */
347         }
348     }
349
350     ret = compute_auth(info, authptr, sockname);
351     if(!ret)
352     {
353         free(info->name);
354         goto no_auth;   /* cannot build auth record */
355     }
356
357     free(sockname);
358     sockname = NULL;
359
360     XauDisposeAuth(authptr);
361     return ret;
362
363  no_auth:
364     free(sockname);
365
366     info->name = 0;
367     info->namelen = 0;
368     XauDisposeAuth(authptr);
369     return 0;
370 }