diff options
| author | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 |
|---|---|---|
| committer | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 |
| commit | 33a9b234e7087f573ef08cd7318c6497ba08b439 (patch) | |
| tree | d0ea40ad3bf5463a3c55795977c71bcb7d781b4b /src/lib/gssapi/krb5 | |
Diffstat (limited to 'src/lib/gssapi/krb5')
55 files changed, 20313 insertions, 0 deletions
diff --git a/src/lib/gssapi/krb5/Makefile.in b/src/lib/gssapi/krb5/Makefile.in new file mode 100644 index 000000000000..364a7a4c9b25 --- /dev/null +++ b/src/lib/gssapi/krb5/Makefile.in @@ -0,0 +1,259 @@ +mydir=lib$(S)gssapi$(S)krb5 +BUILDTOP=$(REL)..$(S)..$(S).. +LOCALINCLUDES = -I. -I$(srcdir) -I$(srcdir)/.. -I../generic -I$(srcdir)/../generic -I../mechglue -I$(srcdir)/../mechglue +DEFINES=-D_GSS_STATIC_LINK=1 + +#MODULE_INSTALL_DIR = $(GSS_MODULE_DIR) +#LIBBASE=mech_krb5 +#LIBMAJOR=0 +#LIBMINOR=0 +#LIBINITFUNC=gss_krb5int_init +#LIBFINIFUNC=gss_krb5int_fini +#STOBJLISTS=../generic/OBJS.ST OBJS.ST +#SUBDIROBJLISTS=../generic/OBJS.ST +#SHLIB_EXPDEPS=$(KRB5_DEPLIB) $(CRYPTO_DEPLIB) $(SUPPORT_DEPLIB) $(COM_ERR_DEPLIB) +#SHLIB_EXPLIBS=-lkrb5 -lk5crypto -lcom_err $(SUPPORT_LIB) $(DL_LIB) $(LIBS) +#RELDIR=gssapi/krb5 + +##DOS##BUILDTOP = ..\..\.. +##DOS##PREFIXDIR=krb5 +##DOS##OBJFILE = ..\$(OUTPRE)krb5.lst + +##DOS##DLL_EXP_TYPE=GSS + +ETSRCS= gssapi_err_krb5.c +ETOBJS= $(OUTPRE)gssapi_err_krb5.$(OBJEXT) +ETHDRS= gssapi_err_krb5.h + +$(OUTPRE)gssapi_err_krb5.$(OBJEXT): gssapi_err_krb5.c +gssapi_err_krb5.h: gssapi_err_krb5.et +gssapi_err_krb5.c: gssapi_err_krb5.et + +SRCS = \ + $(srcdir)/accept_sec_context.c \ + $(srcdir)/acquire_cred.c \ + $(srcdir)/canon_name.c \ + $(srcdir)/compare_name.c \ + $(srcdir)/context_time.c \ + $(srcdir)/copy_ccache.c \ + $(srcdir)/cred_store.c \ + $(srcdir)/delete_sec_context.c \ + $(srcdir)/disp_name.c \ + $(srcdir)/disp_status.c \ + $(srcdir)/duplicate_name.c \ + $(srcdir)/export_cred.c \ + $(srcdir)/export_name.c \ + $(srcdir)/export_sec_context.c \ + $(srcdir)/get_tkt_flags.c \ + $(srcdir)/gssapi_krb5.c \ + $(srcdir)/iakerb.c \ + $(srcdir)/import_cred.c \ + $(srcdir)/import_name.c \ + $(srcdir)/import_sec_context.c \ + $(srcdir)/indicate_mechs.c \ + $(srcdir)/init_sec_context.c \ + $(srcdir)/inq_context.c \ + $(srcdir)/inq_cred.c \ + $(srcdir)/inq_names.c \ + $(srcdir)/k5seal.c \ + $(srcdir)/k5sealiov.c \ + $(srcdir)/k5sealv3.c \ + $(srcdir)/k5sealv3iov.c \ + $(srcdir)/k5unseal.c \ + $(srcdir)/k5unsealiov.c \ + $(srcdir)/krb5_gss_glue.c \ + $(srcdir)/lucid_context.c \ + $(srcdir)/naming_exts.c \ + $(srcdir)/prf.c \ + $(srcdir)/process_context_token.c \ + $(srcdir)/rel_cred.c \ + $(srcdir)/rel_oid.c \ + $(srcdir)/rel_name.c \ + $(srcdir)/s4u_gss_glue.c \ + $(srcdir)/set_allowable_enctypes.c \ + $(srcdir)/ser_sctx.c \ + $(srcdir)/set_ccache.c \ + $(srcdir)/store_cred.c \ + $(srcdir)/util_cksum.c \ + $(srcdir)/util_crypt.c \ + $(srcdir)/util_seed.c \ + $(srcdir)/util_seqnum.c \ + $(srcdir)/val_cred.c \ + $(srcdir)/wrap_size_limit.c + + +OBJS = \ + $(OUTPRE)accept_sec_context.$(OBJEXT) \ + $(OUTPRE)acquire_cred.$(OBJEXT) \ + $(OUTPRE)canon_name.$(OBJEXT) \ + $(OUTPRE)compare_name.$(OBJEXT) \ + $(OUTPRE)context_time.$(OBJEXT) \ + $(OUTPRE)copy_ccache.$(OBJEXT) \ + $(OUTPRE)cred_store.$(OBJEXT) \ + $(OUTPRE)delete_sec_context.$(OBJEXT) \ + $(OUTPRE)disp_name.$(OBJEXT) \ + $(OUTPRE)disp_status.$(OBJEXT) \ + $(OUTPRE)duplicate_name.$(OBJEXT) \ + $(OUTPRE)export_cred.$(OBJEXT) \ + $(OUTPRE)export_name.$(OBJEXT) \ + $(OUTPRE)export_sec_context.$(OBJEXT) \ + $(OUTPRE)get_tkt_flags.$(OBJEXT) \ + $(OUTPRE)gssapi_krb5.$(OBJEXT) \ + $(OUTPRE)iakerb.$(OBJEXT) \ + $(OUTPRE)import_cred.$(OBJEXT) \ + $(OUTPRE)import_name.$(OBJEXT) \ + $(OUTPRE)import_sec_context.$(OBJEXT) \ + $(OUTPRE)indicate_mechs.$(OBJEXT) \ + $(OUTPRE)init_sec_context.$(OBJEXT) \ + $(OUTPRE)inq_context.$(OBJEXT) \ + $(OUTPRE)inq_cred.$(OBJEXT) \ + $(OUTPRE)inq_names.$(OBJEXT) \ + $(OUTPRE)k5seal.$(OBJEXT) \ + $(OUTPRE)k5sealiov.$(OBJEXT) \ + $(OUTPRE)k5sealv3.$(OBJEXT) \ + $(OUTPRE)k5sealv3iov.$(OBJEXT) \ + $(OUTPRE)k5unseal.$(OBJEXT) \ + $(OUTPRE)k5unsealiov.$(OBJEXT) \ + $(OUTPRE)krb5_gss_glue.$(OBJEXT) \ + $(OUTPRE)lucid_context.$(OBJEXT) \ + $(OUTPRE)naming_exts.$(OBJEXT) \ + $(OUTPRE)prf.$(OBJEXT) \ + $(OUTPRE)process_context_token.$(OBJEXT) \ + $(OUTPRE)rel_cred.$(OBJEXT) \ + $(OUTPRE)rel_oid.$(OBJEXT) \ + $(OUTPRE)rel_name.$(OBJEXT) \ + $(OUTPRE)s4u_gss_glue.$(OBJEXT) \ + $(OUTPRE)set_allowable_enctypes.$(OBJEXT) \ + $(OUTPRE)ser_sctx.$(OBJEXT) \ + $(OUTPRE)set_ccache.$(OBJEXT) \ + $(OUTPRE)store_cred.$(OBJEXT) \ + $(OUTPRE)util_cksum.$(OBJEXT) \ + $(OUTPRE)util_crypt.$(OBJEXT) \ + $(OUTPRE)util_seed.$(OBJEXT) \ + $(OUTPRE)util_seqnum.$(OBJEXT) \ + $(OUTPRE)val_cred.$(OBJEXT) \ + $(OUTPRE)wrap_size_limit.$(OBJEXT) \ + $(OUTPRE)gssapi_err_krb5.$(OBJEXT) + +# k5mech.$(OBJEXT) \ +# pname_to_uid.$(OBJEXT) + +STLIBOBJS = \ + accept_sec_context.o \ + acquire_cred.o \ + canon_name.o \ + compare_name.o \ + context_time.o \ + copy_ccache.o \ + cred_store.o \ + delete_sec_context.o \ + disp_name.o \ + disp_status.o \ + duplicate_name.o \ + export_cred.o \ + export_name.o \ + export_sec_context.o \ + get_tkt_flags.o \ + gssapi_krb5.o \ + iakerb.o \ + import_cred.o \ + import_name.o \ + import_sec_context.o \ + indicate_mechs.o \ + init_sec_context.o \ + inq_context.o \ + inq_cred.o \ + inq_names.o \ + k5seal.o \ + k5sealiov.o \ + k5sealv3.o \ + k5sealv3iov.o \ + k5unseal.o \ + k5unsealiov.o \ + krb5_gss_glue.o \ + lucid_context.o \ + naming_exts.o \ + prf.o \ + process_context_token.o \ + rel_cred.o \ + rel_oid.o \ + rel_name.o \ + s4u_gss_glue.o \ + set_allowable_enctypes.o \ + ser_sctx.o \ + set_ccache.o \ + store_cred.o \ + util_cksum.o \ + util_crypt.o \ + util_seed.o \ + util_seqnum.o \ + val_cred.o \ + wrap_size_limit.o \ + gssapi_err_krb5.o + +# k5mech.o \ +# pname_to_uid.o + +HDRS= $(ETHDRS) + +EHDRDIR=$(BUILDTOP)$(S)include$(S)gssapi +EXPORTED_HEADERS= gssapi_krb5.h + +##DOS##LIBOBJS = $(OBJS) + +GSSAPI_KRB5_HDR=$(EHDRDIR)$(S)gssapi_krb5.h + +all-windows: $(EHDRDIR) $(GSSAPI_KRB5_HDR) $(SRCS) $(HDRS) + +##DOS##$(EHDRDIR): +##DOS## mkdir $(EHDRDIR) + +MK_EHDRDIR=if test -d $(EHDRDIR); then :; else (set -x; mkdir $(EHDRDIR)); fi +##DOS##MK_EHDRDIR=rem + +$(GSSAPI_KRB5_HDR): $(srcdir)$(S)gssapi_krb5.h + @$(MK_EHDRDIR) + $(CP) $(srcdir)$(S)gssapi_krb5.h "$@" + +all-unix: $(SRCS) $(HDRS) $(GSSAPI_KRB5_HDR) +all-unix: all-libobjs + +error_map.h: $(top_srcdir)/util/gen-map.pl \ + $(top_srcdir)/util/ktemplate.pm Makefile + $(PERL) -I$(top_srcdir)/util $(top_srcdir)/util/gen-map.pl \ + -oerror_map.new \ + NAME=gsserrmap \ + KEY=OM_uint32 \ + VALUE="char *" \ + COMPARE=compare_OM_uint32 \ + FREEVALUE=free_string + $(RM) $@ + $(MV) error_map.new $@ + +clean-unix:: + $(RM) $(BUILDTOP)/include/gssapi/gssapi_krb5.h + -$(RM) error_map.h + +clean-unix:: clean-libobjs + $(RM) $(ETHDRS) $(ETSRCS) + +clean-windows:: + $(RM) $(EHDRDIR)\gssapi_krb5.h + $(RM) error_map.h + -if exist $(EHDRDIR)\nul rmdir $(EHDRDIR) + + +generate-files-mac: error_map.h + +install-headers-unix install: + @set -x; for f in $(EXPORTED_HEADERS) ; \ + do $(INSTALL_DATA) $(srcdir)/$$f \ + $(DESTDIR)$(KRB5_INCDIR)/gssapi/$$f ; \ + done + +depend: $(ETSRCS) $(ETHDRS) $(GSSAPI_KRB5_HDR) error_map.h + +install: + +@libobj_frag@ + diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c new file mode 100644 index 000000000000..580d08cbf53e --- /dev/null +++ b/src/lib/gssapi/krb5/accept_sec_context.c @@ -0,0 +1,1347 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2000, 2004, 2007, 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#include <assert.h> + +#ifdef CFX_EXERCISE +#define CFX_ACCEPTOR_SUBKEY (time(0) & 1) +#else +#define CFX_ACCEPTOR_SUBKEY 1 +#endif + +#ifndef LEAN_CLIENT + +static OM_uint32 +create_constrained_deleg_creds(OM_uint32 *minor_status, + krb5_gss_cred_id_t verifier_cred_handle, + krb5_ticket *ticket, + krb5_gss_cred_id_t *out_cred, + krb5_context context) +{ + OM_uint32 major_status; + krb5_creds krb_creds; + krb5_data *data; + krb5_error_code code; + + assert(out_cred != NULL); + assert(verifier_cred_handle->usage == GSS_C_BOTH); + + memset(&krb_creds, 0, sizeof(krb_creds)); + krb_creds.client = ticket->enc_part2->client; + krb_creds.server = ticket->server; + krb_creds.keyblock = *(ticket->enc_part2->session); + krb_creds.ticket_flags = ticket->enc_part2->flags; + krb_creds.times = ticket->enc_part2->times; + krb_creds.magic = KV5M_CREDS; + krb_creds.authdata = NULL; + + code = encode_krb5_ticket(ticket, &data); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + krb_creds.ticket = *data; + + major_status = kg_compose_deleg_cred(minor_status, + verifier_cred_handle, + &krb_creds, + GSS_C_INDEFINITE, + out_cred, + NULL, + context); + + krb5_free_data(context, data); + + return major_status; +} + +/* Decode, decrypt and store the forwarded creds in the local ccache. */ +static krb5_error_code +rd_and_store_for_creds(context, auth_context, inbuf, out_cred) + krb5_context context; + krb5_auth_context auth_context; + krb5_data *inbuf; + krb5_gss_cred_id_t *out_cred; +{ + krb5_creds ** creds = NULL; + krb5_error_code retval; + krb5_ccache ccache = NULL; + krb5_gss_cred_id_t cred = NULL; + krb5_auth_context new_auth_ctx = NULL; + krb5_int32 flags_org; + + if ((retval = krb5_auth_con_getflags(context, auth_context, &flags_org))) + return retval; + krb5_auth_con_setflags(context, auth_context, + 0); + + /* + * By the time krb5_rd_cred is called here (after krb5_rd_req has been + * called in krb5_gss_accept_sec_context), the "keyblock" field of + * auth_context contains a pointer to the session key, and the + * "recv_subkey" field might contain a session subkey. Either of + * these (the "recv_subkey" if it isn't NULL, otherwise the + * "keyblock") might have been used to encrypt the encrypted part of + * the KRB_CRED message that contains the forwarded credentials. (The + * Java Crypto and Security Implementation from the DSTC in Australia + * always uses the session key. But apparently it never negotiates a + * subkey, so this code works fine against a JCSI client.) Up to the + * present, though, GSSAPI clients linked against the MIT code (which + * is almost all GSSAPI clients) don't encrypt the KRB_CRED message at + * all -- at this level. So if the first call to krb5_rd_cred fails, + * we should call it a second time with another auth context freshly + * created by krb5_auth_con_init. All of its keyblock fields will be + * NULL, so krb5_rd_cred will assume that the KRB_CRED message is + * unencrypted. (The MIT code doesn't actually send the KRB_CRED + * message in the clear -- the "authenticator" whose "checksum" ends up + * containing the KRB_CRED message does get encrypted.) + */ + if (krb5_rd_cred(context, auth_context, inbuf, &creds, NULL)) { + if ((retval = krb5_auth_con_init(context, &new_auth_ctx))) + goto cleanup; + krb5_auth_con_setflags(context, new_auth_ctx, 0); + if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf, + &creds, NULL))) + goto cleanup; + } + + if ((retval = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) { + ccache = NULL; + goto cleanup; + } + + if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client))) + goto cleanup; + + if ((retval = krb5_cc_store_cred(context, ccache, creds[0]))) + goto cleanup; + + /* generate a delegated credential handle */ + if (out_cred) { + /* allocate memory for a cred_t... */ + if (!(cred = + (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) { + retval = ENOMEM; /* out of memory? */ + goto cleanup; + } + + /* zero it out... */ + memset(cred, 0, sizeof(krb5_gss_cred_id_rec)); + + retval = k5_mutex_init(&cred->lock); + if (retval) { + xfree(cred); + cred = NULL; + goto cleanup; + } + + /* copy the client principle into it... */ + if ((retval = + kg_init_name(context, creds[0]->client, NULL, NULL, NULL, 0, + &cred->name))) { + k5_mutex_destroy(&cred->lock); + retval = ENOMEM; /* out of memory? */ + xfree(cred); /* clean up memory on failure */ + cred = NULL; + goto cleanup; + } + + cred->usage = GSS_C_INITIATE; /* we can't accept with this */ + /* cred->name already set */ + cred->keytab = NULL; /* no keytab associated with this... */ + cred->expire = creds[0]->times.endtime; /* store the end time */ + cred->ccache = ccache; /* the ccache containing the credential */ + cred->destroy_ccache = 1; + ccache = NULL; /* cred takes ownership so don't destroy */ + } + + /* If there were errors, there might have been a memory leak + if (!cred) + if ((retval = krb5_cc_close(context, ccache))) + goto cleanup; + */ +cleanup: + if (creds) + krb5_free_tgt_creds(context, creds); + + if (ccache) + (void)krb5_cc_destroy(context, ccache); + + if (out_cred) + *out_cred = cred; /* return credential */ + + if (new_auth_ctx) + krb5_auth_con_free(context, new_auth_ctx); + + krb5_auth_con_setflags(context, auth_context, flags_org); + + return retval; +} + + +/* + * Performs third leg of DCE authentication + */ +static OM_uint32 +kg_accept_dce(minor_status, context_handle, verifier_cred_handle, + input_token, input_chan_bindings, src_name, mech_type, + output_token, ret_flags, time_rec, delegated_cred_handle) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_cred_id_t verifier_cred_handle; + gss_buffer_t input_token; + gss_channel_bindings_t input_chan_bindings; + gss_name_t *src_name; + gss_OID *mech_type; + gss_buffer_t output_token; + OM_uint32 *ret_flags; + OM_uint32 *time_rec; + gss_cred_id_t *delegated_cred_handle; +{ + krb5_error_code code; + krb5_gss_ctx_id_rec *ctx = 0; + krb5_timestamp now; + krb5_gss_name_t name = NULL; + krb5_ui_4 nonce = 0; + krb5_data ap_rep; + OM_uint32 major_status = GSS_S_FAILURE; + + output_token->length = 0; + output_token->value = NULL; + + if (mech_type) + *mech_type = GSS_C_NULL_OID; + /* return a bogus cred handle */ + if (delegated_cred_handle) + *delegated_cred_handle = GSS_C_NO_CREDENTIAL; + + ctx = (krb5_gss_ctx_id_rec *)*context_handle; + + code = krb5_timeofday(ctx->k5_context, &now); + if (code != 0) { + major_status = GSS_S_FAILURE; + goto fail; + } + + ap_rep.data = input_token->value; + ap_rep.length = input_token->length; + + code = krb5_rd_rep_dce(ctx->k5_context, + ctx->auth_context, + &ap_rep, + &nonce); + if (code != 0) { + major_status = GSS_S_FAILURE; + goto fail; + } + + ctx->established = 1; + + if (src_name) { + code = kg_duplicate_name(ctx->k5_context, ctx->there, &name); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + *src_name = (gss_name_t) name; + } + + if (mech_type) + *mech_type = ctx->mech_used; + + if (time_rec) + *time_rec = ctx->krb_times.endtime + ctx->k5_context->clockskew - now; + + /* Never return GSS_C_DELEG_FLAG since we don't support DCE credential + * delegation yet. */ + if (ret_flags) + *ret_flags = (ctx->gss_flags & ~GSS_C_DELEG_FLAG); + + *minor_status = 0; + + return GSS_S_COMPLETE; + +fail: + /* real failure code follows */ + + (void) krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx, + NULL); + *context_handle = GSS_C_NO_CONTEXT; + *minor_status = code; + + return major_status; +} + +static krb5_error_code +kg_process_extension(krb5_context context, + krb5_auth_context auth_context, + int ext_type, + krb5_data *ext_data, + krb5_gss_ctx_ext_t exts) +{ + krb5_error_code code = 0; + + assert(exts != NULL); + + switch (ext_type) { + case KRB5_GSS_EXTS_IAKERB_FINISHED: + if (exts->iakerb.conv == NULL) { + code = KRB5KRB_AP_ERR_MSG_TYPE; /* XXX */ + } else { + krb5_key key; + + code = krb5_auth_con_getrecvsubkey_k(context, auth_context, &key); + if (code != 0) + break; + + code = iakerb_verify_finished(context, key, exts->iakerb.conv, + ext_data); + if (code == 0) + exts->iakerb.verified = 1; + + krb5_k_free_key(context, key); + } + break; + default: + break; + } + + return code; +} + +static OM_uint32 +kg_accept_krb5(minor_status, context_handle, + verifier_cred_handle, input_token, + input_chan_bindings, src_name, mech_type, + output_token, ret_flags, time_rec, + delegated_cred_handle, exts) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_cred_id_t verifier_cred_handle; + gss_buffer_t input_token; + gss_channel_bindings_t input_chan_bindings; + gss_name_t *src_name; + gss_OID *mech_type; + gss_buffer_t output_token; + OM_uint32 *ret_flags; + OM_uint32 *time_rec; + gss_cred_id_t *delegated_cred_handle; + krb5_gss_ctx_ext_t exts; +{ + krb5_context context; + unsigned char *ptr, *ptr2; + char *sptr; + OM_uint32 tmp; + size_t md5len; + krb5_gss_cred_id_t cred = 0; + krb5_data ap_rep, ap_req; + unsigned int i; + krb5_error_code code; + krb5_address addr, *paddr; + krb5_authenticator *authdat = 0; + krb5_checksum reqcksum; + krb5_gss_name_t name = NULL; + krb5_ui_4 gss_flags = 0; + krb5_gss_ctx_id_rec *ctx = NULL; + krb5_timestamp now; + gss_buffer_desc token; + krb5_auth_context auth_context = NULL; + krb5_ticket * ticket = NULL; + int option_id; + krb5_data option; + const gss_OID_desc *mech_used = NULL; + OM_uint32 major_status = GSS_S_FAILURE; + OM_uint32 tmp_minor_status; + krb5_error krb_error_data; + krb5_data scratch; + gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL; + krb5_gss_cred_id_t deleg_cred = NULL; + krb5int_access kaccess; + int cred_rcache = 0; + int no_encap = 0; + int token_deleg_flag = 0; + krb5_flags ap_req_options = 0; + krb5_enctype negotiated_etype; + krb5_authdata_context ad_context = NULL; + krb5_principal accprinc = NULL; + krb5_ap_req *request = NULL; + + code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); + if (code) { + *minor_status = code; + return(GSS_S_FAILURE); + } + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + /* set up returns to be freeable */ + + if (src_name) + *src_name = (gss_name_t) NULL; + output_token->length = 0; + output_token->value = NULL; + token.value = 0; + reqcksum.contents = 0; + ap_req.data = 0; + ap_rep.data = 0; + + if (mech_type) + *mech_type = GSS_C_NULL_OID; + /* return a bogus cred handle */ + if (delegated_cred_handle) + *delegated_cred_handle = GSS_C_NO_CREDENTIAL; + + /* handle default cred handle */ + if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) { + major_status = krb5_gss_acquire_cred(minor_status, GSS_C_NO_NAME, + GSS_C_INDEFINITE, GSS_C_NO_OID_SET, + GSS_C_ACCEPT, &defcred, + NULL, NULL); + if (major_status != GSS_S_COMPLETE) { + code = *minor_status; + goto fail; + } + verifier_cred_handle = defcred; + } + + /* Resolve any initiator state in the verifier cred and lock it. */ + major_status = kg_cred_resolve(minor_status, context, verifier_cred_handle, + GSS_C_NO_NAME); + if (GSS_ERROR(major_status)) { + code = *minor_status; + goto fail; + } + cred = (krb5_gss_cred_id_t)verifier_cred_handle; + + /* make sure the supplied credentials are valid for accept */ + + if ((cred->usage != GSS_C_ACCEPT) && + (cred->usage != GSS_C_BOTH)) { + code = 0; + major_status = GSS_S_NO_CRED; + goto fail; + } + + /* verify the token's integrity, and leave the token in ap_req. + figure out which mech oid was used, and save it */ + + ptr = (unsigned char *) input_token->value; + + if (!(code = g_verify_token_header(gss_mech_krb5, + &(ap_req.length), + &ptr, KG_TOK_CTX_AP_REQ, + input_token->length, 1))) { + mech_used = gss_mech_krb5; + } else if ((code == G_WRONG_MECH) + &&!(code = g_verify_token_header((gss_OID) gss_mech_iakerb, + &(ap_req.length), + &ptr, KG_TOK_CTX_AP_REQ, + input_token->length, 1))) { + mech_used = gss_mech_iakerb; + } else if ((code == G_WRONG_MECH) + &&!(code = g_verify_token_header((gss_OID) gss_mech_krb5_wrong, + &(ap_req.length), + &ptr, KG_TOK_CTX_AP_REQ, + input_token->length, 1))) { + mech_used = gss_mech_krb5_wrong; + } else if ((code == G_WRONG_MECH) && + !(code = g_verify_token_header(gss_mech_krb5_old, + &(ap_req.length), + &ptr, KG_TOK_CTX_AP_REQ, + input_token->length, 1))) { + /* + * Previous versions of this library used the old mech_id + * and some broken behavior (wrong IV on checksum + * encryption). We support the old mech_id for + * compatibility, and use it to decide when to use the + * old behavior. + */ + mech_used = gss_mech_krb5_old; + } else if (code == G_WRONG_TOKID) { + major_status = GSS_S_CONTINUE_NEEDED; + code = KRB5KRB_AP_ERR_MSG_TYPE; + mech_used = gss_mech_krb5; + goto fail; + } else if (code == G_BAD_TOK_HEADER) { + /* DCE style not encapsulated */ + ap_req.length = input_token->length; + ap_req.data = input_token->value; + mech_used = gss_mech_krb5; + no_encap = 1; + } else { + major_status = GSS_S_DEFECTIVE_TOKEN; + goto fail; + } + + sptr = (char *) ptr; + TREAD_STR(sptr, ap_req.data, ap_req.length); + + /* construct the sender_addr */ + + if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) && + (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) { + /* XXX is this right? */ + addr.addrtype = ADDRTYPE_INET; + addr.length = input_chan_bindings->initiator_address.length; + addr.contents = input_chan_bindings->initiator_address.value; + + paddr = &addr; + } else { + paddr = NULL; + } + + /* decode the AP_REQ message */ + code = decode_krb5_ap_req(&ap_req, &request); + if (code) { + major_status = GSS_S_FAILURE; + goto done; + } + ticket = request->ticket; + + /* decode the message */ + + if ((code = krb5_auth_con_init(context, &auth_context))) { + major_status = GSS_S_FAILURE; + save_error_info((OM_uint32)code, context); + goto fail; + } + if (cred->rcache) { + cred_rcache = 1; + if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) { + major_status = GSS_S_FAILURE; + goto fail; + } + + /* Limit the encryption types negotiated (if requested). */ + if (cred->req_enctypes) { + if ((code = krb5_auth_con_setpermetypes(context, auth_context, + cred->req_enctypes))) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + + if (!cred->default_identity) { + if ((code = kg_acceptor_princ(context, cred->name, &accprinc))) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + + code = krb5_rd_req_decoded(context, &auth_context, request, accprinc, + cred->keytab, &ap_req_options, NULL); + + krb5_free_principal(context, accprinc); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + krb5_auth_con_setflags(context, auth_context, + KRB5_AUTH_CONTEXT_DO_SEQUENCE); + + krb5_auth_con_getauthenticator(context, auth_context, &authdat); + +#if 0 + /* make sure the necessary parts of the authdat are present */ + + if ((authdat->authenticator->subkey == NULL) || + (authdat->ticket->enc_part2 == NULL)) { + code = KG_NO_SUBKEY; + major_status = GSS_S_FAILURE; + goto fail; + } +#endif + + if (authdat->checksum == NULL) { + /* + * Some SMB client implementations use handcrafted GSSAPI code that + * does not provide a checksum. MS-KILE documents that the Microsoft + * implementation considers a missing checksum acceptable; the server + * assumes all flags are unset in this case, and does not check channel + * bindings. + */ + gss_flags = 0; + } else if (authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) { + /* Samba does not send 0x8003 GSS-API checksums */ + krb5_boolean valid; + krb5_key subkey; + krb5_data zero; + + code = krb5_auth_con_getkey_k(context, auth_context, &subkey); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + + zero.length = 0; + zero.data = ""; + + code = krb5_k_verify_checksum(context, + subkey, + KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM, + &zero, + authdat->checksum, + &valid); + krb5_k_free_key(context, subkey); + if (code || !valid) { + major_status = GSS_S_BAD_SIG; + goto fail; + } + + /* Use ap_options from the request to guess the mutual flag. */ + gss_flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG; + if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED) + gss_flags |= GSS_C_MUTUAL_FLAG; + } else { + /* gss krb5 v1 */ + + /* stash this now, for later. */ + code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + + /* verify that the checksum is correct */ + + /* + The checksum may be either exactly 24 bytes, in which case + no options are specified, or greater than 24 bytes, in which case + one or more options are specified. Currently, the only valid + option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ). + */ + + if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) || + (authdat->checksum->length < 24)) { + code = 0; + major_status = GSS_S_BAD_BINDINGS; + goto fail; + } + + ptr = (unsigned char *) authdat->checksum->contents; + + TREAD_INT(ptr, tmp, 0); + + if (tmp != md5len) { + code = KG_BAD_LENGTH; + major_status = GSS_S_FAILURE; + goto fail; + } + + /* + The following section of code attempts to implement the + optional channel binding facility as described in RFC2743. + + Since this facility is optional channel binding may or may + not have been provided by either the client or the server. + + If the server has specified input_chan_bindings equal to + GSS_C_NO_CHANNEL_BINDINGS then we skip the check. If + the server does provide channel bindings then we compute + a checksum and compare against those provided by the + client. */ + + if ((code = kg_checksum_channel_bindings(context, + input_chan_bindings, + &reqcksum))) { + major_status = GSS_S_BAD_BINDINGS; + goto fail; + } + + /* Always read the clients bindings - eventhough we might ignore them */ + TREAD_STR(ptr, ptr2, reqcksum.length); + + if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) { + if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) { + xfree(reqcksum.contents); + reqcksum.contents = 0; + code = 0; + major_status = GSS_S_BAD_BINDINGS; + goto fail; + } + + } + + xfree(reqcksum.contents); + reqcksum.contents = 0; + + /* Read the token flags. Remember if GSS_C_DELEG_FLAG was set, but + * mask it out until we actually read a delegated credential. */ + TREAD_INT(ptr, gss_flags, 0); + token_deleg_flag = (gss_flags & GSS_C_DELEG_FLAG); + gss_flags &= ~GSS_C_DELEG_FLAG; + + /* if the checksum length > 24, there are options to process */ + + i = authdat->checksum->length - 24; + if (i && token_deleg_flag) { + if (i >= 4) { + TREAD_INT16(ptr, option_id, 0); + TREAD_INT16(ptr, option.length, 0); + i -= 4; + + if (i < option.length) { + code = KG_BAD_LENGTH; + major_status = GSS_S_FAILURE; + goto fail; + } + + /* have to use ptr2, since option.data is wrong type and + macro uses ptr as both lvalue and rvalue */ + + TREAD_STR(ptr, ptr2, option.length); + option.data = (char *) ptr2; + + i -= option.length; + + if (option_id != KRB5_GSS_FOR_CREDS_OPTION) { + major_status = GSS_S_FAILURE; + goto fail; + } + + /* store the delegated credential */ + + code = rd_and_store_for_creds(context, auth_context, &option, + (delegated_cred_handle) ? + &deleg_cred : NULL); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + + gss_flags |= GSS_C_DELEG_FLAG; + } /* if i >= 4 */ + /* ignore any additional trailing data, for now */ + } + while (i > 0) { + /* Process Type-Length-Data options */ + if (i < 8) { + code = KG_BAD_LENGTH; + major_status = GSS_S_FAILURE; + goto fail; + } + TREAD_INT(ptr, option_id, 1); + TREAD_INT(ptr, option.length, 1); + i -= 8; + if (i < option.length) { + code = KG_BAD_LENGTH; + major_status = GSS_S_FAILURE; + goto fail; + } + TREAD_STR(ptr, ptr2, option.length); + option.data = (char *)ptr2; + + i -= option.length; + + code = kg_process_extension(context, auth_context, + option_id, &option, exts); + if (code != 0) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + } + + if (exts->iakerb.conv && !exts->iakerb.verified) { + major_status = GSS_S_BAD_SIG; + goto fail; + } + + /* only DCE_STYLE clients are allowed to send raw AP-REQs */ + if (no_encap != ((gss_flags & GSS_C_DCE_STYLE) != 0)) { + major_status = GSS_S_DEFECTIVE_TOKEN; + goto fail; + } + + /* create the ctx struct and start filling it in */ + + if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec))) + == NULL) { + code = ENOMEM; + major_status = GSS_S_FAILURE; + goto fail; + } + + memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); + ctx->magic = KG_CONTEXT; + ctx->mech_used = (gss_OID) mech_used; + ctx->auth_context = auth_context; + ctx->initiate = 0; + ctx->gss_flags = (GSS_C_TRANS_FLAG | + ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG | + GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | + GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG | + GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG | + GSS_C_EXTENDED_ERROR_FLAG))); + ctx->seed_init = 0; + ctx->cred_rcache = cred_rcache; + + /* XXX move this into gss_name_t */ + if ( (code = krb5_merge_authdata(context, + ticket->enc_part2->authorization_data, + authdat->authorization_data, + &ctx->authdata))) { + major_status = GSS_S_FAILURE; + goto fail; + } + if ((code = kg_init_name(context, ticket->server, NULL, NULL, NULL, 0, + &ctx->here))) { + major_status = GSS_S_FAILURE; + goto fail; + } + if ((code = krb5_auth_con_get_authdata_context(context, auth_context, + &ad_context))) { + major_status = GSS_S_FAILURE; + goto fail; + } + if ((code = kg_init_name(context, authdat->client, NULL, NULL, + ad_context, KG_INIT_NAME_NO_COPY, &ctx->there))) { + major_status = GSS_S_FAILURE; + goto fail; + } + /* Now owned by ctx->there */ + authdat->client = NULL; + krb5_auth_con_set_authdata_context(context, auth_context, NULL); + + if ((code = krb5_auth_con_getrecvsubkey_k(context, auth_context, + &ctx->subkey))) { + major_status = GSS_S_FAILURE; + goto fail; + } + + /* use the session key if the subkey isn't present */ + + if (ctx->subkey == NULL) { + if ((code = krb5_auth_con_getkey_k(context, auth_context, + &ctx->subkey))) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + + if (ctx->subkey == NULL) { + /* this isn't a very good error, but it's not clear to me this + can actually happen */ + major_status = GSS_S_FAILURE; + code = KRB5KDC_ERR_NULL_KEY; + goto fail; + } + + ctx->enc = NULL; + ctx->seq = NULL; + ctx->have_acceptor_subkey = 0; + /* DCE_STYLE implies acceptor_subkey */ + if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0) { + code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + ctx->krb_times = ticket->enc_part2->times; /* struct copy */ + ctx->krb_flags = ticket->enc_part2->flags; + + if (delegated_cred_handle != NULL && + deleg_cred == NULL && /* no unconstrained delegation */ + cred->usage == GSS_C_BOTH && + (ticket->enc_part2->flags & TKT_FLG_FORWARDABLE)) { + /* + * Now, we always fabricate a delegated credentials handle + * containing the service ticket to ourselves, which can be + * used for S4U2Proxy. + */ + major_status = create_constrained_deleg_creds(minor_status, cred, + ticket, &deleg_cred, + context); + if (GSS_ERROR(major_status)) + goto fail; + ctx->gss_flags |= GSS_C_DELEG_FLAG; + } + + { + krb5_int32 seq_temp; + krb5_auth_con_getremoteseqnumber(context, auth_context, &seq_temp); + ctx->seq_recv = seq_temp; + } + + if ((code = krb5_timeofday(context, &now))) { + major_status = GSS_S_FAILURE; + goto fail; + } + + code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv, + (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0, + (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, + ctx->proto); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + + /* DCE_STYLE implies mutual authentication */ + if (ctx->gss_flags & GSS_C_DCE_STYLE) + ctx->gss_flags |= GSS_C_MUTUAL_FLAG; + + /* at this point, the entire context structure is filled in, + so it can be released. */ + + /* generate an AP_REP if necessary */ + + if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) { + unsigned char * ptr3; + krb5_int32 seq_temp; + int cfx_generate_subkey; + + /* + * Do not generate a subkey per RFC 4537 unless we are upgrading to CFX, + * because pre-CFX tokens do not indicate which key to use. (Note that + * DCE_STYLE implies that we will use a subkey.) + */ + if (ctx->proto == 0 && + (ctx->gss_flags & GSS_C_DCE_STYLE) == 0 && + (ap_req_options & AP_OPTS_USE_SUBKEY)) { + code = (*kaccess.auth_con_get_subkey_enctype)(context, + auth_context, + &negotiated_etype); + if (code != 0) { + major_status = GSS_S_FAILURE; + goto fail; + } + + switch (negotiated_etype) { + case ENCTYPE_DES_CBC_MD5: + case ENCTYPE_DES_CBC_MD4: + case ENCTYPE_DES_CBC_CRC: + case ENCTYPE_DES3_CBC_SHA1: + case ENCTYPE_ARCFOUR_HMAC: + case ENCTYPE_ARCFOUR_HMAC_EXP: + /* RFC 4121 accidentally omits RC4-HMAC-EXP as a "not-newer" + * enctype, even though RFC 4757 treats it as one. */ + ap_req_options &= ~(AP_OPTS_USE_SUBKEY); + break; + } + } + + if (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) || + (ap_req_options & AP_OPTS_USE_SUBKEY)) + cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY; + else + cfx_generate_subkey = 0; + + if (cfx_generate_subkey) { + krb5_int32 acflags; + code = krb5_auth_con_getflags(context, auth_context, &acflags); + if (code == 0) { + acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY; + code = krb5_auth_con_setflags(context, auth_context, acflags); + } + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + + if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) { + major_status = GSS_S_FAILURE; + goto fail; + } + + krb5_auth_con_getlocalseqnumber(context, auth_context, &seq_temp); + ctx->seq_send = seq_temp & 0xffffffffL; + + if (cfx_generate_subkey) { + /* Get the new acceptor subkey. With the code above, there + should always be one if we make it to this point. */ + code = krb5_auth_con_getsendsubkey_k(context, auth_context, + &ctx->acceptor_subkey); + if (code != 0) { + major_status = GSS_S_FAILURE; + goto fail; + } + ctx->have_acceptor_subkey = 1; + + code = kg_setup_keys(context, ctx, ctx->acceptor_subkey, + &ctx->acceptor_subkey_cksumtype); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + + /* the reply token hasn't been sent yet, but that's ok. */ + if (ctx->gss_flags & GSS_C_DCE_STYLE) { + assert(ctx->have_acceptor_subkey); + + /* in order to force acceptor subkey to be used, don't set PROT_READY */ + + /* Raw AP-REP is returned */ + code = data_to_gss(&ap_rep, output_token); + if (code) + { + major_status = GSS_S_FAILURE; + goto fail; + } + + ctx->established = 0; + + *context_handle = (gss_ctx_id_t)ctx; + *minor_status = 0; + major_status = GSS_S_CONTINUE_NEEDED; + + /* Only last leg should set return arguments */ + goto fail; + } else + ctx->gss_flags |= GSS_C_PROT_READY_FLAG; + + ctx->established = 1; + + token.length = g_token_size(mech_used, ap_rep.length); + + if ((token.value = (unsigned char *) gssalloc_malloc(token.length)) + == NULL) { + major_status = GSS_S_FAILURE; + code = ENOMEM; + goto fail; + } + ptr3 = token.value; + g_make_token_header(mech_used, ap_rep.length, + &ptr3, KG_TOK_CTX_AP_REP); + + TWRITE_STR(ptr3, ap_rep.data, ap_rep.length); + + ctx->established = 1; + + } else { + token.length = 0; + token.value = NULL; + ctx->seq_send = ctx->seq_recv; + + ctx->established = 1; + } + + /* set the return arguments */ + + if (src_name) { + code = kg_duplicate_name(context, ctx->there, &name); + if (code) { + major_status = GSS_S_FAILURE; + goto fail; + } + } + + if (mech_type) + *mech_type = (gss_OID) mech_used; + + /* Add the maximum allowable clock skew as a grace period for context + * expiration, just as we do for the ticket. */ + if (time_rec) + *time_rec = ctx->krb_times.endtime + context->clockskew - now; + + if (ret_flags) + *ret_flags = ctx->gss_flags; + + *context_handle = (gss_ctx_id_t)ctx; + *output_token = token; + + if (src_name) + *src_name = (gss_name_t) name; + + if (delegated_cred_handle) + *delegated_cred_handle = (gss_cred_id_t) deleg_cred; + + /* finally! */ + + *minor_status = 0; + major_status = GSS_S_COMPLETE; + +fail: + if (authdat) + krb5_free_authenticator(context, authdat); + /* The ctx structure has the handle of the auth_context */ + if (auth_context && !ctx) { + if (cred_rcache) + (void)krb5_auth_con_setrcache(context, auth_context, NULL); + + krb5_auth_con_free(context, auth_context); + } + if (reqcksum.contents) + xfree(reqcksum.contents); + if (ap_rep.data) + krb5_free_data_contents(context, &ap_rep); + if (major_status == GSS_S_COMPLETE || + (major_status == GSS_S_CONTINUE_NEEDED && code != KRB5KRB_AP_ERR_MSG_TYPE)) { + ctx->k5_context = context; + context = NULL; + goto done; + } + + /* from here on is the real "fail" code */ + + if (ctx) + (void) krb5_gss_delete_sec_context(&tmp_minor_status, + (gss_ctx_id_t *) &ctx, NULL); + if (deleg_cred) { /* free memory associated with the deleg credential */ + if (deleg_cred->ccache) + (void)krb5_cc_close(context, deleg_cred->ccache); + if (deleg_cred->name) + kg_release_name(context, &deleg_cred->name); + xfree(deleg_cred); + } + if (token.value) + xfree(token.value); + if (name) { + (void) kg_release_name(context, &name); + } + + *minor_status = code; + + /* We may have failed before being able to read the GSS flags from the + * authenticator, so also check the request AP options. */ + if (cred != NULL && request != NULL && + ((gss_flags & GSS_C_MUTUAL_FLAG) || + (request->ap_options & AP_OPTS_MUTUAL_REQUIRED) || + major_status == GSS_S_CONTINUE_NEEDED)) { + unsigned int tmsglen; + int toktype; + + /* + * The client is expecting a response, so we can send an + * error token back + */ + memset(&krb_error_data, 0, sizeof(krb_error_data)); + + code -= ERROR_TABLE_BASE_krb5; + if (code < 0 || code > KRB_ERR_MAX) + code = 60 /* KRB_ERR_GENERIC */; + + krb_error_data.error = code; + (void) krb5_us_timeofday(context, &krb_error_data.stime, + &krb_error_data.susec); + + krb_error_data.server = ticket->server; + code = krb5_mk_error(context, &krb_error_data, &scratch); + if (code) + goto done; + + tmsglen = scratch.length; + toktype = KG_TOK_CTX_ERROR; + + token.length = g_token_size(mech_used, tmsglen); + token.value = gssalloc_malloc(token.length); + if (!token.value) + goto done; + + ptr = token.value; + g_make_token_header(mech_used, tmsglen, &ptr, toktype); + + TWRITE_STR(ptr, scratch.data, scratch.length); + krb5_free_data_contents(context, &scratch); + + *output_token = token; + } + +done: + krb5_free_ap_req(context, request); + if (cred) + k5_mutex_unlock(&cred->lock); + if (defcred) + krb5_gss_release_cred(&tmp_minor_status, &defcred); + if (context) { + if (major_status && *minor_status) + save_error_info(*minor_status, context); + krb5_free_context(context); + } + return (major_status); +} +#endif /* LEAN_CLIENT */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_accept_sec_context_ext( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_cred_id_t verifier_cred_handle, + gss_buffer_t input_token, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle, + krb5_gss_ctx_ext_t exts) +{ + krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)*context_handle; + + /* + * Context handle must be unspecified. Actually, it must be + * non-established, but currently, accept_sec_context never returns + * a non-established context handle. + */ + /*SUPPRESS 29*/ + if (ctx != NULL) { + if (ctx->established == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) { + return kg_accept_dce(minor_status, context_handle, + verifier_cred_handle, input_token, + input_chan_bindings, src_name, mech_type, + output_token, ret_flags, time_rec, + delegated_cred_handle); + } else { + *minor_status = EINVAL; + save_error_string(EINVAL, "accept_sec_context called with existing context handle"); + return GSS_S_FAILURE; + } + } + + return kg_accept_krb5(minor_status, context_handle, + verifier_cred_handle, input_token, + input_chan_bindings, src_name, mech_type, + output_token, ret_flags, time_rec, + delegated_cred_handle, exts); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_accept_sec_context(minor_status, context_handle, + verifier_cred_handle, input_token, + input_chan_bindings, src_name, mech_type, + output_token, ret_flags, time_rec, + delegated_cred_handle) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_cred_id_t verifier_cred_handle; + gss_buffer_t input_token; + gss_channel_bindings_t input_chan_bindings; + gss_name_t *src_name; + gss_OID *mech_type; + gss_buffer_t output_token; + OM_uint32 *ret_flags; + OM_uint32 *time_rec; + gss_cred_id_t *delegated_cred_handle; +{ + krb5_gss_ctx_ext_rec exts; + + memset(&exts, 0, sizeof(exts)); + + return krb5_gss_accept_sec_context_ext(minor_status, + context_handle, + verifier_cred_handle, + input_token, + input_chan_bindings, + src_name, + mech_type, + output_token, + ret_flags, + time_rec, + delegated_cred_handle, + &exts); +} diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c new file mode 100644 index 000000000000..03ee25ec1861 --- /dev/null +++ b/src/lib/gssapi/krb5/acquire_cred.c @@ -0,0 +1,1261 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2000, 2007-2010 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" +#ifdef HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +#ifdef USE_LEASH +#ifdef _WIN64 +#define LEASH_DLL "leashw64.dll" +#else +#define LEASH_DLL "leashw32.dll" +#endif +static void (*pLeash_AcquireInitialTicketsIfNeeded)(krb5_context,krb5_principal,char*,int) = NULL; +static HANDLE hLeashDLL = INVALID_HANDLE_VALUE; +#endif + +#ifndef LEAN_CLIENT +k5_mutex_t gssint_krb5_keytab_lock = K5_MUTEX_PARTIAL_INITIALIZER; +static char *krb5_gss_keytab = NULL; + +/* Heimdal calls this gsskrb5_register_acceptor_identity. */ +OM_uint32 +gss_krb5int_register_acceptor_identity(OM_uint32 *minor_status, + const gss_OID desired_mech, + const gss_OID desired_object, + gss_buffer_t value) +{ + char *new = NULL, *old; + int err; + + err = gss_krb5int_initialize_library(); + if (err != 0) + return GSS_S_FAILURE; + + if (value->value != NULL) { + new = strdup((char *)value->value); + if (new == NULL) + return GSS_S_FAILURE; + } + + k5_mutex_lock(&gssint_krb5_keytab_lock); + old = krb5_gss_keytab; + krb5_gss_keytab = new; + k5_mutex_unlock(&gssint_krb5_keytab_lock); + free(old); + return GSS_S_COMPLETE; +} + +/* Try to verify that keytab contains at least one entry for name. Return 0 if + * it does, KRB5_KT_NOTFOUND if it doesn't, or another error as appropriate. */ +static krb5_error_code +check_keytab(krb5_context context, krb5_keytab kt, krb5_gss_name_t name) +{ + krb5_error_code code; + krb5_keytab_entry ent; + krb5_kt_cursor cursor; + krb5_principal accprinc = NULL; + krb5_boolean match; + char *princname; + + if (name->service == NULL) { + code = krb5_kt_get_entry(context, kt, name->princ, 0, 0, &ent); + if (code == 0) + krb5_kt_free_entry(context, &ent); + return code; + } + + /* If we can't iterate through the keytab, skip this check. */ + if (kt->ops->start_seq_get == NULL) + return 0; + + /* Get the partial principal for the acceptor name. */ + code = kg_acceptor_princ(context, name, &accprinc); + if (code) + return code; + + /* Scan the keytab for host-based entries matching accprinc. */ + code = krb5_kt_start_seq_get(context, kt, &cursor); + if (code) + goto cleanup; + while ((code = krb5_kt_next_entry(context, kt, &ent, &cursor)) == 0) { + match = krb5_sname_match(context, accprinc, ent.principal); + (void)krb5_free_keytab_entry_contents(context, &ent); + if (match) + break; + } + (void)krb5_kt_end_seq_get(context, kt, &cursor); + if (code == KRB5_KT_END) { + code = KRB5_KT_NOTFOUND; + if (krb5_unparse_name(context, accprinc, &princname) == 0) { + k5_setmsg(context, code, _("No key table entry found matching %s"), + princname); + free(princname); + } + } + +cleanup: + krb5_free_principal(context, accprinc); + return code; +} + +/* get credentials corresponding to a key in the krb5 keytab. + If successful, set the keytab-specific fields in cred +*/ + +static OM_uint32 +acquire_accept_cred(krb5_context context, OM_uint32 *minor_status, + krb5_keytab req_keytab, const char *rcname, + krb5_gss_cred_id_rec *cred) +{ + OM_uint32 major; + krb5_error_code code; + krb5_keytab kt = NULL; + krb5_rcache rc = NULL; + + assert(cred->keytab == NULL); + + /* If we have an explicit rcache name, open it. */ + if (rcname != NULL) { + code = krb5_rc_resolve_full(context, &rc, rcname); + if (code) { + major = GSS_S_FAILURE; + goto cleanup; + } + code = krb5_rc_recover_or_initialize(context, rc, context->clockskew); + if (code) { + major = GSS_S_FAILURE; + goto cleanup; + } + } + + if (req_keytab != NULL) { + code = krb5_kt_dup(context, req_keytab, &kt); + } else { + k5_mutex_lock(&gssint_krb5_keytab_lock); + if (krb5_gss_keytab != NULL) { + code = krb5_kt_resolve(context, krb5_gss_keytab, &kt); + k5_mutex_unlock(&gssint_krb5_keytab_lock); + } else { + k5_mutex_unlock(&gssint_krb5_keytab_lock); + code = krb5_kt_default(context, &kt); + } + } + if (code) { + major = GSS_S_CRED_UNAVAIL; + goto cleanup; + } + + if (cred->name != NULL) { + /* Make sure we have keys matching the desired name in the keytab. */ + code = check_keytab(context, kt, cred->name); + if (code) { + if (code == KRB5_KT_NOTFOUND) { + k5_change_error_message_code(context, code, KG_KEYTAB_NOMATCH); + code = KG_KEYTAB_NOMATCH; + } + major = GSS_S_CRED_UNAVAIL; + goto cleanup; + } + + if (rc == NULL) { + /* Open the replay cache for this principal. */ + code = krb5_get_server_rcache(context, &cred->name->princ->data[0], + &rc); + if (code) { + major = GSS_S_FAILURE; + goto cleanup; + } + } + } else { + /* Make sure we have a keytab with keys in it. */ + code = krb5_kt_have_content(context, kt); + if (code) { + major = GSS_S_CRED_UNAVAIL; + goto cleanup; + } + } + + cred->keytab = kt; + kt = NULL; + cred->rcache = rc; + rc = NULL; + major = GSS_S_COMPLETE; + +cleanup: + if (kt != NULL) + krb5_kt_close(context, kt); + if (rc != NULL) + krb5_rc_close(context, rc); + *minor_status = code; + return major; +} +#endif /* LEAN_CLIENT */ + +#ifdef USE_LEASH +static krb5_error_code +get_ccache_leash(krb5_context context, krb5_principal desired_princ, + krb5_ccache *ccache_out) +{ + krb5_error_code code; + krb5_ccache ccache; + char ccname[256] = ""; + + *ccache_out = NULL; + + if (hLeashDLL == INVALID_HANDLE_VALUE) { + hLeashDLL = LoadLibrary(LEASH_DLL); + if (hLeashDLL != INVALID_HANDLE_VALUE) { + (FARPROC) pLeash_AcquireInitialTicketsIfNeeded = + GetProcAddress(hLeashDLL, "not_an_API_Leash_AcquireInitialTicketsIfNeeded"); + } + } + + if (pLeash_AcquireInitialTicketsIfNeeded) { + pLeash_AcquireInitialTicketsIfNeeded(context, desired_princ, ccname, + sizeof(ccname)); + if (!ccname[0]) + return KRB5_CC_NOTFOUND; + + code = krb5_cc_resolve(context, ccname, &ccache); + if (code) + return code; + } else { + /* leash dll not available, open the default credential cache. */ + code = krb5int_cc_default(context, &ccache); + if (code) + return code; + } + + *ccache_out = ccache; + return 0; +} +#endif /* USE_LEASH */ + +/* Set fields in cred according to a ccache config entry whose key (in + * principal form) is config_princ and whose value is value. */ +static krb5_error_code +scan_cc_config(krb5_context context, krb5_gss_cred_id_rec *cred, + krb5_const_principal config_princ, const krb5_data *value) +{ + krb5_error_code code; + krb5_data data0 = empty_data(); + + if (config_princ->length != 2) + return 0; + if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_PROXY_IMPERSONATOR) + && cred->impersonator == NULL) { + code = krb5int_copy_data_contents_add0(context, value, &data0); + if (code) + return code; + code = krb5_parse_name(context, data0.data, &cred->impersonator); + krb5_free_data_contents(context, &data0); + if (code) + return code; + } else if (data_eq_string(config_princ->data[1], KRB5_CC_CONF_REFRESH_TIME) + && cred->refresh_time == 0) { + code = krb5int_copy_data_contents_add0(context, value, &data0); + if (code) + return code; + cred->refresh_time = atol(data0.data); + krb5_free_data_contents(context, &data0); + } + return 0; +} + +/* Return true if it appears that we can non-interactively get initial + * tickets for cred. */ +static krb5_boolean +can_get_initial_creds(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + krb5_keytab_entry entry; + + if (cred->password != NULL) + return TRUE; + + if (cred->client_keytab == NULL) + return FALSE; + + /* If we don't know the client principal yet, check for any keytab keys. */ + if (cred->name == NULL) + return !krb5_kt_have_content(context, cred->client_keytab); + + /* Check if we have a keytab key for the client principal. */ + code = krb5_kt_get_entry(context, cred->client_keytab, cred->name->princ, + 0, 0, &entry); + if (code) { + krb5_clear_error_message(context); + return FALSE; + } + krb5_free_keytab_entry_contents(context, &entry); + return TRUE; +} + +/* Scan cred->ccache for name, expiry time, impersonator, refresh time. */ +static krb5_error_code +scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + krb5_ccache ccache = cred->ccache; + krb5_principal ccache_princ = NULL, tgt_princ = NULL; + krb5_data *realm; + krb5_cc_cursor cursor; + krb5_creds creds; + krb5_timestamp endtime; + krb5_boolean is_tgt; + + /* Turn on NOTICKET, as we don't need session keys here. */ + code = krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET); + if (code) + return code; + + /* Credentials cache principal must match the initiator name. */ + code = krb5_cc_get_principal(context, ccache, &ccache_princ); + if (code != 0) + goto cleanup; + if (cred->name != NULL && + !krb5_principal_compare(context, ccache_princ, cred->name->princ)) { + code = KG_CCACHE_NOMATCH; + goto cleanup; + } + + /* Save the ccache principal as the credential name if not already set. */ + if (!cred->name) { + code = kg_init_name(context, ccache_princ, NULL, NULL, NULL, + KG_INIT_NAME_NO_COPY, &cred->name); + if (code) + goto cleanup; + ccache_princ = NULL; + } + + assert(cred->name->princ != NULL); + realm = krb5_princ_realm(context, cred->name->princ); + code = krb5_build_principal_ext(context, &tgt_princ, + realm->length, realm->data, + KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, + realm->length, realm->data, + 0); + if (code) + return code; + + /* If there's a tgt for the principal's local realm in here, use its expiry + * time. Otherwise use the first key. */ + code = krb5_cc_start_seq_get(context, ccache, &cursor); + if (code) { + krb5_free_principal(context, tgt_princ); + return code; + } + while (!(code = krb5_cc_next_cred(context, ccache, &cursor, &creds))) { + if (krb5_is_config_principal(context, creds.server)) { + code = scan_cc_config(context, cred, creds.server, &creds.ticket); + krb5_free_cred_contents(context, &creds); + if (code) + break; + continue; + } + is_tgt = krb5_principal_compare(context, tgt_princ, creds.server); + endtime = creds.times.endtime; + krb5_free_cred_contents(context, &creds); + if (is_tgt) + cred->have_tgt = TRUE; + if (is_tgt || cred->expire == 0) + cred->expire = endtime; + } + krb5_cc_end_seq_get(context, ccache, &cursor); + if (code && code != KRB5_CC_END) + goto cleanup; + code = 0; + + if (cred->expire == 0 && !can_get_initial_creds(context, cred)) { + code = KG_EMPTY_CCACHE; + goto cleanup; + } + +cleanup: + (void)krb5_cc_set_flags(context, ccache, 0); + krb5_free_principal(context, ccache_princ); + krb5_free_principal(context, tgt_princ); + return code; +} + +/* Find an existing or destination ccache for cred->name. */ +static krb5_error_code +get_cache_for_name(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + krb5_boolean can_get, have_collection; + krb5_ccache defcc = NULL; + krb5_principal princ = NULL; + const char *cctype; + + assert(cred->name != NULL && cred->ccache == NULL); +#ifdef USE_LEASH + code = get_ccache_leash(context, cred->name->princ, &cred->ccache); + return code ? code : scan_ccache(context, cred); +#else + /* Check first whether we can acquire tickets, to avoid overwriting the + * extended error message from krb5_cc_cache_match. */ + can_get = can_get_initial_creds(context, cred); + + /* Look for an existing cache for the client principal. */ + code = krb5_cc_cache_match(context, cred->name->princ, &cred->ccache); + if (code == 0) + return scan_ccache(context, cred); + if (code != KRB5_CC_NOTFOUND || !can_get) + return code; + krb5_clear_error_message(context); + + /* There is no existing ccache, but we can acquire credentials. Get the + * default ccache to help decide where we should put them. */ + code = krb5_cc_default(context, &defcc); + if (code) + return code; + cctype = krb5_cc_get_type(context, defcc); + have_collection = krb5_cc_support_switch(context, cctype); + + /* We can use an empty default ccache if we're using a password or if + * there's no collection. */ + if (cred->password != NULL || !have_collection) { + if (krb5_cc_get_principal(context, defcc, &princ) == KRB5_FCC_NOFILE) { + cred->ccache = defcc; + defcc = NULL; + } + krb5_clear_error_message(context); + } + + /* Otherwise, try to use a new cache in the collection. */ + if (cred->ccache == NULL) { + if (!have_collection) { + code = KG_CCACHE_NOMATCH; + goto cleanup; + } + code = krb5_cc_new_unique(context, cctype, NULL, &cred->ccache); + if (code) + goto cleanup; + } + +cleanup: + krb5_free_principal(context, princ); + if (defcc != NULL) + krb5_cc_close(context, defcc); + return code; +#endif /* not USE_LEASH */ +} + +/* Try to set cred->name using the client keytab. */ +static krb5_error_code +get_name_from_client_keytab(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + krb5_principal princ; + + assert(cred->name == NULL); + + if (cred->client_keytab == NULL) + return KRB5_KT_NOTFOUND; + + code = k5_kt_get_principal(context, cred->client_keytab, &princ); + if (code) + return code; + code = kg_init_name(context, princ, NULL, NULL, NULL, KG_INIT_NAME_NO_COPY, + &cred->name); + if (code) { + krb5_free_principal(context, princ); + return code; + } + return 0; +} + +/* Make a note in ccache that we should attempt to refresh it from the client + * keytab at refresh_time. */ +static void +set_refresh_time(krb5_context context, krb5_ccache ccache, + krb5_timestamp refresh_time) +{ + char buf[128]; + krb5_data d; + + snprintf(buf, sizeof(buf), "%ld", (long)refresh_time); + d = string2data(buf); + (void)krb5_cc_set_config(context, ccache, NULL, KRB5_CC_CONF_REFRESH_TIME, + &d); + krb5_clear_error_message(context); +} + +/* Return true if it's time to refresh cred from the client keytab. If + * returning true, avoid retrying for 30 seconds. */ +krb5_boolean +kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_timestamp now; + + if (krb5_timeofday(context, &now)) + return FALSE; + if (cred->refresh_time != 0 && now >= cred->refresh_time) { + set_refresh_time(context, cred->ccache, cred->refresh_time + 30); + return TRUE; + } + return FALSE; +} + +/* If appropriate, make a note to refresh cred from the client keytab when it + * is halfway to expired. */ +void +kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred, + krb5_ticket_times *times) +{ + krb5_timestamp refresh; + + /* For now, we only mark keytab-acquired credentials for refresh. */ + if (cred->password != NULL) + return; + + /* Make a note to refresh these when they are halfway to expired. */ + refresh = times->starttime + (times->endtime - times->starttime) / 2; + set_refresh_time(context, cred->ccache, refresh); +} + +/* Get initial credentials using the supplied password or client keytab. */ +static krb5_error_code +get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + krb5_get_init_creds_opt *opt = NULL; + krb5_creds creds; + + code = krb5_get_init_creds_opt_alloc(context, &opt); + if (code) + return code; + code = krb5_get_init_creds_opt_set_out_ccache(context, opt, cred->ccache); + if (code) + goto cleanup; + if (cred->password != NULL) { + code = krb5_get_init_creds_password(context, &creds, cred->name->princ, + cred->password, NULL, NULL, 0, + NULL, opt); + } else if (cred->client_keytab != NULL) { + code = krb5_get_init_creds_keytab(context, &creds, cred->name->princ, + cred->client_keytab, 0, NULL, opt); + } else { + code = KRB5_KT_NOTFOUND; + } + if (code) + goto cleanup; + kg_cred_set_initial_refresh(context, cred, &creds.times); + cred->have_tgt = TRUE; + cred->expire = creds.times.endtime; + krb5_free_cred_contents(context, &creds); +cleanup: + krb5_get_init_creds_opt_free(context, opt); + return code; +} + +/* Get initial credentials if we ought to and are able to. */ +static krb5_error_code +maybe_get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + + /* Don't get creds if we don't know the name or are doing IAKERB. */ + if (cred->name == NULL || cred->iakerb_mech) + return 0; + + /* Get creds if we have none or if it's time to refresh. */ + if (cred->expire == 0 || kg_cred_time_to_refresh(context, cred)) { + code = get_initial_cred(context, cred); + /* If we were trying to refresh and failed, we can keep going. */ + if (code && cred->expire == 0) + return code; + krb5_clear_error_message(context); + } + return 0; +} + +static OM_uint32 +acquire_init_cred(krb5_context context, + OM_uint32 *minor_status, + krb5_ccache req_ccache, + gss_buffer_t password, + krb5_keytab client_keytab, + krb5_gss_cred_id_rec *cred) +{ + krb5_error_code code; + krb5_data pwdata, pwcopy; + int caller_ccname = 0; + + /* Get ccache from caller if available. */ + if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) + return GSS_S_FAILURE; + if (GSS_ERROR(kg_caller_provided_ccache_name(minor_status, + &caller_ccname))) + return GSS_S_FAILURE; + + if (password != GSS_C_NO_BUFFER) { + pwdata = make_data(password->value, password->length); + code = krb5int_copy_data_contents_add0(context, &pwdata, &pwcopy); + if (code) + goto error; + cred->password = pwcopy.data; + + /* We will fetch the credential into a private memory ccache. */ + assert(req_ccache == NULL); + code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache); + if (code) + goto error; + cred->destroy_ccache = 1; + } else if (req_ccache != NULL) { + code = krb5_cc_dup(context, req_ccache, &cred->ccache); + if (code) + goto error; + } else if (caller_ccname) { + /* Caller's ccache name has been set as the context default. */ + code = krb5int_cc_default(context, &cred->ccache); + if (code) + goto error; + } + + if (client_keytab != NULL) { + code = krb5_kt_dup(context, client_keytab, &cred->client_keytab); + } else { + code = krb5_kt_client_default(context, &cred->client_keytab); + if (code) { + /* Treat resolution failure similarly to a client keytab which + * resolves but doesn't exist or has no content. */ + TRACE_GSS_CLIENT_KEYTAB_FAIL(context, code); + krb5_clear_error_message(context); + code = 0; + } + } + if (code) + goto error; + + if (cred->ccache != NULL) { + /* The caller specified a ccache; check what's in it. */ + code = scan_ccache(context, cred); + if (code == KRB5_FCC_NOFILE) { + /* See if we can get initial creds. If the caller didn't specify + * a name, pick one from the client keytab. */ + if (cred->name == NULL) { + if (!get_name_from_client_keytab(context, cred)) + code = 0; + } else if (can_get_initial_creds(context, cred)) { + code = 0; + } + } + if (code) + goto error; + } else if (cred->name != NULL) { + /* The caller specified a name but not a ccache; pick a cache. */ + code = get_cache_for_name(context, cred); + if (code) + goto error; + } + +#ifndef USE_LEASH + /* If we haven't picked a name, make sure we have or can get any creds, + * unless we're using Leash and might be able to get them interactively. */ + if (cred->name == NULL && !can_get_initial_creds(context, cred)) { + code = krb5_cccol_have_content(context); + if (code) + goto error; + } +#endif + + code = maybe_get_initial_cred(context, cred); + if (code) + goto error; + + *minor_status = 0; + return GSS_S_COMPLETE; + +error: + *minor_status = code; + return GSS_S_CRED_UNAVAIL; +} + +static OM_uint32 +acquire_cred_context(krb5_context context, OM_uint32 *minor_status, + gss_name_t desired_name, gss_buffer_t password, + OM_uint32 time_req, gss_cred_usage_t cred_usage, + krb5_ccache ccache, krb5_keytab client_keytab, + krb5_keytab keytab, const char *rcname, + krb5_boolean iakerb, gss_cred_id_t *output_cred_handle, + OM_uint32 *time_rec) +{ + krb5_gss_cred_id_t cred = NULL; + krb5_gss_name_t name = (krb5_gss_name_t)desired_name; + OM_uint32 ret; + krb5_error_code code = 0; + + /* make sure all outputs are valid */ + *output_cred_handle = GSS_C_NO_CREDENTIAL; + if (time_rec) + *time_rec = 0; + + /* create the gss cred structure */ + cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code); + if (cred == NULL) + goto krb_error_out; + + cred->usage = cred_usage; + cred->name = NULL; + cred->impersonator = NULL; + cred->iakerb_mech = iakerb; + cred->default_identity = (name == NULL); +#ifndef LEAN_CLIENT + cred->keytab = NULL; +#endif /* LEAN_CLIENT */ + cred->destroy_ccache = 0; + cred->suppress_ci_flags = 0; + cred->ccache = NULL; + + code = k5_mutex_init(&cred->lock); + if (code) + goto krb_error_out; + + switch (cred_usage) { + case GSS_C_INITIATE: + case GSS_C_ACCEPT: + case GSS_C_BOTH: + break; + default: + ret = GSS_S_FAILURE; + *minor_status = (OM_uint32) G_BAD_USAGE; + goto error_out; + } + + if (name != NULL) { + code = kg_duplicate_name(context, name, &cred->name); + if (code) + goto krb_error_out; + } + +#ifndef LEAN_CLIENT + /* + * If requested, acquire credentials for accepting. This will fill + * in cred->name if desired_princ is specified. + */ + if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { + ret = acquire_accept_cred(context, minor_status, keytab, rcname, cred); + if (ret != GSS_S_COMPLETE) + goto error_out; + } +#endif /* LEAN_CLIENT */ + + /* + * If requested, acquire credentials for initiation. This will fill + * in cred->name if it wasn't set above. + */ + if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { + ret = acquire_init_cred(context, minor_status, ccache, password, + client_keytab, cred); + if (ret != GSS_S_COMPLETE) + goto error_out; + } + + assert(cred->default_identity || cred->name != NULL); + + /*** at this point, the cred structure has been completely created */ + + if (cred_usage == GSS_C_ACCEPT) { + if (time_rec) + *time_rec = GSS_C_INDEFINITE; + } else { + krb5_timestamp now; + + code = krb5_timeofday(context, &now); + if (code != 0) + goto krb_error_out; + + if (time_rec) { + /* Resolve cred now to determine the expiration time. */ + ret = kg_cred_resolve(minor_status, context, (gss_cred_id_t)cred, + GSS_C_NO_NAME); + if (GSS_ERROR(ret)) + goto error_out; + *time_rec = (cred->expire > now) ? (cred->expire - now) : 0; + k5_mutex_unlock(&cred->lock); + } + } + + *minor_status = 0; + *output_cred_handle = (gss_cred_id_t) cred; + + return GSS_S_COMPLETE; + +krb_error_out: + *minor_status = code; + ret = GSS_S_FAILURE; + +error_out: + if (cred != NULL) { + if (cred->ccache) { + if (cred->destroy_ccache) + krb5_cc_destroy(context, cred->ccache); + else + krb5_cc_close(context, cred->ccache); + } + if (cred->client_keytab) + krb5_kt_close(context, cred->client_keytab); +#ifndef LEAN_CLIENT + if (cred->keytab) + krb5_kt_close(context, cred->keytab); +#endif /* LEAN_CLIENT */ + if (cred->rcache) + krb5_rc_close(context, cred->rcache); + if (cred->name) + kg_release_name(context, &cred->name); + krb5_free_principal(context, cred->impersonator); + zapfreestr(cred->password); + k5_mutex_destroy(&cred->lock); + xfree(cred); + } + save_error_info(*minor_status, context); + return ret; +} + +static OM_uint32 +acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name, + gss_buffer_t password, OM_uint32 time_req, + gss_cred_usage_t cred_usage, krb5_ccache ccache, + krb5_keytab keytab, krb5_boolean iakerb, + gss_cred_id_t *output_cred_handle, OM_uint32 *time_rec) +{ + krb5_context context = NULL; + krb5_error_code code = 0; + OM_uint32 ret; + + code = gss_krb5int_initialize_library(); + if (code) { + *minor_status = code; + ret = GSS_S_FAILURE; + goto out; + } + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + ret = GSS_S_FAILURE; + goto out; + } + + ret = acquire_cred_context(context, minor_status, desired_name, password, + time_req, cred_usage, ccache, NULL, keytab, + NULL, iakerb, output_cred_handle, time_rec); + +out: + krb5_free_context(context); + return ret; +} + +/* + * Resolve the name and ccache for an initiator credential if it has not yet + * been done. If specified, use the target name to pick an appropriate ccache + * within the collection. Validates cred_handle and leaves it locked on + * success. + */ +OM_uint32 +kg_cred_resolve(OM_uint32 *minor_status, krb5_context context, + gss_cred_id_t cred_handle, gss_name_t target_name) +{ + OM_uint32 maj; + krb5_error_code code; + krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)cred_handle; + krb5_gss_name_t tname = (krb5_gss_name_t)target_name; + krb5_principal client_princ; + + *minor_status = 0; + + maj = krb5_gss_validate_cred_1(minor_status, cred_handle, context); + if (maj != 0) + return maj; + k5_mutex_assert_locked(&cred->lock); + + if (cred->usage == GSS_C_ACCEPT || cred->name != NULL) + return GSS_S_COMPLETE; + /* acquire_init_cred should have set both name and ccache, or neither. */ + assert(cred->ccache == NULL); + + if (tname != NULL) { + /* Use the target name to select an existing ccache or a principal. */ + code = krb5_cc_select(context, tname->princ, &cred->ccache, + &client_princ); + if (code && code != KRB5_CC_NOTFOUND) + goto kerr; + if (client_princ != NULL) { + code = kg_init_name(context, client_princ, NULL, NULL, NULL, + KG_INIT_NAME_NO_COPY, &cred->name); + if (code) { + krb5_free_principal(context, client_princ); + goto kerr; + } + } + if (cred->ccache != NULL) { + code = scan_ccache(context, cred); + if (code) + goto kerr; + } + } + + /* If we still haven't picked a client principal, try using an existing + * default ccache. (On Windows, this may acquire initial creds.) */ + if (cred->name == NULL) { + code = krb5int_cc_default(context, &cred->ccache); + if (code) + goto kerr; + code = scan_ccache(context, cred); + if (code == KRB5_FCC_NOFILE) { + /* Default ccache doesn't exist; fall through to client keytab. */ + krb5_cc_close(context, cred->ccache); + cred->ccache = NULL; + } else if (code) { + goto kerr; + } + } + + /* If that didn't work, try getting a name from the client keytab. */ + if (cred->name == NULL) { + code = get_name_from_client_keytab(context, cred); + if (code) { + code = KG_EMPTY_CCACHE; + goto kerr; + } + } + + if (cred->name != NULL && cred->ccache == NULL) { + /* Pick a cache for the name we chose (from krb5_cc_select or from the + * client keytab). */ + code = get_cache_for_name(context, cred); + if (code) + goto kerr; + } + + /* Resolve name to ccache and possibly get initial credentials. */ + code = maybe_get_initial_cred(context, cred); + if (code) + goto kerr; + + return GSS_S_COMPLETE; + +kerr: + k5_mutex_unlock(&cred->lock); + save_error_info(code, context); + *minor_status = code; + return GSS_S_CRED_UNAVAIL; +} + +OM_uint32 +gss_krb5int_set_cred_rcache(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_oid, + const gss_buffer_t value) +{ + krb5_gss_cred_id_t cred; + krb5_error_code code; + krb5_context context; + krb5_rcache rcache; + + assert(value->length == sizeof(rcache)); + + if (value->length != sizeof(rcache)) + return GSS_S_FAILURE; + + rcache = (krb5_rcache)value->value; + + cred = (krb5_gss_cred_id_t)*cred_handle; + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + if (cred->rcache != NULL) { + code = krb5_rc_close(context, cred->rcache); + if (code) { + *minor_status = code; + krb5_free_context(context); + return GSS_S_FAILURE; + } + } + + cred->rcache = rcache; + + krb5_free_context(context); + + *minor_status = 0; + return GSS_S_COMPLETE; +} + +/* + * krb5 and IAKERB mech API functions follow. The mechglue always passes null + * desired_mechs and actual_mechs, so we ignore those parameters. + */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name, + OM_uint32 time_req, gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, OM_uint32 *time_rec) +{ + return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage, + NULL, NULL, FALSE, output_cred_handle, time_rec); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name, + OM_uint32 time_req, gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, OM_uint32 *time_rec) +{ + return acquire_cred(minor_status, desired_name, NULL, time_req, cred_usage, + NULL, NULL, TRUE, output_cred_handle, time_rec); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_acquire_cred_with_password(OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + return acquire_cred(minor_status, desired_name, password, time_req, + cred_usage, NULL, NULL, FALSE, output_cred_handle, + time_rec); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_acquire_cred_with_password(OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + return acquire_cred(minor_status, desired_name, password, time_req, + cred_usage, NULL, NULL, TRUE, output_cred_handle, + time_rec); +} + +OM_uint32 +gss_krb5int_import_cred(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_oid, + const gss_buffer_t value) +{ + struct krb5_gss_import_cred_req *req; + krb5_gss_name_rec name; + OM_uint32 time_rec; + krb5_error_code code; + gss_cred_usage_t usage; + gss_name_t desired_name = GSS_C_NO_NAME; + + assert(value->length == sizeof(*req)); + + if (value->length != sizeof(*req)) + return GSS_S_FAILURE; + + req = (struct krb5_gss_import_cred_req *)value->value; + + if (req->id != NULL) { + usage = (req->keytab != NULL) ? GSS_C_BOTH : GSS_C_INITIATE; + } else if (req->keytab != NULL) { + usage = GSS_C_ACCEPT; + } else { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + if (req->keytab_principal != NULL) { + memset(&name, 0, sizeof(name)); + code = k5_mutex_init(&name.lock); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + name.princ = req->keytab_principal; + desired_name = (gss_name_t)&name; + } + + code = acquire_cred(minor_status, desired_name, NULL, GSS_C_INDEFINITE, + usage, req->id, req->keytab, FALSE, cred_handle, + &time_rec); + if (req->keytab_principal != NULL) + k5_mutex_destroy(&name.lock); + return code; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_acquire_cred_from(OM_uint32 *minor_status, + const gss_name_t desired_name, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_const_key_value_set_t cred_store, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + krb5_context context = NULL; + krb5_error_code code = 0; + krb5_keytab client_keytab = NULL; + krb5_keytab keytab = NULL; + krb5_ccache ccache = NULL; + const char *rcname, *value; + OM_uint32 ret; + + code = gss_krb5int_initialize_library(); + if (code) { + *minor_status = code; + ret = GSS_S_FAILURE; + goto out; + } + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + ret = GSS_S_FAILURE; + goto out; + } + + ret = kg_value_from_cred_store(cred_store, KRB5_CS_CCACHE_URN, &value); + if (GSS_ERROR(ret)) + goto out; + + if (value) { + code = krb5_cc_resolve(context, value, &ccache); + if (code != 0) { + *minor_status = code; + ret = GSS_S_CRED_UNAVAIL; + goto out; + } + } + + ret = kg_value_from_cred_store(cred_store, KRB5_CS_CLI_KEYTAB_URN, &value); + if (GSS_ERROR(ret)) + goto out; + + if (value) { + code = krb5_kt_resolve(context, value, &client_keytab); + if (code != 0) { + *minor_status = code; + ret = GSS_S_CRED_UNAVAIL; + goto out; + } + } + + ret = kg_value_from_cred_store(cred_store, KRB5_CS_KEYTAB_URN, &value); + if (GSS_ERROR(ret)) + goto out; + + if (value) { + code = krb5_kt_resolve(context, value, &keytab); + if (code != 0) { + *minor_status = code; + ret = GSS_S_CRED_UNAVAIL; + goto out; + } + } + + ret = kg_value_from_cred_store(cred_store, KRB5_CS_RCACHE_URN, &rcname); + if (GSS_ERROR(ret)) + goto out; + + ret = acquire_cred_context(context, minor_status, desired_name, NULL, + time_req, cred_usage, ccache, client_keytab, + keytab, rcname, 0, output_cred_handle, + time_rec); + +out: + if (ccache != NULL) + krb5_cc_close(context, ccache); + if (client_keytab != NULL) + krb5_kt_close(context, client_keytab); + if (keytab != NULL) + krb5_kt_close(context, keytab); + krb5_free_context(context); + return ret; +} diff --git a/src/lib/gssapi/krb5/canon_name.c b/src/lib/gssapi/krb5/canon_name.c new file mode 100644 index 000000000000..81b08a534ec1 --- /dev/null +++ b/src/lib/gssapi/krb5/canon_name.c @@ -0,0 +1,44 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/canon_name.c */ +/* + * Copyright 1997 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "gssapiP_krb5.h" + +/* This is trivial since we're a single mechanism implementation */ + +OM_uint32 krb5_gss_canonicalize_name(OM_uint32 *minor_status, + const gss_name_t input_name, + const gss_OID mech_type, + gss_name_t *output_name) +{ + if ((mech_type != GSS_C_NULL_OID) && + !g_OID_equal(gss_mech_krb5, mech_type) && + !g_OID_equal(gss_mech_krb5_old, mech_type)) { + *minor_status = 0; + return(GSS_S_BAD_MECH); + } + + return(krb5_gss_duplicate_name(minor_status, input_name, output_name)); +} diff --git a/src/lib/gssapi/krb5/compare_name.c b/src/lib/gssapi/krb5/compare_name.c new file mode 100644 index 000000000000..3f3788d2bf1c --- /dev/null +++ b/src/lib/gssapi/krb5/compare_name.c @@ -0,0 +1,52 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_compare_name(minor_status, name1, name2, name_equal) + OM_uint32 *minor_status; + gss_name_t name1; + gss_name_t name2; + int *name_equal; +{ + krb5_context context; + krb5_error_code code; + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + *minor_status = 0; + *name_equal = kg_compare_name(context, + (krb5_gss_name_t)name1, + (krb5_gss_name_t)name2); + krb5_free_context(context); + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/context_time.c b/src/lib/gssapi/krb5/context_time.c new file mode 100644 index 000000000000..a18cfb05b743 --- /dev/null +++ b/src/lib/gssapi/krb5/context_time.c @@ -0,0 +1,63 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +/* + * $Id$ + */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_context_time(minor_status, context_handle, time_rec) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + OM_uint32 *time_rec; +{ + krb5_error_code code; + krb5_gss_ctx_id_rec *ctx; + krb5_timestamp now; + krb5_deltat lifetime; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + if ((code = krb5_timeofday(ctx->k5_context, &now))) { + *minor_status = code; + save_error_info(*minor_status, ctx->k5_context); + return(GSS_S_FAILURE); + } + + if ((lifetime = ctx->krb_times.endtime - now) <= 0) { + *time_rec = 0; + *minor_status = 0; + return(GSS_S_CONTEXT_EXPIRED); + } else { + *time_rec = lifetime; + *minor_status = 0; + return(GSS_S_COMPLETE); + } +} diff --git a/src/lib/gssapi/krb5/copy_ccache.c b/src/lib/gssapi/krb5/copy_ccache.c new file mode 100644 index 000000000000..f3d7666135cd --- /dev/null +++ b/src/lib/gssapi/krb5/copy_ccache.c @@ -0,0 +1,60 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +#include "gssapiP_krb5.h" + +OM_uint32 +gss_krb5int_copy_ccache(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ + krb5_gss_cred_id_t k5creds; + krb5_cc_cursor cursor; + krb5_creds creds; + krb5_error_code code; + krb5_context context; + krb5_ccache out_ccache; + + assert(value->length == sizeof(out_ccache)); + + if (value->length != sizeof(out_ccache)) + return GSS_S_FAILURE; + + out_ccache = (krb5_ccache)value->value; + + /* cred handle will have been validated by gssspi_set_cred_option() */ + k5creds = (krb5_gss_cred_id_t) *cred_handle; + k5_mutex_lock(&k5creds->lock); + if (k5creds->usage == GSS_C_ACCEPT) { + k5_mutex_unlock(&k5creds->lock); + *minor_status = (OM_uint32) G_BAD_USAGE; + return(GSS_S_FAILURE); + } + + code = krb5_gss_init_context(&context); + if (code) { + k5_mutex_unlock(&k5creds->lock); + *minor_status = code; + return GSS_S_FAILURE; + } + + code = krb5_cc_start_seq_get(context, k5creds->ccache, &cursor); + if (code) { + k5_mutex_unlock(&k5creds->lock); + *minor_status = code; + save_error_info(*minor_status, context); + krb5_free_context(context); + return(GSS_S_FAILURE); + } + while (!code && !krb5_cc_next_cred(context, k5creds->ccache, &cursor, + &creds)) { + code = krb5_cc_store_cred(context, out_ccache, &creds); + krb5_free_cred_contents(context, &creds); + } + krb5_cc_end_seq_get(context, k5creds->ccache, &cursor); + k5_mutex_unlock(&k5creds->lock); + *minor_status = code; + if (code) + save_error_info(*minor_status, context); + krb5_free_context(context); + return code ? GSS_S_FAILURE : GSS_S_COMPLETE; +} diff --git a/src/lib/gssapi/krb5/cred_store.c b/src/lib/gssapi/krb5/cred_store.c new file mode 100644 index 000000000000..008dd208bf0c --- /dev/null +++ b/src/lib/gssapi/krb5/cred_store.c @@ -0,0 +1,50 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2011 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 +kg_value_from_cred_store(gss_const_key_value_set_t cred_store, + const char *type, const char **value) +{ + OM_uint32 i; + + if (value == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + *value = NULL; + + if (cred_store == GSS_C_NO_CRED_STORE) + return GSS_S_COMPLETE; + + for (i = 0; i < cred_store->count; i++) { + if (strcmp(cred_store->elements[i].key, type) == 0) { + if (*value != NULL) + return GSS_S_DUPLICATE_ELEMENT; + *value = cred_store->elements[i].value; + } + } + + return GSS_S_COMPLETE; +} diff --git a/src/lib/gssapi/krb5/delete_sec_context.c b/src/lib/gssapi/krb5/delete_sec_context.c new file mode 100644 index 000000000000..4b9dfae0d515 --- /dev/null +++ b/src/lib/gssapi/krb5/delete_sec_context.c @@ -0,0 +1,99 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +/* + * $Id$ + */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_delete_sec_context(minor_status, context_handle, output_token) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_buffer_t output_token; +{ + krb5_context context; + krb5_gss_ctx_id_rec *ctx; + + if (output_token) { + output_token->length = 0; + output_token->value = NULL; + } + + /*SUPPRESS 29*/ + if (*context_handle == GSS_C_NO_CONTEXT) { + *minor_status = 0; + return(GSS_S_COMPLETE); + } + + ctx = (krb5_gss_ctx_id_t) *context_handle; + context = ctx->k5_context; + + /* free all the context state */ + + if (ctx->seqstate) + g_seqstate_free(ctx->seqstate); + + if (ctx->enc) + krb5_k_free_key(context, ctx->enc); + + if (ctx->seq) + krb5_k_free_key(context, ctx->seq); + + if (ctx->here) + kg_release_name(context, &ctx->here); + if (ctx->there) + kg_release_name(context, &ctx->there); + if (ctx->subkey) + krb5_k_free_key(context, ctx->subkey); + if (ctx->acceptor_subkey) + krb5_k_free_key(context, ctx->acceptor_subkey); + + if (ctx->auth_context) { + if (ctx->cred_rcache) + (void)krb5_auth_con_setrcache(context, ctx->auth_context, NULL); + + krb5_auth_con_free(context, ctx->auth_context); + } + + if (ctx->mech_used) + krb5_gss_release_oid(minor_status, &ctx->mech_used); + + if (ctx->authdata) + krb5_free_authdata(context, ctx->authdata); + + if (ctx->k5_context) + krb5_free_context(ctx->k5_context); + + /* Zero out context */ + zap(ctx, sizeof(*ctx)); + xfree(ctx); + + /* zero the handle itself */ + + *context_handle = GSS_C_NO_CONTEXT; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/deps b/src/lib/gssapi/krb5/deps new file mode 100644 index 000000000000..f42f40ae7727 --- /dev/null +++ b/src/lib/gssapi/krb5/deps @@ -0,0 +1,756 @@ +# +# Generated makefile dependencies follow. +# +accept_sec_context.so accept_sec_context.po $(OUTPRE)accept_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h accept_sec_context.c \ + gssapiP_krb5.h gssapi_err_krb5.h gssapi_krb5.h +acquire_cred.so acquire_cred.po $(OUTPRE)acquire_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h acquire_cred.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +canon_name.so canon_name.po $(OUTPRE)canon_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h canon_name.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +compare_name.so compare_name.po $(OUTPRE)compare_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h compare_name.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +context_time.so context_time.po $(OUTPRE)context_time.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h context_time.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +copy_ccache.so copy_ccache.po $(OUTPRE)copy_ccache.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h copy_ccache.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +cred_store.so cred_store.po $(OUTPRE)cred_store.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h cred_store.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +delete_sec_context.so delete_sec_context.po $(OUTPRE)delete_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h delete_sec_context.c \ + gssapiP_krb5.h gssapi_err_krb5.h gssapi_krb5.h +disp_name.so disp_name.po $(OUTPRE)disp_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h disp_name.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +disp_status.so disp_status.po $(OUTPRE)disp_status.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h disp_status.c error_map.h \ + gssapiP_krb5.h gssapi_err_krb5.h gssapi_krb5.h +duplicate_name.so duplicate_name.po $(OUTPRE)duplicate_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h duplicate_name.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +export_cred.so export_cred.po $(OUTPRE)export_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-json.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h ../generic/gssapi_err_generic.h \ + export_cred.c gssapiP_krb5.h gssapi_err_krb5.h gssapi_krb5.h +export_name.so export_name.po $(OUTPRE)export_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h export_name.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +export_sec_context.so export_sec_context.po $(OUTPRE)export_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h export_sec_context.c \ + gssapiP_krb5.h gssapi_err_krb5.h gssapi_krb5.h +get_tkt_flags.so get_tkt_flags.po $(OUTPRE)get_tkt_flags.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h get_tkt_flags.c gssapiP_krb5.h \ + gssapi_err_krb5.h gssapi_krb5.h +gssapi_krb5.so gssapi_krb5.po $(OUTPRE)gssapi_krb5.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(srcdir)/../mechglue/mechglue.h $(srcdir)/../mechglue/mglueP.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.c gssapi_krb5.h +iakerb.so iakerb.po $(OUTPRE)iakerb.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h iakerb.c +import_cred.so import_cred.po $(OUTPRE)import_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-json.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h ../generic/gssapi_err_generic.h \ + gssapiP_krb5.h gssapi_err_krb5.h gssapi_krb5.h import_cred.c +import_name.so import_name.po $(OUTPRE)import_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h import_name.c +import_sec_context.so import_sec_context.po $(OUTPRE)import_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h import_sec_context.c +indicate_mechs.so indicate_mechs.po $(OUTPRE)indicate_mechs.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(srcdir)/../mechglue/mechglue.h $(srcdir)/../mechglue/mglueP.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h indicate_mechs.c +init_sec_context.so init_sec_context.po $(OUTPRE)init_sec_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h init_sec_context.c +inq_context.so inq_context.po $(OUTPRE)inq_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h inq_context.c +inq_cred.so inq_cred.po $(OUTPRE)inq_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h inq_cred.c +inq_names.so inq_names.po $(OUTPRE)inq_names.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h inq_names.c +k5seal.so k5seal.po $(OUTPRE)k5seal.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h k5seal.c +k5sealiov.so k5sealiov.po $(OUTPRE)k5sealiov.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h k5sealiov.c +k5sealv3.so k5sealv3.po $(OUTPRE)k5sealv3.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h k5sealv3.c +k5sealv3iov.so k5sealv3iov.po $(OUTPRE)k5sealv3iov.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h k5sealv3iov.c +k5unseal.so k5unseal.po $(OUTPRE)k5unseal.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h k5unseal.c +k5unsealiov.so k5unsealiov.po $(OUTPRE)k5unsealiov.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h k5unsealiov.c +krb5_gss_glue.so krb5_gss_glue.po $(OUTPRE)krb5_gss_glue.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h krb5_gss_glue.c +lucid_context.so lucid_context.po $(OUTPRE)lucid_context.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h lucid_context.c +naming_exts.so naming_exts.po $(OUTPRE)naming_exts.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h naming_exts.c +prf.so prf.po $(OUTPRE)prf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h prf.c +process_context_token.so process_context_token.po $(OUTPRE)process_context_token.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h process_context_token.c +rel_cred.so rel_cred.po $(OUTPRE)rel_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h rel_cred.c +rel_oid.so rel_oid.po $(OUTPRE)rel_oid.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h rel_oid.c +rel_name.so rel_name.po $(OUTPRE)rel_name.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h rel_name.c +s4u_gss_glue.so s4u_gss_glue.po $(OUTPRE)s4u_gss_glue.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h s4u_gss_glue.c +set_allowable_enctypes.so set_allowable_enctypes.po \ + $(OUTPRE)set_allowable_enctypes.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h set_allowable_enctypes.c +ser_sctx.so ser_sctx.po $(OUTPRE)ser_sctx.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h ser_sctx.c +set_ccache.so set_ccache.po $(OUTPRE)set_ccache.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h set_ccache.c +store_cred.so store_cred.po $(OUTPRE)store_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h store_cred.c +util_cksum.so util_cksum.po $(OUTPRE)util_cksum.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h util_cksum.c +util_crypt.so util_crypt.po $(OUTPRE)util_crypt.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h util_crypt.c +util_seed.so util_seed.po $(OUTPRE)util_seed.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h util_seed.c +util_seqnum.so util_seqnum.po $(OUTPRE)util_seqnum.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h util_seqnum.c +val_cred.so val_cred.po $(OUTPRE)val_cred.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h val_cred.c +wrap_size_limit.so wrap_size_limit.po $(OUTPRE)wrap_size_limit.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_alloc.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../generic/gssapiP_generic.h \ + $(srcdir)/../generic/gssapi_ext.h $(srcdir)/../generic/gssapi_generic.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + ../generic/gssapi_err_generic.h gssapiP_krb5.h gssapi_err_krb5.h \ + gssapi_krb5.h wrap_size_limit.c diff --git a/src/lib/gssapi/krb5/disp_name.c b/src/lib/gssapi/krb5/disp_name.c new file mode 100644 index 000000000000..b097bf0e2181 --- /dev/null +++ b/src/lib/gssapi/krb5/disp_name.c @@ -0,0 +1,81 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_display_name(minor_status, input_name, output_name_buffer, + output_name_type) + OM_uint32 *minor_status; + gss_name_t input_name; + gss_buffer_t output_name_buffer; + gss_OID *output_name_type; +{ + krb5_context context; + krb5_error_code code; + char *str; + krb5_gss_name_t k5name = (krb5_gss_name_t) input_name; + gss_OID nametype = (gss_OID) gss_nt_krb5_name; + + output_name_buffer->length = 0; + output_name_buffer->value = NULL; + if (output_name_type) + *output_name_type = GSS_C_NO_OID; + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + if (krb5_princ_type(context, k5name->princ) == KRB5_NT_WELLKNOWN) { + if (krb5_principal_compare(context, k5name->princ, + krb5_anonymous_principal())) + nametype = GSS_C_NT_ANONYMOUS; + } + + if ((code = krb5_unparse_name(context, + ((krb5_gss_name_t) input_name)->princ, + &str))) { + *minor_status = code; + save_error_info(*minor_status, context); + krb5_free_context(context); + return(GSS_S_FAILURE); + } + + if (! g_make_string_buffer(str, output_name_buffer)) { + krb5_free_unparsed_name(context, str); + krb5_free_context(context); + + *minor_status = (OM_uint32) G_BUFFER_ALLOC; + return(GSS_S_FAILURE); + } + + krb5_free_unparsed_name(context, str); + krb5_free_context(context); + + *minor_status = 0; + if (output_name_type) + *output_name_type = (gss_OID) nametype; + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/disp_status.c b/src/lib/gssapi/krb5/disp_status.c new file mode 100644 index 000000000000..6ff62a9d8415 --- /dev/null +++ b/src/lib/gssapi/krb5/disp_status.c @@ -0,0 +1,200 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" +#include "com_err.h" + +/* XXXX internationalization!! */ + +static inline int +compare_OM_uint32 (OM_uint32 a, OM_uint32 b) +{ + if (a < b) + return -1; + else if (a == b) + return 0; + else + return 1; +} +static inline void +free_string (char *s) +{ + free(s); +} +#include "error_map.h" +#include <stdio.h> +char *get_error_message(OM_uint32 minor_code) +{ + gsserrmap *p = k5_getspecific(K5_KEY_GSS_KRB5_ERROR_MESSAGE); + char *msg = NULL; +#ifdef DEBUG + fprintf(stderr, "%s(%lu, p=%p)", __FUNCTION__, (unsigned long) minor_code, + (void *) p); +#endif + if (p) { + char **v = gsserrmap_find(p, minor_code); + if (v) { + msg = *v; +#ifdef DEBUG + fprintf(stderr, " FOUND!"); +#endif + } + } + if (msg == 0) + msg = (char *)error_message((krb5_error_code)minor_code); +#ifdef DEBUG + fprintf(stderr, " -> %p/%s\n", (void *) msg, msg); +#endif + return msg; +} +#define save_error_string_nocopy gss_krb5_save_error_string_nocopy +static int save_error_string_nocopy(OM_uint32 minor_code, char *msg) +{ + gsserrmap *p; + int ret; + +#ifdef DEBUG + fprintf(stderr, "%s(%lu, %s)", __FUNCTION__, (unsigned long) minor_code, msg); +#endif + p = k5_getspecific(K5_KEY_GSS_KRB5_ERROR_MESSAGE); + if (!p) { + p = malloc(sizeof(*p)); + if (p == NULL) { + ret = 1; + goto fail; + } + if (gsserrmap_init(p) != 0) { + free(p); + p = NULL; + ret = 1; + goto fail; + } + if (k5_setspecific(K5_KEY_GSS_KRB5_ERROR_MESSAGE, p) != 0) { + gsserrmap_destroy(p); + free(p); + p = NULL; + ret = 1; + goto fail; + } + } + ret = gsserrmap_replace_or_insert(p, minor_code, msg); +fail: +#ifdef DEBUG + fprintf(stderr, " p=%p %s\n", (void *)p, ret ? "FAIL" : "SUCCESS"); +#endif + return ret; +} +void save_error_string(OM_uint32 minor_code, char *msg) +{ + char *s = strdup(msg); + if (s) { + if (save_error_string_nocopy(minor_code, s) != 0) + free(s); + } +} +void save_error_message(OM_uint32 minor_code, const char *format, ...) +{ + char *s; + int n; + va_list ap; + + va_start(ap, format); + n = vasprintf(&s, format, ap); + va_end(ap); + if (n >= 0) { + if (save_error_string_nocopy(minor_code, s) != 0) + free(s); + } +} +void krb5_gss_save_error_info(OM_uint32 minor_code, krb5_context ctx) +{ + char *s; + +#ifdef DEBUG + fprintf(stderr, "%s(%lu, ctx=%p)\n", __FUNCTION__, + (unsigned long) minor_code, (void *)ctx); +#endif + s = (char *)krb5_get_error_message(ctx, (krb5_error_code)minor_code); +#ifdef DEBUG + fprintf(stderr, "%s(%lu, ctx=%p) saving: %s\n", __FUNCTION__, + (unsigned long) minor_code, (void *)ctx, s); +#endif + save_error_string(minor_code, s); + /* The get_error_message call above resets the error message in + ctx. Put it back, in case we make this call again *sigh*. */ + k5_setmsg(ctx, (krb5_error_code)minor_code, "%s", s); + krb5_free_error_message(ctx, s); +} +void krb5_gss_delete_error_info(void *p) +{ + gsserrmap_destroy(p); + free(p); +} + +/**/ + +OM_uint32 KRB5_CALLCONV +krb5_gss_display_status(minor_status, status_value, status_type, + mech_type, message_context, status_string) + OM_uint32 *minor_status; + OM_uint32 status_value; + int status_type; + gss_OID mech_type; + OM_uint32 *message_context; + gss_buffer_t status_string; +{ + status_string->length = 0; + status_string->value = NULL; + + if ((mech_type != GSS_C_NULL_OID) && + !g_OID_equal(gss_mech_krb5, mech_type) && + !g_OID_equal(gss_mech_krb5_old, mech_type) && + !g_OID_equal(gss_mech_iakerb, mech_type)) { + *minor_status = 0; + return(GSS_S_BAD_MECH); + } + + if (status_type == GSS_C_GSS_CODE) { + return(g_display_major_status(minor_status, status_value, + message_context, status_string)); + } else if (status_type == GSS_C_MECH_CODE) { + (void) gss_krb5int_initialize_library(); + + if (*message_context) { + *minor_status = (OM_uint32) G_BAD_MSG_CTX; + return(GSS_S_FAILURE); + } + + /* If this fails, there's not much we can do... */ + if (!g_make_string_buffer(krb5_gss_get_error_message(status_value), + status_string)) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + *minor_status = 0; + return(GSS_S_COMPLETE); + } else { + *minor_status = 0; + return(GSS_S_BAD_STATUS); + } +} diff --git a/src/lib/gssapi/krb5/duplicate_name.c b/src/lib/gssapi/krb5/duplicate_name.c new file mode 100644 index 000000000000..b88d97d9d617 --- /dev/null +++ b/src/lib/gssapi/krb5/duplicate_name.c @@ -0,0 +1,59 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/duplicate_name.c */ +/* + * Copyright 1997,2007 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_duplicate_name(OM_uint32 *minor_status, const gss_name_t input_name, + gss_name_t *dest_name) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t princ, outprinc; + + if (minor_status) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code) { + if (minor_status) + *minor_status = code; + return GSS_S_FAILURE; + } + + princ = (krb5_gss_name_t)input_name; + code = kg_duplicate_name(context, princ, &outprinc); + if (code) { + *minor_status = code; + save_error_info(*minor_status, context); + krb5_free_context(context); + return(GSS_S_FAILURE); + } + krb5_free_context(context); + *dest_name = (gss_name_t) outprinc; + return(GSS_S_COMPLETE); + +} diff --git a/src/lib/gssapi/krb5/export_cred.c b/src/lib/gssapi/krb5/export_cred.c new file mode 100644 index 000000000000..652b2604bda3 --- /dev/null +++ b/src/lib/gssapi/krb5/export_cred.c @@ -0,0 +1,481 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/export_cred.c - krb5 export_cred implementation */ +/* + * Copyright (C) 2012 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "k5-int.h" +#include "k5-json.h" +#include "gssapiP_krb5.h" + +/* Return a JSON null or array value representing princ. */ +static krb5_error_code +json_principal(krb5_context context, krb5_principal princ, + k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_string str = NULL; + char *princname; + + *val_out = NULL; + if (princ == NULL) + return k5_json_null_create_val(val_out); + ret = krb5_unparse_name(context, princ, &princname); + if (ret) + return ret; + ret = k5_json_string_create(princname, &str); + krb5_free_unparsed_name(context, princname); + *val_out = str; + return ret; +} + +/* Return a json null or array value representing etypes. */ +static krb5_error_code +json_etypes(krb5_enctype *etypes, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_number num; + k5_json_array array; + + *val_out = NULL; + if (etypes == NULL) + return k5_json_null_create_val(val_out); + ret = k5_json_array_create(&array); + if (ret) + return ret; + for (; *etypes != 0; etypes++) { + ret = k5_json_number_create(*etypes, &num); + if (ret) + goto err; + ret = k5_json_array_add(array, num); + k5_json_release(num); + if (ret) + goto err; + } + *val_out = array; + return 0; +err: + k5_json_release(array); + return ret; +} + +/* Return a JSON null or array value representing name. */ +static krb5_error_code +json_kgname(krb5_context context, krb5_gss_name_t name, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array = NULL; + k5_json_value princ; + + *val_out = NULL; + if (name == NULL) + return k5_json_null_create_val(val_out); + ret = json_principal(context, name->princ, &princ); + if (ret) + return ret; + ret = k5_json_array_fmt(&array, "vss", princ, name->service, name->host); + k5_json_release(princ); + *val_out = array; + return ret; +} + +/* Return a JSON null or string value representing keytab. */ +static krb5_error_code +json_keytab(krb5_context context, krb5_keytab keytab, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_string str; + char name[1024]; + + *val_out = NULL; + if (keytab == NULL) + return k5_json_null_create_val(val_out); + ret = krb5_kt_get_name(context, keytab, name, sizeof(name)); + if (ret) + return ret; + ret = k5_json_string_create(name, &str); + *val_out = str; + return ret; +} + +/* Return a JSON null or string value representing rcache. */ +static krb5_error_code +json_rcache(krb5_context context, krb5_rcache rcache, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_string str = NULL; + char *name; + + if (rcache == NULL) + return k5_json_null_create_val(val_out); + if (asprintf(&name, "%s:%s", krb5_rc_get_type(context, rcache), + krb5_rc_get_name(context, rcache)) < 0) + return ENOMEM; + ret = k5_json_string_create(name, &str); + free(name); + *val_out = str; + return ret; +} + +/* Return a JSON array value representing keyblock. */ +static krb5_error_code +json_keyblock(krb5_keyblock *kb, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + + *val_out = NULL; + ret = k5_json_array_fmt(&array, "iB", kb->enctype, (void *)kb->contents, + (size_t)kb->length); + if (ret) + return ret; + *val_out = array; + return 0; +} + +/* Return a JSON array value representing addr. */ +static krb5_error_code +json_address(krb5_address *addr, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + + *val_out = NULL; + ret = k5_json_array_fmt(&array, "iB", addr->addrtype, + (void *)addr->contents, (size_t)addr->length); + if (ret) + return ret; + *val_out = array; + return 0; +} + +/* Return a JSON null or array value representing addrs. */ +static krb5_error_code +json_addresses(krb5_address **addrs, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + k5_json_value val; + + *val_out = NULL; + if (addrs == NULL) + return k5_json_null_create_val(val_out); + ret = k5_json_array_create(&array); + if (ret) + return ret; + for (; *addrs != NULL; addrs++) { + ret = json_address(*addrs, &val); + if (ret) + goto err; + ret = k5_json_array_add(array, val); + k5_json_release(val); + if (ret) + goto err; + } + *val_out = array; + return 0; +err: + k5_json_release(array); + return ret; +} + +/* Return a JSON array value representing ad. */ +static krb5_error_code +json_authdata_element(krb5_authdata *ad, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + + *val_out = NULL; + ret = k5_json_array_fmt(&array, "iB", ad->ad_type, (void *)ad->contents, + (size_t)ad->length); + if (ret) + return ret; + *val_out = array; + return 0; +} + +/* Return a JSON null or array value representing authdata. */ +static krb5_error_code +json_authdata(krb5_authdata **authdata, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + k5_json_value val; + + *val_out = NULL; + if (authdata == NULL) + return k5_json_null_create_val(val_out); + ret = k5_json_array_create(&array); + if (ret) + return ret; + for (; *authdata != NULL; authdata++) { + ret = json_authdata_element(*authdata, &val); + if (ret) + goto err; + ret = k5_json_array_add(array, val); + k5_json_release(val); + if (ret) + goto err; + } + *val_out = array; + return 0; +err: + k5_json_release(array); + return ret; +} + +/* Return a JSON array value representing creds. */ +static krb5_error_code +json_creds(krb5_context context, krb5_creds *creds, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + k5_json_value client = NULL, server = NULL, keyblock = NULL, addrs = NULL; + k5_json_value authdata = NULL; + + *val_out = NULL; + ret = json_principal(context, creds->client, &client); + if (ret) + goto cleanup; + ret = json_principal(context, creds->server, &server); + if (ret) + goto cleanup; + ret = json_keyblock(&creds->keyblock, &keyblock); + if (ret) + goto cleanup; + ret = json_addresses(creds->addresses, &addrs); + if (ret) + goto cleanup; + ret = json_authdata(creds->authdata, &authdata); + if (ret) + goto cleanup; + + ret = k5_json_array_fmt(&array, "vvviiiibivBBv", client, server, keyblock, + creds->times.authtime, creds->times.starttime, + creds->times.endtime, creds->times.renew_till, + creds->is_skey, creds->ticket_flags, addrs, + (void *)creds->ticket.data, + (size_t)creds->ticket.length, + (void *)creds->second_ticket.data, + (size_t)creds->second_ticket.length, authdata); + if (ret) + goto cleanup; + *val_out = array; + +cleanup: + k5_json_release(client); + k5_json_release(server); + k5_json_release(keyblock); + k5_json_release(addrs); + k5_json_release(authdata); + return ret; +} + +/* Return a JSON array value representing the contents of ccache. */ +static krb5_error_code +json_ccache_contents(krb5_context context, krb5_ccache ccache, + k5_json_value *val_out) +{ + krb5_error_code ret; + krb5_principal princ; + krb5_cc_cursor cursor; + krb5_creds creds; + k5_json_array array; + k5_json_value val; + + *val_out = NULL; + ret = k5_json_array_create(&array); + if (ret) + return ret; + + /* Put the principal in the first array entry. */ + ret = krb5_cc_get_principal(context, ccache, &princ); + if (ret) + goto err; + ret = json_principal(context, princ, &val); + krb5_free_principal(context, princ); + if (ret) + goto err; + ret = k5_json_array_add(array, val); + k5_json_release(val); + if (ret) + goto err; + + /* Put credentials in the remaining array entries. */ + ret = krb5_cc_start_seq_get(context, ccache, &cursor); + if (ret) + goto err; + while ((ret = krb5_cc_next_cred(context, ccache, &cursor, &creds)) == 0) { + ret = json_creds(context, &creds, &val); + krb5_free_cred_contents(context, &creds); + if (ret) + break; + ret = k5_json_array_add(array, val); + k5_json_release(val); + if (ret) + break; + } + krb5_cc_end_seq_get(context, ccache, &cursor); + if (ret != KRB5_CC_END) + goto err; + *val_out = array; + return 0; + +err: + k5_json_release(array); + return ret; +} + +/* Return a JSON null, string, or array value representing ccache. */ +static krb5_error_code +json_ccache(krb5_context context, krb5_ccache ccache, k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_string str; + char *name; + + *val_out = NULL; + if (ccache == NULL) + return k5_json_null_create_val(val_out); + if (strcmp(krb5_cc_get_type(context, ccache), "MEMORY") == 0) { + return json_ccache_contents(context, ccache, val_out); + } else { + ret = krb5_cc_get_full_name(context, ccache, &name); + if (ret) + return ret; + ret = k5_json_string_create(name, &str); + free(name); + *val_out = str; + return ret; + } +} + +/* Return a JSON array value representing cred. */ +static krb5_error_code +json_kgcred(krb5_context context, krb5_gss_cred_id_t cred, + k5_json_value *val_out) +{ + krb5_error_code ret; + k5_json_array array; + k5_json_value name = NULL, imp = NULL, keytab = NULL, rcache = NULL; + k5_json_value ccache = NULL, ckeytab = NULL, etypes = NULL; + + *val_out = NULL; + ret = json_kgname(context, cred->name, &name); + if (ret) + goto cleanup; + ret = json_principal(context, cred->impersonator, &imp); + if (ret) + goto cleanup; + ret = json_keytab(context, cred->keytab, &keytab); + if (ret) + goto cleanup; + ret = json_rcache(context, cred->rcache, &rcache); + if (ret) + goto cleanup; + ret = json_ccache(context, cred->ccache, &ccache); + if (ret) + goto cleanup; + ret = json_keytab(context, cred->client_keytab, &ckeytab); + if (ret) + goto cleanup; + ret = json_etypes(cred->req_enctypes, &etypes); + if (ret) + goto cleanup; + + ret = k5_json_array_fmt(&array, "ivvbbvvvvbiivs", cred->usage, name, imp, + cred->default_identity, cred->iakerb_mech, keytab, + rcache, ccache, ckeytab, cred->have_tgt, + cred->expire, cred->refresh_time, etypes, + cred->password); + if (ret) + goto cleanup; + *val_out = array; + +cleanup: + k5_json_release(name); + k5_json_release(imp); + k5_json_release(keytab); + k5_json_release(rcache); + k5_json_release(ccache); + k5_json_release(ckeytab); + k5_json_release(etypes); + return ret; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle, + gss_buffer_t token) +{ + OM_uint32 status = GSS_S_COMPLETE; + krb5_context context; + krb5_error_code ret; + krb5_gss_cred_id_t cred; + k5_json_array array = NULL; + k5_json_value jcred = NULL; + char *str = NULL; + krb5_data d; + + ret = krb5_gss_init_context(&context); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + + /* Validate and lock cred_handle. */ + status = krb5_gss_validate_cred_1(minor_status, cred_handle, context); + if (status != GSS_S_COMPLETE) + return status; + cred = (krb5_gss_cred_id_t)cred_handle; + + if (json_kgcred(context, cred, &jcred)) + goto oom; + if (k5_json_array_fmt(&array, "sv", CRED_EXPORT_MAGIC, jcred)) + goto oom; + if (k5_json_encode(array, &str)) + goto oom; + d = string2data(str); + if (data_to_gss(&d, token)) + goto oom; + str = NULL; + +cleanup: + free(str); + k5_mutex_unlock(&cred->lock); + k5_json_release(array); + k5_json_release(jcred); + krb5_free_context(context); + return status; + +oom: + *minor_status = ENOMEM; + status = GSS_S_FAILURE; + goto cleanup; +} diff --git a/src/lib/gssapi/krb5/export_name.c b/src/lib/gssapi/krb5/export_name.c new file mode 100644 index 000000000000..485ebfb6cdf3 --- /dev/null +++ b/src/lib/gssapi/krb5/export_name.c @@ -0,0 +1,88 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/export_name.c */ +/* + * Copyright 1997, 2007 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_export_name(OM_uint32 *minor_status, const gss_name_t input_name, + gss_buffer_t exported_name) +{ + krb5_context context; + krb5_error_code code; + size_t length; + char *str; + unsigned char *cp; + + if (minor_status) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code) { + if (minor_status) + *minor_status = code; + return GSS_S_FAILURE; + } + + exported_name->length = 0; + exported_name->value = NULL; + + if ((code = krb5_unparse_name(context, ((krb5_gss_name_t) input_name)->princ, + &str))) { + if (minor_status) + *minor_status = code; + save_error_info((OM_uint32)code, context); + krb5_free_context(context); + return(GSS_S_FAILURE); + } + + krb5_free_context(context); + length = strlen(str); + exported_name->length = 10 + length + gss_mech_krb5->length; + exported_name->value = gssalloc_malloc(exported_name->length); + if (!exported_name->value) { + free(str); + if (minor_status) + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + cp = exported_name->value; + + /* Note: we assume the OID will be less than 128 bytes... */ + *cp++ = 0x04; *cp++ = 0x01; + store_16_be(gss_mech_krb5->length+2, cp); + cp += 2; + *cp++ = 0x06; + *cp++ = (gss_mech_krb5->length) & 0xFF; + memcpy(cp, gss_mech_krb5->elements, gss_mech_krb5->length); + cp += gss_mech_krb5->length; + store_32_be(length, cp); + cp += 4; + memcpy(cp, str, length); + + free(str); + + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/export_sec_context.c b/src/lib/gssapi/krb5/export_sec_context.c new file mode 100644 index 000000000000..49bd76d2bc21 --- /dev/null +++ b/src/lib/gssapi/krb5/export_sec_context.c @@ -0,0 +1,101 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/export_sec_context.c - Externalize a security context */ +/* + * Copyright 1995, 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "gssapiP_krb5.h" +#ifndef LEAN_CLIENT +OM_uint32 KRB5_CALLCONV +krb5_gss_export_sec_context(minor_status, context_handle, interprocess_token) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_buffer_t interprocess_token; +{ + krb5_context context = NULL; + krb5_error_code kret; + OM_uint32 retval; + size_t bufsize, blen; + krb5_gss_ctx_id_t ctx; + krb5_octet *obuffer, *obp; + + /* Assume a tragic failure */ + obuffer = (krb5_octet *) NULL; + retval = GSS_S_FAILURE; + *minor_status = 0; + + ctx = (krb5_gss_ctx_id_t) *context_handle; + if (ctx->terminated) { + *minor_status = KG_CTX_INCOMPLETE; + return (GSS_S_NO_CONTEXT); + } + + context = ctx->k5_context; + kret = krb5_gss_ser_init(context); + if (kret) + goto error_out; + + /* Determine size needed for externalization of context */ + bufsize = 0; + if ((kret = kg_ctx_size(context, (krb5_pointer) ctx, + &bufsize))) + goto error_out; + + /* Allocate the buffer */ + if ((obuffer = gssalloc_malloc(bufsize)) == NULL) { + kret = ENOMEM; + goto error_out; + } + + obp = obuffer; + blen = bufsize; + /* Externalize the context */ + if ((kret = kg_ctx_externalize(context, + (krb5_pointer) ctx, &obp, &blen))) + goto error_out; + + /* Success! Return the buffer */ + interprocess_token->length = bufsize - blen; + interprocess_token->value = obuffer; + *minor_status = 0; + retval = GSS_S_COMPLETE; + + /* Now, clean up the context state */ + (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL); + *context_handle = GSS_C_NO_CONTEXT; + + return (GSS_S_COMPLETE); + +error_out: + if (retval != GSS_S_COMPLETE) + if (kret != 0 && context != 0) + save_error_info((OM_uint32)kret, context); + if (obuffer && bufsize) { + zap(obuffer, bufsize); + xfree(obuffer); + } + if (*minor_status == 0) + *minor_status = (OM_uint32) kret; + return(retval); +} +#endif /* LEAN_CLIENT */ diff --git a/src/lib/gssapi/krb5/get_tkt_flags.c b/src/lib/gssapi/krb5/get_tkt_flags.c new file mode 100644 index 000000000000..636fc04db44c --- /dev/null +++ b/src/lib/gssapi/krb5/get_tkt_flags.c @@ -0,0 +1,45 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +/* + * $Id$ + */ + +OM_uint32 +gss_krb5int_get_tkt_flags(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + krb5_gss_ctx_id_rec *ctx; + gss_buffer_desc rep; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + rep.value = &ctx->krb_flags; + rep.length = sizeof(ctx->krb_flags); + + return generic_gss_add_buffer_set_member(minor_status, &rep, data_set); +} diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h new file mode 100644 index 000000000000..d7bdef7e28a3 --- /dev/null +++ b/src/lib/gssapi/krb5/gssapiP_krb5.h @@ -0,0 +1,1428 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2000, 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _GSSAPIP_KRB5_H_ +#define _GSSAPIP_KRB5_H_ + +#include <k5-int.h> + +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +/* work around sunos braindamage */ +#ifdef major +#undef major +#endif +#ifdef minor +#undef minor +#endif + +#include "gssapiP_generic.h" + +/* The include of gssapi_krb5.h will dtrt with the above #defines in + * effect. + */ +#include "gssapi_krb5.h" +#include "gssapi_err_krb5.h" +#include "gssapi_ext.h" + +/* for debugging */ +#undef CFX_EXERCISE + +/** constants **/ + +#define GSS_MECH_KRB5_OID_LENGTH 9 +#define GSS_MECH_KRB5_OID "\052\206\110\206\367\022\001\002\002" + +#define GSS_MECH_KRB5_OLD_OID_LENGTH 5 +#define GSS_MECH_KRB5_OLD_OID "\053\005\001\005\002" + +/* Incorrect krb5 mech OID emitted by MS. */ +#define GSS_MECH_KRB5_WRONG_OID_LENGTH 9 +#define GSS_MECH_KRB5_WRONG_OID "\052\206\110\202\367\022\001\002\002" + +/* IAKERB variant */ +#define GSS_MECH_IAKERB_OID_LENGTH 6 +#define GSS_MECH_IAKERB_OID "\053\006\001\005\002\005" + +extern const gss_OID_set kg_all_mechs; + +#define CKSUMTYPE_KG_CB 0x8003 + +#define KG_TOK_CTX_AP_REQ 0x0100 +#define KG_TOK_CTX_AP_REP 0x0200 +#define KG_TOK_CTX_ERROR 0x0300 +#define KG_TOK_SIGN_MSG 0x0101 +#define KG_TOK_SEAL_MSG 0x0201 +#define KG_TOK_MIC_MSG 0x0101 +#define KG_TOK_WRAP_MSG 0x0201 +#define KG_TOK_DEL_CTX 0x0102 +#define KG2_TOK_MIC_MSG 0x0404 +#define KG2_TOK_WRAP_MSG 0x0504 +#define KG2_TOK_DEL_CTX 0x0405 +#define IAKERB_TOK_PROXY 0x0501 + +#define KRB5_GSS_FOR_CREDS_OPTION 1 + +#define KG2_RESP_FLAG_ERROR 0x0001 +#define KG2_RESP_FLAG_DELEG_OK 0x0002 + +/** CFX flags **/ +#define FLAG_SENDER_IS_ACCEPTOR 0x01 +#define FLAG_WRAP_CONFIDENTIAL 0x02 +#define FLAG_ACCEPTOR_SUBKEY 0x04 + +/* These are to be stored in little-endian order, i.e., des-mac is + stored as 02 00. */ +enum sgn_alg { + SGN_ALG_DES_MAC_MD5 = 0x0000, + SGN_ALG_MD2_5 = 0x0001, + SGN_ALG_DES_MAC = 0x0002, + SGN_ALG_3 = 0x0003, /* not published */ + SGN_ALG_HMAC_MD5 = 0x0011, /* microsoft w2k; */ + SGN_ALG_HMAC_SHA1_DES3_KD = 0x0004 +}; +enum seal_alg { + SEAL_ALG_NONE = 0xffff, + SEAL_ALG_DES = 0x0000, + SEAL_ALG_1 = 0x0001, /* not published */ + SEAL_ALG_MICROSOFT_RC4 = 0x0010, /* microsoft w2k; */ + SEAL_ALG_DES3KD = 0x0002 +}; + +/* for 3DES */ +#define KG_USAGE_SEAL 22 +#define KG_USAGE_SIGN 23 +#define KG_USAGE_SEQ 24 + +/* for draft-ietf-krb-wg-gssapi-cfx-01 */ +#define KG_USAGE_ACCEPTOR_SEAL 22 +#define KG_USAGE_ACCEPTOR_SIGN 23 +#define KG_USAGE_INITIATOR_SEAL 24 +#define KG_USAGE_INITIATOR_SIGN 25 + +enum qop { + GSS_KRB5_INTEG_C_QOP_MD5 = 0x0001, /* *partial* MD5 = "MD2.5" */ + GSS_KRB5_INTEG_C_QOP_DES_MD5 = 0x0002, + GSS_KRB5_INTEG_C_QOP_DES_MAC = 0x0003, + GSS_KRB5_INTEG_C_QOP_HMAC_SHA1 = 0x0004, + GSS_KRB5_INTEG_C_QOP_MASK = 0x00ff, + GSS_KRB5_CONF_C_QOP_DES = 0x0100, + GSS_KRB5_CONF_C_QOP_DES3_KD = 0x0200, + GSS_KRB5_CONF_C_QOP_MASK = 0xff00 +}; + +/** internal types **/ + +typedef struct _krb5_gss_name_rec { + krb5_principal princ; /* immutable */ + char *service; /* immutable */ + char *host; /* immutable */ + k5_mutex_t lock; /* protects ad_context only for now */ + krb5_authdata_context ad_context; +} krb5_gss_name_rec, *krb5_gss_name_t; + +typedef struct _krb5_gss_cred_id_rec { + /* protect against simultaneous accesses */ + k5_mutex_t lock; + + /* name/type of credential */ + gss_cred_usage_t usage; + krb5_gss_name_t name; + krb5_principal impersonator; + unsigned int default_identity : 1; + unsigned int iakerb_mech : 1; + unsigned int destroy_ccache : 1; + unsigned int suppress_ci_flags : 1; + + /* keytab (accept) data */ + krb5_keytab keytab; + krb5_rcache rcache; + + /* ccache (init) data */ + krb5_ccache ccache; + krb5_keytab client_keytab; + krb5_boolean have_tgt; + krb5_timestamp expire; + krb5_timestamp refresh_time; + krb5_enctype *req_enctypes; /* limit negotiated enctypes to this list */ + char *password; +} krb5_gss_cred_id_rec, *krb5_gss_cred_id_t; + +typedef struct _krb5_gss_ctx_ext_rec { + struct { + krb5_data *conv; + int verified; + } iakerb; +} krb5_gss_ctx_ext_rec, *krb5_gss_ctx_ext_t; + +typedef struct _krb5_gss_ctx_id_rec { + krb5_magic magic; + unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */ + unsigned int established : 1; + unsigned int have_acceptor_subkey : 1; + unsigned int seed_init : 1; /* XXX tested but never actually set */ + unsigned int terminated : 1; + OM_uint32 gss_flags; + unsigned char seed[16]; + krb5_gss_name_t here; + krb5_gss_name_t there; + krb5_key subkey; /* One of two potential keys to use with RFC 4121 + * packets; this key must always be set. */ + int signalg; + size_t cksum_size; + int sealalg; + krb5_key enc; /* RFC 1964 encryption key; seq xored with a constant + * for DES, seq for other RFC 1964 enctypes */ + krb5_key seq; /* RFC 1964 sequencing key */ + krb5_ticket_times krb_times; + krb5_flags krb_flags; + /* XXX these used to be signed. the old spec is inspecific, and + the new spec specifies unsigned. I don't believe that the change + affects the wire encoding. */ + uint64_t seq_send; + uint64_t seq_recv; + g_seqnum_state seqstate; + krb5_context k5_context; + krb5_auth_context auth_context; + gss_OID_desc *mech_used; + /* Protocol spec revision for sending packets + 0 => RFC 1964 with 3DES and RC4 enhancements + 1 => RFC 4121 + No others defined so far. It is always permitted to receive + tokens in RFC 4121 format. If enc is non-null, receiving RFC + 1964 tokens is permitted.*/ + int proto; + krb5_cksumtype cksumtype; /* for "main" subkey */ + krb5_key acceptor_subkey; /* CFX only */ + krb5_cksumtype acceptor_subkey_cksumtype; + int cred_rcache; /* did we get rcache from creds? */ + krb5_authdata **authdata; +} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t; + +extern g_set kg_vdb; + +#ifndef LEAN_CLIENT +extern k5_mutex_t gssint_krb5_keytab_lock; +#endif /* LEAN_CLIENT */ + +/** helper functions **/ + +OM_uint32 kg_get_defcred +(OM_uint32 *minor_status, + gss_cred_id_t *cred); + +krb5_error_code kg_checksum_channel_bindings +(krb5_context context, gss_channel_bindings_t cb, + krb5_checksum *cksum); + +krb5_error_code kg_make_seq_num (krb5_context context, + krb5_key key, + int direction, krb5_ui_4 seqnum, unsigned char *cksum, + unsigned char *buf); + +krb5_error_code kg_get_seq_num (krb5_context context, + krb5_key key, + unsigned char *cksum, unsigned char *buf, int *direction, + krb5_ui_4 *seqnum); + +krb5_error_code kg_make_seed (krb5_context context, + krb5_key key, + unsigned char *seed); + +krb5_error_code +kg_setup_keys(krb5_context context, + krb5_gss_ctx_id_rec *ctx, + krb5_key subkey, + krb5_cksumtype *cksumtype); + +int kg_confounder_size (krb5_context context, krb5_enctype enctype); + +krb5_error_code kg_make_confounder (krb5_context context, + krb5_enctype enctype, unsigned char *buf); + +krb5_error_code kg_encrypt (krb5_context context, + krb5_key key, int usage, + krb5_pointer iv, + krb5_const_pointer in, + krb5_pointer out, + unsigned int length); + +/* Encrypt length bytes at ptr in place, with the given key and usage. If + * iv is not NULL, use it as the cipher state. */ +krb5_error_code kg_encrypt_inplace(krb5_context context, krb5_key key, + int usage, krb5_pointer iv, + krb5_pointer ptr, unsigned int length); + +krb5_error_code kg_encrypt_iov (krb5_context context, + int proto, int dce_style, + size_t ec, size_t rrc, + krb5_key key, int usage, + krb5_pointer iv, + gss_iov_buffer_desc *iov, + int iov_count); + +krb5_error_code +kg_arcfour_docrypt (const krb5_keyblock *keyblock, int usage, + const unsigned char *kd_data, size_t kd_data_len, + const unsigned char *input_buf, size_t input_len, + unsigned char *output_buf); + +krb5_error_code +kg_arcfour_docrypt_iov (krb5_context context, + const krb5_keyblock *keyblock, int usage, + const unsigned char *kd_data, size_t kd_data_len, + gss_iov_buffer_desc *iov, + int iov_count); + +krb5_error_code kg_decrypt (krb5_context context, + krb5_key key, int usage, + krb5_pointer iv, + krb5_const_pointer in, + krb5_pointer out, + unsigned int length); + +krb5_error_code kg_decrypt_iov (krb5_context context, + int proto, int dce_style, + size_t ec, size_t rrc, + krb5_key key, int usage, + krb5_pointer iv, + gss_iov_buffer_desc *iov, + int iov_count); + +OM_uint32 kg_seal (OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_message_buffer, + int *conf_state, + gss_buffer_t output_message_buffer, + int toktype); + +OM_uint32 kg_unseal (OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_buffer_t input_token_buffer, + gss_buffer_t message_buffer, + int *conf_state, + gss_qop_t *qop_state, + int toktype); + +OM_uint32 kg_seal_size (OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + OM_uint32 output_size, + OM_uint32 *input_size); + +krb5_error_code kg_ctx_size (krb5_context kcontext, + krb5_pointer arg, + size_t *sizep); + +krb5_error_code kg_ctx_externalize (krb5_context kcontext, + krb5_pointer arg, + krb5_octet **buffer, + size_t *lenremain); + +krb5_error_code kg_ctx_internalize (krb5_context kcontext, + krb5_pointer *argp, + krb5_octet **buffer, + size_t *lenremain); + +OM_uint32 kg_sync_ccache_name (krb5_context context, OM_uint32 *minor_status); + +OM_uint32 kg_caller_provided_ccache_name (OM_uint32 *minor_status, + int *out_caller_provided_name); + +OM_uint32 kg_get_ccache_name (OM_uint32 *minor_status, + const char **out_name); + +OM_uint32 kg_set_ccache_name (OM_uint32 *minor_status, + const char *name); + +/* AEAD */ + +krb5_error_code gss_krb5int_make_seal_token_v3_iov(krb5_context context, + krb5_gss_ctx_id_rec *ctx, + int conf_req_flag, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype); + +OM_uint32 gss_krb5int_unseal_v3_iov(krb5_context context, + OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + gss_iov_buffer_desc *iov, + int iov_count, + int *conf_state, + gss_qop_t *qop_state, + int toktype); + +gss_iov_buffer_t kg_locate_iov (gss_iov_buffer_desc *iov, + int iov_count, + OM_uint32 type); + +gss_iov_buffer_t kg_locate_header_iov(gss_iov_buffer_desc *iov, int iov_count, + int toktype); + +void kg_iov_msglen(gss_iov_buffer_desc *iov, + int iov_count, + size_t *data_length, + size_t *assoc_data_length); + +void kg_release_iov(gss_iov_buffer_desc *iov, + int iov_count); + +krb5_error_code kg_make_checksum_iov_v1(krb5_context context, + krb5_cksumtype type, + size_t token_cksum_len, + krb5_key seq, + krb5_key enc, /* for conf len */ + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype, + krb5_checksum *checksum); + +krb5_error_code kg_make_checksum_iov_v3(krb5_context context, + krb5_cksumtype type, + size_t rrc, + krb5_key key, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype); + +krb5_error_code kg_verify_checksum_iov_v3(krb5_context context, + krb5_cksumtype type, + size_t rrc, + krb5_key key, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype, + krb5_boolean *valid); + +OM_uint32 kg_seal_iov (OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype); + +OM_uint32 kg_unseal_iov (OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype); + +OM_uint32 kg_seal_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype); + +krb5_cryptotype kg_translate_flag_iov(OM_uint32 type); + +OM_uint32 kg_fixup_padding_iov(OM_uint32 *minor_status, + gss_iov_buffer_desc *iov, + int iov_count); + +krb5_boolean kg_integ_only_iov(gss_iov_buffer_desc *iov, int iov_count); + +krb5_error_code kg_allocate_iov(gss_iov_buffer_t iov, size_t size); + +krb5_error_code +krb5_to_gss_cred(krb5_context context, + krb5_creds *creds, + krb5_gss_cred_id_t *out_cred); + +krb5_boolean +kg_cred_time_to_refresh(krb5_context context, krb5_gss_cred_id_rec *cred); + +void +kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred, + krb5_ticket_times *times); + +OM_uint32 +kg_cred_resolve(OM_uint32 *minor_status, krb5_context context, + gss_cred_id_t cred_handle, gss_name_t target_name); + +/** declarations of internal name mechanism functions **/ + +OM_uint32 KRB5_CALLCONV krb5_gss_acquire_cred +(OM_uint32*, /* minor_status */ + gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t*, /* output_cred_handle */ + gss_OID_set*, /* actual_mechs */ + OM_uint32* /* time_rec */ +); + +OM_uint32 KRB5_CALLCONV iakerb_gss_acquire_cred +(OM_uint32*, /* minor_status */ + gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t*, /* output_cred_handle */ + gss_OID_set*, /* actual_mechs */ + OM_uint32* /* time_rec */ +); + +OM_uint32 KRB5_CALLCONV +krb5_gss_acquire_cred_with_password( + OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_acquire_cred_with_password( + OM_uint32 *minor_status, + const gss_name_t desired_name, + const gss_buffer_t password, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + int cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec); + +OM_uint32 KRB5_CALLCONV krb5_gss_release_cred +(OM_uint32*, /* minor_status */ + gss_cred_id_t* /* cred_handle */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_init_sec_context +(OM_uint32*, /* minor_status */ + gss_cred_id_t, /* claimant_cred_handle */ + gss_ctx_id_t*, /* context_handle */ + gss_name_t, /* target_name */ + gss_OID, /* mech_type */ + OM_uint32, /* req_flags */ + OM_uint32, /* time_req */ + gss_channel_bindings_t, + /* input_chan_bindings */ + gss_buffer_t, /* input_token */ + gss_OID*, /* actual_mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32*, /* ret_flags */ + OM_uint32* /* time_rec */ +); + +OM_uint32 krb5_gss_init_sec_context_ext +(OM_uint32*, /* minor_status */ + gss_cred_id_t, /* claimant_cred_handle */ + gss_ctx_id_t*, /* context_handle */ + gss_name_t, /* target_name */ + gss_OID, /* mech_type */ + OM_uint32, /* req_flags */ + OM_uint32, /* time_req */ + gss_channel_bindings_t, + /* input_chan_bindings */ + gss_buffer_t, /* input_token */ + gss_OID*, /* actual_mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32*, /* ret_flags */ + OM_uint32*, /* time_rec */ + krb5_gss_ctx_ext_t /* exts */ +); + +#ifndef LEAN_CLIENT +OM_uint32 KRB5_CALLCONV krb5_gss_accept_sec_context +(OM_uint32*, /* minor_status */ + gss_ctx_id_t*, /* context_handle */ + gss_cred_id_t, /* verifier_cred_handle */ + gss_buffer_t, /* input_token_buffer */ + gss_channel_bindings_t, + /* input_chan_bindings */ + gss_name_t*, /* src_name */ + gss_OID*, /* mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32*, /* ret_flags */ + OM_uint32*, /* time_rec */ + gss_cred_id_t* /* delegated_cred_handle */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_accept_sec_context_ext +(OM_uint32*, /* minor_status */ + gss_ctx_id_t*, /* context_handle */ + gss_cred_id_t, /* verifier_cred_handle */ + gss_buffer_t, /* input_token_buffer */ + gss_channel_bindings_t, + /* input_chan_bindings */ + gss_name_t*, /* src_name */ + gss_OID*, /* mech_type */ + gss_buffer_t, /* output_token */ + OM_uint32*, /* ret_flags */ + OM_uint32*, /* time_rec */ + gss_cred_id_t*, /* delegated_cred_handle */ + krb5_gss_ctx_ext_t/*exts */ +); +#endif /* LEAN_CLIENT */ + +OM_uint32 KRB5_CALLCONV krb5_gss_inquire_sec_context_by_oid +(OM_uint32*, /* minor_status */ + const gss_ctx_id_t, + /* context_handle */ + const gss_OID, /* desired_object */ + gss_buffer_set_t* /* data_set */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_set_sec_context_option +(OM_uint32*, /* minor_status */ + gss_ctx_id_t*, /* context_handle */ + const gss_OID, /* desired_object */ + const gss_buffer_t/* value */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_process_context_token +(OM_uint32*, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t /* token_buffer */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_delete_sec_context +(OM_uint32*, /* minor_status */ + gss_ctx_id_t*, /* context_handle */ + gss_buffer_t /* output_token */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_context_time +(OM_uint32*, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + OM_uint32* /* time_rec */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_display_status +(OM_uint32*, /* minor_status */ + OM_uint32, /* status_value */ + int, /* status_type */ + gss_OID, /* mech_type */ + OM_uint32*, /* message_context */ + gss_buffer_t /* status_string */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_indicate_mechs +(OM_uint32*, /* minor_status */ + gss_OID_set* /* mech_set */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_compare_name +(OM_uint32*, /* minor_status */ + gss_name_t, /* name1 */ + gss_name_t, /* name2 */ + int* /* name_equal */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_display_name +(OM_uint32*, /* minor_status */ + gss_name_t, /* input_name */ + gss_buffer_t, /* output_name_buffer */ + gss_OID* /* output_name_type */ +); + + +OM_uint32 KRB5_CALLCONV krb5_gss_import_name +(OM_uint32*, /* minor_status */ + gss_buffer_t, /* input_name_buffer */ + gss_OID, /* input_name_type */ + gss_name_t* /* output_name */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_release_name +(OM_uint32*, /* minor_status */ + gss_name_t* /* input_name */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_inquire_cred +(OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + gss_name_t *, /* name */ + OM_uint32 *, /* lifetime */ + gss_cred_usage_t*,/* cred_usage */ + gss_OID_set * /* mechanisms */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_inquire_context +(OM_uint32*, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_name_t*, /* initiator_name */ + gss_name_t*, /* acceptor_name */ + OM_uint32*, /* lifetime_rec */ + gss_OID*, /* mech_type */ + OM_uint32*, /* ret_flags */ + int*, /* locally_initiated */ + int* /* open */ +); + +/* New V2 entry points */ +OM_uint32 KRB5_CALLCONV krb5_gss_get_mic +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t, /* qop_req */ + gss_buffer_t, /* message_buffer */ + gss_buffer_t /* message_token */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_get_mic_iov +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t, /* qop_req */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_get_mic_iov_length +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t, /* qop_req */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_verify_mic +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t, /* message_buffer */ + gss_buffer_t, /* message_token */ + gss_qop_t * /* qop_state */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_verify_mic_iov +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_qop_t *, /* qop_state */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_wrap +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + gss_buffer_t, /* input_message_buffer */ + int *, /* conf_state */ + gss_buffer_t /* output_message_buffer */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_wrap_iov +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + int *, /* conf_state */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_wrap_iov_length +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + int *, /* conf_state */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_unwrap +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + gss_buffer_t, /* input_message_buffer */ + gss_buffer_t, /* output_message_buffer */ + int *, /* conf_state */ + gss_qop_t * /* qop_state */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_unwrap_iov +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int *, /* conf_state */ + gss_qop_t *, /* qop_state */ + gss_iov_buffer_desc *, /* iov */ + int /* iov_count */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_wrap_size_limit +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t, /* context_handle */ + int, /* conf_req_flag */ + gss_qop_t, /* qop_req */ + OM_uint32, /* req_output_size */ + OM_uint32 * /* max_input_size */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_import_name_object +(OM_uint32 *, /* minor_status */ + void *, /* input_name */ + gss_OID, /* input_name_type */ + gss_name_t * /* output_name */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_export_name_object +(OM_uint32 *, /* minor_status */ + gss_name_t, /* input_name */ + gss_OID, /* desired_name_type */ + void * * /* output_name */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_inquire_cred_by_mech +(OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + gss_OID, /* mech_type */ + gss_name_t *, /* name */ + OM_uint32 *, /* initiator_lifetime */ + OM_uint32 *, /* acceptor_lifetime */ + gss_cred_usage_t * /* cred_usage */ +); +#ifndef LEAN_CLIENT +OM_uint32 KRB5_CALLCONV krb5_gss_export_sec_context +(OM_uint32 *, /* minor_status */ + gss_ctx_id_t *, /* context_handle */ + gss_buffer_t /* interprocess_token */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_import_sec_context +(OM_uint32 *, /* minor_status */ + gss_buffer_t, /* interprocess_token */ + gss_ctx_id_t * /* context_handle */ +); +#endif /* LEAN_CLIENT */ + +krb5_error_code krb5_gss_ser_init(krb5_context); + +OM_uint32 krb5_gss_release_oid +(OM_uint32 *, /* minor_status */ + gss_OID * /* oid */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_internal_release_oid +(OM_uint32 *, /* minor_status */ + gss_OID * /* oid */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_inquire_names_for_mech +(OM_uint32 *, /* minor_status */ + gss_OID, /* mechanism */ + gss_OID_set * /* name_types */ +); + +OM_uint32 krb5_gss_canonicalize_name +(OM_uint32 *, /* minor_status */ + const gss_name_t, /* input_name */ + const gss_OID, /* mech_type */ + gss_name_t * /* output_name */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_export_name +(OM_uint32 *, /* minor_status */ + const gss_name_t, /* input_name */ + gss_buffer_t /* exported_name */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_duplicate_name +(OM_uint32 *, /* minor_status */ + const gss_name_t, /* input_name */ + gss_name_t * /* dest_name */ +); + +OM_uint32 krb5_gss_validate_cred +(OM_uint32 *, /* minor_status */ + gss_cred_id_t /* cred */ +); + +OM_uint32 KRB5_CALLCONV krb5_gss_acquire_cred_impersonate_name( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 +krb5_gss_validate_cred_1(OM_uint32 * /* minor_status */, + gss_cred_id_t /* cred_handle */, + krb5_context /* context */); + +gss_OID krb5_gss_convert_static_mech_oid(gss_OID oid); + +krb5_error_code gss_krb5int_make_seal_token_v3(krb5_context, + krb5_gss_ctx_id_rec *, + const gss_buffer_desc *, + gss_buffer_t, + int, int); + +OM_uint32 gss_krb5int_unseal_token_v3(krb5_context *contextptr, + OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + unsigned char *ptr, + unsigned int bodysize, + gss_buffer_t message_buffer, + int *conf_state, gss_qop_t *qop_state, + int toktype); + +int gss_krb5int_rotate_left (void *ptr, size_t bufsiz, size_t rc); + +/* naming_exts.c */ +#define KG_INIT_NAME_NO_COPY 0x1 + +krb5_error_code +kg_init_name(krb5_context context, krb5_principal principal, + char *service, char *host, krb5_authdata_context ad_context, + krb5_flags flags, krb5_gss_name_t *name); + +krb5_error_code +kg_release_name(krb5_context context, krb5_gss_name_t *name); + +krb5_error_code +kg_duplicate_name(krb5_context context, const krb5_gss_name_t src, + krb5_gss_name_t *dst); + +krb5_boolean +kg_compare_name(krb5_context context, + krb5_gss_name_t name1, + krb5_gss_name_t name2); + +krb5_boolean +kg_acceptor_princ(krb5_context context, krb5_gss_name_t name, + krb5_principal *princ_out); + +OM_uint32 KRB5_CALLCONV +krb5_gss_display_name_ext(OM_uint32 *minor_status, + gss_name_t name, + gss_OID display_as_name_type, + gss_buffer_t display_name); + +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_name(OM_uint32 *minor_status, + gss_name_t name, + int *name_is_MN, + gss_OID *MN_mech, + gss_buffer_set_t *attrs); + +OM_uint32 KRB5_CALLCONV +krb5_gss_get_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more); + +OM_uint32 KRB5_CALLCONV +krb5_gss_set_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + int complete, + gss_buffer_t attr, + gss_buffer_t value); + +OM_uint32 KRB5_CALLCONV +krb5_gss_delete_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr); + +OM_uint32 KRB5_CALLCONV +krb5_gss_export_name_composite(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t exp_composite_name); + +OM_uint32 KRB5_CALLCONV +krb5_gss_map_name_to_any(OM_uint32 *minor_status, + gss_name_t name, + int authenticated, + gss_buffer_t type_id, + gss_any_t *output); + +OM_uint32 KRB5_CALLCONV +krb5_gss_release_any_name_mapping(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t type_id, + gss_any_t *input); + +OM_uint32 KRB5_CALLCONV +krb5_gss_pseudo_random(OM_uint32 *minor_status, + gss_ctx_id_t context, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out); + +OM_uint32 KRB5_CALLCONV +krb5_gss_store_cred(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + gss_cred_usage_t cred_usage, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored); + +/* s4u_gss_glue.c */ +OM_uint32 +kg_compose_deleg_cred(OM_uint32 *minor_status, + krb5_gss_cred_id_t impersonator_cred, + krb5_creds *subject_creds, + OM_uint32 time_req, + krb5_gss_cred_id_t *output_cred, + OM_uint32 *time_rec, + krb5_context context); + +/* + * These take unglued krb5-mech-specific contexts. + */ + +#define GSS_KRB5_GET_TKT_FLAGS_OID_LENGTH 11 +#define GSS_KRB5_GET_TKT_FLAGS_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x01" + +OM_uint32 gss_krb5int_get_tkt_flags +(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set); + +#define GSS_KRB5_COPY_CCACHE_OID_LENGTH 11 +#define GSS_KRB5_COPY_CCACHE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x02" + +OM_uint32 gss_krb5int_copy_ccache +(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_oid, + const gss_buffer_t value); + +#define GSS_KRB5_CCACHE_NAME_OID_LENGTH 11 +#define GSS_KRB5_CCACHE_NAME_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x03" + +struct krb5_gss_ccache_name_req { + const char *name; + const char **out_name; +}; + +OM_uint32 +gss_krb5int_ccache_name(OM_uint32 *minor_status, const gss_OID, const gss_OID, + const gss_buffer_t); + +#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11 +#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05" + +OM_uint32 +gss_krb5int_inq_session_key(OM_uint32 *, const gss_ctx_id_t, const gss_OID, gss_buffer_set_t *); + +#define GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID_LENGTH 11 +#define GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x04" + +struct krb5_gss_set_allowable_enctypes_req { + OM_uint32 num_ktypes; + krb5_enctype *ktypes; +}; + +OM_uint32 +gss_krb5int_set_allowable_enctypes(OM_uint32 *minor_status, + gss_cred_id_t *cred, + const gss_OID desired_oid, + const gss_buffer_t value); + +#define GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH 11 +#define GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x06" + +OM_uint32 +gss_krb5int_export_lucid_sec_context(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set); + +#define GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID_LENGTH 11 +#define GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x07" + +OM_uint32 +gss_krb5int_free_lucid_sec_context(OM_uint32 *, const gss_OID, + const gss_OID, gss_buffer_t); + +extern k5_mutex_t kg_kdc_flag_mutex; +krb5_error_code krb5_gss_init_context (krb5_context *ctxp); + +#define GSS_KRB5_USE_KDC_CONTEXT_OID_LENGTH 11 +#define GSS_KRB5_USE_KDC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x08" + +OM_uint32 krb5int_gss_use_kdc_context(OM_uint32 *, const gss_OID, + const gss_OID, gss_buffer_t); + +krb5_error_code krb5_gss_use_kdc_context(void); + +#define GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID_LENGTH 11 +#define GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x09" + +OM_uint32 +gss_krb5int_register_acceptor_identity(OM_uint32 *, const gss_OID, const gss_OID, gss_buffer_t); + +#define GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH 11 +#define GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a" + +OM_uint32 +gss_krb5int_extract_authz_data_from_sec_context(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *ad_data); + +#define GSS_KRB5_SET_CRED_RCACHE_OID_LENGTH 11 +#define GSS_KRB5_SET_CRED_RCACHE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0b" + +OM_uint32 +gss_krb5int_set_cred_rcache(OM_uint32 *, gss_cred_id_t *, const gss_OID, const gss_buffer_t); + +#define GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH 11 +#define GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0c" + +OM_uint32 +gss_krb5int_extract_authtime_from_sec_context(OM_uint32 *, + const gss_ctx_id_t, + const gss_OID, + gss_buffer_set_t *); + +#define GSS_KRB5_IMPORT_CRED_OID_LENGTH 11 +#define GSS_KRB5_IMPORT_CRED_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0d" + +struct krb5_gss_import_cred_req { + krb5_ccache id; + krb5_principal keytab_principal; + krb5_keytab keytab; +}; + +OM_uint32 +gss_krb5int_import_cred(OM_uint32 *minor_status, + gss_cred_id_t *cred, + const gss_OID desired_oid, + const gss_buffer_t value); + +#ifdef _GSS_STATIC_LINK +int gss_krb5int_lib_init(void); +void gss_krb5int_lib_fini(void); +#endif /* _GSS_STATIC_LINK */ + +OM_uint32 gss_krb5int_initialize_library(void); +void gss_krb5int_cleanup_library(void); + +/* For error message handling. */ +/* Returns a shared string, not a private copy! */ +extern char * +krb5_gss_get_error_message(OM_uint32 minor_code); +extern void +krb5_gss_save_error_string(OM_uint32 minor_code, char *msg); +extern void +krb5_gss_save_error_message(OM_uint32 minor_code, const char *format, ...) +#if !defined(__cplusplus) && (__GNUC__ > 2) + __attribute__((__format__(__printf__, 2, 3))) +#endif + ; + extern void + krb5_gss_save_error_info(OM_uint32 minor_code, krb5_context ctx); +#define get_error_message krb5_gss_get_error_message +#define save_error_string krb5_gss_save_error_string +#define save_error_message krb5_gss_save_error_message +#ifdef KRB5_KERNEL +/* Error messages aren't needed in the kernel, so reduce dependencies. */ +#define save_error_info(x,y) +#else +#define save_error_info krb5_gss_save_error_info +#endif +extern void krb5_gss_delete_error_info(void *p); + +/* Prefix concatenated with Kerberos encryption type */ +#define GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH 10 +#define GSS_KRB5_SESSION_KEY_ENCTYPE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04" + +/* IAKERB */ + +OM_uint32 KRB5_CALLCONV +iakerb_gss_init_sec_context(OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_accept_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handler, + gss_cred_id_t verifier_cred_handle, + gss_buffer_t input_token, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_delete_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token); + +krb5_error_code +iakerb_make_finished(krb5_context context, + krb5_key key, + const krb5_data *conv, + krb5_data **finished); + +krb5_error_code +iakerb_verify_finished(krb5_context context, + krb5_key key, + const krb5_data *conv, + const krb5_data *finished); + +/* + * Transfer contents of a krb5_data to a gss_buffer and invalidate the source + * On unix, this is a simple pointer copy + * On windows, memory is reallocated and copied. + */ +static inline krb5_error_code +data_to_gss(krb5_data *input_k5data, gss_buffer_t output_buffer) +{ + krb5_error_code code = 0; + output_buffer->length = input_k5data->length; +#if defined(_WIN32) || defined(DEBUG_GSSALLOC) + if (output_buffer->length > 0) { + output_buffer->value = gssalloc_malloc(output_buffer->length); + if (output_buffer->value) + memcpy(output_buffer->value, input_k5data->data, output_buffer->length); + else + code = ENOMEM; + } else { + output_buffer->value = NULL; + } + free(input_k5data->data); +#else + output_buffer->value = input_k5data->data; +#endif + *input_k5data = empty_data(); + return code; +} + +#define KRB5_GSS_EXTS_IAKERB_FINISHED 1 + + +/* Credential store extensions */ + +#define KRB5_CS_CLI_KEYTAB_URN "client_keytab" +#define KRB5_CS_KEYTAB_URN "keytab" +#define KRB5_CS_CCACHE_URN "ccache" +#define KRB5_CS_RCACHE_URN "rcache" + +OM_uint32 +kg_value_from_cred_store(gss_const_key_value_set_t cred_store, + const char *type, const char **value); + +OM_uint32 KRB5_CALLCONV +krb5_gss_acquire_cred_from( + OM_uint32 *, /* minor_status */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_const_key_value_set_t, /* cred_store */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_store_cred_into( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* input_cred_handle */ + gss_cred_usage_t, /* input_usage */ + const gss_OID, /* desired_mech */ + OM_uint32, /* overwrite_cred */ + OM_uint32, /* default_cred */ + gss_const_key_value_set_t, /* cred_store */ + gss_OID_set *, /* elements_stored */ + gss_cred_usage_t *); /* cred_usage_stored */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle, + gss_buffer_t token); + +OM_uint32 KRB5_CALLCONV +krb5_gss_import_cred(OM_uint32 *minor_status, gss_buffer_t token, + gss_cred_id_t *cred_handle); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_process_context_token(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t token_buffer); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_context_time(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + OM_uint32 *time_rec); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_inquire_context(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, gss_name_t *src_name, + gss_name_t *targ_name, OM_uint32 *lifetime_rec, + gss_OID *mech_type, OM_uint32 *ctx_flags, + int *locally_initiated, int *opened); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t qop_req, gss_buffer_t message_buffer, + gss_buffer_t message_token); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t qop_req, gss_iov_buffer_desc *iov, + int iov_count); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_get_mic_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, gss_qop_t qop_req, + gss_iov_buffer_desc *iov, int iov_count); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_verify_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_buffer_t msg_buffer, gss_buffer_t token_buffer, + gss_qop_t *qop_state); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t *qop_state, gss_iov_buffer_desc *iov, + int iov_count); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int conf_req_flag, gss_qop_t qop_req, + gss_buffer_t input_message_buffer, int *conf_state, + gss_buffer_t output_message_buffer); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int conf_req_flag, gss_qop_t qop_req, int *conf_state, + gss_iov_buffer_desc *iov, int iov_count); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, int conf_req_flag, + gss_qop_t qop_req, int *conf_state, + gss_iov_buffer_desc *iov, int iov_count); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_unwrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, int *conf_state, + gss_qop_t *qop_state); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_unwrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int *conf_state, gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, int iov_count); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap_size_limit(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, int conf_req_flag, + gss_qop_t qop_req, OM_uint32 req_output_size, + OM_uint32 *max_input_size); + +#ifndef LEAN_CLIENT +OM_uint32 KRB5_CALLCONV +iakerb_gss_export_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t interprocess_token); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_import_sec_context(OM_uint32 *minor_status, + const gss_buffer_t interprocess_token, + gss_ctx_id_t *context_handle); +#endif /* LEAN_CLIENT */ + +OM_uint32 KRB5_CALLCONV +iakerb_gss_inquire_sec_context_by_oid(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_set_sec_context_option(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + const gss_OID desired_object, + const gss_buffer_t value); + +OM_uint32 KRB5_CALLCONV +iakerb_gss_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int prf_key, const gss_buffer_t prf_in, + ssize_t desired_output_len, gss_buffer_t prf_out); + +/* Magic string to identify exported krb5 GSS credentials. Increment this if + * the format changes. */ +#define CRED_EXPORT_MAGIC "K5C1" + +#endif /* _GSSAPIP_KRB5_H_ */ diff --git a/src/lib/gssapi/krb5/gssapi_err_krb5.et b/src/lib/gssapi/krb5/gssapi_err_krb5.et new file mode 100644 index 000000000000..6396d5902009 --- /dev/null +++ b/src/lib/gssapi/krb5/gssapi_err_krb5.et @@ -0,0 +1,42 @@ +# +# Copyright 1993 by OpenVision Technologies, Inc. +# +# Permission to use, copy, modify, distribute, and sell this software +# and its documentation for any purpose is hereby granted without fee, +# provided that the above copyright notice appears in all copies and +# that both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of OpenVision not be used +# in advertising or publicity pertaining to distribution of the software +# without specific, written prior permission. OpenVision makes no +# representations about the suitability of this software for any +# purpose. It is provided "as is" without express or implied warranty. +# +# OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +# EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +# USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +# + +error_table k5g + +error_code KG_CCACHE_NOMATCH, "Principal in credential cache does not match desired name" +error_code KG_KEYTAB_NOMATCH, "No principal in keytab matches desired name" +error_code KG_TGT_MISSING, "Credential cache has no TGT" +error_code KG_NO_SUBKEY, "Authenticator has no subkey" +error_code KG_CONTEXT_ESTABLISHED, "Context is already fully established" +error_code KG_BAD_SIGN_TYPE, "Unknown signature type in token" +error_code KG_BAD_LENGTH, "Invalid field length in token" +error_code KG_CTX_INCOMPLETE, "Attempt to use incomplete security context" +error_code KG_CONTEXT, "Bad magic number for krb5_gss_ctx_id_t" +error_code KG_CRED, "Bad magic number for krb5_gss_cred_id_t" +error_code KG_ENC_DESC, "Bad magic number for krb5_gss_enc_desc" +error_code KG_BAD_SEQ, "Sequence number in token is corrupt" +error_code KG_EMPTY_CCACHE, "Credential cache is empty" +error_code KG_NO_CTYPES, "Acceptor and Initiator share no checksum types" +error_code KG_LUCID_VERSION, "Requested lucid context version not supported" +error_code KG_INPUT_TOO_LONG, "PRF input too long" +error_code KG_IAKERB_CONTEXT, "Bad magic number for iakerb_ctx_id_t" +end diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c new file mode 100644 index 000000000000..99092ccab16b --- /dev/null +++ b/src/lib/gssapi/krb5/gssapi_krb5.c @@ -0,0 +1,1159 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + + +/* For declaration of krb5_ser_context_init */ +#include "k5-int.h" +#include "gssapiP_krb5.h" +#include "mglueP.h" + +#ifndef NO_PASSWORD +#include <pwd.h> +#endif + +/** exported constants defined in gssapi_krb5{,_nx}.h **/ + +/* these are bogus, but will compile */ + +/* + * The OID of the draft krb5 mechanism, assigned by IETF, is: + * iso(1) org(3) dod(5) internet(1) security(5) + * kerberosv5(2) = 1.3.5.1.5.2 + * The OID of the krb5_name type is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) krb5_name(1) = 1.2.840.113554.1.2.2.1 + * The OID of the krb5_principal type is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) krb5_principal(2) = 1.2.840.113554.1.2.2.2 + * The OID of the proposed standard krb5 mechanism is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) = 1.2.840.113554.1.2.2 + * The OID of the proposed standard krb5 v2 mechanism is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5v2(3) = 1.2.840.113554.1.2.3 + * Provisionally reserved for Kerberos session key algorithm + * identifiers is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) krb5_enctype(4) = 1.2.840.113554.1.2.2.4 + * Provisionally reserved for Kerberos mechanism-specific APIs: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) krb5_gssapi_ext(5) = 1.2.840.113554.1.2.2.5 + */ + +/* + * Encoding rules: The first two values are encoded in one byte as 40 + * * value1 + value2. Subsequent values are encoded base 128, most + * significant digit first, with the high bit (\200) set on all octets + * except the last in each value's encoding. + */ + +#define NO_CI_FLAGS_X_OID_LENGTH 6 +#define NO_CI_FLAGS_X_OID "\x2a\x85\x70\x2b\x0d\x1d" + +const gss_OID_desc krb5_gss_oid_array[] = { + /* this is the official, rfc-specified OID */ + {GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID}, + /* this pre-RFC mech OID */ + {GSS_MECH_KRB5_OLD_OID_LENGTH, GSS_MECH_KRB5_OLD_OID}, + /* this is the unofficial, incorrect mech OID emitted by MS */ + {GSS_MECH_KRB5_WRONG_OID_LENGTH, GSS_MECH_KRB5_WRONG_OID}, + /* IAKERB OID */ + {GSS_MECH_IAKERB_OID_LENGTH, GSS_MECH_IAKERB_OID}, + /* this is the v2 assigned OID */ + {9, "\052\206\110\206\367\022\001\002\003"}, + /* these two are name type OID's */ + /* 2.1.1. Kerberos Principal Name Form: (rfc 1964) + * This name form shall be represented by the Object Identifier {iso(1) + * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) krb5_name(1)}. The recommended symbolic name for this type + * is "GSS_KRB5_NT_PRINCIPAL_NAME". */ + {10, "\052\206\110\206\367\022\001\002\002\001"}, + /* gss_nt_krb5_principal. Object identifier for a krb5_principal. Do not use. */ + {10, "\052\206\110\206\367\022\001\002\002\002"}, + {NO_CI_FLAGS_X_OID_LENGTH, NO_CI_FLAGS_X_OID}, + { 0, 0 } +}; + +#define kg_oids ((gss_OID)krb5_gss_oid_array) + +const gss_OID gss_mech_krb5 = &kg_oids[0]; +const gss_OID gss_mech_krb5_old = &kg_oids[1]; +const gss_OID gss_mech_krb5_wrong = &kg_oids[2]; +const gss_OID gss_mech_iakerb = &kg_oids[3]; + + +const gss_OID gss_nt_krb5_name = &kg_oids[5]; +const gss_OID gss_nt_krb5_principal = &kg_oids[6]; +const gss_OID GSS_KRB5_NT_PRINCIPAL_NAME = &kg_oids[5]; + +const gss_OID GSS_KRB5_CRED_NO_CI_FLAGS_X = &kg_oids[7]; + +static const gss_OID_set_desc oidsets[] = { + {1, &kg_oids[0]}, /* RFC OID */ + {1, &kg_oids[1]}, /* pre-RFC OID */ + {3, &kg_oids[0]}, /* all names for krb5 mech */ + {4, &kg_oids[0]}, /* all krb5 names and IAKERB */ +}; + +#define kg_oidsets ((gss_OID_set)oidsets) + +const gss_OID_set gss_mech_set_krb5 = &kg_oidsets[0]; +const gss_OID_set gss_mech_set_krb5_old = &kg_oidsets[1]; +const gss_OID_set gss_mech_set_krb5_both = &kg_oidsets[2]; +const gss_OID_set kg_all_mechs = &kg_oidsets[3]; + +g_set kg_vdb = G_SET_INIT; + +/** default credential support */ + +/* + * init_sec_context() will explicitly re-acquire default credentials, + * so handling the expiration/invalidation condition here isn't needed. + */ +OM_uint32 +kg_get_defcred(minor_status, cred) + OM_uint32 *minor_status; + gss_cred_id_t *cred; +{ + OM_uint32 major; + + if ((major = krb5_gss_acquire_cred(minor_status, + (gss_name_t) NULL, GSS_C_INDEFINITE, + GSS_C_NULL_OID_SET, GSS_C_INITIATE, + cred, NULL, NULL)) && GSS_ERROR(major)) { + return(major); + } + *minor_status = 0; + return(GSS_S_COMPLETE); +} + +OM_uint32 +kg_sync_ccache_name (krb5_context context, OM_uint32 *minor_status) +{ + OM_uint32 err = 0; + + /* + * Sync up the context ccache name with the GSSAPI ccache name. + * If kg_ccache_name is NULL -- normal unless someone has called + * gss_krb5_ccache_name() -- then the system default ccache will + * be picked up and used by resetting the context default ccache. + * This is needed for platforms which support multiple ccaches. + */ + + if (!err) { + /* if NULL, resets the context default ccache */ + err = krb5_cc_set_default_name(context, + (char *) k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME)); + } + + *minor_status = err; + return (*minor_status == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + +/* This function returns whether or not the caller set a cccache name. Used by + * gss_acquire_cred to figure out if the caller wants to only look at this + * ccache or search the cache collection for the desired name */ +OM_uint32 +kg_caller_provided_ccache_name (OM_uint32 *minor_status, + int *out_caller_provided_name) +{ + if (out_caller_provided_name) { + *out_caller_provided_name = + (k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME) != NULL); + } + + *minor_status = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +kg_get_ccache_name (OM_uint32 *minor_status, const char **out_name) +{ + const char *name = NULL; + OM_uint32 err = 0; + char *kg_ccache_name; + + kg_ccache_name = k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME); + + if (kg_ccache_name != NULL) { + name = strdup(kg_ccache_name); + if (name == NULL) + err = ENOMEM; + } else { + krb5_context context = NULL; + + /* Reset the context default ccache (see text above), and then + retrieve it. */ + err = krb5_gss_init_context(&context); + if (!err) + err = krb5_cc_set_default_name (context, NULL); + if (!err) { + name = krb5_cc_default_name(context); + if (name) { + name = strdup(name); + if (name == NULL) + err = ENOMEM; + } + } + if (err && context) + save_error_info(err, context); + if (context) + krb5_free_context(context); + } + + if (!err) { + if (out_name) { + *out_name = name; + } + } + + *minor_status = err; + return (*minor_status == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + +OM_uint32 +kg_set_ccache_name (OM_uint32 *minor_status, const char *name) +{ + char *new_name = NULL; + char *swap = NULL; + char *kg_ccache_name; + krb5_error_code kerr; + + if (name) { + new_name = strdup(name); + if (new_name == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + } + + kg_ccache_name = k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME); + swap = kg_ccache_name; + kg_ccache_name = new_name; + new_name = swap; + kerr = k5_setspecific(K5_KEY_GSS_KRB5_CCACHE_NAME, kg_ccache_name); + if (kerr != 0) { + /* Can't store, so free up the storage. */ + free(kg_ccache_name); + /* ??? free(new_name); */ + *minor_status = kerr; + return GSS_S_FAILURE; + } + + free (new_name); + *minor_status = 0; + return GSS_S_COMPLETE; +} + +#define g_OID_prefix_equal(o1, o2) \ + (((o1)->length >= (o2)->length) && \ + (memcmp((o1)->elements, (o2)->elements, (o2)->length) == 0)) + +/* + * gss_inquire_sec_context_by_oid() methods + */ +static struct { + gss_OID_desc oid; + OM_uint32 (*func)(OM_uint32 *, const gss_ctx_id_t, const gss_OID, gss_buffer_set_t *); +} krb5_gss_inquire_sec_context_by_oid_ops[] = { + { + {GSS_KRB5_GET_TKT_FLAGS_OID_LENGTH, GSS_KRB5_GET_TKT_FLAGS_OID}, + gss_krb5int_get_tkt_flags + }, + { + {GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID}, + gss_krb5int_extract_authz_data_from_sec_context + }, + { + {GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH, GSS_KRB5_INQ_SSPI_SESSION_KEY_OID}, + gss_krb5int_inq_session_key + }, + { + {GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID}, + gss_krb5int_export_lucid_sec_context + }, + { + {GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID}, + gss_krb5int_extract_authtime_from_sec_context + } +}; + +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_sec_context_by_oid (OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + krb5_gss_ctx_id_rec *ctx; + size_t i; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + if (desired_object == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (data_set == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *data_set = GSS_C_NO_BUFFER_SET; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + if (ctx->terminated || !ctx->established) + return GSS_S_NO_CONTEXT; + + for (i = 0; i < sizeof(krb5_gss_inquire_sec_context_by_oid_ops)/ + sizeof(krb5_gss_inquire_sec_context_by_oid_ops[0]); i++) { + if (g_OID_prefix_equal(desired_object, &krb5_gss_inquire_sec_context_by_oid_ops[i].oid)) { + return (*krb5_gss_inquire_sec_context_by_oid_ops[i].func)(minor_status, + context_handle, + desired_object, + data_set); + } + } + + *minor_status = EINVAL; + + return GSS_S_UNAVAILABLE; +} + +/* + * gss_inquire_cred_by_oid() methods + */ +#if 0 +static struct { + gss_OID_desc oid; + OM_uint32 (*func)(OM_uint32 *, const gss_cred_id_t, const gss_OID, gss_buffer_set_t *); +} krb5_gss_inquire_cred_by_oid_ops[] = { +}; +#endif + +static OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_cred_by_oid(OM_uint32 *minor_status, + const gss_cred_id_t cred_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + OM_uint32 major_status = GSS_S_FAILURE; +#if 0 + size_t i; +#endif + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + if (desired_object == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (data_set == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *data_set = GSS_C_NO_BUFFER_SET; + if (cred_handle == GSS_C_NO_CREDENTIAL) { + *minor_status = (OM_uint32)KRB5_NOCREDS_SUPPLIED; + return GSS_S_NO_CRED; + } + + major_status = krb5_gss_validate_cred(minor_status, cred_handle); + if (GSS_ERROR(major_status)) + return major_status; + +#if 0 + for (i = 0; i < sizeof(krb5_gss_inquire_cred_by_oid_ops)/ + sizeof(krb5_gss_inquire_cred_by_oid_ops[0]); i++) { + if (g_OID_prefix_equal(desired_object, &krb5_gss_inquire_cred_by_oid_ops[i].oid)) { + return (*krb5_gss_inquire_cred_by_oid_ops[i].func)(minor_status, + cred_handle, + desired_object, + data_set); + } + } +#endif + + *minor_status = EINVAL; + + return GSS_S_UNAVAILABLE; +} + +/* + * gss_set_sec_context_option() methods + * (Disabled until we have something to populate the array.) + */ +#if 0 +static struct { + gss_OID_desc oid; + OM_uint32 (*func)(OM_uint32 *, gss_ctx_id_t *, const gss_OID, const gss_buffer_t); +} krb5_gss_set_sec_context_option_ops[] = { +}; +#endif + +OM_uint32 KRB5_CALLCONV +krb5_gss_set_sec_context_option (OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ +#if 0 + size_t i; +#endif + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + if (context_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (desired_object == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ; + +#if 0 + for (i = 0; i < sizeof(krb5_gss_set_sec_context_option_ops)/ + sizeof(krb5_gss_set_sec_context_option_ops[0]); i++) { + if (g_OID_prefix_equal(desired_object, &krb5_gss_set_sec_context_option_ops[i].oid)) { + return (*krb5_gss_set_sec_context_option_ops[i].func)(minor_status, + context_handle, + desired_object, + value); + } + } +#endif + + *minor_status = EINVAL; + + return GSS_S_UNAVAILABLE; +} + +static OM_uint32 +no_ci_flags(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_oid, + const gss_buffer_t value) +{ + krb5_gss_cred_id_t cred; + + cred = (krb5_gss_cred_id_t) *cred_handle; + cred->suppress_ci_flags = 1; + + *minor_status = 0; + return GSS_S_COMPLETE; +} +/* + * gssspi_set_cred_option() methods + */ +static struct { + gss_OID_desc oid; + OM_uint32 (*func)(OM_uint32 *, gss_cred_id_t *, const gss_OID, const gss_buffer_t); +} krb5_gssspi_set_cred_option_ops[] = { + { + {GSS_KRB5_COPY_CCACHE_OID_LENGTH, GSS_KRB5_COPY_CCACHE_OID}, + gss_krb5int_copy_ccache + }, + { + {GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID_LENGTH, GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID}, + gss_krb5int_set_allowable_enctypes + }, + { + {GSS_KRB5_SET_CRED_RCACHE_OID_LENGTH, GSS_KRB5_SET_CRED_RCACHE_OID}, + gss_krb5int_set_cred_rcache + }, + { + {GSS_KRB5_IMPORT_CRED_OID_LENGTH, GSS_KRB5_IMPORT_CRED_OID}, + gss_krb5int_import_cred + }, + { + {NO_CI_FLAGS_X_OID_LENGTH, NO_CI_FLAGS_X_OID}, + no_ci_flags + }, +}; + +static OM_uint32 KRB5_CALLCONV +krb5_gssspi_set_cred_option(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ + OM_uint32 major_status = GSS_S_FAILURE; + size_t i; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (cred_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + if (desired_object == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (*cred_handle != GSS_C_NO_CREDENTIAL) { + major_status = krb5_gss_validate_cred(minor_status, *cred_handle); + if (GSS_ERROR(major_status)) + return major_status; + } + + for (i = 0; i < sizeof(krb5_gssspi_set_cred_option_ops)/ + sizeof(krb5_gssspi_set_cred_option_ops[0]); i++) { + if (g_OID_prefix_equal(desired_object, &krb5_gssspi_set_cred_option_ops[i].oid)) { + return (*krb5_gssspi_set_cred_option_ops[i].func)(minor_status, + cred_handle, + desired_object, + value); + } + } + + *minor_status = EINVAL; + + return GSS_S_UNAVAILABLE; +} + +/* + * gssspi_mech_invoke() methods + */ +static struct { + gss_OID_desc oid; + OM_uint32 (*func)(OM_uint32 *, const gss_OID, const gss_OID, gss_buffer_t); +} krb5_gssspi_mech_invoke_ops[] = { + { + {GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID_LENGTH, GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID}, + gss_krb5int_register_acceptor_identity + }, + { + {GSS_KRB5_CCACHE_NAME_OID_LENGTH, GSS_KRB5_CCACHE_NAME_OID}, + gss_krb5int_ccache_name + }, + { + {GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID}, + gss_krb5int_free_lucid_sec_context + }, +#ifndef _WIN32 + { + {GSS_KRB5_USE_KDC_CONTEXT_OID_LENGTH, GSS_KRB5_USE_KDC_CONTEXT_OID}, + krb5int_gss_use_kdc_context + }, +#endif +}; + +static OM_uint32 KRB5_CALLCONV +krb5_gssspi_mech_invoke (OM_uint32 *minor_status, + const gss_OID desired_mech, + const gss_OID desired_object, + gss_buffer_t value) +{ + size_t i; + + if (minor_status == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *minor_status = 0; + + if (desired_mech == GSS_C_NO_OID) + return GSS_S_BAD_MECH; + + if (desired_object == GSS_C_NO_OID) + return GSS_S_CALL_INACCESSIBLE_READ; + + for (i = 0; i < sizeof(krb5_gssspi_mech_invoke_ops)/ + sizeof(krb5_gssspi_mech_invoke_ops[0]); i++) { + if (g_OID_prefix_equal(desired_object, &krb5_gssspi_mech_invoke_ops[i].oid)) { + return (*krb5_gssspi_mech_invoke_ops[i].func)(minor_status, + desired_mech, + desired_object, + value); + } + } + + *minor_status = EINVAL; + + return GSS_S_UNAVAILABLE; +} + +#define GS2_KRB5_SASL_NAME "GS2-KRB5" +#define GS2_KRB5_SASL_NAME_LEN (sizeof(GS2_KRB5_SASL_NAME) - 1) + +#define GS2_IAKERB_SASL_NAME "GS2-IAKERB" +#define GS2_IAKERB_SASL_NAME_LEN (sizeof(GS2_IAKERB_SASL_NAME) - 1) + +static OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_mech_for_saslname(OM_uint32 *minor_status, + const gss_buffer_t sasl_mech_name, + gss_OID *mech_type) +{ + *minor_status = 0; + + if (sasl_mech_name->length == GS2_KRB5_SASL_NAME_LEN && + memcmp(sasl_mech_name->value, + GS2_KRB5_SASL_NAME, GS2_KRB5_SASL_NAME_LEN) == 0) { + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_krb5; + return GSS_S_COMPLETE; + } else if (sasl_mech_name->length == GS2_IAKERB_SASL_NAME_LEN && + memcmp(sasl_mech_name->value, + GS2_IAKERB_SASL_NAME, GS2_IAKERB_SASL_NAME_LEN) == 0) { + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_iakerb; + return GSS_S_COMPLETE; + } + + return GSS_S_BAD_MECH; +} + +static OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_saslname_for_mech(OM_uint32 *minor_status, + const gss_OID desired_mech, + gss_buffer_t sasl_mech_name, + gss_buffer_t mech_name, + gss_buffer_t mech_description) +{ + if (g_OID_equal(desired_mech, gss_mech_iakerb)) { + if (!g_make_string_buffer(GS2_IAKERB_SASL_NAME, sasl_mech_name) || + !g_make_string_buffer("iakerb", mech_name) || + !g_make_string_buffer("Initial and Pass Through Authentication " + "Kerberos Mechanism (IAKERB)", + mech_description)) + goto fail; + } else { + if (!g_make_string_buffer(GS2_KRB5_SASL_NAME, sasl_mech_name) || + !g_make_string_buffer("krb5", mech_name) || + !g_make_string_buffer("Kerberos 5 GSS-API Mechanism", + mech_description)) + goto fail; + } + + *minor_status = 0; + return GSS_S_COMPLETE; + +fail: + *minor_status = ENOMEM; + return GSS_S_FAILURE; +} + +static OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_attrs_for_mech(OM_uint32 *minor_status, + gss_const_OID mech, + gss_OID_set *mech_attrs, + gss_OID_set *known_mech_attrs) +{ + OM_uint32 major, tmpMinor; + + if (mech_attrs == NULL) { + *minor_status = 0; + return GSS_S_COMPLETE; + } + + major = gss_create_empty_oid_set(minor_status, mech_attrs); + if (GSS_ERROR(major)) + goto cleanup; + +#define MA_SUPPORTED(ma) do { \ + major = gss_add_oid_set_member(minor_status, (gss_OID)ma, \ + mech_attrs); \ + if (GSS_ERROR(major)) \ + goto cleanup; \ + } while (0) + + MA_SUPPORTED(GSS_C_MA_MECH_CONCRETE); + MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED); + MA_SUPPORTED(GSS_C_MA_AUTH_INIT); + MA_SUPPORTED(GSS_C_MA_AUTH_TARG); + MA_SUPPORTED(GSS_C_MA_DELEG_CRED); + MA_SUPPORTED(GSS_C_MA_INTEG_PROT); + MA_SUPPORTED(GSS_C_MA_CONF_PROT); + MA_SUPPORTED(GSS_C_MA_MIC); + MA_SUPPORTED(GSS_C_MA_WRAP); + MA_SUPPORTED(GSS_C_MA_PROT_READY); + MA_SUPPORTED(GSS_C_MA_REPLAY_DET); + MA_SUPPORTED(GSS_C_MA_OOS_DET); + MA_SUPPORTED(GSS_C_MA_CBINDINGS); + MA_SUPPORTED(GSS_C_MA_CTX_TRANS); + + if (g_OID_equal(mech, gss_mech_iakerb)) { + MA_SUPPORTED(GSS_C_MA_AUTH_INIT_INIT); + MA_SUPPORTED(GSS_C_MA_NOT_DFLT_MECH); + } else if (!g_OID_equal(mech, gss_mech_krb5)) { + MA_SUPPORTED(GSS_C_MA_DEPRECATED); + } + +cleanup: + if (GSS_ERROR(major)) + gss_release_oid_set(&tmpMinor, mech_attrs); + + return major; +} + +static OM_uint32 KRB5_CALLCONV +krb5_gss_localname(OM_uint32 *minor, + const gss_name_t pname, + const gss_const_OID mech_type, + gss_buffer_t localname) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + char lname[BUFSIZ]; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)pname; + + code = krb5_aname_to_localname(context, kname->princ, + sizeof(lname), lname); + if (code != 0) { + *minor = KRB5_NO_LOCALNAME; + krb5_free_context(context); + return GSS_S_FAILURE; + } + + + krb5_free_context(context); + localname->value = gssalloc_strdup(lname); + localname->length = strlen(lname); + + return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + + +static OM_uint32 KRB5_CALLCONV +krb5_gss_authorize_localname(OM_uint32 *minor, + const gss_name_t pname, + gss_const_buffer_t local_user, + gss_const_OID name_type) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + char *user; + int user_ok; + + if (name_type != GSS_C_NO_OID && + !g_OID_equal(name_type, GSS_C_NT_USER_NAME)) { + return GSS_S_BAD_NAMETYPE; + } + + kname = (krb5_gss_name_t)pname; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor = code; + return GSS_S_FAILURE; + } + + user = k5memdup0(local_user->value, local_user->length, &code); + if (user == NULL) { + *minor = code; + krb5_free_context(context); + return GSS_S_FAILURE; + } + + user_ok = krb5_kuserok(context, kname->princ, user); + + free(user); + krb5_free_context(context); + + *minor = 0; + return user_ok ? GSS_S_COMPLETE : GSS_S_UNAUTHORIZED; +} + +static struct gss_config krb5_mechanism = { + { GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID }, + NULL, + krb5_gss_acquire_cred, + krb5_gss_release_cred, + krb5_gss_init_sec_context, +#ifdef LEAN_CLIENT + NULL, +#else + krb5_gss_accept_sec_context, +#endif + krb5_gss_process_context_token, + krb5_gss_delete_sec_context, + krb5_gss_context_time, + krb5_gss_get_mic, + krb5_gss_verify_mic, +#if defined(IOV_SHIM_EXERCISE_WRAP) || defined(IOV_SHIM_EXERCISE) + NULL, +#else + krb5_gss_wrap, +#endif +#if defined(IOV_SHIM_EXERCISE_UNWRAP) || defined(IOV_SHIM_EXERCISE) + NULL, +#else + krb5_gss_unwrap, +#endif + krb5_gss_display_status, + krb5_gss_indicate_mechs, + krb5_gss_compare_name, + krb5_gss_display_name, + krb5_gss_import_name, + krb5_gss_release_name, + krb5_gss_inquire_cred, + NULL, /* add_cred */ +#ifdef LEAN_CLIENT + NULL, + NULL, +#else + krb5_gss_export_sec_context, + krb5_gss_import_sec_context, +#endif + krb5_gss_inquire_cred_by_mech, + krb5_gss_inquire_names_for_mech, + krb5_gss_inquire_context, + krb5_gss_internal_release_oid, + krb5_gss_wrap_size_limit, + krb5_gss_localname, + + krb5_gss_authorize_localname, + krb5_gss_export_name, + krb5_gss_duplicate_name, + krb5_gss_store_cred, + krb5_gss_inquire_sec_context_by_oid, + krb5_gss_inquire_cred_by_oid, + krb5_gss_set_sec_context_option, + krb5_gssspi_set_cred_option, + krb5_gssspi_mech_invoke, + NULL, /* wrap_aead */ + NULL, /* unwrap_aead */ + krb5_gss_wrap_iov, + krb5_gss_unwrap_iov, + krb5_gss_wrap_iov_length, + NULL, /* complete_auth_token */ + krb5_gss_acquire_cred_impersonate_name, + NULL, /* krb5_gss_add_cred_impersonate_name */ + NULL, /* display_name_ext */ + krb5_gss_inquire_name, + krb5_gss_get_name_attribute, + krb5_gss_set_name_attribute, + krb5_gss_delete_name_attribute, + krb5_gss_export_name_composite, + krb5_gss_map_name_to_any, + krb5_gss_release_any_name_mapping, + krb5_gss_pseudo_random, + NULL, /* set_neg_mechs */ + krb5_gss_inquire_saslname_for_mech, + krb5_gss_inquire_mech_for_saslname, + krb5_gss_inquire_attrs_for_mech, + krb5_gss_acquire_cred_from, + krb5_gss_store_cred_into, + krb5_gss_acquire_cred_with_password, + krb5_gss_export_cred, + krb5_gss_import_cred, + NULL, /* import_sec_context_by_mech */ + NULL, /* import_name_by_mech */ + NULL, /* import_cred_by_mech */ + krb5_gss_get_mic_iov, + krb5_gss_verify_mic_iov, + krb5_gss_get_mic_iov_length, +}; + +/* Functions which use security contexts or acquire creds are IAKERB-specific; + * other functions can borrow from the krb5 mech. */ +static struct gss_config iakerb_mechanism = { + { GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID }, + NULL, + iakerb_gss_acquire_cred, + krb5_gss_release_cred, + iakerb_gss_init_sec_context, +#ifdef LEAN_CLIENT + NULL, +#else + iakerb_gss_accept_sec_context, +#endif + iakerb_gss_process_context_token, + iakerb_gss_delete_sec_context, + iakerb_gss_context_time, + iakerb_gss_get_mic, + iakerb_gss_verify_mic, +#if defined(IOV_SHIM_EXERCISE_WRAP) || defined(IOV_SHIM_EXERCISE) + NULL, +#else + iakerb_gss_wrap, +#endif +#if defined(IOV_SHIM_EXERCISE_UNWRAP) || defined(IOV_SHIM_EXERCISE) + NULL, +#else + iakerb_gss_unwrap, +#endif + krb5_gss_display_status, + krb5_gss_indicate_mechs, + krb5_gss_compare_name, + krb5_gss_display_name, + krb5_gss_import_name, + krb5_gss_release_name, + krb5_gss_inquire_cred, + NULL, /* add_cred */ +#ifdef LEAN_CLIENT + NULL, + NULL, +#else + iakerb_gss_export_sec_context, + iakerb_gss_import_sec_context, +#endif + krb5_gss_inquire_cred_by_mech, + krb5_gss_inquire_names_for_mech, + iakerb_gss_inquire_context, + krb5_gss_internal_release_oid, + iakerb_gss_wrap_size_limit, + krb5_gss_localname, + krb5_gss_authorize_localname, + krb5_gss_export_name, + krb5_gss_duplicate_name, + krb5_gss_store_cred, + iakerb_gss_inquire_sec_context_by_oid, + krb5_gss_inquire_cred_by_oid, + iakerb_gss_set_sec_context_option, + krb5_gssspi_set_cred_option, + krb5_gssspi_mech_invoke, + NULL, /* wrap_aead */ + NULL, /* unwrap_aead */ + iakerb_gss_wrap_iov, + iakerb_gss_unwrap_iov, + iakerb_gss_wrap_iov_length, + NULL, /* complete_auth_token */ + NULL, /* acquire_cred_impersonate_name */ + NULL, /* add_cred_impersonate_name */ + NULL, /* display_name_ext */ + krb5_gss_inquire_name, + krb5_gss_get_name_attribute, + krb5_gss_set_name_attribute, + krb5_gss_delete_name_attribute, + krb5_gss_export_name_composite, + krb5_gss_map_name_to_any, + krb5_gss_release_any_name_mapping, + iakerb_gss_pseudo_random, + NULL, /* set_neg_mechs */ + krb5_gss_inquire_saslname_for_mech, + krb5_gss_inquire_mech_for_saslname, + krb5_gss_inquire_attrs_for_mech, + krb5_gss_acquire_cred_from, + krb5_gss_store_cred_into, + iakerb_gss_acquire_cred_with_password, + krb5_gss_export_cred, + krb5_gss_import_cred, + NULL, /* import_sec_context_by_mech */ + NULL, /* import_name_by_mech */ + NULL, /* import_cred_by_mech */ + iakerb_gss_get_mic_iov, + iakerb_gss_verify_mic_iov, + iakerb_gss_get_mic_iov_length, +}; + +#ifdef _GSS_STATIC_LINK +#include "mglueP.h" +static int gss_iakerbmechglue_init(void) +{ + struct gss_mech_config mech_iakerb; + + memset(&mech_iakerb, 0, sizeof(mech_iakerb)); + mech_iakerb.mech = &iakerb_mechanism; + + mech_iakerb.mechNameStr = "iakerb"; + mech_iakerb.mech_type = (gss_OID)gss_mech_iakerb; + gssint_register_mechinfo(&mech_iakerb); + + return 0; +} + +static int gss_krb5mechglue_init(void) +{ + struct gss_mech_config mech_krb5; + + memset(&mech_krb5, 0, sizeof(mech_krb5)); + mech_krb5.mech = &krb5_mechanism; + + mech_krb5.mechNameStr = "kerberos_v5"; + mech_krb5.mech_type = (gss_OID)gss_mech_krb5; + gssint_register_mechinfo(&mech_krb5); + + mech_krb5.mechNameStr = "kerberos_v5_old"; + mech_krb5.mech_type = (gss_OID)gss_mech_krb5_old; + gssint_register_mechinfo(&mech_krb5); + + mech_krb5.mechNameStr = "mskrb"; + mech_krb5.mech_type = (gss_OID)gss_mech_krb5_wrong; + gssint_register_mechinfo(&mech_krb5); + + return 0; +} +#else +MAKE_INIT_FUNCTION(gss_krb5int_lib_init); +MAKE_FINI_FUNCTION(gss_krb5int_lib_fini); + +gss_mechanism KRB5_CALLCONV +gss_mech_initialize(void) +{ + return &krb5_mechanism; +} +#endif /* _GSS_STATIC_LINK */ + +int gss_krb5int_lib_init(void) +{ + int err; + +#ifdef SHOW_INITFINI_FUNCS + printf("gss_krb5int_lib_init\n"); +#endif + + add_error_table(&et_k5g_error_table); + +#ifndef LEAN_CLIENT + err = k5_mutex_finish_init(&gssint_krb5_keytab_lock); + if (err) + return err; +#endif /* LEAN_CLIENT */ + err = k5_key_register(K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME, free); + if (err) + return err; + err = k5_key_register(K5_KEY_GSS_KRB5_CCACHE_NAME, free); + if (err) + return err; + err = k5_key_register(K5_KEY_GSS_KRB5_ERROR_MESSAGE, + krb5_gss_delete_error_info); + if (err) + return err; +#ifndef _WIN32 + err = k5_mutex_finish_init(&kg_kdc_flag_mutex); + if (err) + return err; + err = k5_mutex_finish_init(&kg_vdb.mutex); + if (err) + return err; +#endif +#ifdef _GSS_STATIC_LINK + err = gss_krb5mechglue_init(); + if (err) + return err; + err = gss_iakerbmechglue_init(); + if (err) + return err; +#endif + + return 0; +} + +void gss_krb5int_lib_fini(void) +{ +#ifndef _GSS_STATIC_LINK + if (!INITIALIZER_RAN(gss_krb5int_lib_init) || PROGRAM_EXITING()) { +# ifdef SHOW_INITFINI_FUNCS + printf("gss_krb5int_lib_fini: skipping\n"); +# endif + return; + } +#endif +#ifdef SHOW_INITFINI_FUNCS + printf("gss_krb5int_lib_fini\n"); +#endif + remove_error_table(&et_k5g_error_table); + + k5_key_delete(K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME); + k5_key_delete(K5_KEY_GSS_KRB5_CCACHE_NAME); + k5_key_delete(K5_KEY_GSS_KRB5_ERROR_MESSAGE); + k5_mutex_destroy(&kg_vdb.mutex); +#ifndef _WIN32 + k5_mutex_destroy(&kg_kdc_flag_mutex); +#endif +#ifndef LEAN_CLIENT + k5_mutex_destroy(&gssint_krb5_keytab_lock); +#endif /* LEAN_CLIENT */ +} + +#ifdef _GSS_STATIC_LINK +extern OM_uint32 gssint_lib_init(void); +#endif + +OM_uint32 gss_krb5int_initialize_library (void) +{ +#ifdef _GSS_STATIC_LINK + return gssint_mechglue_initialize_library(); +#else + return CALL_INIT_FUNCTION(gss_krb5int_lib_init); +#endif +} diff --git a/src/lib/gssapi/krb5/gssapi_krb5.h b/src/lib/gssapi/krb5/gssapi_krb5.h new file mode 100644 index 000000000000..390b00032cce --- /dev/null +++ b/src/lib/gssapi/krb5/gssapi_krb5.h @@ -0,0 +1,281 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _GSSAPI_KRB5_H_ +#define _GSSAPI_KRB5_H_ + +#include <gssapi/gssapi.h> +#include <gssapi/gssapi_ext.h> +#include <krb5.h> +#include <stdint.h> + +/* C++ friendlyness */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Reserved static storage for GSS_oids. See rfc 1964 for more details. */ + +/* 2.1.1. Kerberos Principal Name Form: */ +GSS_DLLIMP extern const gss_OID GSS_KRB5_NT_PRINCIPAL_NAME; +/* This name form shall be represented by the Object Identifier {iso(1) + * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) krb5_name(1)}. The recommended symbolic name for this type + * is "GSS_KRB5_NT_PRINCIPAL_NAME". */ + +/* 2.1.2. Host-Based Service Name Form */ +#define GSS_KRB5_NT_HOSTBASED_SERVICE_NAME GSS_C_NT_HOSTBASED_SERVICE +/* This name form shall be represented by the Object Identifier {iso(1) + * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) + * generic(1) service_name(4)}. The previously recommended symbolic + * name for this type is "GSS_KRB5_NT_HOSTBASED_SERVICE_NAME". The + * currently preferred symbolic name for this type is + * "GSS_C_NT_HOSTBASED_SERVICE". */ + +/* 2.2.1. User Name Form */ +#define GSS_KRB5_NT_USER_NAME GSS_C_NT_USER_NAME +/* This name form shall be represented by the Object Identifier {iso(1) + * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) + * generic(1) user_name(1)}. The recommended symbolic name for this + * type is "GSS_KRB5_NT_USER_NAME". */ + +/* 2.2.2. Machine UID Form */ +#define GSS_KRB5_NT_MACHINE_UID_NAME GSS_C_NT_MACHINE_UID_NAME +/* This name form shall be represented by the Object Identifier {iso(1) + * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) + * generic(1) machine_uid_name(2)}. The recommended symbolic name for + * this type is "GSS_KRB5_NT_MACHINE_UID_NAME". */ + +/* 2.2.3. String UID Form */ +#define GSS_KRB5_NT_STRING_UID_NAME GSS_C_NT_STRING_UID_NAME +/* This name form shall be represented by the Object Identifier {iso(1) + * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) + * generic(1) string_uid_name(3)}. The recommended symbolic name for + * this type is "GSS_KRB5_NT_STRING_UID_NAME". */ + +GSS_DLLIMP extern const gss_OID gss_mech_krb5; +GSS_DLLIMP extern const gss_OID gss_mech_krb5_old; +GSS_DLLIMP extern const gss_OID gss_mech_krb5_wrong; +GSS_DLLIMP extern const gss_OID gss_mech_iakerb; +GSS_DLLIMP extern const gss_OID_set gss_mech_set_krb5; +GSS_DLLIMP extern const gss_OID_set gss_mech_set_krb5_old; +GSS_DLLIMP extern const gss_OID_set gss_mech_set_krb5_both; + +GSS_DLLIMP extern const gss_OID gss_nt_krb5_name; +GSS_DLLIMP extern const gss_OID gss_nt_krb5_principal; + +GSS_DLLIMP extern const gss_OID_desc krb5_gss_oid_array[]; + +/* + * This OID can be used with gss_set_cred_option() to suppress the + * confidentiality and integrity flags from being asserted in initial context + * tokens. + * + * iso(1) member-body(2) Sweden(752) Stockholm University(43) Heimdal GSS-API + * Extensions(13) no_ci_flags(29) + */ +GSS_DLLIMP extern const gss_OID GSS_KRB5_CRED_NO_CI_FLAGS_X; + +#define gss_krb5_nt_general_name gss_nt_krb5_name +#define gss_krb5_nt_principal gss_nt_krb5_principal +#define gss_krb5_nt_service_name gss_nt_service_name +#define gss_krb5_nt_user_name gss_nt_user_name +#define gss_krb5_nt_machine_uid_name gss_nt_machine_uid_name +#define gss_krb5_nt_string_uid_name gss_nt_string_uid_name + +typedef struct gss_krb5_lucid_key { + OM_uint32 type; /* key encryption type */ + OM_uint32 length; /* length of key data */ + void * data; /* actual key data */ +} gss_krb5_lucid_key_t; + +typedef struct gss_krb5_rfc1964_keydata { + OM_uint32 sign_alg; /* signing algorthm */ + OM_uint32 seal_alg; /* seal/encrypt algorithm */ + gss_krb5_lucid_key_t ctx_key; + /* Context key + (Kerberos session key or subkey) */ +} gss_krb5_rfc1964_keydata_t; + +typedef struct gss_krb5_cfx_keydata { + OM_uint32 have_acceptor_subkey; + /* 1 if there is an acceptor_subkey + present, 0 otherwise */ + gss_krb5_lucid_key_t ctx_key; + /* Context key + (Kerberos session key or subkey) */ + gss_krb5_lucid_key_t acceptor_subkey; + /* acceptor-asserted subkey or + 0's if no acceptor subkey */ +} gss_krb5_cfx_keydata_t; + +typedef struct gss_krb5_lucid_context_v1 { + OM_uint32 version; /* Structure version number (1) + MUST be at beginning of struct! */ + OM_uint32 initiate; /* Are we the initiator? */ + OM_uint32 endtime; /* expiration time of context */ + uint64_t send_seq; /* sender sequence number */ + uint64_t recv_seq; /* receive sequence number */ + OM_uint32 protocol; /* 0: rfc1964, + 1: draft-ietf-krb-wg-gssapi-cfx-07 */ + /* + * if (protocol == 0) rfc1964_kd should be used + * and cfx_kd contents are invalid and should be zero + * if (protocol == 1) cfx_kd should be used + * and rfc1964_kd contents are invalid and should be zero + */ + gss_krb5_rfc1964_keydata_t rfc1964_kd; + gss_krb5_cfx_keydata_t cfx_kd; +} gss_krb5_lucid_context_v1_t; + +/* + * Mask for determining the version of a lucid context structure. Callers + * should not require this. + */ +typedef struct gss_krb5_lucid_context_version { + OM_uint32 version; /* Structure version number */ +} gss_krb5_lucid_context_version_t; + + + + +/* Alias for Heimdal compat. */ +#define gsskrb5_register_acceptor_identity krb5_gss_register_acceptor_identity + +OM_uint32 KRB5_CALLCONV krb5_gss_register_acceptor_identity(const char *); + +OM_uint32 KRB5_CALLCONV gss_krb5_get_tkt_flags( + OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + krb5_flags *ticket_flags); + +OM_uint32 KRB5_CALLCONV gss_krb5_copy_ccache( + OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + krb5_ccache out_ccache); + +OM_uint32 KRB5_CALLCONV gss_krb5_ccache_name( + OM_uint32 *minor_status, const char *name, + const char **out_name); + +/* + * gss_krb5_set_allowable_enctypes + * + * This function may be called by a context initiator after calling + * gss_acquire_cred(), but before calling gss_init_sec_context(), + * to restrict the set of enctypes which will be negotiated during + * context establishment to those in the provided array. + * + * 'cred' must be a valid credential handle obtained via + * gss_acquire_cred(). It may not be GSS_C_NO_CREDENTIAL. + * gss_acquire_cred() may have been called to get a handle to + * the default credential. + * + * The purpose of this function is to limit the keys that may + * be exported via gss_krb5_export_lucid_sec_context(); thus it + * should limit the enctypes of all keys that will be needed + * after the security context has been established. + * (i.e. context establishment may use a session key with a + * stronger enctype than in the provided array, however a + * subkey must be established within the enctype limits + * established by this function.) + * + */ +OM_uint32 KRB5_CALLCONV +gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status, + gss_cred_id_t cred, + OM_uint32 num_ktypes, + krb5_enctype *ktypes); + +/* + * Returns a non-opaque (lucid) version of the internal context + * information. + * + * Note that context_handle must not be used again by the caller + * after this call. The GSS implementation is free to release any + * resources associated with the original context. It is up to the + * GSS implementation whether it returns pointers to existing data, + * or copies of the data. The caller should treat the returned + * lucid context as read-only. + * + * The caller must call gss_krb5_free_lucid_context() to free + * the context and allocated resources when it is finished with it. + * + * 'version' is an integer indicating the requested version of the lucid + * context. If the implementation does not understand the requested version, + * it will return an error. + * + * For example: + * void *return_ctx; + * gss_krb5_lucid_context_v1_t *ctx; + * OM_uint32 min_stat, maj_stat; + * OM_uint32 vers; + * gss_ctx_id_t *ctx_handle; + * + * maj_stat = gss_krb5_export_lucid_sec_context(&min_stat, + * ctx_handle, 1, &return_ctx); + * // Verify success + * ctx = (gss_krb5_lucid_context_v1_t *) return_ctx; + */ + +OM_uint32 KRB5_CALLCONV +gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + OM_uint32 version, + void **kctx); + +/* + * Frees the allocated storage associated with an + * exported struct gss_krb5_lucid_context. + */ +OM_uint32 KRB5_CALLCONV +gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, + void *kctx); + + +OM_uint32 KRB5_CALLCONV +gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + int ad_type, + gss_buffer_t ad_data); + +OM_uint32 KRB5_CALLCONV +gss_krb5_set_cred_rcache(OM_uint32 *minor_status, + gss_cred_id_t cred, + krb5_rcache rcache); + +OM_uint32 KRB5_CALLCONV +gsskrb5_extract_authtime_from_sec_context(OM_uint32 *, gss_ctx_id_t, krb5_timestamp *); + +OM_uint32 KRB5_CALLCONV +gss_krb5_import_cred(OM_uint32 *minor_status, + krb5_ccache id, + krb5_principal keytab_principal, + krb5_keytab keytab, + gss_cred_id_t *cred); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _GSSAPI_KRB5_H_ */ diff --git a/src/lib/gssapi/krb5/iakerb.c b/src/lib/gssapi/krb5/iakerb.c new file mode 100644 index 000000000000..2dc4d0c1a4d5 --- /dev/null +++ b/src/lib/gssapi/krb5/iakerb.c @@ -0,0 +1,1329 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +#include "k5-int.h" +#include "gssapiP_krb5.h" + +/* + * IAKERB implementation + */ + +extern int gssint_get_der_length(unsigned char **, OM_uint32, unsigned int*); + +enum iakerb_state { + IAKERB_AS_REQ, /* acquiring ticket with initial creds */ + IAKERB_TGS_REQ, /* acquiring ticket with TGT */ + IAKERB_AP_REQ /* hand-off to normal GSS AP-REQ exchange */ +}; + +struct _iakerb_ctx_id_rec { + krb5_magic magic; /* KG_IAKERB_CONTEXT */ + krb5_context k5c; + gss_cred_id_t defcred; /* Initiator only */ + enum iakerb_state state; /* Initiator only */ + krb5_init_creds_context icc; /* Initiator only */ + krb5_tkt_creds_context tcc; /* Initiator only */ + gss_ctx_id_t gssc; + krb5_data conv; /* conversation for checksumming */ + unsigned int count; /* number of round trips */ + int initiate; + int established; + krb5_get_init_creds_opt *gic_opts; +}; + +#define IAKERB_MAX_HOPS ( 16 /* MAX_IN_TKT_LOOPS */ + KRB5_REFERRAL_MAXHOPS ) + +typedef struct _iakerb_ctx_id_rec iakerb_ctx_id_rec; +typedef iakerb_ctx_id_rec *iakerb_ctx_id_t; + +/* + * Release an IAKERB context + */ +static void +iakerb_release_context(iakerb_ctx_id_t ctx) +{ + OM_uint32 tmp; + + if (ctx == NULL) + return; + + krb5_gss_release_cred(&tmp, &ctx->defcred); + krb5_init_creds_free(ctx->k5c, ctx->icc); + krb5_tkt_creds_free(ctx->k5c, ctx->tcc); + krb5_gss_delete_sec_context(&tmp, &ctx->gssc, NULL); + krb5_free_data_contents(ctx->k5c, &ctx->conv); + krb5_get_init_creds_opt_free(ctx->k5c, ctx->gic_opts); + krb5_free_context(ctx->k5c); + free(ctx); +} + +/* + * Create a IAKERB-FINISHED structure containing a checksum of + * the entire IAKERB exchange. + */ +krb5_error_code +iakerb_make_finished(krb5_context context, + krb5_key key, + const krb5_data *conv, + krb5_data **finished) +{ + krb5_error_code code; + krb5_iakerb_finished iaf; + + *finished = NULL; + + memset(&iaf, 0, sizeof(iaf)); + + if (key == NULL) + return KRB5KDC_ERR_NULL_KEY; + + code = krb5_k_make_checksum(context, 0, key, KRB5_KEYUSAGE_IAKERB_FINISHED, + conv, &iaf.checksum); + if (code != 0) + return code; + + code = encode_krb5_iakerb_finished(&iaf, finished); + + krb5_free_checksum_contents(context, &iaf.checksum); + + return code; +} + +/* + * Verify a IAKERB-FINISHED structure submitted by the initiator + */ +krb5_error_code +iakerb_verify_finished(krb5_context context, + krb5_key key, + const krb5_data *conv, + const krb5_data *finished) +{ + krb5_error_code code; + krb5_iakerb_finished *iaf; + krb5_boolean valid = FALSE; + + if (key == NULL) + return KRB5KDC_ERR_NULL_KEY; + + code = decode_krb5_iakerb_finished(finished, &iaf); + if (code != 0) + return code; + + code = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_IAKERB_FINISHED, + conv, &iaf->checksum, &valid); + if (code == 0 && valid == FALSE) + code = KRB5KRB_AP_ERR_BAD_INTEGRITY; + + krb5_free_iakerb_finished(context, iaf); + + return code; +} + +/* + * Save a token for future checksumming. + */ +static krb5_error_code +iakerb_save_token(iakerb_ctx_id_t ctx, const gss_buffer_t token) +{ + char *p; + + p = realloc(ctx->conv.data, ctx->conv.length + token->length); + if (p == NULL) + return ENOMEM; + + memcpy(p + ctx->conv.length, token->value, token->length); + ctx->conv.data = p; + ctx->conv.length += token->length; + + return 0; +} + +/* + * Parse a token into IAKERB-HEADER and KRB-KDC-REQ/REP + */ +static krb5_error_code +iakerb_parse_token(iakerb_ctx_id_t ctx, + int initialContextToken, + const gss_buffer_t token, + krb5_data *realm, + krb5_data **cookie, + krb5_data *request) +{ + krb5_error_code code; + krb5_iakerb_header *iah = NULL; + unsigned int bodysize, lenlen; + int length; + unsigned char *ptr; + int flags = 0; + krb5_data data; + + if (token == GSS_C_NO_BUFFER || token->length == 0) { + code = KRB5_BAD_MSIZE; + goto cleanup; + } + + if (initialContextToken) + flags |= G_VFY_TOKEN_HDR_WRAPPER_REQUIRED; + + ptr = token->value; + + code = g_verify_token_header(gss_mech_iakerb, + &bodysize, &ptr, + IAKERB_TOK_PROXY, + token->length, flags); + if (code != 0) + goto cleanup; + + data.data = (char *)ptr; + + if (bodysize-- == 0 || *ptr++ != 0x30 /* SEQUENCE */) { + code = ASN1_BAD_ID; + goto cleanup; + } + + length = gssint_get_der_length(&ptr, bodysize, &lenlen); + if (length < 0 || bodysize - lenlen < (unsigned int)length) { + code = KRB5_BAD_MSIZE; + goto cleanup; + } + data.length = 1 /* SEQUENCE */ + lenlen + length; + + ptr += length; + bodysize -= (lenlen + length); + + code = decode_krb5_iakerb_header(&data, &iah); + if (code != 0) + goto cleanup; + + if (realm != NULL) { + *realm = iah->target_realm; + iah->target_realm.data = NULL; + } + + if (cookie != NULL) { + *cookie = iah->cookie; + iah->cookie = NULL; + } + + request->data = (char *)ptr; + request->length = bodysize; + + assert(request->data + request->length == + (char *)token->value + token->length); + +cleanup: + krb5_free_iakerb_header(ctx->k5c, iah); + + return code; +} + +/* + * Create a token from IAKERB-HEADER and KRB-KDC-REQ/REP + */ +static krb5_error_code +iakerb_make_token(iakerb_ctx_id_t ctx, + krb5_data *realm, + krb5_data *cookie, + krb5_data *request, + int initialContextToken, + gss_buffer_t token) +{ + krb5_error_code code; + krb5_iakerb_header iah; + krb5_data *data = NULL; + char *p; + unsigned int tokenSize; + unsigned char *q; + + token->value = NULL; + token->length = 0; + + /* + * Assemble the IAKERB-HEADER from the realm and cookie + */ + memset(&iah, 0, sizeof(iah)); + iah.target_realm = *realm; + iah.cookie = cookie; + + code = encode_krb5_iakerb_header(&iah, &data); + if (code != 0) + goto cleanup; + + /* + * Concatenate Kerberos request. + */ + p = realloc(data->data, data->length + request->length); + if (p == NULL) { + code = ENOMEM; + goto cleanup; + } + data->data = p; + + if (request->length > 0) + memcpy(data->data + data->length, request->data, request->length); + data->length += request->length; + + if (initialContextToken) + tokenSize = g_token_size(gss_mech_iakerb, data->length); + else + tokenSize = 2 + data->length; + + token->value = q = gssalloc_malloc(tokenSize); + if (q == NULL) { + code = ENOMEM; + goto cleanup; + } + token->length = tokenSize; + + if (initialContextToken) { + g_make_token_header(gss_mech_iakerb, data->length, &q, + IAKERB_TOK_PROXY); + } else { + store_16_be(IAKERB_TOK_PROXY, q); + q += 2; + } + memcpy(q, data->data, data->length); + q += data->length; + + assert(q == (unsigned char *)token->value + token->length); + +cleanup: + krb5_free_data(ctx->k5c, data); + + return code; +} + +/* + * Parse the IAKERB token in input_token and send the contained KDC + * request to the KDC for the realm. + * + * Wrap the KDC reply in output_token. + */ +static krb5_error_code +iakerb_acceptor_step(iakerb_ctx_id_t ctx, + int initialContextToken, + const gss_buffer_t input_token, + gss_buffer_t output_token) +{ + krb5_error_code code; + krb5_data request = empty_data(), reply = empty_data(); + krb5_data realm = empty_data(); + OM_uint32 tmp; + int tcp_only, use_master; + krb5_ui_4 kdc_code; + + output_token->length = 0; + output_token->value = NULL; + + if (ctx->count >= IAKERB_MAX_HOPS) { + code = KRB5_KDC_UNREACH; + goto cleanup; + } + + code = iakerb_parse_token(ctx, initialContextToken, input_token, &realm, + NULL, &request); + if (code != 0) + goto cleanup; + + if (realm.length == 0 || request.length == 0) { + code = KRB5_BAD_MSIZE; + goto cleanup; + } + + code = iakerb_save_token(ctx, input_token); + if (code != 0) + goto cleanup; + + for (tcp_only = 0; tcp_only <= 1; tcp_only++) { + use_master = 0; + code = krb5_sendto_kdc(ctx->k5c, &request, &realm, + &reply, &use_master, tcp_only); + if (code == 0 && krb5_is_krb_error(&reply)) { + krb5_error *error; + + code = decode_krb5_error(&reply, &error); + if (code != 0) + goto cleanup; + kdc_code = error->error; + krb5_free_error(ctx->k5c, error); + if (kdc_code == KRB_ERR_RESPONSE_TOO_BIG) { + krb5_free_data_contents(ctx->k5c, &reply); + reply = empty_data(); + continue; + } + } + break; + } + + if (code == KRB5_KDC_UNREACH || code == KRB5_REALM_UNKNOWN) { + krb5_error error; + + memset(&error, 0, sizeof(error)); + if (code == KRB5_KDC_UNREACH) + error.error = KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE; + else if (code == KRB5_REALM_UNKNOWN) + error.error = KRB_AP_ERR_IAKERB_KDC_NOT_FOUND; + + code = krb5_mk_error(ctx->k5c, &error, &reply); + if (code != 0) + goto cleanup; + } else if (code != 0) + goto cleanup; + + code = iakerb_make_token(ctx, &realm, NULL, &reply, 0, output_token); + if (code != 0) + goto cleanup; + + code = iakerb_save_token(ctx, output_token); + if (code != 0) + goto cleanup; + + ctx->count++; + +cleanup: + if (code != 0) + gss_release_buffer(&tmp, output_token); + /* request is a pointer into input_token, no need to free */ + krb5_free_data_contents(ctx->k5c, &realm); + krb5_free_data_contents(ctx->k5c, &reply); + + return code; +} + +/* + * Initialise the krb5_init_creds context for the IAKERB context + */ +static krb5_error_code +iakerb_init_creds_ctx(iakerb_ctx_id_t ctx, + krb5_gss_cred_id_t cred, + OM_uint32 time_req) +{ + krb5_error_code code; + + if (cred->iakerb_mech == 0) { + code = EINVAL; + goto cleanup; + } + + assert(cred->name != NULL); + assert(cred->name->princ != NULL); + + code = krb5_get_init_creds_opt_alloc(ctx->k5c, &ctx->gic_opts); + if (code != 0) + goto cleanup; + + if (time_req != 0 && time_req != GSS_C_INDEFINITE) + krb5_get_init_creds_opt_set_tkt_life(ctx->gic_opts, time_req); + + code = krb5_get_init_creds_opt_set_out_ccache(ctx->k5c, ctx->gic_opts, + cred->ccache); + if (code != 0) + goto cleanup; + + code = krb5_init_creds_init(ctx->k5c, + cred->name->princ, + NULL, /* prompter */ + NULL, /* data */ + 0, /* start_time */ + ctx->gic_opts, + &ctx->icc); + if (code != 0) + goto cleanup; + + if (cred->password != NULL) { + code = krb5_init_creds_set_password(ctx->k5c, ctx->icc, + cred->password); + } else if (cred->client_keytab != NULL) { + code = krb5_init_creds_set_keytab(ctx->k5c, ctx->icc, + cred->client_keytab); + } else { + code = KRB5_KT_NOTFOUND; + } + if (code != 0) + goto cleanup; + +cleanup: + return code; +} + +/* + * Initialise the krb5_tkt_creds context for the IAKERB context + */ +static krb5_error_code +iakerb_tkt_creds_ctx(iakerb_ctx_id_t ctx, + krb5_gss_cred_id_t cred, + krb5_gss_name_t name, + OM_uint32 time_req) + +{ + krb5_error_code code; + krb5_creds creds; + krb5_timestamp now; + + assert(cred->name != NULL); + assert(cred->name->princ != NULL); + + memset(&creds, 0, sizeof(creds)); + + creds.client = cred->name->princ; + creds.server = name->princ; + + if (time_req != 0 && time_req != GSS_C_INDEFINITE) { + code = krb5_timeofday(ctx->k5c, &now); + if (code != 0) + goto cleanup; + + creds.times.endtime = now + time_req; + } + + if (cred->name->ad_context != NULL) { + code = krb5_authdata_export_authdata(ctx->k5c, + cred->name->ad_context, + AD_USAGE_TGS_REQ, + &creds.authdata); + if (code != 0) + goto cleanup; + } + + code = krb5_tkt_creds_init(ctx->k5c, cred->ccache, &creds, 0, &ctx->tcc); + if (code != 0) + goto cleanup; + +cleanup: + krb5_free_authdata(ctx->k5c, creds.authdata); + + return code; +} + +/* + * Parse the IAKERB token in input_token and process the KDC + * response. + * + * Emit the next KDC request, if any, in output_token. + */ +static krb5_error_code +iakerb_initiator_step(iakerb_ctx_id_t ctx, + krb5_gss_cred_id_t cred, + krb5_gss_name_t name, + OM_uint32 time_req, + const gss_buffer_t input_token, + gss_buffer_t output_token) +{ + krb5_error_code code = 0; + krb5_data in = empty_data(), out = empty_data(), realm = empty_data(); + krb5_data *cookie = NULL; + OM_uint32 tmp; + unsigned int flags = 0; + krb5_ticket_times times; + + output_token->length = 0; + output_token->value = NULL; + + if (input_token != GSS_C_NO_BUFFER) { + code = iakerb_parse_token(ctx, 0, input_token, NULL, &cookie, &in); + if (code != 0) + goto cleanup; + + code = iakerb_save_token(ctx, input_token); + if (code != 0) + goto cleanup; + } + + switch (ctx->state) { + case IAKERB_AS_REQ: + if (ctx->icc == NULL) { + code = iakerb_init_creds_ctx(ctx, cred, time_req); + if (code != 0) + goto cleanup; + } + + code = krb5_init_creds_step(ctx->k5c, ctx->icc, &in, &out, &realm, + &flags); + if (code != 0) { + if (cred->have_tgt) { + /* We were trying to refresh; keep going with current creds. */ + ctx->state = IAKERB_TGS_REQ; + krb5_clear_error_message(ctx->k5c); + } else { + goto cleanup; + } + } else if (!(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) { + krb5_init_creds_get_times(ctx->k5c, ctx->icc, ×); + kg_cred_set_initial_refresh(ctx->k5c, cred, ×); + cred->expire = times.endtime; + + krb5_init_creds_free(ctx->k5c, ctx->icc); + ctx->icc = NULL; + + ctx->state = IAKERB_TGS_REQ; + } else + break; + in = empty_data(); + /* Done with AS request; fall through to TGS request. */ + case IAKERB_TGS_REQ: + if (ctx->tcc == NULL) { + code = iakerb_tkt_creds_ctx(ctx, cred, name, time_req); + if (code != 0) + goto cleanup; + } + + code = krb5_tkt_creds_step(ctx->k5c, ctx->tcc, &in, &out, &realm, + &flags); + if (code != 0) + goto cleanup; + if (!(flags & KRB5_TKT_CREDS_STEP_FLAG_CONTINUE)) { + krb5_tkt_creds_get_times(ctx->k5c, ctx->tcc, ×); + cred->expire = times.endtime; + + krb5_tkt_creds_free(ctx->k5c, ctx->tcc); + ctx->tcc = NULL; + + ctx->state = IAKERB_AP_REQ; + } else + break; + /* Done with TGS request; fall through to AP request. */ + case IAKERB_AP_REQ: + break; + } + + if (out.length != 0) { + assert(ctx->state != IAKERB_AP_REQ); + + code = iakerb_make_token(ctx, &realm, cookie, &out, + (input_token == GSS_C_NO_BUFFER), + output_token); + if (code != 0) + goto cleanup; + + /* Save the token for generating a future checksum */ + code = iakerb_save_token(ctx, output_token); + if (code != 0) + goto cleanup; + + ctx->count++; + } + +cleanup: + if (code != 0) + gss_release_buffer(&tmp, output_token); + krb5_free_data(ctx->k5c, cookie); + krb5_free_data_contents(ctx->k5c, &out); + krb5_free_data_contents(ctx->k5c, &realm); + + return code; +} + +/* + * Determine the starting IAKERB state for a context. If we already + * have a ticket, we may not need to do IAKERB at all. + */ +static krb5_error_code +iakerb_get_initial_state(iakerb_ctx_id_t ctx, + krb5_gss_cred_id_t cred, + krb5_gss_name_t target, + OM_uint32 time_req, + enum iakerb_state *state) +{ + krb5_creds in_creds, *out_creds = NULL; + krb5_error_code code; + + memset(&in_creds, 0, sizeof(in_creds)); + + in_creds.client = cred->name->princ; + in_creds.server = target->princ; + + if (cred->name->ad_context != NULL) { + code = krb5_authdata_export_authdata(ctx->k5c, + cred->name->ad_context, + AD_USAGE_TGS_REQ, + &in_creds.authdata); + if (code != 0) + goto cleanup; + } + + if (time_req != 0 && time_req != GSS_C_INDEFINITE) { + krb5_timestamp now; + + code = krb5_timeofday(ctx->k5c, &now); + if (code != 0) + goto cleanup; + + in_creds.times.endtime = now + time_req; + } + + /* Make an AS request if we have no creds or it's time to refresh them. */ + if (cred->expire == 0 || kg_cred_time_to_refresh(ctx->k5c, cred)) { + *state = IAKERB_AS_REQ; + code = 0; + goto cleanup; + } + + code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED, cred->ccache, + &in_creds, &out_creds); + if (code == KRB5_CC_NOTFOUND || code == KRB5_CC_NOT_KTYPE) { + *state = cred->have_tgt ? IAKERB_TGS_REQ : IAKERB_AS_REQ; + code = 0; + } else if (code == 0) { + *state = IAKERB_AP_REQ; + krb5_free_creds(ctx->k5c, out_creds); + } + +cleanup: + krb5_free_authdata(ctx->k5c, in_creds.authdata); + + return code; +} + +/* + * Allocate and initialise an IAKERB context + */ +static krb5_error_code +iakerb_alloc_context(iakerb_ctx_id_t *pctx, int initiate) +{ + iakerb_ctx_id_t ctx; + krb5_error_code code; + + *pctx = NULL; + + ctx = k5alloc(sizeof(*ctx), &code); + if (ctx == NULL) + goto cleanup; + ctx->defcred = GSS_C_NO_CREDENTIAL; + ctx->magic = KG_IAKERB_CONTEXT; + ctx->state = IAKERB_AS_REQ; + ctx->count = 0; + ctx->initiate = initiate; + ctx->established = 0; + + code = krb5_gss_init_context(&ctx->k5c); + if (code != 0) + goto cleanup; + + *pctx = ctx; + +cleanup: + if (code != 0) + iakerb_release_context(ctx); + + return code; +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_delete_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) +{ + iakerb_ctx_id_t iakerb_ctx = (iakerb_ctx_id_t)*context_handle; + + if (output_token != GSS_C_NO_BUFFER) { + output_token->length = 0; + output_token->value = NULL; + } + + *minor_status = 0; + *context_handle = GSS_C_NO_CONTEXT; + iakerb_release_context(iakerb_ctx); + + return GSS_S_COMPLETE; +} + +static krb5_boolean +iakerb_is_iakerb_token(const gss_buffer_t token) +{ + krb5_error_code code; + unsigned int bodysize = token->length; + unsigned char *ptr = token->value; + + code = g_verify_token_header(gss_mech_iakerb, + &bodysize, &ptr, + IAKERB_TOK_PROXY, + token->length, 0); + + return (code == 0); +} + +static void +iakerb_make_exts(iakerb_ctx_id_t ctx, krb5_gss_ctx_ext_rec *exts) +{ + memset(exts, 0, sizeof(*exts)); + + if (ctx->conv.length != 0) + exts->iakerb.conv = &ctx->conv; +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_accept_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_cred_id_t verifier_cred_handle, + gss_buffer_t input_token, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle) +{ + OM_uint32 major_status = GSS_S_FAILURE; + OM_uint32 code; + iakerb_ctx_id_t ctx; + int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT); + + if (initialContextToken) { + code = iakerb_alloc_context(&ctx, 0); + if (code != 0) + goto cleanup; + + } else + ctx = (iakerb_ctx_id_t)*context_handle; + + if (iakerb_is_iakerb_token(input_token)) { + if (ctx->gssc != GSS_C_NO_CONTEXT) { + /* We shouldn't get an IAKERB token now. */ + code = G_WRONG_TOKID; + major_status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + code = iakerb_acceptor_step(ctx, initialContextToken, + input_token, output_token); + if (code == (OM_uint32)KRB5_BAD_MSIZE) + major_status = GSS_S_DEFECTIVE_TOKEN; + if (code != 0) + goto cleanup; + if (initialContextToken) { + *context_handle = (gss_ctx_id_t)ctx; + ctx = NULL; + } + if (src_name != NULL) + *src_name = GSS_C_NO_NAME; + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_iakerb; + if (ret_flags != NULL) + *ret_flags = 0; + if (time_rec != NULL) + *time_rec = 0; + if (delegated_cred_handle != NULL) + *delegated_cred_handle = GSS_C_NO_CREDENTIAL; + major_status = GSS_S_CONTINUE_NEEDED; + } else { + krb5_gss_ctx_ext_rec exts; + + iakerb_make_exts(ctx, &exts); + + major_status = krb5_gss_accept_sec_context_ext(&code, + &ctx->gssc, + verifier_cred_handle, + input_token, + input_chan_bindings, + src_name, + NULL, + output_token, + ret_flags, + time_rec, + delegated_cred_handle, + &exts); + if (major_status == GSS_S_COMPLETE) + ctx->established = 1; + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_krb5; + } + +cleanup: + if (initialContextToken && GSS_ERROR(major_status)) { + iakerb_release_context(ctx); + *context_handle = GSS_C_NO_CONTEXT; + } + + *minor_status = code; + return major_status; +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_init_sec_context(OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec) +{ + OM_uint32 major_status = GSS_S_FAILURE; + krb5_error_code code; + iakerb_ctx_id_t ctx; + krb5_gss_cred_id_t kcred; + krb5_gss_name_t kname; + krb5_boolean cred_locked = FALSE; + int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT); + + if (initialContextToken) { + code = iakerb_alloc_context(&ctx, 1); + if (code != 0) { + *minor_status = code; + goto cleanup; + } + if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { + major_status = iakerb_gss_acquire_cred(minor_status, NULL, + GSS_C_INDEFINITE, + GSS_C_NULL_OID_SET, + GSS_C_INITIATE, + &ctx->defcred, NULL, NULL); + if (GSS_ERROR(major_status)) + goto cleanup; + claimant_cred_handle = ctx->defcred; + } + } else { + ctx = (iakerb_ctx_id_t)*context_handle; + if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) + claimant_cred_handle = ctx->defcred; + } + + kname = (krb5_gss_name_t)target_name; + + major_status = kg_cred_resolve(minor_status, ctx->k5c, + claimant_cred_handle, target_name); + if (GSS_ERROR(major_status)) + goto cleanup; + cred_locked = TRUE; + kcred = (krb5_gss_cred_id_t)claimant_cred_handle; + + major_status = GSS_S_FAILURE; + + if (initialContextToken) { + code = iakerb_get_initial_state(ctx, kcred, kname, time_req, + &ctx->state); + if (code != 0) { + *minor_status = code; + goto cleanup; + } + *context_handle = (gss_ctx_id_t)ctx; + } + + if (ctx->state != IAKERB_AP_REQ) { + /* We need to do IAKERB. */ + code = iakerb_initiator_step(ctx, + kcred, + kname, + time_req, + input_token, + output_token); + if (code == KRB5_BAD_MSIZE) + major_status = GSS_S_DEFECTIVE_TOKEN; + if (code != 0) { + *minor_status = code; + goto cleanup; + } + } + + if (ctx->state == IAKERB_AP_REQ) { + krb5_gss_ctx_ext_rec exts; + + if (cred_locked) { + k5_mutex_unlock(&kcred->lock); + cred_locked = FALSE; + } + + iakerb_make_exts(ctx, &exts); + + if (ctx->gssc == GSS_C_NO_CONTEXT) + input_token = GSS_C_NO_BUFFER; + + /* IAKERB is finished, or we skipped to Kerberos directly. */ + major_status = krb5_gss_init_sec_context_ext(minor_status, + (gss_cred_id_t) kcred, + &ctx->gssc, + target_name, + (gss_OID)gss_mech_iakerb, + req_flags, + time_req, + input_chan_bindings, + input_token, + NULL, + output_token, + ret_flags, + time_rec, + &exts); + if (major_status == GSS_S_COMPLETE) + ctx->established = 1; + if (actual_mech_type != NULL) + *actual_mech_type = (gss_OID)gss_mech_krb5; + } else { + if (actual_mech_type != NULL) + *actual_mech_type = (gss_OID)gss_mech_iakerb; + if (ret_flags != NULL) + *ret_flags = 0; + if (time_rec != NULL) + *time_rec = 0; + major_status = GSS_S_CONTINUE_NEEDED; + } + +cleanup: + if (cred_locked) + k5_mutex_unlock(&kcred->lock); + if (initialContextToken && GSS_ERROR(major_status)) { + iakerb_release_context(ctx); + *context_handle = GSS_C_NO_CONTEXT; + } + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_unwrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, int *conf_state, + gss_qop_t *qop_state) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_unwrap(minor_status, ctx->gssc, input_message_buffer, + output_message_buffer, conf_state, qop_state); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int conf_req_flag, gss_qop_t qop_req, + gss_buffer_t input_message_buffer, int *conf_state, + gss_buffer_t output_message_buffer) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_wrap(minor_status, ctx->gssc, conf_req_flag, qop_req, + input_message_buffer, conf_state, + output_message_buffer); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_process_context_token(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_buffer_t token_buffer) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_DEFECTIVE_TOKEN; + + return krb5_gss_process_context_token(minor_status, ctx->gssc, + token_buffer); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_context_time(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + OM_uint32 *time_rec) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_context_time(minor_status, ctx->gssc, time_rec); +} + +#ifndef LEAN_CLIENT + +OM_uint32 KRB5_CALLCONV +iakerb_gss_export_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t interprocess_token) +{ + OM_uint32 maj; + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)*context_handle; + + /* We don't currently support exporting partially established contexts. */ + if (!ctx->established) + return GSS_S_UNAVAILABLE; + + maj = krb5_gss_export_sec_context(minor_status, &ctx->gssc, + interprocess_token); + if (ctx->gssc == GSS_C_NO_CONTEXT) { + iakerb_release_context(ctx); + *context_handle = GSS_C_NO_CONTEXT; + } + return maj; +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_import_sec_context(OM_uint32 *minor_status, + gss_buffer_t interprocess_token, + gss_ctx_id_t *context_handle) +{ + OM_uint32 maj, tmpmin; + krb5_error_code code; + gss_ctx_id_t gssc; + krb5_gss_ctx_id_t kctx; + iakerb_ctx_id_t ctx; + + maj = krb5_gss_import_sec_context(minor_status, interprocess_token, &gssc); + if (maj != GSS_S_COMPLETE) + return maj; + kctx = (krb5_gss_ctx_id_t)gssc; + + if (!kctx->established) { + /* We don't currently support importing partially established + * contexts. */ + krb5_gss_delete_sec_context(&tmpmin, &gssc, GSS_C_NO_BUFFER); + return GSS_S_FAILURE; + } + + code = iakerb_alloc_context(&ctx, kctx->initiate); + if (code != 0) { + krb5_gss_delete_sec_context(&tmpmin, &gssc, GSS_C_NO_BUFFER); + *minor_status = code; + return GSS_S_FAILURE; + } + + ctx->gssc = gssc; + ctx->established = 1; + *context_handle = (gss_ctx_id_t)ctx; + return GSS_S_COMPLETE; +} +#endif /* LEAN_CLIENT */ + +OM_uint32 KRB5_CALLCONV +iakerb_gss_inquire_context(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, gss_name_t *src_name, + gss_name_t *targ_name, OM_uint32 *lifetime_rec, + gss_OID *mech_type, OM_uint32 *ctx_flags, + int *initiate, int *opened) +{ + OM_uint32 ret; + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (src_name != NULL) + *src_name = GSS_C_NO_NAME; + if (targ_name != NULL) + *targ_name = GSS_C_NO_NAME; + if (lifetime_rec != NULL) + *lifetime_rec = 0; + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_iakerb; + if (ctx_flags != NULL) + *ctx_flags = 0; + if (initiate != NULL) + *initiate = ctx->initiate; + if (opened != NULL) + *opened = ctx->established; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_COMPLETE; + + ret = krb5_gss_inquire_context(minor_status, ctx->gssc, src_name, + targ_name, lifetime_rec, mech_type, + ctx_flags, initiate, opened); + + if (!ctx->established) { + /* Report IAKERB as the mech OID until the context is established. */ + if (mech_type != NULL) + *mech_type = (gss_OID)gss_mech_iakerb; + + /* We don't support exporting partially-established contexts. */ + if (ctx_flags != NULL) + *ctx_flags &= ~GSS_C_TRANS_FLAG; + } + + return ret; +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap_size_limit(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, int conf_req_flag, + gss_qop_t qop_req, OM_uint32 req_output_size, + OM_uint32 *max_input_size) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_wrap_size_limit(minor_status, ctx->gssc, conf_req_flag, + qop_req, req_output_size, max_input_size); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t qop_req, gss_buffer_t message_buffer, + gss_buffer_t message_token) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_get_mic(minor_status, ctx->gssc, qop_req, message_buffer, + message_token); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_verify_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_buffer_t msg_buffer, gss_buffer_t token_buffer, + gss_qop_t *qop_state) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_verify_mic(minor_status, ctx->gssc, msg_buffer, + token_buffer, qop_state); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_inquire_sec_context_by_oid(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_UNAVAILABLE; + + return krb5_gss_inquire_sec_context_by_oid(minor_status, ctx->gssc, + desired_object, data_set); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_set_sec_context_option(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + const gss_OID desired_object, + const gss_buffer_t value) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)*context_handle; + + if (ctx == NULL || ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_UNAVAILABLE; + + return krb5_gss_set_sec_context_option(minor_status, &ctx->gssc, + desired_object, value); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int conf_req_flag, gss_qop_t qop_req, int *conf_state, + gss_iov_buffer_desc *iov, int iov_count) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_wrap_iov(minor_status, ctx->gssc, conf_req_flag, qop_req, + conf_state, iov, iov_count); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_unwrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int *conf_state, gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, int iov_count) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_unwrap_iov(minor_status, ctx->gssc, conf_state, qop_state, + iov, iov_count); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_wrap_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, int conf_req_flag, + gss_qop_t qop_req, int *conf_state, + gss_iov_buffer_desc *iov, int iov_count) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_wrap_iov_length(minor_status, ctx->gssc, conf_req_flag, + qop_req, conf_state, iov, iov_count); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + int prf_key, const gss_buffer_t prf_in, + ssize_t desired_output_len, gss_buffer_t prf_out) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_pseudo_random(minor_status, ctx->gssc, prf_key, prf_in, + desired_output_len, prf_out); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t qop_req, gss_iov_buffer_desc *iov, + int iov_count) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_get_mic_iov(minor_status, ctx->gssc, qop_req, iov, + iov_count); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle, + gss_qop_t *qop_state, gss_iov_buffer_desc *iov, + int iov_count) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_verify_mic_iov(minor_status, ctx->gssc, qop_state, iov, + iov_count); +} + +OM_uint32 KRB5_CALLCONV +iakerb_gss_get_mic_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, gss_qop_t qop_req, + gss_iov_buffer_desc *iov, int iov_count) +{ + iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle; + + if (ctx->gssc == GSS_C_NO_CONTEXT) + return GSS_S_NO_CONTEXT; + + return krb5_gss_get_mic_iov_length(minor_status, ctx->gssc, qop_req, iov, + iov_count); +} diff --git a/src/lib/gssapi/krb5/import_cred.c b/src/lib/gssapi/krb5/import_cred.c new file mode 100644 index 000000000000..f0a0373bfb79 --- /dev/null +++ b/src/lib/gssapi/krb5/import_cred.c @@ -0,0 +1,648 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/import_cred.c - krb5 import_cred implementation */ +/* + * Copyright (C) 2012 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "k5-int.h" +#include "k5-json.h" +#include "gssapiP_krb5.h" + +/* Return the idx element of array if it is of type tid; otherwise return + * NULL. The caller is responsible for checking the array length. */ +static k5_json_value +check_element(k5_json_array array, size_t idx, k5_json_tid tid) +{ + k5_json_value v; + + v = k5_json_array_get(array, idx); + return (k5_json_get_tid(v) == tid) ? v : NULL; +} + +/* All of the json_to_x functions return 0 on success, -1 on failure (either + * from running out of memory or from defective input). */ + +/* Convert a JSON value to a C string or to NULL. */ +static int +json_to_optional_string(k5_json_value v, char **string_out) +{ + *string_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_STRING) + return -1; + *string_out = strdup(k5_json_string_utf8(v)); + return (*string_out == NULL) ? -1 : 0; +} + +/* Convert a JSON value to a principal or to NULL. */ +static int +json_to_principal(krb5_context context, k5_json_value v, + krb5_principal *princ_out) +{ + *princ_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_STRING) + return -1; + if (krb5_parse_name(context, k5_json_string_utf8(v), princ_out)) + return -1; + return 0; +} + +/* Convert a JSON value to a zero-terminated enctypes list or to NULL. */ +static int +json_to_etypes(k5_json_value v, krb5_enctype **etypes_out) +{ + krb5_enctype *etypes = NULL; + k5_json_array array; + k5_json_number n; + size_t len, i; + + *etypes_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + len = k5_json_array_length(array); + etypes = calloc(len + 1, sizeof(*etypes)); + for (i = 0; i < len; i++) { + n = check_element(array, i, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + etypes[i] = k5_json_number_value(n); + } + *etypes_out = etypes; + return 0; + +invalid: + free(etypes); + return -1; +} + +/* Convert a JSON value to a krb5 GSS name or to NULL. */ +static int +json_to_kgname(krb5_context context, k5_json_value v, + krb5_gss_name_t *name_out) +{ + k5_json_array array; + krb5_gss_name_t name = NULL; + + *name_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + if (k5_json_array_length(array) != 3) + return -1; + name = calloc(1, sizeof(*name)); + if (name == NULL) + return -1; + if (k5_mutex_init(&name->lock)) { + free(name); + return -1; + } + + if (json_to_principal(context, k5_json_array_get(array, 0), &name->princ)) + goto invalid; + if (json_to_optional_string(k5_json_array_get(array, 1), &name->service)) + goto invalid; + if (json_to_optional_string(k5_json_array_get(array, 2), &name->host)) + goto invalid; + + *name_out = name; + return 0; + +invalid: + kg_release_name(context, &name); + return -1; +} + +/* Convert a JSON value to a keytab handle or to NULL. */ +static int +json_to_keytab(krb5_context context, k5_json_value v, krb5_keytab *keytab_out) +{ + *keytab_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_STRING) + return -1; + if (krb5_kt_resolve(context, k5_json_string_utf8(v), keytab_out)) + return -1; + return 0; +} + +/* Convert a JSON value to an rcache handle or to NULL. */ +static int +json_to_rcache(krb5_context context, k5_json_value v, krb5_rcache *rcache_out) +{ + krb5_rcache rcache; + + *rcache_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_STRING) + return -1; + if (krb5_rc_resolve_full(context, &rcache, (char *)k5_json_string_utf8(v))) + return -1; + if (krb5_rc_recover_or_initialize(context, rcache, context->clockskew)) { + krb5_rc_close(context, rcache); + return -1; + } + *rcache_out = rcache; + return 0; +} + +/* Convert a JSON value to a keyblock, filling in keyblock. */ +static int +json_to_keyblock(k5_json_value v, krb5_keyblock *keyblock) +{ + k5_json_array array; + k5_json_number n; + k5_json_string s; + size_t len; + + memset(keyblock, 0, sizeof(*keyblock)); + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + if (k5_json_array_length(array) != 2) + return -1; + + n = check_element(array, 0, K5_JSON_TID_NUMBER); + if (n == NULL) + return -1; + keyblock->enctype = k5_json_number_value(n); + + s = check_element(array, 1, K5_JSON_TID_STRING); + if (s == NULL) + return -1; + if (k5_json_string_unbase64(s, &keyblock->contents, &len)) + return -1; + keyblock->length = len; + keyblock->magic = KV5M_KEYBLOCK; + return 0; +} + +/* Convert a JSON value to a krb5 address. */ +static int +json_to_address(k5_json_value v, krb5_address **addr_out) +{ + k5_json_array array; + krb5_address *addr = NULL; + k5_json_number n; + k5_json_string s; + size_t len; + + *addr_out = NULL; + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + if (k5_json_array_length(array) != 2) + return -1; + + n = check_element(array, 0, K5_JSON_TID_NUMBER); + if (n == NULL) + return -1; + s = check_element(array, 1, K5_JSON_TID_STRING); + if (s == NULL) + return -1; + + addr = malloc(sizeof(*addr)); + if (addr == NULL) + return -1; + addr->addrtype = k5_json_number_value(n); + if (k5_json_string_unbase64(s, &addr->contents, &len)) { + free(addr); + return -1; + } + addr->length = len; + addr->magic = KV5M_ADDRESS; + *addr_out = addr; + return 0; +} + +/* Convert a JSON value to a null-terminated list of krb5 addresses or to + * NULL. */ +static int +json_to_addresses(krb5_context context, k5_json_value v, + krb5_address ***addresses_out) +{ + k5_json_array array; + krb5_address **addrs = NULL; + size_t len, i; + + *addresses_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + len = k5_json_array_length(array); + addrs = calloc(len + 1, sizeof(*addrs)); + for (i = 0; i < len; i++) { + if (json_to_address(k5_json_array_get(array, i), &addrs[i])) + goto invalid; + } + addrs[i] = NULL; + *addresses_out = addrs; + return 0; + +invalid: + krb5_free_addresses(context, addrs); + return -1; +} + +/* Convert a JSON value to an authdata element. */ +static int +json_to_authdata_element(k5_json_value v, krb5_authdata **ad_out) +{ + k5_json_array array; + krb5_authdata *ad = NULL; + k5_json_number n; + k5_json_string s; + size_t len; + + *ad_out = NULL; + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + if (k5_json_array_length(array) != 2) + return -1; + + n = check_element(array, 0, K5_JSON_TID_NUMBER); + if (n == NULL) + return -1; + s = check_element(array, 1, K5_JSON_TID_STRING); + if (s == NULL) + return -1; + + ad = malloc(sizeof(*ad)); + if (ad == NULL) + return -1; + ad->ad_type = k5_json_number_value(n); + if (k5_json_string_unbase64(s, &ad->contents, &len)) { + free(ad); + return -1; + } + ad->length = len; + ad->magic = KV5M_AUTHDATA; + *ad_out = ad; + return 0; +} + +/* Convert a JSON value to a null-terminated authdata list or to NULL. */ +static int +json_to_authdata(krb5_context context, k5_json_value v, + krb5_authdata ***authdata_out) +{ + k5_json_array array; + krb5_authdata **authdata = NULL; + size_t len, i; + + *authdata_out = NULL; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + len = k5_json_array_length(array); + authdata = calloc(len + 1, sizeof(*authdata)); + for (i = 0; i < len; i++) { + if (json_to_authdata_element(k5_json_array_get(array, i), + &authdata[i])) + goto invalid; + } + authdata[i] = NULL; + *authdata_out = authdata; + return 0; + +invalid: + krb5_free_authdata(context, authdata); + return -1; +} + +/* Convert a JSON value to a krb5 credential structure, filling in creds. */ +static int +json_to_creds(krb5_context context, k5_json_value v, krb5_creds *creds) +{ + k5_json_array array; + k5_json_number n; + k5_json_bool b; + k5_json_string s; + unsigned char *data; + size_t len; + + memset(creds, 0, sizeof(*creds)); + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + if (k5_json_array_length(array) != 13) + return -1; + + if (json_to_principal(context, k5_json_array_get(array, 0), + &creds->client)) + goto invalid; + + if (json_to_principal(context, k5_json_array_get(array, 1), + &creds->server)) + goto invalid; + + if (json_to_keyblock(k5_json_array_get(array, 2), &creds->keyblock)) + goto invalid; + + n = check_element(array, 3, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + creds->times.authtime = k5_json_number_value(n); + + n = check_element(array, 4, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + creds->times.starttime = k5_json_number_value(n); + + n = check_element(array, 5, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + creds->times.endtime = k5_json_number_value(n); + + n = check_element(array, 6, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + creds->times.renew_till = k5_json_number_value(n); + + b = check_element(array, 7, K5_JSON_TID_BOOL); + if (b == NULL) + goto invalid; + creds->is_skey = k5_json_bool_value(b); + + n = check_element(array, 8, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + creds->ticket_flags = k5_json_number_value(n); + + if (json_to_addresses(context, k5_json_array_get(array, 9), + &creds->addresses)) + goto invalid; + + s = check_element(array, 10, K5_JSON_TID_STRING); + if (s == NULL) + goto invalid; + if (k5_json_string_unbase64(s, &data, &len)) + goto invalid; + creds->ticket.data = (char *)data; + creds->ticket.length = len; + + s = check_element(array, 11, K5_JSON_TID_STRING); + if (s == NULL) + goto invalid; + if (k5_json_string_unbase64(s, &data, &len)) + goto invalid; + creds->second_ticket.data = (char *)data; + creds->second_ticket.length = len; + + if (json_to_authdata(context, k5_json_array_get(array, 12), + &creds->authdata)) + goto invalid; + + creds->magic = KV5M_CREDS; + return 0; + +invalid: + krb5_free_cred_contents(context, creds); + memset(creds, 0, sizeof(*creds)); + return -1; +} + +/* Convert a JSON value to a ccache handle or to NULL. Set *new_out to true if + * the ccache handle is a newly created memory ccache, false otherwise. */ +static int +json_to_ccache(krb5_context context, k5_json_value v, krb5_ccache *ccache_out, + krb5_boolean *new_out) +{ + krb5_error_code ret; + krb5_ccache ccache = NULL; + krb5_principal princ; + krb5_creds creds; + k5_json_array array; + size_t i, len; + + *ccache_out = NULL; + *new_out = FALSE; + if (k5_json_get_tid(v) == K5_JSON_TID_NULL) + return 0; + if (k5_json_get_tid(v) == K5_JSON_TID_STRING) { + /* We got a reference to an external ccache; just resolve it. */ + return krb5_cc_resolve(context, k5_json_string_utf8(v), ccache_out) ? + -1 : 0; + } + + /* We got the contents of a memory ccache. */ + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + return -1; + array = v; + len = k5_json_array_length(array); + if (len < 1) + return -1; + + /* Initialize a new memory ccache using the principal in the first array + * entry.*/ + if (krb5_cc_new_unique(context, "MEMORY", NULL, &ccache)) + return -1; + if (json_to_principal(context, k5_json_array_get(array, 0), &princ)) + goto invalid; + ret = krb5_cc_initialize(context, ccache, princ); + krb5_free_principal(context, princ); + if (ret) + goto invalid; + + /* Add remaining array entries to the ccache as credentials. */ + for (i = 1; i < len; i++) { + if (json_to_creds(context, k5_json_array_get(array, i), &creds)) + goto invalid; + ret = krb5_cc_store_cred(context, ccache, &creds); + krb5_free_cred_contents(context, &creds); + if (ret) + goto invalid; + } + + *ccache_out = ccache; + *new_out = TRUE; + return 0; + +invalid: + (void)krb5_cc_destroy(context, ccache); + return -1; +} + +/* Convert a JSON array value to a krb5 GSS credential. */ +static int +json_to_kgcred(krb5_context context, k5_json_array array, + krb5_gss_cred_id_t *cred_out) +{ + krb5_gss_cred_id_t cred; + k5_json_number n; + k5_json_bool b; + krb5_boolean is_new; + OM_uint32 tmp; + + *cred_out = NULL; + if (k5_json_array_length(array) != 14) + return -1; + + cred = calloc(1, sizeof(*cred)); + if (cred == NULL) + return -1; + if (k5_mutex_init(&cred->lock)) { + free(cred); + return -1; + } + + n = check_element(array, 0, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + cred->usage = k5_json_number_value(n); + + if (json_to_kgname(context, k5_json_array_get(array, 1), &cred->name)) + goto invalid; + + if (json_to_principal(context, k5_json_array_get(array, 2), + &cred->impersonator)) + goto invalid; + + b = check_element(array, 3, K5_JSON_TID_BOOL); + if (b == NULL) + goto invalid; + cred->default_identity = k5_json_bool_value(b); + + b = check_element(array, 4, K5_JSON_TID_BOOL); + if (b == NULL) + goto invalid; + cred->iakerb_mech = k5_json_bool_value(b); + + if (json_to_keytab(context, k5_json_array_get(array, 5), &cred->keytab)) + goto invalid; + + if (json_to_rcache(context, k5_json_array_get(array, 6), &cred->rcache)) + goto invalid; + + if (json_to_ccache(context, k5_json_array_get(array, 7), &cred->ccache, + &is_new)) + goto invalid; + cred->destroy_ccache = is_new; + + if (json_to_keytab(context, k5_json_array_get(array, 8), + &cred->client_keytab)) + goto invalid; + + b = check_element(array, 9, K5_JSON_TID_BOOL); + if (b == NULL) + goto invalid; + cred->have_tgt = k5_json_bool_value(b); + + n = check_element(array, 10, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + cred->expire = k5_json_number_value(n); + + n = check_element(array, 11, K5_JSON_TID_NUMBER); + if (n == NULL) + goto invalid; + cred->refresh_time = k5_json_number_value(n); + + if (json_to_etypes(k5_json_array_get(array, 12), &cred->req_enctypes)) + goto invalid; + + if (json_to_optional_string(k5_json_array_get(array, 13), &cred->password)) + goto invalid; + + *cred_out = cred; + return 0; + +invalid: + (void)krb5_gss_release_cred(&tmp, (gss_cred_id_t *)&cred); + return -1; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_import_cred(OM_uint32 *minor_status, gss_buffer_t token, + gss_cred_id_t *cred_handle) +{ + OM_uint32 status = GSS_S_COMPLETE; + krb5_context context; + krb5_error_code ret; + krb5_gss_cred_id_t cred; + k5_json_value v = NULL; + k5_json_array array; + k5_json_string str; + char *copy = NULL; + + ret = krb5_gss_init_context(&context); + if (ret) { + *minor_status = ret; + return GSS_S_FAILURE; + } + + /* Decode token. */ + copy = k5memdup0(token->value, token->length, &ret); + if (copy == NULL) { + status = GSS_S_FAILURE; + *minor_status = ret; + goto cleanup; + } + if (k5_json_decode(copy, &v)) + goto invalid; + + /* Decode the CRED_EXPORT_MAGIC array wrapper. */ + if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY) + goto invalid; + array = v; + if (k5_json_array_length(array) != 2) + goto invalid; + str = check_element(array, 0, K5_JSON_TID_STRING); + if (str == NULL || + strcmp(k5_json_string_utf8(str), CRED_EXPORT_MAGIC) != 0) + goto invalid; + if (json_to_kgcred(context, k5_json_array_get(array, 1), &cred)) + goto invalid; + + *cred_handle = (gss_cred_id_t)cred; + +cleanup: + free(copy); + k5_json_release(v); + krb5_free_context(context); + return status; + +invalid: + status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; +} diff --git a/src/lib/gssapi/krb5/import_name.c b/src/lib/gssapi/krb5/import_name.c new file mode 100644 index 000000000000..3f5492b99f6b --- /dev/null +++ b/src/lib/gssapi/krb5/import_name.c @@ -0,0 +1,332 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_krb5.h" + +#ifndef NO_PASSWORD +#include <pwd.h> +#include <stdio.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +/* + * errors: + * GSS_S_BAD_NAMETYPE if the type is bogus + * GSS_S_BAD_NAME if the type is good but the name is bogus + * GSS_S_FAILURE if memory allocation fails + */ + +/* + * Import serialized authdata context + */ +static krb5_error_code +import_name_composite(krb5_context context, + unsigned char *enc_data, size_t enc_length, + krb5_authdata_context *pad_context) +{ + krb5_authdata_context ad_context; + krb5_error_code code; + krb5_data data; + + if (enc_length == 0) + return 0; + + code = krb5_authdata_context_init(context, &ad_context); + if (code != 0) + return code; + + data.data = (char *)enc_data; + data.length = enc_length; + + code = krb5_authdata_import_attributes(context, + ad_context, + AD_USAGE_MASK, + &data); + if (code != 0) { + krb5_authdata_context_free(context, ad_context); + return code; + } + + *pad_context = ad_context; + + return 0; +} + +/* Split a host-based name "service[@host]" into allocated strings + * placed in *service_out and *host_out (possibly NULL). */ +static krb5_error_code +parse_hostbased(const char *str, size_t len, + char **service_out, char **host_out) +{ + const char *at; + size_t servicelen, hostlen; + char *service, *host = NULL; + + *service_out = *host_out = NULL; + + /* Find the bound of the service name and copy it. */ + at = memchr(str, '@', len); + servicelen = (at == NULL) ? len : (size_t)(at - str); + service = xmalloc(servicelen + 1); + if (service == NULL) + return ENOMEM; + memcpy(service, str, servicelen); + service[servicelen] = '\0'; + + /* If present, copy the hostname. */ + if (at != NULL) { + hostlen = len - servicelen - 1; + host = malloc(hostlen + 1); + if (host == NULL) { + free(service); + return ENOMEM; + } + memcpy(host, at + 1, hostlen); + host[hostlen] = '\0'; + } + + *service_out = service; + *host_out = host; + return 0; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_import_name(minor_status, input_name_buffer, + input_name_type, output_name) + OM_uint32 *minor_status; + gss_buffer_t input_name_buffer; + gss_OID input_name_type; + gss_name_t *output_name; +{ + krb5_context context; + krb5_principal princ = NULL; + krb5_error_code code; + unsigned char *cp, *end; + char *tmp = NULL, *tmp2 = NULL, *service = NULL, *host = NULL, *stringrep; + ssize_t length; +#ifndef NO_PASSWORD + struct passwd *pw; +#endif + int is_composite = 0; + krb5_authdata_context ad_context = NULL; + OM_uint32 status = GSS_S_FAILURE; + krb5_gss_name_t name; + + *output_name = NULL; + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code) + goto cleanup; + + if ((input_name_type != GSS_C_NULL_OID) && + (g_OID_equal(input_name_type, gss_nt_service_name) || + g_OID_equal(input_name_type, gss_nt_service_name_v2))) { + /* Split the name into service and host (or NULL). */ + code = parse_hostbased(input_name_buffer->value, + input_name_buffer->length, &service, &host); + if (code) + goto cleanup; + + /* + * Compute the initiator target name. In some cases this is a waste of + * getaddrinfo/getnameinfo queries, but computing the name when we need + * it would require a lot of code changes. + */ + code = krb5_sname_to_principal(context, host, service, KRB5_NT_SRV_HST, + &princ); + if (code) + goto cleanup; + } else if ((input_name_type != GSS_C_NULL_OID) && + (g_OID_equal(input_name_type, gss_nt_krb5_principal))) { + krb5_principal input; + + if (input_name_buffer->length != sizeof(krb5_principal)) { + code = G_WRONG_SIZE; + status = GSS_S_BAD_NAME; + goto cleanup; + } + + input = *((krb5_principal *) input_name_buffer->value); + + code = krb5_copy_principal(context, input, &princ); + if (code) + goto cleanup; + } else if ((input_name_type != NULL) && + g_OID_equal(input_name_type, GSS_C_NT_ANONYMOUS)) { + code = krb5_copy_principal(context, krb5_anonymous_principal(), + &princ); + if (code) + goto cleanup; + } else { +#ifndef NO_PASSWORD + uid_t uid; + struct passwd pwx; + char pwbuf[BUFSIZ]; +#endif + + stringrep = NULL; + + tmp = k5memdup0(input_name_buffer->value, input_name_buffer->length, + &code); + if (tmp == NULL) + goto cleanup; + tmp2 = NULL; + + /* Find the appropriate string rep to pass into parse_name. */ + if ((input_name_type == GSS_C_NULL_OID) || + g_OID_equal(input_name_type, gss_nt_krb5_name) || + g_OID_equal(input_name_type, gss_nt_user_name)) { + stringrep = (char *) tmp; +#ifndef NO_PASSWORD + } else if (g_OID_equal(input_name_type, gss_nt_machine_uid_name)) { + uid = *(uid_t *) input_name_buffer->value; + do_getpwuid: + if (k5_getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw) == 0) + stringrep = pw->pw_name; + else + code = G_NOUSER; + } else if (g_OID_equal(input_name_type, gss_nt_string_uid_name)) { + uid = atoi(tmp); + goto do_getpwuid; +#endif + } else if (g_OID_equal(input_name_type, gss_nt_exported_name) || + g_OID_equal(input_name_type, GSS_C_NT_COMPOSITE_EXPORT)) { +#define BOUNDS_CHECK(cp, end, n) \ + do { if ((end) - (cp) < (n)) goto fail_name; } while (0) + cp = (unsigned char *)tmp; + end = cp + input_name_buffer->length; + + BOUNDS_CHECK(cp, end, 2); + if (*cp++ != 0x04) + goto fail_name; + switch (*cp++) { + case 0x01: + break; + case 0x02: + is_composite++; + break; + default: + goto fail_name; + } + + BOUNDS_CHECK(cp, end, 2); + if (*cp++ != 0x00) + goto fail_name; + length = *cp++; + if (length != (ssize_t)gss_mech_krb5->length+2) + goto fail_name; + + BOUNDS_CHECK(cp, end, 2); + if (*cp++ != 0x06) + goto fail_name; + length = *cp++; + if (length != (ssize_t)gss_mech_krb5->length) + goto fail_name; + + BOUNDS_CHECK(cp, end, length); + if (memcmp(cp, gss_mech_krb5->elements, length) != 0) + goto fail_name; + cp += length; + + BOUNDS_CHECK(cp, end, 4); + length = *cp++; + length = (length << 8) | *cp++; + length = (length << 8) | *cp++; + length = (length << 8) | *cp++; + + BOUNDS_CHECK(cp, end, length); + tmp2 = k5alloc(length + 1, &code); + if (tmp2 == NULL) + goto cleanup; + strncpy(tmp2, (char *)cp, length); + tmp2[length] = 0; + stringrep = tmp2; + cp += length; + + if (is_composite) { + BOUNDS_CHECK(cp, end, 4); + length = *cp++; + length = (length << 8) | *cp++; + length = (length << 8) | *cp++; + length = (length << 8) | *cp++; + + BOUNDS_CHECK(cp, end, length); + code = import_name_composite(context, + cp, length, + &ad_context); + if (code != 0) + goto fail_name; + cp += length; + } + assert(cp == end); + } else { + status = GSS_S_BAD_NAMETYPE; + goto cleanup; + } + + /* At this point, stringrep is set, or if not, code is. */ + if (stringrep) { + code = krb5_parse_name(context, (char *)stringrep, &princ); + if (code) + goto cleanup; + } else { + fail_name: + status = GSS_S_BAD_NAME; + goto cleanup; + } + } + + /* Create a name and save it in the validation database. */ + code = kg_init_name(context, princ, service, host, ad_context, + KG_INIT_NAME_NO_COPY, &name); + if (code) + goto cleanup; + princ = NULL; + ad_context = NULL; + service = host = NULL; + *output_name = (gss_name_t)name; + status = GSS_S_COMPLETE; + +cleanup: + *minor_status = (OM_uint32)code; + if (*minor_status) + save_error_info(*minor_status, context); + krb5_free_principal(context, princ); + krb5_authdata_context_free(context, ad_context); + krb5_free_context(context); + free(tmp); + free(tmp2); + free(service); + free(host); + return status; +} diff --git a/src/lib/gssapi/krb5/import_sec_context.c b/src/lib/gssapi/krb5/import_sec_context.c new file mode 100644 index 000000000000..4190adf35cab --- /dev/null +++ b/src/lib/gssapi/krb5/import_sec_context.c @@ -0,0 +1,118 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/import_sec_context.c - Internalize the security context */ +/* + * Copyright 1995,2004,2007,2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "gssapiP_krb5.h" +/* for serialization initialization functions */ +#include "k5-int.h" + +/* + * Fix up the OID of the mechanism so that uses the static version of + * the OID if possible. + */ +gss_OID krb5_gss_convert_static_mech_oid(oid) + gss_OID oid; +{ + const gss_OID_desc *p; + OM_uint32 minor_status; + + for (p = krb5_gss_oid_array; p->length; p++) { + if ((oid->length == p->length) && + (memcmp(oid->elements, p->elements, p->length) == 0)) { + generic_gss_release_oid(&minor_status, &oid); + return (gss_OID) p; + } + } + return oid; +} + +krb5_error_code +krb5_gss_ser_init (krb5_context context) +{ + krb5_error_code code; + static krb5_error_code (KRB5_CALLCONV *const fns[])(krb5_context) = { + krb5_ser_context_init, krb5_ser_auth_context_init, + krb5_ser_ccache_init, krb5_ser_rcache_init, krb5_ser_keytab_init, + }; + unsigned int i; + + for (i = 0; i < sizeof(fns)/sizeof(fns[0]); i++) + if ((code = (fns[i])(context)) != 0) + return code; + return 0; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_import_sec_context(minor_status, interprocess_token, context_handle) + OM_uint32 *minor_status; + gss_buffer_t interprocess_token; + gss_ctx_id_t *context_handle; +{ + krb5_context context; + krb5_error_code kret = 0; + size_t blen; + krb5_gss_ctx_id_t ctx; + krb5_octet *ibp; + + /* This is a bit screwy. We create a krb5 context because we need + one when calling the serialization code. However, one of the + objects we're unpacking is a krb5 context, so when we finish, + we can throw this one away. */ + kret = krb5_gss_init_context(&context); + if (kret) { + *minor_status = kret; + return GSS_S_FAILURE; + } + kret = krb5_gss_ser_init(context); + if (kret) { + *minor_status = kret; + save_error_info(*minor_status, context); + krb5_free_context(context); + return GSS_S_FAILURE; + } + + /* Assume a tragic failure */ + ctx = (krb5_gss_ctx_id_t) NULL; + *minor_status = 0; + + /* Internalize the context */ + ibp = (krb5_octet *) interprocess_token->value; + blen = (size_t) interprocess_token->length; + kret = kg_ctx_internalize(context, (krb5_pointer *) &ctx, &ibp, &blen); + if (kret) { + *minor_status = (OM_uint32) kret; + save_error_info(*minor_status, context); + krb5_free_context(context); + return(GSS_S_FAILURE); + } + krb5_free_context(context); + + ctx->mech_used = krb5_gss_convert_static_mech_oid(ctx->mech_used); + + *context_handle = (gss_ctx_id_t) ctx; + + *minor_status = 0; + return (GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/indicate_mechs.c b/src/lib/gssapi/krb5/indicate_mechs.c new file mode 100644 index 000000000000..45538cb779aa --- /dev/null +++ b/src/lib/gssapi/krb5/indicate_mechs.c @@ -0,0 +1,37 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_krb5.h" +#include "mglueP.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_indicate_mechs(minor_status, mech_set) + OM_uint32 *minor_status; + gss_OID_set *mech_set; +{ + return generic_gss_copy_oid_set(minor_status, kg_all_mechs, mech_set); +} diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c new file mode 100644 index 000000000000..70f7955ae1ae --- /dev/null +++ b/src/lib/gssapi/krb5/init_sec_context.c @@ -0,0 +1,1104 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2000, 2002, 2003, 2007, 2008 by the Massachusetts Institute of + * Technology. All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#include <stdlib.h> +#include <assert.h> + +/* + * $Id$ + */ + +/* XXX This is for debugging only!!! Should become a real bitfield + at some point */ +int krb5_gss_dbg_client_expcreds = 0; + +/* + * Common code which fetches the correct krb5 credentials from the + * ccache. + */ +static krb5_error_code get_credentials(context, cred, server, now, + endtime, out_creds) + krb5_context context; + krb5_gss_cred_id_t cred; + krb5_gss_name_t server; + krb5_timestamp now; + krb5_timestamp endtime; + krb5_creds **out_creds; +{ + krb5_error_code code; + krb5_creds in_creds, evidence_creds, mcreds, *result_creds = NULL; + krb5_flags flags = 0; + + *out_creds = NULL; + + k5_mutex_assert_locked(&cred->lock); + memset(&in_creds, 0, sizeof(krb5_creds)); + memset(&evidence_creds, 0, sizeof(krb5_creds)); + in_creds.client = in_creds.server = NULL; + + assert(cred->name != NULL); + + in_creds.client = cred->name->princ; + in_creds.server = server->princ; + in_creds.times.endtime = endtime; + in_creds.authdata = NULL; + in_creds.keyblock.enctype = 0; + + /* + * cred->name is immutable, so there is no need to acquire + * cred->name->lock. + */ + if (cred->name->ad_context != NULL) { + code = krb5_authdata_export_authdata(context, + cred->name->ad_context, + AD_USAGE_TGS_REQ, + &in_creds.authdata); + if (code != 0) + goto cleanup; + } + + /* + * For IAKERB or constrained delegation, only check the cache in this step. + * For IAKERB we will ask the server to make any necessary TGS requests; + * for constrained delegation we will adjust in_creds and make an S4U2Proxy + * request below if the cache lookup fails. + */ + if (cred->impersonator != NULL || cred->iakerb_mech) + flags |= KRB5_GC_CACHED; + + code = krb5_get_credentials(context, flags, cred->ccache, + &in_creds, &result_creds); + + /* + * Try constrained delegation if we have proxy credentials, unless + * we are trying to get a ticket to ourselves (in which case we could + * just use the evidence ticket directly from cache). + */ + if (code == KRB5_CC_NOTFOUND && cred->impersonator != NULL && + !cred->iakerb_mech && + !krb5_principal_compare(context, cred->impersonator, server->princ)) { + + memset(&mcreds, 0, sizeof(mcreds)); + mcreds.magic = KV5M_CREDS; + mcreds.server = cred->impersonator; + mcreds.client = cred->name->princ; + code = krb5_cc_retrieve_cred(context, cred->ccache, + KRB5_TC_MATCH_AUTHDATA, &mcreds, + &evidence_creds); + if (code) + goto cleanup; + + assert(evidence_creds.ticket_flags & TKT_FLG_FORWARDABLE); + in_creds.client = cred->impersonator; + in_creds.second_ticket = evidence_creds.ticket; + flags = KRB5_GC_CANONICALIZE | KRB5_GC_CONSTRAINED_DELEGATION; + code = krb5_get_credentials(context, flags, cred->ccache, + &in_creds, &result_creds); + } + + if (code) + goto cleanup; + + if (flags & KRB5_GC_CONSTRAINED_DELEGATION) { + if (!krb5_principal_compare(context, cred->name->princ, + result_creds->client)) { + /* server did not support constrained delegation */ + code = KRB5_KDCREP_MODIFIED; + goto cleanup; + } + } + + /* + * Enforce a stricter limit (without timeskew forgiveness at the + * boundaries) because accept_sec_context code is also similarly + * non-forgiving. + */ + if (!krb5_gss_dbg_client_expcreds && result_creds->times.endtime < now) { + code = KRB5KRB_AP_ERR_TKT_EXPIRED; + goto cleanup; + } + + *out_creds = result_creds; + result_creds = NULL; + +cleanup: + krb5_free_authdata(context, in_creds.authdata); + krb5_free_cred_contents(context, &evidence_creds); + krb5_free_creds(context, result_creds); + + return code; +} +struct gss_checksum_data { + krb5_gss_ctx_id_rec *ctx; + krb5_gss_cred_id_t cred; + krb5_checksum md5; + krb5_data checksum_data; + krb5_gss_ctx_ext_t exts; +}; + +#ifdef CFX_EXERCISE +#include "../../krb5/krb/auth_con.h" +#endif +static krb5_error_code KRB5_CALLCONV +make_gss_checksum (krb5_context context, krb5_auth_context auth_context, + void *cksum_data, krb5_data **out) +{ + krb5_error_code code; + krb5_int32 con_flags; + unsigned char *ptr; + struct gss_checksum_data *data = cksum_data; + krb5_data credmsg; + unsigned int junk; + krb5_data *finished = NULL; + krb5_key send_subkey; + + data->checksum_data.data = 0; + credmsg.data = 0; + /* build the checksum field */ + + if (data->ctx->gss_flags & GSS_C_DELEG_FLAG) { + /* first get KRB_CRED message, so we know its length */ + + /* clear the time check flag that was set in krb5_auth_con_init() */ + krb5_auth_con_getflags(context, auth_context, &con_flags); + krb5_auth_con_setflags(context, auth_context, + con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME); + + assert(data->cred->name != NULL); + + /* + * RFC 4121 4.1.1 specifies forwarded credentials must be encrypted in + * the session key, but krb5_fwd_tgt_creds will use the send subkey if + * it's set in the auth context. Suppress the send subkey + * temporarily. + */ + krb5_auth_con_getsendsubkey_k(context, auth_context, &send_subkey); + krb5_auth_con_setsendsubkey_k(context, auth_context, NULL); + + code = krb5_fwd_tgt_creds(context, auth_context, 0, + data->cred->name->princ, data->ctx->there->princ, + data->cred->ccache, 1, + &credmsg); + + /* Turn KRB5_AUTH_CONTEXT_DO_TIME back on and reset the send subkey. */ + krb5_auth_con_setflags(context, auth_context, con_flags); + krb5_auth_con_setsendsubkey_k(context, auth_context, send_subkey); + krb5_k_free_key(context, send_subkey); + + if (code) { + /* don't fail here; just don't accept/do the delegation + request */ + data->ctx->gss_flags &= ~(GSS_C_DELEG_FLAG | + GSS_C_DELEG_POLICY_FLAG); + + data->checksum_data.length = 24; + } else { + if (credmsg.length+28 > KRB5_INT16_MAX) { + code = KRB5KRB_ERR_FIELD_TOOLONG; + goto cleanup; + } + + data->checksum_data.length = 28+credmsg.length; + } + } else { + data->checksum_data.length = 24; + } +#ifdef CFX_EXERCISE + if (data->ctx->auth_context->keyblock != NULL + && data->ctx->auth_context->keyblock->enctype == 18) { + srand(time(0) ^ getpid()); + /* Our ftp client code stupidly assumes a base64-encoded + version of the token will fit in 10K, so don't make this + too big. */ + junk = rand() & 0xff; + } else + junk = 0; +#else + junk = 0; +#endif + + assert(data->exts != NULL); + + if (data->exts->iakerb.conv) { + krb5_key key; + + code = krb5_auth_con_getsendsubkey_k(context, auth_context, &key); + if (code != 0) + goto cleanup; + + code = iakerb_make_finished(context, key, data->exts->iakerb.conv, + &finished); + if (code != 0) { + krb5_k_free_key(context, key); + goto cleanup; + } + + krb5_k_free_key(context, key); + data->checksum_data.length += 8 + finished->length; + } + + data->checksum_data.length += junk; + + /* now allocate a buffer to hold the checksum data and + (maybe) KRB_CRED msg */ + + if ((data->checksum_data.data = + (char *) xmalloc(data->checksum_data.length)) == NULL) { + code = ENOMEM; + goto cleanup; + } + + ptr = (unsigned char *)data->checksum_data.data; + + TWRITE_INT(ptr, data->md5.length, 0); + TWRITE_STR(ptr, data->md5.contents, data->md5.length); + TWRITE_INT(ptr, data->ctx->gss_flags, 0); + + /* done with this, free it */ + xfree(data->md5.contents); + + if (credmsg.data) { + TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0); + TWRITE_INT16(ptr, credmsg.length, 0); + TWRITE_STR(ptr, credmsg.data, credmsg.length); + } + if (data->exts->iakerb.conv) { + TWRITE_INT(ptr, KRB5_GSS_EXTS_IAKERB_FINISHED, 1); + TWRITE_INT(ptr, finished->length, 1); + TWRITE_STR(ptr, finished->data, finished->length); + } + if (junk) + memset(ptr, 'i', junk); + *out = &data->checksum_data; + code = 0; +cleanup: + krb5_free_data_contents(context, &credmsg); + krb5_free_data(context, finished); + return code; +} + +static krb5_error_code +make_ap_req_v1(context, ctx, cred, k_cred, ad_context, + chan_bindings, mech_type, token, exts) + krb5_context context; + krb5_gss_ctx_id_rec *ctx; + krb5_gss_cred_id_t cred; + krb5_creds *k_cred; + krb5_authdata_context ad_context; + gss_channel_bindings_t chan_bindings; + gss_OID mech_type; + gss_buffer_t token; + krb5_gss_ctx_ext_t exts; +{ + krb5_flags mk_req_flags = 0; + krb5_error_code code; + struct gss_checksum_data cksum_struct; + krb5_checksum md5; + krb5_data ap_req; + unsigned char *ptr; + unsigned char *t; + unsigned int tlen; + + k5_mutex_assert_locked(&cred->lock); + ap_req.data = 0; + + /* compute the hash of the channel bindings */ + + if ((code = kg_checksum_channel_bindings(context, chan_bindings, &md5))) + return(code); + + krb5_auth_con_set_req_cksumtype(context, ctx->auth_context, + CKSUMTYPE_KG_CB); + cksum_struct.md5 = md5; + cksum_struct.ctx = ctx; + cksum_struct.cred = cred; + cksum_struct.checksum_data.data = NULL; + cksum_struct.exts = exts; + krb5_auth_con_set_checksum_func(context, ctx->auth_context, + make_gss_checksum, &cksum_struct); + + /* call mk_req. subkey and ap_req need to be used or destroyed */ + + mk_req_flags = AP_OPTS_USE_SUBKEY; + + if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) + mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_ETYPE_NEGOTIATION; + + krb5_auth_con_set_authdata_context(context, ctx->auth_context, ad_context); + code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags, + NULL, k_cred, &ap_req); + krb5_auth_con_set_authdata_context(context, ctx->auth_context, NULL); + krb5_free_data_contents(context, &cksum_struct.checksum_data); + if (code) + goto cleanup; + + /* store the interesting stuff from creds and authent */ + ctx->krb_times = k_cred->times; + ctx->krb_flags = k_cred->ticket_flags; + + /* build up the token */ + if (ctx->gss_flags & GSS_C_DCE_STYLE) { + /* + * For DCE RPC, do not encapsulate the AP-REQ in the + * typical GSS wrapping. + */ + code = data_to_gss(&ap_req, token); + if (code) + goto cleanup; + } else { + /* allocate space for the token */ + tlen = g_token_size((gss_OID) mech_type, ap_req.length); + + if ((t = (unsigned char *) gssalloc_malloc(tlen)) == NULL) { + code = ENOMEM; + goto cleanup; + } + + /* fill in the buffer */ + ptr = t; + + g_make_token_header(mech_type, ap_req.length, + &ptr, KG_TOK_CTX_AP_REQ); + + TWRITE_STR(ptr, ap_req.data, ap_req.length); + + /* pass it back */ + + token->length = tlen; + token->value = (void *) t; + } + + code = 0; + +cleanup: + if (ap_req.data) + krb5_free_data_contents(context, &ap_req); + + return (code); +} + +/* + * new_connection + * + * Do the grunt work of setting up a new context. + */ +static OM_uint32 +kg_new_connection( + OM_uint32 *minor_status, + krb5_gss_cred_id_t cred, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + krb5_context context, + krb5_gss_ctx_ext_t exts) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_creds *k_cred = NULL; + krb5_gss_ctx_id_rec *ctx, *ctx_free; + krb5_timestamp now; + gss_buffer_desc token; + krb5_keyblock *keyblock; + + k5_mutex_assert_locked(&cred->lock); + major_status = GSS_S_FAILURE; + token.length = 0; + token.value = NULL; + + /* make sure the cred is usable for init */ + + if ((cred->usage != GSS_C_INITIATE) && + (cred->usage != GSS_C_BOTH)) { + *minor_status = 0; + return(GSS_S_NO_CRED); + } + + /* complain if the input token is non-null */ + + if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) { + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); + } + + /* create the ctx */ + + if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec))) + == NULL) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + /* fill in the ctx */ + memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); + ctx->magic = KG_CONTEXT; + ctx_free = ctx; + if ((code = krb5_auth_con_init(context, &ctx->auth_context))) + goto cleanup; + krb5_auth_con_setflags(context, ctx->auth_context, + KRB5_AUTH_CONTEXT_DO_SEQUENCE); + + /* limit the encryption types negotiated (if requested) */ + if (cred->req_enctypes) { + if ((code = krb5_set_default_tgs_enctypes(context, + cred->req_enctypes))) { + goto cleanup; + } + } + + ctx->initiate = 1; + ctx->seed_init = 0; + ctx->seqstate = 0; + + ctx->gss_flags = req_flags & (GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | + GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | + GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG | + GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG | + GSS_C_EXTENDED_ERROR_FLAG); + ctx->gss_flags |= GSS_C_TRANS_FLAG; + if (!cred->suppress_ci_flags) + ctx->gss_flags |= (GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG); + if (req_flags & GSS_C_DCE_STYLE) + ctx->gss_flags |= GSS_C_MUTUAL_FLAG; + + if ((code = krb5_timeofday(context, &now))) + goto cleanup; + + if (time_req == 0 || time_req == GSS_C_INDEFINITE) { + ctx->krb_times.endtime = 0; + } else { + ctx->krb_times.endtime = now + time_req; + } + + if ((code = kg_duplicate_name(context, cred->name, &ctx->here))) + goto cleanup; + + if ((code = kg_duplicate_name(context, (krb5_gss_name_t)target_name, + &ctx->there))) + goto cleanup; + + code = get_credentials(context, cred, ctx->there, now, + ctx->krb_times.endtime, &k_cred); + if (code) + goto cleanup; + + ctx->krb_times = k_cred->times; + + /* + * GSS_C_DELEG_POLICY_FLAG means to delegate only if the + * ok-as-delegate ticket flag is set. + */ + if ((req_flags & GSS_C_DELEG_POLICY_FLAG) + && (k_cred->ticket_flags & TKT_FLG_OK_AS_DELEGATE)) + ctx->gss_flags |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG; + + if (generic_gss_copy_oid(minor_status, mech_type, &ctx->mech_used) + != GSS_S_COMPLETE) { + code = *minor_status; + goto cleanup; + } + /* + * Now try to make it static if at all possible.... + */ + ctx->mech_used = krb5_gss_convert_static_mech_oid(ctx->mech_used); + + { + /* gsskrb5 v1 */ + krb5_int32 seq_temp; + if ((code = make_ap_req_v1(context, ctx, + cred, k_cred, ctx->here->ad_context, + input_chan_bindings, + mech_type, &token, exts))) { + if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) || + (code == KG_EMPTY_CCACHE)) + major_status = GSS_S_NO_CRED; + if (code == KRB5KRB_AP_ERR_TKT_EXPIRED) + major_status = GSS_S_CREDENTIALS_EXPIRED; + goto cleanup; + } + + krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &seq_temp); + ctx->seq_send = seq_temp; + code = krb5_auth_con_getsendsubkey(context, ctx->auth_context, + &keyblock); + if (code != 0) + goto cleanup; + code = krb5_k_create_key(context, keyblock, &ctx->subkey); + krb5_free_keyblock(context, keyblock); + if (code != 0) + goto cleanup; + } + + ctx->enc = NULL; + ctx->seq = NULL; + ctx->have_acceptor_subkey = 0; + code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype); + if (code != 0) + goto cleanup; + + if (!(ctx->gss_flags & GSS_C_MUTUAL_FLAG)) { + /* There will be no AP-REP, so set up sequence state now. */ + ctx->seq_recv = ctx->seq_send; + code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv, + (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0, + (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, + ctx->proto); + if (code != 0) + goto cleanup; + } + + /* compute time_rec */ + if (time_rec) { + if ((code = krb5_timeofday(context, &now))) + goto cleanup; + *time_rec = ctx->krb_times.endtime - now; + } + + /* set the other returns */ + *output_token = token; + + if (ret_flags) + *ret_flags = ctx->gss_flags; + + if (actual_mech_type) + *actual_mech_type = mech_type; + + /* return successfully */ + + *context_handle = (gss_ctx_id_t) ctx; + ctx_free = NULL; + if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) { + ctx->established = 0; + major_status = GSS_S_CONTINUE_NEEDED; + } else { + ctx->gss_flags |= GSS_C_PROT_READY_FLAG; + ctx->established = 1; + major_status = GSS_S_COMPLETE; + } + +cleanup: + krb5_free_creds(context, k_cred); + if (ctx_free) { + if (ctx_free->auth_context) + krb5_auth_con_free(context, ctx_free->auth_context); + if (ctx_free->here) + kg_release_name(context, &ctx_free->here); + if (ctx_free->there) + kg_release_name(context, &ctx_free->there); + if (ctx_free->subkey) + krb5_k_free_key(context, ctx_free->subkey); + xfree(ctx_free); + } + + *minor_status = code; + return (major_status); +} + +/* + * mutual_auth + * + * Handle the reply from the acceptor, if we're doing mutual auth. + */ +static OM_uint32 +mutual_auth( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + krb5_context context) +{ + OM_uint32 major_status; + unsigned char *ptr; + char *sptr; + krb5_data ap_rep; + krb5_ap_rep_enc_part *ap_rep_data; + krb5_timestamp now; + krb5_gss_ctx_id_rec *ctx; + krb5_error *krb_error; + krb5_error_code code; + krb5int_access kaccess; + + major_status = GSS_S_FAILURE; + + code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); + if (code) + goto fail; + + ctx = (krb5_gss_ctx_id_t) *context_handle; + + /* make sure the context is non-established, and that certain + arguments are unchanged */ + + if ((ctx->established) || + ((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) { + code = KG_CONTEXT_ESTABLISHED; + goto fail; + } + + if (! kg_compare_name(context, ctx->there, (krb5_gss_name_t)target_name)) { + (void)krb5_gss_delete_sec_context(minor_status, + context_handle, NULL); + code = 0; + major_status = GSS_S_BAD_NAME; + goto fail; + } + + /* verify the token and leave the AP_REP message in ap_rep */ + + if (input_token == GSS_C_NO_BUFFER) { + (void)krb5_gss_delete_sec_context(minor_status, + context_handle, NULL); + code = 0; + major_status = GSS_S_DEFECTIVE_TOKEN; + goto fail; + } + + ptr = (unsigned char *) input_token->value; + + if (ctx->gss_flags & GSS_C_DCE_STYLE) { + /* Raw AP-REP */ + ap_rep.length = input_token->length; + ap_rep.data = (char *)input_token->value; + } else if (g_verify_token_header(ctx->mech_used, + &(ap_rep.length), + &ptr, KG_TOK_CTX_AP_REP, + input_token->length, 1)) { + if (g_verify_token_header((gss_OID) ctx->mech_used, + &(ap_rep.length), + &ptr, KG_TOK_CTX_ERROR, + input_token->length, 1) == 0) { + + /* Handle a KRB_ERROR message from the server */ + + sptr = (char *) ptr; /* PC compiler bug */ + TREAD_STR(sptr, ap_rep.data, ap_rep.length); + + code = krb5_rd_error(context, &ap_rep, &krb_error); + if (code) + goto fail; + if (krb_error->error) + code = (krb5_error_code)krb_error->error + ERROR_TABLE_BASE_krb5; + else + code = 0; + krb5_free_error(context, krb_error); + goto fail; + } else { + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); + } + } + + sptr = (char *) ptr; /* PC compiler bug */ + TREAD_STR(sptr, ap_rep.data, ap_rep.length); + + /* decode the ap_rep */ + if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep, + &ap_rep_data))) { + /* + * XXX A hack for backwards compatiblity. + * To be removed in 1999 -- proven + */ + krb5_auth_con_setuseruserkey(context, ctx->auth_context, + &ctx->subkey->keyblock); + if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep, + &ap_rep_data))) + goto fail; + } + + /* store away the sequence number */ + ctx->seq_recv = ap_rep_data->seq_number; + code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv, + (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0, + (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, + ctx->proto); + if (code) { + krb5_free_ap_rep_enc_part(context, ap_rep_data); + goto fail; + } + + if (ap_rep_data->subkey != NULL && + (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) || + ap_rep_data->subkey->enctype != ctx->subkey->keyblock.enctype)) { + /* Keep acceptor's subkey. */ + ctx->have_acceptor_subkey = 1; + code = krb5_k_create_key(context, ap_rep_data->subkey, + &ctx->acceptor_subkey); + if (code) { + krb5_free_ap_rep_enc_part(context, ap_rep_data); + goto fail; + } + code = kg_setup_keys(context, ctx, ctx->acceptor_subkey, + &ctx->acceptor_subkey_cksumtype); + if (code) { + krb5_free_ap_rep_enc_part(context, ap_rep_data); + goto fail; + } + } + /* free the ap_rep_data */ + krb5_free_ap_rep_enc_part(context, ap_rep_data); + + if (ctx->gss_flags & GSS_C_DCE_STYLE) { + krb5_data outbuf; + + code = krb5_mk_rep_dce(context, ctx->auth_context, &outbuf); + if (code) + goto fail; + + code = data_to_gss(&outbuf, output_token); + if (code) + goto fail; + } + + /* set established */ + ctx->established = 1; + + /* set returns */ + + if (time_rec) { + if ((code = krb5_timeofday(context, &now))) + goto fail; + *time_rec = ctx->krb_times.endtime - now; + } + + if (ret_flags) + *ret_flags = ctx->gss_flags; + + if (actual_mech_type) + *actual_mech_type = mech_type; + + /* success */ + + *minor_status = 0; + return GSS_S_COMPLETE; + +fail: + (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL); + + *minor_status = code; + return (major_status); +} + +OM_uint32 +krb5_gss_init_sec_context_ext( + OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + krb5_gss_ctx_ext_t exts) +{ + krb5_context context; + gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL; + krb5_gss_cred_id_t cred; + krb5_error_code kerr; + OM_uint32 major_status; + OM_uint32 tmp_min_stat; + + if (*context_handle == GSS_C_NO_CONTEXT) { + kerr = krb5_gss_init_context(&context); + if (kerr) { + *minor_status = kerr; + return GSS_S_FAILURE; + } + if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) { + save_error_info(*minor_status, context); + krb5_free_context(context); + return GSS_S_FAILURE; + } + } else { + context = ((krb5_gss_ctx_id_rec *)*context_handle)->k5_context; + } + + /* set up return values so they can be "freed" successfully */ + + major_status = GSS_S_FAILURE; /* Default major code */ + output_token->length = 0; + output_token->value = NULL; + if (actual_mech_type) + *actual_mech_type = NULL; + + /* verify the mech_type */ + + if (mech_type == GSS_C_NULL_OID || g_OID_equal(mech_type, gss_mech_krb5)) { + mech_type = (gss_OID) gss_mech_krb5; + } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) { + mech_type = (gss_OID) gss_mech_krb5_old; + } else if (g_OID_equal(mech_type, gss_mech_krb5_wrong)) { + mech_type = (gss_OID) gss_mech_krb5_wrong; + } else if (g_OID_equal(mech_type, gss_mech_iakerb)) { + mech_type = (gss_OID) gss_mech_iakerb; + } else { + *minor_status = 0; + if (*context_handle == GSS_C_NO_CONTEXT) + krb5_free_context(context); + return(GSS_S_BAD_MECH); + } + + /* is this a new connection or not? */ + + /*SUPPRESS 29*/ + if (*context_handle == GSS_C_NO_CONTEXT) { + /* verify the credential, or use the default */ + /*SUPPRESS 29*/ + if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { + major_status = kg_get_defcred(minor_status, &defcred); + if (major_status && GSS_ERROR(major_status)) { + if (*context_handle == GSS_C_NO_CONTEXT) + krb5_free_context(context); + return(major_status); + } + claimant_cred_handle = defcred; + } + + major_status = kg_cred_resolve(minor_status, context, + claimant_cred_handle, target_name); + if (GSS_ERROR(major_status)) { + save_error_info(*minor_status, context); + krb5_gss_release_cred(&tmp_min_stat, &defcred); + if (*context_handle == GSS_C_NO_CONTEXT) + krb5_free_context(context); + return(major_status); + } + cred = (krb5_gss_cred_id_t)claimant_cred_handle; + + major_status = kg_new_connection(minor_status, cred, context_handle, + target_name, mech_type, req_flags, + time_req, input_chan_bindings, + input_token, actual_mech_type, + output_token, ret_flags, time_rec, + context, exts); + k5_mutex_unlock(&cred->lock); + krb5_gss_release_cred(&tmp_min_stat, &defcred); + if (*context_handle == GSS_C_NO_CONTEXT) { + save_error_info (*minor_status, context); + krb5_free_context(context); + } else + ((krb5_gss_ctx_id_rec *) *context_handle)->k5_context = context; + } else { + /* mutual_auth doesn't care about the credentials */ + major_status = mutual_auth(minor_status, context_handle, + target_name, mech_type, req_flags, + time_req, input_chan_bindings, + input_token, actual_mech_type, + output_token, ret_flags, time_rec, + context); + /* If context_handle is now NO_CONTEXT, mutual_auth called + delete_sec_context, which would've zapped the krb5 context + too. */ + } + + return(major_status); +} + +#ifndef _WIN32 +k5_mutex_t kg_kdc_flag_mutex = K5_MUTEX_PARTIAL_INITIALIZER; +static int kdc_flag = 0; +#endif + +krb5_error_code +krb5_gss_init_context (krb5_context *ctxp) +{ + krb5_error_code err; +#ifndef _WIN32 + int is_kdc; +#endif + + err = gss_krb5int_initialize_library(); + if (err) + return err; +#ifndef _WIN32 + k5_mutex_lock(&kg_kdc_flag_mutex); + is_kdc = kdc_flag; + k5_mutex_unlock(&kg_kdc_flag_mutex); + + if (is_kdc) + return krb5int_init_context_kdc(ctxp); +#endif + + return krb5_init_context(ctxp); +} + +#ifndef _WIN32 +OM_uint32 +krb5int_gss_use_kdc_context(OM_uint32 *minor_status, + const gss_OID desired_mech, + const gss_OID desired_object, + gss_buffer_t value) +{ + OM_uint32 err; + + *minor_status = 0; + + err = gss_krb5int_initialize_library(); + if (err) + return err; + k5_mutex_lock(&kg_kdc_flag_mutex); + kdc_flag = 1; + k5_mutex_unlock(&kg_kdc_flag_mutex); + return GSS_S_COMPLETE; +} +#endif + +OM_uint32 KRB5_CALLCONV +krb5_gss_init_sec_context(minor_status, claimant_cred_handle, + context_handle, target_name, mech_type, + req_flags, time_req, input_chan_bindings, + input_token, actual_mech_type, output_token, + ret_flags, time_rec) + OM_uint32 *minor_status; + gss_cred_id_t claimant_cred_handle; + gss_ctx_id_t *context_handle; + gss_name_t target_name; + gss_OID mech_type; + OM_uint32 req_flags; + OM_uint32 time_req; + gss_channel_bindings_t input_chan_bindings; + gss_buffer_t input_token; + gss_OID *actual_mech_type; + gss_buffer_t output_token; + OM_uint32 *ret_flags; + OM_uint32 *time_rec; +{ + krb5_gss_ctx_ext_rec exts; + + memset(&exts, 0, sizeof(exts)); + + return krb5_gss_init_sec_context_ext(minor_status, + claimant_cred_handle, + context_handle, + target_name, + mech_type, + req_flags, + time_req, + input_chan_bindings, + input_token, + actual_mech_type, + output_token, + ret_flags, + time_rec, + &exts); +} diff --git a/src/lib/gssapi/krb5/inq_context.c b/src/lib/gssapi/krb5/inq_context.c new file mode 100644 index 000000000000..9024b3c7ea9c --- /dev/null +++ b/src/lib/gssapi/krb5/inq_context.c @@ -0,0 +1,312 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_context(minor_status, context_handle, initiator_name, + acceptor_name, lifetime_rec, mech_type, ret_flags, + locally_initiated, opened) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_name_t *initiator_name; + gss_name_t *acceptor_name; + OM_uint32 *lifetime_rec; + gss_OID *mech_type; + OM_uint32 *ret_flags; + int *locally_initiated; + int *opened; +{ + krb5_context context; + krb5_error_code code; + krb5_gss_ctx_id_rec *ctx; + krb5_gss_name_t initiator, acceptor; + krb5_timestamp now; + krb5_deltat lifetime; + + if (initiator_name) + *initiator_name = (gss_name_t) NULL; + if (acceptor_name) + *acceptor_name = (gss_name_t) NULL; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + context = ctx->k5_context; + + /* RFC 2743 states that a partially completed context must return + * flags, locally_initiated, and open, and *may* return mech_type. */ + if (ctx->established) { + initiator = NULL; + acceptor = NULL; + + if ((code = krb5_timeofday(context, &now))) { + *minor_status = code; + save_error_info(*minor_status, context); + return(GSS_S_FAILURE); + } + + /* Add the maximum allowable clock skew as a grace period for context + * expiration, just as we do for the ticket during authentication. */ + lifetime = ctx->krb_times.endtime - now; + if (!ctx->initiate) + lifetime += context->clockskew; + if (lifetime < 0) + lifetime = 0; + + if (initiator_name) { + code = kg_duplicate_name(context, + ctx->initiate ? ctx->here : ctx->there, + &initiator); + if (code) { + *minor_status = code; + save_error_info(*minor_status, context); + return(GSS_S_FAILURE); + } + } + + if (acceptor_name) { + code = kg_duplicate_name(context, + ctx->initiate ? ctx->there : ctx->here, + &acceptor); + if (code) { + if (initiator) + kg_release_name(context, &initiator); + *minor_status = code; + save_error_info(*minor_status, context); + return(GSS_S_FAILURE); + } + } + + if (initiator_name) + *initiator_name = (gss_name_t) initiator; + + if (acceptor_name) + *acceptor_name = (gss_name_t) acceptor; + + if (lifetime_rec) + *lifetime_rec = lifetime; + } else { + lifetime = 0; + if (initiator_name) + *initiator_name = GSS_C_NO_NAME; + + if (acceptor_name) + *acceptor_name = GSS_C_NO_NAME; + + if (lifetime_rec) + *lifetime_rec = 0; + } + + if (mech_type) + *mech_type = (gss_OID) ctx->mech_used; + + if (ret_flags) + *ret_flags = ctx->gss_flags; + + if (locally_initiated) + *locally_initiated = ctx->initiate; + + if (opened) + *opened = ctx->established; + + *minor_status = 0; + if (ctx->established) + return((lifetime == 0)?GSS_S_CONTEXT_EXPIRED:GSS_S_COMPLETE); + else + return GSS_S_COMPLETE; +} + +OM_uint32 +gss_krb5int_inq_session_key( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + krb5_gss_ctx_id_rec *ctx; + krb5_key key; + gss_buffer_desc keyvalue, keyinfo; + OM_uint32 major_status, minor; + unsigned char oid_buf[GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH + 6]; + gss_OID_desc oid; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + key = ctx->have_acceptor_subkey ? ctx->acceptor_subkey : ctx->subkey; + + keyvalue.value = key->keyblock.contents; + keyvalue.length = key->keyblock.length; + + major_status = generic_gss_add_buffer_set_member(minor_status, &keyvalue, data_set); + if (GSS_ERROR(major_status)) + goto cleanup; + + oid.elements = oid_buf; + oid.length = sizeof(oid_buf); + + major_status = generic_gss_oid_compose(minor_status, + GSS_KRB5_SESSION_KEY_ENCTYPE_OID, + GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH, + key->keyblock.enctype, + &oid); + if (GSS_ERROR(major_status)) + goto cleanup; + + keyinfo.value = oid.elements; + keyinfo.length = oid.length; + + major_status = generic_gss_add_buffer_set_member(minor_status, &keyinfo, data_set); + if (GSS_ERROR(major_status)) + goto cleanup; + + return GSS_S_COMPLETE; + +cleanup: + if (*data_set != GSS_C_NO_BUFFER_SET) { + if ((*data_set)->count != 0) + memset((*data_set)->elements[0].value, 0, (*data_set)->elements[0].length); + gss_release_buffer_set(&minor, data_set); + } + + return major_status; +} + +OM_uint32 +gss_krb5int_extract_authz_data_from_sec_context( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + OM_uint32 major_status; + krb5_gss_ctx_id_rec *ctx; + int ad_type = 0; + size_t i; + + *data_set = GSS_C_NO_BUFFER_SET; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + major_status = generic_gss_oid_decompose(minor_status, + GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID, + GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH, + desired_object, + &ad_type); + if (major_status != GSS_S_COMPLETE || ad_type == 0) { + *minor_status = ENOENT; + return GSS_S_FAILURE; + } + + if (ctx->authdata != NULL) { + for (i = 0; ctx->authdata[i] != NULL; i++) { + if (ctx->authdata[i]->ad_type == ad_type) { + gss_buffer_desc ad_data; + + ad_data.length = ctx->authdata[i]->length; + ad_data.value = ctx->authdata[i]->contents; + + major_status = generic_gss_add_buffer_set_member(minor_status, + &ad_data, data_set); + if (GSS_ERROR(major_status)) + break; + } + } + } + + if (GSS_ERROR(major_status)) { + OM_uint32 tmp; + + generic_gss_release_buffer_set(&tmp, data_set); + } + + return major_status; +} + +OM_uint32 +gss_krb5int_extract_authtime_from_sec_context(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_oid, + gss_buffer_set_t *data_set) +{ + krb5_gss_ctx_id_rec *ctx; + gss_buffer_desc rep; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + rep.value = &ctx->krb_times.authtime; + rep.length = sizeof(ctx->krb_times.authtime); + + return generic_gss_add_buffer_set_member(minor_status, &rep, data_set); +} diff --git a/src/lib/gssapi/krb5/inq_cred.c b/src/lib/gssapi/krb5/inq_cred.c new file mode 100644 index 000000000000..4e35a056316f --- /dev/null +++ b/src/lib/gssapi/krb5/inq_cred.c @@ -0,0 +1,247 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2000, 2007 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret, + cred_usage, mechanisms) + OM_uint32 *minor_status; + gss_cred_id_t cred_handle; + gss_name_t *name; + OM_uint32 *lifetime_ret; + gss_cred_usage_t *cred_usage; + gss_OID_set *mechanisms; +{ + krb5_context context; + gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL; + krb5_gss_cred_id_t cred = NULL; + krb5_error_code code; + krb5_timestamp now; + krb5_deltat lifetime; + krb5_gss_name_t ret_name; + krb5_principal princ; + gss_OID_set mechs; + OM_uint32 major, tmpmin, ret; + + ret = GSS_S_FAILURE; + ret_name = NULL; + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + if (name) *name = NULL; + if (mechanisms) *mechanisms = NULL; + + /* check for default credential */ + /*SUPPRESS 29*/ + if (cred_handle == GSS_C_NO_CREDENTIAL) { + major = kg_get_defcred(minor_status, &defcred); + if (GSS_ERROR(major)) { + krb5_free_context(context); + return(major); + } + cred_handle = defcred; + } + + major = kg_cred_resolve(minor_status, context, cred_handle, GSS_C_NO_NAME); + if (GSS_ERROR(major)) { + krb5_gss_release_cred(minor_status, &defcred); + krb5_free_context(context); + return(major); + } + cred = (krb5_gss_cred_id_t)cred_handle; + + if ((code = krb5_timeofday(context, &now))) { + *minor_status = code; + ret = GSS_S_FAILURE; + goto fail; + } + + if (cred->expire > 0) { + if ((lifetime = cred->expire - now) < 0) + lifetime = 0; + } + else + lifetime = GSS_C_INDEFINITE; + + if (name) { + if (cred->name) { + code = kg_duplicate_name(context, cred->name, &ret_name); + } else if ((cred->usage == GSS_C_ACCEPT || cred->usage == GSS_C_BOTH) + && cred->keytab != NULL) { + /* This is a default acceptor cred; use a name from the keytab if + * we can. */ + code = k5_kt_get_principal(context, cred->keytab, &princ); + if (code == 0) { + code = kg_init_name(context, princ, NULL, NULL, NULL, + KG_INIT_NAME_NO_COPY, &ret_name); + if (code) + krb5_free_principal(context, princ); + } else if (code == KRB5_KT_NOTFOUND) + code = 0; + } + if (code) { + *minor_status = code; + save_error_info(*minor_status, context); + ret = GSS_S_FAILURE; + goto fail; + } + } + + if (mechanisms) { + if (GSS_ERROR(ret = generic_gss_create_empty_oid_set(minor_status, + &mechs)) || + GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5_old, + &mechs)) || + GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5, + &mechs))) { + if (ret_name) + kg_release_name(context, &ret_name); + /* *minor_status set above */ + goto fail; + } + } + + if (name) { + if (ret_name != NULL) + *name = (gss_name_t) ret_name; + else + *name = GSS_C_NO_NAME; + } + + if (lifetime_ret) + *lifetime_ret = lifetime; + + if (cred_usage) + *cred_usage = cred->usage; + k5_mutex_unlock(&cred->lock); + + if (mechanisms) + *mechanisms = mechs; + + if (cred_handle == GSS_C_NO_CREDENTIAL) + krb5_gss_release_cred(minor_status, (gss_cred_id_t *)&cred); + + krb5_free_context(context); + *minor_status = 0; + return((lifetime == 0)?GSS_S_CREDENTIALS_EXPIRED:GSS_S_COMPLETE); +fail: + k5_mutex_unlock(&cred->lock); + krb5_gss_release_cred(&tmpmin, &defcred); + krb5_free_context(context); + return ret; +} + +/* V2 interface */ +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_cred_by_mech(minor_status, cred_handle, + mech_type, name, initiator_lifetime, + acceptor_lifetime, cred_usage) + OM_uint32 *minor_status; + gss_cred_id_t cred_handle; + gss_OID mech_type; + gss_name_t *name; + OM_uint32 *initiator_lifetime; + OM_uint32 *acceptor_lifetime; + gss_cred_usage_t *cred_usage; +{ + krb5_gss_cred_id_t cred; + OM_uint32 lifetime; + OM_uint32 mstat; + + cred = (krb5_gss_cred_id_t) cred_handle; + mstat = krb5_gss_inquire_cred(minor_status, + cred_handle, + name, + &lifetime, + cred_usage, + (gss_OID_set *) NULL); + if (mstat == GSS_S_COMPLETE) { + if (cred && + ((cred->usage == GSS_C_INITIATE) || + (cred->usage == GSS_C_BOTH)) && + initiator_lifetime) + *initiator_lifetime = lifetime; + if (cred && + ((cred->usage == GSS_C_ACCEPT) || + (cred->usage == GSS_C_BOTH)) && + acceptor_lifetime) + *acceptor_lifetime = lifetime; + } + return(mstat); +} diff --git a/src/lib/gssapi/krb5/inq_names.c b/src/lib/gssapi/krb5/inq_names.c new file mode 100644 index 000000000000..b326adbb5f23 --- /dev/null +++ b/src/lib/gssapi/krb5/inq_names.c @@ -0,0 +1,100 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/inq_names.c - Return nametypes supported by krb5 mech */ +/* + * Copyright 1995 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_names_for_mech(minor_status, mechanism, name_types) + OM_uint32 *minor_status; + gss_OID mechanism; + gss_OID_set *name_types; +{ + OM_uint32 major, minor; + + /* + * We only know how to handle our own mechanism. + */ + if ((mechanism != GSS_C_NULL_OID) && + !g_OID_equal(gss_mech_krb5, mechanism) && + !g_OID_equal(gss_mech_krb5_old, mechanism) && + !g_OID_equal(gss_mech_krb5_wrong, mechanism) && + !g_OID_equal(gss_mech_iakerb, mechanism)) { + *minor_status = 0; + return(GSS_S_BAD_MECH); + } + + /* We're okay. Create an empty OID set */ + major = generic_gss_create_empty_oid_set(minor_status, name_types); + if (major == GSS_S_COMPLETE) { + /* Now add our members. */ + if ( + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_user_name, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_machine_uid_name, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_string_uid_name, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_service_name, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_service_name_v2, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_exported_name, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + gss_nt_krb5_name, + name_types) + ) == GSS_S_COMPLETE) && + ((major = generic_gss_add_oid_set_member(minor_status, + GSS_C_NT_COMPOSITE_EXPORT, + name_types) + ) == GSS_S_COMPLETE) + ) { + major = generic_gss_add_oid_set_member(minor_status, + gss_nt_krb5_principal, + name_types); + } + + /* + * If we choked, then release the set, but don't overwrite the minor + * status with the release call. + */ + if (major != GSS_S_COMPLETE) + (void) generic_gss_release_oid_set(&minor, name_types); + } + return(major); +} diff --git a/src/lib/gssapi/krb5/k5seal.c b/src/lib/gssapi/krb5/k5seal.c new file mode 100644 index 000000000000..4da531b58245 --- /dev/null +++ b/src/lib/gssapi/krb5/k5seal.c @@ -0,0 +1,413 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "gssapiP_krb5.h" + +#include <assert.h> + +static krb5_error_code +make_seal_token_v1 (krb5_context context, + krb5_key enc, + krb5_key seq, + uint64_t *seqnum, + int direction, + gss_buffer_t text, + gss_buffer_t token, + int signalg, + size_t cksum_size, + int sealalg, + int do_encrypt, + int toktype, + gss_OID oid) +{ + krb5_error_code code; + size_t sumlen; + char *data_ptr; + krb5_data plaind; + krb5_checksum md5cksum; + krb5_checksum cksum; + /* msglen contains the message length + * we are signing/encrypting. tmsglen + * contains the length of the message + * we plan to write out to the token. + * tlen is the length of the token + * including header. */ + unsigned int conflen=0, tmsglen, tlen, msglen; + unsigned char *t, *ptr; + unsigned char *plain; + unsigned char pad; + krb5_keyusage sign_usage = KG_USAGE_SIGN; + + + assert((!do_encrypt) || (toktype == KG_TOK_SEAL_MSG)); + /* create the token buffer */ + /* Do we need confounder? */ + if (do_encrypt || toktype == KG_TOK_SEAL_MSG) + conflen = kg_confounder_size(context, enc->keyblock.enctype); + else conflen = 0; + + if (toktype == KG_TOK_SEAL_MSG) { + switch (sealalg) { + case SEAL_ALG_MICROSOFT_RC4: + msglen = conflen + text->length+1; + pad = 1; + break; + default: + /* XXX knows that des block size is 8 */ + msglen = (conflen+text->length+8)&(~7); + pad = 8-(text->length%8); + } + tmsglen = msglen; + } else { + tmsglen = 0; + msglen = text->length; + pad = 0; + } + tlen = g_token_size((gss_OID) oid, 14+cksum_size+tmsglen); + + if ((t = (unsigned char *) gssalloc_malloc(tlen)) == NULL) + return(ENOMEM); + + /*** fill in the token */ + + ptr = t; + g_make_token_header(oid, 14+cksum_size+tmsglen, &ptr, toktype); + + /* 0..1 SIGN_ALG */ + store_16_le(signalg, &ptr[0]); + + /* 2..3 SEAL_ALG or Filler */ + if ((toktype == KG_TOK_SEAL_MSG) && do_encrypt) { + store_16_le(sealalg, &ptr[2]); + } else { + /* No seal */ + ptr[2] = 0xff; + ptr[3] = 0xff; + } + + /* 4..5 Filler */ + ptr[4] = 0xff; + ptr[5] = 0xff; + + /* pad the plaintext, encrypt if needed, and stick it in the token */ + + /* initialize the the cksum */ + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_MD2_5: + md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; + break; + case SGN_ALG_HMAC_MD5: + md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; + if (toktype != KG_TOK_SEAL_MSG) + sign_usage = 15; + break; + default: + case SGN_ALG_DES_MAC: + abort (); + } + + code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen); + if (code) { + gssalloc_free(t); + return(code); + } + md5cksum.length = sumlen; + + + if ((plain = (unsigned char *) xmalloc(msglen ? msglen : 1)) == NULL) { + gssalloc_free(t); + return(ENOMEM); + } + + if (conflen) { + if ((code = kg_make_confounder(context, enc->keyblock.enctype, + plain))) { + xfree(plain); + gssalloc_free(t); + return(code); + } + } + + memcpy(plain+conflen, text->value, text->length); + if (pad) memset(plain+conflen+text->length, pad, pad); + + /* compute the checksum */ + + /* 8 = head of token body as specified by mech spec */ + if (! (data_ptr = xmalloc(8 + msglen))) { + xfree(plain); + gssalloc_free(t); + return(ENOMEM); + } + (void) memcpy(data_ptr, ptr-2, 8); + (void) memcpy(data_ptr+8, plain, msglen); + plaind.length = 8 + msglen; + plaind.data = data_ptr; + code = krb5_k_make_checksum(context, md5cksum.checksum_type, seq, + sign_usage, &plaind, &md5cksum); + xfree(data_ptr); + + if (code) { + xfree(plain); + gssalloc_free(t); + return(code); + } + switch(signalg) { + case SGN_ALG_DES_MAC_MD5: + case 3: + + code = kg_encrypt_inplace(context, seq, KG_USAGE_SEAL, + (g_OID_equal(oid, gss_mech_krb5_old) ? + seq->keyblock.contents : NULL), + md5cksum.contents, 16); + if (code) { + krb5_free_checksum_contents(context, &md5cksum); + xfree (plain); + gssalloc_free(t); + return code; + } + + cksum.length = cksum_size; + cksum.contents = md5cksum.contents + 16 - cksum.length; + + memcpy(ptr+14, cksum.contents, cksum.length); + break; + + case SGN_ALG_HMAC_SHA1_DES3_KD: + /* + * Using key derivation, the call to krb5_c_make_checksum + * already dealt with encrypting. + */ + if (md5cksum.length != cksum_size) + abort (); + memcpy (ptr+14, md5cksum.contents, md5cksum.length); + break; + case SGN_ALG_HMAC_MD5: + memcpy (ptr+14, md5cksum.contents, cksum_size); + break; + } + + krb5_free_checksum_contents(context, &md5cksum); + + /* create the seq_num */ + + if ((code = kg_make_seq_num(context, seq, direction?0:0xff, + (krb5_ui_4)*seqnum, ptr+14, ptr+6))) { + xfree (plain); + gssalloc_free(t); + return(code); + } + + if (do_encrypt) { + switch(sealalg) { + case SEAL_ALG_MICROSOFT_RC4: + { + unsigned char bigend_seqnum[4]; + krb5_keyblock *enc_key; + int i; + store_32_be(*seqnum, bigend_seqnum); + code = krb5_k_key_keyblock(context, enc, &enc_key); + if (code) + { + xfree(plain); + gssalloc_free(t); + return(code); + } + assert (enc_key->length == 16); + for (i = 0; i <= 15; i++) + ((char *) enc_key->contents)[i] ^=0xf0; + code = kg_arcfour_docrypt (enc_key, 0, + bigend_seqnum, 4, + plain, tmsglen, + ptr+14+cksum_size); + krb5_free_keyblock (context, enc_key); + if (code) + { + xfree(plain); + gssalloc_free(t); + return(code); + } + } + break; + default: + if ((code = kg_encrypt(context, enc, KG_USAGE_SEAL, NULL, + (krb5_pointer) plain, + (krb5_pointer) (ptr+cksum_size+14), + tmsglen))) { + xfree(plain); + gssalloc_free(t); + return(code); + } + } + }else { + if (tmsglen) + memcpy(ptr+14+cksum_size, plain, tmsglen); + } + xfree(plain); + + + /* that's it. return the token */ + + (*seqnum)++; + *seqnum &= 0xffffffffL; + + token->length = tlen; + token->value = (void *) t; + + return(0); +} + +/* if signonly is true, ignore conf_req, conf_state, + and do not encode the ENC_TYPE, MSG_LENGTH, or MSG_TEXT fields */ + +OM_uint32 +kg_seal(minor_status, context_handle, conf_req_flag, qop_req, + input_message_buffer, conf_state, output_message_buffer, toktype) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + int conf_req_flag; + gss_qop_t qop_req; + gss_buffer_t input_message_buffer; + int *conf_state; + gss_buffer_t output_message_buffer; + int toktype; +{ + krb5_gss_ctx_id_rec *ctx; + krb5_error_code code; + krb5_context context; + + output_message_buffer->length = 0; + output_message_buffer->value = NULL; + + /* Only default qop or matching established cryptosystem is allowed. + + There are NO EXTENSIONS to this set for AES and friends! The + new spec says "just use 0". The old spec plus extensions would + actually allow for certain non-zero values. Fix this to handle + them later. */ + if (qop_req != 0) { + *minor_status = (OM_uint32) G_UNKNOWN_QOP; + return GSS_S_BAD_QOP; + } + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + context = ctx->k5_context; + switch (ctx->proto) + { + case 0: + code = make_seal_token_v1(context, ctx->enc, ctx->seq, + &ctx->seq_send, ctx->initiate, + input_message_buffer, output_message_buffer, + ctx->signalg, ctx->cksum_size, ctx->sealalg, + conf_req_flag, toktype, ctx->mech_used); + break; + case 1: + code = gss_krb5int_make_seal_token_v3(context, ctx, + input_message_buffer, + output_message_buffer, + conf_req_flag, toktype); + break; + default: + code = G_UNKNOWN_QOP; /* XXX */ + break; + } + + if (code) { + *minor_status = code; + save_error_info(*minor_status, context); + return(GSS_S_FAILURE); + } + + if (conf_state) + *conf_state = conf_req_flag; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_wrap(minor_status, context_handle, conf_req_flag, + qop_req, input_message_buffer, conf_state, + output_message_buffer) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + int conf_req_flag; + gss_qop_t qop_req; + gss_buffer_t input_message_buffer; + int *conf_state; + gss_buffer_t output_message_buffer; +{ + return(kg_seal(minor_status, context_handle, conf_req_flag, + qop_req, input_message_buffer, conf_state, + output_message_buffer, KG_TOK_WRAP_MSG)); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_get_mic(minor_status, context_handle, qop_req, + message_buffer, message_token) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_qop_t qop_req; + gss_buffer_t message_buffer; + gss_buffer_t message_token; +{ + return(kg_seal(minor_status, context_handle, 0, + qop_req, message_buffer, NULL, + message_token, KG_TOK_MIC_MSG)); +} diff --git a/src/lib/gssapi/krb5/k5sealiov.c b/src/lib/gssapi/krb5/k5sealiov.c new file mode 100644 index 000000000000..88caa856f86a --- /dev/null +++ b/src/lib/gssapi/krb5/k5sealiov.c @@ -0,0 +1,557 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/k5sealiov.c */ +/* + * Copyright 2008, 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +static krb5_error_code +make_seal_token_v1_iov(krb5_context context, + krb5_gss_ctx_id_rec *ctx, + int conf_req_flag, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + krb5_error_code code = 0; + gss_iov_buffer_t header; + gss_iov_buffer_t padding; + gss_iov_buffer_t trailer; + krb5_checksum md5cksum; + krb5_checksum cksum; + size_t k5_headerlen = 0, k5_trailerlen = 0; + size_t data_length = 0, assoc_data_length = 0; + size_t tmsglen = 0, tlen; + unsigned char *ptr; + krb5_keyusage sign_usage = KG_USAGE_SIGN; + + md5cksum.length = cksum.length = 0; + md5cksum.contents = cksum.contents = NULL; + + header = kg_locate_header_iov(iov, iov_count, toktype); + if (header == NULL) + return EINVAL; + + padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding == NULL && toktype == KG_TOK_WRAP_MSG && + (ctx->gss_flags & GSS_C_DCE_STYLE) == 0) + return EINVAL; + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + if (trailer != NULL) + trailer->buffer.length = 0; + + /* Determine confounder length */ + if (toktype == KG_TOK_WRAP_MSG || conf_req_flag) + k5_headerlen = kg_confounder_size(context, ctx->enc->keyblock.enctype); + + /* Check padding length */ + if (toktype == KG_TOK_WRAP_MSG) { + size_t k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8; + size_t gss_padlen; + size_t conf_data_length; + + kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); + conf_data_length = k5_headerlen + data_length - assoc_data_length; + + if (k5_padlen == 1) + gss_padlen = 1; /* one byte to indicate one byte of padding */ + else + gss_padlen = k5_padlen - (conf_data_length % k5_padlen); + + if (ctx->gss_flags & GSS_C_DCE_STYLE) { + /* DCE will pad the actual data itself; padding buffer optional and will be zeroed */ + gss_padlen = 0; + + if (conf_data_length % k5_padlen) + code = KRB5_BAD_MSIZE; + } else if (padding->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { + code = kg_allocate_iov(padding, gss_padlen); + } else if (padding->buffer.length < gss_padlen) { + code = KRB5_BAD_MSIZE; + } + if (code != 0) + goto cleanup; + + /* Initialize padding buffer to pad itself */ + if (padding != NULL) { + padding->buffer.length = gss_padlen; + memset(padding->buffer.value, (int)gss_padlen, gss_padlen); + } + + if (ctx->gss_flags & GSS_C_DCE_STYLE) + tmsglen = k5_headerlen; /* confounder length */ + else + tmsglen = conf_data_length + padding->buffer.length; + } + + /* Determine token size */ + tlen = g_token_size(ctx->mech_used, 14 + ctx->cksum_size + tmsglen); + + k5_headerlen += tlen - tmsglen; + + if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = kg_allocate_iov(header, k5_headerlen); + else if (header->buffer.length < k5_headerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + + header->buffer.length = k5_headerlen; + + ptr = (unsigned char *)header->buffer.value; + g_make_token_header(ctx->mech_used, 14 + ctx->cksum_size + tmsglen, &ptr, toktype); + + /* 0..1 SIGN_ALG */ + store_16_le(ctx->signalg, &ptr[0]); + + /* 2..3 SEAL_ALG or Filler */ + if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { + store_16_le(ctx->sealalg, &ptr[2]); + } else { + /* No seal */ + ptr[2] = 0xFF; + ptr[3] = 0xFF; + } + + /* 4..5 Filler */ + ptr[4] = 0xFF; + ptr[5] = 0xFF; + + /* pad the plaintext, encrypt if needed, and stick it in the token */ + + /* initialize the checksum */ + switch (ctx->signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_MD2_5: + md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; + break; + case SGN_ALG_HMAC_MD5: + md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; + if (toktype != KG_TOK_WRAP_MSG) + sign_usage = 15; + break; + default: + case SGN_ALG_DES_MAC: + abort (); + } + + code = krb5_c_checksum_length(context, md5cksum.checksum_type, &k5_trailerlen); + if (code != 0) + goto cleanup; + md5cksum.length = k5_trailerlen; + + if (k5_headerlen != 0 && toktype == KG_TOK_WRAP_MSG) { + code = kg_make_confounder(context, ctx->enc->keyblock.enctype, + ptr + 14 + ctx->cksum_size); + if (code != 0) + goto cleanup; + } + + /* compute the checksum */ + code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type, + ctx->cksum_size, ctx->seq, ctx->enc, + sign_usage, iov, iov_count, toktype, + &md5cksum); + if (code != 0) + goto cleanup; + + switch (ctx->signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_3: + code = kg_encrypt_inplace(context, ctx->seq, KG_USAGE_SEAL, + (g_OID_equal(ctx->mech_used, + gss_mech_krb5_old) ? + ctx->seq->keyblock.contents : NULL), + md5cksum.contents, 16); + if (code != 0) + goto cleanup; + + cksum.length = ctx->cksum_size; + cksum.contents = md5cksum.contents + 16 - cksum.length; + + memcpy(ptr + 14, cksum.contents, cksum.length); + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + assert(md5cksum.length == ctx->cksum_size); + memcpy(ptr + 14, md5cksum.contents, md5cksum.length); + break; + case SGN_ALG_HMAC_MD5: + memcpy(ptr + 14, md5cksum.contents, ctx->cksum_size); + break; + } + + /* create the seq_num */ + code = kg_make_seq_num(context, ctx->seq, ctx->initiate ? 0 : 0xFF, + (OM_uint32)ctx->seq_send, ptr + 14, ptr + 6); + if (code != 0) + goto cleanup; + + if (conf_req_flag) { + if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) { + unsigned char bigend_seqnum[4]; + krb5_keyblock *enc_key; + size_t i; + + store_32_be(ctx->seq_send, bigend_seqnum); + + code = krb5_k_key_keyblock(context, ctx->enc, &enc_key); + if (code != 0) + goto cleanup; + + assert(enc_key->length == 16); + + for (i = 0; i < enc_key->length; i++) + ((char *)enc_key->contents)[i] ^= 0xF0; + + code = kg_arcfour_docrypt_iov(context, enc_key, 0, + bigend_seqnum, 4, + iov, iov_count); + krb5_free_keyblock(context, enc_key); + } else { + code = kg_encrypt_iov(context, ctx->proto, + ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), + 0 /*EC*/, 0 /*RRC*/, + ctx->enc, KG_USAGE_SEAL, NULL, + iov, iov_count); + } + if (code != 0) + goto cleanup; + } + + ctx->seq_send++; + ctx->seq_send &= 0xFFFFFFFFL; + + code = 0; + + if (conf_state != NULL) + *conf_state = conf_req_flag; + +cleanup: + if (code != 0) + kg_release_iov(iov, iov_count); + krb5_free_checksum_contents(context, &md5cksum); + + return code; +} + +OM_uint32 +kg_seal_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + krb5_gss_ctx_id_rec *ctx; + krb5_error_code code; + krb5_context context; + + if (qop_req != 0) { + *minor_status = (OM_uint32)G_UNKNOWN_QOP; + return GSS_S_BAD_QOP; + } + + ctx = (krb5_gss_ctx_id_rec *)context_handle; + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + if (conf_req_flag && kg_integ_only_iov(iov, iov_count)) { + /* may be more sensible to return an error here */ + conf_req_flag = FALSE; + } + + context = ctx->k5_context; + switch (ctx->proto) { + case 0: + code = make_seal_token_v1_iov(context, ctx, conf_req_flag, + conf_state, iov, iov_count, toktype); + break; + case 1: + code = gss_krb5int_make_seal_token_v3_iov(context, ctx, conf_req_flag, + conf_state, iov, iov_count, toktype); + break; + default: + code = G_UNKNOWN_QOP; + break; + } + + if (code != 0) { + *minor_status = code; + save_error_info(*minor_status, context); + return GSS_S_FAILURE; + } + + *minor_status = 0; + + return GSS_S_COMPLETE; +} + +#define INIT_IOV_DATA(_iov) do { (_iov)->buffer.value = NULL; \ + (_iov)->buffer.length = 0; } \ + while (0) + +OM_uint32 +kg_seal_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + krb5_gss_ctx_id_rec *ctx; + gss_iov_buffer_t header, trailer, padding; + size_t data_length, assoc_data_length; + size_t gss_headerlen, gss_padlen, gss_trailerlen; + unsigned int k5_headerlen = 0, k5_trailerlen = 0, k5_padlen = 0; + krb5_error_code code; + krb5_context context; + int dce_or_mic; + + if (qop_req != GSS_C_QOP_DEFAULT) { + *minor_status = (OM_uint32)G_UNKNOWN_QOP; + return GSS_S_BAD_QOP; + } + + ctx = (krb5_gss_ctx_id_rec *)context_handle; + if (!ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + header = kg_locate_header_iov(iov, iov_count, toktype); + if (header == NULL) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + INIT_IOV_DATA(header); + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + if (trailer != NULL) { + INIT_IOV_DATA(trailer); + } + + /* MIC tokens and DCE-style wrap tokens have similar length considerations: + * no padding, and the framing surrounds the header only, not the data. */ + dce_or_mic = ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0 || + toktype == KG_TOK_MIC_MSG); + + /* For CFX, EC is used instead of padding, and is placed in header or trailer */ + padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding == NULL) { + if (conf_req_flag && ctx->proto == 0 && !dce_or_mic) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + } else { + INIT_IOV_DATA(padding); + } + + kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); + + if (conf_req_flag && kg_integ_only_iov(iov, iov_count)) + conf_req_flag = FALSE; + + context = ctx->k5_context; + + gss_headerlen = gss_padlen = gss_trailerlen = 0; + + if (ctx->proto == 1) { + krb5_key key; + krb5_enctype enctype; + size_t ec; + + key = (ctx->have_acceptor_subkey) ? ctx->acceptor_subkey : ctx->subkey; + enctype = key->keyblock.enctype; + + code = krb5_c_crypto_length(context, enctype, + conf_req_flag ? + KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM, + &k5_trailerlen); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + if (conf_req_flag) { + code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + } + + gss_headerlen = 16; /* Header */ + if (conf_req_flag) { + gss_headerlen += k5_headerlen; /* Kerb-Header */ + gss_trailerlen = 16 /* E(Header) */ + k5_trailerlen; /* Kerb-Trailer */ + + code = krb5_c_padding_length(context, enctype, + data_length - assoc_data_length + 16 /* E(Header) */, &k5_padlen); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + if (k5_padlen == 0 && dce_or_mic) { + /* Windows rejects AEAD tokens with non-zero EC */ + code = krb5_c_block_size(context, enctype, &ec); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + } else + ec = k5_padlen; + + gss_trailerlen += ec; + } else { + gss_trailerlen = k5_trailerlen; /* Kerb-Checksum */ + } + } else if (!dce_or_mic) { + k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8; + + if (k5_padlen == 1) + gss_padlen = 1; + else + gss_padlen = k5_padlen - ((data_length - assoc_data_length) % k5_padlen); + } + + data_length += gss_padlen; + + if (ctx->proto == 0) { + /* Header | Checksum | Confounder | Data | Pad */ + size_t data_size; + + k5_headerlen = kg_confounder_size(context, ctx->enc->keyblock.enctype); + + data_size = 14 /* Header */ + ctx->cksum_size + k5_headerlen; + + if (!dce_or_mic) + data_size += data_length; + + gss_headerlen = g_token_size(ctx->mech_used, data_size); + + /* g_token_size() will include data_size as well as the overhead, so + * subtract data_length just to get the overhead (ie. token size) */ + if (!dce_or_mic) + gss_headerlen -= data_length; + } + + if (minor_status != NULL) + *minor_status = 0; + + if (trailer == NULL) + gss_headerlen += gss_trailerlen; + else + trailer->buffer.length = gss_trailerlen; + + assert(gss_padlen == 0 || padding != NULL); + + if (padding != NULL) + padding->buffer.length = gss_padlen; + + header->buffer.length = gss_headerlen; + + if (conf_state != NULL) + *conf_state = conf_req_flag; + + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_wrap_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major_status; + + major_status = kg_seal_iov(minor_status, context_handle, conf_req_flag, + qop_req, conf_state, + iov, iov_count, KG_TOK_WRAP_MSG); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_wrap_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major_status; + + major_status = kg_seal_iov_length(minor_status, context_handle, + conf_req_flag, qop_req, conf_state, iov, + iov_count, KG_TOK_WRAP_MSG); + return major_status; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_get_mic_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_qop_t qop_req, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major_status; + + major_status = kg_seal_iov(minor_status, context_handle, FALSE, + qop_req, NULL, + iov, iov_count, KG_TOK_MIC_MSG); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_get_mic_iov_length(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_qop_t qop_req, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major_status; + + major_status = kg_seal_iov_length(minor_status, context_handle, FALSE, + qop_req, NULL, iov, iov_count, + KG_TOK_MIC_MSG); + return major_status; +} diff --git a/src/lib/gssapi/krb5/k5sealv3.c b/src/lib/gssapi/krb5/k5sealv3.c new file mode 100644 index 000000000000..1a5c14c2713b --- /dev/null +++ b/src/lib/gssapi/krb5/k5sealv3.c @@ -0,0 +1,512 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/k5sealv3.c */ +/* + * Copyright 2003,2004,2007 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* draft-ietf-krb-wg-gssapi-cfx-05 */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +int +gss_krb5int_rotate_left (void *ptr, size_t bufsiz, size_t rc) +{ + /* Optimize for receiving. After some debugging is done, the MIT + implementation won't do any rotates on sending, and while + debugging, they'll be randomly chosen. + + Return 1 for success, 0 for failure (ENOMEM). */ + void *tbuf; + + if (bufsiz == 0) + return 1; + rc = rc % bufsiz; + if (rc == 0) + return 1; + + tbuf = malloc(rc); + if (tbuf == 0) + return 0; + memcpy(tbuf, ptr, rc); + memmove(ptr, (char *)ptr + rc, bufsiz - rc); + memcpy((char *)ptr + bufsiz - rc, tbuf, rc); + free(tbuf); + return 1; +} + +static const gss_buffer_desc empty_message = { 0, 0 }; + +krb5_error_code +gss_krb5int_make_seal_token_v3 (krb5_context context, + krb5_gss_ctx_id_rec *ctx, + const gss_buffer_desc * message, + gss_buffer_t token, + int conf_req_flag, int toktype) +{ + size_t bufsize = 16; + unsigned char *outbuf = 0; + krb5_error_code err; + int key_usage; + unsigned char acceptor_flag; + const gss_buffer_desc *message2 = message; +#ifdef CFX_EXERCISE + size_t rrc; +#endif + size_t ec; + unsigned short tok_id; + krb5_checksum sum; + krb5_key key; + krb5_cksumtype cksumtype; + + acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR; + key_usage = (toktype == KG_TOK_WRAP_MSG + ? (ctx->initiate + ? KG_USAGE_INITIATOR_SEAL + : KG_USAGE_ACCEPTOR_SEAL) + : (ctx->initiate + ? KG_USAGE_INITIATOR_SIGN + : KG_USAGE_ACCEPTOR_SIGN)); + if (ctx->have_acceptor_subkey) { + key = ctx->acceptor_subkey; + cksumtype = ctx->acceptor_subkey_cksumtype; + } else { + key = ctx->subkey; + cksumtype = ctx->cksumtype; + } + assert(key != NULL); + +#ifdef CFX_EXERCISE + { + static int initialized = 0; + if (!initialized) { + srand(time(0)); + initialized = 1; + } + } +#endif + + if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { + krb5_data plain; + krb5_enc_data cipher; + size_t ec_max; + + /* 300: Adds some slop. */ + if (SIZE_MAX - 300 < message->length) + return ENOMEM; + ec_max = SIZE_MAX - message->length - 300; + if (ec_max > 0xffff) + ec_max = 0xffff; +#ifdef CFX_EXERCISE + /* For testing only. For performance, always set ec = 0. */ + ec = ec_max & rand(); +#else + ec = 0; +#endif + err = alloc_data(&plain, message->length + 16 + ec); + if (err) + return err; + + /* Get size of ciphertext. */ + bufsize = 16 + krb5_encrypt_size (plain.length, key->keyblock.enctype); + /* Allocate space for header plus encrypted data. */ + outbuf = gssalloc_malloc(bufsize); + if (outbuf == NULL) { + free(plain.data); + return ENOMEM; + } + + /* TOK_ID */ + store_16_be(KG2_TOK_WRAP_MSG, outbuf); + /* flags */ + outbuf[2] = (acceptor_flag + | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0) + | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); + /* filler */ + outbuf[3] = 0xff; + /* EC */ + store_16_be(ec, outbuf+4); + /* RRC */ + store_16_be(0, outbuf+6); + store_64_be(ctx->seq_send, outbuf+8); + + memcpy(plain.data, message->value, message->length); + if (ec != 0) + memset(plain.data + message->length, 'x', ec); + memcpy(plain.data + message->length + ec, outbuf, 16); + + cipher.ciphertext.data = (char *)outbuf + 16; + cipher.ciphertext.length = bufsize - 16; + cipher.enctype = key->keyblock.enctype; + err = krb5_k_encrypt(context, key, key_usage, 0, &plain, &cipher); + zap(plain.data, plain.length); + free(plain.data); + plain.data = 0; + if (err) + goto error; + + /* Now that we know we're returning a valid token.... */ + ctx->seq_send++; + +#ifdef CFX_EXERCISE + rrc = rand() & 0xffff; + if (gss_krb5int_rotate_left(outbuf+16, bufsize-16, + (bufsize-16) - (rrc % (bufsize - 16)))) + store_16_be(rrc, outbuf+6); + /* If the rotate fails, don't worry about it. */ +#endif + } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) { + krb5_data plain; + size_t cksumsize; + + /* Here, message is the application-supplied data; message2 is + what goes into the output token. They may be the same, or + message2 may be empty (for MIC). */ + + tok_id = KG2_TOK_WRAP_MSG; + + wrap_with_checksum: + err = alloc_data(&plain, message->length + 16); + if (err) + return err; + + err = krb5_c_checksum_length(context, cksumtype, &cksumsize); + if (err) + goto error; + + assert(cksumsize <= 0xffff); + + bufsize = 16 + message2->length + cksumsize; + outbuf = gssalloc_malloc(bufsize); + if (outbuf == NULL) { + free(plain.data); + plain.data = 0; + err = ENOMEM; + goto error; + } + + /* TOK_ID */ + store_16_be(tok_id, outbuf); + /* flags */ + outbuf[2] = (acceptor_flag + | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); + /* filler */ + outbuf[3] = 0xff; + if (toktype == KG_TOK_WRAP_MSG) { + /* Use 0 for checksum calculation, substitute + checksum length later. */ + /* EC */ + store_16_be(0, outbuf+4); + /* RRC */ + store_16_be(0, outbuf+6); + } else { + /* MIC and DEL store 0xFF in EC and RRC. */ + store_16_be(0xffff, outbuf+4); + store_16_be(0xffff, outbuf+6); + } + store_64_be(ctx->seq_send, outbuf+8); + + memcpy(plain.data, message->value, message->length); + memcpy(plain.data + message->length, outbuf, 16); + + /* Fill in the output token -- data contents, if any, and + space for the checksum. */ + if (message2->length) + memcpy(outbuf + 16, message2->value, message2->length); + + sum.contents = outbuf + 16 + message2->length; + sum.length = cksumsize; + + err = krb5_k_make_checksum(context, cksumtype, key, + key_usage, &plain, &sum); + zap(plain.data, plain.length); + free(plain.data); + plain.data = 0; + if (err) { + zap(outbuf,bufsize); + goto error; + } + if (sum.length != cksumsize) + abort(); + memcpy(outbuf + 16 + message2->length, sum.contents, cksumsize); + krb5_free_checksum_contents(context, &sum); + sum.contents = 0; + /* Now that we know we're actually generating the token... */ + ctx->seq_send++; + + if (toktype == KG_TOK_WRAP_MSG) { +#ifdef CFX_EXERCISE + rrc = rand() & 0xffff; + /* If the rotate fails, don't worry about it. */ + if (gss_krb5int_rotate_left(outbuf+16, bufsize-16, + (bufsize-16) - (rrc % (bufsize - 16)))) + store_16_be(rrc, outbuf+6); +#endif + /* Fix up EC field. */ + store_16_be(cksumsize, outbuf+4); + } else { + store_16_be(0xffff, outbuf+6); + } + } else if (toktype == KG_TOK_MIC_MSG) { + tok_id = KG2_TOK_MIC_MSG; + message2 = &empty_message; + goto wrap_with_checksum; + } else if (toktype == KG_TOK_DEL_CTX) { + tok_id = KG2_TOK_DEL_CTX; + message = message2 = &empty_message; + goto wrap_with_checksum; + } else + abort(); + + token->value = outbuf; + token->length = bufsize; + return 0; + +error: + gssalloc_free(outbuf); + token->value = NULL; + token->length = 0; + return err; +} + +/* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX + conf_state is only valid if SEAL. */ + +OM_uint32 +gss_krb5int_unseal_token_v3(krb5_context *contextptr, + OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + unsigned char *ptr, unsigned int bodysize, + gss_buffer_t message_buffer, + int *conf_state, gss_qop_t *qop_state, int toktype) +{ + krb5_context context = *contextptr; + krb5_data plain; + uint64_t seqnum; + size_t ec, rrc; + int key_usage; + unsigned char acceptor_flag; + krb5_checksum sum; + krb5_error_code err; + krb5_boolean valid; + krb5_key key; + krb5_cksumtype cksumtype; + + if (qop_state) + *qop_state = GSS_C_QOP_DEFAULT; + + acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0; + key_usage = (toktype == KG_TOK_WRAP_MSG + ? (!ctx->initiate + ? KG_USAGE_INITIATOR_SEAL + : KG_USAGE_ACCEPTOR_SEAL) + : (!ctx->initiate + ? KG_USAGE_INITIATOR_SIGN + : KG_USAGE_ACCEPTOR_SIGN)); + + /* Oops. I wrote this code assuming ptr would be at the start of + the token header. */ + ptr -= 2; + bodysize += 2; + + if (bodysize < 16) { + defective: + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) { + *minor_status = (OM_uint32)G_BAD_DIRECTION; + return GSS_S_BAD_SIG; + } + + /* Two things to note here. + + First, we can't really enforce the use of the acceptor's subkey, + if we're the acceptor; the initiator may have sent messages + before getting the subkey. We could probably enforce it if + we're the initiator. + + Second, if someone tweaks the code to not set the flag telling + the krb5 library to generate a new subkey in the AP-REP + message, the MIT library may include a subkey anyways -- + namely, a copy of the AP-REQ subkey, if it was provided. So + the initiator may think we wanted a subkey, and set the flag, + even though we weren't trying to set the subkey. The "other" + key, the one not asserted by the acceptor, will have the same + value in that case, though, so we can just ignore the flag. */ + if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) { + key = ctx->acceptor_subkey; + cksumtype = ctx->acceptor_subkey_cksumtype; + } else { + key = ctx->subkey; + cksumtype = ctx->cksumtype; + } + assert(key != NULL); + + if (toktype == KG_TOK_WRAP_MSG) { + if (load_16_be(ptr) != KG2_TOK_WRAP_MSG) + goto defective; + if (ptr[3] != 0xff) + goto defective; + ec = load_16_be(ptr+4); + rrc = load_16_be(ptr+6); + seqnum = load_64_be(ptr+8); + if (!gss_krb5int_rotate_left(ptr+16, bodysize-16, rrc)) { + no_mem: + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) { + /* confidentiality */ + krb5_enc_data cipher; + unsigned char *althdr; + + if (conf_state) + *conf_state = 1; + /* Do we have no decrypt_size function? + + For all current cryptosystems, the ciphertext size will + be larger than the plaintext size. */ + cipher.enctype = key->keyblock.enctype; + cipher.ciphertext.length = bodysize - 16; + cipher.ciphertext.data = (char *)ptr + 16; + plain.length = bodysize - 16; + plain.data = gssalloc_malloc(plain.length); + if (plain.data == NULL) + goto no_mem; + err = krb5_k_decrypt(context, key, key_usage, 0, + &cipher, &plain); + if (err) { + gssalloc_free(plain.data); + goto error; + } + /* Don't use bodysize here! Use the fact that + cipher.ciphertext.length has been adjusted to the + correct length. */ + althdr = (unsigned char *)plain.data + plain.length - 16; + if (load_16_be(althdr) != KG2_TOK_WRAP_MSG + || althdr[2] != ptr[2] + || althdr[3] != ptr[3] + || memcmp(althdr+8, ptr+8, 8)) { + free(plain.data); + goto defective; + } + message_buffer->value = plain.data; + message_buffer->length = plain.length - ec - 16; + if(message_buffer->length == 0) { + gssalloc_free(message_buffer->value); + message_buffer->value = NULL; + } + } else { + size_t cksumsize; + + err = krb5_c_checksum_length(context, cksumtype, &cksumsize); + if (err) + goto error; + + /* no confidentiality */ + if (conf_state) + *conf_state = 0; + if (ec + 16 < ec) + /* overflow check */ + goto defective; + if (ec + 16 > bodysize) + goto defective; + /* We have: header | msg | cksum. + We need cksum(msg | header). + Rotate the first two. */ + store_16_be(0, ptr+4); + store_16_be(0, ptr+6); + plain = make_data(ptr, bodysize - ec); + if (!gss_krb5int_rotate_left(ptr, bodysize-ec, 16)) + goto no_mem; + sum.length = ec; + if (sum.length != cksumsize) { + *minor_status = 0; + return GSS_S_BAD_SIG; + } + sum.contents = ptr+bodysize-ec; + sum.checksum_type = cksumtype; + err = krb5_k_verify_checksum(context, key, key_usage, + &plain, &sum, &valid); + if (err) + goto error; + if (!valid) { + *minor_status = 0; + return GSS_S_BAD_SIG; + } + message_buffer->length = plain.length - 16; + message_buffer->value = gssalloc_malloc(message_buffer->length); + if (message_buffer->value == NULL) + goto no_mem; + memcpy(message_buffer->value, plain.data, message_buffer->length); + } + err = g_seqstate_check(ctx->seqstate, seqnum); + *minor_status = 0; + return err; + } else if (toktype == KG_TOK_MIC_MSG) { + /* wrap token, no confidentiality */ + if (load_16_be(ptr) != KG2_TOK_MIC_MSG) + goto defective; + verify_mic_1: + if (ptr[3] != 0xff) + goto defective; + if (load_32_be(ptr+4) != 0xffffffffL) + goto defective; + seqnum = load_64_be(ptr+8); + plain.length = message_buffer->length + 16; + plain.data = malloc(plain.length); + if (plain.data == NULL) + goto no_mem; + if (message_buffer->length) + memcpy(plain.data, message_buffer->value, message_buffer->length); + memcpy(plain.data + message_buffer->length, ptr, 16); + sum.length = bodysize - 16; + sum.contents = ptr + 16; + sum.checksum_type = cksumtype; + err = krb5_k_verify_checksum(context, key, key_usage, + &plain, &sum, &valid); + free(plain.data); + plain.data = NULL; + if (err) { + error: + *minor_status = err; + save_error_info(*minor_status, context); + return GSS_S_BAD_SIG; /* XXX */ + } + if (!valid) { + *minor_status = 0; + return GSS_S_BAD_SIG; + } + err = g_seqstate_check(ctx->seqstate, seqnum); + *minor_status = 0; + return err; + } else if (toktype == KG_TOK_DEL_CTX) { + if (load_16_be(ptr) != KG2_TOK_DEL_CTX) + goto defective; + message_buffer = (gss_buffer_t)&empty_message; + goto verify_mic_1; + } else { + goto defective; + } +} diff --git a/src/lib/gssapi/krb5/k5sealv3iov.c b/src/lib/gssapi/krb5/k5sealv3iov.c new file mode 100644 index 000000000000..a73edb6a4ffd --- /dev/null +++ b/src/lib/gssapi/krb5/k5sealv3iov.c @@ -0,0 +1,468 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/k5sealv3iov.c */ +/* + * Copyright 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +krb5_error_code +gss_krb5int_make_seal_token_v3_iov(krb5_context context, + krb5_gss_ctx_id_rec *ctx, + int conf_req_flag, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + krb5_error_code code = 0; + gss_iov_buffer_t header; + gss_iov_buffer_t padding; + gss_iov_buffer_t trailer; + unsigned char acceptor_flag; + unsigned short tok_id; + unsigned char *outbuf = NULL; + unsigned char *tbuf = NULL; + int key_usage; + size_t rrc = 0; + unsigned int gss_headerlen, gss_trailerlen; + krb5_key key; + krb5_cksumtype cksumtype; + size_t data_length, assoc_data_length; + + assert(ctx->proto == 1); + + acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR; + key_usage = (toktype == KG_TOK_WRAP_MSG + ? (ctx->initiate + ? KG_USAGE_INITIATOR_SEAL + : KG_USAGE_ACCEPTOR_SEAL) + : (ctx->initiate + ? KG_USAGE_INITIATOR_SIGN + : KG_USAGE_ACCEPTOR_SIGN)); + if (ctx->have_acceptor_subkey) { + key = ctx->acceptor_subkey; + cksumtype = ctx->acceptor_subkey_cksumtype; + } else { + key = ctx->subkey; + cksumtype = ctx->cksumtype; + } + assert(key != NULL); + assert(cksumtype != 0); + + kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); + + header = kg_locate_header_iov(iov, iov_count, toktype); + if (header == NULL) + return EINVAL; + + padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding != NULL) + padding->buffer.length = 0; + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + + if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { + unsigned int k5_headerlen, k5_trailerlen, k5_padlen; + size_t ec = 0; + size_t conf_data_length = data_length - assoc_data_length; + + code = krb5_c_crypto_length(context, key->keyblock.enctype, + KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); + if (code != 0) + goto cleanup; + + code = krb5_c_padding_length(context, key->keyblock.enctype, + conf_data_length + 16 /* E(Header) */, &k5_padlen); + if (code != 0) + goto cleanup; + + if (k5_padlen == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) { + /* Windows rejects AEAD tokens with non-zero EC */ + code = krb5_c_block_size(context, key->keyblock.enctype, &ec); + if (code != 0) + goto cleanup; + } else + ec = k5_padlen; + + code = krb5_c_crypto_length(context, key->keyblock.enctype, + KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen); + if (code != 0) + goto cleanup; + + gss_headerlen = 16 /* Header */ + k5_headerlen; + gss_trailerlen = ec + 16 /* E(Header) */ + k5_trailerlen; + + if (trailer == NULL) { + rrc = gss_trailerlen; + /* Workaround for Windows bug where it rotates by EC + RRC */ + if (ctx->gss_flags & GSS_C_DCE_STYLE) + rrc -= ec; + gss_headerlen += gss_trailerlen; + } + + if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { + code = kg_allocate_iov(header, (size_t) gss_headerlen); + } else if (header->buffer.length < gss_headerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + outbuf = (unsigned char *)header->buffer.value; + header->buffer.length = (size_t) gss_headerlen; + + if (trailer != NULL) { + if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = kg_allocate_iov(trailer, (size_t) gss_trailerlen); + else if (trailer->buffer.length < gss_trailerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + trailer->buffer.length = (size_t) gss_trailerlen; + } + + /* TOK_ID */ + store_16_be(KG2_TOK_WRAP_MSG, outbuf); + /* flags */ + outbuf[2] = (acceptor_flag + | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0) + | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); + /* filler */ + outbuf[3] = 0xFF; + /* EC */ + store_16_be(ec, outbuf + 4); + /* RRC */ + store_16_be(0, outbuf + 6); + store_64_be(ctx->seq_send, outbuf + 8); + + /* EC | copy of header to be encrypted, located in (possibly rotated) trailer */ + if (trailer == NULL) + tbuf = (unsigned char *)header->buffer.value + 16; /* Header */ + else + tbuf = (unsigned char *)trailer->buffer.value; + + memset(tbuf, 0xFF, ec); + memcpy(tbuf + ec, header->buffer.value, 16); + + code = kg_encrypt_iov(context, ctx->proto, + ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), + ec, rrc, key, key_usage, 0, iov, iov_count); + if (code != 0) + goto cleanup; + + /* RRC */ + store_16_be(rrc, outbuf + 6); + + ctx->seq_send++; + } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) { + tok_id = KG2_TOK_WRAP_MSG; + + wrap_with_checksum: + + gss_headerlen = 16; + + code = krb5_c_crypto_length(context, key->keyblock.enctype, + KRB5_CRYPTO_TYPE_CHECKSUM, + &gss_trailerlen); + if (code != 0) + goto cleanup; + + assert(gss_trailerlen <= 0xFFFF); + + if (trailer == NULL) { + rrc = gss_trailerlen; + gss_headerlen += gss_trailerlen; + } + + if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = kg_allocate_iov(header, (size_t) gss_headerlen); + else if (header->buffer.length < gss_headerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + outbuf = (unsigned char *)header->buffer.value; + header->buffer.length = (size_t) gss_headerlen; + + if (trailer != NULL) { + if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = kg_allocate_iov(trailer, (size_t) gss_trailerlen); + else if (trailer->buffer.length < gss_trailerlen) + code = KRB5_BAD_MSIZE; + if (code != 0) + goto cleanup; + trailer->buffer.length = (size_t) gss_trailerlen; + } + + /* TOK_ID */ + store_16_be(tok_id, outbuf); + /* flags */ + outbuf[2] = (acceptor_flag + | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); + /* filler */ + outbuf[3] = 0xFF; + if (toktype == KG_TOK_WRAP_MSG) { + /* Use 0 for checksum calculation, substitute + * checksum length later. + */ + /* EC */ + store_16_be(0, outbuf + 4); + /* RRC */ + store_16_be(0, outbuf + 6); + } else { + /* MIC and DEL store 0xFF in EC and RRC */ + store_16_be(0xFFFF, outbuf + 4); + store_16_be(0xFFFF, outbuf + 6); + } + store_64_be(ctx->seq_send, outbuf + 8); + + code = kg_make_checksum_iov_v3(context, cksumtype, + rrc, key, key_usage, + iov, iov_count, toktype); + if (code != 0) + goto cleanup; + + ctx->seq_send++; + + if (toktype == KG_TOK_WRAP_MSG) { + /* Fix up EC field */ + store_16_be(gss_trailerlen, outbuf + 4); + /* Fix up RRC field */ + store_16_be(rrc, outbuf + 6); + } + } else if (toktype == KG_TOK_MIC_MSG) { + tok_id = KG2_TOK_MIC_MSG; + trailer = NULL; + goto wrap_with_checksum; + } else if (toktype == KG_TOK_DEL_CTX) { + tok_id = KG2_TOK_DEL_CTX; + goto wrap_with_checksum; + } else { + abort(); + } + + code = 0; + if (conf_state != NULL) + *conf_state = conf_req_flag; + +cleanup: + if (code != 0) + kg_release_iov(iov, iov_count); + + return code; +} + +OM_uint32 +gss_krb5int_unseal_v3_iov(krb5_context context, + OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + gss_iov_buffer_desc *iov, + int iov_count, + int *conf_state, + gss_qop_t *qop_state, + int toktype) +{ + OM_uint32 code; + gss_iov_buffer_t header; + gss_iov_buffer_t padding; + gss_iov_buffer_t trailer; + unsigned char acceptor_flag; + unsigned char *ptr = NULL; + int key_usage; + size_t rrc, ec; + size_t data_length, assoc_data_length; + krb5_key key; + uint64_t seqnum; + krb5_boolean valid; + krb5_cksumtype cksumtype; + int conf_flag = 0; + + if (qop_state != NULL) + *qop_state = GSS_C_QOP_DEFAULT; + + header = kg_locate_header_iov(iov, iov_count, toktype); + assert(header != NULL); + + padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding != NULL && padding->buffer.length != 0) + return GSS_S_DEFECTIVE_TOKEN; + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + + acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0; + key_usage = (toktype == KG_TOK_WRAP_MSG + ? (!ctx->initiate + ? KG_USAGE_INITIATOR_SEAL + : KG_USAGE_ACCEPTOR_SEAL) + : (!ctx->initiate + ? KG_USAGE_INITIATOR_SIGN + : KG_USAGE_ACCEPTOR_SIGN)); + + kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); + + ptr = (unsigned char *)header->buffer.value; + + if (header->buffer.length < 16) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) { + *minor_status = (OM_uint32)G_BAD_DIRECTION; + return GSS_S_BAD_SIG; + } + + if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) { + key = ctx->acceptor_subkey; + cksumtype = ctx->acceptor_subkey_cksumtype; + } else { + key = ctx->subkey; + cksumtype = ctx->cksumtype; + } + assert(key != NULL); + + + if (toktype == KG_TOK_WRAP_MSG) { + unsigned int k5_trailerlen; + + if (load_16_be(ptr) != KG2_TOK_WRAP_MSG) + goto defective; + conf_flag = ((ptr[2] & FLAG_WRAP_CONFIDENTIAL) != 0); + if (ptr[3] != 0xFF) + goto defective; + ec = load_16_be(ptr + 4); + rrc = load_16_be(ptr + 6); + seqnum = load_64_be(ptr + 8); + + code = krb5_c_crypto_length(context, key->keyblock.enctype, + conf_flag ? KRB5_CRYPTO_TYPE_TRAILER : + KRB5_CRYPTO_TYPE_CHECKSUM, + &k5_trailerlen); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + /* Deal with RRC */ + if (trailer == NULL) { + size_t desired_rrc = k5_trailerlen; + + if (conf_flag) { + desired_rrc += 16; /* E(Header) */ + + if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0) + desired_rrc += ec; + } + + /* According to MS, we only need to deal with a fixed RRC for DCE */ + if (rrc != desired_rrc) + goto defective; + } else if (rrc != 0) { + /* Should have been rotated by kg_unseal_stream_iov() */ + goto defective; + } + + if (conf_flag) { + unsigned char *althdr; + + /* Decrypt */ + code = kg_decrypt_iov(context, ctx->proto, + ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), + ec, rrc, + key, key_usage, 0, iov, iov_count); + if (code != 0) { + *minor_status = code; + return GSS_S_BAD_SIG; + } + + /* Validate header integrity */ + if (trailer == NULL) + althdr = (unsigned char *)header->buffer.value + 16 + ec; + else + althdr = (unsigned char *)trailer->buffer.value + ec; + + if (load_16_be(althdr) != KG2_TOK_WRAP_MSG + || althdr[2] != ptr[2] + || althdr[3] != ptr[3] + || memcmp(althdr + 8, ptr + 8, 8) != 0) { + *minor_status = 0; + return GSS_S_BAD_SIG; + } + } else { + /* Verify checksum: note EC is checksum size here, not padding */ + if (ec != k5_trailerlen) + goto defective; + + /* Zero EC, RRC before computing checksum */ + store_16_be(0, ptr + 4); + store_16_be(0, ptr + 6); + + code = kg_verify_checksum_iov_v3(context, cksumtype, rrc, + key, key_usage, + iov, iov_count, toktype, &valid); + if (code != 0 || valid == FALSE) { + *minor_status = code; + return GSS_S_BAD_SIG; + } + } + + code = g_seqstate_check(ctx->seqstate, seqnum); + } else if (toktype == KG_TOK_MIC_MSG) { + if (load_16_be(ptr) != KG2_TOK_MIC_MSG) + goto defective; + + verify_mic_1: + if (ptr[3] != 0xFF) + goto defective; + seqnum = load_64_be(ptr + 8); + + /* For MIC tokens, the GSS header and checksum are in the same buffer. + * Fake up an RRC so that the checksum is expected in the header. */ + rrc = (trailer != NULL) ? 0 : header->buffer.length - 16; + code = kg_verify_checksum_iov_v3(context, cksumtype, rrc, + key, key_usage, + iov, iov_count, toktype, &valid); + if (code != 0 || valid == FALSE) { + *minor_status = code; + return GSS_S_BAD_SIG; + } + code = g_seqstate_check(ctx->seqstate, seqnum); + } else if (toktype == KG_TOK_DEL_CTX) { + if (load_16_be(ptr) != KG2_TOK_DEL_CTX) + goto defective; + goto verify_mic_1; + } else { + goto defective; + } + + *minor_status = 0; + + if (conf_state != NULL) + *conf_state = conf_flag; + + return code; + +defective: + *minor_status = 0; + + return GSS_S_DEFECTIVE_TOKEN; +} diff --git a/src/lib/gssapi/krb5/k5unseal.c b/src/lib/gssapi/krb5/k5unseal.c new file mode 100644 index 000000000000..26a2d33e7a2c --- /dev/null +++ b/src/lib/gssapi/krb5/k5unseal.c @@ -0,0 +1,588 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2001, 2007 by the Massachusetts Institute of Technology. + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#include <assert.h> + +/* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX + conf_state is only valid if SEAL. */ + +static OM_uint32 +kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer, + conf_state, qop_state, toktype) + krb5_context context; + OM_uint32 *minor_status; + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + int bodysize; + gss_buffer_t message_buffer; + int *conf_state; + gss_qop_t *qop_state; + int toktype; +{ + krb5_error_code code; + int conflen = 0; + int signalg; + int sealalg; + int bad_pad = 0; + gss_buffer_desc token; + krb5_checksum cksum; + krb5_checksum md5cksum; + krb5_data plaind; + char *data_ptr; + unsigned char *plain; + unsigned int cksum_len = 0; + size_t plainlen; + int direction; + krb5_ui_4 seqnum; + OM_uint32 retval; + size_t sumlen; + size_t padlen; + krb5_keyusage sign_usage = KG_USAGE_SIGN; + + if (toktype == KG_TOK_SEAL_MSG) { + message_buffer->length = 0; + message_buffer->value = NULL; + } + + /* Sanity checks */ + + if (ctx->seq == NULL) { + /* ctx was established using a newer enctype, and cannot process RFC + * 1964 tokens. */ + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if ((bodysize < 22) || (ptr[4] != 0xff) || (ptr[5] != 0xff)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + signalg = ptr[0] + (ptr[1]<<8); + sealalg = ptr[2] + (ptr[3]<<8); + + if ((toktype != KG_TOK_SEAL_MSG) && + (sealalg != 0xffff)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* in the current spec, there is only one valid seal algorithm per + key type, so a simple comparison is ok */ + + if ((toktype == KG_TOK_SEAL_MSG) && + !((sealalg == 0xffff) || + (sealalg == ctx->sealalg))) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* there are several mappings of seal algorithms to sign algorithms, + but few enough that we can try them all. */ + + if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) || + (ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) || + (ctx->sealalg == SEAL_ALG_DES3KD && + signalg != SGN_ALG_HMAC_SHA1_DES3_KD)|| + (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4 && + signalg != SGN_ALG_HMAC_MD5)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_MD2_5: + case SGN_ALG_HMAC_MD5: + cksum_len = 8; + if (toktype != KG_TOK_SEAL_MSG) + sign_usage = 15; + break; + case SGN_ALG_3: + cksum_len = 16; + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + cksum_len = 20; + break; + default: + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if ((size_t)bodysize < 14 + cksum_len) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* get the token parameters */ + + if ((code = kg_get_seq_num(context, ctx->seq, ptr+14, ptr+6, &direction, + &seqnum))) { + *minor_status = code; + return(GSS_S_BAD_SIG); + } + + /* decode the message, if SEAL */ + + if (toktype == KG_TOK_SEAL_MSG) { + size_t tmsglen = bodysize-(14+cksum_len); + if (sealalg != 0xffff) { + if ((plain = (unsigned char *) xmalloc(tmsglen)) == NULL) { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) { + unsigned char bigend_seqnum[4]; + krb5_keyblock *enc_key; + int i; + store_32_be(seqnum, bigend_seqnum); + code = krb5_k_key_keyblock(context, ctx->enc, &enc_key); + if (code) + { + xfree(plain); + *minor_status = code; + return(GSS_S_FAILURE); + } + + assert (enc_key->length == 16); + for (i = 0; i <= 15; i++) + ((char *) enc_key->contents)[i] ^=0xf0; + code = kg_arcfour_docrypt (enc_key, 0, + &bigend_seqnum[0], 4, + ptr+14+cksum_len, tmsglen, + plain); + krb5_free_keyblock (context, enc_key); + } else { + code = kg_decrypt(context, ctx->enc, KG_USAGE_SEAL, NULL, + ptr+14+cksum_len, plain, tmsglen); + } + if (code) { + xfree(plain); + *minor_status = code; + return(GSS_S_FAILURE); + } + } else { + plain = ptr+14+cksum_len; + } + + plainlen = tmsglen; + + conflen = kg_confounder_size(context, ctx->enc->keyblock.enctype); + if (tmsglen < conflen) { + if (sealalg != 0xffff) + xfree(plain); + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); + } + padlen = plain[tmsglen - 1]; + if (tmsglen - conflen < padlen) { + /* Don't error out yet, to avoid padding oracle attacks. We will + * treat this as a checksum failure later on. */ + padlen = 0; + bad_pad = 1; + } + token.length = tmsglen - conflen - padlen; + + if (token.length) { + if ((token.value = (void *) gssalloc_malloc(token.length)) == NULL) { + if (sealalg != 0xffff) + xfree(plain); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + memcpy(token.value, plain+conflen, token.length); + } else { + token.value = NULL; + } + } else if (toktype == KG_TOK_SIGN_MSG) { + token = *message_buffer; + plain = token.value; + plainlen = token.length; + } else { + token.length = 0; + token.value = NULL; + plain = token.value; + plainlen = token.length; + } + + /* compute the checksum of the message */ + + /* initialize the the cksum */ + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_MD2_5: + case SGN_ALG_DES_MAC: + case SGN_ALG_3: + md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; + break; + case SGN_ALG_HMAC_MD5: + md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; + break; + default: + abort (); + } + + code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen); + if (code) + return(code); + md5cksum.length = sumlen; + + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_3: + /* compute the checksum of the message */ + + /* 8 = bytes of token body to be checksummed according to spec */ + + if (! (data_ptr = xmalloc(8 + plainlen))) { + if (sealalg != 0xffff) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + (void) memcpy(data_ptr, ptr-2, 8); + + (void) memcpy(data_ptr+8, plain, plainlen); + + plaind.length = 8 + plainlen; + plaind.data = data_ptr; + code = krb5_k_make_checksum(context, md5cksum.checksum_type, + ctx->seq, sign_usage, + &plaind, &md5cksum); + xfree(data_ptr); + + if (code) { + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = code; + return(GSS_S_FAILURE); + } + + code = kg_encrypt_inplace(context, ctx->seq, KG_USAGE_SEAL, + (g_OID_equal(ctx->mech_used, + gss_mech_krb5_old) ? + ctx->seq->keyblock.contents : NULL), + md5cksum.contents, 16); + if (code) { + krb5_free_checksum_contents(context, &md5cksum); + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = code; + return GSS_S_FAILURE; + } + + if (signalg == 0) + cksum.length = 8; + else + cksum.length = 16; + cksum.contents = md5cksum.contents + 16 - cksum.length; + + code = k5_bcmp(cksum.contents, ptr + 14, cksum.length); + break; + + case SGN_ALG_MD2_5: + if (!ctx->seed_init && + (code = kg_make_seed(context, ctx->subkey, ctx->seed))) { + krb5_free_checksum_contents(context, &md5cksum); + if (sealalg != 0xffff) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = code; + return GSS_S_FAILURE; + } + + if (! (data_ptr = xmalloc(sizeof(ctx->seed) + 8 + plainlen))) { + krb5_free_checksum_contents(context, &md5cksum); + if (sealalg == 0) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + (void) memcpy(data_ptr, ptr-2, 8); + (void) memcpy(data_ptr+8, ctx->seed, sizeof(ctx->seed)); + (void) memcpy(data_ptr+8+sizeof(ctx->seed), plain, plainlen); + plaind.length = 8 + sizeof(ctx->seed) + plainlen; + plaind.data = data_ptr; + krb5_free_checksum_contents(context, &md5cksum); + code = krb5_k_make_checksum(context, md5cksum.checksum_type, + ctx->seq, sign_usage, + &plaind, &md5cksum); + xfree(data_ptr); + + if (code) { + if (sealalg == 0) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = code; + return(GSS_S_FAILURE); + } + + code = k5_bcmp(md5cksum.contents, ptr + 14, 8); + /* Falls through to defective-token?? */ + + default: + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); + + case SGN_ALG_HMAC_SHA1_DES3_KD: + case SGN_ALG_HMAC_MD5: + /* compute the checksum of the message */ + + /* 8 = bytes of token body to be checksummed according to spec */ + + if (! (data_ptr = xmalloc(8 + plainlen))) { + if (sealalg != 0xffff) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + (void) memcpy(data_ptr, ptr-2, 8); + + (void) memcpy(data_ptr+8, plain, plainlen); + + plaind.length = 8 + plainlen; + plaind.data = data_ptr; + code = krb5_k_make_checksum(context, md5cksum.checksum_type, + ctx->seq, sign_usage, + &plaind, &md5cksum); + xfree(data_ptr); + + if (code) { + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = code; + return(GSS_S_FAILURE); + } + + code = k5_bcmp(md5cksum.contents, ptr + 14, cksum_len); + break; + } + + krb5_free_checksum_contents(context, &md5cksum); + if (sealalg != 0xffff) + xfree(plain); + + /* compare the computed checksum against the transmitted checksum */ + + if (code || bad_pad) { + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = 0; + return(GSS_S_BAD_SIG); + } + + + /* It got through unscathed. Make sure the context is unexpired. */ + + if (toktype == KG_TOK_SEAL_MSG) + *message_buffer = token; + + if (conf_state) + *conf_state = (sealalg != 0xffff); + + if (qop_state) + *qop_state = GSS_C_QOP_DEFAULT; + + /* do sequencing checks */ + + if ((ctx->initiate && direction != 0xff) || + (!ctx->initiate && direction != 0)) { + if (toktype == KG_TOK_SEAL_MSG) { + gssalloc_free(token.value); + message_buffer->value = NULL; + message_buffer->length = 0; + } + *minor_status = (OM_uint32)G_BAD_DIRECTION; + return(GSS_S_BAD_SIG); + } + + retval = g_seqstate_check(ctx->seqstate, (uint64_t)seqnum); + + /* success or ordering violation */ + + *minor_status = 0; + return(retval); +} + +/* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX + conf_state is only valid if SEAL. */ + +OM_uint32 +kg_unseal(minor_status, context_handle, input_token_buffer, + message_buffer, conf_state, qop_state, toktype) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t input_token_buffer; + gss_buffer_t message_buffer; + int *conf_state; + gss_qop_t *qop_state; + int toktype; +{ + krb5_gss_ctx_id_rec *ctx; + unsigned char *ptr; + unsigned int bodysize; + int err; + int toktype2; + int vfyflags = 0; + OM_uint32 ret; + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + /* parse the token, leave the data in message_buffer, setting conf_state */ + + /* verify the header */ + + ptr = (unsigned char *) input_token_buffer->value; + + + err = g_verify_token_header(ctx->mech_used, + &bodysize, &ptr, -1, + input_token_buffer->length, + vfyflags); + if (err) { + *minor_status = err; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (bodysize < 2) { + *minor_status = (OM_uint32)G_BAD_TOK_HEADER; + return GSS_S_DEFECTIVE_TOKEN; + } + + toktype2 = load_16_be(ptr); + + ptr += 2; + bodysize -= 2; + + switch (toktype2) { + case KG2_TOK_MIC_MSG: + case KG2_TOK_WRAP_MSG: + case KG2_TOK_DEL_CTX: + ret = gss_krb5int_unseal_token_v3(&ctx->k5_context, minor_status, ctx, + ptr, bodysize, message_buffer, + conf_state, qop_state, toktype); + break; + case KG_TOK_MIC_MSG: + case KG_TOK_WRAP_MSG: + case KG_TOK_DEL_CTX: + ret = kg_unseal_v1(ctx->k5_context, minor_status, ctx, ptr, bodysize, + message_buffer, conf_state, qop_state, + toktype); + break; + default: + *minor_status = (OM_uint32)G_BAD_TOK_HEADER; + ret = GSS_S_DEFECTIVE_TOKEN; + break; + } + + if (ret != 0) + save_error_info (*minor_status, ctx->k5_context); + + return ret; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_unwrap(minor_status, context_handle, + input_message_buffer, output_message_buffer, + conf_state, qop_state) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t input_message_buffer; + gss_buffer_t output_message_buffer; + int *conf_state; + gss_qop_t *qop_state; +{ + OM_uint32 rstat; + + rstat = kg_unseal(minor_status, context_handle, + input_message_buffer, output_message_buffer, + conf_state, qop_state, KG_TOK_WRAP_MSG); + return(rstat); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_verify_mic(minor_status, context_handle, + message_buffer, token_buffer, + qop_state) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t message_buffer; + gss_buffer_t token_buffer; + gss_qop_t *qop_state; +{ + OM_uint32 rstat; + + rstat = kg_unseal(minor_status, context_handle, + token_buffer, message_buffer, + NULL, qop_state, KG_TOK_MIC_MSG); + return(rstat); +} diff --git a/src/lib/gssapi/krb5/k5unsealiov.c b/src/lib/gssapi/krb5/k5unsealiov.c new file mode 100644 index 000000000000..8b6704274db8 --- /dev/null +++ b/src/lib/gssapi/krb5/k5unsealiov.c @@ -0,0 +1,675 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/k5unsealiov.c */ +/* + * Copyright 2008, 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +static OM_uint32 +kg_unseal_v1_iov(krb5_context context, + OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + gss_iov_buffer_desc *iov, + int iov_count, + size_t token_wrapper_len, + int *conf_state, + gss_qop_t *qop_state, + int toktype) +{ + OM_uint32 code; + gss_iov_buffer_t header; + gss_iov_buffer_t trailer; + unsigned char *ptr; + int sealalg; + int signalg; + krb5_checksum cksum; + krb5_checksum md5cksum; + size_t cksum_len = 0; + size_t conflen = 0; + int direction; + krb5_ui_4 seqnum; + OM_uint32 retval; + size_t sumlen; + krb5_keyusage sign_usage = KG_USAGE_SIGN; + + md5cksum.length = cksum.length = 0; + md5cksum.contents = cksum.contents = NULL; + + header = kg_locate_header_iov(iov, iov_count, toktype); + assert(header != NULL); + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + if (trailer != NULL && trailer->buffer.length != 0) { + *minor_status = (OM_uint32)KRB5_BAD_MSIZE; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (ctx->seq == NULL) { + /* ctx was established using a newer enctype, and cannot process RFC + * 1964 tokens. */ + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (header->buffer.length < token_wrapper_len + 22) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + ptr = (unsigned char *)header->buffer.value + token_wrapper_len; + + signalg = ptr[0]; + signalg |= ptr[1] << 8; + + sealalg = ptr[2]; + sealalg |= ptr[3] << 8; + + if (ptr[4] != 0xFF || ptr[5] != 0xFF) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (toktype != KG_TOK_WRAP_MSG && sealalg != 0xFFFF) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (toktype == KG_TOK_WRAP_MSG && + !(sealalg == 0xFFFF || sealalg == ctx->sealalg)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) || + (ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) || + (ctx->sealalg == SEAL_ALG_DES3KD && + signalg != SGN_ALG_HMAC_SHA1_DES3_KD)|| + (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4 && + signalg != SGN_ALG_HMAC_MD5)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_MD2_5: + case SGN_ALG_HMAC_MD5: + cksum_len = 8; + if (toktype != KG_TOK_WRAP_MSG) + sign_usage = 15; + break; + case SGN_ALG_3: + cksum_len = 16; + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + cksum_len = 20; + break; + default: + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* get the token parameters */ + code = kg_get_seq_num(context, ctx->seq, ptr + 14, ptr + 6, &direction, + &seqnum); + if (code != 0) { + *minor_status = code; + return GSS_S_BAD_SIG; + } + + /* decode the message, if SEAL */ + if (toktype == KG_TOK_WRAP_MSG) { + if (sealalg != 0xFFFF) { + if (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) { + unsigned char bigend_seqnum[4]; + krb5_keyblock *enc_key; + size_t i; + + store_32_be(seqnum, bigend_seqnum); + + code = krb5_k_key_keyblock(context, ctx->enc, &enc_key); + if (code != 0) { + retval = GSS_S_FAILURE; + goto cleanup; + } + + assert(enc_key->length == 16); + + for (i = 0; i < enc_key->length; i++) + ((char *)enc_key->contents)[i] ^= 0xF0; + + code = kg_arcfour_docrypt_iov(context, enc_key, 0, + &bigend_seqnum[0], 4, + iov, iov_count); + krb5_free_keyblock(context, enc_key); + } else { + code = kg_decrypt_iov(context, 0, + ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0), + 0 /*EC*/, 0 /*RRC*/, + ctx->enc, KG_USAGE_SEAL, NULL, + iov, iov_count); + } + if (code != 0) { + retval = GSS_S_FAILURE; + goto cleanup; + } + } + conflen = kg_confounder_size(context, ctx->enc->keyblock.enctype); + } + + if (header->buffer.length != token_wrapper_len + 14 + cksum_len + conflen) { + retval = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + + /* compute the checksum of the message */ + + /* initialize the checksum */ + + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_MD2_5: + case SGN_ALG_DES_MAC: + case SGN_ALG_3: + md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; + break; + case SGN_ALG_HMAC_MD5: + md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR; + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3; + break; + default: + abort(); + } + + code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen); + if (code != 0) { + retval = GSS_S_FAILURE; + goto cleanup; + } + md5cksum.length = sumlen; + + /* compute the checksum of the message */ + code = kg_make_checksum_iov_v1(context, md5cksum.checksum_type, + cksum_len, ctx->seq, ctx->enc, + sign_usage, iov, iov_count, toktype, + &md5cksum); + if (code != 0) { + retval = GSS_S_FAILURE; + goto cleanup; + } + + switch (signalg) { + case SGN_ALG_DES_MAC_MD5: + case SGN_ALG_3: + code = kg_encrypt_inplace(context, ctx->seq, KG_USAGE_SEAL, + (g_OID_equal(ctx->mech_used, + gss_mech_krb5_old) ? + ctx->seq->keyblock.contents : NULL), + md5cksum.contents, 16); + if (code != 0) { + retval = GSS_S_FAILURE; + goto cleanup; + } + + cksum.length = cksum_len; + cksum.contents = md5cksum.contents + 16 - cksum.length; + + code = k5_bcmp(cksum.contents, ptr + 14, cksum.length); + break; + case SGN_ALG_HMAC_SHA1_DES3_KD: + case SGN_ALG_HMAC_MD5: + code = k5_bcmp(md5cksum.contents, ptr + 14, cksum_len); + break; + default: + code = 0; + retval = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + break; + } + + if (code != 0) { + code = 0; + retval = GSS_S_BAD_SIG; + goto cleanup; + } + + /* + * For GSS_C_DCE_STYLE, the caller manages the padding, because the + * pad length is in the RPC PDU. The value of the padding may be + * uninitialized. For normal GSS, the last bytes of the decrypted + * data contain the pad length. kg_fixup_padding_iov() will find + * this and fixup the last data IOV appropriately. + */ + if (toktype == KG_TOK_WRAP_MSG && + (ctx->gss_flags & GSS_C_DCE_STYLE) == 0) { + retval = kg_fixup_padding_iov(&code, iov, iov_count); + if (retval != GSS_S_COMPLETE) + goto cleanup; + } + + if (conf_state != NULL) + *conf_state = (sealalg != 0xFFFF); + + if (qop_state != NULL) + *qop_state = GSS_C_QOP_DEFAULT; + + if ((ctx->initiate && direction != 0xff) || + (!ctx->initiate && direction != 0)) { + *minor_status = (OM_uint32)G_BAD_DIRECTION; + retval = GSS_S_BAD_SIG; + } + + code = 0; + retval = g_seqstate_check(ctx->seqstate, (uint64_t)seqnum); + +cleanup: + krb5_free_checksum_contents(context, &md5cksum); + + *minor_status = code; + + return retval; +} + +/* + * Caller must provide TOKEN | DATA | PADDING | TRAILER, except + * for DCE in which case it can just provide TOKEN | DATA (must + * guarantee that DATA is padded) + */ +static OM_uint32 +kg_unseal_iov_token(OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + krb5_error_code code; + krb5_context context = ctx->k5_context; + unsigned char *ptr; + gss_iov_buffer_t header; + gss_iov_buffer_t padding; + gss_iov_buffer_t trailer; + size_t input_length; + unsigned int bodysize; + int toktype2; + + header = kg_locate_header_iov(iov, iov_count, toktype); + if (header == NULL) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + + ptr = (unsigned char *)header->buffer.value; + input_length = header->buffer.length; + + if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0 && + toktype == KG_TOK_WRAP_MSG) { + size_t data_length, assoc_data_length; + + kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length); + + input_length += data_length - assoc_data_length; + + if (padding != NULL) + input_length += padding->buffer.length; + + if (trailer != NULL) + input_length += trailer->buffer.length; + } + + code = g_verify_token_header(ctx->mech_used, + &bodysize, &ptr, -1, + input_length, 0); + if (code != 0) { + *minor_status = code; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (bodysize < 2) { + *minor_status = (OM_uint32)G_BAD_TOK_HEADER; + return GSS_S_DEFECTIVE_TOKEN; + } + + toktype2 = load_16_be(ptr); + + ptr += 2; + bodysize -= 2; + + switch (toktype2) { + case KG2_TOK_MIC_MSG: + case KG2_TOK_WRAP_MSG: + case KG2_TOK_DEL_CTX: + code = gss_krb5int_unseal_v3_iov(context, minor_status, ctx, iov, iov_count, + conf_state, qop_state, toktype); + break; + case KG_TOK_MIC_MSG: + case KG_TOK_WRAP_MSG: + case KG_TOK_DEL_CTX: + code = kg_unseal_v1_iov(context, minor_status, ctx, iov, iov_count, + (size_t)(ptr - (unsigned char *)header->buffer.value), + conf_state, qop_state, toktype); + break; + default: + *minor_status = (OM_uint32)G_BAD_TOK_HEADER; + code = GSS_S_DEFECTIVE_TOKEN; + break; + } + + if (code != 0) + save_error_info(*minor_status, context); + + return code; +} + +/* + * Split a STREAM | SIGN_DATA | DATA into + * HEADER | SIGN_DATA | DATA | PADDING | TRAILER + */ +static OM_uint32 +kg_unseal_stream_iov(OM_uint32 *minor_status, + krb5_gss_ctx_id_rec *ctx, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + unsigned char *ptr; + unsigned int bodysize; + OM_uint32 code = 0, major_status = GSS_S_FAILURE; + krb5_context context = ctx->k5_context; + int conf_req_flag, toktype2; + int i = 0, j; + gss_iov_buffer_desc *tiov = NULL; + gss_iov_buffer_t stream, data = NULL; + gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer; + + assert(toktype == KG_TOK_WRAP_MSG); + + if (toktype != KG_TOK_WRAP_MSG || (ctx->gss_flags & GSS_C_DCE_STYLE)) { + code = EINVAL; + goto cleanup; + } + + stream = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM); + assert(stream != NULL); + + ptr = (unsigned char *)stream->buffer.value; + + code = g_verify_token_header(ctx->mech_used, + &bodysize, &ptr, -1, + stream->buffer.length, 0); + if (code != 0) { + major_status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + + if (bodysize < 2) { + *minor_status = (OM_uint32)G_BAD_TOK_HEADER; + return GSS_S_DEFECTIVE_TOKEN; + } + + toktype2 = load_16_be(ptr); + + ptr += 2; + bodysize -= 2; + + tiov = (gss_iov_buffer_desc *)calloc((size_t)iov_count + 2, sizeof(gss_iov_buffer_desc)); + if (tiov == NULL) { + code = ENOMEM; + goto cleanup; + } + + /* HEADER */ + theader = &tiov[i++]; + theader->type = GSS_IOV_BUFFER_TYPE_HEADER; + theader->buffer.value = stream->buffer.value; + theader->buffer.length = ptr - (unsigned char *)stream->buffer.value; + if (bodysize < 14 || + stream->buffer.length != theader->buffer.length + bodysize) { + major_status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + theader->buffer.length += 14; + + /* n[SIGN_DATA] | DATA | m[SIGN_DATA] */ + for (j = 0; j < iov_count; j++) { + OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[j].type); + + if (type == GSS_IOV_BUFFER_TYPE_DATA) { + if (data != NULL) { + /* only a single DATA buffer can appear */ + code = EINVAL; + goto cleanup; + } + + data = &iov[j]; + tdata = &tiov[i]; + } + if (type == GSS_IOV_BUFFER_TYPE_DATA || + type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY) + tiov[i++] = iov[j]; + } + + if (data == NULL) { + /* a single DATA buffer must be present */ + code = EINVAL; + goto cleanup; + } + + /* PADDING | TRAILER */ + tpadding = &tiov[i++]; + tpadding->type = GSS_IOV_BUFFER_TYPE_PADDING; + tpadding->buffer.length = 0; + tpadding->buffer.value = NULL; + + ttrailer = &tiov[i++]; + ttrailer->type = GSS_IOV_BUFFER_TYPE_TRAILER; + + switch (toktype2) { + case KG2_TOK_MIC_MSG: + case KG2_TOK_WRAP_MSG: + case KG2_TOK_DEL_CTX: { + size_t ec, rrc; + krb5_enctype enctype; + unsigned int k5_headerlen = 0; + unsigned int k5_trailerlen = 0; + + if (ctx->have_acceptor_subkey) + enctype = ctx->acceptor_subkey->keyblock.enctype; + else + enctype = ctx->subkey->keyblock.enctype; + conf_req_flag = ((ptr[0] & FLAG_WRAP_CONFIDENTIAL) != 0); + ec = conf_req_flag ? load_16_be(ptr + 2) : 0; + rrc = load_16_be(ptr + 4); + + if (rrc != 0) { + if (!gss_krb5int_rotate_left((unsigned char *)stream->buffer.value + 16, + stream->buffer.length - 16, rrc)) { + code = ENOMEM; + goto cleanup; + } + store_16_be(0, ptr + 4); /* set RRC to zero */ + } + + if (conf_req_flag) { + code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); + if (code != 0) + goto cleanup; + theader->buffer.length += k5_headerlen; /* length validated later */ + } + + /* no PADDING for CFX, EC is used instead */ + code = krb5_c_crypto_length(context, enctype, + conf_req_flag ? KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM, + &k5_trailerlen); + if (code != 0) + goto cleanup; + + ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) + k5_trailerlen; + ttrailer->buffer.value = (unsigned char *)stream->buffer.value + + stream->buffer.length - ttrailer->buffer.length; + break; + } + case KG_TOK_MIC_MSG: + case KG_TOK_WRAP_MSG: + case KG_TOK_DEL_CTX: + theader->buffer.length += ctx->cksum_size + + kg_confounder_size(context, ctx->enc->keyblock.enctype); + + /* + * we can't set the padding accurately until decryption; + * kg_fixup_padding_iov() will take care of this + */ + tpadding->buffer.length = 1; + tpadding->buffer.value = (unsigned char *)stream->buffer.value + stream->buffer.length - 1; + + /* no TRAILER for pre-CFX */ + ttrailer->buffer.length = 0; + ttrailer->buffer.value = NULL; + + break; + default: + code = (OM_uint32)G_BAD_TOK_HEADER; + major_status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + break; + } + + /* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/ + /* Old: GSS-Header | Conf | Data | Pad | */ + /* CFX: GSS-Header | Kerb-Header | Data | | EC | E(Header) | Kerb-Trailer */ + /* GSS: -------GSS-HEADER--------+-DATA--+-PAD-+----------GSS-TRAILER----------*/ + + /* validate lengths */ + if (stream->buffer.length < theader->buffer.length + + tpadding->buffer.length + + ttrailer->buffer.length) + { + code = (OM_uint32)KRB5_BAD_MSIZE; + major_status = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + + /* setup data */ + tdata->buffer.length = stream->buffer.length - ttrailer->buffer.length - + tpadding->buffer.length - theader->buffer.length; + + assert(data != NULL); + + if (data->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { + code = kg_allocate_iov(tdata, tdata->buffer.length); + if (code != 0) + goto cleanup; + memcpy(tdata->buffer.value, + (unsigned char *)stream->buffer.value + theader->buffer.length, tdata->buffer.length); + } else + tdata->buffer.value = (unsigned char *)stream->buffer.value + theader->buffer.length; + + assert(i <= iov_count + 2); + + major_status = kg_unseal_iov_token(&code, ctx, conf_state, qop_state, + tiov, i, toktype); + if (major_status == GSS_S_COMPLETE) + *data = *tdata; + else + kg_release_iov(tdata, 1); + +cleanup: + if (tiov != NULL) + free(tiov); + + *minor_status = code; + + return major_status; +} + +OM_uint32 +kg_unseal_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + krb5_gss_ctx_id_rec *ctx; + OM_uint32 code; + + ctx = (krb5_gss_ctx_id_rec *)context_handle; + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + if (kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM) != NULL) { + code = kg_unseal_stream_iov(minor_status, ctx, conf_state, qop_state, + iov, iov_count, toktype); + } else { + code = kg_unseal_iov_token(minor_status, ctx, conf_state, qop_state, + iov, iov_count, toktype); + } + + return code; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_unwrap_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major_status; + + major_status = kg_unseal_iov(minor_status, context_handle, + conf_state, qop_state, + iov, iov_count, KG_TOK_WRAP_MSG); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_verify_mic_iov(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major_status; + + major_status = kg_unseal_iov(minor_status, context_handle, + NULL, qop_state, + iov, iov_count, KG_TOK_MIC_MSG); + + return major_status; +} diff --git a/src/lib/gssapi/krb5/krb5_gss_glue.c b/src/lib/gssapi/krb5/krb5_gss_glue.c new file mode 100644 index 000000000000..b35a2152344e --- /dev/null +++ b/src/lib/gssapi/krb5/krb5_gss_glue.c @@ -0,0 +1,445 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Copyright (c) 2006-2008, Novell, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The copyright holder's name is not used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +gss_krb5_get_tkt_flags(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + krb5_flags *ticket_flags) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_GET_TKT_FLAGS_OID_LENGTH, + GSS_KRB5_GET_TKT_FLAGS_OID }; + OM_uint32 major_status; + gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; + + if (ticket_flags == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + major_status = gss_inquire_sec_context_by_oid(minor_status, + context_handle, + (gss_OID)&req_oid, + &data_set); + if (major_status != GSS_S_COMPLETE) + return major_status; + + if (data_set == GSS_C_NO_BUFFER_SET || + data_set->count != 1 || + data_set->elements[0].length != sizeof(*ticket_flags)) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + *ticket_flags = *((krb5_flags *)data_set->elements[0].value); + + gss_release_buffer_set(minor_status, &data_set); + + *minor_status = 0; + + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_copy_ccache(OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + krb5_ccache out_ccache) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_COPY_CCACHE_OID_LENGTH, + GSS_KRB5_COPY_CCACHE_OID }; + OM_uint32 major_status; + gss_buffer_desc req_buffer; + + if (out_ccache == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + req_buffer.value = out_ccache; + req_buffer.length = sizeof(out_ccache); + + major_status = gss_set_cred_option(minor_status, + &cred_handle, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_import_cred(OM_uint32 *minor_status, + krb5_ccache id, + krb5_principal keytab_principal, + krb5_keytab keytab, + gss_cred_id_t *cred) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_IMPORT_CRED_OID_LENGTH, + GSS_KRB5_IMPORT_CRED_OID }; + OM_uint32 major_status; + struct krb5_gss_import_cred_req req; + gss_buffer_desc req_buffer; + + if (cred == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *cred = GSS_C_NO_CREDENTIAL; + + req.id = id; + req.keytab_principal = keytab_principal; + req.keytab = keytab; + + req_buffer.value = &req; + req_buffer.length = sizeof(req); + + major_status = gss_set_cred_option(minor_status, + cred, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + OM_uint32 version, + void **kctx) +{ + unsigned char oid_buf[GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH + 6]; + gss_OID_desc req_oid; + OM_uint32 major_status, minor; + gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; + + if (kctx == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + *kctx = NULL; + + req_oid.elements = oid_buf; + req_oid.length = sizeof(oid_buf); + + major_status = generic_gss_oid_compose(minor_status, + GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID, + GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH, + (int)version, + &req_oid); + if (GSS_ERROR(major_status)) + return major_status; + + major_status = gss_inquire_sec_context_by_oid(minor_status, + *context_handle, + &req_oid, + &data_set); + if (GSS_ERROR(major_status)) + return major_status; + + if (data_set == GSS_C_NO_BUFFER_SET || + data_set->count != 1 || + data_set->elements[0].length != sizeof(void *)) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + *kctx = *((void **)data_set->elements[0].value); + + /* Clean up the context state (it is an error for + * someone to attempt to use this context again) + */ + (void)gss_delete_sec_context(minor_status, context_handle, NULL); + *context_handle = GSS_C_NO_CONTEXT; + + generic_gss_release_buffer_set(&minor, &data_set); + + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status, + gss_cred_id_t cred, + OM_uint32 num_ktypes, + krb5_enctype *ktypes) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID_LENGTH, + GSS_KRB5_SET_ALLOWABLE_ENCTYPES_OID }; + OM_uint32 major_status; + struct krb5_gss_set_allowable_enctypes_req req; + gss_buffer_desc req_buffer; + + req.num_ktypes = num_ktypes; + req.ktypes = ktypes; + + req_buffer.length = sizeof(req); + req_buffer.value = &req; + + major_status = gss_set_cred_option(minor_status, + &cred, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_ccache_name(OM_uint32 *minor_status, + const char *name, + const char **out_name) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_CCACHE_NAME_OID_LENGTH, + GSS_KRB5_CCACHE_NAME_OID }; + OM_uint32 major_status; + struct krb5_gss_ccache_name_req req; + gss_buffer_desc req_buffer; + + req.name = name; + req.out_name = out_name; + + req_buffer.length = sizeof(req); + req_buffer.value = &req; + + major_status = gssspi_mech_invoke(minor_status, + (gss_OID)gss_mech_krb5, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *kctx) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID_LENGTH, + GSS_KRB5_FREE_LUCID_SEC_CONTEXT_OID }; + OM_uint32 major_status; + gss_buffer_desc req_buffer; + + req_buffer.length = sizeof(kctx); + req_buffer.value = kctx; + + major_status = gssspi_mech_invoke(minor_status, + (gss_OID)gss_mech_krb5, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_register_acceptor_identity(const char *keytab) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID_LENGTH, + GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_OID }; + OM_uint32 major_status; + OM_uint32 minor_status; + gss_buffer_desc req_buffer; + + req_buffer.length = (keytab == NULL) ? 0 : strlen(keytab); + req_buffer.value = (char *)keytab; + + major_status = gssspi_mech_invoke(&minor_status, + (gss_OID)gss_mech_krb5, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +#ifndef _WIN32 +krb5_error_code +krb5_gss_use_kdc_context(void) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_USE_KDC_CONTEXT_OID_LENGTH, + GSS_KRB5_USE_KDC_CONTEXT_OID }; + OM_uint32 major_status; + OM_uint32 minor_status; + gss_buffer_desc req_buffer; + krb5_error_code ret; + + req_buffer.length = 0; + req_buffer.value = NULL; + + major_status = gssspi_mech_invoke(&minor_status, + (gss_OID)gss_mech_krb5, + (gss_OID)&req_oid, + &req_buffer); + + if (major_status != GSS_S_COMPLETE) { + if (minor_status != 0) + ret = (krb5_error_code)minor_status; + else + ret = KRB5KRB_ERR_GENERIC; + } else + ret = 0; + + return ret; +} +#endif + +/* + * This API should go away and be replaced with an accessor + * into a gss_name_t. + */ +OM_uint32 KRB5_CALLCONV +gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + int ad_type, + gss_buffer_t ad_data) +{ + gss_OID_desc req_oid; + unsigned char oid_buf[GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH + 6]; + OM_uint32 major_status; + gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; + + if (ad_data == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + req_oid.elements = oid_buf; + req_oid.length = sizeof(oid_buf); + + major_status = generic_gss_oid_compose(minor_status, + GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID, + GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH, + ad_type, + &req_oid); + if (GSS_ERROR(major_status)) + return major_status; + + major_status = gss_inquire_sec_context_by_oid(minor_status, + context_handle, + (gss_OID)&req_oid, + &data_set); + if (major_status != GSS_S_COMPLETE) { + return major_status; + } + + if (data_set == GSS_C_NO_BUFFER_SET || + data_set->count != 1) { + return GSS_S_FAILURE; + } + + ad_data->length = data_set->elements[0].length; + ad_data->value = data_set->elements[0].value; + + data_set->elements[0].length = 0; + data_set->elements[0].value = NULL; + + data_set->count = 0; + + gss_release_buffer_set(minor_status, &data_set); + + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +gss_krb5_set_cred_rcache(OM_uint32 *minor_status, + gss_cred_id_t cred, + krb5_rcache rcache) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_SET_CRED_RCACHE_OID_LENGTH, + GSS_KRB5_SET_CRED_RCACHE_OID }; + OM_uint32 major_status; + gss_buffer_desc req_buffer; + + req_buffer.length = sizeof(rcache); + req_buffer.value = rcache; + + major_status = gss_set_cred_option(minor_status, + &cred, + (gss_OID)&req_oid, + &req_buffer); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t context_handle, + krb5_timestamp *authtime) +{ + static const gss_OID_desc req_oid = { + GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH, + GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID }; + OM_uint32 major_status; + gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; + + if (authtime == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + major_status = gss_inquire_sec_context_by_oid(minor_status, + context_handle, + (gss_OID)&req_oid, + &data_set); + if (major_status != GSS_S_COMPLETE) + return major_status; + + if (data_set == GSS_C_NO_BUFFER_SET || + data_set->count != 1 || + data_set->elements[0].length != sizeof(*authtime)) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + *authtime = *((krb5_timestamp *)data_set->elements[0].value); + + gss_release_buffer_set(minor_status, &data_set); + + *minor_status = 0; + + return GSS_S_COMPLETE; +} diff --git a/src/lib/gssapi/krb5/lucid_context.c b/src/lib/gssapi/krb5/lucid_context.c new file mode 100644 index 000000000000..a894f0e7355c --- /dev/null +++ b/src/lib/gssapi/krb5/lucid_context.c @@ -0,0 +1,292 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/lucid_context.c */ +/* + * Copyright 2004, 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* Externalize a "lucid" security context from a krb5_gss_ctx_id_rec + * structure. */ +#include "gssapiP_krb5.h" +#include "gssapi_krb5.h" + +/* + * Local routine prototypes + */ +static void +free_external_lucid_ctx_v1( + gss_krb5_lucid_context_v1_t *ctx); + +static void +free_lucid_key_data( + gss_krb5_lucid_key_t *key); + +static krb5_error_code +copy_keyblock_to_lucid_key( + krb5_keyblock *k5key, + gss_krb5_lucid_key_t *lkey); + +static krb5_error_code +make_external_lucid_ctx_v1( + krb5_gss_ctx_id_rec * gctx, + int version, + void **out_ptr); + + +/* + * Exported routines + */ + +OM_uint32 +gss_krb5int_export_lucid_sec_context( + OM_uint32 *minor_status, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + krb5_error_code kret = 0; + OM_uint32 retval; + krb5_gss_ctx_id_t ctx = (krb5_gss_ctx_id_t)context_handle; + void *lctx = NULL; + int version = 0; + gss_buffer_desc rep; + + /* Assume failure */ + retval = GSS_S_FAILURE; + *minor_status = 0; + *data_set = GSS_C_NO_BUFFER_SET; + + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + retval = generic_gss_oid_decompose(minor_status, + GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID, + GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH, + desired_object, + &version); + if (GSS_ERROR(retval)) + return retval; + + /* Externalize a structure of the right version */ + switch (version) { + case 1: + kret = make_external_lucid_ctx_v1((krb5_pointer)ctx, + version, &lctx); + break; + default: + kret = (OM_uint32) KG_LUCID_VERSION; + break; + } + + if (kret) + goto error_out; + + rep.value = &lctx; + rep.length = sizeof(lctx); + + retval = generic_gss_add_buffer_set_member(minor_status, &rep, data_set); + if (GSS_ERROR(retval)) + goto error_out; + +error_out: + if (*minor_status == 0) + *minor_status = (OM_uint32) kret; + return(retval); +} + +/* + * Frees the storage associated with an + * exported lucid context structure. + */ +OM_uint32 +gss_krb5int_free_lucid_sec_context( + OM_uint32 *minor_status, + const gss_OID desired_mech, + const gss_OID desired_object, + gss_buffer_t value) +{ + OM_uint32 retval; + krb5_error_code kret = 0; + int version; + void *kctx; + + /* Assume failure */ + retval = GSS_S_FAILURE; + *minor_status = 0; + + kctx = value->value; + if (!kctx) { + kret = EINVAL; + goto error_out; + } + + /* Determine version and call correct free routine */ + version = ((gss_krb5_lucid_context_version_t *)kctx)->version; + switch (version) { + case 1: + free_external_lucid_ctx_v1((gss_krb5_lucid_context_v1_t*) kctx); + break; + default: + kret = EINVAL; + break; + } + + if (kret) + goto error_out; + + /* Success! */ + *minor_status = 0; + retval = GSS_S_COMPLETE; + + return (retval); + +error_out: + if (*minor_status == 0) + *minor_status = (OM_uint32) kret; + return(retval); +} + +/* + * Local routines + */ + +static krb5_error_code +make_external_lucid_ctx_v1( + krb5_gss_ctx_id_rec * gctx, + int version, + void **out_ptr) +{ + gss_krb5_lucid_context_v1_t *lctx = NULL; + unsigned int bufsize = sizeof(gss_krb5_lucid_context_v1_t); + krb5_error_code retval; + + /* Allocate the structure */ + if ((lctx = xmalloc(bufsize)) == NULL) { + retval = ENOMEM; + goto error_out; + } + + memset(lctx, 0, bufsize); + + lctx->version = 1; + lctx->initiate = gctx->initiate ? 1 : 0; + lctx->endtime = gctx->krb_times.endtime; + lctx->send_seq = gctx->seq_send; + lctx->recv_seq = gctx->seq_recv; + lctx->protocol = gctx->proto; + /* gctx->proto == 0 ==> rfc1964-style key information + gctx->proto == 1 ==> cfx-style (draft-ietf-krb-wg-gssapi-cfx-07) keys */ + if (gctx->proto == 0) { + lctx->rfc1964_kd.sign_alg = gctx->signalg; + lctx->rfc1964_kd.seal_alg = gctx->sealalg; + /* Copy key */ + if ((retval = copy_keyblock_to_lucid_key(&gctx->seq->keyblock, + &lctx->rfc1964_kd.ctx_key))) + goto error_out; + } + else if (gctx->proto == 1) { + /* Copy keys */ + /* (subkey is always present, either a copy of the kerberos + session key or a subkey) */ + if ((retval = copy_keyblock_to_lucid_key(&gctx->subkey->keyblock, + &lctx->cfx_kd.ctx_key))) + goto error_out; + if (gctx->have_acceptor_subkey) { + if ((retval = copy_keyblock_to_lucid_key(&gctx->acceptor_subkey->keyblock, + &lctx->cfx_kd.acceptor_subkey))) + goto error_out; + lctx->cfx_kd.have_acceptor_subkey = 1; + } + } + else { + xfree(lctx); + return EINVAL; /* XXX better error code? */ + } + + /* Success! */ + *out_ptr = lctx; + return 0; + +error_out: + if (lctx) { + free_external_lucid_ctx_v1(lctx); + } + return retval; + +} + +/* Copy the contents of a krb5_keyblock to a gss_krb5_lucid_key_t structure */ +static krb5_error_code +copy_keyblock_to_lucid_key( + krb5_keyblock *k5key, + gss_krb5_lucid_key_t *lkey) +{ + if (!k5key || !k5key->contents || k5key->length == 0) + return EINVAL; + + memset(lkey, 0, sizeof(gss_krb5_lucid_key_t)); + + /* Allocate storage for the key data */ + if ((lkey->data = xmalloc(k5key->length)) == NULL) { + return ENOMEM; + } + memcpy(lkey->data, k5key->contents, k5key->length); + lkey->length = k5key->length; + lkey->type = k5key->enctype; + + return 0; +} + + +/* Free any storage associated with a gss_krb5_lucid_key_t structure */ +static void +free_lucid_key_data( + gss_krb5_lucid_key_t *key) +{ + if (key) { + if (key->data && key->length) { + zap(key->data, key->length); + xfree(key->data); + zap(key, sizeof(gss_krb5_lucid_key_t)); + } + } +} +/* Free any storage associated with a gss_krb5_lucid_context_v1 structure */ +static void +free_external_lucid_ctx_v1( + gss_krb5_lucid_context_v1_t *ctx) +{ + if (ctx) { + if (ctx->protocol == 0) { + free_lucid_key_data(&ctx->rfc1964_kd.ctx_key); + } + if (ctx->protocol == 1) { + free_lucid_key_data(&ctx->cfx_kd.ctx_key); + if (ctx->cfx_kd.have_acceptor_subkey) + free_lucid_key_data(&ctx->cfx_kd.acceptor_subkey); + } + xfree(ctx); + ctx = NULL; + } +} diff --git a/src/lib/gssapi/krb5/naming_exts.c b/src/lib/gssapi/krb5/naming_exts.c new file mode 100644 index 000000000000..6062a6dd8052 --- /dev/null +++ b/src/lib/gssapi/krb5/naming_exts.c @@ -0,0 +1,684 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/naming_exts.c */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +krb5_error_code +kg_init_name(krb5_context context, krb5_principal principal, + char *service, char *host, krb5_authdata_context ad_context, + krb5_flags flags, krb5_gss_name_t *ret_name) +{ + krb5_error_code code; + krb5_gss_name_t name; + + *ret_name = NULL; + + assert(principal != NULL); + + if (principal == NULL) + return EINVAL; + + name = xmalloc(sizeof(krb5_gss_name_rec)); + if (name == NULL) + return ENOMEM; + + memset(name, 0, sizeof(krb5_gss_name_rec)); + + code = k5_mutex_init(&name->lock); + if (code != 0) + goto cleanup; + + if ((flags & KG_INIT_NAME_NO_COPY) == 0) { + code = krb5_copy_principal(context, principal, &name->princ); + if (code != 0) + goto cleanup; + + if (ad_context != NULL) { + code = krb5_authdata_context_copy(context, + ad_context, + &name->ad_context); + if (code != 0) + goto cleanup; + } + + code = ENOMEM; + if (service != NULL) { + name->service = strdup(service); + if (name->service == NULL) + goto cleanup; + } + if (host != NULL) { + name->host = strdup(host); + if (name->host == NULL) + goto cleanup; + } + code = 0; + } else { + name->princ = principal; + name->service = service; + name->host = host; + name->ad_context = ad_context; + } + + *ret_name = name; + +cleanup: + if (code != 0) + kg_release_name(context, &name); + + return code; +} + +krb5_error_code +kg_release_name(krb5_context context, + krb5_gss_name_t *name) +{ + if (*name != NULL) { + krb5_free_principal(context, (*name)->princ); + free((*name)->service); + free((*name)->host); + krb5_authdata_context_free(context, (*name)->ad_context); + k5_mutex_destroy(&(*name)->lock); + free(*name); + *name = NULL; + } + + return 0; +} + +krb5_error_code +kg_duplicate_name(krb5_context context, + const krb5_gss_name_t src, + krb5_gss_name_t *dst) +{ + krb5_error_code code; + + k5_mutex_lock(&src->lock); + code = kg_init_name(context, src->princ, src->service, src->host, + src->ad_context, 0, dst); + k5_mutex_unlock(&src->lock); + return code; +} + + +krb5_boolean +kg_compare_name(krb5_context context, + krb5_gss_name_t name1, + krb5_gss_name_t name2) +{ + return krb5_principal_compare(context, name1->princ, name2->princ); +} + +/* Determine the principal to use for an acceptor name, which is different from + * name->princ for host-based names. */ +krb5_boolean +kg_acceptor_princ(krb5_context context, krb5_gss_name_t name, + krb5_principal *princ_out) +{ + krb5_error_code code; + const char *host; + char *tmp = NULL; + + *princ_out = NULL; + if (name == NULL) + return 0; + + /* If it's not a host-based name, just copy name->princ. */ + if (name->service == NULL) + return krb5_copy_principal(context, name->princ, princ_out); + + if (name->host != NULL && name->princ->length == 2) { + /* If a host was given, we have to use the canonicalized form of it (as + * given by krb5_sname_to_principal) for backward compatibility. */ + const krb5_data *d = &name->princ->data[1]; + tmp = k5memdup0(d->data, d->length, &code); + if (tmp == NULL) + return ENOMEM; + host = tmp; + } else /* No host was given; use an empty string. */ + host = ""; + + code = krb5_build_principal(context, princ_out, 0, "", name->service, host, + (char *)NULL); + if (*princ_out != NULL) + (*princ_out)->type = KRB5_NT_SRV_HST; + free(tmp); + return code; +} + +static OM_uint32 +kg_map_name_error(OM_uint32 *minor_status, krb5_error_code code) +{ + OM_uint32 major_status; + + switch (code) { + case 0: + major_status = GSS_S_COMPLETE; + break; + case ENOENT: + case EPERM: + major_status = GSS_S_UNAVAILABLE; + break; + default: + major_status = GSS_S_FAILURE; + break; + } + + *minor_status = code; + + return major_status; +} + +/* Owns data on success */ +static krb5_error_code +data_list_to_buffer_set(krb5_context context, + krb5_data *data, + gss_buffer_set_t *buffer_set) +{ + gss_buffer_set_t set = GSS_C_NO_BUFFER_SET; + OM_uint32 minor_status; + int i; + krb5_error_code code = 0; + + if (data == NULL) + goto cleanup; + + if (buffer_set == NULL) + goto cleanup; + + if (GSS_ERROR(gss_create_empty_buffer_set(&minor_status, + &set))) { + assert(minor_status != 0); + code = minor_status; + goto cleanup; + } + + for (i = 0; data[i].data != NULL; i++) + ; + + set->count = i; + set->elements = gssalloc_calloc(i, sizeof(gss_buffer_desc)); + if (set->elements == NULL) { + gss_release_buffer_set(&minor_status, &set); + code = ENOMEM; + goto cleanup; + } + + /* + * Copy last element first so data remains properly + * NULL-terminated in case of allocation failure + * in data_to_gss() on windows. + */ + for (i = set->count-1; i >= 0; i--) { + if (data_to_gss(&data[i], &set->elements[i])) { + gss_release_buffer_set(&minor_status, &set); + code = ENOMEM; + goto cleanup; + } + } +cleanup: + krb5int_free_data_list(context, data); + + if (buffer_set != NULL) + *buffer_set = set; + + return code; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_inquire_name(OM_uint32 *minor_status, + gss_name_t name, + int *name_is_MN, + gss_OID *MN_mech, + gss_buffer_set_t *attrs) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + krb5_data *kattrs = NULL; + + if (minor_status != NULL) + *minor_status = 0; + + if (attrs != NULL) + *attrs = GSS_C_NO_BUFFER_SET; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + + k5_mutex_lock(&kname->lock); + + if (kname->ad_context == NULL) { + code = krb5_authdata_context_init(context, &kname->ad_context); + if (code != 0) + goto cleanup; + } + + code = krb5_authdata_get_attribute_types(context, + kname->ad_context, + &kattrs); + if (code != 0) + goto cleanup; + + code = data_list_to_buffer_set(context, kattrs, attrs); + kattrs = NULL; + if (code != 0) + goto cleanup; + +cleanup: + k5_mutex_unlock(&kname->lock); + krb5int_free_data_list(context, kattrs); + + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_get_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + krb5_data kattr; + krb5_boolean kauthenticated; + krb5_boolean kcomplete; + krb5_data kvalue; + krb5_data kdisplay_value; + + if (minor_status != NULL) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + k5_mutex_lock(&kname->lock); + + if (kname->ad_context == NULL) { + code = krb5_authdata_context_init(context, &kname->ad_context); + if (code != 0) { + *minor_status = code; + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + } + + kattr.data = (char *)attr->value; + kattr.length = attr->length; + + kauthenticated = FALSE; + kcomplete = FALSE; + + code = krb5_authdata_get_attribute(context, + kname->ad_context, + &kattr, + &kauthenticated, + &kcomplete, + value ? &kvalue : NULL, + display_value ? &kdisplay_value : NULL, + more); + if (code == 0) { + if (value != NULL) + code = data_to_gss(&kvalue, value); + + if (authenticated != NULL) + *authenticated = kauthenticated; + if (complete != NULL) + *complete = kcomplete; + + if (display_value != NULL) { + if (code == 0) + code = data_to_gss(&kdisplay_value, display_value); + else + free(kdisplay_value.data); + } + } + + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_set_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + int complete, + gss_buffer_t attr, + gss_buffer_t value) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + krb5_data kattr; + krb5_data kvalue; + + if (minor_status != NULL) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + k5_mutex_lock(&kname->lock); + + if (kname->ad_context == NULL) { + code = krb5_authdata_context_init(context, &kname->ad_context); + if (code != 0) { + *minor_status = code; + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + } + + kattr.data = (char *)attr->value; + kattr.length = attr->length; + + kvalue.data = (char *)value->value; + kvalue.length = value->length; + + code = krb5_authdata_set_attribute(context, + kname->ad_context, + complete, + &kattr, + &kvalue); + + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_delete_name_attribute(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t attr) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + krb5_data kattr; + + if (minor_status != NULL) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + k5_mutex_lock(&kname->lock); + + if (kname->ad_context == NULL) { + code = krb5_authdata_context_init(context, &kname->ad_context); + if (code != 0) { + *minor_status = code; + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + } + + kattr.data = (char *)attr->value; + kattr.length = attr->length; + + code = krb5_authdata_delete_attribute(context, + kname->ad_context, + &kattr); + + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_map_name_to_any(OM_uint32 *minor_status, + gss_name_t name, + int authenticated, + gss_buffer_t type_id, + gss_any_t *output) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + char *kmodule; + + if (minor_status != NULL) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + k5_mutex_lock(&kname->lock); + + if (kname->ad_context == NULL) { + code = krb5_authdata_context_init(context, &kname->ad_context); + if (code != 0) { + *minor_status = code; + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + } + + kmodule = (char *)type_id->value; + if (kmodule[type_id->length] != '\0') { + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + + code = krb5_authdata_export_internal(context, + kname->ad_context, + authenticated, + kmodule, + (void **)output); + + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_release_any_name_mapping(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t type_id, + gss_any_t *input) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + char *kmodule; + + if (minor_status != NULL) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + k5_mutex_lock(&kname->lock); + + if (kname->ad_context == NULL) { + code = krb5_authdata_context_init(context, &kname->ad_context); + if (code != 0) { + *minor_status = code; + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + } + + kmodule = (char *)type_id->value; + if (kmodule[type_id->length] != '\0') { + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + return GSS_S_UNAVAILABLE; + } + + code = krb5_authdata_free_internal(context, + kname->ad_context, + kmodule, + *input); + if (code == 0) + *input = NULL; + + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); + +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_export_name_composite(OM_uint32 *minor_status, + gss_name_t name, + gss_buffer_t exp_composite_name) +{ + krb5_context context; + krb5_error_code code; + krb5_gss_name_t kname; + krb5_data *attrs = NULL; + char *princstr = NULL; + unsigned char *cp; + size_t princlen; + + if (minor_status != NULL) + *minor_status = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kname = (krb5_gss_name_t)name; + k5_mutex_lock(&kname->lock); + + code = krb5_unparse_name(context, kname->princ, &princstr); + if (code != 0) + goto cleanup; + + princlen = strlen(princstr); + + if (kname->ad_context != NULL) { + code = krb5_authdata_export_attributes(context, + kname->ad_context, + AD_USAGE_MASK, + &attrs); + if (code != 0) + goto cleanup; + } + + /* 04 02 OID Name AuthData */ + + exp_composite_name->length = 10 + gss_mech_krb5->length + princlen; + exp_composite_name->length += 4; /* length of encoded attributes */ + if (attrs != NULL) + exp_composite_name->length += attrs->length; + exp_composite_name->value = malloc(exp_composite_name->length); + if (exp_composite_name->value == NULL) { + code = ENOMEM; + goto cleanup; + } + + cp = exp_composite_name->value; + + /* Note: we assume the OID will be less than 128 bytes... */ + *cp++ = 0x04; + *cp++ = 0x02; + + store_16_be(gss_mech_krb5->length + 2, cp); + cp += 2; + *cp++ = 0x06; + *cp++ = (gss_mech_krb5->length) & 0xFF; + memcpy(cp, gss_mech_krb5->elements, gss_mech_krb5->length); + cp += gss_mech_krb5->length; + + store_32_be(princlen, cp); + cp += 4; + memcpy(cp, princstr, princlen); + cp += princlen; + + store_32_be(attrs != NULL ? attrs->length : 0, cp); + cp += 4; + + if (attrs != NULL) { + memcpy(cp, attrs->data, attrs->length); + cp += attrs->length; + } + +cleanup: + krb5_free_unparsed_name(context, princstr); + krb5_free_data(context, attrs); + k5_mutex_unlock(&kname->lock); + krb5_free_context(context); + + return kg_map_name_error(minor_status, code); +} + +#if 0 +OM_uint32 +krb5_gss_display_name_ext(OM_uint32 *minor_status, + gss_name_t name, + gss_OID display_as_name_type, + gss_buffer_t display_name) +{ +} +#endif diff --git a/src/lib/gssapi/krb5/prf.c b/src/lib/gssapi/krb5/prf.c new file mode 100644 index 000000000000..e897074fc193 --- /dev/null +++ b/src/lib/gssapi/krb5/prf.c @@ -0,0 +1,139 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/prf.c */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +#ifndef MIN /* Usually found in <sys/param.h>. */ +#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#endif + +OM_uint32 KRB5_CALLCONV +krb5_gss_pseudo_random(OM_uint32 *minor_status, + gss_ctx_id_t context, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out) +{ + krb5_error_code code; + krb5_key key = NULL; + krb5_gss_ctx_id_t ctx; + int i; + OM_uint32 minor; + size_t prflen; + krb5_data t, ns; + unsigned char *p; + + prf_out->length = 0; + prf_out->value = NULL; + + t.length = 0; + t.data = NULL; + + ns.length = 0; + ns.data = NULL; + + ctx = (krb5_gss_ctx_id_t)context; + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + switch (prf_key) { + case GSS_C_PRF_KEY_FULL: + if (ctx->have_acceptor_subkey) { + key = ctx->acceptor_subkey; + break; + } + /* fallthrough */ + case GSS_C_PRF_KEY_PARTIAL: + key = ctx->subkey; + break; + default: + code = EINVAL; + goto cleanup; + } + + if (key == NULL) { + code = EINVAL; + goto cleanup; + } + + if (desired_output_len == 0) + return GSS_S_COMPLETE; + + prf_out->value = k5alloc(desired_output_len, &code); + if (prf_out->value == NULL) { + code = KG_INPUT_TOO_LONG; + goto cleanup; + } + prf_out->length = desired_output_len; + + code = krb5_c_prf_length(ctx->k5_context, + krb5_k_key_enctype(ctx->k5_context, key), + &prflen); + if (code != 0) + goto cleanup; + + ns.length = 4 + prf_in->length; + ns.data = k5alloc(ns.length, &code); + if (ns.data == NULL) { + code = KG_INPUT_TOO_LONG; + goto cleanup; + } + + t.length = prflen; + t.data = k5alloc(t.length, &code); + if (t.data == NULL) + goto cleanup; + + memcpy(ns.data + 4, prf_in->value, prf_in->length); + i = 0; + p = (unsigned char *)prf_out->value; + while (desired_output_len > 0) { + store_32_be(i, ns.data); + + code = krb5_k_prf(ctx->k5_context, key, &ns, &t); + if (code != 0) + goto cleanup; + + memcpy(p, t.data, MIN(t.length, desired_output_len)); + + p += t.length; + desired_output_len -= t.length; + i++; + } + +cleanup: + if (code != 0) + gss_release_buffer(&minor, prf_out); + krb5_free_data_contents(ctx->k5_context, &ns); + krb5_free_data_contents(ctx->k5_context, &t); + + *minor_status = (OM_uint32)code; + return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} diff --git a/src/lib/gssapi/krb5/process_context_token.c b/src/lib/gssapi/krb5/process_context_token.c new file mode 100644 index 000000000000..a672f48c859b --- /dev/null +++ b/src/lib/gssapi/krb5/process_context_token.c @@ -0,0 +1,66 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +/* + * $Id$ + */ + +OM_uint32 KRB5_CALLCONV +krb5_gss_process_context_token(minor_status, context_handle, + token_buffer) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t token_buffer; +{ + krb5_gss_ctx_id_rec *ctx; + OM_uint32 majerr; + + ctx = (krb5_gss_ctx_id_t) context_handle; + + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + /* We only support context deletion tokens for now, and RFC 4121 does not + * define a context deletion token. */ + if (ctx->proto) { + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); + } + + /* "unseal" the token */ + + if (GSS_ERROR(majerr = kg_unseal(minor_status, context_handle, + token_buffer, + GSS_C_NO_BUFFER, NULL, NULL, + KG_TOK_DEL_CTX))) + return(majerr); + + /* Mark the context as terminated, but do not delete it (as that would + * leave the caller with a dangling context handle). */ + ctx->terminated = 1; + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/rel_cred.c b/src/lib/gssapi/krb5/rel_cred.c new file mode 100644 index 000000000000..8db745086009 --- /dev/null +++ b/src/lib/gssapi/krb5/rel_cred.c @@ -0,0 +1,101 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_release_cred(minor_status, cred_handle) + OM_uint32 *minor_status; + gss_cred_id_t *cred_handle; +{ + krb5_context context; + krb5_gss_cred_id_t cred; + krb5_error_code code1, code2, code3; + + code1 = krb5_gss_init_context(&context); + if (code1) { + *minor_status = code1; + return GSS_S_FAILURE; + } + + if (*cred_handle == GSS_C_NO_CREDENTIAL) { + *minor_status = 0; + krb5_free_context(context); + return(GSS_S_COMPLETE); + } + + cred = (krb5_gss_cred_id_t)*cred_handle; + + k5_mutex_destroy(&cred->lock); + /* ignore error destroying mutex */ + + if (cred->ccache) { + if (cred->destroy_ccache) + code1 = krb5_cc_destroy(context, cred->ccache); + else + code1 = krb5_cc_close(context, cred->ccache); + } else + code1 = 0; + + if (cred->client_keytab) + krb5_kt_close(context, cred->client_keytab); + +#ifndef LEAN_CLIENT + if (cred->keytab) + code2 = krb5_kt_close(context, cred->keytab); + else +#endif /* LEAN_CLIENT */ + code2 = 0; + + if (cred->rcache) + code3 = krb5_rc_close(context, cred->rcache); + else + code3 = 0; + if (cred->name) + kg_release_name(context, &cred->name); + + krb5_free_principal(context, cred->impersonator); + + if (cred->req_enctypes) + free(cred->req_enctypes); + + if (cred->password != NULL) + zapfree(cred->password, strlen(cred->password)); + + xfree(cred); + + *cred_handle = NULL; + + *minor_status = 0; + if (code1) + *minor_status = code1; + if (code2) + *minor_status = code2; + if (code3) + *minor_status = code3; + + if (*minor_status) + save_error_info(*minor_status, context); + krb5_free_context(context); + return(*minor_status?GSS_S_FAILURE:GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/rel_name.c b/src/lib/gssapi/krb5/rel_name.c new file mode 100644 index 000000000000..3dabe32f3342 --- /dev/null +++ b/src/lib/gssapi/krb5/rel_name.c @@ -0,0 +1,47 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 KRB5_CALLCONV +krb5_gss_release_name(minor_status, input_name) + OM_uint32 *minor_status; + gss_name_t *input_name; +{ + krb5_context context; + krb5_error_code code; + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + kg_release_name(context, (krb5_gss_name_t *)input_name); + krb5_free_context(context); + + *input_name = (gss_name_t) NULL; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/krb5/rel_oid.c b/src/lib/gssapi/krb5/rel_oid.c new file mode 100644 index 000000000000..739efe4680b7 --- /dev/null +++ b/src/lib/gssapi/krb5/rel_oid.c @@ -0,0 +1,78 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/rel_oid.c - Release an OID */ +/* + * Copyright 1995, 2007 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "gssapiP_krb5.h" + +OM_uint32 +krb5_gss_release_oid(minor_status, oid) + OM_uint32 *minor_status; + gss_OID *oid; +{ + /* + * The V2 API says the following! + * + * gss_release_oid[()] will recognize any of the GSSAPI's own OID values, + * and will silently ignore attempts to free these OIDs; for other OIDs + * it will call the C free() routine for both the OID data and the + * descriptor. This allows applications to freely mix their own heap- + * allocated OID values with OIDs returned by GSS-API. + */ + if (krb5_gss_internal_release_oid(minor_status, oid) != GSS_S_COMPLETE) { + /* Pawn it off on the generic routine */ + return(generic_gss_release_oid(minor_status, oid)); + } + else { + *oid = GSS_C_NO_OID; + *minor_status = 0; + return(GSS_S_COMPLETE); + } +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_internal_release_oid(minor_status, oid) + OM_uint32 *minor_status; + gss_OID *oid; +{ + /* + * This function only knows how to release internal OIDs. It will + * return GSS_S_CONTINUE_NEEDED for any OIDs it does not recognize. + */ + + *minor_status = 0; + if ((*oid != gss_mech_krb5) && + (*oid != gss_mech_krb5_old) && + (*oid != gss_mech_krb5_wrong) && + (*oid != gss_mech_iakerb) && + (*oid != gss_nt_krb5_name) && + (*oid != gss_nt_krb5_principal)) { + /* We don't know about this OID */ + return(GSS_S_CONTINUE_NEEDED); + } + else { + *oid = GSS_C_NO_OID; + return(GSS_S_COMPLETE); + } +} diff --git a/src/lib/gssapi/krb5/s4u_gss_glue.c b/src/lib/gssapi/krb5/s4u_gss_glue.c new file mode 100644 index 000000000000..ff1c310bce5e --- /dev/null +++ b/src/lib/gssapi/krb5/s4u_gss_glue.c @@ -0,0 +1,308 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +#include "k5-int.h" +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#include <assert.h> + +static int +kg_is_initiator_cred(krb5_gss_cred_id_t cred) +{ + return (cred->usage == GSS_C_INITIATE || cred->usage == GSS_C_BOTH) && + (cred->ccache != NULL); +} + +static OM_uint32 +kg_impersonate_name(OM_uint32 *minor_status, + const krb5_gss_cred_id_t impersonator_cred, + const krb5_gss_name_t user, + OM_uint32 time_req, + krb5_gss_cred_id_t *output_cred, + OM_uint32 *time_rec, + krb5_context context) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_creds in_creds, *out_creds = NULL; + + *output_cred = NULL; + memset(&in_creds, 0, sizeof(in_creds)); + + in_creds.client = user->princ; + in_creds.server = impersonator_cred->name->princ; + + if (impersonator_cred->req_enctypes != NULL) + in_creds.keyblock.enctype = impersonator_cred->req_enctypes[0]; + + k5_mutex_lock(&user->lock); + + if (user->ad_context != NULL) { + code = krb5_authdata_export_authdata(context, + user->ad_context, + AD_USAGE_TGS_REQ, + &in_creds.authdata); + if (code != 0) { + k5_mutex_unlock(&user->lock); + *minor_status = code; + return GSS_S_FAILURE; + } + } + + k5_mutex_unlock(&user->lock); + + code = krb5_get_credentials_for_user(context, + KRB5_GC_CANONICALIZE | KRB5_GC_NO_STORE, + impersonator_cred->ccache, + &in_creds, + NULL, &out_creds); + if (code != 0) { + krb5_free_authdata(context, in_creds.authdata); + *minor_status = code; + return GSS_S_FAILURE; + } + + major_status = kg_compose_deleg_cred(minor_status, + impersonator_cred, + out_creds, + time_req, + output_cred, + time_rec, + context); + + krb5_free_authdata(context, in_creds.authdata); + krb5_free_creds(context, out_creds); + + return major_status; +} + +/* The mechglue always passes null desired_mechs and actual_mechs. */ +OM_uint32 KRB5_CALLCONV +krb5_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_name_t desired_name, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + gss_cred_usage_t cred_usage, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_gss_cred_id_t imp_cred = (krb5_gss_cred_id_t)impersonator_cred_handle; + krb5_gss_cred_id_t cred; + krb5_context context; + + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (desired_name == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (output_cred_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (cred_usage != GSS_C_INITIATE) { + *minor_status = (OM_uint32)G_BAD_USAGE; + return GSS_S_FAILURE; + } + + if (imp_cred->usage != GSS_C_INITIATE && imp_cred->usage != GSS_C_BOTH) { + *minor_status = 0; + return GSS_S_NO_CRED; + } + + *output_cred_handle = GSS_C_NO_CREDENTIAL; + if (time_rec != NULL) + *time_rec = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + major_status = kg_cred_resolve(minor_status, context, + impersonator_cred_handle, NULL); + if (GSS_ERROR(major_status)) { + krb5_free_context(context); + return major_status; + } + + major_status = kg_impersonate_name(minor_status, + imp_cred, + (krb5_gss_name_t)desired_name, + time_req, + &cred, + time_rec, + context); + + if (!GSS_ERROR(major_status)) + *output_cred_handle = (gss_cred_id_t)cred; + + k5_mutex_unlock(&imp_cred->lock); + krb5_free_context(context); + + return major_status; + +} + +/* + * Set up cred to be an S4U2Proxy credential by copying in the impersonator's + * creds, setting a cache config variable with the impersonator principal name, + * and saving the impersonator principal name in the cred structure. + */ +static krb5_error_code +make_proxy_cred(krb5_context context, krb5_gss_cred_id_t cred, + krb5_gss_cred_id_t impersonator_cred) +{ + krb5_error_code code; + krb5_data data; + char *str; + + code = krb5_cc_copy_creds(context, impersonator_cred->ccache, + cred->ccache); + if (code) + return code; + + code = krb5_unparse_name(context, impersonator_cred->name->princ, &str); + if (code) + return code; + + data = string2data(str); + code = krb5_cc_set_config(context, cred->ccache, NULL, + KRB5_CC_CONF_PROXY_IMPERSONATOR, &data); + krb5_free_unparsed_name(context, str); + if (code) + return code; + + return krb5_copy_principal(context, impersonator_cred->name->princ, + &cred->impersonator); +} + +OM_uint32 +kg_compose_deleg_cred(OM_uint32 *minor_status, + krb5_gss_cred_id_t impersonator_cred, + krb5_creds *subject_creds, + OM_uint32 time_req, + krb5_gss_cred_id_t *output_cred, + OM_uint32 *time_rec, + krb5_context context) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_gss_cred_id_t cred = NULL; + + *output_cred = NULL; + k5_mutex_assert_locked(&impersonator_cred->lock); + + if (!kg_is_initiator_cred(impersonator_cred) || + impersonator_cred->name == NULL || + impersonator_cred->impersonator != NULL) { + code = G_BAD_USAGE; + goto cleanup; + } + + assert(impersonator_cred->name->princ != NULL); + + assert(subject_creds != NULL); + assert(subject_creds->client != NULL); + + cred = xmalloc(sizeof(*cred)); + if (cred == NULL) { + code = ENOMEM; + goto cleanup; + } + memset(cred, 0, sizeof(*cred)); + + code = k5_mutex_init(&cred->lock); + if (code != 0) + goto cleanup; + + cred->usage = GSS_C_INITIATE; + + cred->expire = subject_creds->times.endtime; + + code = kg_init_name(context, subject_creds->client, NULL, NULL, NULL, 0, + &cred->name); + if (code != 0) + goto cleanup; + + code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache); + if (code != 0) + goto cleanup; + cred->destroy_ccache = 1; + + code = krb5_cc_initialize(context, cred->ccache, subject_creds->client); + if (code != 0) + goto cleanup; + + /* + * Only return a "proxy" credential for use with constrained + * delegation if the subject credentials are forwardable. + * Submitting non-forwardable credentials to the KDC for use + * with constrained delegation will only return an error. + */ + if (subject_creds->ticket_flags & TKT_FLG_FORWARDABLE) { + code = make_proxy_cred(context, cred, impersonator_cred); + if (code != 0) + goto cleanup; + } + + code = krb5_cc_store_cred(context, cred->ccache, subject_creds); + if (code != 0) + goto cleanup; + + if (time_rec != NULL) { + krb5_timestamp now; + + code = krb5_timeofday(context, &now); + if (code != 0) + goto cleanup; + + *time_rec = cred->expire - now; + } + + major_status = GSS_S_COMPLETE; + *minor_status = 0; + *output_cred = cred; + +cleanup: + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + } + + if (GSS_ERROR(major_status) && cred != NULL) { + k5_mutex_destroy(&cred->lock); + krb5_cc_destroy(context, cred->ccache); + kg_release_name(context, &cred->name); + xfree(cred); + } + + return major_status; +} diff --git a/src/lib/gssapi/krb5/ser_sctx.c b/src/lib/gssapi/krb5/ser_sctx.c new file mode 100644 index 000000000000..79c4c710e6d4 --- /dev/null +++ b/src/lib/gssapi/krb5/ser_sctx.c @@ -0,0 +1,798 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/ser_sctx.c - [De]serialization of security context */ +/* + * Copyright 1995, 2004, 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +/* + * This module contains routines to [de]serialize + * krb5_gss_enc_desc and krb5_gss_ctx_id_t. + * XXX This whole serialization abstraction is unnecessary in a + * non-messaging environment, which krb5 is. Someday, this should + * all get redone without the extra level of indirection. I've done + * some of this work here, since adding new serializers is an internal + * krb5 interface, and I won't use those. There is some more + * deobfuscation (no longer anonymizing pointers, mostly) which could + * still be done. --marc + */ + +static krb5_error_code +kg_oid_externalize(kcontext, arg, buffer, lenremain) + krb5_context kcontext; + krb5_pointer arg; + krb5_octet **buffer; + size_t *lenremain; +{ + gss_OID oid = (gss_OID) arg; + krb5_error_code err; + + err = krb5_ser_pack_int32(KV5M_GSS_OID, buffer, lenremain); + if (err) + return err; + err = krb5_ser_pack_int32((krb5_int32) oid->length, + buffer, lenremain); + if (err) + return err; + err = krb5_ser_pack_bytes((krb5_octet *) oid->elements, + oid->length, buffer, lenremain); + if (err) + return err; + err = krb5_ser_pack_int32(KV5M_GSS_OID, buffer, lenremain); + return err; +} + +static krb5_error_code +kg_oid_internalize(kcontext, argp, buffer, lenremain) + krb5_context kcontext; + krb5_pointer *argp; + krb5_octet **buffer; + size_t *lenremain; +{ + gss_OID oid; + krb5_int32 ibuf; + krb5_octet *bp; + size_t remain; + + bp = *buffer; + remain = *lenremain; + + /* Read in and check our magic number */ + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) + return (EINVAL); + + if (ibuf != KV5M_GSS_OID) + return (EINVAL); + + oid = (gss_OID) malloc(sizeof(gss_OID_desc)); + if (oid == NULL) + return ENOMEM; + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) { + free(oid); + return EINVAL; + } + oid->length = ibuf; + oid->elements = malloc((size_t)ibuf); + if (oid->elements == 0) { + free(oid); + return ENOMEM; + } + if (krb5_ser_unpack_bytes((krb5_octet *) oid->elements, + oid->length, &bp, &remain)) { + free(oid->elements); + free(oid); + return EINVAL; + } + + /* Read in and check our trailing magic number */ + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) { + free(oid->elements); + free(oid); + return (EINVAL); + } + + if (ibuf != KV5M_GSS_OID) { + free(oid->elements); + free(oid); + return (EINVAL); + } + + *buffer = bp; + *lenremain = remain; + *argp = (krb5_pointer) oid; + return 0; +} + +static krb5_error_code +kg_oid_size(kcontext, arg, sizep) + krb5_context kcontext; + krb5_pointer arg; + size_t *sizep; +{ + krb5_error_code kret; + gss_OID oid; + size_t required; + + kret = EINVAL; + if ((oid = (gss_OID) arg)) { + required = 2*sizeof(krb5_int32); /* For the header and trailer */ + required += sizeof(krb5_int32); + required += oid->length; + + kret = 0; + + *sizep += required; + } + + return(kret); +} + +static krb5_error_code +kg_seqstate_externalize(kcontext, arg, buffer, lenremain) + krb5_context kcontext; + g_seqnum_state arg; + krb5_octet **buffer; + size_t *lenremain; +{ + krb5_error_code err; + err = krb5_ser_pack_int32(KV5M_GSS_QUEUE, buffer, lenremain); + if (err == 0) + err = g_seqstate_externalize(arg, buffer, lenremain); + if (err == 0) + err = krb5_ser_pack_int32(KV5M_GSS_QUEUE, buffer, lenremain); + return err; +} + +static krb5_error_code +kg_seqstate_internalize(kcontext, argp, buffer, lenremain) + krb5_context kcontext; + g_seqnum_state *argp; + krb5_octet **buffer; + size_t *lenremain; +{ + krb5_int32 ibuf; + krb5_octet *bp; + size_t remain; + krb5_error_code err; + + bp = *buffer; + remain = *lenremain; + + /* Read in and check our magic number */ + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) + return (EINVAL); + + if (ibuf != KV5M_GSS_QUEUE) + return (EINVAL); + + err = g_seqstate_internalize(argp, &bp, &remain); + if (err) + return err; + + /* Read in and check our trailing magic number */ + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) { + g_seqstate_free(*argp); + return (EINVAL); + } + + if (ibuf != KV5M_GSS_QUEUE) { + g_seqstate_free(*argp); + return (EINVAL); + } + + *buffer = bp; + *lenremain = remain; + return 0; +} + +static krb5_error_code +kg_seqstate_size(kcontext, arg, sizep) + krb5_context kcontext; + g_seqnum_state arg; + size_t *sizep; +{ + krb5_error_code kret; + size_t required; + + kret = EINVAL; + if (arg) { + required = 2*sizeof(krb5_int32); /* For the header and trailer */ + g_seqstate_size(arg, &required); + + kret = 0; + *sizep += required; + } + return(kret); +} + +/* + * Determine the size required for this krb5_gss_ctx_id_rec. + */ +krb5_error_code +kg_ctx_size(kcontext, arg, sizep) + krb5_context kcontext; + krb5_pointer arg; + size_t *sizep; +{ + krb5_error_code kret; + krb5_gss_ctx_id_rec *ctx; + size_t required; + + /* + * krb5_gss_ctx_id_rec requires: + * krb5_int32 for KG_CONTEXT + * krb5_int32 for initiate. + * krb5_int32 for established. + * krb5_int32 for have_acceptor_subkey. + * krb5_int32 for seed_init. + * krb5_int32 for gss_flags. + * sizeof(seed) for seed + * ... for here + * ... for there + * ... for subkey + * krb5_int32 for signalg. + * krb5_int32 for cksum_size. + * krb5_int32 for sealalg. + * ... for enc + * ... for seq + * krb5_int32 for authtime. + * krb5_int32 for starttime. + * krb5_int32 for endtime. + * krb5_int32 for renew_till. + * krb5_int32 for flags. + * int64_t for seq_send. + * int64_t for seq_recv. + * ... for seqstate + * ... for auth_context + * ... for mech_used + * krb5_int32 for proto + * krb5_int32 for cksumtype + * ... for acceptor_subkey + * krb5_int32 for acceptor_key_cksumtype + * krb5_int32 for cred_rcache + * krb5_int32 for number of elements in authdata array + * ... for authdata array + * krb5_int32 for trailer. + */ + kret = EINVAL; + if ((ctx = (krb5_gss_ctx_id_rec *) arg)) { + required = 21*sizeof(krb5_int32); + required += 2*sizeof(int64_t); + required += sizeof(ctx->seed); + + kret = 0; + if (!kret && ctx->here) + kret = krb5_size_opaque(kcontext, + KV5M_PRINCIPAL, + (krb5_pointer) ctx->here->princ, + &required); + + if (!kret && ctx->there) + kret = krb5_size_opaque(kcontext, + KV5M_PRINCIPAL, + (krb5_pointer) ctx->there->princ, + &required); + + if (!kret && ctx->subkey) + kret = krb5_size_opaque(kcontext, + KV5M_KEYBLOCK, + (krb5_pointer) &ctx->subkey->keyblock, + &required); + + if (!kret && ctx->enc) + kret = krb5_size_opaque(kcontext, + KV5M_KEYBLOCK, + (krb5_pointer) &ctx->enc->keyblock, + &required); + + if (!kret && ctx->seq) + kret = krb5_size_opaque(kcontext, + KV5M_KEYBLOCK, + (krb5_pointer) &ctx->seq->keyblock, + &required); + + if (!kret) + kret = kg_oid_size(kcontext, + (krb5_pointer) ctx->mech_used, + &required); + + if (!kret && ctx->seqstate) + kret = kg_seqstate_size(kcontext, ctx->seqstate, &required); + + if (!kret) + kret = krb5_size_opaque(kcontext, + KV5M_CONTEXT, + (krb5_pointer) ctx->k5_context, + &required); + if (!kret) + kret = krb5_size_opaque(kcontext, + KV5M_AUTH_CONTEXT, + (krb5_pointer) ctx->auth_context, + &required); + if (!kret && ctx->acceptor_subkey) + kret = krb5_size_opaque(kcontext, + KV5M_KEYBLOCK, (krb5_pointer) + &ctx->acceptor_subkey->keyblock, + &required); + if (!kret && ctx->authdata) { + krb5_int32 i; + + for (i = 0; !kret && ctx->authdata[i]; i++) { + kret = krb5_size_opaque(kcontext, + KV5M_AUTHDATA, + (krb5_pointer)ctx->authdata[i], + &required); + } + } + if (!kret) { + krb5_gss_name_t initiator_name; + + initiator_name = ctx->initiate ? ctx->here : ctx->there; + + if (initiator_name && initiator_name->ad_context) { + kret = krb5_size_opaque(kcontext, + KV5M_AUTHDATA_CONTEXT, + initiator_name->ad_context, + &required); + } + } + *sizep += required; + } + return(kret); +} + +/* + * Externalize this krb5_gss_ctx_id_ret. + */ +krb5_error_code +kg_ctx_externalize(kcontext, arg, buffer, lenremain) + krb5_context kcontext; + krb5_pointer arg; + krb5_octet **buffer; + size_t *lenremain; +{ + krb5_error_code kret; + krb5_gss_ctx_id_rec *ctx; + size_t required; + krb5_octet *bp; + size_t remain; + krb5int_access kaccess; + + kret = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); + if (kret) + return(kret); + + required = 0; + bp = *buffer; + remain = *lenremain; + kret = EINVAL; + if ((ctx = (krb5_gss_ctx_id_rec *) arg)) { + kret = ENOMEM; + if (!kg_ctx_size(kcontext, arg, &required) && + (required <= remain)) { + /* Our identifier */ + (void) krb5_ser_pack_int32(KG_CONTEXT, &bp, &remain); + + /* Now static data */ + (void) krb5_ser_pack_int32((krb5_int32) ctx->initiate, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->established, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->have_acceptor_subkey, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->seed_init, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->gss_flags, + &bp, &remain); + (void) krb5_ser_pack_bytes((krb5_octet *) ctx->seed, + sizeof(ctx->seed), + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->signalg, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->cksum_size, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->sealalg, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->krb_times.authtime, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->krb_times.starttime, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->krb_times.endtime, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->krb_times.renew_till, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->krb_flags, + &bp, &remain); + (void) (*kaccess.ser_pack_int64)((int64_t) ctx->seq_send, + &bp, &remain); + (void) (*kaccess.ser_pack_int64)((int64_t) ctx->seq_recv, + &bp, &remain); + + /* Now dynamic data */ + kret = 0; + + if (!kret && ctx->mech_used) + kret = kg_oid_externalize(kcontext, ctx->mech_used, + &bp, &remain); + + if (!kret && ctx->here) + kret = krb5_externalize_opaque(kcontext, + KV5M_PRINCIPAL, + (krb5_pointer) ctx->here->princ, + &bp, &remain); + + if (!kret && ctx->there) + kret = krb5_externalize_opaque(kcontext, + KV5M_PRINCIPAL, + (krb5_pointer) ctx->there->princ, + &bp, &remain); + + if (!kret && ctx->subkey) + kret = krb5_externalize_opaque(kcontext, + KV5M_KEYBLOCK, (krb5_pointer) + &ctx->subkey->keyblock, + &bp, &remain); + + if (!kret && ctx->enc) + kret = krb5_externalize_opaque(kcontext, + KV5M_KEYBLOCK, (krb5_pointer) + &ctx->enc->keyblock, + &bp, &remain); + + if (!kret && ctx->seq) + kret = krb5_externalize_opaque(kcontext, + KV5M_KEYBLOCK, (krb5_pointer) + &ctx->seq->keyblock, + &bp, &remain); + + if (!kret && ctx->seqstate) + kret = kg_seqstate_externalize(kcontext, + ctx->seqstate, &bp, &remain); + + if (!kret) + kret = krb5_externalize_opaque(kcontext, + KV5M_CONTEXT, + (krb5_pointer) ctx->k5_context, + &bp, &remain); + + if (!kret) + kret = krb5_externalize_opaque(kcontext, + KV5M_AUTH_CONTEXT, + (krb5_pointer) ctx->auth_context, + &bp, &remain); + + if (!kret) + kret = krb5_ser_pack_int32((krb5_int32) ctx->proto, + &bp, &remain); + if (!kret) + kret = krb5_ser_pack_int32((krb5_int32) ctx->cksumtype, + &bp, &remain); + if (!kret && ctx->acceptor_subkey) + kret = krb5_externalize_opaque(kcontext, + KV5M_KEYBLOCK, (krb5_pointer) + &ctx->acceptor_subkey->keyblock, + &bp, &remain); + if (!kret) + kret = krb5_ser_pack_int32((krb5_int32) ctx->acceptor_subkey_cksumtype, + &bp, &remain); + + if (!kret) + kret = krb5_ser_pack_int32((krb5_int32) ctx->cred_rcache, + &bp, &remain); + if (!kret) { + krb5_int32 i = 0; + + if (ctx->authdata) { + for (; ctx->authdata[i]; i++) + ; + } + /* authdata count */ + kret = krb5_ser_pack_int32(i, &bp, &remain); + if (!kret && ctx->authdata) { + /* authdata */ + for (i = 0; !kret && ctx->authdata[i]; i++) + kret = krb5_externalize_opaque(kcontext, + KV5M_AUTHDATA, + ctx->authdata[i], + &bp, + &remain); + } + } + /* authdata context */ + if (!kret) { + krb5_gss_name_t initiator_name; + + initiator_name = ctx->initiate ? ctx->here : ctx->there; + + if (initiator_name && initiator_name->ad_context) { + kret = krb5_externalize_opaque(kcontext, + KV5M_AUTHDATA_CONTEXT, + initiator_name->ad_context, + &bp, + &remain); + } + } + /* trailer */ + if (!kret) + kret = krb5_ser_pack_int32(KG_CONTEXT, &bp, &remain); + if (!kret) { + *buffer = bp; + *lenremain = remain; + } + } + } + return(kret); +} + +/* Internalize a keyblock and convert it to a key. */ +static krb5_error_code +intern_key(krb5_context ctx, krb5_key *key, krb5_octet **bp, size_t *sp) +{ + krb5_keyblock *keyblock; + krb5_error_code ret; + + ret = krb5_internalize_opaque(ctx, KV5M_KEYBLOCK, + (krb5_pointer *) &keyblock, bp, sp); + if (ret != 0) + return ret; + ret = krb5_k_create_key(ctx, keyblock, key); + krb5_free_keyblock(ctx, keyblock); + return ret; +} + +/* + * Internalize this krb5_gss_ctx_id_t. + */ +krb5_error_code +kg_ctx_internalize(kcontext, argp, buffer, lenremain) + krb5_context kcontext; + krb5_pointer *argp; + krb5_octet **buffer; + size_t *lenremain; +{ + krb5_error_code kret; + krb5_gss_ctx_id_rec *ctx; + krb5_int32 ibuf; + krb5_octet *bp; + size_t remain; + krb5int_access kaccess; + krb5_principal princ; + + kret = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION); + if (kret) + return(kret); + + bp = *buffer; + remain = *lenremain; + kret = EINVAL; + princ = NULL; + /* Read our magic number */ + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) + ibuf = 0; + if (ibuf == KG_CONTEXT) { + kret = ENOMEM; + + /* Get a context */ + if ((remain >= (17*sizeof(krb5_int32) + + 2*sizeof(int64_t) + + sizeof(ctx->seed))) && + (ctx = (krb5_gss_ctx_id_rec *) + xmalloc(sizeof(krb5_gss_ctx_id_rec)))) { + memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); + + ctx->magic = ibuf; + ctx->k5_context = kcontext; + + /* Get static data */ + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->initiate = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->established = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->have_acceptor_subkey = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->seed_init = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->gss_flags = (int) ibuf; + (void) krb5_ser_unpack_bytes((krb5_octet *) ctx->seed, + sizeof(ctx->seed), + &bp, &remain); + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->signalg = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->cksum_size = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->sealalg = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->krb_times.authtime = (krb5_timestamp) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->krb_times.starttime = (krb5_timestamp) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->krb_times.endtime = (krb5_timestamp) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->krb_times.renew_till = (krb5_timestamp) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->krb_flags = (krb5_flags) ibuf; + (void) (*kaccess.ser_unpack_int64)((int64_t *)&ctx->seq_send, + &bp, &remain); + kret = (*kaccess.ser_unpack_int64)((int64_t *)&ctx->seq_recv, + &bp, &remain); + if (kret) { + free(ctx); + return kret; + } + + { + krb5_pointer tmp; + kret = kg_oid_internalize(kcontext, &tmp, &bp, + &remain); + if (kret == 0) + ctx->mech_used = tmp; + else if (kret == EINVAL) + kret = 0; + } + /* Now get substructure data */ + kret = krb5_internalize_opaque(kcontext, + KV5M_PRINCIPAL, + (krb5_pointer *) &princ, + &bp, &remain); + if (kret == 0) { + kret = kg_init_name(kcontext, princ, NULL, NULL, NULL, + KG_INIT_NAME_NO_COPY, &ctx->here); + if (kret) + krb5_free_principal(kcontext, princ); + } else if (kret == EINVAL) + kret = 0; + if (!kret) { + kret = krb5_internalize_opaque(kcontext, + KV5M_PRINCIPAL, + (krb5_pointer *) &princ, + &bp, &remain); + if (kret == 0) { + kret = kg_init_name(kcontext, princ, NULL, NULL, NULL, + KG_INIT_NAME_NO_COPY, &ctx->there); + if (kret) + krb5_free_principal(kcontext, princ); + } else if (kret == EINVAL) + kret = 0; + } + if (!kret && + (kret = intern_key(kcontext, &ctx->subkey, &bp, &remain))) { + if (kret == EINVAL) + kret = 0; + } + if (!kret && + (kret = intern_key(kcontext, &ctx->enc, &bp, &remain))) { + if (kret == EINVAL) + kret = 0; + } + if (!kret && + (kret = intern_key(kcontext, &ctx->seq, &bp, &remain))) { + if (kret == EINVAL) + kret = 0; + } + + if (!kret) { + kret = kg_seqstate_internalize(kcontext, &ctx->seqstate, + &bp, &remain); + if (kret == EINVAL) + kret = 0; + } + + if (!kret) + kret = krb5_internalize_opaque(kcontext, + KV5M_CONTEXT, + (krb5_pointer *) &ctx->k5_context, + &bp, &remain); + + if (!kret) + kret = krb5_internalize_opaque(kcontext, + KV5M_AUTH_CONTEXT, + (krb5_pointer *) &ctx->auth_context, + &bp, &remain); + + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->proto = ibuf; + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->cksumtype = ibuf; + if (!kret && + (kret = intern_key(kcontext, &ctx->acceptor_subkey, + &bp, &remain))) { + if (kret == EINVAL) + kret = 0; + } + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->acceptor_subkey_cksumtype = ibuf; + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->cred_rcache = ibuf; + /* authdata */ + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + if (!kret) { + krb5_int32 nadata = ibuf, i; + + if (nadata > 0) { + ctx->authdata = (krb5_authdata **)calloc((size_t)nadata + 1, + sizeof(krb5_authdata *)); + if (ctx->authdata == NULL) { + kret = ENOMEM; + } else { + for (i = 0; !kret && i < nadata; i++) + kret = krb5_internalize_opaque(kcontext, + KV5M_AUTHDATA, + (krb5_pointer *)&ctx->authdata[i], + &bp, + &remain); + } + } + } + /* authdata context */ + if (!kret) { + krb5_gss_name_t initiator_name; + + initiator_name = ctx->initiate ? ctx->here : ctx->there; + if (initiator_name == NULL) { + kret = EINVAL; + } else { + kret = krb5_internalize_opaque(kcontext, + KV5M_AUTHDATA_CONTEXT, + (krb5_pointer *)&initiator_name->ad_context, + &bp, + &remain); + if (kret == EINVAL) + kret = 0; + } + } + /* Get trailer */ + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + if (!kret && ibuf != KG_CONTEXT) + kret = EINVAL; + + if (!kret) { + *buffer = bp; + *lenremain = remain; + *argp = (krb5_pointer) ctx; + } else { + if (ctx->seq) + krb5_k_free_key(kcontext, ctx->seq); + if (ctx->enc) + krb5_k_free_key(kcontext, ctx->enc); + if (ctx->subkey) + krb5_k_free_key(kcontext, ctx->subkey); + if (ctx->there) + kg_release_name(kcontext, &ctx->there); + if (ctx->here) + kg_release_name(kcontext, &ctx->here); + xfree(ctx); + } + } + } + return(kret); +} diff --git a/src/lib/gssapi/krb5/set_allowable_enctypes.c b/src/lib/gssapi/krb5/set_allowable_enctypes.c new file mode 100644 index 000000000000..d9fd279ed234 --- /dev/null +++ b/src/lib/gssapi/krb5/set_allowable_enctypes.c @@ -0,0 +1,123 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/set_allowable_enctypes.c */ +/* + * Copyright 2004 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* + * krb5_gss_set_allowable_enctypes() + */ + +/* + * gss_krb5_set_allowable_enctypes + * + * This function may be called by a context initiator after calling + * gss_acquire_cred(), but before calling gss_init_sec_context(), + * to restrict the set of enctypes which will be negotiated during + * context establishment to those in the provided array. + * + * 'cred_handle' must be a valid credential handle obtained via + * gss_acquire_cred(). It may not be GSS_C_NO_CREDENTIAL. + * gss_acquire_cred() may be called with GSS_C_NO_CREDENTIAL + * to get a handle to the default credential. + * + * The purpose of this function is to limit the keys that may + * be exported via gss_krb5_export_lucid_sec_context(); thus it + * should limit the enctypes of all keys that will be needed + * after the security context has been established. + * (i.e. context establishment may use a session key with a + * stronger enctype than in the provided array, however a + * subkey must be established within the enctype limits + * established by this function.) + * + */ + +#include "gssapiP_krb5.h" +#ifdef HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif +#include "gssapi_krb5.h" + +OM_uint32 +gss_krb5int_set_allowable_enctypes(OM_uint32 *minor_status, + gss_cred_id_t *cred_handle, + const gss_OID desired_oid, + const gss_buffer_t value) +{ + unsigned int i; + krb5_enctype * new_ktypes; + OM_uint32 major_status; + krb5_gss_cred_id_t cred; + krb5_error_code kerr = 0; + struct krb5_gss_set_allowable_enctypes_req *req; + + /* Assume a failure */ + *minor_status = 0; + major_status = GSS_S_FAILURE; + + assert(value->length == sizeof(*req)); + req = (struct krb5_gss_set_allowable_enctypes_req *)value->value; + + /* verify and valildate cred handle */ + cred = (krb5_gss_cred_id_t) *cred_handle; + + if (req->ktypes) { + for (i = 0; i < req->num_ktypes && req->ktypes[i]; i++) { + if (!krb5_c_valid_enctype(req->ktypes[i])) { + kerr = KRB5_PROG_ETYPE_NOSUPP; + goto error_out; + } + } + } else { + k5_mutex_lock(&cred->lock); + if (cred->req_enctypes) + free(cred->req_enctypes); + cred->req_enctypes = NULL; + k5_mutex_unlock(&cred->lock); + return GSS_S_COMPLETE; + } + + /* Copy the requested ktypes into the cred structure */ + if ((new_ktypes = (krb5_enctype *)malloc(sizeof(krb5_enctype) * (i + 1)))) { + memcpy(new_ktypes, req->ktypes, sizeof(krb5_enctype) * i); + new_ktypes[i] = 0; /* "null-terminate" the list */ + } + else { + kerr = ENOMEM; + goto error_out; + } + k5_mutex_lock(&cred->lock); + if (cred->req_enctypes) + free(cred->req_enctypes); + cred->req_enctypes = new_ktypes; + k5_mutex_unlock(&cred->lock); + + /* Success! */ + return GSS_S_COMPLETE; + +error_out: + *minor_status = kerr; + return(major_status); +} diff --git a/src/lib/gssapi/krb5/set_ccache.c b/src/lib/gssapi/krb5/set_ccache.c new file mode 100644 index 000000000000..8acf3ec90cde --- /dev/null +++ b/src/lib/gssapi/krb5/set_ccache.c @@ -0,0 +1,101 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/set_ccache.c */ +/* + * Copyright 1999, 2003 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* + * Set ccache name used by gssapi, and optionally obtain old ccache + * name. Caller should not free returned name. + */ + +#include <string.h> +#include "gssapiP_krb5.h" + +OM_uint32 +gss_krb5int_ccache_name(OM_uint32 *minor_status, + const gss_OID desired_mech, + const gss_OID desired_object, + const gss_buffer_t value) +{ + char *old_name = NULL; + OM_uint32 err = 0; + OM_uint32 minor = 0; + char *gss_out_name; + struct krb5_gss_ccache_name_req *req; + + err = gss_krb5int_initialize_library(); + if (err) { + *minor_status = err; + return GSS_S_FAILURE; + } + + assert(value->length == sizeof(*req)); + + if (value->length != sizeof(*req)) + return GSS_S_FAILURE; + + req = (struct krb5_gss_ccache_name_req *)value->value; + + gss_out_name = k5_getspecific(K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME); + + if (req->out_name) { + const char *tmp_name = NULL; + + if (!err) { + kg_get_ccache_name (&err, &tmp_name); + } + if (!err) { + old_name = gss_out_name; + gss_out_name = (char *)tmp_name; + } + } + /* If out_name was NULL, we keep the same gss_out_name value, and + don't free up any storage (leave old_name NULL). */ + + if (!err) + kg_set_ccache_name (&err, req->name); + + minor = k5_setspecific(K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME, gss_out_name); + if (minor) { + /* Um. Now what? */ + if (err == 0) { + err = minor; + } + free(gss_out_name); + gss_out_name = NULL; + } + + if (!err) { + if (req->out_name) { + *(req->out_name) = gss_out_name; + } + } + + if (old_name != NULL) { + free (old_name); + } + + *minor_status = err; + return (*minor_status == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} diff --git a/src/lib/gssapi/krb5/store_cred.c b/src/lib/gssapi/krb5/store_cred.c new file mode 100644 index 000000000000..654d96541726 --- /dev/null +++ b/src/lib/gssapi/krb5/store_cred.c @@ -0,0 +1,234 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/krb5/store_cred.c */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" + +static int +has_unexpired_creds(krb5_gss_cred_id_t kcred, + const gss_OID desired_mech, + int default_cred, + gss_const_key_value_set_t cred_store) +{ + OM_uint32 major_status, minor; + gss_name_t cred_name; + gss_OID_set_desc desired_mechs; + gss_cred_id_t tmp_cred = GSS_C_NO_CREDENTIAL; + OM_uint32 time_rec; + + desired_mechs.count = 1; + desired_mechs.elements = (gss_OID)desired_mech; + + if (default_cred) + cred_name = GSS_C_NO_NAME; + else + cred_name = (gss_name_t)kcred->name; + + major_status = krb5_gss_acquire_cred_from(&minor, cred_name, 0, + &desired_mechs, GSS_C_INITIATE, + cred_store, &tmp_cred, NULL, + &time_rec); + + krb5_gss_release_cred(&minor, &tmp_cred); + + return (GSS_ERROR(major_status) || time_rec); +} + +static OM_uint32 +copy_initiator_creds(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_const_key_value_set_t cred_store) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_gss_cred_id_t kcred = NULL; + krb5_context context = NULL; + krb5_ccache ccache = NULL; + const char *ccache_name; + + *minor_status = 0; + + if (!default_cred && cred_store == GSS_C_NO_CRED_STORE) { + *minor_status = G_STORE_NON_DEFAULT_CRED_NOSUPP; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + major_status = krb5_gss_validate_cred_1(minor_status, + input_cred_handle, + context); + if (GSS_ERROR(major_status)) + goto cleanup; + + kcred = (krb5_gss_cred_id_t)input_cred_handle; + + if (kcred->ccache == NULL) { + *minor_status = KG_CCACHE_NOMATCH; + major_status = GSS_S_DEFECTIVE_CREDENTIAL; + goto cleanup; + } + + if (!overwrite_cred && + has_unexpired_creds(kcred, desired_mech, default_cred, cred_store)) { + major_status = GSS_S_DUPLICATE_ELEMENT; + goto cleanup; + } + + major_status = kg_value_from_cred_store(cred_store, + KRB5_CS_CCACHE_URN, &ccache_name); + if (GSS_ERROR(major_status)) + goto cleanup; + + if (ccache_name != NULL) { + code = krb5_cc_resolve(context, ccache_name, &ccache); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_CRED_UNAVAIL; + goto cleanup; + } + code = krb5_cc_initialize(context, ccache, + kcred->name->princ); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_CRED_UNAVAIL; + goto cleanup; + } + } + + if (ccache == NULL) { + if (!default_cred) { + *minor_status = G_STORE_NON_DEFAULT_CRED_NOSUPP; + major_status = GSS_S_FAILURE; + goto cleanup; + } + code = krb5int_cc_default(context, &ccache); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + } + + code = krb5_cc_copy_creds(context, kcred->ccache, ccache); + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + goto cleanup; + } + + *minor_status = 0; + major_status = GSS_S_COMPLETE; + +cleanup: + if (kcred != NULL) + k5_mutex_unlock(&kcred->lock); + if (ccache != NULL) + krb5_cc_close(context, ccache); + krb5_free_context(context); + + return major_status; +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_store_cred(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + gss_cred_usage_t cred_usage, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored) +{ + return krb5_gss_store_cred_into(minor_status, input_cred_handle, + cred_usage, desired_mech, + overwrite_cred, default_cred, + GSS_C_NO_CRED_STORE, + elements_stored, cred_usage_stored); +} + +OM_uint32 KRB5_CALLCONV +krb5_gss_store_cred_into(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + gss_cred_usage_t cred_usage, + const gss_OID desired_mech, + OM_uint32 overwrite_cred, + OM_uint32 default_cred, + gss_const_key_value_set_t cred_store, + gss_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored) +{ + OM_uint32 major_status; + gss_cred_usage_t actual_usage; + OM_uint32 lifetime; + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_NO_CRED; + + major_status = GSS_S_FAILURE; + + if (cred_usage == GSS_C_ACCEPT) { + *minor_status = G_STORE_ACCEPTOR_CRED_NOSUPP; + return GSS_S_FAILURE; + } else if (cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { + *minor_status = G_BAD_USAGE; + return GSS_S_FAILURE; + } + + major_status = krb5_gss_inquire_cred(minor_status, input_cred_handle, + NULL, &lifetime, + &actual_usage, elements_stored); + if (GSS_ERROR(major_status)) + return major_status; + + if (lifetime == 0) + return GSS_S_CREDENTIALS_EXPIRED; + + if (actual_usage != GSS_C_INITIATE && actual_usage != GSS_C_BOTH) { + *minor_status = G_STORE_ACCEPTOR_CRED_NOSUPP; + return GSS_S_FAILURE; + } + + major_status = copy_initiator_creds(minor_status, input_cred_handle, + desired_mech, overwrite_cred, + default_cred, cred_store); + if (GSS_ERROR(major_status)) + return major_status; + + if (cred_usage_stored != NULL) + *cred_usage_stored = GSS_C_INITIATE; + + return GSS_S_COMPLETE; +} diff --git a/src/lib/gssapi/krb5/util_cksum.c b/src/lib/gssapi/krb5/util_cksum.c new file mode 100644 index 000000000000..cfd585ec7219 --- /dev/null +++ b/src/lib/gssapi/krb5/util_cksum.c @@ -0,0 +1,295 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +/* Checksumming the channel bindings always uses plain MD5. */ +krb5_error_code +kg_checksum_channel_bindings(context, cb, cksum) + krb5_context context; + gss_channel_bindings_t cb; + krb5_checksum *cksum; +{ + size_t len; + char *buf = 0; + char *ptr; + size_t sumlen; + krb5_data plaind; + krb5_error_code code; + void *temp; + + /* initialize the the cksum */ + code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &sumlen); + if (code) + return(code); + + cksum->checksum_type = CKSUMTYPE_RSA_MD5; + cksum->length = sumlen; + + /* generate a buffer full of zeros if no cb specified */ + + if (cb == GSS_C_NO_CHANNEL_BINDINGS) { + if ((cksum->contents = (krb5_octet *) xmalloc(cksum->length)) == NULL) { + return(ENOMEM); + } + memset(cksum->contents, '\0', cksum->length); + return(0); + } + + /* create the buffer to checksum into */ + + len = (sizeof(krb5_int32)*5+ + cb->initiator_address.length+ + cb->acceptor_address.length+ + cb->application_data.length); + + if ((buf = (char *) xmalloc(len)) == NULL) + return(ENOMEM); + + /* helper macros. This code currently depends on a long being 32 + bits, and htonl dtrt. */ + + ptr = buf; + + TWRITE_INT(ptr, cb->initiator_addrtype, 0); + TWRITE_BUF(ptr, cb->initiator_address, 0); + TWRITE_INT(ptr, cb->acceptor_addrtype, 0); + TWRITE_BUF(ptr, cb->acceptor_address, 0); + TWRITE_BUF(ptr, cb->application_data, 0); + + /* checksum the data */ + + plaind.length = len; + plaind.data = buf; + + code = krb5_c_make_checksum(context, CKSUMTYPE_RSA_MD5, 0, 0, + &plaind, cksum); + if (code) + goto cleanup; + + if ((temp = xmalloc(cksum->length)) == NULL) { + krb5_free_checksum_contents(context, cksum); + code = ENOMEM; + goto cleanup; + } + + memcpy(temp, cksum->contents, cksum->length); + krb5_free_checksum_contents(context, cksum); + cksum->contents = (krb5_octet *)temp; + + /* success */ +cleanup: + if (buf) + xfree(buf); + return code; +} + +krb5_error_code +kg_make_checksum_iov_v1(krb5_context context, + krb5_cksumtype type, + size_t cksum_len, + krb5_key seq, + krb5_key enc, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype, + krb5_checksum *checksum) +{ + krb5_error_code code; + gss_iov_buffer_desc *header; + krb5_crypto_iov *kiov; + int i = 0, j; + size_t conf_len = 0, token_header_len; + + header = kg_locate_header_iov(iov, iov_count, toktype); + assert(header != NULL); + + kiov = calloc(iov_count + 3, sizeof(krb5_crypto_iov)); + if (kiov == NULL) + return ENOMEM; + + /* Checksum over ( Header | Confounder | Data | Pad ) */ + if (toktype == KG_TOK_WRAP_MSG) + conf_len = kg_confounder_size(context, enc->keyblock.enctype); + + /* Checksum output */ + kiov[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM; + kiov[i].data.length = checksum->length; + kiov[i].data.data = xmalloc(checksum->length); + if (kiov[i].data.data == NULL) { + xfree(kiov); + return ENOMEM; + } + i++; + + /* Header | SND_SEQ | SGN_CKSUM | Confounder */ + token_header_len = 16 + cksum_len + conf_len; + + /* Header (calculate from end because of variable length ASN.1 header) */ + kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; + kiov[i].data.length = 8; + kiov[i].data.data = (char *)header->buffer.value + header->buffer.length - token_header_len; + i++; + + /* Confounder */ + if (toktype == KG_TOK_WRAP_MSG) { + kiov[i].flags = KRB5_CRYPTO_TYPE_DATA; + kiov[i].data.length = conf_len; + kiov[i].data.data = (char *)header->buffer.value + header->buffer.length - conf_len; + i++; + } + + for (j = 0; j < iov_count; j++) { + kiov[i].flags = kg_translate_flag_iov(iov[j].type); + kiov[i].data.length = iov[j].buffer.length; + kiov[i].data.data = (char *)iov[j].buffer.value; + i++; + } + + code = krb5_k_make_checksum_iov(context, type, seq, sign_usage, kiov, i); + if (code == 0) { + checksum->length = kiov[0].data.length; + checksum->contents = (unsigned char *)kiov[0].data.data; + } else + free(kiov[0].data.data); + + xfree(kiov); + + return code; +} + +static krb5_error_code +checksum_iov_v3(krb5_context context, + krb5_cksumtype type, + size_t rrc, + krb5_key key, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype, + krb5_boolean verify, + krb5_boolean *valid) +{ + krb5_error_code code; + gss_iov_buffer_desc *header; + gss_iov_buffer_desc *trailer; + krb5_crypto_iov *kiov; + size_t kiov_count; + int i = 0, j; + unsigned int k5_checksumlen; + + if (verify) + *valid = FALSE; + + code = krb5_c_crypto_length(context, key->keyblock.enctype, KRB5_CRYPTO_TYPE_CHECKSUM, &k5_checksumlen); + if (code != 0) + return code; + + header = kg_locate_header_iov(iov, iov_count, toktype); + assert(header != NULL); + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + assert(rrc != 0 || trailer != NULL); + + if (trailer == NULL) { + if (rrc != k5_checksumlen) + return KRB5_BAD_MSIZE; + if (header->buffer.length != 16 + k5_checksumlen) + return KRB5_BAD_MSIZE; + } else if (trailer->buffer.length != k5_checksumlen) + return KRB5_BAD_MSIZE; + + kiov_count = 2 + iov_count; + kiov = (krb5_crypto_iov *)xmalloc(kiov_count * sizeof(krb5_crypto_iov)); + if (kiov == NULL) + return ENOMEM; + + /* Checksum over ( Data | Header ) */ + + /* Data */ + for (j = 0; j < iov_count; j++) { + kiov[i].flags = kg_translate_flag_iov(iov[j].type); + kiov[i].data.length = iov[j].buffer.length; + kiov[i].data.data = (char *)iov[j].buffer.value; + i++; + } + + /* Header */ + kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; + kiov[i].data.length = 16; + kiov[i].data.data = (char *)header->buffer.value; + i++; + + /* Checksum */ + kiov[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM; + if (trailer == NULL) { + kiov[i].data.length = header->buffer.length - 16; + kiov[i].data.data = (char *)header->buffer.value + 16; + } else { + kiov[i].data.length = trailer->buffer.length; + kiov[i].data.data = (char *)trailer->buffer.value; + } + i++; + + if (verify) + code = krb5_k_verify_checksum_iov(context, type, key, sign_usage, kiov, kiov_count, valid); + else + code = krb5_k_make_checksum_iov(context, type, key, sign_usage, kiov, kiov_count); + + xfree(kiov); + + return code; +} + +krb5_error_code +kg_make_checksum_iov_v3(krb5_context context, + krb5_cksumtype type, + size_t rrc, + krb5_key key, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype) +{ + return checksum_iov_v3(context, type, rrc, key, + sign_usage, iov, iov_count, toktype, 0, NULL); +} + +krb5_error_code +kg_verify_checksum_iov_v3(krb5_context context, + krb5_cksumtype type, + size_t rrc, + krb5_key key, + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int toktype, + krb5_boolean *valid) +{ + return checksum_iov_v3(context, type, rrc, key, + sign_usage, iov, iov_count, toktype, 1, valid); +} diff --git a/src/lib/gssapi/krb5/util_crypt.c b/src/lib/gssapi/krb5/util_crypt.c new file mode 100644 index 000000000000..0cebde12a85c --- /dev/null +++ b/src/lib/gssapi/krb5/util_crypt.c @@ -0,0 +1,782 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2001, 2008 by the Massachusetts Institute of Technology. + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "k5-int.h" +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +static krb5_error_code +kg_copy_keys(krb5_context context, krb5_gss_ctx_id_rec *ctx, krb5_key subkey) +{ + krb5_error_code code; + + krb5_k_free_key(context, ctx->enc); + ctx->enc = NULL; + code = krb5_k_create_key(context, &subkey->keyblock, &ctx->enc); + if (code != 0) + return code; + + krb5_k_free_key(context, ctx->seq); + ctx->seq = NULL; + code = krb5_k_create_key(context, &subkey->keyblock, &ctx->seq); + if (code != 0) + return code; + + return 0; +} + +static krb5_error_code +kg_derive_des_enc_key(krb5_context context, krb5_key subkey, krb5_key *out) +{ + krb5_error_code code; + krb5_keyblock *keyblock; + unsigned int i; + + *out = NULL; + + code = krb5_k_key_keyblock(context, subkey, &keyblock); + if (code != 0) + return code; + + for (i = 0; i < keyblock->length; i++) + keyblock->contents[i] ^= 0xF0; + + code = krb5_k_create_key(context, keyblock, out); + krb5_free_keyblock(context, keyblock); + return code; +} + +krb5_error_code +kg_setup_keys(krb5_context context, krb5_gss_ctx_id_rec *ctx, krb5_key subkey, + krb5_cksumtype *cksumtype) +{ + krb5_error_code code; + + assert(ctx != NULL); + assert(subkey != NULL); + + *cksumtype = 0; + ctx->proto = 0; + + if (ctx->enc == NULL) { + ctx->signalg = -1; + ctx->sealalg = -1; + } + + code = krb5int_c_mandatory_cksumtype(context, subkey->keyblock.enctype, + cksumtype); + if (code != 0) + return code; + + switch (subkey->keyblock.enctype) { + case ENCTYPE_DES_CBC_MD5: + case ENCTYPE_DES_CBC_MD4: + case ENCTYPE_DES_CBC_CRC: + krb5_k_free_key(context, ctx->seq); + code = krb5_k_create_key(context, &subkey->keyblock, &ctx->seq); + if (code != 0) + return code; + + krb5_k_free_key(context, ctx->enc); + code = kg_derive_des_enc_key(context, subkey, &ctx->enc); + if (code != 0) + return code; + + ctx->enc->keyblock.enctype = ENCTYPE_DES_CBC_RAW; + ctx->seq->keyblock.enctype = ENCTYPE_DES_CBC_RAW; + ctx->signalg = SGN_ALG_DES_MAC_MD5; + ctx->cksum_size = 8; + ctx->sealalg = SEAL_ALG_DES; + + break; + case ENCTYPE_DES3_CBC_SHA1: + code = kg_copy_keys(context, ctx, subkey); + if (code != 0) + return code; + + ctx->enc->keyblock.enctype = ENCTYPE_DES3_CBC_RAW; + ctx->seq->keyblock.enctype = ENCTYPE_DES3_CBC_RAW; + ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD; + ctx->cksum_size = 20; + ctx->sealalg = SEAL_ALG_DES3KD; + break; + case ENCTYPE_ARCFOUR_HMAC: + case ENCTYPE_ARCFOUR_HMAC_EXP: + /* RFC 4121 accidentally omits RC4-HMAC-EXP as a "not-newer" enctype, + * even though RFC 4757 treats it as one. */ + code = kg_copy_keys(context, ctx, subkey); + if (code != 0) + return code; + + ctx->signalg = SGN_ALG_HMAC_MD5; + ctx->cksum_size = 8; + ctx->sealalg = SEAL_ALG_MICROSOFT_RC4; + break; + default: + ctx->proto = 1; + break; + } + + return 0; +} + +int +kg_confounder_size(krb5_context context, krb5_enctype enctype) +{ + krb5_error_code code; + size_t blocksize; + /* We special case rc4*/ + if (enctype == ENCTYPE_ARCFOUR_HMAC || enctype == ENCTYPE_ARCFOUR_HMAC_EXP) + return 8; + code = krb5_c_block_size(context, enctype, &blocksize); + if (code) + return(-1); /* XXX */ + + return(blocksize); +} + +krb5_error_code +kg_make_confounder(krb5_context context, krb5_enctype enctype, + unsigned char *buf) +{ + int confsize; + krb5_data lrandom; + + confsize = kg_confounder_size(context, enctype); + if (confsize < 0) + return KRB5_BAD_MSIZE; + + lrandom.length = confsize; + lrandom.data = (char *)buf; + + return(krb5_c_random_make_octets(context, &lrandom)); +} + +/* Set *data_out to a krb5_data structure containing iv, or to NULL if iv is + * NULL. */ +static krb5_error_code +iv_to_state(krb5_context context, krb5_key key, krb5_pointer iv, + krb5_data **data_out) +{ + krb5_error_code code; + krb5_data *data; + size_t blocksize; + + *data_out = NULL; + if (iv == NULL) + return 0; + + code = krb5_c_block_size(context, key->keyblock.enctype, &blocksize); + if (code) + return code; + + data = k5alloc(sizeof(*data), &code); + if (data == NULL) + return code; + code = alloc_data(data, blocksize); + if (code) { + free(data); + return code; + } + memcpy(data->data, iv, blocksize); + *data_out = data; + return 0; +} + +krb5_error_code +kg_encrypt(krb5_context context, krb5_key key, int usage, krb5_pointer iv, + krb5_const_pointer in, krb5_pointer out, unsigned int length) +{ + krb5_error_code code; + krb5_data *state, inputd; + krb5_enc_data outputd; + + code = iv_to_state(context, key, iv, &state); + if (code) + return code; + + inputd.length = length; + inputd.data = (char *)in; + + outputd.ciphertext.length = length; + outputd.ciphertext.data = out; + + code = krb5_k_encrypt(context, key, usage, state, &inputd, &outputd); + krb5_free_data(context, state); + return code; +} + +krb5_error_code +kg_encrypt_inplace(krb5_context context, krb5_key key, int usage, + krb5_pointer iv, krb5_pointer ptr, unsigned int length) +{ + krb5_error_code code; + krb5_crypto_iov iov; + krb5_data *state; + + code = iv_to_state(context, key, iv, &state); + if (code) + return code; + + iov.flags = KRB5_CRYPTO_TYPE_DATA; + iov.data = make_data((void *)ptr, length); + code = krb5_k_encrypt_iov(context, key, usage, state, &iov, 1); + krb5_free_data(context, state); + return code; +} + +/* length is the length of the cleartext. */ + +krb5_error_code +kg_decrypt(krb5_context context, krb5_key key, int usage, krb5_pointer iv, + krb5_const_pointer in, krb5_pointer out, unsigned int length) +{ + krb5_error_code code; + krb5_data *state, outputd; + krb5_enc_data inputd; + + code = iv_to_state(context, key, iv, &state); + if (code) + return code; + + inputd.enctype = ENCTYPE_UNKNOWN; + inputd.ciphertext.length = length; + inputd.ciphertext.data = (char *)in; + + outputd.length = length; + outputd.data = out; + + code = krb5_k_decrypt(context, key, usage, state, &inputd, &outputd); + krb5_free_data(context, state); + return code; +} + +krb5_error_code +kg_arcfour_docrypt(const krb5_keyblock *keyblock, int usage, + const unsigned char *kd_data, size_t kd_data_len, + const unsigned char *input_buf, size_t input_len, + unsigned char *output_buf) +{ + krb5_data kd = make_data((char *) kd_data, kd_data_len); + krb5_crypto_iov kiov; + + memcpy(output_buf, input_buf, input_len); + kiov.flags = KRB5_CRYPTO_TYPE_DATA; + kiov.data = make_data(output_buf, input_len); + return krb5int_arcfour_gsscrypt(keyblock, usage, &kd, &kiov, 1); +} + +/* AEAD */ +static krb5_error_code +kg_translate_iov_v1(krb5_context context, krb5_enctype enctype, + gss_iov_buffer_desc *iov, int iov_count, + krb5_crypto_iov **pkiov, size_t *pkiov_count) +{ + gss_iov_buffer_desc *header; + gss_iov_buffer_desc *trailer; + int i = 0, j; + size_t kiov_count; + krb5_crypto_iov *kiov; + size_t conf_len; + + *pkiov = NULL; + *pkiov_count = 0; + + conf_len = kg_confounder_size(context, enctype); + + header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); + assert(header != NULL); + + if (header->buffer.length < conf_len) + return KRB5_BAD_MSIZE; + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + assert(trailer == NULL || trailer->buffer.length == 0); + + kiov_count = 3 + iov_count; + kiov = (krb5_crypto_iov *)malloc(kiov_count * sizeof(krb5_crypto_iov)); + if (kiov == NULL) + return ENOMEM; + + /* For pre-CFX (raw enctypes) there is no krb5 header */ + kiov[i].flags = KRB5_CRYPTO_TYPE_HEADER; + kiov[i].data.length = 0; + kiov[i].data.data = NULL; + i++; + + /* For pre-CFX, the confounder is at the end of the GSS header */ + kiov[i].flags = KRB5_CRYPTO_TYPE_DATA; + kiov[i].data.length = conf_len; + kiov[i].data.data = (char *)header->buffer.value + header->buffer.length - conf_len; + i++; + + for (j = 0; j < iov_count; j++) { + kiov[i].flags = kg_translate_flag_iov(iov[j].type); + if (kiov[i].flags == KRB5_CRYPTO_TYPE_EMPTY) + continue; + + kiov[i].data.length = iov[j].buffer.length; + kiov[i].data.data = (char *)iov[j].buffer.value; + i++; + } + + kiov[i].flags = KRB5_CRYPTO_TYPE_TRAILER; + kiov[i].data.length = 0; + kiov[i].data.data = NULL; + i++; + + *pkiov = kiov; + *pkiov_count = i; + + return 0; +} + +/* + * DCE_STYLE indicates actual RRC is EC + RRC + * EC is extra rotate count for DCE_STYLE, pad length otherwise + * RRC is rotate count. + */ +static krb5_error_code +kg_translate_iov_v3(krb5_context context, int dce_style, size_t ec, size_t rrc, + krb5_enctype enctype, gss_iov_buffer_desc *iov, + int iov_count, krb5_crypto_iov **pkiov, + size_t *pkiov_count) +{ + gss_iov_buffer_t header; + gss_iov_buffer_t trailer; + int i = 0, j; + size_t kiov_count; + krb5_crypto_iov *kiov; + unsigned int k5_headerlen = 0, k5_trailerlen = 0; + size_t gss_headerlen, gss_trailerlen; + krb5_error_code code; + + *pkiov = NULL; + *pkiov_count = 0; + + header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); + assert(header != NULL); + + trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + assert(trailer == NULL || rrc == 0); + + code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, + &k5_headerlen); + if (code != 0) + return code; + + code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_TRAILER, + &k5_trailerlen); + if (code != 0) + return code; + + /* Check header and trailer sizes */ + gss_headerlen = 16 /* GSS-Header */ + k5_headerlen; /* Kerb-Header */ + gss_trailerlen = ec + 16 /* E(GSS-Header) */ + k5_trailerlen; /* Kerb-Trailer */ + + /* If we're caller without a trailer, we must rotate by trailer length */ + if (trailer == NULL) { + size_t actual_rrc = rrc; + + if (dce_style) + actual_rrc += ec; /* compensate for Windows bug */ + + if (actual_rrc != gss_trailerlen) + return KRB5_BAD_MSIZE; + + gss_headerlen += gss_trailerlen; + gss_trailerlen = 0; + } else { + if (trailer->buffer.length != gss_trailerlen) + return KRB5_BAD_MSIZE; + } + + if (header->buffer.length != gss_headerlen) + return KRB5_BAD_MSIZE; + + kiov_count = 3 + iov_count; + kiov = (krb5_crypto_iov *)malloc(kiov_count * sizeof(krb5_crypto_iov)); + if (kiov == NULL) + return ENOMEM; + + /* + * The krb5 header is located at the end of the GSS header. + */ + kiov[i].flags = KRB5_CRYPTO_TYPE_HEADER; + kiov[i].data.length = k5_headerlen; + kiov[i].data.data = (char *)header->buffer.value + header->buffer.length - k5_headerlen; + i++; + + for (j = 0; j < iov_count; j++) { + kiov[i].flags = kg_translate_flag_iov(iov[j].type); + if (kiov[i].flags == KRB5_CRYPTO_TYPE_EMPTY) + continue; + + kiov[i].data.length = iov[j].buffer.length; + kiov[i].data.data = (char *)iov[j].buffer.value; + i++; + } + + /* + * The EC and encrypted GSS header are placed in the trailer, which may + * be rotated directly after the plaintext header if no trailer buffer + * is provided. + */ + kiov[i].flags = KRB5_CRYPTO_TYPE_DATA; + kiov[i].data.length = ec + 16; /* E(Header) */ + if (trailer == NULL) + kiov[i].data.data = (char *)header->buffer.value + 16; + else + kiov[i].data.data = (char *)trailer->buffer.value; + i++; + + /* + * The krb5 trailer is placed after the encrypted copy of the + * krb5 header (which may be in the GSS header or trailer). + */ + kiov[i].flags = KRB5_CRYPTO_TYPE_TRAILER; + kiov[i].data.length = k5_trailerlen; + kiov[i].data.data = kiov[i - 1].data.data + ec + 16; /* E(Header) */ + i++; + + *pkiov = kiov; + *pkiov_count = i; + + return 0; +} + +/* PROTO is 1 if CFX, 0 if pre-CFX */ +static krb5_error_code +kg_translate_iov(krb5_context context, int proto, int dce_style, size_t ec, + size_t rrc, krb5_enctype enctype, gss_iov_buffer_desc *iov, + int iov_count, krb5_crypto_iov **pkiov, size_t *pkiov_count) +{ + return proto ? + kg_translate_iov_v3(context, dce_style, ec, rrc, enctype, + iov, iov_count, pkiov, pkiov_count) : + kg_translate_iov_v1(context, enctype, iov, iov_count, + pkiov, pkiov_count); +} + +krb5_error_code +kg_encrypt_iov(krb5_context context, int proto, int dce_style, size_t ec, + size_t rrc, krb5_key key, int usage, krb5_pointer iv, + gss_iov_buffer_desc *iov, int iov_count) +{ + krb5_error_code code; + krb5_data *state; + size_t kiov_len; + krb5_crypto_iov *kiov; + + code = iv_to_state(context, key, iv, &state); + if (code) + return code; + + code = kg_translate_iov(context, proto, dce_style, ec, rrc, + key->keyblock.enctype, iov, iov_count, + &kiov, &kiov_len); + if (code == 0) { + code = krb5_k_encrypt_iov(context, key, usage, state, kiov, kiov_len); + free(kiov); + } + + krb5_free_data(context, state); + return code; +} + +/* length is the length of the cleartext. */ + +krb5_error_code +kg_decrypt_iov(krb5_context context, int proto, int dce_style, size_t ec, + size_t rrc, krb5_key key, int usage, krb5_pointer iv, + gss_iov_buffer_desc *iov, int iov_count) +{ + krb5_error_code code; + krb5_data *state; + size_t kiov_len; + krb5_crypto_iov *kiov; + + code = iv_to_state(context, key, iv, &state); + if (code) + return code; + + code = kg_translate_iov(context, proto, dce_style, ec, rrc, + key->keyblock.enctype, iov, iov_count, + &kiov, &kiov_len); + if (code == 0) { + code = krb5_k_decrypt_iov(context, key, usage, state, kiov, kiov_len); + free(kiov); + } + + krb5_free_data(context, state); + return code; +} + +krb5_error_code +kg_arcfour_docrypt_iov(krb5_context context, const krb5_keyblock *keyblock, + int usage, const unsigned char *kd_data, + size_t kd_data_len, gss_iov_buffer_desc *iov, + int iov_count) +{ + krb5_error_code code; + krb5_data kd = make_data((char *) kd_data, kd_data_len); + krb5_crypto_iov *kiov = NULL; + size_t kiov_len = 0; + + code = kg_translate_iov(context, 0 /* proto */, 0 /* dce_style */, + 0 /* ec */, 0 /* rrc */, keyblock->enctype, + iov, iov_count, &kiov, &kiov_len); + if (code) + return code; + code = krb5int_arcfour_gsscrypt(keyblock, usage, &kd, kiov, kiov_len); + free(kiov); + return code; +} + +krb5_cryptotype +kg_translate_flag_iov(OM_uint32 type) +{ + krb5_cryptotype ktype; + + switch (GSS_IOV_BUFFER_TYPE(type)) { + case GSS_IOV_BUFFER_TYPE_DATA: + case GSS_IOV_BUFFER_TYPE_PADDING: + ktype = KRB5_CRYPTO_TYPE_DATA; + break; + case GSS_IOV_BUFFER_TYPE_SIGN_ONLY: + ktype = KRB5_CRYPTO_TYPE_SIGN_ONLY; + break; + default: + ktype = KRB5_CRYPTO_TYPE_EMPTY; + break; + } + + return ktype; +} + +gss_iov_buffer_t +kg_locate_iov(gss_iov_buffer_desc *iov, int iov_count, OM_uint32 type) +{ + int i; + gss_iov_buffer_t p = GSS_C_NO_IOV_BUFFER; + + if (iov == GSS_C_NO_IOV_BUFFER) + return GSS_C_NO_IOV_BUFFER; + + for (i = iov_count - 1; i >= 0; i--) { + if (GSS_IOV_BUFFER_TYPE(iov[i].type) == type) { + if (p == GSS_C_NO_IOV_BUFFER) + p = &iov[i]; + else + return GSS_C_NO_IOV_BUFFER; + } + } + + return p; +} + +/* Return the IOV where the GSSAPI token header should be placed (and possibly + * the checksum as well, depending on the token type). */ +gss_iov_buffer_t +kg_locate_header_iov(gss_iov_buffer_desc *iov, int iov_count, int toktype) +{ + if (toktype == KG_TOK_MIC_MSG) + return kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_MIC_TOKEN); + else + return kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); +} + +void +kg_iov_msglen(gss_iov_buffer_desc *iov, int iov_count, size_t *data_length_p, + size_t *assoc_data_length_p) +{ + int i; + size_t data_length = 0, assoc_data_length = 0; + + assert(iov != GSS_C_NO_IOV_BUFFER); + + *data_length_p = *assoc_data_length_p = 0; + + for (i = 0; i < iov_count; i++) { + OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[i].type); + + if (type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY) + assoc_data_length += iov[i].buffer.length; + + if (type == GSS_IOV_BUFFER_TYPE_DATA || + type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY) + data_length += iov[i].buffer.length; + } + + *data_length_p = data_length; + *assoc_data_length_p = assoc_data_length; +} + +void +kg_release_iov(gss_iov_buffer_desc *iov, int iov_count) +{ + int i; + + assert(iov != GSS_C_NO_IOV_BUFFER); + + for (i = 0; i < iov_count; i++) { + if (iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATED) { + gssalloc_free(iov[i].buffer.value); + iov[i].buffer.length = 0; + iov[i].buffer.value = NULL; + iov[i].type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED); + } + } +} + +OM_uint32 +kg_fixup_padding_iov(OM_uint32 *minor_status, gss_iov_buffer_desc *iov, + int iov_count) +{ + gss_iov_buffer_t padding = NULL; + gss_iov_buffer_t data = NULL; + size_t padlength, relative_padlength; + unsigned char *p; + + data = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_DATA); + padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + + if (data == NULL) { + *minor_status = 0; + return GSS_S_COMPLETE; + } + + if (padding == NULL || padding->buffer.length == 0) { + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + p = (unsigned char *)padding->buffer.value; + padlength = p[padding->buffer.length - 1]; + + if (data->buffer.length + padding->buffer.length < padlength || + padlength == 0) { + *minor_status = (OM_uint32)KRB5_BAD_MSIZE; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* + * kg_unseal_stream_iov() will place one byte of padding in the + * padding buffer; its true value is unknown until after decryption. + * + * relative_padlength contains the number of bytes to compensate the + * padding and data buffers by; it will be zero if the caller manages + * the padding length. + * + * If the caller manages the padding length, then relative_padlength + * wil be zero. + * + * eg. if the buffers are structured as follows: + * + * +---DATA---+-PAD-+ + * | ABCDE444 | 4 | + * +----------+-----+ + * + * after compensation they would look like: + * + * +-DATA--+-PAD--+ + * | ABCDE | NULL | + * +-------+------+ + */ + relative_padlength = padlength - padding->buffer.length; + + assert(data->buffer.length >= relative_padlength); + + data->buffer.length -= relative_padlength; + + kg_release_iov(padding, 1); + padding->buffer.length = 0; + padding->buffer.value = NULL; + + return GSS_S_COMPLETE; +} + +krb5_boolean +kg_integ_only_iov(gss_iov_buffer_desc *iov, int iov_count) +{ + int i; + krb5_boolean has_conf_data = FALSE; + + assert(iov != GSS_C_NO_IOV_BUFFER); + + for (i = 0; i < iov_count; i++) { + if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA) { + has_conf_data = TRUE; + break; + } + } + + return (has_conf_data == FALSE); +} + +krb5_error_code +kg_allocate_iov(gss_iov_buffer_t iov, size_t size) +{ + assert(iov != GSS_C_NO_IOV_BUFFER); + assert(iov->type & GSS_IOV_BUFFER_FLAG_ALLOCATE); + + iov->buffer.length = size; + iov->buffer.value = gssalloc_malloc(size); + if (iov->buffer.value == NULL) { + iov->buffer.length = 0; + return ENOMEM; + } + + iov->type |= GSS_IOV_BUFFER_FLAG_ALLOCATED; + + return 0; +} diff --git a/src/lib/gssapi/krb5/util_seed.c b/src/lib/gssapi/krb5/util_seed.c new file mode 100644 index 000000000000..6e1c9ac8aecb --- /dev/null +++ b/src/lib/gssapi/krb5/util_seed.c @@ -0,0 +1,61 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +static const unsigned char zeros[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; + +krb5_error_code +kg_make_seed(context, key, seed) + krb5_context context; + krb5_key key; + unsigned char *seed; +{ + krb5_error_code code; + krb5_key rkey = NULL; + krb5_keyblock *tmpkey, *kb; + unsigned int i; + + code = krb5_k_key_keyblock(context, key, &tmpkey); + if (code) + return(code); + + /* reverse the key bytes, as per spec */ + kb = &key->keyblock; + for (i=0; i<tmpkey->length; i++) + tmpkey->contents[i] = kb->contents[kb->length - 1 - i]; + + code = krb5_k_create_key(context, tmpkey, &rkey); + if (code) + goto cleanup; + + code = kg_encrypt(context, rkey, KG_USAGE_SEAL, NULL, zeros, seed, 16); + +cleanup: + krb5_free_keyblock(context, tmpkey); + krb5_k_free_key(context, rkey); + return(code); +} diff --git a/src/lib/gssapi/krb5/util_seqnum.c b/src/lib/gssapi/krb5/util_seqnum.c new file mode 100644 index 000000000000..bef631da9d1a --- /dev/null +++ b/src/lib/gssapi/krb5/util_seqnum.c @@ -0,0 +1,102 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2001, 2009 by the Massachusetts Institute of Technology. + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" +#include "k5-int.h" + +/* + * $Id$ + */ + +krb5_error_code +kg_make_seq_num(context, key, direction, seqnum, cksum, buf) + krb5_context context; + krb5_key key; + int direction; + krb5_ui_4 seqnum; + unsigned char *cksum; + unsigned char *buf; +{ + unsigned char plain[8]; + + plain[4] = direction; + plain[5] = direction; + plain[6] = direction; + plain[7] = direction; + if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC || + key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { + /* Yes, Microsoft used big-endian sequence number.*/ + store_32_be(seqnum, plain); + return kg_arcfour_docrypt (&key->keyblock, 0, + cksum, 8, + &plain[0], 8, + buf); + + } + + store_32_le(seqnum, plain); + return(kg_encrypt(context, key, KG_USAGE_SEQ, cksum, plain, buf, 8)); +} + +krb5_error_code kg_get_seq_num(context, key, cksum, buf, direction, seqnum) + krb5_context context; + krb5_key key; + unsigned char *cksum; + unsigned char *buf; + int *direction; + krb5_ui_4 *seqnum; +{ + krb5_error_code code; + unsigned char plain[8]; + + if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC || + key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { + code = kg_arcfour_docrypt (&key->keyblock, 0, + cksum, 8, + buf, 8, + plain); + } else { + code = kg_decrypt(context, key, KG_USAGE_SEQ, cksum, buf, plain, 8); + } + if (code) + return(code); + + if ((plain[4] != plain[5]) || + (plain[4] != plain[6]) || + (plain[4] != plain[7])) + return((krb5_error_code) KG_BAD_SEQ); + + *direction = plain[4]; + if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC || + key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { + *seqnum = (plain[3]|(plain[2]<<8) | (plain[1]<<16)| (plain[0]<<24)); + } else { + *seqnum = ((plain[0]) | + (plain[1]<<8) | + (plain[2]<<16) | + (plain[3]<<24)); + } + + return(0); +} diff --git a/src/lib/gssapi/krb5/val_cred.c b/src/lib/gssapi/krb5/val_cred.c new file mode 100644 index 000000000000..cb1cb9393acf --- /dev/null +++ b/src/lib/gssapi/krb5/val_cred.c @@ -0,0 +1,83 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1997, 2007 by Massachusetts Institute of Technology + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "gssapiP_krb5.h" + +/* Check to see whether or not a GSSAPI krb5 credential is valid. If + * it is not, return an error. */ + +OM_uint32 +krb5_gss_validate_cred_1(OM_uint32 *minor_status, gss_cred_id_t cred_handle, + krb5_context context) +{ + krb5_gss_cred_id_t cred; + krb5_error_code code; + krb5_principal princ; + + cred = (krb5_gss_cred_id_t) cred_handle; + k5_mutex_lock(&cred->lock); + + if (cred->ccache && cred->expire != 0) { + if ((code = krb5_cc_get_principal(context, cred->ccache, &princ))) { + k5_mutex_unlock(&cred->lock); + *minor_status = code; + return(GSS_S_DEFECTIVE_CREDENTIAL); + } + if (!krb5_principal_compare(context, princ, cred->name->princ)) { + k5_mutex_unlock(&cred->lock); + *minor_status = KG_CCACHE_NOMATCH; + return(GSS_S_DEFECTIVE_CREDENTIAL); + } + (void)krb5_free_principal(context, princ); + } + *minor_status = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +krb5_gss_validate_cred(minor_status, cred_handle) + OM_uint32 *minor_status; + gss_cred_id_t cred_handle; +{ + krb5_context context; + krb5_error_code code; + OM_uint32 maj; + + code = krb5_gss_init_context(&context); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + maj = krb5_gss_validate_cred_1(minor_status, cred_handle, context); + if (maj == 0) { + krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t) cred_handle; + k5_mutex_assert_locked(&cred->lock); + k5_mutex_unlock(&cred->lock); + } + save_error_info(*minor_status, context); + krb5_free_context(context); + return maj; +} diff --git a/src/lib/gssapi/krb5/wrap_size_limit.c b/src/lib/gssapi/krb5/wrap_size_limit.c new file mode 100644 index 000000000000..7959f424ec96 --- /dev/null +++ b/src/lib/gssapi/krb5/wrap_size_limit.c @@ -0,0 +1,178 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2000 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "gssapiP_krb5.h" + +/* V2 interface */ +OM_uint32 KRB5_CALLCONV +krb5_gss_wrap_size_limit(minor_status, context_handle, conf_req_flag, + qop_req, req_output_size, max_input_size) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + int conf_req_flag; + gss_qop_t qop_req; + OM_uint32 req_output_size; + OM_uint32 *max_input_size; +{ + krb5_gss_ctx_id_rec *ctx; + OM_uint32 data_size, conflen; + OM_uint32 ohlen; + int overhead; + + /* only default qop is allowed */ + if (qop_req != GSS_C_QOP_DEFAULT) { + *minor_status = (OM_uint32) G_UNKNOWN_QOP; + return GSS_S_BAD_QOP; + } + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + if (ctx->terminated || !ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + if (ctx->proto == 1) { + /* No pseudo-ASN.1 wrapper overhead, so no sequence length and + OID. */ + OM_uint32 sz = req_output_size; + + /* Token header: 16 octets. */ + if (conf_req_flag) { + krb5_key key; + krb5_enctype enctype; + + key = ctx->have_acceptor_subkey ? ctx->acceptor_subkey + : ctx->subkey; + enctype = key->keyblock.enctype; + + while (sz > 0 && krb5_encrypt_size(sz, enctype) + 16 > req_output_size) + sz--; + /* Allow for encrypted copy of header. */ + if (sz > 16) + sz -= 16; + else + sz = 0; +#ifdef CFX_EXERCISE + /* Allow for EC padding. In the MIT implementation, only + added while testing. */ + if (sz > 65535) + sz -= 65535; + else + sz = 0; +#endif + } else { + krb5_cksumtype cksumtype; + krb5_error_code err; + size_t cksumsize; + + cksumtype = ctx->have_acceptor_subkey ? ctx->acceptor_subkey_cksumtype + : ctx->cksumtype; + + err = krb5_c_checksum_length(ctx->k5_context, cksumtype, &cksumsize); + if (err) { + *minor_status = err; + return GSS_S_FAILURE; + } + + /* Allow for token header and checksum. */ + if (sz < 16 + cksumsize) + sz = 0; + else + sz -= (16 + cksumsize); + } + + *max_input_size = sz; + *minor_status = 0; + return GSS_S_COMPLETE; + } + + /* Calculate the token size and subtract that from the output size */ + overhead = 7 + ctx->mech_used->length; + data_size = req_output_size; + conflen = kg_confounder_size(ctx->k5_context, ctx->enc->keyblock.enctype); + data_size = (conflen + data_size + 8) & (~(OM_uint32)7); + ohlen = g_token_size(ctx->mech_used, + (unsigned int) (data_size + ctx->cksum_size + 14)) + - req_output_size; + + if (ohlen+overhead < req_output_size) + /* + * Cannot have trailer length that will cause us to pad over our + * length. + */ + *max_input_size = (req_output_size - ohlen - overhead) & (~(OM_uint32)7); + else + *max_input_size = 0; + + *minor_status = 0; + return(GSS_S_COMPLETE); +} |
