1/*
2
3Copyright 1988, 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*/
28
29/*
30 * XDM-AUTHENTICATION-1 (XDMCP authentication) and
31 * XDM-AUTHORIZATION-1 (client authorization) protocols
32 *
33 * Author:  Keith Packard, MIT X Consortium
34 */
35
36#ifdef HAVE_DIX_CONFIG_H
37#include <dix-config.h>
38#endif
39
40#include <stdio.h>
41#include <X11/X.h>
42#define XSERV_t
43#define TRANS_SERVER
44#define TRANS_REOPEN
45#include <X11/Xtrans/Xtrans.h>
46#include "os.h"
47#include "osdep.h"
48#include "dixstruct.h"
49
50#ifdef HASXDMAUTH
51
52static Bool authFromXDMCP;
53
54#ifdef XDMCP
55#include <X11/Xmd.h>
56#undef REQUEST
57#include <X11/Xdmcp.h>
58
59/* XDM-AUTHENTICATION-1 */
60
61static XdmAuthKeyRec privateKey;
62static char XdmAuthenticationName[] = "XDM-AUTHENTICATION-1";
63
64#define XdmAuthenticationNameLen (sizeof XdmAuthenticationName - 1)
65static XdmAuthKeyRec global_rho;
66
67static Bool
68XdmAuthenticationValidator(ARRAY8Ptr privateData, ARRAY8Ptr incomingData,
69                           xdmOpCode packet_type)
70{
71    XdmAuthKeyPtr incoming;
72
73    XdmcpUnwrap(incomingData->data, (unsigned char *) &privateKey,
74                incomingData->data, incomingData->length);
75    if (packet_type == ACCEPT) {
76        if (incomingData->length != 8)
77            return FALSE;
78        incoming = (XdmAuthKeyPtr) incomingData->data;
79        XdmcpDecrementKey(incoming);
80        return XdmcpCompareKeys(incoming, &global_rho);
81    }
82    return FALSE;
83}
84
85static Bool
86XdmAuthenticationGenerator(ARRAY8Ptr privateData, ARRAY8Ptr outgoingData,
87                           xdmOpCode packet_type)
88{
89    outgoingData->length = 0;
90    outgoingData->data = 0;
91    if (packet_type == REQUEST) {
92        if (XdmcpAllocARRAY8(outgoingData, 8))
93            XdmcpWrap((unsigned char *) &global_rho, (unsigned char *) &privateKey,
94                      outgoingData->data, 8);
95    }
96    return TRUE;
97}
98
99static Bool
100XdmAuthenticationAddAuth(int name_len, const char *name,
101                         int data_len, char *data)
102{
103    Bool ret;
104
105    XdmcpUnwrap((unsigned char *) data, (unsigned char *) &privateKey,
106                (unsigned char *) data, data_len);
107    authFromXDMCP = TRUE;
108    ret = AddAuthorization(name_len, name, data_len, data);
109    authFromXDMCP = FALSE;
110    return ret;
111}
112
113#define atox(c)	('0' <= c && c <= '9' ? c - '0' : \
114		 'a' <= c && c <= 'f' ? c - 'a' + 10 : \
115		 'A' <= c && c <= 'F' ? c - 'A' + 10 : -1)
116
117static int
118HexToBinary(const char *in, char *out, int len)
119{
120    int top, bottom;
121
122    while (len > 0) {
123        top = atox(in[0]);
124        if (top == -1)
125            return 0;
126        bottom = atox(in[1]);
127        if (bottom == -1)
128            return 0;
129        *out++ = (top << 4) | bottom;
130        in += 2;
131        len -= 2;
132    }
133    if (len)
134        return 0;
135    *out++ = '\0';
136    return 1;
137}
138
139void
140XdmAuthenticationInit(const char *cookie, int cookie_len)
141{
142    memset(privateKey.data, 0, 8);
143    if (!strncmp(cookie, "0x", 2) || !strncmp(cookie, "0X", 2)) {
144        if (cookie_len > 2 + 2 * 8)
145            cookie_len = 2 + 2 * 8;
146        HexToBinary(cookie + 2, (char *) privateKey.data, cookie_len - 2);
147    }
148    else {
149        if (cookie_len > 7)
150            cookie_len = 7;
151        memmove(privateKey.data + 1, cookie, cookie_len);
152    }
153    XdmcpGenerateKey(&global_rho);
154    XdmcpRegisterAuthentication(XdmAuthenticationName, XdmAuthenticationNameLen,
155                                (char *) &global_rho,
156                                sizeof(global_rho),
157                                (ValidatorFunc) XdmAuthenticationValidator,
158                                (GeneratorFunc) XdmAuthenticationGenerator,
159                                (AddAuthorFunc) XdmAuthenticationAddAuth);
160}
161
162#endif                          /* XDMCP */
163
164/* XDM-AUTHORIZATION-1 */
165typedef struct _XdmAuthorization {
166    struct _XdmAuthorization *next;
167    XdmAuthKeyRec rho;
168    XdmAuthKeyRec key;
169    XID id;
170} XdmAuthorizationRec, *XdmAuthorizationPtr;
171
172static XdmAuthorizationPtr xdmAuth;
173
174typedef struct _XdmClientAuth {
175    struct _XdmClientAuth *next;
176    XdmAuthKeyRec rho;
177    char client[6];
178    long time;
179} XdmClientAuthRec, *XdmClientAuthPtr;
180
181static XdmClientAuthPtr xdmClients;
182static long clockOffset;
183static Bool gotClock;
184
185#define TwentyMinutes	(20 * 60)
186#define TwentyFiveMinutes (25 * 60)
187
188static Bool
189XdmClientAuthCompare(const XdmClientAuthPtr a, const XdmClientAuthPtr b)
190{
191    int i;
192
193    if (!XdmcpCompareKeys(&a->rho, &b->rho))
194        return FALSE;
195    for (i = 0; i < 6; i++)
196        if (a->client[i] != b->client[i])
197            return FALSE;
198    return a->time == b->time;
199}
200
201static void
202XdmClientAuthDecode(const unsigned char *plain, XdmClientAuthPtr auth)
203{
204    int i, j;
205
206    j = 0;
207    for (i = 0; i < 8; i++) {
208        auth->rho.data[i] = plain[j];
209        ++j;
210    }
211    for (i = 0; i < 6; i++) {
212        auth->client[i] = plain[j];
213        ++j;
214    }
215    auth->time = 0;
216    for (i = 0; i < 4; i++) {
217        auth->time |= plain[j] << ((3 - i) << 3);
218        j++;
219    }
220}
221
222static void
223XdmClientAuthTimeout(long now)
224{
225    XdmClientAuthPtr client, next, prev;
226
227    prev = 0;
228    for (client = xdmClients; client; client = next) {
229        next = client->next;
230        if (labs(now - client->time) > TwentyFiveMinutes) {
231            if (prev)
232                prev->next = next;
233            else
234                xdmClients = next;
235            free(client);
236        }
237        else
238            prev = client;
239    }
240}
241
242static XdmClientAuthPtr
243XdmAuthorizationValidate(unsigned char *plain, int length,
244                         XdmAuthKeyPtr rho, ClientPtr xclient,
245                         const char **reason)
246{
247    XdmClientAuthPtr client, existing;
248    long now;
249    int i;
250
251    if (length != (192 / 8)) {
252        if (reason)
253            *reason = "Bad XDM authorization key length";
254        return NULL;
255    }
256    client = malloc(sizeof(XdmClientAuthRec));
257    if (!client)
258        return NULL;
259    XdmClientAuthDecode(plain, client);
260    if (!XdmcpCompareKeys(&client->rho, rho)) {
261        free(client);
262        if (reason)
263            *reason = "Invalid XDM-AUTHORIZATION-1 key (failed key comparison)";
264        return NULL;
265    }
266    for (i = 18; i < 24; i++)
267        if (plain[i] != 0) {
268            free(client);
269            if (reason)
270                *reason = "Invalid XDM-AUTHORIZATION-1 key (failed NULL check)";
271            return NULL;
272        }
273    if (xclient) {
274        int family, addr_len;
275        Xtransaddr *addr;
276
277        if (_XSERVTransGetPeerAddr(((OsCommPtr) xclient->osPrivate)->trans_conn,
278                                   &family, &addr_len, &addr) == 0
279            && _XSERVTransConvertAddress(&family, &addr_len, &addr) == 0) {
280#if defined(TCPCONN)
281            if (family == FamilyInternet &&
282                memcmp((char *) addr, client->client, 4) != 0) {
283                free(client);
284                free(addr);
285                if (reason)
286                    *reason =
287                        "Invalid XDM-AUTHORIZATION-1 key (failed address comparison)";
288                return NULL;
289
290            }
291#endif
292            free(addr);
293        }
294    }
295    now = time(0);
296    if (!gotClock) {
297        clockOffset = client->time - now;
298        gotClock = TRUE;
299    }
300    now += clockOffset;
301    XdmClientAuthTimeout(now);
302    if (labs(client->time - now) > TwentyMinutes) {
303        free(client);
304        if (reason)
305            *reason = "Excessive XDM-AUTHORIZATION-1 time offset";
306        return NULL;
307    }
308    for (existing = xdmClients; existing; existing = existing->next) {
309        if (XdmClientAuthCompare(existing, client)) {
310            free(client);
311            if (reason)
312                *reason = "XDM authorization key matches an existing client!";
313            return NULL;
314        }
315    }
316    return client;
317}
318
319int
320XdmAddCookie(unsigned short data_length, const char *data, XID id)
321{
322    XdmAuthorizationPtr new;
323    unsigned char *rho_bits, *key_bits;
324
325    switch (data_length) {
326    case 16:                   /* auth from files is 16 bytes long */
327#ifdef XDMCP
328        if (authFromXDMCP) {
329            /* R5 xdm sent bogus authorization data in the accept packet,
330             * but we can recover */
331            rho_bits = global_rho.data;
332            key_bits = (unsigned char *) data;
333            key_bits[0] = '\0';
334        }
335        else
336#endif
337        {
338            rho_bits = (unsigned char *) data;
339            key_bits = (unsigned char *) (data + 8);
340        }
341        break;
342#ifdef XDMCP
343    case 8:                    /* auth from XDMCP is 8 bytes long */
344        rho_bits = global_rho.data;
345        key_bits = (unsigned char *) data;
346        break;
347#endif
348    default:
349        return 0;
350    }
351    /* the first octet of the key must be zero */
352    if (key_bits[0] != '\0')
353        return 0;
354    new = malloc(sizeof(XdmAuthorizationRec));
355    if (!new)
356        return 0;
357    new->next = xdmAuth;
358    xdmAuth = new;
359    memmove(new->key.data, key_bits, (int) 8);
360    memmove(new->rho.data, rho_bits, (int) 8);
361    new->id = id;
362    return 1;
363}
364
365XID
366XdmCheckCookie(unsigned short cookie_length, const char *cookie,
367               ClientPtr xclient, const char **reason)
368{
369    XdmAuthorizationPtr auth;
370    XdmClientAuthPtr client;
371    unsigned char *plain;
372
373    /* Auth packets must be a multiple of 8 bytes long */
374    if (cookie_length & 7)
375        return (XID) -1;
376    plain = malloc(cookie_length);
377    if (!plain)
378        return (XID) -1;
379    for (auth = xdmAuth; auth; auth = auth->next) {
380        XdmcpUnwrap((unsigned char *) cookie, (unsigned char *) &auth->key,
381                    plain, cookie_length);
382        if ((client =
383             XdmAuthorizationValidate(plain, cookie_length, &auth->rho, xclient,
384                                      reason)) != NULL) {
385            client->next = xdmClients;
386            xdmClients = client;
387            free(plain);
388            return auth->id;
389        }
390    }
391    free(plain);
392    return (XID) -1;
393}
394
395int
396XdmResetCookie(void)
397{
398    XdmAuthorizationPtr auth, next_auth;
399    XdmClientAuthPtr client, next_client;
400
401    for (auth = xdmAuth; auth; auth = next_auth) {
402        next_auth = auth->next;
403        free(auth);
404    }
405    xdmAuth = 0;
406    for (client = xdmClients; client; client = next_client) {
407        next_client = client->next;
408        free(client);
409    }
410    xdmClients = (XdmClientAuthPtr) 0;
411    return 1;
412}
413
414int
415XdmFromID(XID id, unsigned short *data_lenp, char **datap)
416{
417    XdmAuthorizationPtr auth;
418
419    for (auth = xdmAuth; auth; auth = auth->next) {
420        if (id == auth->id) {
421            *data_lenp = 16;
422            *datap = (char *) &auth->rho;
423            return 1;
424        }
425    }
426    return 0;
427}
428
429int
430XdmRemoveCookie(unsigned short data_length, const char *data)
431{
432    XdmAuthorizationPtr auth;
433    XdmAuthKeyPtr key_bits, rho_bits;
434
435    switch (data_length) {
436    case 16:
437        rho_bits = (XdmAuthKeyPtr) data;
438        key_bits = (XdmAuthKeyPtr) (data + 8);
439        break;
440#ifdef XDMCP
441    case 8:
442        rho_bits = &global_rho;
443        key_bits = (XdmAuthKeyPtr) data;
444        break;
445#endif
446    default:
447        return 0;
448    }
449    for (auth = xdmAuth; auth; auth = auth->next) {
450        if (XdmcpCompareKeys(rho_bits, &auth->rho) &&
451            XdmcpCompareKeys(key_bits, &auth->key)) {
452            xdmAuth = auth->next;
453            free(auth);
454            return 1;
455        }
456    }
457    return 0;
458}
459
460#endif
461