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.
26 * Copyright 1993 by OpenVision Technologies, Inc.
28 * Permission to use, copy, modify, distribute, and sell this software
29 * and its documentation for any purpose is hereby granted without fee,
30 * provided that the above copyright notice appears in all copies and
31 * that both that copyright notice and this permission notice appear in
32 * supporting documentation, and that the name of OpenVision not be used
33 * in advertising or publicity pertaining to distribution of the software
34 * without specific, written prior permission. OpenVision makes no
35 * representations about the suitability of this software for any
36 * purpose. It is provided "as is" without express or implied warranty.
38 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
39 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
40 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
41 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
42 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
43 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
44 * PERFORMANCE OF THIS SOFTWARE.
48 * Copyright (C) 1998 by the FundsXpress, INC.
50 * All rights reserved.
52 * Export of this software from the United States of America may require
53 * a specific license from the United States Government. It is the
54 * responsibility of any person or organization contemplating export to
55 * obtain such a license before exporting.
57 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
58 * distribute this software and its documentation for any purpose and
59 * without fee is hereby granted, provided that the above copyright
60 * notice appear in all copies and that both that copyright notice and
61 * this permission notice appear in supporting documentation, and that
62 * the name of FundsXpress. not be used in advertising or publicity pertaining
63 * to distribution of the software without specific, written prior
64 * permission. FundsXpress makes no representations about the suitability of
65 * this software for any purpose. It is provided "as is" without express
66 * or implied warranty.
68 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
69 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
70 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
73 * Copyright (c) 2006-2008, Novell, Inc.
74 * All rights reserved.
76 * Redistribution and use in source and binary forms, with or without
77 * modification, are permitted provided that the following conditions are met:
79 * * Redistributions of source code must retain the above copyright notice,
80 * this list of conditions and the following disclaimer.
81 * * Redistributions in binary form must reproduce the above copyright
82 * notice, this list of conditions and the following disclaimer in the
83 * documentation and/or other materials provided with the distribution.
84 * * The copyright holder's name is not used to endorse or promote products
85 * derived from this software without specific prior written permission.
87 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
88 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
89 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
90 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
91 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
92 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
93 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
94 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
95 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
96 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
97 * POSSIBILITY OF SUCH DAMAGE.
101 #include "gssapiP_krb5.h"
108 #define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
110 #define CFX_ACCEPTOR_SUBKEY 1
116 create_constrained_deleg_creds(OM_uint32 *minor_status,
117 krb5_gss_cred_id_t verifier_cred_handle,
119 krb5_gss_cred_id_t *out_cred,
120 krb5_context context)
122 OM_uint32 major_status;
123 krb5_creds krb_creds;
125 krb5_error_code code;
127 assert(out_cred != NULL);
128 assert(verifier_cred_handle->usage == GSS_C_BOTH);
130 memset(&krb_creds, 0, sizeof(krb_creds));
131 krb_creds.client = ticket->enc_part2->client;
132 krb_creds.server = ticket->server;
133 krb_creds.keyblock = *(ticket->enc_part2->session);
134 krb_creds.ticket_flags = ticket->enc_part2->flags;
135 krb_creds.times = ticket->enc_part2->times;
136 krb_creds.magic = KV5M_CREDS;
137 krb_creds.authdata = NULL;
139 code = encode_krb5_ticket(ticket, &data);
141 *minor_status = code;
142 return GSS_S_FAILURE;
145 krb_creds.ticket = *data;
147 major_status = kg_compose_deleg_cred(minor_status,
148 verifier_cred_handle,
155 krb5_free_data(context, data);
160 /* Decode, decrypt and store the forwarded creds in the local ccache. */
161 static krb5_error_code
162 rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
163 krb5_context context;
164 krb5_auth_context auth_context;
166 krb5_gss_cred_id_t *out_cred;
168 krb5_creds ** creds = NULL;
169 krb5_error_code retval;
170 krb5_ccache ccache = NULL;
171 krb5_gss_cred_id_t cred = NULL;
172 krb5_auth_context new_auth_ctx = NULL;
173 krb5_int32 flags_org;
175 if ((retval = krb5_auth_con_getflags(context, auth_context, &flags_org)))
177 krb5_auth_con_setflags(context, auth_context,
181 * By the time krb5_rd_cred is called here (after krb5_rd_req has been
182 * called in krb5_gss_accept_sec_context), the "keyblock" field of
183 * auth_context contains a pointer to the session key, and the
184 * "recv_subkey" field might contain a session subkey. Either of
185 * these (the "recv_subkey" if it isn't NULL, otherwise the
186 * "keyblock") might have been used to encrypt the encrypted part of
187 * the KRB_CRED message that contains the forwarded credentials. (The
188 * Java Crypto and Security Implementation from the DSTC in Australia
189 * always uses the session key. But apparently it never negotiates a
190 * subkey, so this code works fine against a JCSI client.) Up to the
191 * present, though, GSSAPI clients linked against the MIT code (which
192 * is almost all GSSAPI clients) don't encrypt the KRB_CRED message at
193 * all -- at this level. So if the first call to krb5_rd_cred fails,
194 * we should call it a second time with another auth context freshly
195 * created by krb5_auth_con_init. All of its keyblock fields will be
196 * NULL, so krb5_rd_cred will assume that the KRB_CRED message is
197 * unencrypted. (The MIT code doesn't actually send the KRB_CRED
198 * message in the clear -- the "authenticator" whose "checksum" ends up
199 * containing the KRB_CRED message does get encrypted.)
201 if (krb5_rd_cred(context, auth_context, inbuf, &creds, NULL)) {
202 if ((retval = krb5_auth_con_init(context, &new_auth_ctx)))
204 krb5_auth_con_setflags(context, new_auth_ctx, 0);
205 if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf,
210 if ((retval = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) {
215 if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client)))
218 if ((retval = krb5_cc_store_cred(context, ccache, creds[0])))
221 /* generate a delegated credential handle */
223 /* allocate memory for a cred_t... */
225 (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) {
226 retval = ENOMEM; /* out of memory? */
231 memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
233 retval = k5_mutex_init(&cred->lock);
240 /* copy the client principle into it... */
242 kg_init_name(context, creds[0]->client, NULL, NULL, NULL, 0,
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->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 ap_rep.data = input_token->value;
329 ap_rep.length = input_token->length;
331 code = krb5_rd_rep_dce(ctx->k5_context,
336 major_status = GSS_S_FAILURE;
340 ctx->established = 1;
343 code = kg_duplicate_name(ctx->k5_context, ctx->there, &name);
345 major_status = GSS_S_FAILURE;
348 *src_name = (gss_name_t) name;
352 *mech_type = ctx->mech_used;
355 *time_rec = ts_delta(ctx->krb_times.endtime, now) +
356 ctx->k5_context->clockskew;
359 /* Never return GSS_C_DELEG_FLAG since we don't support DCE credential
362 *ret_flags = (ctx->gss_flags & ~GSS_C_DELEG_FLAG);
366 return GSS_S_COMPLETE;
369 /* real failure code follows */
371 (void) krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx,
373 *context_handle = GSS_C_NO_CONTEXT;
374 *minor_status = code;
379 static krb5_error_code
380 kg_process_extension(krb5_context context,
381 krb5_auth_context auth_context,
384 krb5_gss_ctx_ext_t exts)
386 krb5_error_code code = 0;
388 assert(exts != NULL);
391 case KRB5_GSS_EXTS_IAKERB_FINISHED:
392 if (exts->iakerb.conv == NULL) {
393 code = KRB5KRB_AP_ERR_MSG_TYPE; /* XXX */
397 code = krb5_auth_con_getrecvsubkey_k(context, auth_context, &key);
401 code = iakerb_verify_finished(context, key, exts->iakerb.conv,
404 exts->iakerb.verified = 1;
406 krb5_k_free_key(context, key);
417 kg_accept_krb5(minor_status, context_handle,
418 verifier_cred_handle, input_token,
419 input_chan_bindings, src_name, mech_type,
420 output_token, ret_flags, time_rec,
421 delegated_cred_handle, exts)
422 OM_uint32 *minor_status;
423 gss_ctx_id_t *context_handle;
424 gss_cred_id_t verifier_cred_handle;
425 gss_buffer_t input_token;
426 gss_channel_bindings_t input_chan_bindings;
427 gss_name_t *src_name;
429 gss_buffer_t output_token;
430 OM_uint32 *ret_flags;
432 gss_cred_id_t *delegated_cred_handle;
433 krb5_gss_ctx_ext_t exts;
435 krb5_context context;
436 unsigned char *ptr, *ptr2;
440 krb5_gss_cred_id_t cred = 0;
441 krb5_data ap_rep, ap_req;
443 krb5_error_code code;
444 krb5_address addr, *paddr;
445 krb5_authenticator *authdat = 0;
446 krb5_checksum reqcksum;
447 krb5_gss_name_t name = NULL;
448 krb5_ui_4 gss_flags = 0;
449 krb5_gss_ctx_id_rec *ctx = NULL;
451 gss_buffer_desc token;
452 krb5_auth_context auth_context = NULL;
453 krb5_ticket * ticket = NULL;
456 const gss_OID_desc *mech_used = NULL;
457 OM_uint32 major_status = GSS_S_FAILURE;
458 OM_uint32 tmp_minor_status;
459 krb5_error krb_error_data;
461 gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL;
462 krb5_gss_cred_id_t deleg_cred = NULL;
463 krb5int_access kaccess;
466 int token_deleg_flag = 0;
467 krb5_flags ap_req_options = 0;
468 krb5_enctype negotiated_etype;
469 krb5_authdata_context ad_context = NULL;
470 krb5_principal accprinc = NULL;
471 krb5_ap_req *request = NULL;
473 code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
475 *minor_status = code;
476 return(GSS_S_FAILURE);
479 code = krb5_gss_init_context(&context);
481 *minor_status = code;
482 return GSS_S_FAILURE;
485 /* set up returns to be freeable */
488 *src_name = (gss_name_t) NULL;
489 output_token->length = 0;
490 output_token->value = NULL;
492 reqcksum.contents = 0;
497 *mech_type = GSS_C_NULL_OID;
498 /* return a bogus cred handle */
499 if (delegated_cred_handle)
500 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
502 /* handle default cred handle */
503 if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
504 major_status = krb5_gss_acquire_cred(minor_status, GSS_C_NO_NAME,
505 GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
506 GSS_C_ACCEPT, &defcred,
508 if (major_status != GSS_S_COMPLETE) {
509 code = *minor_status;
512 verifier_cred_handle = defcred;
515 /* Resolve any initiator state in the verifier cred and lock it. */
516 major_status = kg_cred_resolve(minor_status, context, verifier_cred_handle,
518 if (GSS_ERROR(major_status)) {
519 code = *minor_status;
522 cred = (krb5_gss_cred_id_t)verifier_cred_handle;
524 /* make sure the supplied credentials are valid for accept */
526 if ((cred->usage != GSS_C_ACCEPT) &&
527 (cred->usage != GSS_C_BOTH)) {
529 major_status = GSS_S_NO_CRED;
533 /* verify the token's integrity, and leave the token in ap_req.
534 figure out which mech oid was used, and save it */
536 ptr = (unsigned char *) input_token->value;
538 if (!(code = g_verify_token_header(gss_mech_krb5,
540 &ptr, KG_TOK_CTX_AP_REQ,
541 input_token->length, 1))) {
542 mech_used = gss_mech_krb5;
543 } else if ((code == G_WRONG_MECH)
544 &&!(code = g_verify_token_header((gss_OID) gss_mech_iakerb,
546 &ptr, KG_TOK_CTX_AP_REQ,
547 input_token->length, 1))) {
548 mech_used = gss_mech_iakerb;
549 } else if ((code == G_WRONG_MECH)
550 &&!(code = g_verify_token_header((gss_OID) gss_mech_krb5_wrong,
552 &ptr, KG_TOK_CTX_AP_REQ,
553 input_token->length, 1))) {
554 mech_used = gss_mech_krb5_wrong;
555 } else if ((code == G_WRONG_MECH) &&
556 !(code = g_verify_token_header(gss_mech_krb5_old,
558 &ptr, KG_TOK_CTX_AP_REQ,
559 input_token->length, 1))) {
561 * Previous versions of this library used the old mech_id
562 * and some broken behavior (wrong IV on checksum
563 * encryption). We support the old mech_id for
564 * compatibility, and use it to decide when to use the
567 mech_used = gss_mech_krb5_old;
568 } else if (code == G_WRONG_TOKID) {
569 major_status = GSS_S_CONTINUE_NEEDED;
570 code = KRB5KRB_AP_ERR_MSG_TYPE;
571 mech_used = gss_mech_krb5;
573 } else if (code == G_BAD_TOK_HEADER) {
574 /* DCE style not encapsulated */
575 ap_req.length = input_token->length;
576 ap_req.data = input_token->value;
577 mech_used = gss_mech_krb5;
580 major_status = GSS_S_DEFECTIVE_TOKEN;
585 TREAD_STR(sptr, ap_req.data, ap_req.length);
587 /* construct the sender_addr */
589 if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) &&
590 (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) {
591 /* XXX is this right? */
592 addr.addrtype = ADDRTYPE_INET;
593 addr.length = input_chan_bindings->initiator_address.length;
594 addr.contents = input_chan_bindings->initiator_address.value;
601 /* decode the AP_REQ message */
602 code = decode_krb5_ap_req(&ap_req, &request);
604 major_status = GSS_S_FAILURE;
607 ticket = request->ticket;
609 /* decode the message */
611 if ((code = krb5_auth_con_init(context, &auth_context))) {
612 major_status = GSS_S_FAILURE;
613 save_error_info((OM_uint32)code, context);
618 if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) {
619 major_status = GSS_S_FAILURE;
623 if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) {
624 major_status = GSS_S_FAILURE;
628 /* Limit the encryption types negotiated (if requested). */
629 if (cred->req_enctypes) {
630 if ((code = krb5_auth_con_setpermetypes(context, auth_context,
631 cred->req_enctypes))) {
632 major_status = GSS_S_FAILURE;
637 if (!cred->default_identity) {
638 if ((code = kg_acceptor_princ(context, cred->name, &accprinc))) {
639 major_status = GSS_S_FAILURE;
644 code = krb5_rd_req_decoded(context, &auth_context, request, accprinc,
645 cred->keytab, &ap_req_options, NULL);
647 krb5_free_principal(context, accprinc);
649 major_status = GSS_S_FAILURE;
652 krb5_auth_con_setflags(context, auth_context,
653 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
655 krb5_auth_con_getauthenticator(context, auth_context, &authdat);
657 if (authdat->checksum == NULL) {
659 * Some SMB client implementations use handcrafted GSSAPI code that
660 * does not provide a checksum. MS-KILE documents that the Microsoft
661 * implementation considers a missing checksum acceptable; the server
662 * assumes all flags are unset in this case, and does not check channel
666 } else if (authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) {
667 /* Samba does not send 0x8003 GSS-API checksums */
672 code = krb5_auth_con_getkey_k(context, auth_context, &subkey);
674 major_status = GSS_S_FAILURE;
681 code = krb5_k_verify_checksum(context,
683 KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
687 krb5_k_free_key(context, subkey);
688 if (code || !valid) {
689 major_status = GSS_S_BAD_SIG;
693 /* Use ap_options from the request to guess the mutual flag. */
694 gss_flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
695 if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED)
696 gss_flags |= GSS_C_MUTUAL_FLAG;
700 /* stash this now, for later. */
701 code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len);
703 major_status = GSS_S_FAILURE;
707 /* verify that the checksum is correct */
710 The checksum may be either exactly 24 bytes, in which case
711 no options are specified, or greater than 24 bytes, in which case
712 one or more options are specified. Currently, the only valid
713 option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
716 if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
717 (authdat->checksum->length < 24)) {
719 major_status = GSS_S_BAD_BINDINGS;
723 ptr = (unsigned char *) authdat->checksum->contents;
725 TREAD_INT(ptr, tmp, 0);
728 code = KG_BAD_LENGTH;
729 major_status = GSS_S_FAILURE;
734 The following section of code attempts to implement the
735 optional channel binding facility as described in RFC2743.
737 Since this facility is optional channel binding may or may
738 not have been provided by either the client or the server.
740 If the server has specified input_chan_bindings equal to
741 GSS_C_NO_CHANNEL_BINDINGS then we skip the check. If
742 the server does provide channel bindings then we compute
743 a checksum and compare against those provided by the
746 if ((code = kg_checksum_channel_bindings(context,
749 major_status = GSS_S_BAD_BINDINGS;
753 /* Always read the clients bindings - eventhough we might ignore them */
754 TREAD_STR(ptr, ptr2, reqcksum.length);
756 if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) {
757 if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
758 xfree(reqcksum.contents);
759 reqcksum.contents = 0;
761 major_status = GSS_S_BAD_BINDINGS;
767 xfree(reqcksum.contents);
768 reqcksum.contents = 0;
770 /* Read the token flags. Remember if GSS_C_DELEG_FLAG was set, but
771 * mask it out until we actually read a delegated credential. */
772 TREAD_INT(ptr, gss_flags, 0);
773 token_deleg_flag = (gss_flags & GSS_C_DELEG_FLAG);
774 gss_flags &= ~GSS_C_DELEG_FLAG;
776 /* if the checksum length > 24, there are options to process */
778 i = authdat->checksum->length - 24;
779 if (i && token_deleg_flag) {
781 TREAD_INT16(ptr, option_id, 0);
782 TREAD_INT16(ptr, option.length, 0);
785 if (i < option.length) {
786 code = KG_BAD_LENGTH;
787 major_status = GSS_S_FAILURE;
791 /* have to use ptr2, since option.data is wrong type and
792 macro uses ptr as both lvalue and rvalue */
794 TREAD_STR(ptr, ptr2, option.length);
795 option.data = (char *) ptr2;
799 if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
800 major_status = GSS_S_FAILURE;
804 /* store the delegated credential */
806 code = rd_and_store_for_creds(context, auth_context, &option,
807 (delegated_cred_handle) ?
810 major_status = GSS_S_FAILURE;
814 gss_flags |= GSS_C_DELEG_FLAG;
816 /* ignore any additional trailing data, for now */
819 /* Process Type-Length-Data options */
821 code = KG_BAD_LENGTH;
822 major_status = GSS_S_FAILURE;
825 TREAD_INT(ptr, option_id, 1);
826 TREAD_INT(ptr, option.length, 1);
828 if (i < option.length) {
829 code = KG_BAD_LENGTH;
830 major_status = GSS_S_FAILURE;
833 TREAD_STR(ptr, ptr2, option.length);
834 option.data = (char *)ptr2;
838 code = kg_process_extension(context, auth_context,
839 option_id, &option, exts);
841 major_status = GSS_S_FAILURE;
847 if (exts->iakerb.conv && !exts->iakerb.verified) {
848 major_status = GSS_S_BAD_SIG;
852 /* only DCE_STYLE clients are allowed to send raw AP-REQs */
853 if (no_encap != ((gss_flags & GSS_C_DCE_STYLE) != 0)) {
854 major_status = GSS_S_DEFECTIVE_TOKEN;
858 /* create the ctx struct and start filling it in */
860 if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
863 major_status = GSS_S_FAILURE;
867 memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
868 ctx->magic = KG_CONTEXT;
869 ctx->mech_used = (gss_OID) mech_used;
870 ctx->auth_context = auth_context;
872 ctx->gss_flags = (GSS_C_TRANS_FLAG |
873 ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
874 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
875 GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG |
876 GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG |
877 GSS_C_EXTENDED_ERROR_FLAG)));
879 ctx->cred_rcache = cred_rcache;
881 /* XXX move this into gss_name_t */
882 if ( (code = krb5_merge_authdata(context,
883 ticket->enc_part2->authorization_data,
884 authdat->authorization_data,
886 major_status = GSS_S_FAILURE;
889 if ((code = kg_init_name(context, ticket->server, NULL, NULL, NULL, 0,
891 major_status = GSS_S_FAILURE;
894 if ((code = krb5_auth_con_get_authdata_context(context, auth_context,
896 major_status = GSS_S_FAILURE;
899 if ((code = kg_init_name(context, authdat->client, NULL, NULL,
900 ad_context, KG_INIT_NAME_NO_COPY, &ctx->there))) {
901 major_status = GSS_S_FAILURE;
904 /* Now owned by ctx->there */
905 authdat->client = NULL;
906 krb5_auth_con_set_authdata_context(context, auth_context, NULL);
908 if ((code = krb5_auth_con_getrecvsubkey_k(context, auth_context,
910 major_status = GSS_S_FAILURE;
914 /* use the session key if the subkey isn't present */
916 if (ctx->subkey == NULL) {
917 if ((code = krb5_auth_con_getkey_k(context, auth_context,
919 major_status = GSS_S_FAILURE;
924 if (ctx->subkey == NULL) {
925 /* this isn't a very good error, but it's not clear to me this
926 can actually happen */
927 major_status = GSS_S_FAILURE;
928 code = KRB5KDC_ERR_NULL_KEY;
934 ctx->have_acceptor_subkey = 0;
935 /* DCE_STYLE implies acceptor_subkey */
936 if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0) {
937 code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype);
939 major_status = GSS_S_FAILURE;
943 ctx->krb_times = ticket->enc_part2->times; /* struct copy */
944 ctx->krb_flags = ticket->enc_part2->flags;
946 if (delegated_cred_handle != NULL &&
947 deleg_cred == NULL && /* no unconstrained delegation */
948 cred->usage == GSS_C_BOTH &&
949 (ticket->enc_part2->flags & TKT_FLG_FORWARDABLE)) {
951 * Now, we always fabricate a delegated credentials handle
952 * containing the service ticket to ourselves, which can be
953 * used for S4U2Proxy.
955 major_status = create_constrained_deleg_creds(minor_status, cred,
958 if (GSS_ERROR(major_status))
960 ctx->gss_flags |= GSS_C_DELEG_FLAG;
965 krb5_auth_con_getremoteseqnumber(context, auth_context, &seq_temp);
966 ctx->seq_recv = seq_temp;
969 if ((code = krb5_timeofday(context, &now))) {
970 major_status = GSS_S_FAILURE;
974 code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv,
975 (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
976 (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0,
979 major_status = GSS_S_FAILURE;
983 /* DCE_STYLE implies mutual authentication */
984 if (ctx->gss_flags & GSS_C_DCE_STYLE)
985 ctx->gss_flags |= GSS_C_MUTUAL_FLAG;
987 /* at this point, the entire context structure is filled in,
988 so it can be released. */
990 /* generate an AP_REP if necessary */
992 if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
993 unsigned char * ptr3;
995 int cfx_generate_subkey;
998 * Do not generate a subkey per RFC 4537 unless we are upgrading to CFX,
999 * because pre-CFX tokens do not indicate which key to use. (Note that
1000 * DCE_STYLE implies that we will use a subkey.)
1002 if (ctx->proto == 0 &&
1003 (ctx->gss_flags & GSS_C_DCE_STYLE) == 0 &&
1004 (ap_req_options & AP_OPTS_USE_SUBKEY)) {
1005 code = (*kaccess.auth_con_get_subkey_enctype)(context,
1009 major_status = GSS_S_FAILURE;
1013 switch (negotiated_etype) {
1014 case ENCTYPE_DES_CBC_MD5:
1015 case ENCTYPE_DES_CBC_MD4:
1016 case ENCTYPE_DES_CBC_CRC:
1017 case ENCTYPE_DES3_CBC_SHA1:
1018 case ENCTYPE_ARCFOUR_HMAC:
1019 case ENCTYPE_ARCFOUR_HMAC_EXP:
1020 /* RFC 4121 accidentally omits RC4-HMAC-EXP as a "not-newer"
1021 * enctype, even though RFC 4757 treats it as one. */
1022 ap_req_options &= ~(AP_OPTS_USE_SUBKEY);
1027 if (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) ||
1028 (ap_req_options & AP_OPTS_USE_SUBKEY))
1029 cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY;
1031 cfx_generate_subkey = 0;
1033 if (cfx_generate_subkey) {
1035 code = krb5_auth_con_getflags(context, auth_context, &acflags);
1037 acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY;
1038 code = krb5_auth_con_setflags(context, auth_context, acflags);
1041 major_status = GSS_S_FAILURE;
1046 if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) {
1047 major_status = GSS_S_FAILURE;
1051 krb5_auth_con_getlocalseqnumber(context, auth_context, &seq_temp);
1052 ctx->seq_send = seq_temp & 0xffffffffL;
1054 if (cfx_generate_subkey) {
1055 /* Get the new acceptor subkey. With the code above, there
1056 should always be one if we make it to this point. */
1057 code = krb5_auth_con_getsendsubkey_k(context, auth_context,
1058 &ctx->acceptor_subkey);
1060 major_status = GSS_S_FAILURE;
1063 ctx->have_acceptor_subkey = 1;
1065 code = kg_setup_keys(context, ctx, ctx->acceptor_subkey,
1066 &ctx->acceptor_subkey_cksumtype);
1068 major_status = GSS_S_FAILURE;
1073 /* the reply token hasn't been sent yet, but that's ok. */
1074 if (ctx->gss_flags & GSS_C_DCE_STYLE) {
1075 assert(ctx->have_acceptor_subkey);
1077 /* in order to force acceptor subkey to be used, don't set PROT_READY */
1079 /* Raw AP-REP is returned */
1080 code = data_to_gss(&ap_rep, output_token);
1083 major_status = GSS_S_FAILURE;
1087 ctx->established = 0;
1089 *context_handle = (gss_ctx_id_t)ctx;
1091 major_status = GSS_S_CONTINUE_NEEDED;
1093 /* Only last leg should set return arguments */
1096 ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
1098 ctx->established = 1;
1100 token.length = g_token_size(mech_used, ap_rep.length);
1102 if ((token.value = (unsigned char *) gssalloc_malloc(token.length))
1104 major_status = GSS_S_FAILURE;
1109 g_make_token_header(mech_used, ap_rep.length,
1110 &ptr3, KG_TOK_CTX_AP_REP);
1112 TWRITE_STR(ptr3, ap_rep.data, ap_rep.length);
1114 ctx->established = 1;
1119 ctx->seq_send = ctx->seq_recv;
1121 ctx->established = 1;
1124 /* set the return arguments */
1127 code = kg_duplicate_name(context, ctx->there, &name);
1129 major_status = GSS_S_FAILURE;
1135 *mech_type = (gss_OID) mech_used;
1137 /* Add the maximum allowable clock skew as a grace period for context
1138 * expiration, just as we do for the ticket. */
1140 *time_rec = ts_delta(ctx->krb_times.endtime, now) + context->clockskew;
1143 *ret_flags = ctx->gss_flags;
1145 *context_handle = (gss_ctx_id_t)ctx;
1146 *output_token = token;
1149 *src_name = (gss_name_t) name;
1151 if (delegated_cred_handle)
1152 *delegated_cred_handle = (gss_cred_id_t) deleg_cred;
1157 major_status = GSS_S_COMPLETE;
1161 krb5_free_authenticator(context, authdat);
1162 /* The ctx structure has the handle of the auth_context */
1163 if (auth_context && !ctx) {
1165 (void)krb5_auth_con_setrcache(context, auth_context, NULL);
1167 krb5_auth_con_free(context, auth_context);
1169 if (reqcksum.contents)
1170 xfree(reqcksum.contents);
1172 krb5_free_data_contents(context, &ap_rep);
1173 if (major_status == GSS_S_COMPLETE ||
1174 (major_status == GSS_S_CONTINUE_NEEDED && code != KRB5KRB_AP_ERR_MSG_TYPE)) {
1175 ctx->k5_context = context;
1180 /* from here on is the real "fail" code */
1183 (void) krb5_gss_delete_sec_context(&tmp_minor_status,
1184 (gss_ctx_id_t *) &ctx, NULL);
1185 if (deleg_cred) { /* free memory associated with the deleg credential */
1186 if (deleg_cred->ccache)
1187 (void)krb5_cc_close(context, deleg_cred->ccache);
1188 if (deleg_cred->name)
1189 kg_release_name(context, &deleg_cred->name);
1195 (void) kg_release_name(context, &name);
1198 *minor_status = code;
1200 /* We may have failed before being able to read the GSS flags from the
1201 * authenticator, so also check the request AP options. */
1202 if (cred != NULL && request != NULL &&
1203 ((gss_flags & GSS_C_MUTUAL_FLAG) ||
1204 (request->ap_options & AP_OPTS_MUTUAL_REQUIRED) ||
1205 major_status == GSS_S_CONTINUE_NEEDED)) {
1206 unsigned int tmsglen;
1210 * The client is expecting a response, so we can send an
1213 memset(&krb_error_data, 0, sizeof(krb_error_data));
1215 code -= ERROR_TABLE_BASE_krb5;
1216 if (code < 0 || code > KRB_ERR_MAX)
1217 code = 60 /* KRB_ERR_GENERIC */;
1219 krb_error_data.error = code;
1220 (void) krb5_us_timeofday(context, &krb_error_data.stime,
1221 &krb_error_data.susec);
1223 krb_error_data.server = ticket->server;
1224 code = krb5_mk_error(context, &krb_error_data, &scratch);
1228 tmsglen = scratch.length;
1229 toktype = KG_TOK_CTX_ERROR;
1231 token.length = g_token_size(mech_used, tmsglen);
1232 token.value = gssalloc_malloc(token.length);
1237 g_make_token_header(mech_used, tmsglen, &ptr, toktype);
1239 TWRITE_STR(ptr, scratch.data, scratch.length);
1240 krb5_free_data_contents(context, &scratch);
1242 *output_token = token;
1246 krb5_free_ap_req(context, request);
1248 k5_mutex_unlock(&cred->lock);
1250 krb5_gss_release_cred(&tmp_minor_status, &defcred);
1252 if (major_status && *minor_status)
1253 save_error_info(*minor_status, context);
1254 krb5_free_context(context);
1256 return (major_status);
1258 #endif /* LEAN_CLIENT */
1260 OM_uint32 KRB5_CALLCONV
1261 krb5_gss_accept_sec_context_ext(
1262 OM_uint32 *minor_status,
1263 gss_ctx_id_t *context_handle,
1264 gss_cred_id_t verifier_cred_handle,
1265 gss_buffer_t input_token,
1266 gss_channel_bindings_t input_chan_bindings,
1267 gss_name_t *src_name,
1269 gss_buffer_t output_token,
1270 OM_uint32 *ret_flags,
1271 OM_uint32 *time_rec,
1272 gss_cred_id_t *delegated_cred_handle,
1273 krb5_gss_ctx_ext_t exts)
1275 krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)*context_handle;
1278 * Context handle must be unspecified. Actually, it must be
1279 * non-established, but currently, accept_sec_context never returns
1280 * a non-established context handle.
1284 if (ctx->established == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) {
1285 return kg_accept_dce(minor_status, context_handle,
1286 verifier_cred_handle, input_token,
1287 input_chan_bindings, src_name, mech_type,
1288 output_token, ret_flags, time_rec,
1289 delegated_cred_handle);
1291 *minor_status = EINVAL;
1292 save_error_string(EINVAL, "accept_sec_context called with existing context handle");
1293 return GSS_S_FAILURE;
1297 return kg_accept_krb5(minor_status, context_handle,
1298 verifier_cred_handle, input_token,
1299 input_chan_bindings, src_name, mech_type,
1300 output_token, ret_flags, time_rec,
1301 delegated_cred_handle, exts);
1304 OM_uint32 KRB5_CALLCONV
1305 krb5_gss_accept_sec_context(minor_status, context_handle,
1306 verifier_cred_handle, input_token,
1307 input_chan_bindings, src_name, mech_type,
1308 output_token, ret_flags, time_rec,
1309 delegated_cred_handle)
1310 OM_uint32 *minor_status;
1311 gss_ctx_id_t *context_handle;
1312 gss_cred_id_t verifier_cred_handle;
1313 gss_buffer_t input_token;
1314 gss_channel_bindings_t input_chan_bindings;
1315 gss_name_t *src_name;
1317 gss_buffer_t output_token;
1318 OM_uint32 *ret_flags;
1319 OM_uint32 *time_rec;
1320 gss_cred_id_t *delegated_cred_handle;
1322 krb5_gss_ctx_ext_rec exts;
1324 memset(&exts, 0, sizeof(exts));
1326 return krb5_gss_accept_sec_context_ext(minor_status,
1328 verifier_cred_handle,
1330 input_chan_bindings,
1336 delegated_cred_handle,