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