1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * Copyright 2000, 2004, 2007, 2008 by the Massachusetts Institute of Technology.
6 * Export of this software from the United States of America may
7 * require a specific license from the United States Government.
8 * It is the responsibility of any person or organization contemplating
9 * export to obtain such a license before exporting.
11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12 * distribute this software and its documentation for any purpose and
13 * without fee is hereby granted, provided that the above copyright
14 * notice appear in all copies and that both that copyright notice and
15 * this permission notice appear in supporting documentation, and that
16 * the name of M.I.T. not be used in advertising or publicity pertaining
17 * to distribution of the software without specific, written prior
18 * permission. Furthermore if you modify this software you must label
19 * your software as modified software and not distribute it in such a
20 * fashion that it might be confused with the original M.I.T. software.
21 * M.I.T. makes no representations about the suitability of
22 * this software for any purpose. It is provided "as is" without express
23 * or implied warranty.
27 * Copyright 1993 by OpenVision Technologies, Inc.
29 * Permission to use, copy, modify, distribute, and sell this software
30 * and its documentation for any purpose is hereby granted without fee,
31 * provided that the above copyright notice appears in all copies and
32 * that both that copyright notice and this permission notice appear in
33 * supporting documentation, and that the name of OpenVision not be used
34 * in advertising or publicity pertaining to distribution of the software
35 * without specific, written prior permission. OpenVision makes no
36 * representations about the suitability of this software for any
37 * purpose. It is provided "as is" without express or implied warranty.
39 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
40 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
41 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
42 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
43 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
44 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
45 * PERFORMANCE OF THIS SOFTWARE.
49 * Copyright (C) 1998 by the FundsXpress, INC.
51 * All rights reserved.
53 * Export of this software from the United States of America may require
54 * a specific license from the United States Government. It is the
55 * responsibility of any person or organization contemplating export to
56 * obtain such a license before exporting.
58 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
59 * distribute this software and its documentation for any purpose and
60 * without fee is hereby granted, provided that the above copyright
61 * notice appear in all copies and that both that copyright notice and
62 * this permission notice appear in supporting documentation, and that
63 * the name of FundsXpress. not be used in advertising or publicity pertaining
64 * to distribution of the software without specific, written prior
65 * permission. FundsXpress makes no representations about the suitability of
66 * this software for any purpose. It is provided "as is" without express
67 * or implied warranty.
69 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
70 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
71 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
74 * Copyright (c) 2006-2008, Novell, Inc.
75 * All rights reserved.
77 * Redistribution and use in source and binary forms, with or without
78 * modification, are permitted provided that the following conditions are met:
80 * * Redistributions of source code must retain the above copyright notice,
81 * this list of conditions and the following disclaimer.
82 * * Redistributions in binary form must reproduce the above copyright
83 * notice, this list of conditions and the following disclaimer in the
84 * documentation and/or other materials provided with the distribution.
85 * * The copyright holder's name is not used to endorse or promote products
86 * derived from this software without specific prior written permission.
88 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
89 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
90 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
91 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
92 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
93 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
94 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
95 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
96 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
97 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
98 * POSSIBILITY OF SUCH DAMAGE.
102 #include "gssapiP_krb5.h"
109 #define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
111 #define CFX_ACCEPTOR_SUBKEY 1
117 create_constrained_deleg_creds(OM_uint32 *minor_status,
118 krb5_gss_cred_id_t verifier_cred_handle,
120 krb5_gss_cred_id_t *out_cred,
121 krb5_context context)
123 OM_uint32 major_status;
124 krb5_creds krb_creds;
126 krb5_error_code code;
128 assert(out_cred != NULL);
129 assert(verifier_cred_handle->usage == GSS_C_BOTH);
131 memset(&krb_creds, 0, sizeof(krb_creds));
132 krb_creds.client = ticket->enc_part2->client;
133 krb_creds.server = ticket->server;
134 krb_creds.keyblock = *(ticket->enc_part2->session);
135 krb_creds.ticket_flags = ticket->enc_part2->flags;
136 krb_creds.times = ticket->enc_part2->times;
137 krb_creds.magic = KV5M_CREDS;
138 krb_creds.authdata = NULL;
140 code = encode_krb5_ticket(ticket, &data);
142 *minor_status = code;
143 return GSS_S_FAILURE;
146 krb_creds.ticket = *data;
148 major_status = kg_compose_deleg_cred(minor_status,
149 verifier_cred_handle,
156 krb5_free_data(context, data);
161 /* Decode, decrypt and store the forwarded creds in the local ccache. */
162 static krb5_error_code
163 rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
164 krb5_context context;
165 krb5_auth_context auth_context;
167 krb5_gss_cred_id_t *out_cred;
169 krb5_creds ** creds = NULL;
170 krb5_error_code retval;
171 krb5_ccache ccache = NULL;
172 krb5_gss_cred_id_t cred = NULL;
173 krb5_auth_context new_auth_ctx = NULL;
174 krb5_int32 flags_org;
176 if ((retval = krb5_auth_con_getflags(context, auth_context, &flags_org)))
178 krb5_auth_con_setflags(context, auth_context,
182 * By the time krb5_rd_cred is called here (after krb5_rd_req has been
183 * called in krb5_gss_accept_sec_context), the "keyblock" field of
184 * auth_context contains a pointer to the session key, and the
185 * "recv_subkey" field might contain a session subkey. Either of
186 * these (the "recv_subkey" if it isn't NULL, otherwise the
187 * "keyblock") might have been used to encrypt the encrypted part of
188 * the KRB_CRED message that contains the forwarded credentials. (The
189 * Java Crypto and Security Implementation from the DSTC in Australia
190 * always uses the session key. But apparently it never negotiates a
191 * subkey, so this code works fine against a JCSI client.) Up to the
192 * present, though, GSSAPI clients linked against the MIT code (which
193 * is almost all GSSAPI clients) don't encrypt the KRB_CRED message at
194 * all -- at this level. So if the first call to krb5_rd_cred fails,
195 * we should call it a second time with another auth context freshly
196 * created by krb5_auth_con_init. All of its keyblock fields will be
197 * NULL, so krb5_rd_cred will assume that the KRB_CRED message is
198 * unencrypted. (The MIT code doesn't actually send the KRB_CRED
199 * message in the clear -- the "authenticator" whose "checksum" ends up
200 * containing the KRB_CRED message does get encrypted.)
202 if (krb5_rd_cred(context, auth_context, inbuf, &creds, NULL)) {
203 if ((retval = krb5_auth_con_init(context, &new_auth_ctx)))
205 krb5_auth_con_setflags(context, new_auth_ctx, 0);
206 if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf,
211 if ((retval = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) {
216 if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client)))
219 if ((retval = krb5_cc_store_cred(context, ccache, creds[0])))
222 /* generate a delegated credential handle */
224 /* allocate memory for a cred_t... */
226 (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) {
227 retval = ENOMEM; /* out of memory? */
232 memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
234 retval = k5_mutex_init(&cred->lock);
241 /* copy the client principle into it... */
243 kg_init_name(context, creds[0]->client, NULL, 0, &cred->name))) {
244 k5_mutex_destroy(&cred->lock);
245 retval = ENOMEM; /* out of memory? */
246 xfree(cred); /* clean up memory on failure */
251 cred->usage = GSS_C_INITIATE; /* we can't accept with this */
252 /* cred->name already set */
253 cred->keytab = NULL; /* no keytab associated with this... */
254 cred->tgt_expire = creds[0]->times.endtime; /* store the end time */
255 cred->ccache = ccache; /* the ccache containing the credential */
256 cred->destroy_ccache = 1;
257 ccache = NULL; /* cred takes ownership so don't destroy */
260 /* If there were errors, there might have been a memory leak
262 if ((retval = krb5_cc_close(context, ccache)))
267 krb5_free_tgt_creds(context, creds);
270 (void)krb5_cc_destroy(context, ccache);
273 *out_cred = cred; /* return credential */
276 krb5_auth_con_free(context, new_auth_ctx);
278 krb5_auth_con_setflags(context, auth_context, flags_org);
285 * Performs third leg of DCE authentication
288 kg_accept_dce(minor_status, context_handle, verifier_cred_handle,
289 input_token, input_chan_bindings, src_name, mech_type,
290 output_token, ret_flags, time_rec, delegated_cred_handle)
291 OM_uint32 *minor_status;
292 gss_ctx_id_t *context_handle;
293 gss_cred_id_t verifier_cred_handle;
294 gss_buffer_t input_token;
295 gss_channel_bindings_t input_chan_bindings;
296 gss_name_t *src_name;
298 gss_buffer_t output_token;
299 OM_uint32 *ret_flags;
301 gss_cred_id_t *delegated_cred_handle;
303 krb5_error_code code;
304 krb5_gss_ctx_id_rec *ctx = 0;
306 krb5_gss_name_t name = NULL;
309 OM_uint32 major_status = GSS_S_FAILURE;
311 output_token->length = 0;
312 output_token->value = NULL;
315 *mech_type = GSS_C_NULL_OID;
316 /* return a bogus cred handle */
317 if (delegated_cred_handle)
318 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
320 ctx = (krb5_gss_ctx_id_rec *)*context_handle;
322 code = krb5_timeofday(ctx->k5_context, &now);
324 major_status = GSS_S_FAILURE;
328 if (ctx->krb_times.endtime < now) {
330 major_status = GSS_S_CREDENTIALS_EXPIRED;
334 ap_rep.data = input_token->value;
335 ap_rep.length = input_token->length;
337 code = krb5_rd_rep_dce(ctx->k5_context,
342 major_status = GSS_S_FAILURE;
346 ctx->established = 1;
349 if ((code = kg_duplicate_name(ctx->k5_context, ctx->there,
350 KG_INIT_NAME_INTERN, &name))) {
351 major_status = GSS_S_FAILURE;
354 *src_name = (gss_name_t) name;
358 *mech_type = ctx->mech_used;
361 *time_rec = ctx->krb_times.endtime - now;
364 *ret_flags = ctx->gss_flags;
366 /* XXX no support for delegated credentials yet */
370 return GSS_S_COMPLETE;
373 /* real failure code follows */
375 (void) krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx,
377 *context_handle = GSS_C_NO_CONTEXT;
378 *minor_status = code;
383 static krb5_error_code
384 kg_process_extension(krb5_context context,
385 krb5_auth_context auth_context,
388 krb5_gss_ctx_ext_t exts)
390 krb5_error_code code = 0;
392 assert(exts != NULL);
395 case KRB5_GSS_EXTS_IAKERB_FINISHED:
396 if (exts->iakerb.conv == NULL) {
397 code = KRB5KRB_AP_ERR_MSG_TYPE; /* XXX */
401 code = krb5_auth_con_getrecvsubkey_k(context, auth_context, &key);
405 code = iakerb_verify_finished(context, key, exts->iakerb.conv,
408 exts->iakerb.verified = 1;
410 krb5_k_free_key(context, key);
421 kg_accept_krb5(minor_status, context_handle,
422 verifier_cred_handle, input_token,
423 input_chan_bindings, src_name, mech_type,
424 output_token, ret_flags, time_rec,
425 delegated_cred_handle, exts)
426 OM_uint32 *minor_status;
427 gss_ctx_id_t *context_handle;
428 gss_cred_id_t verifier_cred_handle;
429 gss_buffer_t input_token;
430 gss_channel_bindings_t input_chan_bindings;
431 gss_name_t *src_name;
433 gss_buffer_t output_token;
434 OM_uint32 *ret_flags;
436 gss_cred_id_t *delegated_cred_handle;
437 krb5_gss_ctx_ext_t exts;
439 krb5_context context;
440 unsigned char *ptr, *ptr2;
445 krb5_gss_cred_id_t cred = 0;
446 krb5_data ap_rep, ap_req;
448 krb5_error_code code;
449 krb5_address addr, *paddr;
450 krb5_authenticator *authdat = 0;
451 krb5_checksum reqcksum;
452 krb5_gss_name_t name = NULL;
453 krb5_ui_4 gss_flags = 0;
454 int decode_req_message = 0;
455 krb5_gss_ctx_id_rec *ctx = NULL;
457 gss_buffer_desc token;
458 krb5_auth_context auth_context = NULL;
459 krb5_ticket * ticket = NULL;
462 const gss_OID_desc *mech_used = NULL;
463 OM_uint32 major_status = GSS_S_FAILURE;
464 OM_uint32 tmp_minor_status;
465 krb5_error krb_error_data;
467 gss_cred_id_t cred_handle = NULL;
468 krb5_gss_cred_id_t deleg_cred = NULL;
469 krb5int_access kaccess;
472 krb5_flags ap_req_options = 0;
473 krb5_enctype negotiated_etype;
474 krb5_authdata_context ad_context = NULL;
476 code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
478 *minor_status = code;
479 return(GSS_S_FAILURE);
482 code = krb5_gss_init_context(&context);
484 *minor_status = code;
485 return GSS_S_FAILURE;
488 /* set up returns to be freeable */
491 *src_name = (gss_name_t) NULL;
492 output_token->length = 0;
493 output_token->value = NULL;
495 reqcksum.contents = 0;
500 *mech_type = GSS_C_NULL_OID;
501 /* return a bogus cred handle */
502 if (delegated_cred_handle)
503 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
505 /* handle default cred handle */
506 if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
507 major_status = krb5_gss_acquire_cred(minor_status, GSS_C_NO_NAME,
508 GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
509 GSS_C_ACCEPT, &cred_handle,
511 if (major_status != GSS_S_COMPLETE) {
512 code = *minor_status;
516 major_status = krb5_gss_validate_cred(minor_status,
517 verifier_cred_handle);
518 if (GSS_ERROR(major_status)) {
519 code = *minor_status;
522 cred_handle = verifier_cred_handle;
525 cred = (krb5_gss_cred_id_t) cred_handle;
527 /* make sure the supplied credentials are valid for accept */
529 if ((cred->usage != GSS_C_ACCEPT) &&
530 (cred->usage != GSS_C_BOTH)) {
532 major_status = GSS_S_NO_CRED;
536 /* verify the token's integrity, and leave the token in ap_req.
537 figure out which mech oid was used, and save it */
539 ptr = (unsigned char *) input_token->value;
541 if (!(code = g_verify_token_header(gss_mech_krb5,
543 &ptr, KG_TOK_CTX_AP_REQ,
544 input_token->length, 1))) {
545 mech_used = gss_mech_krb5;
546 } else if ((code == G_WRONG_MECH)
547 &&!(code = g_verify_token_header((gss_OID) gss_mech_iakerb,
549 &ptr, KG_TOK_CTX_AP_REQ,
550 input_token->length, 1))) {
551 mech_used = gss_mech_iakerb;
552 } else if ((code == G_WRONG_MECH)
553 &&!(code = g_verify_token_header((gss_OID) gss_mech_krb5_wrong,
555 &ptr, KG_TOK_CTX_AP_REQ,
556 input_token->length, 1))) {
557 mech_used = gss_mech_krb5_wrong;
558 } else if ((code == G_WRONG_MECH) &&
559 !(code = g_verify_token_header(gss_mech_krb5_old,
561 &ptr, KG_TOK_CTX_AP_REQ,
562 input_token->length, 1))) {
564 * Previous versions of this library used the old mech_id
565 * and some broken behavior (wrong IV on checksum
566 * encryption). We support the old mech_id for
567 * compatibility, and use it to decide when to use the
570 mech_used = gss_mech_krb5_old;
571 } else if (code == G_WRONG_TOKID) {
572 major_status = GSS_S_CONTINUE_NEEDED;
573 code = KRB5KRB_AP_ERR_MSG_TYPE;
574 mech_used = gss_mech_krb5;
576 } else if (code == G_BAD_TOK_HEADER) {
577 /* DCE style not encapsulated */
578 ap_req.length = input_token->length;
579 ap_req.data = input_token->value;
580 mech_used = gss_mech_krb5;
583 major_status = GSS_S_DEFECTIVE_TOKEN;
588 TREAD_STR(sptr, ap_req.data, ap_req.length);
589 decode_req_message = 1;
591 /* construct the sender_addr */
593 if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) &&
594 (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) {
595 /* XXX is this right? */
596 addr.addrtype = ADDRTYPE_INET;
597 addr.length = input_chan_bindings->initiator_address.length;
598 addr.contents = input_chan_bindings->initiator_address.value;
605 /* decode the AP_REQ message */
607 /* decode the message */
609 if ((code = krb5_auth_con_init(context, &auth_context))) {
610 major_status = GSS_S_FAILURE;
611 save_error_info((OM_uint32)code, context);
616 if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) {
617 major_status = GSS_S_FAILURE;
621 if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) {
622 major_status = GSS_S_FAILURE;
626 if ((code = krb5_rd_req(context, &auth_context, &ap_req,
627 cred->default_identity ? NULL : cred->name->princ,
631 major_status = GSS_S_FAILURE;
634 krb5_auth_con_setflags(context, auth_context,
635 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
637 krb5_auth_con_getauthenticator(context, auth_context, &authdat);
640 /* make sure the necessary parts of the authdat are present */
642 if ((authdat->authenticator->subkey == NULL) ||
643 (authdat->ticket->enc_part2 == NULL)) {
645 major_status = GSS_S_FAILURE;
650 if (authdat->checksum == NULL) {
651 /* missing checksum counts as "inappropriate type" */
652 code = KRB5KRB_AP_ERR_INAPP_CKSUM;
653 major_status = GSS_S_FAILURE;
657 if (authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) {
658 /* Samba does not send 0x8003 GSS-API checksums */
663 code = krb5_auth_con_getkey_k(context, auth_context, &subkey);
665 major_status = GSS_S_FAILURE;
672 code = krb5_k_verify_checksum(context,
674 KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
678 krb5_k_free_key(context, subkey);
679 if (code || !valid) {
680 major_status = GSS_S_BAD_SIG;
684 gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
686 decode_req_message = 0;
690 /* stash this now, for later. */
691 code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len);
693 major_status = GSS_S_FAILURE;
697 /* verify that the checksum is correct */
700 The checksum may be either exactly 24 bytes, in which case
701 no options are specified, or greater than 24 bytes, in which case
702 one or more options are specified. Currently, the only valid
703 option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
706 if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
707 (authdat->checksum->length < 24)) {
709 major_status = GSS_S_BAD_BINDINGS;
714 "Be liberal in what you accept, and
715 conservative in what you send"
718 This code will let this acceptor interoperate with an initiator
719 using little-endian or big-endian integer encoding.
722 ptr = (unsigned char *) authdat->checksum->contents;
725 TREAD_INT(ptr, tmp, bigend);
728 ptr = (unsigned char *) authdat->checksum->contents;
731 TREAD_INT(ptr, tmp, bigend);
734 code = KG_BAD_LENGTH;
735 major_status = GSS_S_FAILURE;
740 /* at this point, bigend is set according to the initiator's
745 The following section of code attempts to implement the
746 optional channel binding facility as described in RFC2743.
748 Since this facility is optional channel binding may or may
749 not have been provided by either the client or the server.
751 If the server has specified input_chan_bindings equal to
752 GSS_C_NO_CHANNEL_BINDINGS then we skip the check. If
753 the server does provide channel bindings then we compute
754 a checksum and compare against those provided by the
757 if ((code = kg_checksum_channel_bindings(context,
759 &reqcksum, bigend))) {
760 major_status = GSS_S_BAD_BINDINGS;
764 /* Always read the clients bindings - eventhough we might ignore them */
765 TREAD_STR(ptr, ptr2, reqcksum.length);
767 if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) {
768 if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
769 xfree(reqcksum.contents);
770 reqcksum.contents = 0;
772 major_status = GSS_S_BAD_BINDINGS;
778 xfree(reqcksum.contents);
779 reqcksum.contents = 0;
781 TREAD_INT(ptr, gss_flags, bigend);
783 gss_flags &= ~GSS_C_DELEG_FLAG; /* mask out the delegation flag; if
784 there's a delegation, we'll set
787 decode_req_message = 0;
789 /* if the checksum length > 24, there are options to process */
791 i = authdat->checksum->length - 24;
792 if (i && (gss_flags & GSS_C_DELEG_FLAG)) {
794 TREAD_INT16(ptr, option_id, bigend);
795 TREAD_INT16(ptr, option.length, bigend);
798 if (i < option.length || option.length < 0) {
799 code = KG_BAD_LENGTH;
800 major_status = GSS_S_FAILURE;
804 /* have to use ptr2, since option.data is wrong type and
805 macro uses ptr as both lvalue and rvalue */
807 TREAD_STR(ptr, ptr2, option.length);
808 option.data = (char *) ptr2;
812 if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
813 major_status = GSS_S_FAILURE;
817 /* store the delegated credential */
819 code = rd_and_store_for_creds(context, auth_context, &option,
820 (delegated_cred_handle) ?
823 major_status = GSS_S_FAILURE;
828 /* ignore any additional trailing data, for now */
831 /* Process Type-Length-Data options */
833 code = KG_BAD_LENGTH;
834 major_status = GSS_S_FAILURE;
837 TREAD_INT(ptr, option_id, 1);
838 TREAD_INT(ptr, option.length, 1);
840 if (i < option.length) {
841 code = KG_BAD_LENGTH;
842 major_status = GSS_S_FAILURE;
845 TREAD_STR(ptr, ptr2, option.length);
846 option.data = (char *)ptr2;
850 code = kg_process_extension(context, auth_context,
851 option_id, &option, exts);
853 major_status = GSS_S_FAILURE;
859 if (exts->iakerb.conv && !exts->iakerb.verified) {
860 major_status = GSS_S_BAD_SIG;
864 /* only DCE_STYLE clients are allowed to send raw AP-REQs */
865 if (no_encap != ((gss_flags & GSS_C_DCE_STYLE) != 0)) {
866 major_status = GSS_S_DEFECTIVE_TOKEN;
870 /* create the ctx struct and start filling it in */
872 if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
875 major_status = GSS_S_FAILURE;
879 memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
880 ctx->magic = KG_CONTEXT;
881 ctx->mech_used = (gss_OID) mech_used;
882 ctx->auth_context = auth_context;
884 ctx->gss_flags = (GSS_C_TRANS_FLAG |
885 ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
886 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
887 GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG |
888 GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG |
889 GSS_C_EXTENDED_ERROR_FLAG)));
891 ctx->big_endian = bigend;
892 ctx->cred_rcache = cred_rcache;
894 /* Intern the ctx pointer so that delete_sec_context works */
895 if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
899 code = G_VALIDATE_FAILED;
900 major_status = GSS_S_FAILURE;
904 /* XXX move this into gss_name_t */
905 if ( (code = krb5_merge_authdata(context,
906 ticket->enc_part2->authorization_data,
907 authdat->authorization_data,
909 major_status = GSS_S_FAILURE;
912 if ((code = kg_init_name(context, ticket->server, NULL, 0, &ctx->here))) {
913 major_status = GSS_S_FAILURE;
916 if ((code = krb5_auth_con_get_authdata_context(context, auth_context,
918 major_status = GSS_S_FAILURE;
921 if ((code = kg_init_name(context, authdat->client,
922 ad_context, KG_INIT_NAME_NO_COPY, &ctx->there))) {
923 major_status = GSS_S_FAILURE;
926 /* Now owned by ctx->there */
927 authdat->client = NULL;
928 krb5_auth_con_set_authdata_context(context, auth_context, NULL);
930 if ((code = krb5_auth_con_getrecvsubkey_k(context, auth_context,
932 major_status = GSS_S_FAILURE;
936 /* use the session key if the subkey isn't present */
938 if (ctx->subkey == NULL) {
939 if ((code = krb5_auth_con_getkey_k(context, auth_context,
941 major_status = GSS_S_FAILURE;
946 if (ctx->subkey == NULL) {
947 /* this isn't a very good error, but it's not clear to me this
948 can actually happen */
949 major_status = GSS_S_FAILURE;
950 code = KRB5KDC_ERR_NULL_KEY;
956 ctx->have_acceptor_subkey = 0;
957 /* DCE_STYLE implies acceptor_subkey */
958 if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0) {
959 code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype);
961 major_status = GSS_S_FAILURE;
965 ctx->krb_times = ticket->enc_part2->times; /* struct copy */
966 ctx->krb_flags = ticket->enc_part2->flags;
968 if (delegated_cred_handle != NULL &&
969 deleg_cred == NULL && /* no unconstrained delegation */
970 cred->usage == GSS_C_BOTH &&
971 (ticket->enc_part2->flags & TKT_FLG_FORWARDABLE)) {
973 * Now, we always fabricate a delegated credentials handle
974 * containing the service ticket to ourselves, which can be
975 * used for S4U2Proxy.
977 major_status = create_constrained_deleg_creds(minor_status, cred,
980 if (GSS_ERROR(major_status))
982 ctx->gss_flags |= GSS_C_DELEG_FLAG;
985 krb5_free_ticket(context, ticket); /* Done with ticket */
989 krb5_auth_con_getremoteseqnumber(context, auth_context, &seq_temp);
990 ctx->seq_recv = seq_temp;
993 if ((code = krb5_timeofday(context, &now))) {
994 major_status = GSS_S_FAILURE;
998 if (ctx->krb_times.endtime < now) {
1000 major_status = GSS_S_CREDENTIALS_EXPIRED;
1004 g_order_init(&(ctx->seqstate), ctx->seq_recv,
1005 (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
1006 (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
1008 /* DCE_STYLE implies mutual authentication */
1009 if (ctx->gss_flags & GSS_C_DCE_STYLE)
1010 ctx->gss_flags |= GSS_C_MUTUAL_FLAG;
1012 /* at this point, the entire context structure is filled in,
1013 so it can be released. */
1015 /* generate an AP_REP if necessary */
1017 if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
1018 unsigned char * ptr3;
1019 krb5_int32 seq_temp;
1020 int cfx_generate_subkey;
1023 * Do not generate a subkey per RFC 4537 unless we are upgrading to CFX,
1024 * because pre-CFX tokens do not indicate which key to use. (Note that
1025 * DCE_STYLE implies that we will use a subkey.)
1027 if (ctx->proto == 0 &&
1028 (ctx->gss_flags & GSS_C_DCE_STYLE) == 0 &&
1029 (ap_req_options & AP_OPTS_USE_SUBKEY)) {
1030 code = (*kaccess.auth_con_get_subkey_enctype)(context,
1034 major_status = GSS_S_FAILURE;
1038 switch (negotiated_etype) {
1039 case ENCTYPE_DES_CBC_MD5:
1040 case ENCTYPE_DES_CBC_MD4:
1041 case ENCTYPE_DES_CBC_CRC:
1042 case ENCTYPE_DES3_CBC_SHA1:
1043 case ENCTYPE_ARCFOUR_HMAC:
1044 case ENCTYPE_ARCFOUR_HMAC_EXP:
1045 ap_req_options &= ~(AP_OPTS_USE_SUBKEY);
1050 if (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) ||
1051 (ap_req_options & AP_OPTS_USE_SUBKEY))
1052 cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY;
1054 cfx_generate_subkey = 0;
1056 if (cfx_generate_subkey) {
1058 code = krb5_auth_con_getflags(context, auth_context, &acflags);
1060 acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY;
1061 code = krb5_auth_con_setflags(context, auth_context, acflags);
1064 major_status = GSS_S_FAILURE;
1069 if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) {
1070 major_status = GSS_S_FAILURE;
1074 krb5_auth_con_getlocalseqnumber(context, auth_context, &seq_temp);
1075 ctx->seq_send = seq_temp & 0xffffffffL;
1077 if (cfx_generate_subkey) {
1078 /* Get the new acceptor subkey. With the code above, there
1079 should always be one if we make it to this point. */
1080 code = krb5_auth_con_getsendsubkey_k(context, auth_context,
1081 &ctx->acceptor_subkey);
1083 major_status = GSS_S_FAILURE;
1086 ctx->have_acceptor_subkey = 1;
1088 code = kg_setup_keys(context, ctx, ctx->acceptor_subkey,
1089 &ctx->acceptor_subkey_cksumtype);
1091 major_status = GSS_S_FAILURE;
1096 /* the reply token hasn't been sent yet, but that's ok. */
1097 if (ctx->gss_flags & GSS_C_DCE_STYLE) {
1098 assert(ctx->have_acceptor_subkey);
1100 /* in order to force acceptor subkey to be used, don't set PROT_READY */
1102 /* Raw AP-REP is returned */
1103 output_token->length = ap_rep.length;
1104 output_token->value = ap_rep.data;
1105 ap_rep.data = NULL; /* don't double free */
1107 ctx->established = 0;
1109 *context_handle = (gss_ctx_id_t)ctx;
1111 major_status = GSS_S_CONTINUE_NEEDED;
1113 /* Only last leg should set return arguments */
1116 ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
1118 ctx->established = 1;
1120 token.length = g_token_size(mech_used, ap_rep.length);
1122 if ((token.value = (unsigned char *) xmalloc(token.length))
1124 major_status = GSS_S_FAILURE;
1129 g_make_token_header(mech_used, ap_rep.length,
1130 &ptr3, KG_TOK_CTX_AP_REP);
1132 TWRITE_STR(ptr3, ap_rep.data, ap_rep.length);
1134 ctx->established = 1;
1139 ctx->seq_send = ctx->seq_recv;
1141 ctx->established = 1;
1144 /* set the return arguments */
1147 if ((code = kg_duplicate_name(context, ctx->there,
1148 KG_INIT_NAME_INTERN, &name))) {
1149 major_status = GSS_S_FAILURE;
1155 *mech_type = (gss_OID) mech_used;
1158 *time_rec = ctx->krb_times.endtime - now;
1161 *ret_flags = ctx->gss_flags;
1163 *context_handle = (gss_ctx_id_t)ctx;
1164 *output_token = token;
1167 *src_name = (gss_name_t) name;
1169 if (delegated_cred_handle) {
1170 if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) {
1171 major_status = GSS_S_FAILURE;
1172 code = G_VALIDATE_FAILED;
1176 *delegated_cred_handle = (gss_cred_id_t) deleg_cred;
1182 major_status = GSS_S_COMPLETE;
1186 krb5_free_authenticator(context, authdat);
1187 /* The ctx structure has the handle of the auth_context */
1188 if (auth_context && !ctx) {
1190 (void)krb5_auth_con_setrcache(context, auth_context, NULL);
1192 krb5_auth_con_free(context, auth_context);
1194 if (reqcksum.contents)
1195 xfree(reqcksum.contents);
1197 krb5_free_data_contents(context, &ap_rep);
1198 if (major_status == GSS_S_COMPLETE ||
1199 (major_status == GSS_S_CONTINUE_NEEDED && code != KRB5KRB_AP_ERR_MSG_TYPE)) {
1200 ctx->k5_context = context;
1205 /* from here on is the real "fail" code */
1208 (void) krb5_gss_delete_sec_context(&tmp_minor_status,
1209 (gss_ctx_id_t *) &ctx, NULL);
1210 if (deleg_cred) { /* free memory associated with the deleg credential */
1211 if (deleg_cred->ccache)
1212 (void)krb5_cc_close(context, deleg_cred->ccache);
1213 if (deleg_cred->name)
1214 kg_release_name(context, 0, &deleg_cred->name);
1220 (void) kg_release_name(context, 0, &name);
1223 *minor_status = code;
1226 * If decode_req_message is set, then we need to decode the ap_req
1227 * message to determine whether or not to send a response token.
1228 * We need to do this because for some errors we won't be able to
1229 * decode the authenticator to read out the gss_flags field.
1231 if (decode_req_message) {
1232 krb5_ap_req * request;
1234 if (decode_krb5_ap_req(&ap_req, &request))
1237 if (request->ap_options & AP_OPTS_MUTUAL_REQUIRED)
1238 gss_flags |= GSS_C_MUTUAL_FLAG;
1239 krb5_free_ap_req(context, request);
1243 && ((gss_flags & GSS_C_MUTUAL_FLAG)
1244 || (major_status == GSS_S_CONTINUE_NEEDED))) {
1245 unsigned int tmsglen;
1249 * The client is expecting a response, so we can send an
1252 memset(&krb_error_data, 0, sizeof(krb_error_data));
1254 code -= ERROR_TABLE_BASE_krb5;
1255 if (code < 0 || code > 128)
1256 code = 60 /* KRB_ERR_GENERIC */;
1258 krb_error_data.error = code;
1259 (void) krb5_us_timeofday(context, &krb_error_data.stime,
1260 &krb_error_data.susec);
1261 krb_error_data.server = cred->name ? cred->name->princ : NULL;
1263 code = krb5_mk_error(context, &krb_error_data, &scratch);
1267 tmsglen = scratch.length;
1268 toktype = KG_TOK_CTX_ERROR;
1270 token.length = g_token_size(mech_used, tmsglen);
1271 token.value = (unsigned char *) xmalloc(token.length);
1276 g_make_token_header(mech_used, tmsglen, &ptr, toktype);
1278 TWRITE_STR(ptr, scratch.data, scratch.length);
1279 krb5_free_data_contents(context, &scratch);
1281 *output_token = token;
1285 if (!verifier_cred_handle && cred_handle) {
1286 krb5_gss_release_cred(&tmp_minor_status, &cred_handle);
1289 if (major_status && *minor_status)
1290 save_error_info(*minor_status, context);
1291 krb5_free_context(context);
1293 return (major_status);
1295 #endif /* LEAN_CLIENT */
1298 krb5_gss_accept_sec_context_ext(
1299 OM_uint32 *minor_status,
1300 gss_ctx_id_t *context_handle,
1301 gss_cred_id_t verifier_cred_handle,
1302 gss_buffer_t input_token,
1303 gss_channel_bindings_t input_chan_bindings,
1304 gss_name_t *src_name,
1306 gss_buffer_t output_token,
1307 OM_uint32 *ret_flags,
1308 OM_uint32 *time_rec,
1309 gss_cred_id_t *delegated_cred_handle,
1310 krb5_gss_ctx_ext_t exts)
1312 krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)*context_handle;
1315 * Context handle must be unspecified. Actually, it must be
1316 * non-established, but currently, accept_sec_context never returns
1317 * a non-established context handle.
1321 if (ctx->established == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) {
1322 return kg_accept_dce(minor_status, context_handle,
1323 verifier_cred_handle, input_token,
1324 input_chan_bindings, src_name, mech_type,
1325 output_token, ret_flags, time_rec,
1326 delegated_cred_handle);
1328 *minor_status = EINVAL;
1329 save_error_string(EINVAL, "accept_sec_context called with existing context handle");
1330 return GSS_S_FAILURE;
1334 return kg_accept_krb5(minor_status, context_handle,
1335 verifier_cred_handle, input_token,
1336 input_chan_bindings, src_name, mech_type,
1337 output_token, ret_flags, time_rec,
1338 delegated_cred_handle, exts);
1342 krb5_gss_accept_sec_context(minor_status, context_handle,
1343 verifier_cred_handle, input_token,
1344 input_chan_bindings, src_name, mech_type,
1345 output_token, ret_flags, time_rec,
1346 delegated_cred_handle)
1347 OM_uint32 *minor_status;
1348 gss_ctx_id_t *context_handle;
1349 gss_cred_id_t verifier_cred_handle;
1350 gss_buffer_t input_token;
1351 gss_channel_bindings_t input_chan_bindings;
1352 gss_name_t *src_name;
1354 gss_buffer_t output_token;
1355 OM_uint32 *ret_flags;
1356 OM_uint32 *time_rec;
1357 gss_cred_id_t *delegated_cred_handle;
1359 krb5_gss_ctx_ext_rec exts;
1361 memset(&exts, 0, sizeof(exts));
1363 return krb5_gss_accept_sec_context_ext(minor_status,
1365 verifier_cred_handle,
1367 input_chan_bindings,
1373 delegated_cred_handle,