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->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 code = kg_duplicate_name(ctx->k5_context, ctx->there, &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;
475 krb5_principal accprinc = NULL;
477 code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
479 *minor_status = code;
480 return(GSS_S_FAILURE);
483 code = krb5_gss_init_context(&context);
485 *minor_status = code;
486 return GSS_S_FAILURE;
489 /* set up returns to be freeable */
492 *src_name = (gss_name_t) NULL;
493 output_token->length = 0;
494 output_token->value = NULL;
496 reqcksum.contents = 0;
501 *mech_type = GSS_C_NULL_OID;
502 /* return a bogus cred handle */
503 if (delegated_cred_handle)
504 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
506 /* handle default cred handle */
507 if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
508 major_status = krb5_gss_acquire_cred(minor_status, GSS_C_NO_NAME,
509 GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
510 GSS_C_ACCEPT, &cred_handle,
512 if (major_status != GSS_S_COMPLETE) {
513 code = *minor_status;
517 major_status = krb5_gss_validate_cred(minor_status,
518 verifier_cred_handle);
519 if (GSS_ERROR(major_status)) {
520 code = *minor_status;
523 cred_handle = verifier_cred_handle;
526 cred = (krb5_gss_cred_id_t) cred_handle;
528 /* make sure the supplied credentials are valid for accept */
530 if ((cred->usage != GSS_C_ACCEPT) &&
531 (cred->usage != GSS_C_BOTH)) {
533 major_status = GSS_S_NO_CRED;
537 /* verify the token's integrity, and leave the token in ap_req.
538 figure out which mech oid was used, and save it */
540 ptr = (unsigned char *) input_token->value;
542 if (!(code = g_verify_token_header(gss_mech_krb5,
544 &ptr, KG_TOK_CTX_AP_REQ,
545 input_token->length, 1))) {
546 mech_used = gss_mech_krb5;
547 } else if ((code == G_WRONG_MECH)
548 &&!(code = g_verify_token_header((gss_OID) gss_mech_iakerb,
550 &ptr, KG_TOK_CTX_AP_REQ,
551 input_token->length, 1))) {
552 mech_used = gss_mech_iakerb;
553 } else if ((code == G_WRONG_MECH)
554 &&!(code = g_verify_token_header((gss_OID) gss_mech_krb5_wrong,
556 &ptr, KG_TOK_CTX_AP_REQ,
557 input_token->length, 1))) {
558 mech_used = gss_mech_krb5_wrong;
559 } else if ((code == G_WRONG_MECH) &&
560 !(code = g_verify_token_header(gss_mech_krb5_old,
562 &ptr, KG_TOK_CTX_AP_REQ,
563 input_token->length, 1))) {
565 * Previous versions of this library used the old mech_id
566 * and some broken behavior (wrong IV on checksum
567 * encryption). We support the old mech_id for
568 * compatibility, and use it to decide when to use the
571 mech_used = gss_mech_krb5_old;
572 } else if (code == G_WRONG_TOKID) {
573 major_status = GSS_S_CONTINUE_NEEDED;
574 code = KRB5KRB_AP_ERR_MSG_TYPE;
575 mech_used = gss_mech_krb5;
577 } else if (code == G_BAD_TOK_HEADER) {
578 /* DCE style not encapsulated */
579 ap_req.length = input_token->length;
580 ap_req.data = input_token->value;
581 mech_used = gss_mech_krb5;
584 major_status = GSS_S_DEFECTIVE_TOKEN;
589 TREAD_STR(sptr, ap_req.data, ap_req.length);
590 decode_req_message = 1;
592 /* construct the sender_addr */
594 if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) &&
595 (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) {
596 /* XXX is this right? */
597 addr.addrtype = ADDRTYPE_INET;
598 addr.length = input_chan_bindings->initiator_address.length;
599 addr.contents = input_chan_bindings->initiator_address.value;
606 /* decode the AP_REQ message */
608 /* decode the message */
610 if ((code = krb5_auth_con_init(context, &auth_context))) {
611 major_status = GSS_S_FAILURE;
612 save_error_info((OM_uint32)code, context);
617 if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) {
618 major_status = GSS_S_FAILURE;
622 if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) {
623 major_status = GSS_S_FAILURE;
627 /* Limit the encryption types negotiated (if requested). */
628 if (cred->req_enctypes) {
629 if ((code = krb5_set_default_tgs_enctypes(context,
630 cred->req_enctypes))) {
631 major_status = GSS_S_FAILURE;
636 if (!cred->default_identity) {
637 if ((code = kg_acceptor_princ(context, cred->name, &accprinc))) {
638 major_status = GSS_S_FAILURE;
643 code = krb5_rd_req(context, &auth_context, &ap_req, accprinc,
644 cred->keytab, &ap_req_options, &ticket);
645 krb5_free_principal(context, accprinc);
647 major_status = GSS_S_FAILURE;
650 krb5_auth_con_setflags(context, auth_context,
651 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
653 krb5_auth_con_getauthenticator(context, auth_context, &authdat);
656 /* make sure the necessary parts of the authdat are present */
658 if ((authdat->authenticator->subkey == NULL) ||
659 (authdat->ticket->enc_part2 == NULL)) {
661 major_status = GSS_S_FAILURE;
666 if (authdat->checksum == NULL) {
667 /* missing checksum counts as "inappropriate type" */
668 code = KRB5KRB_AP_ERR_INAPP_CKSUM;
669 major_status = GSS_S_FAILURE;
673 if (authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) {
674 /* Samba does not send 0x8003 GSS-API checksums */
679 code = krb5_auth_con_getkey_k(context, auth_context, &subkey);
681 major_status = GSS_S_FAILURE;
688 code = krb5_k_verify_checksum(context,
690 KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
694 krb5_k_free_key(context, subkey);
695 if (code || !valid) {
696 major_status = GSS_S_BAD_SIG;
700 gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
702 decode_req_message = 0;
706 /* stash this now, for later. */
707 code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len);
709 major_status = GSS_S_FAILURE;
713 /* verify that the checksum is correct */
716 The checksum may be either exactly 24 bytes, in which case
717 no options are specified, or greater than 24 bytes, in which case
718 one or more options are specified. Currently, the only valid
719 option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
722 if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
723 (authdat->checksum->length < 24)) {
725 major_status = GSS_S_BAD_BINDINGS;
730 "Be liberal in what you accept, and
731 conservative in what you send"
734 This code will let this acceptor interoperate with an initiator
735 using little-endian or big-endian integer encoding.
738 ptr = (unsigned char *) authdat->checksum->contents;
741 TREAD_INT(ptr, tmp, bigend);
744 ptr = (unsigned char *) authdat->checksum->contents;
747 TREAD_INT(ptr, tmp, bigend);
750 code = KG_BAD_LENGTH;
751 major_status = GSS_S_FAILURE;
756 /* at this point, bigend is set according to the initiator's
761 The following section of code attempts to implement the
762 optional channel binding facility as described in RFC2743.
764 Since this facility is optional channel binding may or may
765 not have been provided by either the client or the server.
767 If the server has specified input_chan_bindings equal to
768 GSS_C_NO_CHANNEL_BINDINGS then we skip the check. If
769 the server does provide channel bindings then we compute
770 a checksum and compare against those provided by the
773 if ((code = kg_checksum_channel_bindings(context,
775 &reqcksum, bigend))) {
776 major_status = GSS_S_BAD_BINDINGS;
780 /* Always read the clients bindings - eventhough we might ignore them */
781 TREAD_STR(ptr, ptr2, reqcksum.length);
783 if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) {
784 if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
785 xfree(reqcksum.contents);
786 reqcksum.contents = 0;
788 major_status = GSS_S_BAD_BINDINGS;
794 xfree(reqcksum.contents);
795 reqcksum.contents = 0;
797 TREAD_INT(ptr, gss_flags, bigend);
799 gss_flags &= ~GSS_C_DELEG_FLAG; /* mask out the delegation flag; if
800 there's a delegation, we'll set
803 decode_req_message = 0;
805 /* if the checksum length > 24, there are options to process */
807 i = authdat->checksum->length - 24;
808 if (i && (gss_flags & GSS_C_DELEG_FLAG)) {
810 TREAD_INT16(ptr, option_id, bigend);
811 TREAD_INT16(ptr, option.length, bigend);
814 if (i < option.length || option.length < 0) {
815 code = KG_BAD_LENGTH;
816 major_status = GSS_S_FAILURE;
820 /* have to use ptr2, since option.data is wrong type and
821 macro uses ptr as both lvalue and rvalue */
823 TREAD_STR(ptr, ptr2, option.length);
824 option.data = (char *) ptr2;
828 if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
829 major_status = GSS_S_FAILURE;
833 /* store the delegated credential */
835 code = rd_and_store_for_creds(context, auth_context, &option,
836 (delegated_cred_handle) ?
839 major_status = GSS_S_FAILURE;
844 /* ignore any additional trailing data, for now */
847 /* Process Type-Length-Data options */
849 code = KG_BAD_LENGTH;
850 major_status = GSS_S_FAILURE;
853 TREAD_INT(ptr, option_id, 1);
854 TREAD_INT(ptr, option.length, 1);
856 if (i < option.length) {
857 code = KG_BAD_LENGTH;
858 major_status = GSS_S_FAILURE;
861 TREAD_STR(ptr, ptr2, option.length);
862 option.data = (char *)ptr2;
866 code = kg_process_extension(context, auth_context,
867 option_id, &option, exts);
869 major_status = GSS_S_FAILURE;
875 if (exts->iakerb.conv && !exts->iakerb.verified) {
876 major_status = GSS_S_BAD_SIG;
880 /* only DCE_STYLE clients are allowed to send raw AP-REQs */
881 if (no_encap != ((gss_flags & GSS_C_DCE_STYLE) != 0)) {
882 major_status = GSS_S_DEFECTIVE_TOKEN;
886 /* create the ctx struct and start filling it in */
888 if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
891 major_status = GSS_S_FAILURE;
895 memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
896 ctx->magic = KG_CONTEXT;
897 ctx->mech_used = (gss_OID) mech_used;
898 ctx->auth_context = auth_context;
900 ctx->gss_flags = (GSS_C_TRANS_FLAG |
901 ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
902 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
903 GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG |
904 GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG |
905 GSS_C_EXTENDED_ERROR_FLAG)));
907 ctx->big_endian = bigend;
908 ctx->cred_rcache = cred_rcache;
910 /* XXX move this into gss_name_t */
911 if ( (code = krb5_merge_authdata(context,
912 ticket->enc_part2->authorization_data,
913 authdat->authorization_data,
915 major_status = GSS_S_FAILURE;
918 if ((code = kg_init_name(context, ticket->server, NULL, NULL, NULL, 0,
920 major_status = GSS_S_FAILURE;
923 if ((code = krb5_auth_con_get_authdata_context(context, auth_context,
925 major_status = GSS_S_FAILURE;
928 if ((code = kg_init_name(context, authdat->client, NULL, NULL,
929 ad_context, KG_INIT_NAME_NO_COPY, &ctx->there))) {
930 major_status = GSS_S_FAILURE;
933 /* Now owned by ctx->there */
934 authdat->client = NULL;
935 krb5_auth_con_set_authdata_context(context, auth_context, NULL);
937 if ((code = krb5_auth_con_getrecvsubkey_k(context, auth_context,
939 major_status = GSS_S_FAILURE;
943 /* use the session key if the subkey isn't present */
945 if (ctx->subkey == NULL) {
946 if ((code = krb5_auth_con_getkey_k(context, auth_context,
948 major_status = GSS_S_FAILURE;
953 if (ctx->subkey == NULL) {
954 /* this isn't a very good error, but it's not clear to me this
955 can actually happen */
956 major_status = GSS_S_FAILURE;
957 code = KRB5KDC_ERR_NULL_KEY;
963 ctx->have_acceptor_subkey = 0;
964 /* DCE_STYLE implies acceptor_subkey */
965 if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0) {
966 code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype);
968 major_status = GSS_S_FAILURE;
972 ctx->krb_times = ticket->enc_part2->times; /* struct copy */
973 ctx->krb_flags = ticket->enc_part2->flags;
975 if (delegated_cred_handle != NULL &&
976 deleg_cred == NULL && /* no unconstrained delegation */
977 cred->usage == GSS_C_BOTH &&
978 (ticket->enc_part2->flags & TKT_FLG_FORWARDABLE)) {
980 * Now, we always fabricate a delegated credentials handle
981 * containing the service ticket to ourselves, which can be
982 * used for S4U2Proxy.
984 major_status = create_constrained_deleg_creds(minor_status, cred,
987 if (GSS_ERROR(major_status))
989 ctx->gss_flags |= GSS_C_DELEG_FLAG;
992 krb5_free_ticket(context, ticket); /* Done with ticket */
996 krb5_auth_con_getremoteseqnumber(context, auth_context, &seq_temp);
997 ctx->seq_recv = seq_temp;
1000 if ((code = krb5_timeofday(context, &now))) {
1001 major_status = GSS_S_FAILURE;
1005 if (ctx->krb_times.endtime < now) {
1007 major_status = GSS_S_CREDENTIALS_EXPIRED;
1011 g_order_init(&(ctx->seqstate), ctx->seq_recv,
1012 (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
1013 (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
1015 /* DCE_STYLE implies mutual authentication */
1016 if (ctx->gss_flags & GSS_C_DCE_STYLE)
1017 ctx->gss_flags |= GSS_C_MUTUAL_FLAG;
1019 /* at this point, the entire context structure is filled in,
1020 so it can be released. */
1022 /* generate an AP_REP if necessary */
1024 if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
1025 unsigned char * ptr3;
1026 krb5_int32 seq_temp;
1027 int cfx_generate_subkey;
1030 * Do not generate a subkey per RFC 4537 unless we are upgrading to CFX,
1031 * because pre-CFX tokens do not indicate which key to use. (Note that
1032 * DCE_STYLE implies that we will use a subkey.)
1034 if (ctx->proto == 0 &&
1035 (ctx->gss_flags & GSS_C_DCE_STYLE) == 0 &&
1036 (ap_req_options & AP_OPTS_USE_SUBKEY)) {
1037 code = (*kaccess.auth_con_get_subkey_enctype)(context,
1041 major_status = GSS_S_FAILURE;
1045 switch (negotiated_etype) {
1046 case ENCTYPE_DES_CBC_MD5:
1047 case ENCTYPE_DES_CBC_MD4:
1048 case ENCTYPE_DES_CBC_CRC:
1049 case ENCTYPE_DES3_CBC_SHA1:
1050 case ENCTYPE_ARCFOUR_HMAC:
1051 case ENCTYPE_ARCFOUR_HMAC_EXP:
1052 /* RFC 4121 accidentally omits RC4-HMAC-EXP as a "not-newer"
1053 * enctype, even though RFC 4757 treats it as one. */
1054 ap_req_options &= ~(AP_OPTS_USE_SUBKEY);
1059 if (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) ||
1060 (ap_req_options & AP_OPTS_USE_SUBKEY))
1061 cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY;
1063 cfx_generate_subkey = 0;
1065 if (cfx_generate_subkey) {
1067 code = krb5_auth_con_getflags(context, auth_context, &acflags);
1069 acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY;
1070 code = krb5_auth_con_setflags(context, auth_context, acflags);
1073 major_status = GSS_S_FAILURE;
1078 if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) {
1079 major_status = GSS_S_FAILURE;
1083 krb5_auth_con_getlocalseqnumber(context, auth_context, &seq_temp);
1084 ctx->seq_send = seq_temp & 0xffffffffL;
1086 if (cfx_generate_subkey) {
1087 /* Get the new acceptor subkey. With the code above, there
1088 should always be one if we make it to this point. */
1089 code = krb5_auth_con_getsendsubkey_k(context, auth_context,
1090 &ctx->acceptor_subkey);
1092 major_status = GSS_S_FAILURE;
1095 ctx->have_acceptor_subkey = 1;
1097 code = kg_setup_keys(context, ctx, ctx->acceptor_subkey,
1098 &ctx->acceptor_subkey_cksumtype);
1100 major_status = GSS_S_FAILURE;
1105 /* the reply token hasn't been sent yet, but that's ok. */
1106 if (ctx->gss_flags & GSS_C_DCE_STYLE) {
1107 assert(ctx->have_acceptor_subkey);
1109 /* in order to force acceptor subkey to be used, don't set PROT_READY */
1111 /* Raw AP-REP is returned */
1112 output_token->length = ap_rep.length;
1113 output_token->value = ap_rep.data;
1114 ap_rep.data = NULL; /* don't double free */
1116 ctx->established = 0;
1118 *context_handle = (gss_ctx_id_t)ctx;
1120 major_status = GSS_S_CONTINUE_NEEDED;
1122 /* Only last leg should set return arguments */
1125 ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
1127 ctx->established = 1;
1129 token.length = g_token_size(mech_used, ap_rep.length);
1131 if ((token.value = (unsigned char *) xmalloc(token.length))
1133 major_status = GSS_S_FAILURE;
1138 g_make_token_header(mech_used, ap_rep.length,
1139 &ptr3, KG_TOK_CTX_AP_REP);
1141 TWRITE_STR(ptr3, ap_rep.data, ap_rep.length);
1143 ctx->established = 1;
1148 ctx->seq_send = ctx->seq_recv;
1150 ctx->established = 1;
1153 /* set the return arguments */
1156 code = kg_duplicate_name(context, ctx->there, &name);
1158 major_status = GSS_S_FAILURE;
1164 *mech_type = (gss_OID) mech_used;
1167 *time_rec = ctx->krb_times.endtime - now;
1170 *ret_flags = ctx->gss_flags;
1172 *context_handle = (gss_ctx_id_t)ctx;
1173 *output_token = token;
1176 *src_name = (gss_name_t) name;
1178 if (delegated_cred_handle)
1179 *delegated_cred_handle = (gss_cred_id_t) deleg_cred;
1184 major_status = GSS_S_COMPLETE;
1188 krb5_free_authenticator(context, authdat);
1189 /* The ctx structure has the handle of the auth_context */
1190 if (auth_context && !ctx) {
1192 (void)krb5_auth_con_setrcache(context, auth_context, NULL);
1194 krb5_auth_con_free(context, auth_context);
1196 if (reqcksum.contents)
1197 xfree(reqcksum.contents);
1199 krb5_free_data_contents(context, &ap_rep);
1200 if (major_status == GSS_S_COMPLETE ||
1201 (major_status == GSS_S_CONTINUE_NEEDED && code != KRB5KRB_AP_ERR_MSG_TYPE)) {
1202 ctx->k5_context = context;
1207 /* from here on is the real "fail" code */
1210 (void) krb5_gss_delete_sec_context(&tmp_minor_status,
1211 (gss_ctx_id_t *) &ctx, NULL);
1212 if (deleg_cred) { /* free memory associated with the deleg credential */
1213 if (deleg_cred->ccache)
1214 (void)krb5_cc_close(context, deleg_cred->ccache);
1215 if (deleg_cred->name)
1216 kg_release_name(context, &deleg_cred->name);
1222 (void) kg_release_name(context, &name);
1225 *minor_status = code;
1228 * If decode_req_message is set, then we need to decode the ap_req
1229 * message to determine whether or not to send a response token.
1230 * We need to do this because for some errors we won't be able to
1231 * decode the authenticator to read out the gss_flags field.
1233 if (decode_req_message) {
1234 krb5_ap_req * request;
1236 if (decode_krb5_ap_req(&ap_req, &request))
1239 if (request->ap_options & AP_OPTS_MUTUAL_REQUIRED)
1240 gss_flags |= GSS_C_MUTUAL_FLAG;
1241 krb5_free_ap_req(context, request);
1245 && ((gss_flags & GSS_C_MUTUAL_FLAG)
1246 || (major_status == GSS_S_CONTINUE_NEEDED))) {
1247 unsigned int tmsglen;
1251 * The client is expecting a response, so we can send an
1254 memset(&krb_error_data, 0, sizeof(krb_error_data));
1256 code -= ERROR_TABLE_BASE_krb5;
1257 if (code < 0 || code > 128)
1258 code = 60 /* KRB_ERR_GENERIC */;
1260 krb_error_data.error = code;
1261 (void) krb5_us_timeofday(context, &krb_error_data.stime,
1262 &krb_error_data.susec);
1264 code = krb5_mk_error(context, &krb_error_data, &scratch);
1268 tmsglen = scratch.length;
1269 toktype = KG_TOK_CTX_ERROR;
1271 token.length = g_token_size(mech_used, tmsglen);
1272 token.value = (unsigned char *) xmalloc(token.length);
1277 g_make_token_header(mech_used, tmsglen, &ptr, toktype);
1279 TWRITE_STR(ptr, scratch.data, scratch.length);
1280 krb5_free_data_contents(context, &scratch);
1282 *output_token = token;
1286 if (!verifier_cred_handle && cred_handle) {
1287 krb5_gss_release_cred(&tmp_minor_status, &cred_handle);
1290 if (major_status && *minor_status)
1291 save_error_info(*minor_status, context);
1292 krb5_free_context(context);
1294 return (major_status);
1296 #endif /* LEAN_CLIENT */
1298 OM_uint32 KRB5_CALLCONV
1299 krb5_gss_accept_sec_context_ext(
1300 OM_uint32 *minor_status,
1301 gss_ctx_id_t *context_handle,
1302 gss_cred_id_t verifier_cred_handle,
1303 gss_buffer_t input_token,
1304 gss_channel_bindings_t input_chan_bindings,
1305 gss_name_t *src_name,
1307 gss_buffer_t output_token,
1308 OM_uint32 *ret_flags,
1309 OM_uint32 *time_rec,
1310 gss_cred_id_t *delegated_cred_handle,
1311 krb5_gss_ctx_ext_t exts)
1313 krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)*context_handle;
1316 * Context handle must be unspecified. Actually, it must be
1317 * non-established, but currently, accept_sec_context never returns
1318 * a non-established context handle.
1322 if (ctx->established == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) {
1323 return kg_accept_dce(minor_status, context_handle,
1324 verifier_cred_handle, input_token,
1325 input_chan_bindings, src_name, mech_type,
1326 output_token, ret_flags, time_rec,
1327 delegated_cred_handle);
1329 *minor_status = EINVAL;
1330 save_error_string(EINVAL, "accept_sec_context called with existing context handle");
1331 return GSS_S_FAILURE;
1335 return kg_accept_krb5(minor_status, context_handle,
1336 verifier_cred_handle, input_token,
1337 input_chan_bindings, src_name, mech_type,
1338 output_token, ret_flags, time_rec,
1339 delegated_cred_handle, exts);
1342 OM_uint32 KRB5_CALLCONV
1343 krb5_gss_accept_sec_context(minor_status, context_handle,
1344 verifier_cred_handle, input_token,
1345 input_chan_bindings, src_name, mech_type,
1346 output_token, ret_flags, time_rec,
1347 delegated_cred_handle)
1348 OM_uint32 *minor_status;
1349 gss_ctx_id_t *context_handle;
1350 gss_cred_id_t verifier_cred_handle;
1351 gss_buffer_t input_token;
1352 gss_channel_bindings_t input_chan_bindings;
1353 gss_name_t *src_name;
1355 gss_buffer_t output_token;
1356 OM_uint32 *ret_flags;
1357 OM_uint32 *time_rec;
1358 gss_cred_id_t *delegated_cred_handle;
1360 krb5_gss_ctx_ext_rec exts;
1362 memset(&exts, 0, sizeof(exts));
1364 return krb5_gss_accept_sec_context_ext(minor_status,
1366 verifier_cred_handle,
1368 input_chan_bindings,
1374 delegated_cred_handle,