1 /* $XConsortium: k5auth.c,v 1.9 95/04/06 16:10:29 mor Exp $ */
2 /* $XFree86: xc/programs/Xserver/os/k5auth.c,v 3.2 1996/05/10 07:02:15 dawes Exp $ */
5 Copyright (c) 1993, 1994 X Consortium
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 "Software"), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
15 The above copyright notice and this permission notice shall be included
16 in all copies or substantial portions of the Software.
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
22 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 OTHER DEALINGS IN THE SOFTWARE.
26 Except as contained in this notice, the name of the X Consortium shall
27 not be used in advertising or otherwise to promote the sale, use or
28 other dealings in this Software without prior written authorization
29 from the X Consortium.
34 * Kerberos V5 authentication scheme
35 * Author: Tom Yu <tlyu@MIT.EDU>
37 * Mostly snarfed wholesale from the user_user demo in the
38 * krb5 distribution. (At least the checking part)
41 #include <sys/types.h>
42 #include <sys/socket.h>
44 #include <netinet/in.h>
47 #include <netdnet/dn.h>
49 #include <arpa/inet.h>
50 #include <krb5/krb5.h>
51 /* 9/93: krb5.h leaks some symbols */
54 #include <krb5/los-proto.h>
60 #include "dixstruct.h"
64 extern int (*k5_Vector[256])();
65 extern int SendConnSetup();
66 extern char *display; /* need this to generate rcache name */
68 static XID krb5_id = ~0L;
69 static krb5_principal srvname = NULL; /* service name */
70 static char *ccname = NULL;
71 static char *ktname = NULL; /* key table name */
72 static char kerror[256];
77 * extract session key from a credentials struct
79 krb5_error_code tgt_keyproc(keyprocarg, principal, vno, key)
80 krb5_pointer keyprocarg;
81 krb5_principal principal;
85 krb5_creds *creds = (krb5_creds *)keyprocarg;
87 return krb5_copy_keyblock(&creds->keyblock, key);
93 * compare "encoded" principals
95 Bool k5_cmpenc(pname, plen, buf)
100 return (plen == buf->length &&
101 memcmp(pname, buf->data, plen) == 0);
107 * This is stage 0 of the krb5 authentication protocol. It
108 * goes through the current credentials cache and extracts the
109 * primary principal and tgt to send to the client, or as
110 * appropriate, extracts from a keytab.
112 * The packet sent to the client has the following format:
116 * CARD16 length = total length of packet (in 32 bit units)
117 * CARD16 plen = length of encoded principal following
118 * STRING8 princ = encoded principal
119 * STRING8 ticket = server tgt
121 * For client-server authentication, the packet is as follows:
125 * CARD16 length = total length
126 * STRING8 princ = encoded principal of server
128 XID K5Check(data_length, data, client, reason)
129 unsigned short data_length;
134 krb5_error_code retval;
136 krb5_principal sprinc, cprinc;
146 if (!ccname && !srvname)
150 if ((creds = (krb5_creds *)malloc(sizeof(krb5_creds))) == NULL)
152 if (retval = krb5_cc_resolve(ccname, &cc))
154 bzero((char*)creds, sizeof (krb5_creds));
155 if (retval = krb5_cc_get_principal(cc, &cprinc))
157 krb5_free_creds(creds);
161 creds->client = cprinc;
163 krb5_build_principal_ext(&sprinc,
164 krb5_princ_realm(creds->client)->length,
165 krb5_princ_realm(creds->client)->data,
167 krb5_princ_realm(creds->client)->length,
168 krb5_princ_realm(creds->client)->data,
171 krb5_free_creds(creds);
175 creds->server = sprinc;
176 retval = krb5_get_credentials(KRB5_GC_CACHED, cc, creds);
180 krb5_free_creds(creds);
183 if (retval = XauKrb5Encode(cprinc, &princ))
185 krb5_free_creds(creds);
188 tlen = sz_xReq + 2 + princ.length + creds->ticket.length;
189 prefix.reqType = 2; /* opcode = authenticate user-to-user */
193 if (retval = XauKrb5Encode(srvname, &princ))
197 tlen = sz_xReq + princ.length;
198 prefix.reqType = 3; /* opcode = authenticate client-server */
200 prefix.data = 0; /* stage = 0 */
201 prefix.length = (tlen + 3) >> 2; /* round up to nearest multiple
205 swaps(&prefix.length, n);
207 if ((cp = outbuf = (char *)malloc(tlen)) == NULL)
211 krb5_free_creds(creds);
216 memcpy(cp, &prefix, sz_xReq);
220 memcpy(cp, &princ.length, 2);
223 swaps((CARD16 *)cp, n);
227 memcpy(cp, princ.data, princ.length);
229 free(princ.data); /* we don't need that anymore */
231 memcpy(cp, creds->ticket.data, creds->ticket.length);
232 WriteToClient(client, tlen, outbuf);
234 client->requestVector = k5_Vector; /* hack in our dispatch vector */
235 client->clientState = ClientStateAuthenticating;
238 ((OsCommPtr)client->osPrivate)->authstate.srvcreds = (pointer)creds; /* save tgt creds */
239 ((OsCommPtr)client->osPrivate)->authstate.ktname = NULL;
240 ((OsCommPtr)client->osPrivate)->authstate.srvname = NULL;
244 ((OsCommPtr)client->osPrivate)->authstate.srvcreds = NULL;
245 ((OsCommPtr)client->osPrivate)->authstate.ktname = (pointer)ktname;
246 ((OsCommPtr)client->osPrivate)->authstate.srvname = (pointer)srvname;
248 ((OsCommPtr)client->osPrivate)->authstate.stageno = 1; /* next stage is 1 */
255 * This gets called out of the dispatcher after K5Check frobs with the
256 * client->requestVector. It accepts the ap_req from the client and verifies
257 * it. In addition, if the client has set AP_OPTS_MUTUAL_REQUIRED, it then
258 * sends an ap_rep to the client to achieve mutual authentication.
260 * client stage1 packet format is as follows:
263 * CARD8 data = ignored
264 * CARD16 length = total length
265 * STRING8 data = the actual ap_req
267 * stage2 packet sent back to client for mutual authentication:
271 * CARD16 length = total length
272 * STRING8 data = the ap_rep
274 int k5_stage1(client)
275 register ClientPtr client;
278 krb5_error_code retval, retval2;
280 struct sockaddr cli_net_addr;
282 krb5_principal cprinc;
284 krb5_creds *creds = (krb5_creds *)((OsCommPtr)client->osPrivate)->authstate.srvcreds;
286 krb5_address cli_addr, **localaddrs = NULL;
287 krb5_tkt_authent *authdat;
288 krb5_ap_rep_enc_part rep;
289 krb5_int32 ctime, cusec;
290 krb5_rcache rcache = NULL;
291 char *cachename = NULL, *rc_type = NULL, *rc_base = "rcX", *kt = NULL;
294 if (((OsCommPtr)client->osPrivate)->authstate.stageno != 1)
297 krb5_free_creds(creds);
298 return(SendConnSetup(client, "expected Krb5 stage1 packet"));
300 addrlen = sizeof (cli_net_addr);
301 if (getpeername(((OsCommPtr)client->osPrivate)->fd,
302 &cli_net_addr, &addrlen) == -1)
305 krb5_free_creds(creds);
306 return(SendConnSetup(client, "Krb5 stage1: getpeername failed"));
308 if (cli_net_addr.sa_family == AF_UNSPEC
309 #if defined(UNIXCONN) || defined(LOCALCONN) || defined(OS2PIPECONN)
310 || cli_net_addr.sa_family == AF_UNIX
312 ) /* assume local host */
314 krb5_os_localaddr(&localaddrs);
315 if (!localaddrs || !localaddrs[0])
318 krb5_free_creds(creds);
319 return(SendConnSetup(client, "Krb5 failed to get localaddrs"));
321 cli_addr.addrtype = localaddrs[0]->addrtype;
322 cli_addr.length = localaddrs[0]->length;
323 cli_addr.contents = localaddrs[0]->contents;
327 cli_addr.addrtype = cli_net_addr.sa_family; /* the values
329 switch (cli_net_addr.sa_family)
333 cli_addr.length = sizeof (struct in_addr);
335 (krb5_octet *)&((struct sockaddr_in *)&cli_net_addr)->sin_addr;
340 cli_addr.length = sizeof (struct dn_naddr);
342 (krb5_octet *)&((struct sockaddr_dn *)&cli_net_addr)->sdn_add;
347 krb5_free_addresses(localaddrs);
349 krb5_free_creds(creds);
350 sprintf(kerror, "Krb5 stage1: unknown address family %d from getpeername",
351 cli_net_addr.sa_family);
352 return(SendConnSetup(client, kerror));
355 if ((rcache = (krb5_rcache)malloc(sizeof(*rcache))) == NULL)
358 krb5_free_addresses(localaddrs);
360 krb5_free_creds(creds);
361 return(SendConnSetup(client, "malloc bombed for krb5_rcache"));
363 if ((rc_type = krb5_rc_default_type()) == NULL)
365 if (retval = krb5_rc_resolve_type(&rcache, rc_type))
368 krb5_free_addresses(localaddrs);
370 krb5_free_creds(creds);
372 strcpy(kerror, "krb5_rc_resolve_type failed: ");
373 strncat(kerror, error_message(retval), 231);
374 return(SendConnSetup(client, kerror));
376 if ((cachename = (char *)malloc(strlen(rc_base) + strlen(display) + 1))
380 krb5_free_addresses(localaddrs);
382 krb5_free_creds(creds);
384 return(SendConnSetup(client, "Krb5: malloc bombed for cachename"));
386 strcpy(cachename, rc_base);
387 strcat(cachename, display);
388 if (retval = krb5_rc_resolve(rcache, cachename))
391 krb5_free_addresses(localaddrs);
393 krb5_free_creds(creds);
396 strcpy(kerror, "krb5_rc_resolve failed: ");
397 strncat(kerror, error_message(retval), 236);
398 return(SendConnSetup(client, kerror));
401 if (krb5_rc_recover(rcache))
403 extern krb5_deltat krb5_clockskew;
404 if (retval = krb5_rc_initialize(rcache, krb5_clockskew))
407 krb5_free_addresses(localaddrs);
409 krb5_free_creds(creds);
410 if (retval2 = krb5_rc_close(rcache))
412 strcpy(kerror, "krb5_rc_close failed: ");
413 strncat(kerror, error_message(retval2), 238);
414 return(SendConnSetup(client, kerror));
417 strcpy(kerror, "krb5_rc_initialize failed: ");
418 strncat(kerror, error_message(retval), 233);
419 return(SendConnSetup(client, kerror));
422 buf.length = (stuff->length << 2) - sz_xReq;
423 buf.data = (char *)stuff + sz_xReq;
426 retval = krb5_rd_req(&buf,
427 NULL, /* don't bother with server name */
429 NULL, /* no fetchfrom */
431 creds, /* credentials as arg to
435 krb5_free_creds(creds);
437 else if (kt = (char *)((OsCommPtr)client->osPrivate)->authstate.ktname)
439 retval = krb5_rd_req(&buf, srvname, &cli_addr, kt, NULL, NULL,
441 ((OsCommPtr)client->osPrivate)->authstate.ktname = NULL;
446 krb5_free_addresses(localaddrs);
447 return(SendConnSetup(client, "Krb5: neither srvcreds nor ktname set"));
450 krb5_free_addresses(localaddrs);
453 if (retval2 = krb5_rc_close(rcache))
455 strcpy(kerror, "krb5_rc_close failed (2): ");
456 strncat(kerror, error_message(retval2), 230);
457 return(SendConnSetup(client, kerror));
463 strcpy(kerror, "Krb5: Bad application request: ");
464 strncat(kerror, error_message(retval), 224);
465 return(SendConnSetup(client, kerror));
467 cprinc = authdat->ticket->enc_part2->client;
468 skey = authdat->ticket->enc_part2->session;
469 if (XauKrb5Encode(cprinc, &buf))
471 krb5_free_tkt_authent(authdat);
472 return(SendConnSetup(client, "XauKrb5Encode bombed"));
475 * Now check to see if the principal we got is one that we want to let in
477 if (ForEachHostInFamily(FamilyKrb5Principal, k5_cmpenc, (pointer)&buf))
481 * The following deals with sending an ap_rep to the client to
482 * achieve mutual authentication. The client sends back a stage 3
483 * packet if all is ok.
485 if (authdat->ap_options | AP_OPTS_MUTUAL_REQUIRED)
488 * stage 2: send ap_rep to client
490 if (retval = krb5_us_timeofday(&ctime, &cusec))
492 krb5_free_tkt_authent(authdat);
493 strcpy(kerror, "error in krb5_us_timeofday: ");
494 strncat(kerror, error_message(retval), 234);
495 return(SendConnSetup(client, kerror));
501 if (retval = krb5_mk_rep(&rep, skey, &buf))
503 krb5_free_tkt_authent(authdat);
504 strcpy(kerror, "error in krb5_mk_rep: ");
505 strncat(kerror, error_message(retval), 238);
506 return(SendConnSetup(client, kerror));
508 prefix.reqType = 2; /* opcode = authenticate */
509 prefix.data = 2; /* stage = 2 */
510 prefix.length = (buf.length + sz_xReq + 3) >> 2;
513 swaps(&prefix.length, n);
515 WriteToClient(client, sz_xReq, (char *)&prefix);
516 WriteToClient(client, buf.length, buf.data);
518 krb5_free_tkt_authent(authdat);
519 ((OsCommPtr)client->osPrivate)->authstate.stageno = 3; /* expect stage3 packet */
525 krb5_free_tkt_authent(authdat);
526 return(SendConnSetup(client, NULL)); /* success! */
533 krb5_free_tkt_authent(authdat);
535 retval = krb5_unparse_name(cprinc, &kname);
538 sprintf(kerror, "Principal \"%s\" is not authorized to connect",
542 return(SendConnSetup(client, kerror));
545 return(SendConnSetup(client,"Principal is not authorized to connect to Server"));
552 * Get the short ack packet from the client. This packet can conceivably
553 * be expanded to allow for switching on end-to-end encryption.
555 * stage3 packet format:
558 * CARD8 data = ignored (for now)
559 * CARD16 length = should be zero
561 int k5_stage3(client)
562 register ClientPtr client;
566 if (((OsCommPtr)client->osPrivate)->authstate.stageno != 3)
568 return(SendConnSetup(client, "expected Krb5 stage3 packet"));
571 return(SendConnSetup(client, NULL)); /* success! */
575 register ClientPtr client;
577 if (((OsCommPtr)client->osPrivate)->authstate.srvcreds)
578 krb5_free_creds((krb5_creds *)((OsCommPtr)client->osPrivate)->authstate.srvcreds);
579 sprintf(kerror, "unrecognized Krb5 auth packet %d, expecting %d",
580 ((xReq *)client->requestBuffer)->reqType,
581 ((OsCommPtr)client->osPrivate)->authstate.stageno);
582 return(SendConnSetup(client, kerror));
588 * Takes the name of a credentials cache and resolves it. Also adds the
589 * primary principal of the ccache to the acl.
591 * Now will also take a service name.
593 int K5Add(data_length, data, id)
594 unsigned short data_length;
598 krb5_principal princ;
599 krb5_error_code retval;
600 krb5_keytab_entry tmp_entry;
608 krb5_init_ets(); /* can't think of a better place to put it */
612 if ((nbuf = (char *)malloc(data_length - 2)) == NULL)
614 memcpy(nbuf, data + 3, data_length - 3);
615 nbuf[data_length - 3] = '\0';
623 krb5_free_principal(srvname);
631 if (!strncmp(data, "UU:", 3))
633 if (retval = krb5_cc_resolve(nbuf, &cc))
635 ErrorF("K5Add: krb5_cc_resolve of \"%s\" failed: %s\n",
636 nbuf, error_message(retval));
640 if (cc && !(retval = krb5_cc_get_principal(cc, &princ)))
642 if (XauKrb5Encode(princ, &kbuf))
645 krb5_free_principal(princ);
649 if (krb5_cc_close(cc))
651 AddHost(NULL, FamilyKrb5Principal, kbuf.length, kbuf.data);
652 krb5_free_principal(princ);
660 ErrorF("K5Add: getting principal from cache \"%s\" failed: %s\n",
661 nbuf, error_message(retval));
664 else if (!strncmp(data, "CS:", 3))
666 if ((cp = strchr(nbuf, ',')) == NULL)
671 *cp = '\0'; /* gross but it works :-) */
672 ktlen = strlen(cp + 1);
673 if ((ktname = (char *)malloc(ktlen + 1)) == NULL)
678 strcpy(ktname, cp + 1);
679 retval = krb5_sname_to_principal(NULL, /* NULL for hostname uses
681 nbuf, KRB5_NT_SRV_HST,
690 if (retval = krb5_kt_resolve(ktname, &keytab))
694 krb5_free_principal(srvname);
698 retval = krb5_kt_get_entry(keytab, srvname, kvno, &tmp_entry);
699 krb5_kt_free_entry(&tmp_entry);
704 krb5_free_principal(srvname);
708 if (XauKrb5Encode(srvname, &kbuf))
712 krb5_free_principal(srvname);
716 AddHost(NULL, FamilyKrb5Principal, kbuf.length, kbuf.data);
722 ErrorF("K5Add: credentials cache name \"%.*s\" in auth file: unknown type\n",
731 * Reset krb5_id, also nuke the current principal from the acl.
735 krb5_principal princ;
736 krb5_error_code retval;
743 if (retval = krb5_cc_resolve(ccname, &cc))
748 if (cc && !(retval = krb5_cc_get_principal(cc, &princ)))
750 if (XauKrb5Encode(princ, &kbuf))
752 RemoveHost(NULL, FamilyKrb5Principal, kbuf.length, kbuf.data);
753 krb5_free_principal(princ);
755 if (krb5_cc_close(cc))
763 if (XauKrb5Encode(srvname, &kbuf))
765 RemoveHost(NULL, FamilyKrb5Principal, kbuf.length, kbuf.data);
766 krb5_free_principal(srvname);
779 XID K5ToID(data_length, data)
780 unsigned short data_length;
786 int K5FromID(id, data_lenp, datap)
788 unsigned short *data_lenp;
794 int K5Remove(data_length, data)
795 unsigned short data_length;