1
/* $Xorg: k5auth.c,v 1.4 2001/02/09 02:05:23 xorgcvs Exp $ */
4
Copyright 1993, 1994, 1998 The Open Group
6
Permission to use, copy, modify, distribute, and sell this software and its
7
documentation for any purpose is hereby granted without fee, provided that
8
the above copyright notice appear in all copies and that both that
9
copyright notice and this permission notice appear in supporting
12
The above copyright notice and this permission notice shall be included
13
in all copies or substantial portions of the Software.
15
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
19
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21
OTHER DEALINGS IN THE SOFTWARE.
23
Except as contained in this notice, the name of The Open Group shall
24
not be used in advertising or otherwise to promote the sale, use or
25
other dealings in this Software without prior written authorization
29
/* $XFree86: xc/programs/Xserver/os/k5auth.c,v 3.4 2001/01/17 22:37:10 dawes Exp $ */
32
* Kerberos V5 authentication scheme
33
* Author: Tom Yu <tlyu@MIT.EDU>
35
* Mostly snarfed wholesale from the user_user demo in the
36
* krb5 distribution. (At least the checking part)
39
#ifdef HAVE_DIX_CONFIG_H
40
#include <dix-config.h>
43
#include <sys/types.h>
44
#include <sys/socket.h>
46
#include <netinet/in.h>
49
#include <netdnet/dn.h>
51
#include <arpa/inet.h>
52
#include <krb5/krb5.h>
53
/* 9/93: krb5.h leaks some symbols */
56
#include <krb5/los-proto.h>
60
#include <X11/Xproto.h>
61
#include <X11/Xfuncs.h>
62
#include "dixstruct.h"
66
extern int (*k5_Vector[256])();
67
extern int SendConnSetup();
68
extern char *display; /* need this to generate rcache name */
70
static XID krb5_id = ~0L;
71
static krb5_principal srvname = NULL; /* service name */
72
static char *ccname = NULL;
73
static char *ktname = NULL; /* key table name */
74
static char kerror[256];
79
* extract session key from a credentials struct
81
krb5_error_code tgt_keyproc(keyprocarg, principal, vno, key)
82
krb5_pointer keyprocarg;
83
krb5_principal principal;
87
krb5_creds *creds = (krb5_creds *)keyprocarg;
89
return krb5_copy_keyblock(&creds->keyblock, key);
95
* compare "encoded" principals
97
Bool k5_cmpenc(pname, plen, buf)
102
return (plen == buf->length &&
103
memcmp(pname, buf->data, plen) == 0);
109
* This is stage 0 of the krb5 authentication protocol. It
110
* goes through the current credentials cache and extracts the
111
* primary principal and tgt to send to the client, or as
112
* appropriate, extracts from a keytab.
114
* The packet sent to the client has the following format:
118
* CARD16 length = total length of packet (in 32 bit units)
119
* CARD16 plen = length of encoded principal following
120
* STRING8 princ = encoded principal
121
* STRING8 ticket = server tgt
123
* For client-server authentication, the packet is as follows:
127
* CARD16 length = total length
128
* STRING8 princ = encoded principal of server
130
XID K5Check(data_length, data, client, reason)
131
unsigned short data_length;
136
krb5_error_code retval;
138
krb5_principal sprinc, cprinc;
148
if (!ccname && !srvname)
152
if ((creds = (krb5_creds *)malloc(sizeof(krb5_creds))) == NULL)
154
if (retval = krb5_cc_resolve(ccname, &cc))
156
bzero((char*)creds, sizeof (krb5_creds));
157
if (retval = krb5_cc_get_principal(cc, &cprinc))
159
krb5_free_creds(creds);
163
creds->client = cprinc;
165
krb5_build_principal_ext(&sprinc,
166
krb5_princ_realm(creds->client)->length,
167
krb5_princ_realm(creds->client)->data,
169
krb5_princ_realm(creds->client)->length,
170
krb5_princ_realm(creds->client)->data,
173
krb5_free_creds(creds);
177
creds->server = sprinc;
178
retval = krb5_get_credentials(KRB5_GC_CACHED, cc, creds);
182
krb5_free_creds(creds);
185
if (retval = XauKrb5Encode(cprinc, &princ))
187
krb5_free_creds(creds);
190
tlen = sz_xReq + 2 + princ.length + creds->ticket.length;
191
prefix.reqType = 2; /* opcode = authenticate user-to-user */
195
if (retval = XauKrb5Encode(srvname, &princ))
199
tlen = sz_xReq + princ.length;
200
prefix.reqType = 3; /* opcode = authenticate client-server */
202
prefix.data = 0; /* stage = 0 */
203
prefix.length = (tlen + 3) >> 2; /* round up to nearest multiple
207
swaps(&prefix.length, n);
209
if ((cp = outbuf = (char *)malloc(tlen)) == NULL)
213
krb5_free_creds(creds);
218
memcpy(cp, &prefix, sz_xReq);
222
memcpy(cp, &princ.length, 2);
225
swaps((CARD16 *)cp, n);
229
memcpy(cp, princ.data, princ.length);
231
free(princ.data); /* we don't need that anymore */
233
memcpy(cp, creds->ticket.data, creds->ticket.length);
234
WriteToClient(client, tlen, outbuf);
236
client->requestVector = k5_Vector; /* hack in our dispatch vector */
237
client->clientState = ClientStateAuthenticating;
240
((OsCommPtr)client->osPrivate)->authstate.srvcreds = (pointer)creds; /* save tgt creds */
241
((OsCommPtr)client->osPrivate)->authstate.ktname = NULL;
242
((OsCommPtr)client->osPrivate)->authstate.srvname = NULL;
246
((OsCommPtr)client->osPrivate)->authstate.srvcreds = NULL;
247
((OsCommPtr)client->osPrivate)->authstate.ktname = (pointer)ktname;
248
((OsCommPtr)client->osPrivate)->authstate.srvname = (pointer)srvname;
250
((OsCommPtr)client->osPrivate)->authstate.stageno = 1; /* next stage is 1 */
257
* This gets called out of the dispatcher after K5Check frobs with the
258
* client->requestVector. It accepts the ap_req from the client and verifies
259
* it. In addition, if the client has set AP_OPTS_MUTUAL_REQUIRED, it then
260
* sends an ap_rep to the client to achieve mutual authentication.
262
* client stage1 packet format is as follows:
265
* CARD8 data = ignored
266
* CARD16 length = total length
267
* STRING8 data = the actual ap_req
269
* stage2 packet sent back to client for mutual authentication:
273
* CARD16 length = total length
274
* STRING8 data = the ap_rep
276
int k5_stage1(client)
277
register ClientPtr client;
280
krb5_error_code retval, retval2;
282
struct sockaddr cli_net_addr;
284
krb5_principal cprinc;
286
krb5_creds *creds = (krb5_creds *)((OsCommPtr)client->osPrivate)->authstate.srvcreds;
288
krb5_address cli_addr, **localaddrs = NULL;
289
krb5_tkt_authent *authdat;
290
krb5_ap_rep_enc_part rep;
291
krb5_int32 ctime, cusec;
292
krb5_rcache rcache = NULL;
293
char *cachename = NULL, *rc_type = NULL, *rc_base = "rcX", *kt = NULL;
296
if (((OsCommPtr)client->osPrivate)->authstate.stageno != 1)
299
krb5_free_creds(creds);
300
return(SendConnSetup(client, "expected Krb5 stage1 packet"));
302
addrlen = sizeof (cli_net_addr);
303
if (getpeername(((OsCommPtr)client->osPrivate)->fd,
304
&cli_net_addr, &addrlen) == -1)
307
krb5_free_creds(creds);
308
return(SendConnSetup(client, "Krb5 stage1: getpeername failed"));
310
if (cli_net_addr.sa_family == AF_UNSPEC
311
#if defined(UNIXCONN) || defined(LOCALCONN) || defined(OS2PIPECONN)
312
|| cli_net_addr.sa_family == AF_UNIX
314
) /* assume local host */
316
krb5_os_localaddr(&localaddrs);
317
if (!localaddrs || !localaddrs[0])
320
krb5_free_creds(creds);
321
return(SendConnSetup(client, "Krb5 failed to get localaddrs"));
323
cli_addr.addrtype = localaddrs[0]->addrtype;
324
cli_addr.length = localaddrs[0]->length;
325
cli_addr.contents = localaddrs[0]->contents;
329
cli_addr.addrtype = cli_net_addr.sa_family; /* the values
331
switch (cli_net_addr.sa_family)
335
cli_addr.length = sizeof (struct in_addr);
337
(krb5_octet *)&((struct sockaddr_in *)&cli_net_addr)->sin_addr;
342
cli_addr.length = sizeof (struct dn_naddr);
344
(krb5_octet *)&((struct sockaddr_dn *)&cli_net_addr)->sdn_add;
349
krb5_free_addresses(localaddrs);
351
krb5_free_creds(creds);
352
sprintf(kerror, "Krb5 stage1: unknown address family %d from getpeername",
353
cli_net_addr.sa_family);
354
return(SendConnSetup(client, kerror));
357
if ((rcache = (krb5_rcache)malloc(sizeof(*rcache))) == NULL)
360
krb5_free_addresses(localaddrs);
362
krb5_free_creds(creds);
363
return(SendConnSetup(client, "malloc bombed for krb5_rcache"));
365
if ((rc_type = krb5_rc_default_type()) == NULL)
367
if (retval = krb5_rc_resolve_type(&rcache, rc_type))
370
krb5_free_addresses(localaddrs);
372
krb5_free_creds(creds);
374
strcpy(kerror, "krb5_rc_resolve_type failed: ");
375
strncat(kerror, error_message(retval), 231);
376
return(SendConnSetup(client, kerror));
378
if ((cachename = (char *)malloc(strlen(rc_base) + strlen(display) + 1))
382
krb5_free_addresses(localaddrs);
384
krb5_free_creds(creds);
386
return(SendConnSetup(client, "Krb5: malloc bombed for cachename"));
388
strcpy(cachename, rc_base);
389
strcat(cachename, display);
390
if (retval = krb5_rc_resolve(rcache, cachename))
393
krb5_free_addresses(localaddrs);
395
krb5_free_creds(creds);
398
strcpy(kerror, "krb5_rc_resolve failed: ");
399
strncat(kerror, error_message(retval), 236);
400
return(SendConnSetup(client, kerror));
403
if (krb5_rc_recover(rcache))
405
extern krb5_deltat krb5_clockskew;
406
if (retval = krb5_rc_initialize(rcache, krb5_clockskew))
409
krb5_free_addresses(localaddrs);
411
krb5_free_creds(creds);
412
if (retval2 = krb5_rc_close(rcache))
414
strcpy(kerror, "krb5_rc_close failed: ");
415
strncat(kerror, error_message(retval2), 238);
416
return(SendConnSetup(client, kerror));
419
strcpy(kerror, "krb5_rc_initialize failed: ");
420
strncat(kerror, error_message(retval), 233);
421
return(SendConnSetup(client, kerror));
424
buf.length = (stuff->length << 2) - sz_xReq;
425
buf.data = (char *)stuff + sz_xReq;
428
retval = krb5_rd_req(&buf,
429
NULL, /* don't bother with server name */
431
NULL, /* no fetchfrom */
433
creds, /* credentials as arg to
437
krb5_free_creds(creds);
439
else if (kt = (char *)((OsCommPtr)client->osPrivate)->authstate.ktname)
441
retval = krb5_rd_req(&buf, srvname, &cli_addr, kt, NULL, NULL,
443
((OsCommPtr)client->osPrivate)->authstate.ktname = NULL;
448
krb5_free_addresses(localaddrs);
449
return(SendConnSetup(client, "Krb5: neither srvcreds nor ktname set"));
452
krb5_free_addresses(localaddrs);
455
if (retval2 = krb5_rc_close(rcache))
457
strcpy(kerror, "krb5_rc_close failed (2): ");
458
strncat(kerror, error_message(retval2), 230);
459
return(SendConnSetup(client, kerror));
465
strcpy(kerror, "Krb5: Bad application request: ");
466
strncat(kerror, error_message(retval), 224);
467
return(SendConnSetup(client, kerror));
469
cprinc = authdat->ticket->enc_part2->client;
470
skey = authdat->ticket->enc_part2->session;
471
if (XauKrb5Encode(cprinc, &buf))
473
krb5_free_tkt_authent(authdat);
474
return(SendConnSetup(client, "XauKrb5Encode bombed"));
477
* Now check to see if the principal we got is one that we want to let in
479
if (ForEachHostInFamily(FamilyKrb5Principal, k5_cmpenc, (pointer)&buf))
483
* The following deals with sending an ap_rep to the client to
484
* achieve mutual authentication. The client sends back a stage 3
485
* packet if all is ok.
487
if (authdat->ap_options | AP_OPTS_MUTUAL_REQUIRED)
490
* stage 2: send ap_rep to client
492
if (retval = krb5_us_timeofday(&ctime, &cusec))
494
krb5_free_tkt_authent(authdat);
495
strcpy(kerror, "error in krb5_us_timeofday: ");
496
strncat(kerror, error_message(retval), 234);
497
return(SendConnSetup(client, kerror));
503
if (retval = krb5_mk_rep(&rep, skey, &buf))
505
krb5_free_tkt_authent(authdat);
506
strcpy(kerror, "error in krb5_mk_rep: ");
507
strncat(kerror, error_message(retval), 238);
508
return(SendConnSetup(client, kerror));
510
prefix.reqType = 2; /* opcode = authenticate */
511
prefix.data = 2; /* stage = 2 */
512
prefix.length = (buf.length + sz_xReq + 3) >> 2;
515
swaps(&prefix.length, n);
517
WriteToClient(client, sz_xReq, (char *)&prefix);
518
WriteToClient(client, buf.length, buf.data);
520
krb5_free_tkt_authent(authdat);
521
((OsCommPtr)client->osPrivate)->authstate.stageno = 3; /* expect stage3 packet */
527
krb5_free_tkt_authent(authdat);
528
return(SendConnSetup(client, NULL)); /* success! */
535
krb5_free_tkt_authent(authdat);
537
retval = krb5_unparse_name(cprinc, &kname);
540
sprintf(kerror, "Principal \"%s\" is not authorized to connect",
544
return(SendConnSetup(client, kerror));
547
return(SendConnSetup(client,"Principal is not authorized to connect to Server"));
554
* Get the short ack packet from the client. This packet can conceivably
555
* be expanded to allow for switching on end-to-end encryption.
557
* stage3 packet format:
560
* CARD8 data = ignored (for now)
561
* CARD16 length = should be zero
563
int k5_stage3(client)
564
register ClientPtr client;
568
if (((OsCommPtr)client->osPrivate)->authstate.stageno != 3)
570
return(SendConnSetup(client, "expected Krb5 stage3 packet"));
573
return(SendConnSetup(client, NULL)); /* success! */
577
register ClientPtr client;
579
if (((OsCommPtr)client->osPrivate)->authstate.srvcreds)
580
krb5_free_creds((krb5_creds *)((OsCommPtr)client->osPrivate)->authstate.srvcreds);
581
sprintf(kerror, "unrecognized Krb5 auth packet %d, expecting %d",
582
((xReq *)client->requestBuffer)->reqType,
583
((OsCommPtr)client->osPrivate)->authstate.stageno);
584
return(SendConnSetup(client, kerror));
590
* Takes the name of a credentials cache and resolves it. Also adds the
591
* primary principal of the ccache to the acl.
593
* Now will also take a service name.
595
int K5Add(data_length, data, id)
596
unsigned short data_length;
600
krb5_principal princ;
601
krb5_error_code retval;
602
krb5_keytab_entry tmp_entry;
610
krb5_init_ets(); /* can't think of a better place to put it */
614
if ((nbuf = (char *)malloc(data_length - 2)) == NULL)
616
memcpy(nbuf, data + 3, data_length - 3);
617
nbuf[data_length - 3] = '\0';
625
krb5_free_principal(srvname);
633
if (!strncmp(data, "UU:", 3))
635
if (retval = krb5_cc_resolve(nbuf, &cc))
637
ErrorF("K5Add: krb5_cc_resolve of \"%s\" failed: %s\n",
638
nbuf, error_message(retval));
642
if (cc && !(retval = krb5_cc_get_principal(cc, &princ)))
644
if (XauKrb5Encode(princ, &kbuf))
647
krb5_free_principal(princ);
651
if (krb5_cc_close(cc))
653
AddHost(NULL, FamilyKrb5Principal, kbuf.length, kbuf.data);
654
krb5_free_principal(princ);
662
ErrorF("K5Add: getting principal from cache \"%s\" failed: %s\n",
663
nbuf, error_message(retval));
666
else if (!strncmp(data, "CS:", 3))
668
if ((cp = strchr(nbuf, ',')) == NULL)
673
*cp = '\0'; /* gross but it works :-) */
674
ktlen = strlen(cp + 1);
675
if ((ktname = (char *)malloc(ktlen + 1)) == NULL)
680
strcpy(ktname, cp + 1);
681
retval = krb5_sname_to_principal(NULL, /* NULL for hostname uses
683
nbuf, KRB5_NT_SRV_HST,
692
if (retval = krb5_kt_resolve(ktname, &keytab))
696
krb5_free_principal(srvname);
700
retval = krb5_kt_get_entry(keytab, srvname, kvno, &tmp_entry);
701
krb5_kt_free_entry(&tmp_entry);
706
krb5_free_principal(srvname);
710
if (XauKrb5Encode(srvname, &kbuf))
714
krb5_free_principal(srvname);
718
AddHost(NULL, FamilyKrb5Principal, kbuf.length, kbuf.data);
724
ErrorF("K5Add: credentials cache name \"%.*s\" in auth file: unknown type\n",
733
* Reset krb5_id, also nuke the current principal from the acl.
737
krb5_principal princ;
738
krb5_error_code retval;
745
if (retval = krb5_cc_resolve(ccname, &cc))
750
if (cc && !(retval = krb5_cc_get_principal(cc, &princ)))
752
if (XauKrb5Encode(princ, &kbuf))
754
RemoveHost(NULL, FamilyKrb5Principal, kbuf.length, kbuf.data);
755
krb5_free_principal(princ);
757
if (krb5_cc_close(cc))
765
if (XauKrb5Encode(srvname, &kbuf))
767
RemoveHost(NULL, FamilyKrb5Principal, kbuf.length, kbuf.data);
768
krb5_free_principal(srvname);
781
XID K5ToID(data_length, data)
782
unsigned short data_length;
788
int K5FromID(id, data_lenp, datap)
790
unsigned short *data_lenp;
796
int K5Remove(data_length, data)
797
unsigned short data_length;