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