From: Luke Howard Date: Sun, 2 Jan 2011 08:57:19 +0000 (+1100) Subject: Merge branch 'master' of ssh://moonshot.suchdamage.org:822/srv/git/moonshot X-Git-Tag: vm/20110310~67 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mech_eap.git;a=commitdiff_plain;h=7db57acddeddad5f96d16288b3776baf6c10c0b1;hp=c21aba70c31e6b716107624d5a169012e0df5602 Merge branch 'master' of ssh://moonshot.suchdamage.org:822/srv/git/moonshot Reauth fixes Conflicts: shibboleth/opensaml2 shibboleth/sp --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..06a3924 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +/aclocal.m4 +/autom4te.cache +/compile +/config.guess +/config.log +/config.status +/config.sub +/config.h +/configure +/config.h.in +/depcomp + +/libtool +/ltmain.sh +/missing + +/gsseap_err.[ch] +/radsec_err.[ch] + +.DS_Store + +Makefile.in +Makefile + +*.la +*.lo +*~ + +.deps +.libs +.a.out.dSYM +.dSYM diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..83bbb33 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +mech_eap was written by Luek Howard . + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..106e690 --- /dev/null +++ b/COPYING @@ -0,0 +1,3 @@ +Copyright (c) 2010 JANET(UK) + +See the LICENSE file for licensing terms. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8fb8504 --- /dev/null +++ b/LICENSE @@ -0,0 +1,35 @@ +Note: this license becomes effective upon execution of a contract between +PADL Software Pty Ltd and JANET(UK) for implementation of the GSS EAP +mechanism. + +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..533a2d4 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,121 @@ +AUTOMAKE_OPTIONS = foreign + +gssincludedir = $(includedir)/gssapi +gssinclude_HEADERS = gssapi_eap.h + +gssdir = $(libdir)/gss +gss_LTLIBRARIES = mech_eap.la + +mech_eap_la_CPPFLAGS = -DBUILD_GSSEAP_LIB -DSYSCONFDIR=\"${sysconfdir}\" -DDATAROOTDIR=\"${datarootdir}\" +mech_eap_la_CFLAGS = -g -Wall -fno-strict-aliasing \ + @KRB5_CFLAGS@ @EAP_CFLAGS@ @RADSEC_CFLAGS@ @TARGET_CFLAGS@ +mech_eap_la_CXXFLAGS = -g -Wall \ + @KRB5_CFLAGS@ @EAP_CFLAGS@ @RADSEC_CFLAGS@ \ + @SHIBSP_CXXFLAGS@ @SHIBRESOLVER_CXXFLAGS@ @TARGET_CFLAGS@ +mech_eap_la_LDFLAGS = -avoid-version -module \ + -export-symbols mech_eap.exports -no-undefined \ + @EAP_LDFLAGS@ @RADSEC_LDFLAGS@ @TARGET_LDFLAGS@ +mech_eap_la_LIBADD = @KRB5_LIBS@ @EAP_LIBS@ @RADSEC_LIBS@ @SHIBSP_LIBS@ \ + @SHIBRESOLVER_LIBS@ + +mech_eap_la_SOURCES = \ + accept_sec_context.c \ + acquire_cred.c \ + acquire_cred_with_password.c \ + add_cred.c \ + add_cred_with_password.c \ + canonicalize_name.c \ + compare_name.c \ + context_time.c \ + delete_name_attribute.c \ + delete_sec_context.c \ + display_name.c \ + display_name_ext.c \ + display_status.c \ + duplicate_name.c \ + eap_mech.c \ + export_name.c \ + export_name_composite.c \ + export_sec_context.c \ + get_mic.c \ + get_name_attribute.c \ + gsseap_err.c \ + import_name.c \ + import_sec_context.c \ + indicate_mechs.c \ + init_sec_context.c \ + inquire_attrs_for_mech.c \ + inquire_context.c \ + inquire_cred.c \ + inquire_cred_by_oid.c \ + inquire_mech_for_saslname.c \ + inquire_mechs_for_name.c \ + inquire_name.c \ + inquire_names_for_mech.c \ + inquire_saslname_for_mech.c \ + inquire_sec_context_by_oid.c \ + map_name_to_any.c \ + process_context_token.c \ + pseudo_random.c \ + radsec_err.c \ + release_any_name_mapping.c \ + release_cred.c \ + release_name.c \ + release_oid.c \ + set_name_attribute.c \ + set_cred_option.c \ + set_sec_context_option.c \ + store_cred.c \ + unwrap.c \ + unwrap_iov.c \ + util_attr.cpp \ + util_buffer.c \ + util_context.c \ + util_cksum.c \ + util_cred.c \ + util_crypt.c \ + util_exts.c \ + util_krb.c \ + util_lucid.c \ + util_mech.c \ + util_name.c \ + util_oid.c \ + util_ordering.c \ + util_radius.cpp \ + util_saml.cpp \ + util_shib.cpp \ + util_token.c \ + verify_mic.c \ + wrap.c \ + wrap_iov.c \ + wrap_iov_length.c \ + wrap_size_limit.c + +if GSSEAP_ENABLE_REAUTH +mech_eap_la_SOURCES += util_reauth.c + +if !HEIMDAL +krb5pluginsdir = $(libdir)/krb5/plugins/authdata +krb5plugins_LTLIBRARIES = radius_ad.la + +radius_ad_la_CFLAGS = -g -Wall -fno-strict-aliasing \ + @EAP_CFLAGS@ @KRB5_CFLAGS@ @TARGET_CFLAGS@ +radius_ad_la_LDFLAGS = -avoid-version -module \ + -export-symbols radius_ad.exports -no-undefined +radius_ad_la_LIBADD = @KRB5_LIBS@ +radius_ad_la_SOURCES = util_adshim.c +endif +endif + +gsseap_err.h: gsseap_err.et + $(COMPILE_ET) $< + +gsseap_err.c: gsseap_err.h + +radsec_err.h: radsec_err.et + $(COMPILE_ET) $< + +radsec_err.c: radsec_err.h + +clean-generic: + rm -f gsseap_err.[ch] radsec_err.[ch] diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/NOTES b/NOTES new file mode 100644 index 0000000..cce432f --- /dev/null +++ b/NOTES @@ -0,0 +1,2 @@ +- gss_xxx routines acquire lock, gssXxx don't + diff --git a/README b/README new file mode 100644 index 0000000..9955646 --- /dev/null +++ b/README @@ -0,0 +1,42 @@ +Overview +======== + +This is an implementation of the GSS EAP mechanism, as described in +draft-ietf-abfab-gss-eap-00.txt. + +Building +======== + +In order to build this, a recent Kerberos implementation (MIT or +Heimdal), Shibboleth, and EAP libraries are required, along with +all of their dependencies. + +Note: not all SPIs are supported by the Heimdal mechanism glue, +so not all features will be available. + +Installing +========== + +When installing, be sure to edit $prefix/etc/gss/mech to register +the EAP mechanisms. A sample configuration file is in this directory. + +Make sure your RADIUS library is configured to talk to the server of +your choice: see the example radsec.conf in this directory. + +Testing +======= + +You can then test the MIT or Cyrus GSS and SASL example programs. +Sample usage is given below. Substitute , and +appropriately ( is the name of the host running the server, +not the RADIUS server). + +% gss-client -port 5555 -spnego -mech "{1 3 6 1 4 1 5322 22 1 18}" \ + -user -pass host@ "Testing GSS EAP" +% gss-server -port 5555 -export host@ + +Note: for SASL you will be prompted for a username and password. + +% client -C -p 5556 -s host -m EAP-AES128 +% server -c -p 5556 -s host -h + diff --git a/TODO b/TODO new file mode 100644 index 0000000..205440e --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +- integration with initiator-side EAP channel bindings +- integration with final supplicant architecture +- test Heimdal port diff --git a/accept_sec_context.c b/accept_sec_context.c new file mode 100644 index 0000000..bbce729 --- /dev/null +++ b/accept_sec_context.c @@ -0,0 +1,783 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Establish a security context on the acceptor (server). These functions + * wrap around libradsec and (thus) talk to a RADIUS server or proxy. + */ + +#include "gssapiP_eap.h" + +#ifdef GSSEAP_ENABLE_REAUTH +static OM_uint32 +eapGssSmAcceptGssReauth(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t inputToken, + gss_channel_bindings_t chanBindings, + gss_buffer_t outputToken); +#endif + +/* + * Mark an acceptor context as ready for cryptographic operations + */ +static OM_uint32 +acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred) +{ + OM_uint32 major, tmpMinor; + VALUE_PAIR *vp; + gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER; + + /* Cache encryption type derived from selected mechanism OID */ + major = gssEapOidToEnctype(minor, ctx->mechanismUsed, + &ctx->encryptionType); + if (GSS_ERROR(major)) + return major; + + gssEapReleaseName(&tmpMinor, &ctx->initiatorName); + + major = gssEapRadiusGetRawAvp(minor, ctx->acceptorCtx.vps, + PW_USER_NAME, 0, &vp); + if (major == GSS_S_COMPLETE) { + nameBuf.length = vp->length; + nameBuf.value = vp->vp_strvalue; + } else { + ctx->gssFlags |= GSS_C_ANON_FLAG; + } + + major = gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME, + &ctx->initiatorName); + if (GSS_ERROR(major)) + return major; + + major = gssEapRadiusGetRawAvp(minor, ctx->acceptorCtx.vps, + PW_MS_MPPE_SEND_KEY, VENDORPEC_MS, &vp); + if (GSS_ERROR(major)) { + *minor = GSSEAP_KEY_UNAVAILABLE; + return GSS_S_UNAVAILABLE; + } + + major = gssEapDeriveRfc3961Key(minor, + vp->vp_octets, + vp->length, + ctx->encryptionType, + &ctx->rfc3961Key); + if (GSS_ERROR(major)) + return major; + + major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key, + &ctx->checksumType); + if (GSS_ERROR(major)) + return major; + + major = sequenceInit(minor, + &ctx->seqState, ctx->recvSeq, + ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0), + ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0), + TRUE); + if (GSS_ERROR(major)) + return major; + + major = gssEapCreateAttrContext(minor, cred, ctx, + &ctx->initiatorName->attrCtx, + &ctx->expiryTime); + if (GSS_ERROR(major)) + return major; + + *minor = 0; + return GSS_S_COMPLETE; +} + +/* + * Emit a identity EAP request to force the initiator (peer) to identify + * itself. + */ +static OM_uint32 +eapGssSmAcceptIdentity(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t inputToken, + gss_channel_bindings_t chanBindings, + gss_buffer_t outputToken) +{ + OM_uint32 major; + union { + struct eap_hdr pdu; + unsigned char data[5]; + } pkt; + gss_buffer_desc pktBuffer; + + if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) { + *minor = GSSEAP_WRONG_SIZE; + return GSS_S_DEFECTIVE_TOKEN; + } + + assert(ctx->acceptorName == GSS_C_NO_NAME); + + if (cred->name != GSS_C_NO_NAME) { + major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName); + if (GSS_ERROR(major)) + return major; + } + + pkt.pdu.code = EAP_CODE_REQUEST; + pkt.pdu.identifier = 0; + pkt.pdu.length = htons(sizeof(pkt.data)); + pkt.data[4] = EAP_TYPE_IDENTITY; + + pktBuffer.length = sizeof(pkt.data); + pktBuffer.value = pkt.data; + + major = duplicateBuffer(minor, &pktBuffer, outputToken); + if (GSS_ERROR(major)) + return major; + + ctx->state = GSSEAP_STATE_AUTHENTICATE; + + *minor = 0; + return GSS_S_CONTINUE_NEEDED; +} + +/* + * Pass the asserted acceptor identity to the authentication server. + */ +static OM_uint32 +setAcceptorIdentity(OM_uint32 *minor, + gss_ctx_id_t ctx, + VALUE_PAIR **vps) +{ + OM_uint32 major; + gss_buffer_desc nameBuf; + krb5_context krbContext = NULL; + krb5_principal krbPrinc; + struct rs_context *rc = ctx->acceptorCtx.radContext; + + assert(rc != NULL); + + if (ctx->acceptorName == GSS_C_NO_NAME) { + *minor = 0; + return GSS_S_COMPLETE; + } + + if ((ctx->acceptorName->flags & NAME_FLAG_SERVICE) == 0) { + *minor = GSSEAP_BAD_SERVICE_NAME; + return GSS_S_BAD_NAME; + } + + GSSEAP_KRB_INIT(&krbContext); + + krbPrinc = ctx->acceptorName->krbPrincipal; + assert(krbPrinc != NULL); + assert(KRB_PRINC_LENGTH(krbPrinc) >= 2); + + /* Acceptor-Service-Name */ + krbPrincComponentToGssBuffer(krbPrinc, 0, &nameBuf); + + major = gssEapRadiusAddAvp(minor, vps, + PW_GSS_ACCEPTOR_SERVICE_NAME, + VENDORPEC_UKERNA, + &nameBuf); + if (GSS_ERROR(major)) + return major; + + /* Acceptor-Host-Name */ + krbPrincComponentToGssBuffer(krbPrinc, 1, &nameBuf); + + major = gssEapRadiusAddAvp(minor, vps, + PW_GSS_ACCEPTOR_HOST_NAME, + VENDORPEC_UKERNA, + &nameBuf); + if (GSS_ERROR(major)) + return major; + + if (KRB_PRINC_LENGTH(krbPrinc) > 2) { + /* Acceptor-Service-Specific */ + krb5_principal_data ssiPrinc = *krbPrinc; + char *ssi; + + KRB_PRINC_LENGTH(&ssiPrinc) -= 2; + KRB_PRINC_NAME(&ssiPrinc) += 2; + + *minor = krb5_unparse_name_flags(krbContext, &ssiPrinc, + KRB5_PRINCIPAL_UNPARSE_NO_REALM, &ssi); + if (*minor != 0) + return GSS_S_FAILURE; + + nameBuf.value = ssi; + nameBuf.length = strlen(ssi); + + major = gssEapRadiusAddAvp(minor, vps, + PW_GSS_ACCEPTOR_SERVICE_SPECIFIC, + VENDORPEC_UKERNA, + &nameBuf); + + if (GSS_ERROR(major)) { + krb5_free_unparsed_name(krbContext, ssi); + return major; + } + krb5_free_unparsed_name(krbContext, ssi); + } + + krbPrincRealmToGssBuffer(krbPrinc, &nameBuf); + if (nameBuf.length != 0) { + /* Acceptor-Realm-Name */ + major = gssEapRadiusAddAvp(minor, vps, + PW_GSS_ACCEPTOR_REALM_NAME, + VENDORPEC_UKERNA, + &nameBuf); + if (GSS_ERROR(major)) + return major; + } + + *minor = 0; + return GSS_S_COMPLETE; +} + +/* + * Allocate a RadSec handle + */ +static OM_uint32 +createRadiusHandle(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx) +{ + struct gss_eap_acceptor_ctx *actx = &ctx->acceptorCtx; + const char *configFile = RS_CONFIG_FILE; + const char *configStanza = "gss-eap"; + struct rs_alloc_scheme ralloc; + struct rs_error *err; + + assert(actx->radContext == NULL); + assert(actx->radConn == NULL); + + if (rs_context_create(&actx->radContext, RS_DICT_FILE) != 0) { + *minor = GSSEAP_RADSEC_CONTEXT_FAILURE; + return GSS_S_FAILURE; + } + + if (cred->radiusConfigFile != NULL) + configFile = cred->radiusConfigFile; + if (cred->radiusConfigStanza != NULL) + configStanza = cred->radiusConfigStanza; + + ralloc.calloc = GSSEAP_CALLOC; + ralloc.malloc = GSSEAP_MALLOC; + ralloc.free = GSSEAP_FREE; + ralloc.realloc = GSSEAP_REALLOC; + + rs_context_set_alloc_scheme(actx->radContext, &ralloc); + + if (rs_context_read_config(actx->radContext, configFile) != 0) { + err = rs_err_ctx_pop(actx->radContext); + goto fail; + } + + if (rs_conn_create(actx->radContext, &actx->radConn, configStanza) != 0) { + err = rs_err_conn_pop(actx->radConn); + goto fail; + } + + if (actx->radServer != NULL) { + if (rs_conn_select_server(actx->radConn, actx->radServer) != 0) { + err = rs_err_conn_pop(actx->radConn); + goto fail; + } + } + + *minor = 0; + return GSS_S_COMPLETE; + +fail: + return gssEapRadiusMapError(minor, err); +} + +/* + * Process a EAP response from the initiator. + */ +static OM_uint32 +eapGssSmAcceptAuthenticate(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t inputToken, + gss_channel_bindings_t chanBindings, + gss_buffer_t outputToken) +{ + OM_uint32 major, tmpMinor; + struct rs_connection *rconn; + struct rs_request *request = NULL; + struct rs_packet *req = NULL, *resp = NULL; + struct radius_packet *frreq, *frresp; + int sendAcceptorIdentity = 0; + + if (ctx->acceptorCtx.radContext == NULL) { + /* May be NULL from an imported partial context */ + major = createRadiusHandle(minor, cred, ctx); + if (GSS_ERROR(major)) + goto cleanup; + + sendAcceptorIdentity = 1; + } + + rconn = ctx->acceptorCtx.radConn; + + if (rs_packet_create_acc_request(rconn, &req, NULL, NULL) != 0) { + major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn)); + goto cleanup; + } + frreq = rs_packet_frpkt(req); + + if (sendAcceptorIdentity) { + major = setAcceptorIdentity(minor, ctx, &frreq->vps); + if (GSS_ERROR(major)) + goto cleanup; + } + + major = gssEapRadiusAddAvp(minor, &frreq->vps, + PW_EAP_MESSAGE, 0, inputToken); + if (GSS_ERROR(major)) + goto cleanup; + + if (ctx->acceptorCtx.state.length != 0) { + major = gssEapRadiusAddAvp(minor, &frreq->vps, PW_STATE, 0, + &ctx->acceptorCtx.state); + if (GSS_ERROR(major)) + goto cleanup; + + gss_release_buffer(&tmpMinor, &ctx->acceptorCtx.state); + } + + if (rs_request_create(rconn, &request) != 0 || + rs_request_send(request, req, &resp) != 0) { + major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn)); + goto cleanup; + } + + assert(resp != NULL); + + frresp = rs_packet_frpkt(resp); + switch (frresp->code) { + case PW_AUTHENTICATION_ACK: + case PW_ACCESS_CHALLENGE: + major = GSS_S_CONTINUE_NEEDED; + break; + case PW_AUTHENTICATION_REJECT: + *minor = GSSEAP_RADIUS_AUTH_FAILURE; + major = GSS_S_DEFECTIVE_CREDENTIAL; + goto cleanup; + break; + default: + *minor = GSSEAP_UNKNOWN_RADIUS_CODE; + major = GSS_S_FAILURE; + goto cleanup; + break; + } + + major = gssEapRadiusGetAvp(minor, frresp->vps, PW_EAP_MESSAGE, 0, + outputToken, TRUE); + if (major == GSS_S_UNAVAILABLE && frresp->code == PW_ACCESS_CHALLENGE) { + *minor = GSSEAP_MISSING_EAP_REQUEST; + major = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } else if (GSS_ERROR(major)) + goto cleanup; + + if (frresp->code == PW_ACCESS_CHALLENGE) { + major = gssEapRadiusGetAvp(minor, frresp->vps, PW_STATE, 0, + &ctx->acceptorCtx.state, TRUE); + if (GSS_ERROR(major) && *minor != GSSEAP_NO_SUCH_ATTR) + goto cleanup; + } else { + ctx->acceptorCtx.vps = frresp->vps; + frresp->vps = NULL; + + rs_conn_destroy(ctx->acceptorCtx.radConn); + ctx->acceptorCtx.radConn = NULL; + + major = acceptReadyEap(minor, ctx, cred); + if (GSS_ERROR(major)) + goto cleanup; + + ctx->state = GSSEAP_STATE_EXTENSIONS_REQ; + } + + *minor = 0; + major = GSS_S_CONTINUE_NEEDED; + +cleanup: + rs_request_destroy(request); + + return major; +} + +static OM_uint32 +eapGssSmAcceptExtensionsReq(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t inputToken, + gss_channel_bindings_t chanBindings, + gss_buffer_t outputToken) +{ + OM_uint32 major; + + major = gssEapVerifyExtensions(minor, cred, ctx, chanBindings, inputToken); + if (GSS_ERROR(major)) + return major; + + outputToken->length = 0; + outputToken->value = NULL; + + ctx->state = GSSEAP_STATE_EXTENSIONS_RESP; + + *minor = 0; + return GSS_S_CONTINUE_NEEDED; +} + +static OM_uint32 +eapGssSmAcceptExtensionsResp(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t inputToken, + gss_channel_bindings_t chanBindings, + gss_buffer_t outputToken) +{ + OM_uint32 major; + + major = gssEapMakeExtensions(minor, cred, ctx, chanBindings, outputToken); + if (GSS_ERROR(major)) + return major; + + ctx->state = GSSEAP_STATE_ESTABLISHED; + + *minor = 0; + return GSS_S_COMPLETE; +} + +static OM_uint32 +eapGssSmAcceptEstablished(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t inputToken, + gss_channel_bindings_t chanBindings, + gss_buffer_t outputToken) +{ + /* Called with already established context */ + *minor = GSSEAP_CONTEXT_ESTABLISHED; + return GSS_S_BAD_STATUS; +} + +static OM_uint32 +makeErrorToken(OM_uint32 *minor, + OM_uint32 majorStatus, + OM_uint32 minorStatus, + gss_buffer_t outputToken) +{ + unsigned char errorData[8]; + gss_buffer_desc errorBuffer; + + assert(GSS_ERROR(majorStatus)); + + /* + * Only return error codes that the initiator could have caused, + * to avoid information leakage. + */ + if (IS_RADIUS_ERROR(minorStatus)) { + /* Squash RADIUS error codes */ + minorStatus = GSSEAP_RADIUS_PROT_FAILURE; + } else if (!IS_WIRE_ERROR(minorStatus)) { + /* Don't return non-wire error codes */ + return GSS_S_COMPLETE; + } + + minorStatus -= ERROR_TABLE_BASE_eapg; + + store_uint32_be(majorStatus, &errorData[0]); + store_uint32_be(minorStatus, &errorData[4]); + + errorBuffer.length = sizeof(errorData); + errorBuffer.value = errorData; + + return duplicateBuffer(minor, &errorBuffer, outputToken); +} + +static struct gss_eap_acceptor_sm { + enum gss_eap_token_type inputTokenType; + enum gss_eap_token_type outputTokenType; + OM_uint32 (*processToken)(OM_uint32 *, + gss_ctx_id_t, + gss_cred_id_t, + gss_buffer_t, + gss_channel_bindings_t, + gss_buffer_t); +} eapGssAcceptorSm[] = { + { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, eapGssSmAcceptIdentity }, + { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, eapGssSmAcceptAuthenticate }, + { TOK_TYPE_EXT_REQ, TOK_TYPE_NONE, eapGssSmAcceptExtensionsReq }, + { TOK_TYPE_NONE, TOK_TYPE_EXT_RESP, eapGssSmAcceptExtensionsResp }, + { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmAcceptEstablished }, + { TOK_TYPE_NONE, TOK_TYPE_CONTEXT_ERR, NULL }, +#ifdef GSSEAP_ENABLE_REAUTH + { TOK_TYPE_GSS_REAUTH, TOK_TYPE_GSS_REAUTH, eapGssSmAcceptGssReauth }, +#endif +}; + +OM_uint32 +gss_accept_sec_context(OM_uint32 *minor, + gss_ctx_id_t *context_handle, + gss_cred_id_t cred, + 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; + OM_uint32 tmpMajor, tmpMinor; + gss_ctx_id_t ctx = *context_handle; + struct gss_eap_acceptor_sm *sm = NULL; + gss_buffer_desc innerInputToken = GSS_C_EMPTY_BUFFER; + gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER; + enum gss_eap_token_type tokType; + int initialContextToken = 0; + + *minor = 0; + + output_token->length = 0; + output_token->value = NULL; + + if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (ctx == GSS_C_NO_CONTEXT) { + major = gssEapAllocContext(minor, &ctx); + if (GSS_ERROR(major)) + return major; + + initialContextToken = 1; + *context_handle = ctx; + } + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (cred == GSS_C_NO_CREDENTIAL) { + if (ctx->defaultCred == GSS_C_NO_CREDENTIAL) { + major = gssEapAcquireCred(minor, + GSS_C_NO_NAME, + GSS_C_NO_BUFFER, + GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, + GSS_C_ACCEPT, + &ctx->defaultCred, + NULL, + NULL); + if (GSS_ERROR(major)) + goto cleanup; + } + + cred = ctx->defaultCred; + } + + GSSEAP_MUTEX_LOCK(&cred->mutex); + + sm = &eapGssAcceptorSm[ctx->state]; + + major = gssEapVerifyToken(minor, ctx, input_token, + &tokType, &innerInputToken); + if (GSS_ERROR(major)) + goto cleanup; + + if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) { + *minor = GSSEAP_CRED_MECH_MISMATCH; + major = GSS_S_BAD_MECH; + goto cleanup; + } + +#ifdef GSSEAP_ENABLE_REAUTH + /* + * If we're built with fast reauthentication support, it's valid + * for an initiator to send a GSS reauthentication token as its + * initial context token, causing us to short-circuit the state + * machine and process Kerberos GSS messages instead. + */ + if (tokType == TOK_TYPE_GSS_REAUTH && initialContextToken) { + ctx->state = GSSEAP_STATE_KRB_REAUTH; + } else +#endif + if (tokType != sm->inputTokenType) { + *minor = GSSEAP_WRONG_TOK_ID; + major = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + + do { + sm = &eapGssAcceptorSm[ctx->state]; + + major = (sm->processToken)(minor, + ctx, + cred, + &innerInputToken, + input_chan_bindings, + &innerOutputToken); + if (GSS_ERROR(major)) { + /* Possibly generate an error token */ + tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputToken); + if (GSS_ERROR(tmpMajor)) { + major = tmpMajor; + goto cleanup; + } + + sm = &eapGssAcceptorSm[GSSEAP_STATE_ERROR]; + goto send_token; + } + } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.length == 0); + + if (mech_type != NULL) { + if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type)) + duplicateOid(&tmpMinor, ctx->mechanismUsed, mech_type); + } + if (ret_flags != NULL) + *ret_flags = ctx->gssFlags; + if (delegated_cred_handle != NULL) + *delegated_cred_handle = GSS_C_NO_CREDENTIAL; + + if (major == GSS_S_COMPLETE) { + if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) { + major = gssEapDuplicateName(&tmpMinor, ctx->initiatorName, src_name); + if (GSS_ERROR(major)) + goto cleanup; + } + if (time_rec != NULL) { + major = gssEapContextTime(&tmpMinor, ctx, time_rec); + if (GSS_ERROR(major)) + goto cleanup; + } + } + + assert(ctx->state == GSSEAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED); + +send_token: + if (innerOutputToken.value != NULL) { + tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken, + sm->outputTokenType, output_token); + if (GSS_ERROR(tmpMajor)) { + major = tmpMajor; + *minor = tmpMinor; + goto cleanup; + } + } + +cleanup: + if (cred != GSS_C_NO_CREDENTIAL) + GSSEAP_MUTEX_UNLOCK(&cred->mutex); + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + if (GSS_ERROR(major)) + gssEapReleaseContext(&tmpMinor, context_handle); + + gss_release_buffer(&tmpMinor, &innerOutputToken); + + return major; +} + +#ifdef GSSEAP_ENABLE_REAUTH +static OM_uint32 +acceptReadyKrb(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + const gss_name_t initiator, + const gss_OID mech, + OM_uint32 timeRec) +{ + OM_uint32 major; + + major = gssEapGlueToMechName(minor, ctx, initiator, &ctx->initiatorName); + if (GSS_ERROR(major)) + return major; + + if (cred->name != GSS_C_NO_NAME) { + major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName); + if (GSS_ERROR(major)) + return major; + } + + major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec); + if (GSS_ERROR(major)) + return major; + + ctx->state = GSSEAP_STATE_ESTABLISHED; + + *minor = 0; + return GSS_S_COMPLETE; +} + +static OM_uint32 +eapGssSmAcceptGssReauth(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t inputToken, + gss_channel_bindings_t chanBindings, + gss_buffer_t outputToken) +{ + OM_uint32 major, tmpMinor; + gss_name_t krbInitiator = GSS_C_NO_NAME; + gss_OID mech = GSS_C_NO_OID; + OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE; + + ctx->flags |= CTX_FLAG_KRB_REAUTH; + + major = gssAcceptSecContext(minor, + &ctx->kerberosCtx, + cred->krbCred, + inputToken, + chanBindings, + &krbInitiator, + &mech, + outputToken, + &gssFlags, + &timeRec, + NULL); + if (major == GSS_S_COMPLETE) { + major = acceptReadyKrb(minor, ctx, cred, + krbInitiator, mech, timeRec); + } + + ctx->gssFlags = gssFlags; + + gssReleaseName(&tmpMinor, &krbInitiator); + + return major; +} +#endif /* GSSEAP_ENABLE_REAUTH */ diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..ba85af3 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,222 @@ +dnl Based on the one from the Boinc project by Reinhard + +AC_DEFUN([AX_CHECK_KRB5], +[AC_MSG_CHECKING(for GSS-API and Kerberos implementation) +KRB5_DIR= +found_krb5="no" +AC_ARG_WITH(krb5, + AC_HELP_STRING([--with-krb5], + [Use krb5 (in specified installation directory)]), + [check_krb5_dir="$withval"], + [check_krb5_dir=]) +for dir in $check_krb5_dir /usr /usr/local ; do + krb5dir="$dir" + if test -x "$dir/bin/krb5-config"; then + found_krb5="yes"; + KRB5_CFLAGS=`$dir/bin/krb5-config gssapi --cflags`; + KRB5_LIBS=`$dir/bin/krb5-config gssapi --libs`; + COMPILE_ET="$dir/bin/compile_et"; + break; + fi +done +AC_MSG_RESULT($found_krb5) +if test x_$found_krb5 != x_yes; then + AC_MSG_ERROR([ +---------------------------------------------------------------------- + Cannot find GSS-API/Kerberos libraries. + + Please install MIT or Heimdal or specify installation directory with + --with-krb5=(dir). +---------------------------------------------------------------------- +]) +else + printf "Kerberos found in $krb5dir\n"; + AC_SUBST(KRB5_CFLAGS) + AC_SUBST(KRB5_LIBS) + AC_SUBST(COMPILE_ET) + AC_CHECK_LIB(gssapi_krb5, GSS_C_NT_COMPOSITE_EXPORT, [AC_DEFINE_UNQUOTED([HAVE_GSS_C_NT_COMPOSITE_EXPORT], 1, [Define if GSS-API library supports recent naming extensions draft])], [], "$KRB5_LIBS") + AC_CHECK_LIB(gssapi_krb5, gss_inquire_attrs_for_mech, [AC_DEFINE_UNQUOTED([HAVE_GSS_INQUIRE_ATTRS_FOR_MECH], 1, [Define if GSS-API library supports RFC 5587])], [], "$KRB5_LIBS") + AC_CHECK_LIB(gssapi_krb5, gss_krb5_import_cred, [AC_DEFINE_UNQUOTED([HAVE_GSS_KRB5_IMPORT_CRED], 1, [Define if GSS-API library supports gss_krb5_import_cred])], [], "$KRB5_LIBS") + AC_CHECK_LIB(krb5, heimdal_version, [AC_DEFINE_UNQUOTED([HAVE_HEIMDAL_VERSION], 1, [Define if building against Heimdal Kerberos implementation]), heimdal=yes], [heimdal=no], "$KRB5_LIBS") + AM_CONDITIONAL(HEIMDAL, test "x$heimdal" != "xno") +fi +])dnl + +AC_DEFUN([AX_CHECK_EAP], +[AC_MSG_CHECKING(for EAP implementation) +EAP_DIR= +found_eap="no" +AC_ARG_WITH(eap, + AC_HELP_STRING([--with-eap], + [Use eap (in specified installation directory)]), + [check_eap_dir="$withval"], + [check_eap_dir=]) +for dir in $check_eap_dir /usr /usr/local ; do + eapdir="$dir" + if test -f "$dir/src/eap_peer/eap.h"; then + found_eap="yes"; + EAP_DIR="${eapdir}" + EAP_CFLAGS="-I$eapdir/src/common -I$eapdir/src -I$eapdir/src/utils"; + break; + fi +done +AC_MSG_RESULT($found_eap) +if test x_$found_eap != x_yes; then + AC_MSG_ERROR([ +---------------------------------------------------------------------- + Cannot find EAP libraries. + + Please install wpa_supplicant or specify installation directory with + --with-eap=(dir). +---------------------------------------------------------------------- +]) +else + printf "EAP found in $eapdir\n"; + EAP_CFLAGS="$EAP_CFLAGS \ +-DEAP_TLS \ +-DEAP_PEAP \ +-DEAP_TTLS \ +-DEAP_MD5 \ +-DEAP_MSCHAPv2 \ +-DEAP_GTC \ +-DEAP_OTP \ +-DEAP_LEAP \ +-DEAP_PSK \ +-DEAP_PAX \ +-DEAP_SAKE \ +-DEAP_GPSK \ +-DEAP_GPSK_SHA256 \ +-DEAP_SERVER_IDENTITY \ +-DEAP_SERVER_TLS \ +-DEAP_SERVER_PEAP \ +-DEAP_SERVER_TTLS \ +-DEAP_SERVER_MD5 \ +-DEAP_SERVER_MSCHAPV2 \ +-DEAP_SERVER_GTC \ +-DEAP_SERVER_PSK \ +-DEAP_SERVER_PAX \ +-DEAP_SERVER_SAKE \ +-DEAP_SERVER_GPSK \ +-DEAP_SERVER_GPSK_SHA256 \ +-DIEEE8021X_EAPOL"; + EAP_LIBS="-leap -lutils -lcrypto -ltls"; + EAP_LDFLAGS="-L$eapdir/eap_example -L$eapdir/src/utils -L$eapdir/src/crypto -L$eapdir/src/tls"; + AC_SUBST(EAP_CFLAGS) + AC_SUBST(EAP_LDFLAGS) + AC_SUBST(EAP_LIBS) +fi +])dnl + +AC_DEFUN([AX_CHECK_SHIBSP], +[AC_MSG_CHECKING(for Shibboleth implementation) +SHIBSP_DIR= +found_shibsp="no" +AC_ARG_WITH(shibsp, + AC_HELP_STRING([--with-shibsp], + [Use shibspboleth (in specified installation directory)]), + [check_shibsp_dir="$withval"], + [check_shibsp_dir=]) +for dir in $check_shibsp_dir /usr /usr/local ; do + shibspdir="$dir" + if test -f "$dir/include/shibsp/SPConfig.h"; then + found_shibsp="yes"; + SHIBSP_DIR="${shibspdir}" + SHIBSP_CXXFLAGS="-I$shibspdir/include"; + break; + fi +done +AC_MSG_RESULT($found_shibsp) +if test x_$found_shibsp != x_yes; then + AC_MSG_ERROR([ +---------------------------------------------------------------------- + Cannot find Shibboleth/OpenSAML libraries. + + Please install Shibboleth or specify installation directory with + --with-shibsp=(dir). +---------------------------------------------------------------------- +]) +else + printf "Shibboleth found in $shibspdir\n"; + SHIBSP_LIBS="-lshibsp -llog4shib -lsaml -lxml-security-c -lxmltooling -lxerces-c"; + SHIBSP_LDFLAGS="-L$shibspdir/lib"; + AC_SUBST(SHIBSP_CXXFLAGS) + AC_SUBST(SHIBSP_LDFLAGS) + AC_SUBST(SHIBSP_LIBS) +fi +])dnl + +AC_DEFUN([AX_CHECK_SHIBRESOLVER], +[AC_MSG_CHECKING(for Shibboleth resolver implementation) +SHIBRESOLVER_DIR= +found_shibresolver="no" +AC_ARG_WITH(shibresolver, + AC_HELP_STRING([--with-shibresolver], + [Use Shibboleth resolver (in specified installation directory)]), + [check_shibresolver_dir="$withval"], + [check_shibresolver_dir=]) +for dir in $check_shibresolver_dir /usr /usr/local ; do + shibresolverdir="$dir" + if test -f "$dir/include/shibresolver/resolver.h"; then + found_shibresolver="yes"; + SHIBRESOLVER_DIR="${shibresolverdir}" + SHIBRESOLVER_CXXFLAGS="-I$shibresolverdir/include"; + break; + fi +done +AC_MSG_RESULT($found_shibresolver) +if test x_$found_shibresolver != x_yes; then + AC_MSG_ERROR([ +---------------------------------------------------------------------- + Cannot find Shibboleth resolver libraries. + + Please install Shibboleth or specify installation directory with + --with-shibresolver=(dir). +---------------------------------------------------------------------- +]) +else + printf "Shibboleth resolver found in $shibresolverdir\n"; + SHIBRESOLVER_LIBS="-lshibresolver"; + SHIBRESOLVER_LDFLAGS="-L$shibresolverdir/lib"; + AC_SUBST(SHIBRESOLVER_CXXFLAGS) + AC_SUBST(SHIBRESOLVER_LDFLAGS) + AC_SUBST(SHIBRESOLVER_LIBS) +fi +])dnl + +AC_DEFUN([AX_CHECK_RADSEC], +[AC_MSG_CHECKING(for radsec) +RADSEC_DIR= +found_radsec="no" +AC_ARG_WITH(radsec, + AC_HELP_STRING([--with-radsec], + [Use radsec (in specified installation directory)]), + [check_radsec_dir="$withval"], + [check_radsec_dir=]) +for dir in $check_radsec_dir /usr /usr/local ; do + radsecdir="$dir" + if test -f "$dir/include/radsec/radsec.h"; then + found_radsec="yes"; + RADSEC_DIR="${radsecdir}" + RADSEC_CFLAGS="-I$radsecdir/include"; + break; + fi +done +AC_MSG_RESULT($found_radsec) +if test x_$found_radsec != x_yes; then + AC_MSG_ERROR([ +---------------------------------------------------------------------- + Cannot find radsec libraries. + + Please install libradsec or specify installation directory with + --with-radsec=(dir). +---------------------------------------------------------------------- +]) +else + printf "radsec found in $radsecdir\n"; + RADSEC_LIBS="-lradsec"; + RADSEC_LDFLAGS="-L$radsecdir/lib"; + AC_SUBST(RADSEC_CFLAGS) + AC_SUBST(RADSEC_LDFLAGS) + AC_SUBST(RADSEC_LIBS) +fi +])dnl diff --git a/acquire_cred.c b/acquire_cred.c new file mode 100644 index 0000000..ab5cb97 --- /dev/null +++ b/acquire_cred.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Wrapper for acquiring a credential handle. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_acquire_cred(OM_uint32 *minor, + 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 gssEapAcquireCred(minor, desired_name, GSS_C_NO_BUFFER, + time_req, desired_mechs, cred_usage, + output_cred_handle, actual_mechs, time_rec); +} diff --git a/acquire_cred_with_password.c b/acquire_cred_with_password.c new file mode 100644 index 0000000..8d2e3c8 --- /dev/null +++ b/acquire_cred_with_password.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Wrapper for acquiring a credential handle using a password. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gssspi_acquire_cred_with_password(OM_uint32 *minor, + const gss_name_t desired_name, + const gss_buffer_t password, + 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) +{ + return gssEapAcquireCred(minor, desired_name, password, + time_req, desired_mechs, cred_usage, + output_cred_handle, actual_mechs, time_rec); +} diff --git a/add_cred.c b/add_cred.c new file mode 100644 index 0000000..c831f4a --- /dev/null +++ b/add_cred.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Wrapper for acquiring a credential handle. + */ + +#include "gssapiP_eap.h" + +/* + * Note that this shouldn't really be required to be implemented by anything + * apart from the mechanism glue layer. However, Heimdal does call into the + * mechanism here. + */ +OM_uint32 +gss_add_cred(OM_uint32 *minor, + gss_cred_id_t input_cred_handle, + gss_name_t desired_name, + gss_OID desired_mech, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + OM_uint32 major; + OM_uint32 time_req, time_rec = 0; + gss_OID_set_desc mechs; + + *minor = 0; + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (cred_usage == GSS_C_ACCEPT) + time_req = acceptor_time_req; + else + time_req = initiator_time_req; + + mechs.count = 1; + mechs.elements = desired_mech; + + major = gssEapAcquireCred(minor, + desired_name, + GSS_C_NO_BUFFER, + time_req, + &mechs, + cred_usage, + output_cred_handle, + actual_mechs, + &time_rec); + + if (initiator_time_rec != NULL) + *initiator_time_rec = time_rec; + if (acceptor_time_rec != NULL) + *acceptor_time_rec = time_rec; + + return major; +} diff --git a/add_cred_with_password.c b/add_cred_with_password.c new file mode 100644 index 0000000..ca2dd6e --- /dev/null +++ b/add_cred_with_password.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Wrapper for acquiring a credential handle using a password. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_add_cred_with_password(OM_uint32 *minor, + const gss_cred_id_t input_cred_handle, + const gss_name_t desired_name, + const gss_OID desired_mech, + const gss_buffer_t password, + gss_cred_usage_t cred_usage, + OM_uint32 initiator_time_req, + OM_uint32 acceptor_time_req, + gss_cred_id_t *output_cred_handle, + gss_OID_set *actual_mechs, + OM_uint32 *initiator_time_rec, + OM_uint32 *acceptor_time_rec) +{ + OM_uint32 major; + OM_uint32 time_req, time_rec = 0; + gss_OID_set_desc mechs; + + *minor = 0; + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (cred_usage == GSS_C_ACCEPT) + time_req = acceptor_time_req; + else + time_req = initiator_time_req; + + mechs.count = 1; + mechs.elements = desired_mech; + + major = gssEapAcquireCred(minor, + desired_name, + password, + time_req, + &mechs, + cred_usage, + output_cred_handle, + actual_mechs, + &time_rec); + + if (initiator_time_rec != NULL) + *initiator_time_rec = time_rec; + if (acceptor_time_rec != NULL) + *acceptor_time_rec = time_rec; + + return major; +} diff --git a/authdata_plugin.h b/authdata_plugin.h new file mode 100644 index 0000000..32bff2f --- /dev/null +++ b/authdata_plugin.h @@ -0,0 +1,331 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * krb5/authdata_plugin.h + * + * Copyright (C) 2007 Apple 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 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. + * + * AuthorizationData plugin definitions for Kerberos 5. + */ + +/* + * This is considered an INTERNAL interface at this time. + * + * Some work is needed before exporting it: + * + * + Documentation. + * + Sample code. + * + Test cases (preferably automated testing under "make check"). + * + Hook into TGS exchange too; will change API. + * + Examine memory management issues, especially for Windows; may + * change API. + * + * Other changes that would be nice to have, but not necessarily + * before making this interface public: + * + * + Library support for AD-IF-RELEVANT and similar wrappers. (We can + * make the plugin construct them if it wants them.) + * + KDC could combine/optimize wrapped AD elements provided by + * multiple plugins, e.g., two IF-RELEVANT sequences could be + * merged. (The preauth plugin API also has this bug, we're going + * to need a general fix.) + */ + +#ifndef KRB5_AUTHDATA_PLUGIN_H_INCLUDED +#define KRB5_AUTHDATA_PLUGIN_H_INCLUDED +#include + +/* + * While arguments of these types are passed-in, for the most part a + * authorization data module can treat them as opaque. If we need + * keying data, we can ask for it directly. + */ +struct _krb5_db_entry_new; + +/* + * The function table / structure which an authdata server module must export as + * "authdata_server_0". NOTE: replace "0" with "1" for the type and + * variable names if this gets picked up by upstream. If the interfaces work + * correctly, future versions of the table will add either more callbacks or + * more arguments to callbacks, and in both cases we'll be able to wrap the v0 + * functions. + */ +/* extern krb5plugin_authdata_ftable_v0 authdata_server_0; */ +typedef struct krb5plugin_authdata_server_ftable_v0 { + /* Not-usually-visible name. */ + char *name; + + /* + * Per-plugin initialization/cleanup. The init function is called + * by the KDC when the plugin is loaded, and the fini function is + * called before the plugin is unloaded. Both are optional. + */ + krb5_error_code (*init_proc)(krb5_context, void **); + void (*fini_proc)(krb5_context, void *); + /* + * Actual authorization data handling function. If this field + * holds a null pointer, this mechanism will be skipped, and the + * init/fini functions will not be run. + * + * This function should only modify the field + * enc_tkt_reply->authorization_data. All other values should be + * considered inputs only. And, it should *modify* the field, not + * overwrite it and assume that there are no other authdata + * plugins in use. + * + * Memory management: authorization_data is a malloc-allocated, + * null-terminated sequence of malloc-allocated pointers to + * authorization data structures. This plugin code currently + * assumes the libraries, KDC, and plugin all use the same malloc + * pool, which may be a problem if/when we get the KDC code + * running on Windows. + * + * If this function returns a non-zero error code, a message + * is logged, but no other action is taken. Other authdata + * plugins will be called, and a response will be sent to the + * client (barring other problems). + */ + krb5_error_code (*authdata_proc)(krb5_context, + struct _krb5_db_entry_new *client, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_enc_tkt_part *enc_tkt_reply); +} krb5plugin_server_authdata_ftable_v0; + +typedef krb5plugin_server_authdata_ftable_v0 krb5plugin_authdata_ftable_v0; + +typedef struct krb5plugin_authdata_server_ftable_v2 { + /* Not-usually-visible name. */ + char *name; + + /* + * Per-plugin initialization/cleanup. The init function is called + * by the KDC when the plugin is loaded, and the fini function is + * called before the plugin is unloaded. Both are optional. + */ + krb5_error_code (*init_proc)(krb5_context, void **); + void (*fini_proc)(krb5_context, void *); + /* + * Actual authorization data handling function. If this field + * holds a null pointer, this mechanism will be skipped, and the + * init/fini functions will not be run. + * + * This function should only modify the field + * enc_tkt_reply->authorization_data. All other values should be + * considered inputs only. And, it should *modify* the field, not + * overwrite it and assume that there are no other authdata + * plugins in use. + * + * Memory management: authorization_data is a malloc-allocated, + * null-terminated sequence of malloc-allocated pointers to + * authorization data structures. This plugin code currently + * assumes the libraries, KDC, and plugin all use the same malloc + * pool, which may be a problem if/when we get the KDC code + * running on Windows. + * + * If this function returns a non-zero error code, a message + * is logged, but no other action is taken. Other authdata + * plugins will be called, and a response will be sent to the + * client (barring other problems). + */ + krb5_error_code (*authdata_proc)(krb5_context, + unsigned int flags, + struct _krb5_db_entry_new *client, + struct _krb5_db_entry_new *server, + struct _krb5_db_entry_new *tgs, + krb5_keyblock *client_key, + krb5_keyblock *server_key, + krb5_keyblock *tgs_key, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_const_principal for_user_princ, + krb5_enc_tkt_part *enc_tkt_request, + krb5_enc_tkt_part *enc_tkt_reply); +} krb5plugin_authdata_server_ftable_v2; + +typedef krb5plugin_authdata_server_ftable_v2 krb5plugin_authdata_ftable_v2; + +typedef krb5_error_code +(*authdata_client_plugin_init_proc)(krb5_context context, + void **plugin_context); + +#define AD_USAGE_AS_REQ 0x01 +#define AD_USAGE_TGS_REQ 0x02 +#define AD_USAGE_AP_REQ 0x04 +#define AD_USAGE_KDC_ISSUED 0x08 +#define AD_USAGE_MASK 0x0F +#define AD_INFORMATIONAL 0x10 + +struct _krb5_authdata_context; + +typedef void +(*authdata_client_plugin_flags_proc)(krb5_context kcontext, + void *plugin_context, + krb5_authdatatype ad_type, + krb5_flags *flags); + +typedef void +(*authdata_client_plugin_fini_proc)(krb5_context kcontext, + void *plugin_context); + +typedef krb5_error_code +(*authdata_client_request_init_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void **request_context); + +typedef void +(*authdata_client_request_fini_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context); + +typedef krb5_error_code +(*authdata_client_import_authdata_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_authdata **authdata, + krb5_boolean kdc_issued_flag, + krb5_const_principal issuer); + +typedef krb5_error_code +(*authdata_client_export_authdata_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_flags usage, + krb5_authdata ***authdata); + +typedef krb5_error_code +(*authdata_client_get_attribute_types_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_data **attrs); + +typedef krb5_error_code +(*authdata_client_get_attribute_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + const krb5_data *attribute, + krb5_boolean *authenticated, + krb5_boolean *complete, + krb5_data *value, + krb5_data *display_value, + int *more); + +typedef krb5_error_code +(*authdata_client_set_attribute_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_boolean complete, + const krb5_data *attribute, + const krb5_data *value); + +typedef krb5_error_code +(*authdata_client_delete_attribute_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + const krb5_data *attribute); + +typedef krb5_error_code +(*authdata_client_export_internal_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_boolean restrict_authenticated, + void **ptr); + +typedef void +(*authdata_client_free_internal_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + void *ptr); + +typedef krb5_error_code +(*authdata_client_verify_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + const krb5_auth_context *auth_context, + const krb5_keyblock *key, + const krb5_ap_req *req); + +typedef krb5_error_code +(*authdata_client_size_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + size_t *sizep); + +typedef krb5_error_code +(*authdata_client_externalize_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_octet **buffer, + size_t *lenremain); + +typedef krb5_error_code +(*authdata_client_internalize_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_octet **buffer, + size_t *lenremain); + +typedef krb5_error_code +(*authdata_client_copy_proc)(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + void *dst_plugin_context, + void *dst_request_context); + +typedef struct krb5plugin_authdata_client_ftable_v0 { + char *name; + krb5_authdatatype *ad_type_list; + authdata_client_plugin_init_proc init; + authdata_client_plugin_fini_proc fini; + authdata_client_plugin_flags_proc flags; + authdata_client_request_init_proc request_init; + authdata_client_request_fini_proc request_fini; + authdata_client_get_attribute_types_proc get_attribute_types; + authdata_client_get_attribute_proc get_attribute; + authdata_client_set_attribute_proc set_attribute; + authdata_client_delete_attribute_proc delete_attribute; + authdata_client_export_authdata_proc export_authdata; + authdata_client_import_authdata_proc import_authdata; + authdata_client_export_internal_proc export_internal; + authdata_client_free_internal_proc free_internal; + authdata_client_verify_proc verify; + authdata_client_size_proc size; + authdata_client_externalize_proc externalize; + authdata_client_internalize_proc internalize; + authdata_client_copy_proc copy; /* optional */ +} krb5plugin_authdata_client_ftable_v0; + +#endif /* KRB5_AUTHDATA_PLUGIN_H_INCLUDED */ diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..a209369 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,13 @@ +#! /bin/sh + +PATH=/usr/local/bin:$PATH + +if [ -x "`which autoreconf 2>/dev/null`" ] ; then + exec autoreconf -ivf +fi + +aclocal -I . -I m4 && \ + autoheader && \ + libtoolize --automake -c && \ + autoconf && \ + automake --add-missing --copy diff --git a/canonicalize_name.c b/canonicalize_name.c new file mode 100644 index 0000000..acfbbc3 --- /dev/null +++ b/canonicalize_name.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Function for canonicalizing a name; presently just duplicates it. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_canonicalize_name(OM_uint32 *minor, + const gss_name_t input_name, + const gss_OID mech_type, + gss_name_t *output_name) +{ + OM_uint32 major; + + *minor = 0; + + if (!gssEapIsMechanismOid(mech_type)) + return GSS_S_BAD_MECH; + + if (input_name == GSS_C_NO_NAME) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + } + + GSSEAP_MUTEX_LOCK(&input_name->mutex); + + major = gssEapDuplicateName(minor, input_name, output_name); + + GSSEAP_MUTEX_UNLOCK(&input_name->mutex); + + return major; +} diff --git a/compare_name.c b/compare_name.c new file mode 100644 index 0000000..47c573e --- /dev/null +++ b/compare_name.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Compare two names. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_compare_name(OM_uint32 *minor, + gss_name_t name1, + gss_name_t name2, + int *name_equal) +{ + krb5_context krbContext; + + *minor = 0; + + if (name1 == GSS_C_NO_NAME && name2 == GSS_C_NO_NAME) { + *name_equal = 1; + } else if (name1 != GSS_C_NO_NAME && name2 != GSS_C_NO_NAME) { + GSSEAP_KRB_INIT(&krbContext); + + /* krbPrincipal is immutable, so lock not required */ + *name_equal = krb5_principal_compare(krbContext, + name1->krbPrincipal, + name2->krbPrincipal); + } + + return GSS_S_COMPLETE; +} diff --git a/config.guess b/config.guess new file mode 100755 index 0000000..dc84c68 --- /dev/null +++ b/config.guess @@ -0,0 +1,1501 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +# Free Software Foundation, Inc. + +timestamp='2009-11-20' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner. Please send patches (context +# diff format) to and include a ChangeLog +# entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm:riscos:*:*|arm:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[456]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + case ${UNAME_MACHINE} in + pc98) + echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-gnu + else + echo ${UNAME_MACHINE}-unknown-linux-gnueabi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + LIBC=gnu + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + or32:Linux:*:*) + echo or32-unknown-linux-gnu + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-gnu + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + i386) + eval $set_cc_for_build + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + UNAME_PROCESSOR="x86_64" + fi + fi ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config.sub b/config.sub new file mode 100755 index 0000000..2a55a50 --- /dev/null +++ b/config.sub @@ -0,0 +1,1705 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +# Free Software Foundation, Inc. + +timestamp='2009-11-20' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Please send patches to . Submit a context +# diff and a properly formatted GNU ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ + uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nios | nios2 \ + | ns16k | ns32k \ + | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e \ + | we32k \ + | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12 | picochip) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nios-* | nios2-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze) + basic_machine=microblaze-xilinx + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tile*) + basic_machine=tile-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..e5a351b --- /dev/null +++ b/configure.ac @@ -0,0 +1,54 @@ +AC_PREREQ([2.61]) +AC_INIT([mech_eap], [0.1], [bugs@project-moonshot.org]) +dnl AC_CONFIG_MACRO_DIR([m4]) +AM_INIT_AUTOMAKE +LT_PREREQ([2.2]) +LT_INIT([dlopen disable-static]) + +AC_PROG_CC +AC_PROG_CXX +AC_CONFIG_HEADERS([config.h]) + +dnl Check if we're on Solaris and set CFLAGS accordingly +dnl AC_CANONICAL_TARGET +dnl case "${target_os}" in +dnl solaris*) +dnl TARGET_CFLAGS="-DSYS_SOLARIS9 -D_POSIX_PTHREAD_SEMANTICS" +dnl if test "$GCC" != yes ; then +dnl TARGET_CFLAGS="$TARGET_CFLAGS -mt" +dnl else +dnl TARGET_CFLAGS="$TARGET_CFLAGS -pthreads" +dnl fi +dnl TARGET_LDFLAGS="-lpthread -lsocket -lnsl" +dnl ;; +dnl *) +dnl TARGET_CFLAGS="-Wall -pedantic -pthread" +dnl TARGET_LDFLAGS="" +dnl esac + +reauth=no +AC_ARG_ENABLE(reauth, + [ --enable-reauth whether to enable fast reauthentication protocol: yes/no; default no ], + [ if test "x$enableval" = "xyes" -o "x$enableval" = "xno" ; then + reauth=$enableval + else + echo "--enable-reauth argument must be yes or no" + exit -1 + fi + ]) + +if test "x$reauth" = "xyes" ; then + echo "Fast reauthentication protocol enabled" + TARGET_CFLAGS="$TARGET_CFLAGS -DGSSEAP_ENABLE_REAUTH" +fi +AM_CONDITIONAL(GSSEAP_ENABLE_REAUTH, test "x$reauth" != "xno") + +AC_SUBST(TARGET_CFLAGS) +AC_SUBST(TARGET_LDFLAGS) +AX_CHECK_KRB5 +AX_CHECK_EAP +AX_CHECK_SHIBSP +AX_CHECK_SHIBRESOLVER +AX_CHECK_RADSEC +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/context_time.c b/context_time.c new file mode 100644 index 0000000..c29493c --- /dev/null +++ b/context_time.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Determine remaining lifetime of a context handle. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_context_time(OM_uint32 *minor, + gss_ctx_id_t ctx, + OM_uint32 *time_rec) +{ + OM_uint32 major; + + if (ctx == GSS_C_NO_CONTEXT) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + *minor = 0; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (!CTX_IS_ESTABLISHED(ctx)) { + *minor = GSSEAP_CONTEXT_INCOMPLETE; + major = GSS_S_NO_CONTEXT; + goto cleanup; + } + + major = gssEapContextTime(minor, ctx, time_rec); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return major; +} diff --git a/delete_name_attribute.c b/delete_name_attribute.c new file mode 100644 index 0000000..61f7b43 --- /dev/null +++ b/delete_name_attribute.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Wrapper for removing a name attribute. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_delete_name_attribute(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t attr) +{ + OM_uint32 major; + + *minor = 0; + + if (name == GSS_C_NO_NAME) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + } + + GSSEAP_MUTEX_LOCK(&name->mutex); + + major = gssEapDeleteNameAttribute(minor, name, attr); + + GSSEAP_MUTEX_UNLOCK(&name->mutex); + + return major; +} diff --git a/delete_sec_context.c b/delete_sec_context.c new file mode 100644 index 0000000..4f11148 --- /dev/null +++ b/delete_sec_context.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Release a context handle. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_delete_sec_context(OM_uint32 *minor, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) +{ + OM_uint32 major; + gss_ctx_id_t ctx = *context_handle; + + *minor = 0; + + if (output_token != GSS_C_NO_BUFFER) { + output_token->length = 0; + output_token->value = NULL; + } + + if (ctx == GSS_C_NO_CONTEXT) + return GSS_S_COMPLETE; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (output_token != GSS_C_NO_BUFFER) { + gss_iov_buffer_desc iov[2]; + + iov[0].type = GSS_IOV_BUFFER_TYPE_DATA; + iov[0].buffer.value = NULL; + iov[0].buffer.length = 0; + + iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE; + iov[1].buffer.value = NULL; + iov[1].buffer.length = 0; + + major = gssEapWrapOrGetMIC(minor, ctx, FALSE, FALSE, + iov, 2, TOK_TYPE_DELETE_CONTEXT); + if (GSS_ERROR(major)) { + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + return major; + } + } + + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return gssEapReleaseContext(minor, context_handle); +} diff --git a/display_name.c b/display_name.c new file mode 100644 index 0000000..21f48c6 --- /dev/null +++ b/display_name.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Wrapper for "displaying" (returning string representation of) a name. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_display_name(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t output_name_buffer, + gss_OID *output_name_type) +{ + /* Lock not required as long as attributes are not used */ + return gssEapDisplayName(minor, name, output_name_buffer, + output_name_type); +} diff --git a/display_name_ext.c b/display_name_ext.c new file mode 100644 index 0000000..ffc5ad8 --- /dev/null +++ b/display_name_ext.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Parameterized version of gss_display_name(), currently unimplemented. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_display_name_ext(OM_uint32 *minor, + gss_name_t name, + gss_OID display_as_name_type, + gss_buffer_t display_name) +{ + *minor = 0; + + display_name->length = 0; + display_name->value = NULL; + + return GSS_S_UNAVAILABLE; +} diff --git a/display_status.c b/display_status.c new file mode 100644 index 0000000..5033620 --- /dev/null +++ b/display_status.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Function for converting mechanism error codes to strings. + */ + +#include "gssapiP_eap.h" + +static GSSEAP_THREAD_ONCE gssEapStatusInfoKeyOnce = GSSEAP_ONCE_INITIALIZER; +static GSSEAP_THREAD_KEY gssEapStatusInfoKey; + +struct gss_eap_status_info { + OM_uint32 code; + char *message; + struct gss_eap_status_info *next; +}; + +static void +destroyStatusInfo(void *arg) +{ + struct gss_eap_status_info *p = arg, *next; + + for (p = arg; p != NULL; p = next) { + next = p->next; + GSSEAP_FREE(p->message); + GSSEAP_FREE(p); + } +} + +static void +createStatusInfoKey(void) +{ + GSSEAP_KEY_CREATE(&gssEapStatusInfoKey, destroyStatusInfo); +} + +/* + * Associate a message with a mechanism (minor) status code. This function + * takes ownership of the message regardless of success. The message must + * be explicitly cleared, if required, so it is suggested that a specific + * minor code is either always or never associated with a message, to avoid + * dangling (and potentially confusing) error messages. + */ +static void +saveStatusInfoNoCopy(OM_uint32 minor, char *message) +{ + struct gss_eap_status_info **next = NULL, *p; + + GSSEAP_ONCE(&gssEapStatusInfoKeyOnce, createStatusInfoKey); + + p = GSSEAP_GETSPECIFIC(gssEapStatusInfoKey); + for (; p != NULL; p = p->next) { + if (p->code == minor) { + /* Set message in-place */ + if (p->message != NULL) + GSSEAP_FREE(p->message); + p->message = message; + return; + } + next = &p->next; + } + + p = GSSEAP_CALLOC(1, sizeof(*p)); + if (p == NULL) { + if (message != NULL) + GSSEAP_FREE(message); + return; + } + + p->code = minor; + p->message = message; + + if (p != NULL) + *next = p; + else + GSSEAP_SETSPECIFIC(gssEapStatusInfoKey, p); +} + +static const char * +getStatusInfo(OM_uint32 minor) +{ + struct gss_eap_status_info *p; + + GSSEAP_ONCE(&gssEapStatusInfoKeyOnce, createStatusInfoKey); + + for (p = GSSEAP_GETSPECIFIC(gssEapStatusInfoKey); + p != NULL; + p = p->next) { + if (p->code == minor) + return p->message; + } + + return NULL; +} + +void +gssEapSaveStatusInfo(OM_uint32 minor, 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) + saveStatusInfoNoCopy(minor, s); +} + +OM_uint32 +gss_display_status(OM_uint32 *minor, + OM_uint32 status_value, + int status_type, + gss_OID mech_type, + OM_uint32 *message_context, + gss_buffer_t status_string) +{ + OM_uint32 major; + krb5_context krbContext = NULL; + const char *errMsg; + + status_string->length = 0; + status_string->value = NULL; + + if (!gssEapIsMechanismOid(mech_type)) { + *minor = GSSEAP_WRONG_MECH; + return GSS_S_BAD_MECH; + } + + if (status_type != GSS_C_MECH_CODE) { + /* we rely on the mechglue for GSS_C_GSS_CODE */ + *minor = 0; + return GSS_S_BAD_STATUS; + } + + errMsg = getStatusInfo(status_value); + if (errMsg == NULL) { + GSSEAP_KRB_INIT(&krbContext); + + /* Try the com_err message */ + errMsg = krb5_get_error_message(krbContext, status_value); + } + + if (errMsg != NULL) { + major = makeStringBuffer(minor, errMsg, status_string); + } else { + major = GSS_S_COMPLETE; + *minor = 0; + } + + if (krbContext != NULL) + krb5_free_error_message(krbContext, errMsg); + + return major; +} diff --git a/duplicate_name.c b/duplicate_name.c new file mode 100644 index 0000000..deb6420 --- /dev/null +++ b/duplicate_name.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Duplicate a name. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_duplicate_name(OM_uint32 *minor, + const gss_name_t input_name, + gss_name_t *dest_name) +{ + OM_uint32 major; + + *minor = 0; + + if (input_name == GSS_C_NO_NAME) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + } + + GSSEAP_MUTEX_LOCK(&input_name->mutex); + + major = gssEapDuplicateName(minor, input_name, dest_name); + + GSSEAP_MUTEX_UNLOCK(&input_name->mutex); + + return major; +} diff --git a/eap_mech.c b/eap_mech.c new file mode 100644 index 0000000..c9b03b6 --- /dev/null +++ b/eap_mech.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Initialisation and finalise functions. + */ + +#include "gssapiP_eap.h" + +static OM_uint32 +eapPeerRegisterMethods(OM_uint32 *minor) +{ + OM_uint32 ret = 0; + +#ifdef EAP_MD5 + if (ret == 0) + ret = eap_peer_md5_register(); +#endif /* EAP_MD5 */ + +#ifdef EAP_TLS + if (ret == 0) + ret = eap_peer_tls_register(); +#endif /* EAP_TLS */ + +#ifdef EAP_MSCHAPv2 + if (ret == 0) + ret = eap_peer_mschapv2_register(); +#endif /* EAP_MSCHAPv2 */ + +#ifdef EAP_PEAP + if (ret == 0) + ret = eap_peer_peap_register(); +#endif /* EAP_PEAP */ + +#ifdef EAP_TTLS + if (ret == 0) + ret = eap_peer_ttls_register(); +#endif /* EAP_TTLS */ + +#ifdef EAP_GTC + if (ret == 0) + ret = eap_peer_gtc_register(); +#endif /* EAP_GTC */ + +#ifdef EAP_OTP + if (ret == 0) + ret = eap_peer_otp_register(); +#endif /* EAP_OTP */ + +#ifdef EAP_SIM + if (ret == 0) + ret = eap_peer_sim_register(); +#endif /* EAP_SIM */ + +#ifdef EAP_LEAP + if (ret == 0) + ret = eap_peer_leap_register(); +#endif /* EAP_LEAP */ + +#ifdef EAP_PSK + if (ret == 0) + ret = eap_peer_psk_register(); +#endif /* EAP_PSK */ + +#ifdef EAP_AKA + if (ret == 0) + ret = eap_peer_aka_register(); +#endif /* EAP_AKA */ + +#ifdef EAP_AKA_PRIME + if (ret == 0) + ret = eap_peer_aka_prime_register(); +#endif /* EAP_AKA_PRIME */ + +#ifdef EAP_FAST + if (ret == 0) + ret = eap_peer_fast_register(); +#endif /* EAP_FAST */ + +#ifdef EAP_PAX + if (ret == 0) + ret = eap_peer_pax_register(); +#endif /* EAP_PAX */ + +#ifdef EAP_SAKE + if (ret == 0) + ret = eap_peer_sake_register(); +#endif /* EAP_SAKE */ + +#ifdef EAP_GPSK + if (ret == 0) + ret = eap_peer_gpsk_register(); +#endif /* EAP_GPSK */ + +#ifdef EAP_WSC + if (ret == 0) + ret = eap_peer_wsc_register(); +#endif /* EAP_WSC */ + +#ifdef EAP_IKEV2 + if (ret == 0) + ret = eap_peer_ikev2_register(); +#endif /* EAP_IKEV2 */ + +#ifdef EAP_VENDOR_TEST + if (ret == 0) + ret = eap_peer_vendor_test_register(); +#endif /* EAP_VENDOR_TEST */ + +#ifdef EAP_TNC + if (ret == 0) + ret = eap_peer_tnc_register(); +#endif /* EAP_TNC */ + + if (ret == 0) + return GSS_S_COMPLETE; + + *minor = GSSEAP_LIBEAP_INIT_FAILURE; + return GSS_S_FAILURE; +} + +static OM_uint32 +gssEapInitLibEap(OM_uint32 *minor) +{ + return eapPeerRegisterMethods(minor); +} + +static OM_uint32 +gssEapInitLibRadsec(OM_uint32 *minor) +{ + if (0) { + *minor = GSSEAP_RADSEC_INIT_FAILURE; + return GSS_S_FAILURE; + } + + return GSS_S_COMPLETE; +} + +static void gssEapInitiatorInit(void) __attribute__((constructor)); +static void gssEapFinalize(void) __attribute__((destructor)); + +static void +gssEapInitiatorInit(void) +{ + OM_uint32 major, minor; + + initialize_eapg_error_table(); + initialize_rse_error_table(); + + major = gssEapInitLibEap(&minor); + assert(major == GSS_S_COMPLETE); + + major = gssEapInitLibRadsec(&minor); + assert(major == GSS_S_COMPLETE); + +#ifdef GSSEAP_ENABLE_REAUTH + major = gssEapReauthInitialize(&minor); + assert(major == GSS_S_COMPLETE); +#endif +} + +static void +gssEapFinalize(void) +{ + OM_uint32 minor; + + gssEapAttrProvidersFinalize(&minor); + eap_peer_unregister_methods(); +} diff --git a/export_name.c b/export_name.c new file mode 100644 index 0000000..901f7ed --- /dev/null +++ b/export_name.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Serialise a name. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_export_name(OM_uint32 *minor, + const gss_name_t input_name, + gss_buffer_t exported_name) +{ + OM_uint32 major; + + *minor = 0; + + if (input_name == GSS_C_NO_NAME) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + } + + GSSEAP_MUTEX_LOCK(&input_name->mutex); + + major = gssEapExportName(minor, input_name, exported_name); + + GSSEAP_MUTEX_UNLOCK(&input_name->mutex); + + return major; +} diff --git a/export_name_composite.c b/export_name_composite.c new file mode 100644 index 0000000..35c9bee --- /dev/null +++ b/export_name_composite.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Serialise a name and its attributes. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_export_name_composite(OM_uint32 *minor, + gss_name_t input_name, + gss_buffer_t exported_name) +{ + OM_uint32 major; + + *minor = 0; + + if (input_name == GSS_C_NO_NAME) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + } + + GSSEAP_MUTEX_LOCK(&input_name->mutex); + + major = gssEapExportNameInternal(minor, input_name, exported_name, + EXPORT_NAME_FLAG_OID | + EXPORT_NAME_FLAG_COMPOSITE); + + GSSEAP_MUTEX_UNLOCK(&input_name->mutex); + + return major; +} diff --git a/export_sec_context.c b/export_sec_context.c new file mode 100644 index 0000000..e7080a0 --- /dev/null +++ b/export_sec_context.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Serialise a security context. On the acceptor, this may be partially + * established. + */ + +#include "gssapiP_eap.h" + +static OM_uint32 +gssEapExportPartialContext(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_buffer_t token) +{ + OM_uint32 major, tmpMinor; + size_t length, serverLen = 0; + unsigned char *p; + char serverBuf[MAXHOSTNAMELEN]; + + if (ctx->acceptorCtx.radConn != NULL) { + if (rs_conn_get_current_server(ctx->acceptorCtx.radConn, + serverBuf, sizeof(serverBuf)) != 0) { + return gssEapRadiusMapError(minor, + rs_err_conn_pop(ctx->acceptorCtx.radConn)); + } + serverLen = strlen(serverBuf); + } + + length = 4 + serverLen + 4 + ctx->acceptorCtx.state.length; + + token->value = GSSEAP_MALLOC(length); + if (token->value == NULL) { + major = GSS_S_FAILURE; + *minor = ENOMEM; + goto cleanup; + } + token->length = length; + + p = (unsigned char *)token->value; + + store_uint32_be(serverLen, p); + p += 4; + if (serverLen != 0) { + memcpy(p, serverBuf, serverLen); + p += serverLen; + } + + store_uint32_be(ctx->acceptorCtx.state.length, p); + p += 4; + if (ctx->acceptorCtx.state.length != 0) { + memcpy(p, ctx->acceptorCtx.state.value, + ctx->acceptorCtx.state.length); + p += ctx->acceptorCtx.state.length; + } + + assert(p == (unsigned char *)token->value + token->length); + + major = GSS_S_COMPLETE; + *minor = 0; + +cleanup: + if (GSS_ERROR(major)) + gss_release_buffer(&tmpMinor, token); + + return major; +} + +static OM_uint32 +gssEapExportSecContext(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_buffer_t token) +{ + OM_uint32 major, tmpMinor; + size_t length; + gss_buffer_desc initiatorName = GSS_C_EMPTY_BUFFER; + gss_buffer_desc acceptorName = GSS_C_EMPTY_BUFFER; + gss_buffer_desc partialCtx = GSS_C_EMPTY_BUFFER; + gss_buffer_desc key; + unsigned char *p; + + if ((CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) || + ctx->mechanismUsed == GSS_C_NO_OID) { + *minor = GSSEAP_CONTEXT_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + key.length = KRB_KEY_LENGTH(&ctx->rfc3961Key); + key.value = KRB_KEY_DATA(&ctx->rfc3961Key); + + if (ctx->initiatorName != GSS_C_NO_NAME) { + major = gssEapExportNameInternal(minor, ctx->initiatorName, + &initiatorName, + EXPORT_NAME_FLAG_COMPOSITE); + if (GSS_ERROR(major)) + goto cleanup; + } + if (ctx->acceptorName != GSS_C_NO_NAME) { + major = gssEapExportNameInternal(minor, ctx->acceptorName, + &acceptorName, + EXPORT_NAME_FLAG_COMPOSITE); + if (GSS_ERROR(major)) + goto cleanup; + } + + /* + * The partial context is only transmitted for unestablished acceptor + * contexts. + */ + if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) { + assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0); + + major = gssEapExportPartialContext(minor, ctx, &partialCtx); + if (GSS_ERROR(major)) + goto cleanup; + } + + length = 16; /* version, state, flags, */ + length += 4 + ctx->mechanismUsed->length; /* mechanismUsed */ + length += 12 + key.length; /* rfc3961Key.value */ + length += 4 + initiatorName.length; /* initiatorName.value */ + length += 4 + acceptorName.length; /* acceptorName.value */ + length += 24 + sequenceSize(ctx->seqState); /* seqState */ + + if (partialCtx.value != NULL) + length += 4 + partialCtx.length; /* partialCtx.value */ + + token->value = GSSEAP_MALLOC(length); + if (token->value == NULL) { + major = GSS_S_FAILURE; + *minor = ENOMEM; + goto cleanup; + } + token->length = length; + + p = (unsigned char *)token->value; + + store_uint32_be(EAP_EXPORT_CONTEXT_V1, &p[0]); /* version */ + store_uint32_be(ctx->state, &p[4]); + store_uint32_be(ctx->flags, &p[8]); + store_uint32_be(ctx->gssFlags, &p[12]); + p = store_oid(ctx->mechanismUsed, &p[16]); + + store_uint32_be(ctx->checksumType, &p[0]); + store_uint32_be(ctx->encryptionType, &p[4]); + p = store_buffer(&key, &p[8], FALSE); + + p = store_buffer(&initiatorName, p, FALSE); + p = store_buffer(&acceptorName, p, FALSE); + + store_uint64_be(ctx->expiryTime, &p[0]); + store_uint64_be(ctx->sendSeq, &p[8]); + store_uint64_be(ctx->recvSeq, &p[16]); + p += 24; + + major = sequenceExternalize(minor, ctx->seqState, &p, &length); + if (GSS_ERROR(major)) + goto cleanup; + + if (partialCtx.value != NULL) + p = store_buffer(&partialCtx, p, FALSE); + + assert(p == (unsigned char *)token->value + token->length); + + major = GSS_S_COMPLETE; + *minor = 0; + +cleanup: + if (GSS_ERROR(major)) + gss_release_buffer(&tmpMinor, token); + gss_release_buffer(&tmpMinor, &initiatorName); + gss_release_buffer(&tmpMinor, &acceptorName); + gss_release_buffer(&tmpMinor, &partialCtx); + + return major; +} + +OM_uint32 +gss_export_sec_context(OM_uint32 *minor, + gss_ctx_id_t *context_handle, + gss_buffer_t interprocess_token) +{ + OM_uint32 major, tmpMinor; + gss_ctx_id_t ctx = *context_handle; + + interprocess_token->length = 0; + interprocess_token->value = NULL; + + if (ctx == GSS_C_NO_CONTEXT) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + *minor = 0; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + major = gssEapExportSecContext(minor, ctx, interprocess_token); + if (GSS_ERROR(major)) { + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + return major; + } + + *context_handle = GSS_C_NO_CONTEXT; + + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + gssEapReleaseContext(&tmpMinor, &ctx); + + return GSS_S_COMPLETE; +} diff --git a/get_mic.c b/get_mic.c new file mode 100644 index 0000000..13849f8 --- /dev/null +++ b/get_mic.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Message protection services: make a message integerity check. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_get_mic(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_qop_t qop_req, + gss_buffer_t message_buffer, + gss_buffer_t message_token) +{ + OM_uint32 major; + gss_iov_buffer_desc iov[2]; + + if (ctx == GSS_C_NO_CONTEXT) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + if (qop_req != GSS_C_QOP_DEFAULT) { + *minor = GSSEAP_UNKNOWN_QOP; + return GSS_S_UNAVAILABLE; + } + + *minor = 0; + + message_token->value = NULL; + message_token->length = 0; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (!CTX_IS_ESTABLISHED(ctx)) { + major = GSS_S_NO_CONTEXT; + *minor = GSSEAP_CONTEXT_INCOMPLETE; + goto cleanup; + } + + iov[0].type = GSS_IOV_BUFFER_TYPE_DATA; + iov[0].buffer = *message_buffer; + + iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE; + iov[1].buffer.value = NULL; + iov[1].buffer.length = 0; + + major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL, iov, 2, TOK_TYPE_MIC); + if (GSS_ERROR(major)) + goto cleanup; + + *message_token = iov[1].buffer; + +cleanup: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return major; +} diff --git a/get_name_attribute.c b/get_name_attribute.c new file mode 100644 index 0000000..dfaa0d5 --- /dev/null +++ b/get_name_attribute.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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 "gssapiP_eap.h" + +/* + * Wrapper for retrieving a naming attribute. + */ + +OM_uint32 +gss_get_name_attribute(OM_uint32 *minor, + 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 major; + + *minor = 0; + + if (name == GSS_C_NO_NAME) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + } + + GSSEAP_MUTEX_LOCK(&name->mutex); + + major = gssEapGetNameAttribute(minor, name, attr, + authenticated, complete, + value, display_value, more); + + GSSEAP_MUTEX_UNLOCK(&name->mutex); + + return major; +} diff --git a/gssapiP_eap.h b/gssapiP_eap.h new file mode 100644 index 0000000..65d01bf --- /dev/null +++ b/gssapiP_eap.h @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +#ifndef _GSSAPIP_EAP_H_ +#define _GSSAPIP_EAP_H_ 1 + +#include "config.h" + +#ifdef HAVE_HEIMDAL_VERSION +#define KRB5_DEPRECATED /* so we can use krb5_free_unparsed_name() */ +#endif + +#include +#include +#include +#include +#include +#include +#include + +/* GSS headers */ +#include +#include +#ifndef HAVE_HEIMDAL_VERSION +#include +#endif +#include "gssapi_eap.h" + +/* Kerberos headers */ +#include + +/* EAP headers */ +#include +#include +#include +#include +#include + +/* FreeRADIUS headers */ +#ifdef __cplusplus +extern "C" { +#define operator fr_operator +#endif +#include +#include +#include +#include +#ifdef __cplusplus +#undef operator +} +#endif + +#include "gsseap_err.h" +#include "radsec_err.h" +#include "util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* These name flags are informative and not actually used by anything yet */ +#define NAME_FLAG_NAI 0x00000001 +#define NAME_FLAG_SERVICE 0x00000002 +#define NAME_FLAG_COMPOSITE 0x00000004 + +struct gss_eap_saml_attr_ctx; +struct gss_eap_attr_ctx; + +#ifdef HAVE_HEIMDAL_VERSION +struct gss_name_t_desc_struct +#else +struct gss_name_struct +#endif +{ + GSSEAP_MUTEX mutex; /* mutex protects attrCtx */ + OM_uint32 flags; + krb5_principal krbPrincipal; /* this is immutable */ + struct gss_eap_attr_ctx *attrCtx; +}; + +#define CRED_FLAG_INITIATE 0x00010000 +#define CRED_FLAG_ACCEPT 0x00020000 +#define CRED_FLAG_DEFAULT_IDENTITY 0x00040000 +#define CRED_FLAG_PASSWORD 0x00080000 +#define CRED_FLAG_DEFAULT_CCACHE 0x00100000 +#define CRED_FLAG_PUBLIC_MASK 0x0000FFFF + +#ifdef HAVE_HEIMDAL_VERSION +struct gss_cred_id_t_desc_struct +#else +struct gss_cred_id_struct +#endif +{ + GSSEAP_MUTEX mutex; + OM_uint32 flags; + gss_name_t name; + gss_buffer_desc password; + gss_OID_set mechanisms; + time_t expiryTime; + char *radiusConfigFile; + char *radiusConfigStanza; +#ifdef GSSEAP_ENABLE_REAUTH + krb5_ccache krbCredCache; + gss_cred_id_t krbCred; +#endif +}; + +#define CTX_FLAG_INITIATOR 0x00000001 +#define CTX_FLAG_KRB_REAUTH 0x00000002 + +#define CTX_IS_INITIATOR(ctx) (((ctx)->flags & CTX_FLAG_INITIATOR) != 0) + +enum gss_eap_state { + GSSEAP_STATE_IDENTITY = 0, /* identify peer */ + GSSEAP_STATE_AUTHENTICATE, /* exchange EAP messages */ + GSSEAP_STATE_EXTENSIONS_REQ, /* initiator extensions */ + GSSEAP_STATE_EXTENSIONS_RESP, /* acceptor extensions */ + GSSEAP_STATE_ESTABLISHED, /* context established */ + GSSEAP_STATE_ERROR, /* context error */ +#ifdef GSSEAP_ENABLE_REAUTH + GSSEAP_STATE_KRB_REAUTH /* fast reauthentication */ +#endif +}; + +#define CTX_IS_ESTABLISHED(ctx) ((ctx)->state == GSSEAP_STATE_ESTABLISHED) + +/* Initiator context flags */ +#define CTX_FLAG_EAP_SUCCESS 0x00010000 +#define CTX_FLAG_EAP_RESTART 0x00020000 +#define CTX_FLAG_EAP_FAIL 0x00040000 +#define CTX_FLAG_EAP_RESP 0x00080000 +#define CTX_FLAG_EAP_NO_RESP 0x00100000 +#define CTX_FLAG_EAP_REQ 0x00200000 +#define CTX_FLAG_EAP_PORT_ENABLED 0x00400000 +#define CTX_FLAG_EAP_ALT_ACCEPT 0x00800000 +#define CTX_FLAG_EAP_ALT_REJECT 0x01000000 +#define CTX_FLAG_EAP_MASK 0xFFFF0000 + +struct gss_eap_initiator_ctx { + unsigned int idleWhile; +#ifndef __cplusplus + struct eap_peer_config eapPeerConfig; + struct eap_sm *eap; + struct wpabuf reqData; +#endif +}; + +struct gss_eap_acceptor_ctx { + struct rs_context *radContext; + struct rs_connection *radConn; + char *radServer; + gss_buffer_desc state; + VALUE_PAIR *vps; +}; + +#ifdef HAVE_HEIMDAL_VERSION +struct gss_ctx_id_t_desc_struct +#else +struct gss_ctx_id_struct +#endif +{ + GSSEAP_MUTEX mutex; + enum gss_eap_state state; + OM_uint32 flags; + OM_uint32 gssFlags; + gss_OID mechanismUsed; + krb5_cksumtype checksumType; + krb5_enctype encryptionType; + krb5_keyblock rfc3961Key; + gss_name_t initiatorName; + gss_name_t acceptorName; + time_t expiryTime; + uint64_t sendSeq, recvSeq; + void *seqState; + gss_cred_id_t defaultCred; + union { + struct gss_eap_initiator_ctx initiator; + #define initiatorCtx ctxU.initiator + struct gss_eap_acceptor_ctx acceptor; + #define acceptorCtx ctxU.acceptor +#ifdef GSSEAP_ENABLE_REAUTH + gss_ctx_id_t kerberos; + #define kerberosCtx ctxU.kerberos +#endif + } ctxU; +}; + +#define TOK_FLAG_SENDER_IS_ACCEPTOR 0x01 +#define TOK_FLAG_WRAP_CONFIDENTIAL 0x02 +#define TOK_FLAG_ACCEPTOR_SUBKEY 0x04 + +#define KEY_USAGE_ACCEPTOR_SEAL 22 +#define KEY_USAGE_ACCEPTOR_SIGN 23 +#define KEY_USAGE_INITIATOR_SEAL 24 +#define KEY_USAGE_INITIATOR_SIGN 25 + +/* wrap_iov.c */ +OM_uint32 +gssEapWrapOrGetMIC(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + enum gss_eap_token_type toktype); + +OM_uint32 +gssEapUnwrapOrVerifyMIC(OM_uint32 *minor_status, + gss_ctx_id_t ctx, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + enum gss_eap_token_type toktype); + +OM_uint32 +gssEapWrapIovLength(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count); +OM_uint32 +gssEapWrap(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + gss_qop_t qop_req, + gss_buffer_t input_message_buffer, + int *conf_state, + gss_buffer_t output_message_buffer); + +unsigned char +rfc4121Flags(gss_ctx_id_t ctx, int receiving); + +/* display_status.c */ +void +gssEapSaveStatusInfo(OM_uint32 minor, const char *format, ...); + +#define IS_WIRE_ERROR(err) ((err) > GSSEAP_RESERVED && \ + (err) <= GSSEAP_RADIUS_PROT_FAILURE) + +#ifdef __cplusplus +} +#endif + +#endif /* _GSSAPIP_EAP_H_ */ diff --git a/gssapi_eap.h b/gssapi_eap.h new file mode 100644 index 0000000..07ad411 --- /dev/null +++ b/gssapi_eap.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +#ifndef _GSSAPI_EAP_H_ +#define _GSSAPI_EAP_H_ 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * GSS EAP mechanism OIDs. + */ +extern gss_OID GSS_EAP_AES128_CTS_HMAC_SHA1_96_MECHANISM; +extern gss_OID GSS_EAP_AES256_CTS_HMAC_SHA1_96_MECHANISM; + +/* + * Mechanism name OID. + */ +extern gss_OID GSS_EAP_NT_PRINCIPAL_NAME; + +/* + * The libradsec configuration file; defaults to radsec.conf + * in the system configuration directory if unspecified. + */ +extern gss_OID GSS_EAP_CRED_SET_RADIUS_CONFIG_FILE; + +/* + * The stanza in the libradsec configuration file; defaults + * to "gss-eap" if unspecified. + */ +extern gss_OID GSS_EAP_CRED_SET_RADIUS_CONFIG_STANZA; + +/* + * Flags as a 32-bit integer in network byte order, + * followed by a boolean octet indicating whether to + * clear the specified flags (if absent, defaults to + * FALSE, ie. set flags). + */ +extern gss_OID GSS_EAP_CRED_SET_CRED_FLAG; + +/* + * Credentials flag indicating the local attributes + * processing should be skipped. + */ +#define GSS_EAP_DISABLE_LOCAL_ATTRS_FLAG 0x00000001 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _GSSAPI_EAP_H_ */ diff --git a/gsseap_err.et b/gsseap_err.et new file mode 100644 index 0000000..9569512 --- /dev/null +++ b/gsseap_err.et @@ -0,0 +1,143 @@ +# +# Copyright (c) 2010, JANET(UK) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. 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. +# +# 3. Neither the name of JANET(UK) nor the names of its contributors +# may be 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 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. +# + +error_table eapg + +# +# Protocol errors that can be returned in an error token. This should match +# up with makeErrorToken in accept_sec_context.c. +# +error_code GSSEAP_RESERVED, "" +error_code GSSEAP_WRONG_SIZE, "Buffer is incorrect size" +error_code GSSEAP_WRONG_MECH, "Mechanism OID is incorrect" +error_code GSSEAP_BAD_TOK_HEADER, "Token header is malformed or corrupt" +error_code GSSEAP_TOK_TRUNC, "Token is missing data" +error_code GSSEAP_BAD_DIRECTION, "Packet was replayed in wrong direction" +error_code GSSEAP_WRONG_TOK_ID, "Received token ID does not match expected token ID" +error_code GSSEAP_CRIT_EXT_UNAVAILABLE, "Critical extension unavailable" +error_code GSSEAP_MISSING_REQUIRED_EXT, "Missing required extension" +error_code GSSEAP_KEY_UNAVAILABLE, "EAP key unavailable" +error_code GSSEAP_KEY_TOO_SHORT, "EAP key too short" +error_code GSSEAP_RADIUS_AUTH_FAILURE, "Authentication rejected by RADIUS server" +error_code GSSEAP_UNKNOWN_RADIUS_CODE, "Received unknown response code from RADIUS server" +error_code GSSEAP_MISSING_EAP_REQUEST, "RADIUS response is missing EAP request" +error_code GSSEAP_RADIUS_PROT_FAILURE, "Generic RADIUS failure" + +# +# Context errors +# +error_code GSSEAP_CONTEXT_ESTABLISHED, "Context is already fully established" +error_code GSSEAP_CONTEXT_INCOMPLETE, "Attempt to use incomplete security context" +error_code GSSEAP_BAD_CONTEXT_TOKEN, "Context token is malformed or corrupt" +error_code GSSEAP_BAD_ERROR_TOKEN, "Error token is malformed or corrupt" +error_code GSSEAP_BAD_CONTEXT_OPTION, "Bad context option" + +# +# Name errors +# +error_code GSSEAP_BAD_SERVICE_NAME, "Name is not a valid service name" +error_code GSSEAP_BAD_INITIATOR_NAME, "Initiator identity must be a valid name" +error_code GSSEAP_NO_HOSTNAME, "Could not determine local host name" + +# +# Credential errors +# +error_code GSSEAP_BAD_USAGE, "Credential usage type is unknown" +error_code GSSEAP_CRED_USAGE_MISMATCH, "Credential usage does not match requested usage" +error_code GSSEAP_CRED_MECH_MISMATCH, "Credential is not usable with this mechanism" +error_code GSSEAP_BAD_CRED_OPTION, "Bad credential option" +error_code GSSEAP_MISSING_PASSWORD, "Missing initiator credential password" + +# +# Wrap/unwrap/PRF errors +# +error_code GSSEAP_BAD_WRAP_TOKEN, "Bad RFC 4121 wrap or MIC token" +error_code GSSEAP_MISSING_IOV, "IOV is missing required buffer" +error_code GSSEAP_BAD_STREAM_IOV, "Stream IOV can only contain a single data buffer" +error_code GSSEAP_BAD_PADDING_IOV, "Padding IOV is not permitted for RFC 4121 tokens" +error_code GSSEAP_UNKNOWN_QOP, "Unknown quality of protection specified" +error_code GSSEAP_INPUT_TOO_LONG, "PRF input too long" +error_code GSSEAP_BAD_PRF_KEY, "PRF key usage type is unknown" + +# +# libeap errors +# +error_code GSSEAP_LIBEAP_INIT_FAILURE, "Failed to initialize EAP library" +error_code GSSEAP_PEER_SM_INIT_FAILURE, "Failed to create EAP state machine" +error_code GSSEAP_PEER_AUTH_FAILURE, "EAP peer authentication failure" +error_code GSSEAP_PEER_BAD_MESSAGE, "Received bad EAP message" + +# +# RadSec initialisation errors +# +error_code GSSEAP_RADSEC_INIT_FAILURE, "Failed to initialize RadSec library" +error_code GSSEAP_RADSEC_CONTEXT_FAILURE, "Failed to create RadSec context" + +# +# Attribute errors +# +error_code GSSEAP_NO_ATTR_CONTEXT, "Name has no attributes" +error_code GSSEAP_NO_ATTR_PROVIDERS, "Failed to initialize attribute providers" +error_code GSSEAP_NO_SUCH_ATTR, "Unknown naming attribute" +error_code GSSEAP_BAD_ATTR_TOKEN, "Serialised attributes are malformed or corrupt" +error_code GSSEAP_ATTR_CONTEXT_FAILURE, "Failed to initialize attribute context" + +# +# OpenSAML errors +# +error_code GSSEAP_SAML_INIT_FAILURE, "Failed to initialize SAML library" +error_code GSSEAP_SAML_SEC_POLICY_FAILURE, "Failed to process SAML security policy" +error_code GSSEAP_SAML_BINDING_FAILURE, "Failed in SAML binding processing" +error_code GSSEAP_SAML_PROFILE_FAILURE, "Failed to process SAML profile" +error_code GSSEAP_SAML_FATAL_PROFILE_FAILURE, "Non-recoverable failure in SAML profile processing" +error_code GSSEAP_SAML_RETRY_PROFILE_FAILURE, "Temporary failure in SAML profile processing" +error_code GSSEAP_SAML_METADATA_FAILURE, "Failure related to SAML metadata use" + +# +# Shibboleth errors +# +error_code GSSEAP_SHIB_INIT_FAILURE, "Failed to initialize Shibboleth" +error_code GSSEAP_SHIB_ATTR_FAILURE, "Failure during local attribute processing" +error_code GSSEAP_SHIB_ATTR_EXTRACT_FAILURE, "Failed to extract local attributes" +error_code GSSEAP_SHIB_ATTR_FILTER_FAILURE, "Failed to filter local attributes" +error_code GSSEAP_SHIB_ATTR_RESOLVE_FAILURE, "Failed to resolve local attributes" +error_code GSSEAP_SHIB_CONFIG_FAILURE, "Local attribute configuration failure" +error_code GSSEAP_SHIB_LISTENER_FAILURE, "Failed to communicate with local attribute server" + +# +# Extensions +# +error_code GSSEAP_BINDINGS_MISMATCH, "Channel bindings do not match" +error_code GSSEAP_NO_MECHGLUE_SYMBOL, "Could not find symbol in mechanism glue" +error_code GSSEAP_BAD_INVOCATION, "Bad mechanism invoke OID" + +end diff --git a/import_name.c b/import_name.c new file mode 100644 index 0000000..d616fc0 --- /dev/null +++ b/import_name.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Deserialise a name. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_import_name(OM_uint32 *minor, + gss_buffer_t import_name_buffer, + gss_OID input_name_type, + gss_name_t *output_name) +{ + return gssEapImportName(minor, import_name_buffer, + input_name_type, output_name); +} diff --git a/import_sec_context.c b/import_sec_context.c new file mode 100644 index 0000000..6ff212c --- /dev/null +++ b/import_sec_context.c @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Deserialise a context handle. + */ + +#include "gssapiP_eap.h" + +#define UPDATE_REMAIN(n) do { \ + p += (n); \ + remain -= (n); \ + } while (0) + +#define CHECK_REMAIN(n) do { \ + if (remain < (n)) { \ + *minor = GSSEAP_TOK_TRUNC; \ + return GSS_S_DEFECTIVE_TOKEN; \ + } \ + } while (0) + +static OM_uint32 +gssEapImportPartialContext(OM_uint32 *minor, + unsigned char **pBuf, + size_t *pRemain, + gss_ctx_id_t ctx) +{ + OM_uint32 major; + unsigned char *p = *pBuf; + size_t remain = *pRemain; + gss_buffer_desc buf; + size_t serverLen; + + /* Selected RADIUS server */ + CHECK_REMAIN(4); + serverLen = load_uint32_be(p); + UPDATE_REMAIN(4); + + if (serverLen != 0) { + CHECK_REMAIN(serverLen); + + ctx->acceptorCtx.radServer = GSSEAP_MALLOC(serverLen + 1); + if (ctx->acceptorCtx.radServer == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + memcpy(ctx->acceptorCtx.radServer, p, serverLen); + ctx->acceptorCtx.radServer[serverLen] = '\0'; + + UPDATE_REMAIN(serverLen); + } + + /* RADIUS state blob */ + CHECK_REMAIN(4); + buf.length = load_uint32_be(p); + UPDATE_REMAIN(4); + + if (buf.length != 0) { + CHECK_REMAIN(buf.length); + + buf.value = p; + + major = duplicateBuffer(minor, &buf, &ctx->acceptorCtx.state); + if (GSS_ERROR(major)) + return major; + + UPDATE_REMAIN(buf.length); + } + + *pBuf = p; + *pRemain = remain; + + return GSS_S_COMPLETE; +} + +static OM_uint32 +importMechanismOid(OM_uint32 *minor, + unsigned char **pBuf, + size_t *pRemain, + gss_OID *pOid) +{ + OM_uint32 major; + unsigned char *p = *pBuf; + size_t remain = *pRemain; + gss_OID_desc oidBuf; + + oidBuf.length = load_uint32_be(p); + if (remain < 4 + oidBuf.length || oidBuf.length == 0) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + oidBuf.elements = &p[4]; + + if (!gssEapIsConcreteMechanismOid(&oidBuf)) { + *minor = GSSEAP_WRONG_MECH; + return GSS_S_BAD_MECH; + } + + if (!gssEapInternalizeOid(&oidBuf, pOid)) { + major = duplicateOid(minor, &oidBuf, pOid); + if (GSS_ERROR(major)) + return major; + } + + *pBuf += 4 + oidBuf.length; + *pRemain -= 4 + oidBuf.length; + + *minor = 0; + return GSS_S_COMPLETE; +} + +static OM_uint32 +importKerberosKey(OM_uint32 *minor, + unsigned char **pBuf, + size_t *pRemain, + krb5_cksumtype *checksumType, + krb5_enctype *pEncryptionType, + krb5_keyblock *key) +{ + unsigned char *p = *pBuf; + size_t remain = *pRemain; + OM_uint32 encryptionType; + OM_uint32 length; + gss_buffer_desc tmp; + + if (remain < 12) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + *checksumType = load_uint32_be(&p[0]); + encryptionType = load_uint32_be(&p[4]); + length = load_uint32_be(&p[8]); + + if ((length != 0) != (encryptionType != ENCTYPE_NULL)) { + *minor = GSSEAP_BAD_CONTEXT_TOKEN; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (remain - 12 < length) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + if (load_buffer(&p[12], length, &tmp) == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + KRB_KEY_TYPE(key) = encryptionType; + KRB_KEY_LENGTH(key) = tmp.length; + KRB_KEY_DATA(key) = (unsigned char *)tmp.value; + + *pBuf += 12 + length; + *pRemain -= 12 + length; + *pEncryptionType = encryptionType; + + *minor = 0; + return GSS_S_COMPLETE; +} + +static OM_uint32 +importName(OM_uint32 *minor, + unsigned char **pBuf, + size_t *pRemain, + gss_name_t *pName) +{ + OM_uint32 major; + unsigned char *p = *pBuf; + size_t remain = *pRemain; + gss_buffer_desc tmp; + + if (remain < 4) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + tmp.length = load_uint32_be(p); + if (tmp.length != 0) { + if (remain - 4 < tmp.length) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + tmp.value = p + 4; + + major = gssEapImportNameInternal(minor, &tmp, pName, + EXPORT_NAME_FLAG_COMPOSITE); + if (GSS_ERROR(major)) + return major; + } + + *pBuf += 4 + tmp.length; + *pRemain -= 4 + tmp.length; + + *minor = 0; + return GSS_S_COMPLETE; +} + +static OM_uint32 +gssEapImportContext(OM_uint32 *minor, + gss_buffer_t token, + gss_ctx_id_t ctx) +{ + OM_uint32 major; + unsigned char *p = (unsigned char *)token->value; + size_t remain = token->length; + + if (remain < 16) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + if (load_uint32_be(&p[0]) != EAP_EXPORT_CONTEXT_V1) { + *minor = GSSEAP_BAD_CONTEXT_TOKEN; + return GSS_S_DEFECTIVE_TOKEN; + } + ctx->state = load_uint32_be(&p[4]); + ctx->flags = load_uint32_be(&p[8]); + ctx->gssFlags = load_uint32_be(&p[12]); + p += 16; + remain -= 16; + + /* Validate state */ + if (ctx->state < GSSEAP_STATE_IDENTITY || + ctx->state > GSSEAP_STATE_ESTABLISHED) + return GSS_S_DEFECTIVE_TOKEN; + + /* Only acceptor can export partial context tokens */ + if (CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) + return GSS_S_DEFECTIVE_TOKEN; + + major = importMechanismOid(minor, &p, &remain, &ctx->mechanismUsed); + if (GSS_ERROR(major)) + return major; + + major = importKerberosKey(minor, &p, &remain, + &ctx->checksumType, + &ctx->encryptionType, + &ctx->rfc3961Key); + if (GSS_ERROR(major)) + return major; + + major = importName(minor, &p, &remain, &ctx->initiatorName); + if (GSS_ERROR(major)) + return major; + + major = importName(minor, &p, &remain, &ctx->acceptorName); + if (GSS_ERROR(major)) + return major; + + /* Check that, if context is established, names are valid */ + if (CTX_IS_ESTABLISHED(ctx) && + (CTX_IS_INITIATOR(ctx) ? ctx->acceptorName == GSS_C_NO_NAME + : ctx->initiatorName == GSS_C_NO_NAME)) { + return GSS_S_DEFECTIVE_TOKEN; + } + + if (remain < 24 + sequenceSize(ctx->seqState)) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + ctx->expiryTime = (time_t)load_uint64_be(&p[0]); + ctx->sendSeq = load_uint64_be(&p[8]); + ctx->recvSeq = load_uint64_be(&p[16]); + p += 24; + remain -= 24; + + major = sequenceInternalize(minor, &ctx->seqState, &p, &remain); + if (GSS_ERROR(major)) + return major; + + /* + * The partial context should only be expected for unestablished + * acceptor contexts. + */ + if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) { + assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0); + + major = gssEapImportPartialContext(minor, &p, &remain, ctx); + if (GSS_ERROR(major)) + return major; + } + +#ifdef GSSEAP_DEBUG + assert(remain == 0); +#endif + + major = GSS_S_COMPLETE; + *minor = 0; + + return major; +} + +OM_uint32 +gss_import_sec_context(OM_uint32 *minor, + gss_buffer_t interprocess_token, + gss_ctx_id_t *context_handle) +{ + OM_uint32 major, tmpMinor; + gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; + + *context_handle = GSS_C_NO_CONTEXT; + + if (interprocess_token == GSS_C_NO_BUFFER || + interprocess_token->length == 0) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + major = gssEapAllocContext(minor, &ctx); + if (GSS_ERROR(major)) + goto cleanup; + + major = gssEapImportContext(minor, interprocess_token, ctx); + if (GSS_ERROR(major)) + goto cleanup; + + *context_handle = ctx; + +cleanup: + if (GSS_ERROR(major)) + gssEapReleaseContext(&tmpMinor, &ctx); + + return major; +} diff --git a/indicate_mechs.c b/indicate_mechs.c new file mode 100644 index 0000000..ecda509 --- /dev/null +++ b/indicate_mechs.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Enumerate the supported mechanism OIDs. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_indicate_mechs(OM_uint32 *minor, + gss_OID_set *mech_set) +{ + return gssEapIndicateMechs(minor, mech_set); +} diff --git a/init_sec_context.c b/init_sec_context.c new file mode 100644 index 0000000..ae0a65c --- /dev/null +++ b/init_sec_context.c @@ -0,0 +1,868 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Establish a security context on the initiator (client). These functions + * wrap around libeap. + */ + +#include "gssapiP_eap.h" + +#ifdef GSSEAP_ENABLE_REAUTH +static OM_uint32 +eapGssSmInitGssReauth(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken); +#endif + +static OM_uint32 +policyVariableToFlag(enum eapol_bool_var variable) +{ + OM_uint32 flag = 0; + + switch (variable) { + case EAPOL_eapSuccess: + flag = CTX_FLAG_EAP_SUCCESS; + break; + case EAPOL_eapRestart: + flag = CTX_FLAG_EAP_RESTART; + break; + case EAPOL_eapFail: + flag = CTX_FLAG_EAP_FAIL; + break; + case EAPOL_eapResp: + flag = CTX_FLAG_EAP_RESP; + break; + case EAPOL_eapNoResp: + flag = CTX_FLAG_EAP_NO_RESP; + break; + case EAPOL_eapReq: + flag = CTX_FLAG_EAP_REQ; + break; + case EAPOL_portEnabled: + flag = CTX_FLAG_EAP_PORT_ENABLED; + break; + case EAPOL_altAccept: + flag = CTX_FLAG_EAP_ALT_ACCEPT; + break; + case EAPOL_altReject: + flag = CTX_FLAG_EAP_ALT_REJECT; + break; + } + + return flag; +} + +static struct eap_peer_config * +peerGetConfig(void *ctx) +{ + gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx; + + return &gssCtx->initiatorCtx.eapPeerConfig; +} + +static Boolean +peerGetBool(void *data, enum eapol_bool_var variable) +{ + gss_ctx_id_t ctx = data; + OM_uint32 flag; + + if (ctx == GSS_C_NO_CONTEXT) + return FALSE; + + flag = policyVariableToFlag(variable); + + return ((ctx->flags & flag) != 0); +} + +static void +peerSetBool(void *data, enum eapol_bool_var variable, + Boolean value) +{ + gss_ctx_id_t ctx = data; + OM_uint32 flag; + + if (ctx == GSS_C_NO_CONTEXT) + return; + + flag = policyVariableToFlag(variable); + + if (value) + ctx->flags |= flag; + else + ctx->flags &= ~(flag); +} + +static unsigned int +peerGetInt(void *data, enum eapol_int_var variable) +{ + gss_ctx_id_t ctx = data; + + if (ctx == GSS_C_NO_CONTEXT) + return FALSE; + + assert(CTX_IS_INITIATOR(ctx)); + + switch (variable) { + case EAPOL_idleWhile: + return ctx->initiatorCtx.idleWhile; + break; + } + + return 0; +} + +static void +peerSetInt(void *data, enum eapol_int_var variable, + unsigned int value) +{ + gss_ctx_id_t ctx = data; + + if (ctx == GSS_C_NO_CONTEXT) + return; + + assert(CTX_IS_INITIATOR(ctx)); + + switch (variable) { + case EAPOL_idleWhile: + ctx->initiatorCtx.idleWhile = value; + break; + } +} + +static struct wpabuf * +peerGetEapReqData(void *ctx) +{ + gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx; + + return &gssCtx->initiatorCtx.reqData; +} + +static void +peerSetConfigBlob(void *ctx, struct wpa_config_blob *blob) +{ +} + +static const struct wpa_config_blob * +peerGetConfigBlob(void *ctx, const char *name) +{ + return NULL; +} + +static void +peerNotifyPending(void *ctx) +{ +} + +static struct eapol_callbacks gssEapPolicyCallbacks = { + peerGetConfig, + peerGetBool, + peerSetBool, + peerGetInt, + peerSetInt, + peerGetEapReqData, + peerSetConfigBlob, + peerGetConfigBlob, + peerNotifyPending, +}; + +#ifdef GSSEAP_DEBUG +extern int wpa_debug_level; +#endif + +static OM_uint32 +peerConfigInit(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx) +{ + krb5_context krbContext; + struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig; + krb5_error_code code; + char *identity; + + eapPeerConfig->identity = NULL; + eapPeerConfig->identity_len = 0; + eapPeerConfig->password = NULL; + eapPeerConfig->password_len = 0; + + assert(cred != GSS_C_NO_CREDENTIAL); + + GSSEAP_KRB_INIT(&krbContext); + + eapPeerConfig->fragment_size = 1024; +#ifdef GSSEAP_DEBUG + wpa_debug_level = 0; +#endif + + assert(cred->name != GSS_C_NO_NAME); + + if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) { + *minor = GSSEAP_BAD_INITIATOR_NAME; + return GSS_S_BAD_NAME; + } + + code = krb5_unparse_name(krbContext, cred->name->krbPrincipal, &identity); + if (code != 0) { + *minor = code; + return GSS_S_FAILURE; + } + + eapPeerConfig->identity = (unsigned char *)identity; + eapPeerConfig->identity_len = strlen(identity); + eapPeerConfig->password = (unsigned char *)cred->password.value; + eapPeerConfig->password_len = cred->password.length; + + *minor = 0; + return GSS_S_COMPLETE; +} + +static OM_uint32 +peerConfigFree(OM_uint32 *minor, + gss_ctx_id_t ctx) +{ + krb5_context krbContext; + struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig; + + GSSEAP_KRB_INIT(&krbContext); + + krb5_free_unparsed_name(krbContext, (char *)eapPeerConfig->identity); + + *minor = 0; + return GSS_S_COMPLETE; +} + +/* + * Mark an initiator context as ready for cryptographic operations + */ +static OM_uint32 +initReady(OM_uint32 *minor, gss_ctx_id_t ctx, OM_uint32 reqFlags) +{ + OM_uint32 major; + const unsigned char *key; + size_t keyLength; + +#if 1 + /* XXX actually check for mutual auth */ + if (reqFlags & GSS_C_MUTUAL_FLAG) + ctx->gssFlags |= GSS_C_MUTUAL_FLAG; +#endif + + /* Cache encryption type derived from selected mechanism OID */ + major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType); + if (GSS_ERROR(major)) + return major; + + if (!eap_key_available(ctx->initiatorCtx.eap)) { + *minor = GSSEAP_KEY_UNAVAILABLE; + return GSS_S_UNAVAILABLE; + } + + key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength); + + if (keyLength < EAP_EMSK_LEN) { + *minor = GSSEAP_KEY_TOO_SHORT; + return GSS_S_UNAVAILABLE; + } + + major = gssEapDeriveRfc3961Key(minor, + &key[EAP_EMSK_LEN / 2], + EAP_EMSK_LEN / 2, + ctx->encryptionType, + &ctx->rfc3961Key); + if (GSS_ERROR(major)) + return major; + + major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key, + &ctx->checksumType); + if (GSS_ERROR(major)) + return major; + + major = sequenceInit(minor, + &ctx->seqState, + ctx->recvSeq, + ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0), + ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0), + TRUE); + if (GSS_ERROR(major)) + return major; + + *minor = 0; + return GSS_S_COMPLETE; +} + +static OM_uint32 +initBegin(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + OM_uint32 major; + + assert(cred != GSS_C_NO_CREDENTIAL); + + if (cred->expiryTime) + ctx->expiryTime = cred->expiryTime; + else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE) + ctx->expiryTime = 0; + else + ctx->expiryTime = time(NULL) + timeReq; + + /* + * The credential mutex protects its name, however we need to + * explicitly lock the acceptor name (unlikely as it may be + * that it has attributes set on it). + */ + major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName); + if (GSS_ERROR(major)) + return major; + + GSSEAP_MUTEX_LOCK(&target->mutex); + + major = gssEapDuplicateName(minor, target, &ctx->acceptorName); + if (GSS_ERROR(major)) { + GSSEAP_MUTEX_UNLOCK(&target->mutex); + return major; + } + + GSSEAP_MUTEX_UNLOCK(&target->mutex); + + if (mech == GSS_C_NULL_OID) { + major = gssEapDefaultMech(minor, &ctx->mechanismUsed); + } else if (gssEapIsConcreteMechanismOid(mech)) { + if (!gssEapInternalizeOid(mech, &ctx->mechanismUsed)) + major = duplicateOid(minor, mech, &ctx->mechanismUsed); + } else { + major = GSS_S_BAD_MECH; + *minor = GSSEAP_WRONG_MECH; + } + if (GSS_ERROR(major)) + return major; + + /* If credentials were provided, check they're usable with this mech */ + if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) { + *minor = GSSEAP_CRED_MECH_MISMATCH; + return GSS_S_BAD_MECH; + } + + *minor = 0; + return GSS_S_COMPLETE; +} + +static OM_uint32 +eapGssSmInitIdentity(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + OM_uint32 major; + int initialContextToken; + + initialContextToken = (inputToken->length == 0); + if (!initialContextToken) { + *minor = GSSEAP_WRONG_SIZE; + return GSS_S_DEFECTIVE_TOKEN; + } + + major = initBegin(minor, cred, ctx, target, mech, + reqFlags, timeReq, chanBindings, + inputToken, outputToken); + if (GSS_ERROR(major)) + return major; + + ctx->state = GSSEAP_STATE_AUTHENTICATE; + + *minor = 0; + return GSS_S_CONTINUE_NEEDED; +} + +static struct wpabuf emptyWpaBuffer; + +static OM_uint32 +eapGssSmInitAuthenticate(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + OM_uint32 major; + OM_uint32 tmpMinor; + int code; + struct wpabuf *resp = NULL; + int initialContextToken; + + *minor = 0; + + initialContextToken = (inputToken == GSS_C_NO_BUFFER || + inputToken->length == 0); + + major = peerConfigInit(minor, cred, ctx); + if (GSS_ERROR(major)) + goto cleanup; + + if (ctx->initiatorCtx.eap == NULL) { + struct eap_config eapConfig; + + memset(&eapConfig, 0, sizeof(eapConfig)); + + ctx->initiatorCtx.eap = eap_peer_sm_init(ctx, + &gssEapPolicyCallbacks, + ctx, + &eapConfig); + if (ctx->initiatorCtx.eap == NULL) { + major = GSS_S_FAILURE; + *minor = GSSEAP_PEER_SM_INIT_FAILURE; + goto cleanup; + } + + ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED; + } + + ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */ + + wpabuf_set(&ctx->initiatorCtx.reqData, + inputToken->value, inputToken->length); + + major = GSS_S_CONTINUE_NEEDED; + + code = eap_peer_sm_step(ctx->initiatorCtx.eap); + if (ctx->flags & CTX_FLAG_EAP_RESP) { + ctx->flags &= ~(CTX_FLAG_EAP_RESP); + + resp = eap_get_eapRespData(ctx->initiatorCtx.eap); + } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) { + major = initReady(minor, ctx, reqFlags); + if (GSS_ERROR(major)) + goto cleanup; + + ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS); + major = GSS_S_CONTINUE_NEEDED; + ctx->state = GSSEAP_STATE_EXTENSIONS_REQ; + } else if (ctx->flags & CTX_FLAG_EAP_FAIL) { + major = GSS_S_DEFECTIVE_CREDENTIAL; + *minor = GSSEAP_PEER_AUTH_FAILURE; + } else if (code == 0 && initialContextToken) { + resp = &emptyWpaBuffer; + major = GSS_S_CONTINUE_NEEDED; + } else { + major = GSS_S_DEFECTIVE_TOKEN; + *minor = GSSEAP_PEER_BAD_MESSAGE; + } + +cleanup: + if (resp != NULL) { + OM_uint32 tmpMajor; + gss_buffer_desc respBuf; + + assert(major == GSS_S_CONTINUE_NEEDED); + + respBuf.length = wpabuf_len(resp); + respBuf.value = (void *)wpabuf_head(resp); + + tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken); + if (GSS_ERROR(tmpMajor)) { + major = tmpMajor; + *minor = tmpMinor; + } + } + + wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0); + peerConfigFree(&tmpMinor, ctx); + + return major; +} + +static OM_uint32 +eapGssSmInitExtensionsReq(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + OM_uint32 major; + + major = gssEapMakeExtensions(minor, cred, ctx, chanBindings, outputToken); + if (GSS_ERROR(major)) + return major; + + assert(outputToken->value != NULL); + + ctx->state = GSSEAP_STATE_EXTENSIONS_RESP; + + *minor = 0; + return GSS_S_CONTINUE_NEEDED; +} + +static OM_uint32 +eapGssSmInitExtensionsResp(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + OM_uint32 major; + + major = gssEapVerifyExtensions(minor, cred, ctx, chanBindings, inputToken); + if (GSS_ERROR(major)) + return major; + + ctx->state = GSSEAP_STATE_ESTABLISHED; + + *minor = 0; + return GSS_S_COMPLETE; +} + +static OM_uint32 +eapGssSmInitEstablished(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + /* Called with already established context */ + *minor = GSSEAP_CONTEXT_ESTABLISHED; + return GSS_S_BAD_STATUS; +} + +static OM_uint32 +eapGssSmInitError(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + OM_uint32 major; + unsigned char *p; + + if (inputToken->length < 8) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + p = (unsigned char *)inputToken->value; + + major = load_uint32_be(&p[0]); + *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]); + + if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) { + major = GSS_S_FAILURE; + *minor = GSSEAP_BAD_ERROR_TOKEN; + } + + return major; +} + +static struct gss_eap_initiator_sm { + enum gss_eap_token_type inputTokenType; + enum gss_eap_token_type outputTokenType; + OM_uint32 (*processToken)(OM_uint32 *, + gss_cred_id_t, + gss_ctx_id_t, + gss_name_t, + gss_OID, + OM_uint32, + OM_uint32, + gss_channel_bindings_t, + gss_buffer_t, + gss_buffer_t); +} eapGssInitiatorSm[] = { + { TOK_TYPE_NONE, TOK_TYPE_EAP_RESP, eapGssSmInitIdentity }, + { TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP, eapGssSmInitAuthenticate }, + { TOK_TYPE_NONE, TOK_TYPE_EXT_REQ, eapGssSmInitExtensionsReq }, + { TOK_TYPE_EXT_RESP, TOK_TYPE_NONE, eapGssSmInitExtensionsResp }, + { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmInitEstablished }, + { TOK_TYPE_CONTEXT_ERR, TOK_TYPE_NONE, eapGssSmInitError }, +#ifdef GSSEAP_ENABLE_REAUTH + { TOK_TYPE_GSS_REAUTH, TOK_TYPE_GSS_REAUTH, eapGssSmInitGssReauth }, +#endif +}; + +OM_uint32 +gss_init_sec_context(OM_uint32 *minor, + 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) +{ + OM_uint32 major; + OM_uint32 tmpMajor, tmpMinor; + gss_ctx_id_t ctx = *context_handle; + struct gss_eap_initiator_sm *sm = NULL; + gss_buffer_desc innerInputToken; + gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER; + enum gss_eap_token_type tokType; + int initialContextToken = 0; + + *minor = 0; + + output_token->length = 0; + output_token->value = NULL; + + if (ctx == GSS_C_NO_CONTEXT) { + if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) { + *minor = GSSEAP_WRONG_SIZE; + return GSS_S_DEFECTIVE_TOKEN; + } + + major = gssEapAllocContext(minor, &ctx); + if (GSS_ERROR(major)) + return major; + + ctx->flags |= CTX_FLAG_INITIATOR; + + initialContextToken = 1; + *context_handle = ctx; + } + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (cred == GSS_C_NO_CREDENTIAL) { + if (ctx->defaultCred == GSS_C_NO_CREDENTIAL) { + major = gssEapAcquireCred(minor, + GSS_C_NO_NAME, + GSS_C_NO_BUFFER, + time_req, + GSS_C_NO_OID_SET, + GSS_C_INITIATE, + &ctx->defaultCred, + NULL, + NULL); + if (GSS_ERROR(major)) + goto cleanup; + } + + cred = ctx->defaultCred; + } + + GSSEAP_MUTEX_LOCK(&cred->mutex); + +#ifdef GSSEAP_ENABLE_REAUTH + if (initialContextToken && gssEapCanReauthP(cred, target_name, time_req)) + ctx->state = GSSEAP_STATE_KRB_REAUTH; +#endif + + if ((cred->flags & CRED_FLAG_INITIATE) == 0) { + major = GSS_S_NO_CRED; + *minor = GSSEAP_CRED_USAGE_MISMATCH; + goto cleanup; + } + + sm = &eapGssInitiatorSm[ctx->state]; + + if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) { + major = gssEapVerifyToken(minor, ctx, input_token, + &tokType, &innerInputToken); + if (GSS_ERROR(major)) + goto cleanup; + + if (tokType == TOK_TYPE_CONTEXT_ERR) { + ctx->state = GSSEAP_STATE_ERROR; + } else if (tokType != sm->inputTokenType) { + major = GSS_S_DEFECTIVE_TOKEN; + *minor = GSSEAP_WRONG_TOK_ID; + goto cleanup; + } + } else { + innerInputToken.length = 0; + innerInputToken.value = NULL; + } + + /* + * Advance through state machine whilst empty tokens are emitted and + * the status is not GSS_S_COMPLETE or an error status. + */ + do { + sm = &eapGssInitiatorSm[ctx->state]; + + major = (sm->processToken)(minor, + cred, + ctx, + target_name, + mech_type, + req_flags, + time_req, + input_chan_bindings, + &innerInputToken, + &innerOutputToken); + if (GSS_ERROR(major)) + goto cleanup; + } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.value == NULL); + + if (actual_mech_type != NULL) { + if (!gssEapInternalizeOid(ctx->mechanismUsed, actual_mech_type)) + duplicateOid(&tmpMinor, ctx->mechanismUsed, actual_mech_type); + } + if (innerOutputToken.value != NULL) { + tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken, + sm->outputTokenType, output_token); + if (GSS_ERROR(tmpMajor)) { + major = tmpMajor; + *minor = tmpMinor; + goto cleanup; + } + } + if (ret_flags != NULL) + *ret_flags = ctx->gssFlags; + if (time_rec != NULL) + gssEapContextTime(&tmpMinor, ctx, time_rec); + + assert(ctx->state == GSSEAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED); + +cleanup: + if (cred != GSS_C_NO_CREDENTIAL) + GSSEAP_MUTEX_UNLOCK(&cred->mutex); + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + if (GSS_ERROR(major)) + gssEapReleaseContext(&tmpMinor, context_handle); + + gss_release_buffer(&tmpMinor, &innerOutputToken); + + return major; +} + +#ifdef GSSEAP_ENABLE_REAUTH +static OM_uint32 +eapGssSmInitGssReauth(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_name_t target, + gss_OID mech, + OM_uint32 reqFlags, + OM_uint32 timeReq, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken, + gss_buffer_t outputToken) +{ + OM_uint32 major, tmpMinor; + gss_name_t mechTarget = GSS_C_NO_NAME; + gss_OID actualMech = GSS_C_NO_OID; + OM_uint32 gssFlags, timeRec; + + assert(cred != GSS_C_NO_CREDENTIAL); + + ctx->flags |= CTX_FLAG_KRB_REAUTH; + + if (inputToken->length == 0) { + major = initBegin(minor, cred, ctx, target, mech, + reqFlags, timeReq, chanBindings, + inputToken, outputToken); + if (GSS_ERROR(major)) + goto cleanup; + } + + major = gssEapMechToGlueName(minor, target, &mechTarget); + if (GSS_ERROR(major)) + goto cleanup; + + major = gssInitSecContext(minor, + cred->krbCred, + &ctx->kerberosCtx, + mechTarget, + (gss_OID)gss_mech_krb5, + reqFlags, /* | GSS_C_DCE_STYLE, */ + timeReq, + chanBindings, + inputToken, + &actualMech, + outputToken, + &gssFlags, + &timeRec); + if (GSS_ERROR(major)) + goto cleanup; + + ctx->gssFlags = gssFlags; + + if (major == GSS_S_COMPLETE) { + major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec); + if (GSS_ERROR(major)) + goto cleanup; + ctx->state = GSSEAP_STATE_ESTABLISHED; + } + +cleanup: + gssReleaseName(&tmpMinor, &mechTarget); + + return major; +} +#endif /* GSSEAP_ENABLE_REAUTH */ diff --git a/inquire_attrs_for_mech.c b/inquire_attrs_for_mech.c new file mode 100644 index 0000000..dec08c8 --- /dev/null +++ b/inquire_attrs_for_mech.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Enumerate the features supported by the GSS EAP mechanism. + */ + +#include "gssapiP_eap.h" + +#define MA_ADD(ma, set) do { \ + major = gss_add_oid_set_member(minor, (gss_OID)(ma), (set)); \ + if (GSS_ERROR(major)) \ + goto cleanup; \ + } while (0) + +#define MA_SUPPORTED(ma) MA_ADD((ma), mech_attrs) +#define MA_KNOWN(ma) MA_ADD((ma), known_mech_attrs) + +#ifndef HAVE_GSS_INQUIRE_ATTRS_FOR_MECH +typedef const gss_OID_desc *gss_const_OID; +#endif + +OM_uint32 +gss_inquire_attrs_for_mech(OM_uint32 *minor, + gss_const_OID mech_oid, + gss_OID_set *mech_attrs, + gss_OID_set *known_mech_attrs) +{ + OM_uint32 major, tmpMinor; + + if (mech_attrs != NULL) + *mech_attrs = GSS_C_NO_OID_SET; + if (known_mech_attrs != NULL) + *known_mech_attrs = GSS_C_NO_OID_SET; + + if (!gssEapIsConcreteMechanismOid((const gss_OID)mech_oid)) { + *minor = GSSEAP_WRONG_MECH; + return GSS_S_BAD_MECH; + } + + if (mech_attrs != NULL) { + major = gss_create_empty_oid_set(minor, mech_attrs); + if (GSS_ERROR(major)) + goto cleanup; + +#ifdef HAVE_GSS_INQUIRE_ATTRS_FOR_MECH + if (oidEqual(mech_oid, GSS_EAP_MECHANISM)) + MA_SUPPORTED(GSS_C_MA_MECH_PSEUDO); + else + 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_AUTH_INIT_INIT); + 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_REPLAY_DET); + MA_SUPPORTED(GSS_C_MA_OOS_DET); + MA_SUPPORTED(GSS_C_MA_CBINDINGS); + MA_SUPPORTED(GSS_C_MA_CTX_TRANS); +#endif + } + + if (known_mech_attrs != NULL) { + major = gss_create_empty_oid_set(minor, known_mech_attrs); + if (GSS_ERROR(major)) + goto cleanup; + +#ifdef HAVE_GSS_INQUIRE_ATTRS_FOR_MECH + MA_KNOWN(GSS_C_MA_MECH_CONCRETE); + MA_KNOWN(GSS_C_MA_MECH_PSEUDO); + MA_KNOWN(GSS_C_MA_MECH_COMPOSITE); + MA_KNOWN(GSS_C_MA_MECH_NEGO); + MA_KNOWN(GSS_C_MA_MECH_GLUE); + MA_KNOWN(GSS_C_MA_NOT_MECH); + MA_KNOWN(GSS_C_MA_DEPRECATED); + MA_KNOWN(GSS_C_MA_NOT_DFLT_MECH); + MA_KNOWN(GSS_C_MA_ITOK_FRAMED); + MA_KNOWN(GSS_C_MA_AUTH_INIT); + MA_KNOWN(GSS_C_MA_AUTH_TARG); + MA_KNOWN(GSS_C_MA_AUTH_INIT_INIT); + MA_KNOWN(GSS_C_MA_AUTH_TARG_INIT); + MA_KNOWN(GSS_C_MA_AUTH_INIT_ANON); + MA_KNOWN(GSS_C_MA_AUTH_TARG_ANON); + MA_KNOWN(GSS_C_MA_DELEG_CRED); + MA_KNOWN(GSS_C_MA_INTEG_PROT); + MA_KNOWN(GSS_C_MA_CONF_PROT); + MA_KNOWN(GSS_C_MA_MIC); + MA_KNOWN(GSS_C_MA_WRAP); + MA_KNOWN(GSS_C_MA_PROT_READY); + MA_KNOWN(GSS_C_MA_REPLAY_DET); + MA_KNOWN(GSS_C_MA_OOS_DET); + MA_KNOWN(GSS_C_MA_CBINDINGS); + MA_KNOWN(GSS_C_MA_PFS); + MA_KNOWN(GSS_C_MA_COMPRESS); + MA_KNOWN(GSS_C_MA_CTX_TRANS); +#endif + } + + major = GSS_S_COMPLETE; + *minor = 0; + +cleanup: + if (GSS_ERROR(major)) { + gss_release_oid_set(&tmpMinor, mech_attrs); + gss_release_oid_set(&tmpMinor, known_mech_attrs); + } + + return major; +} diff --git a/inquire_context.c b/inquire_context.c new file mode 100644 index 0000000..e3c67e9 --- /dev/null +++ b/inquire_context.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Return context handle properties. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_inquire_context(OM_uint32 *minor, + gss_ctx_id_t ctx, + 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 *open) +{ + OM_uint32 major, tmpMinor; + + if (ctx == GSS_C_NO_CONTEXT) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (src_name != NULL) { + major = gssEapDuplicateName(minor, ctx->initiatorName, src_name); + if (GSS_ERROR(major)) + goto cleanup; + } + + if (targ_name != NULL) { + major = gssEapDuplicateName(minor, ctx->acceptorName, targ_name); + if (GSS_ERROR(major)) + goto cleanup; + } + + if (lifetime_rec != NULL) { + time_t now, lifetime; + + if (ctx->expiryTime == 0) { + lifetime = GSS_C_INDEFINITE; + } else { + now = time(NULL); + lifetime = now - ctx->expiryTime; + if (lifetime < 0) + lifetime = 0; + } + + *lifetime_rec = lifetime; + } + + if (mech_type != NULL) { + if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type)) { + major = duplicateOid(minor, ctx->mechanismUsed, mech_type); + if (GSS_ERROR(major)) + goto cleanup; + } + } + + if (ctx_flags != NULL) { + *ctx_flags = ctx->gssFlags; + } + + if (locally_initiated != NULL) { + *locally_initiated = CTX_IS_INITIATOR(ctx); + } + + if (open != NULL) { + *open = CTX_IS_ESTABLISHED(ctx); + } + + major = GSS_S_COMPLETE; + *minor = 0; + +cleanup: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + if (GSS_ERROR(major)) { + gssEapReleaseName(&tmpMinor, src_name); + gssEapReleaseName(&tmpMinor, targ_name); + } + + return major; +} diff --git a/inquire_cred.c b/inquire_cred.c new file mode 100644 index 0000000..01c3b4a --- /dev/null +++ b/inquire_cred.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Return credential handle properties. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_inquire_cred(OM_uint32 *minor, + gss_cred_id_t cred, + gss_name_t *name, + OM_uint32 *pLifetime, + gss_cred_usage_t *cred_usage, + gss_OID_set *mechanisms) +{ + OM_uint32 major; + + if (cred == NULL) { + *minor = EINVAL; + return GSS_S_NO_CRED; + } + + GSSEAP_MUTEX_LOCK(&cred->mutex); + + if (name != NULL) { + major = gssEapDuplicateName(minor, cred->name, name); + if (GSS_ERROR(major)) + goto cleanup; + } + + if (pLifetime != NULL) { + time_t now, lifetime; + + if (cred->expiryTime == 0) { + lifetime = GSS_C_INDEFINITE; + } else { + now = time(NULL); + lifetime = now - cred->expiryTime; + if (lifetime < 0) + lifetime = 0; + } + + *pLifetime = lifetime; + } + + if (cred_usage != NULL) { + OM_uint32 flags = (cred->flags & (CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT)); + + switch (flags) { + case CRED_FLAG_INITIATE: + *cred_usage = GSS_C_INITIATE; + break; + case CRED_FLAG_ACCEPT: + *cred_usage = GSS_C_ACCEPT; + break; + default: + *cred_usage = GSS_C_BOTH; + break; + } + } + + if (mechanisms != NULL) { + if (cred->mechanisms != GSS_C_NO_OID_SET) + major = duplicateOidSet(minor, cred->mechanisms, mechanisms); + else + major = gssEapIndicateMechs(minor, mechanisms); + if (GSS_ERROR(major)) + goto cleanup; + } + + major = GSS_S_COMPLETE; + *minor = 0; + +cleanup: + GSSEAP_MUTEX_UNLOCK(&cred->mutex); + + return major; +} diff --git a/inquire_cred_by_oid.c b/inquire_cred_by_oid.c new file mode 100644 index 0000000..33bfce3 --- /dev/null +++ b/inquire_cred_by_oid.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Return extended credential handle properties. + */ + +#include "gssapiP_eap.h" + +static struct { + gss_OID_desc oid; + OM_uint32 (*inquire)(OM_uint32 *, const gss_cred_id_t, + const gss_OID, gss_buffer_set_t *); +} inquireCredOps[] = { +}; + +OM_uint32 +gss_inquire_cred_by_oid(OM_uint32 *minor, + const gss_cred_id_t cred_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + OM_uint32 major; + int i; + + *data_set = GSS_C_NO_BUFFER_SET; + + if (cred_handle == GSS_C_NO_CREDENTIAL) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED; + } + + GSSEAP_MUTEX_LOCK(&cred_handle->mutex); + + major = GSS_S_UNAVAILABLE; + *minor = GSSEAP_BAD_CRED_OPTION; + + for (i = 0; i < sizeof(inquireCredOps) / sizeof(inquireCredOps[0]); i++) { + if (oidEqual(&inquireCredOps[i].oid, desired_object)) { + major = (*inquireCredOps[i].inquire)(minor, cred_handle, + desired_object, data_set); + break; + } + } + + GSSEAP_MUTEX_UNLOCK(&cred_handle->mutex); + + return major; +} diff --git a/inquire_mech_for_saslname.c b/inquire_mech_for_saslname.c new file mode 100644 index 0000000..6de0399 --- /dev/null +++ b/inquire_mech_for_saslname.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Map mechanism OID to a SASL mechanism name. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_inquire_saslname_for_mech(OM_uint32 *minor, + const gss_OID mech, + gss_buffer_t sasl_mech_name, + gss_buffer_t mech_name, + gss_buffer_t mech_description) +{ + OM_uint32 major; + gss_buffer_t name; + krb5_enctype etype = ENCTYPE_NULL; + + /* Dynamically construct mechanism name from Kerberos string enctype */ + major = gssEapOidToEnctype(minor, mech, &etype); + if (GSS_ERROR(major)) + return major; + + if (mech_name != GSS_C_NO_BUFFER) { + krb5_context krbContext; + + GSSEAP_KRB_INIT(&krbContext); + + *minor = krbEnctypeToString(krbContext, etype, "eap-", mech_name); + if (*minor != 0) + return GSS_S_FAILURE; + } + + if (mech_description != GSS_C_NO_BUFFER) { + major = makeStringBuffer(minor, + "Extensible Authentication Protocol GSS-API Mechanism", + mech_description); + if (GSS_ERROR(major)) + return major; + } + + if (sasl_mech_name != GSS_C_NO_BUFFER) { + name = gssEapOidToSaslName(mech); + if (name == GSS_C_NO_BUFFER) { + major = GSS_S_BAD_MECH; + *minor = GSSEAP_WRONG_MECH; + } else { + major = duplicateBuffer(minor, name, sasl_mech_name); + } + } + + return major; +} diff --git a/inquire_mechs_for_name.c b/inquire_mechs_for_name.c new file mode 100644 index 0000000..14def4e --- /dev/null +++ b/inquire_mechs_for_name.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Determine mechanism OIDs supported by name. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_inquire_mechs_for_name(OM_uint32 *minor, + const gss_name_t input_name, + gss_OID_set *mech_types) +{ + return gssEapIndicateMechs(minor, mech_types); +} diff --git a/inquire_name.c b/inquire_name.c new file mode 100644 index 0000000..2cf3229 --- /dev/null +++ b/inquire_name.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Enumerate name attributes. + */ + +#include "gssapiP_eap.h" + +OM_uint32 gss_inquire_name(OM_uint32 *minor, + gss_name_t name, + int *name_is_MN, + gss_OID *MN_mech, + gss_buffer_set_t *attrs) +{ + OM_uint32 major, tmpMinor; + + *minor = 0; + + if (name_is_MN != NULL) + *name_is_MN = 1; + if (MN_mech != NULL) + *MN_mech = GSS_EAP_MECHANISM; + if (attrs != NULL) + *attrs = GSS_C_NO_BUFFER_SET; + + if (name == GSS_C_NO_NAME) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + } + + if (attrs == NULL) + return GSS_S_COMPLETE; + + GSSEAP_MUTEX_LOCK(&name->mutex); + + major = gssEapInquireName(minor, name, name_is_MN, MN_mech, attrs); + + GSSEAP_MUTEX_UNLOCK(&name->mutex); + + if (GSS_ERROR(major)) + gss_release_buffer_set(&tmpMinor, attrs); + + return major; +} diff --git a/inquire_names_for_mech.c b/inquire_names_for_mech.c new file mode 100644 index 0000000..3f53217 --- /dev/null +++ b/inquire_names_for_mech.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Return supported name OID types. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_inquire_names_for_mech(OM_uint32 *minor, + gss_OID mechanism, + gss_OID_set *ret_name_types) +{ + OM_uint32 major, tmpMinor; + gss_OID nameTypes[] = { + GSS_C_NT_USER_NAME, + GSS_C_NT_HOSTBASED_SERVICE, + GSS_C_NT_EXPORT_NAME, +#ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT + GSS_C_NT_COMPOSITE_EXPORT, +#endif + GSS_EAP_NT_PRINCIPAL_NAME, + GSS_C_NT_ANONYMOUS, + }; + size_t i; + + if (!gssEapIsMechanismOid(mechanism)) { + *minor = GSSEAP_WRONG_MECH; + return GSS_S_BAD_MECH; + } + + major = gss_create_empty_oid_set(minor, ret_name_types); + if (GSS_ERROR(major)) + goto cleanup; + + for (i = 0; i < sizeof(nameTypes)/sizeof(nameTypes[0]); i++) { + major = gss_add_oid_set_member(minor, nameTypes[i], ret_name_types); + if (GSS_ERROR(major)) + goto cleanup; + } + +cleanup: + if (GSS_ERROR(major)) + gss_release_oid_set(&tmpMinor, ret_name_types); + + return major; +} diff --git a/inquire_saslname_for_mech.c b/inquire_saslname_for_mech.c new file mode 100644 index 0000000..a26dd17 --- /dev/null +++ b/inquire_saslname_for_mech.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Map SASL mechanism name to a mechanism OID. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_inquire_mech_for_saslname(OM_uint32 *minor, + const gss_buffer_t sasl_mech_name, + gss_OID *mech_type) +{ + *mech_type = gssEapSaslNameToOid(sasl_mech_name); + if (*mech_type == GSS_C_NO_OID) { + *minor = GSSEAP_WRONG_MECH; + return GSS_S_BAD_MECH; + } + + return GSS_S_COMPLETE; +} diff --git a/inquire_sec_context_by_oid.c b/inquire_sec_context_by_oid.c new file mode 100644 index 0000000..cf74bea --- /dev/null +++ b/inquire_sec_context_by_oid.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Return extended properties of a context handle. + */ + +#include "gssapiP_eap.h" + +static OM_uint32 +inquireSessionKey(OM_uint32 *minor, + const gss_ctx_id_t ctx, + const gss_OID desired_object, + gss_buffer_set_t *dataSet) +{ + OM_uint32 major, tmpMinor; + unsigned char oidBuf[16]; + gss_buffer_desc buf; + gss_OID_desc oid; + + buf.length = KRB_KEY_LENGTH(&ctx->rfc3961Key); + buf.value = KRB_KEY_DATA(&ctx->rfc3961Key); + + major = gss_add_buffer_set_member(minor, &buf, dataSet); + if (GSS_ERROR(major)) + goto cleanup; + + oid.length = sizeof(oidBuf); + oid.elements = oidBuf; + + major = composeOid(minor, + "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04", + 10, + ctx->encryptionType, + &oid); + if (GSS_ERROR(major)) + goto cleanup; + + buf.length = oid.length; + buf.value = oid.elements; + + major = gss_add_buffer_set_member(minor, &buf, dataSet); + if (GSS_ERROR(major)) + goto cleanup; + + major = GSS_S_COMPLETE; + *minor = 0; + +cleanup: + if (GSS_ERROR(major) && *dataSet != GSS_C_NO_BUFFER_SET) { + gss_buffer_set_t set = *dataSet; + + if (set->count != 0) + memset(set->elements[0].value, 0, set->elements[0].length); + gss_release_buffer_set(&tmpMinor, dataSet); + } + + return major; +} + +static struct { + gss_OID_desc oid; + OM_uint32 (*inquire)(OM_uint32 *, const gss_ctx_id_t, + const gss_OID, gss_buffer_set_t *); +} inquireCtxOps[] = { + { + /* GSS_C_INQ_SSPI_SESSION_KEY */ + { 11, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05" }, + inquireSessionKey + }, + { + /* GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT + v1 */ + { 12, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x06\x01" }, + gssEapExportLucidSecContext + }, +}; + +OM_uint32 +gss_inquire_sec_context_by_oid(OM_uint32 *minor, + const gss_ctx_id_t ctx, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + OM_uint32 major; + int i; + + *data_set = GSS_C_NO_BUFFER_SET; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (!CTX_IS_ESTABLISHED(ctx)) { + *minor = GSSEAP_CONTEXT_INCOMPLETE; + major = GSS_S_NO_CONTEXT; + goto cleanup; + } + + major = GSS_S_UNAVAILABLE; + *minor = GSSEAP_BAD_CONTEXT_OPTION; + + for (i = 0; i < sizeof(inquireCtxOps) / sizeof(inquireCtxOps[0]); i++) { + if (oidEqual(&inquireCtxOps[i].oid, desired_object)) { + major = (*inquireCtxOps[i].inquire)(minor, ctx, + desired_object, data_set); + break; + } + } + +cleanup: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return major; +} diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..6781b98 --- /dev/null +++ b/install-sh @@ -0,0 +1,520 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2009-04-28.21; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# 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 +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + trap '(exit $?); exit' 1 2 13 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dst_arg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writeable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + -*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test -z "$d" && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/map_name_to_any.c b/map_name_to_any.c new file mode 100644 index 0000000..9bba387 --- /dev/null +++ b/map_name_to_any.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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 "gssapiP_eap.h" + +OM_uint32 +gss_map_name_to_any(OM_uint32 *minor, + gss_name_t name, + int authenticated, + gss_buffer_t type_id, + gss_any_t *output) +{ + OM_uint32 major; + + *output = (gss_any_t)NULL; + + if (name == GSS_C_NO_NAME) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + } + + GSSEAP_MUTEX_LOCK(&name->mutex); + + major = gssEapMapNameToAny(minor, name, authenticated, type_id, output); + + GSSEAP_MUTEX_UNLOCK(&name->mutex); + + return major; +} diff --git a/mech b/mech new file mode 100644 index 0000000..393c022 --- /dev/null +++ b/mech @@ -0,0 +1,7 @@ +# +# Sample MIT mechanism glue configuration for EAP GSS mechanism. +# Any encryption type supported by Kerberos can be defined as the +# last element of the OID arc. +# +eap-aes128 1.3.6.1.4.1.5322.22.1.17 mech_eap.so +eap-aes256 1.3.6.1.4.1.5322.22.1.18 mech_eap.so diff --git a/mech_eap.exports b/mech_eap.exports new file mode 100644 index 0000000..bd1df48 --- /dev/null +++ b/mech_eap.exports @@ -0,0 +1,53 @@ +gss_accept_sec_context +gss_acquire_cred +gss_add_cred +gss_add_cred_with_password +gss_canonicalize_name +gss_compare_name +gss_context_time +gss_delete_sec_context +gss_display_name +gss_display_name_ext +gss_display_status +gss_duplicate_name +gss_export_name +gss_export_name_composite +gss_export_sec_context +gss_get_mic +gss_get_name_attribute +gss_import_name +gss_import_sec_context +gss_indicate_mechs +gss_init_sec_context +gss_inquire_attrs_for_mech +gss_inquire_context +gss_inquire_cred +gss_inquire_cred_by_oid +gss_inquire_mechs_for_name +gss_inquire_mech_for_saslname +gss_inquire_name +gss_inquire_names_for_mech +gss_inquire_saslname_for_mech +gss_inquire_sec_context_by_oid +gss_map_name_to_any +gss_process_context_token +gss_pseudo_random +gss_release_any_name_mapping +gss_release_cred +gss_release_name +gss_internal_release_oid +gss_set_name_attribute +gss_set_sec_context_option +gss_store_cred +gss_unwrap +gss_unwrap_iov +gss_verify_mic +gss_wrap +gss_wrap_iov +gss_wrap_iov_length +gss_wrap_size_limit +GSS_EAP_AES128_CTS_HMAC_SHA1_96_MECHANISM +GSS_EAP_AES256_CTS_HMAC_SHA1_96_MECHANISM +GSS_EAP_NT_PRINCIPAL_NAME +gssspi_acquire_cred_with_password +gssspi_set_cred_option diff --git a/mech_invoke.c b/mech_invoke.c new file mode 100644 index 0000000..62aad48 --- /dev/null +++ b/mech_invoke.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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 "gssapiP_eap.h" + +OM_uint32 +gssspi_mech_invoke(OM_uint32 *minor, + const gss_OID desired_mech, + const gss_OID desired_object, + gss_buffer_t value) +{ + *minor = GSSEAP_BAD_INVOCATION; + + return GSS_S_UNAVAILABLE; +} diff --git a/process_context_token.c b/process_context_token.c new file mode 100644 index 0000000..a68c359 --- /dev/null +++ b/process_context_token.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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 "gssapiP_eap.h" + +OM_uint32 +gss_process_context_token(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_buffer_t token_buffer) +{ + OM_uint32 major; + gss_iov_buffer_desc iov[1]; + + *minor = 0; + + if (ctx == NULL) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (!CTX_IS_ESTABLISHED(ctx)) { + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + *minor = GSSEAP_CONTEXT_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; + iov[0].buffer = *token_buffer; + + major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL, + iov, 1, TOK_TYPE_DELETE_CONTEXT); + if (GSS_ERROR(major)) { + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + return major; + } + + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return gssEapReleaseContext(minor, &ctx); +} diff --git a/pseudo_random.c b/pseudo_random.c new file mode 100644 index 0000000..b92eb3c --- /dev/null +++ b/pseudo_random.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ +/* + * 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. + */ + +/* + * PRF + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_pseudo_random(OM_uint32 *minor, + gss_ctx_id_t ctx, + int prf_key, + const gss_buffer_t prf_in, + ssize_t desired_output_len, + gss_buffer_t prf_out) +{ + krb5_error_code code; + int i; + OM_uint32 tmpMinor; + size_t prflen; + krb5_data t, ns; + unsigned char *p; + krb5_context krbContext; + + prf_out->length = 0; + prf_out->value = NULL; + + if (ctx == GSS_C_NO_CONTEXT) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + *minor = 0; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (!CTX_IS_ESTABLISHED(ctx)) { + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + *minor = GSSEAP_CONTEXT_INCOMPLETE; + return GSS_S_NO_CONTEXT; + } + + GSSEAP_KRB_INIT(&krbContext); + + t.length = 0; + t.data = NULL; + + ns.length = 0; + ns.data = NULL; + + if (prf_key != GSS_C_PRF_KEY_PARTIAL && + prf_key != GSS_C_PRF_KEY_FULL) { + code = GSSEAP_BAD_PRF_KEY; + goto cleanup; + } + + prf_out->value = GSSEAP_MALLOC(desired_output_len); + if (prf_out->value == NULL) { + code = ENOMEM; + goto cleanup; + } + prf_out->length = desired_output_len; + + code = krb5_c_prf_length(krbContext, + ctx->encryptionType, + &prflen); + if (code != 0) + goto cleanup; + + ns.length = 4 + prf_in->length; + ns.data = GSSEAP_MALLOC(ns.length); + if (ns.data == NULL) { + code = ENOMEM; + goto cleanup; + } + + t.length = prflen; + t.data = GSSEAP_MALLOC(t.length); + if (t.data == NULL) { + code = ENOMEM; + 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_uint32_be(i, ns.data); + + code = krb5_c_prf(krbContext, &ctx->rfc3961Key, &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: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + if (code != 0) + gss_release_buffer(&tmpMinor, prf_out); + krb5_free_data_contents(krbContext, &ns); + krb5_free_data_contents(krbContext, &t); + + *minor = code; + + return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} diff --git a/radius_ad.exports b/radius_ad.exports new file mode 100644 index 0000000..8d5d5c4 --- /dev/null +++ b/radius_ad.exports @@ -0,0 +1 @@ +authdata_client_0 diff --git a/radsec.conf b/radsec.conf new file mode 100644 index 0000000..3b09894 --- /dev/null +++ b/radsec.conf @@ -0,0 +1,10 @@ +config gss-eap { + type = UDP + server { + hostname = "localhost" + service = "1812" + secret = "testing123" + timeout = 1 + tries = 10 + } +} diff --git a/radsec_err.et b/radsec_err.et new file mode 100644 index 0000000..887ef0a --- /dev/null +++ b/radsec_err.et @@ -0,0 +1,38 @@ +# +# Copyright (c) 2010, JANET(UK) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. 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. +# +# 3. Neither the name of JANET(UK) nor the names of its contributors +# may be 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 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. +# + +# Placeholders only +error_table rse + +error_code GSSEAP_RSE_OK, "" + +end diff --git a/release_any_name_mapping.c b/release_any_name_mapping.c new file mode 100644 index 0000000..8ac29a0 --- /dev/null +++ b/release_any_name_mapping.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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 "gssapiP_eap.h" + +OM_uint32 +gss_release_any_name_mapping(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t type_id, + gss_any_t *input) +{ + OM_uint32 major; + + *minor = 0; + + if (name == GSS_C_NO_NAME) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + } + + GSSEAP_MUTEX_LOCK(&name->mutex); + + major = gssEapReleaseAnyNameMapping(minor, name, type_id, input); + + *input = NULL; + + GSSEAP_MUTEX_UNLOCK(&name->mutex); + + return major; +} diff --git a/release_cred.c b/release_cred.c new file mode 100644 index 0000000..3a429a0 --- /dev/null +++ b/release_cred.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Release a credential handle. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_release_cred(OM_uint32 *minor, + gss_cred_id_t *cred_handle) +{ + return gssEapReleaseCred(minor, cred_handle); +} diff --git a/release_name.c b/release_name.c new file mode 100644 index 0000000..7491691 --- /dev/null +++ b/release_name.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Release a name. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_release_name(OM_uint32 *minor, + gss_name_t *name) +{ + return gssEapReleaseName(minor, name); +} diff --git a/release_oid.c b/release_oid.c new file mode 100644 index 0000000..184dbab --- /dev/null +++ b/release_oid.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Mark an internalized OID as not required to be released. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_internal_release_oid(OM_uint32 *minor, + gss_OID *oid) +{ + return gssEapReleaseOid(minor, oid); +} diff --git a/set_cred_option.c b/set_cred_option.c new file mode 100644 index 0000000..e80918b --- /dev/null +++ b/set_cred_option.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Set an extended property on a credential handle. + */ + +#include "gssapiP_eap.h" + +static OM_uint32 +setCredRadiusConfigFile(OM_uint32 *minor, + gss_cred_id_t cred, + const gss_OID oid, + const gss_buffer_t buffer) +{ + OM_uint32 major; + gss_buffer_desc configFileBuffer = GSS_C_EMPTY_BUFFER; + + if (buffer != GSS_C_NO_BUFFER && buffer->length != 0) { + major = duplicateBuffer(minor, buffer, &configFileBuffer); + if (GSS_ERROR(major)) + return major; + } + + if (cred->radiusConfigFile != NULL) + GSSEAP_FREE(cred->radiusConfigFile); + + cred->radiusConfigFile = (char *)configFileBuffer.value; + + *minor = 0; + return GSS_S_COMPLETE; +} + +static OM_uint32 +setCredRadiusConfigStanza(OM_uint32 *minor, + gss_cred_id_t cred, + const gss_OID oid, + const gss_buffer_t buffer) +{ + OM_uint32 major; + gss_buffer_desc configStanzaBuffer = GSS_C_EMPTY_BUFFER; + + if (buffer != GSS_C_NO_BUFFER && buffer->length != 0) { + major = duplicateBuffer(minor, buffer, &configStanzaBuffer); + if (GSS_ERROR(major)) + return major; + } + + if (cred->radiusConfigStanza != NULL) + GSSEAP_FREE(cred->radiusConfigStanza); + + cred->radiusConfigStanza = (char *)configStanzaBuffer.value; + + *minor = 0; + return GSS_S_COMPLETE; +} + +static OM_uint32 +setCredFlag(OM_uint32 *minor, + gss_cred_id_t cred, + const gss_OID oid, + const gss_buffer_t buffer) +{ + OM_uint32 flags; + unsigned char *p; + + if (buffer == GSS_C_NO_BUFFER) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_FAILURE; + } + + if (buffer->length < 4) { + *minor = GSSEAP_WRONG_SIZE; + return GSS_S_FAILURE; + } + + p = (unsigned char *)buffer->value; + + flags = load_uint32_be(buffer->value) & CRED_FLAG_PUBLIC_MASK; + + if (buffer->length > 4 && p[4]) + cred->flags &= ~(flags); + else + cred->flags |= flags; + + *minor = 0; + return GSS_S_COMPLETE; +} + +static struct { + gss_OID_desc oid; + OM_uint32 (*setOption)(OM_uint32 *, gss_cred_id_t cred, + const gss_OID, const gss_buffer_t); +} setCredOps[] = { + /* 1.3.6.1.4.1.5322.22.3.3.1 */ + { + { 11, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x03\x03\x01" }, + setCredRadiusConfigFile, + }, + /* 1.3.6.1.4.1.5322.22.3.3.2 */ + { + { 11, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x03\x03\x02" }, + setCredRadiusConfigStanza, + }, + /* 1.3.6.1.4.1.5322.22.3.3.3 */ + { + { 11, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x03\x03\x03" }, + setCredFlag, + }, +}; + +gss_OID GSS_EAP_CRED_SET_RADIUS_CONFIG_FILE = &setCredOps[0].oid; +gss_OID GSS_EAP_CRED_SET_RADIUS_CONFIG_STANZA = &setCredOps[1].oid; +gss_OID GSS_EAP_CRED_SET_CRED_FLAG = &setCredOps[2].oid; + +OM_uint32 +gssspi_set_cred_option(OM_uint32 *minor, + gss_cred_id_t *pCred, + const gss_OID desired_object, + const gss_buffer_t value) +{ + OM_uint32 major; + gss_cred_id_t cred = *pCred; + int i; + + if (cred == GSS_C_NO_CREDENTIAL) { + *minor = EINVAL; + return GSS_S_UNAVAILABLE; + } + + GSSEAP_MUTEX_LOCK(&cred->mutex); + + major = GSS_S_UNAVAILABLE; + *minor = GSSEAP_BAD_CRED_OPTION; + + for (i = 0; i < sizeof(setCredOps) / sizeof(setCredOps[0]); i++) { + if (oidEqual(&setCredOps[i].oid, desired_object)) { + major = (*setCredOps[i].setOption)(minor, cred, + desired_object, value); + break; + } + } + + GSSEAP_MUTEX_UNLOCK(&cred->mutex); + + return major; +} + +#if 0 +OM_uint32 +gsseap_set_cred_flag(OM_uint32 *minor, + gss_cred_id_t cred, + OM_uint32 flag, + int clear) +{ + unsigned char buf[5]; + gss_buffer_desc value; + + value.length = sizeof(buf); + value.value = buf; + + store_uint32_be(flag, buf); + buf[4] = (clear != 0); + + return gssspi_set_cred_option(minor, cred, + GSS_EAP_CRED_SET_CRED_FLAG, &value); +} +#endif diff --git a/set_name_attribute.c b/set_name_attribute.c new file mode 100644 index 0000000..e2f635b --- /dev/null +++ b/set_name_attribute.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Set an attribute on a name. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_set_name_attribute(OM_uint32 *minor, + gss_name_t name, + int complete, + gss_buffer_t attr, + gss_buffer_t value) +{ + OM_uint32 major; + + if (name == GSS_C_NO_NAME) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + } + + GSSEAP_MUTEX_LOCK(&name->mutex); + + major = gssEapSetNameAttribute(minor, name, complete, attr, value); + + GSSEAP_MUTEX_UNLOCK(&name->mutex); + + return major; +} diff --git a/set_sec_context_option.c b/set_sec_context_option.c new file mode 100644 index 0000000..cfe0384 --- /dev/null +++ b/set_sec_context_option.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Set an extended property on a context handle. + */ + +#include "gssapiP_eap.h" + +static struct { + gss_OID_desc oid; + OM_uint32 (*setOption)(OM_uint32 *, gss_ctx_id_t *pCtx, + const gss_OID, const gss_buffer_t); +} setCtxOps[] = { +}; + +OM_uint32 +gss_set_sec_context_option(OM_uint32 *minor, + gss_ctx_id_t *pCtx, + const gss_OID desired_object, + const gss_buffer_t value) +{ + OM_uint32 major; + gss_ctx_id_t ctx = *pCtx; + int i; + + major = GSS_S_UNAVAILABLE; + *minor = GSSEAP_BAD_CONTEXT_OPTION; + + if (ctx != GSS_C_NO_CONTEXT) + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + for (i = 0; i < sizeof(setCtxOps) / sizeof(setCtxOps[0]); i++) { + if (oidEqual(&setCtxOps[i].oid, desired_object)) { + major = (*setCtxOps[i].setOption)(minor, &ctx, + desired_object, value); + break; + } + } + + if (*pCtx == NULL) + *pCtx = ctx; + else + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return major; +} diff --git a/store_cred.c b/store_cred.c new file mode 100644 index 0000000..9e308c4 --- /dev/null +++ b/store_cred.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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 "gssapiP_eap.h" + +OM_uint32 +gss_store_cred(OM_uint32 *minor, + const gss_cred_id_t cred, + gss_cred_usage_t input_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) +{ + OM_uint32 major; + + if (elements_stored != NULL) + *elements_stored = GSS_C_NO_OID_SET; + if (cred_usage_stored != NULL) + *cred_usage_stored = input_usage; + + if (cred == GSS_C_NO_CREDENTIAL) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED; + } + + GSSEAP_MUTEX_LOCK(&cred->mutex); + + major = GSS_S_COMPLETE; + *minor = 0; + +#ifdef GSSEAP_ENABLE_REAUTH + if (cred->krbCred != GSS_C_NO_CREDENTIAL) { + major = gssStoreCred(minor, + cred->krbCred, + input_usage, + (gss_OID)gss_mech_krb5, + overwrite_cred, + default_cred, + elements_stored, + cred_usage_stored); + } +#endif + + GSSEAP_MUTEX_UNLOCK(&cred->mutex); + + return major; +} diff --git a/unwrap.c b/unwrap.c new file mode 100644 index 0000000..7c8d062 --- /dev/null +++ b/unwrap.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Message protection services: unwrap. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_unwrap(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_buffer_t input_message_buffer, + gss_buffer_t output_message_buffer, + int *conf_state, + gss_qop_t *qop_state) +{ + OM_uint32 major, tmpMinor; + gss_iov_buffer_desc iov[2]; + + if (ctx == GSS_C_NO_CONTEXT) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + *minor = 0; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (!CTX_IS_ESTABLISHED(ctx)) { + major = GSS_S_NO_CONTEXT; + *minor = GSSEAP_CONTEXT_INCOMPLETE; + goto cleanup; + } + + iov[0].type = GSS_IOV_BUFFER_TYPE_STREAM; + iov[0].buffer = *input_message_buffer; + + iov[1].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE; + iov[1].buffer.value = NULL; + iov[1].buffer.length = 0; + + major = gssEapUnwrapOrVerifyMIC(minor, ctx, conf_state, qop_state, + iov, 2, TOK_TYPE_WRAP); + if (major == GSS_S_COMPLETE) { + *output_message_buffer = iov[1].buffer; + } else { + if (iov[1].type & GSS_IOV_BUFFER_FLAG_ALLOCATED) + gss_release_buffer(&tmpMinor, &iov[1].buffer); + } + +cleanup: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return major; +} diff --git a/unwrap_iov.c b/unwrap_iov.c new file mode 100644 index 0000000..e76f3d4 --- /dev/null +++ b/unwrap_iov.c @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ +/* + * 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. + */ + +/* + * Message protection services: unwrap with scatter-gather API. + */ + +#include "gssapiP_eap.h" + +/* + * 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) + */ +OM_uint32 +unwrapToken(OM_uint32 *minor, + gss_ctx_id_t ctx, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *unused __attribute__((__unused__)), +#endif + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + enum gss_eap_token_type toktype) +{ + OM_uint32 major = GSS_S_FAILURE, code; + gss_iov_buffer_t header; + gss_iov_buffer_t padding; + gss_iov_buffer_t trailer; + unsigned char flags; + unsigned char *ptr = NULL; + int keyUsage; + size_t rrc, ec; + size_t dataLen, assocDataLen; + uint64_t seqnum; + int valid = 0; + int conf_flag = 0; + krb5_context krbContext; +#ifdef HAVE_HEIMDAL_VERSION + int freeCrypto = (krbCrypto == NULL); +#endif + + GSSEAP_KRB_INIT(&krbContext); + + *minor = 0; + + if (qop_state != NULL) + *qop_state = GSS_C_QOP_DEFAULT; + + header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); + assert(header != NULL); + + padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding != NULL && padding->buffer.length != 0) { + code = GSSEAP_BAD_PADDING_IOV; + major = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + + trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + + flags = rfc4121Flags(ctx, TRUE); + + if (toktype == TOK_TYPE_WRAP) { + keyUsage = !CTX_IS_INITIATOR(ctx) + ? KEY_USAGE_INITIATOR_SEAL + : KEY_USAGE_ACCEPTOR_SEAL; + } else { + keyUsage = !CTX_IS_INITIATOR(ctx) + ? KEY_USAGE_INITIATOR_SIGN + : KEY_USAGE_ACCEPTOR_SIGN; + } + + gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen); + + ptr = (unsigned char *)header->buffer.value; + + if (header->buffer.length < 16) { + code = GSSEAP_TOK_TRUNC; + major = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + + if ((ptr[2] & flags) != flags) { + code = GSSEAP_BAD_DIRECTION; + major = GSS_S_BAD_SIG; + goto cleanup; + } + +#ifdef HAVE_HEIMDAL_VERSION + if (krbCrypto == NULL) { + code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, + ETYPE_NULL, &krbCrypto); + if (code != 0) + goto cleanup; + } +#endif + + if (toktype == TOK_TYPE_WRAP) { + size_t krbTrailerLen; + + if (load_uint16_be(ptr) != TOK_TYPE_WRAP) + goto defective; + conf_flag = ((ptr[2] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0); + if (ptr[3] != 0xFF) + goto defective; + ec = load_uint16_be(ptr + 4); + rrc = load_uint16_be(ptr + 6); + seqnum = load_uint64_be(ptr + 8); + + code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + conf_flag ? KRB5_CRYPTO_TYPE_TRAILER : + KRB5_CRYPTO_TYPE_CHECKSUM, + &krbTrailerLen); + if (code != 0) + goto cleanup; + + /* Deal with RRC */ + if (trailer == NULL) { + size_t desired_rrc = krbTrailerLen; + + if (conf_flag) { + desired_rrc += 16; /* E(Header) */ + + if ((ctx->gssFlags & 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) { + goto defective; + } + + if (conf_flag) { + unsigned char *althdr; + + /* Decrypt */ + code = gssEapDecrypt(krbContext, + ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0), + ec, rrc, KRB_CRYPTO_CONTEXT(ctx), keyUsage, + iov, iov_count); + if (code != 0) { + major = GSS_S_BAD_SIG; + goto cleanup; + } + + /* Validate header integrity */ + if (trailer == NULL) + althdr = (unsigned char *)header->buffer.value + 16 + ec; + else + althdr = (unsigned char *)trailer->buffer.value + ec; + + if (load_uint16_be(althdr) != TOK_TYPE_WRAP + || althdr[2] != ptr[2] + || althdr[3] != ptr[3] + || memcmp(althdr + 8, ptr + 8, 8) != 0) { + code = GSSEAP_BAD_WRAP_TOKEN; + major = GSS_S_BAD_SIG; + goto cleanup; + } + } else { + /* Verify checksum: note EC is checksum size here, not padding */ + if (ec != krbTrailerLen) + goto defective; + + /* Zero EC, RRC before computing checksum */ + store_uint16_be(0, ptr + 4); + store_uint16_be(0, ptr + 6); + + code = gssEapVerify(krbContext, ctx->checksumType, rrc, + KRB_CRYPTO_CONTEXT(ctx), keyUsage, + iov, iov_count, &valid); + if (code != 0 || valid == FALSE) { + major = GSS_S_BAD_SIG; + goto cleanup; + } + } + + code = sequenceCheck(minor, &ctx->seqState, seqnum); + } else if (toktype == TOK_TYPE_MIC) { + if (load_uint16_be(ptr) != toktype) + goto defective; + + verify_mic_1: + if (ptr[3] != 0xFF) + goto defective; + seqnum = load_uint64_be(ptr + 8); + + code = gssEapVerify(krbContext, ctx->checksumType, 0, + KRB_CRYPTO_CONTEXT(ctx), keyUsage, + iov, iov_count, &valid); + if (code != 0 || valid == FALSE) { + major = GSS_S_BAD_SIG; + goto cleanup; + } + code = sequenceCheck(minor, &ctx->seqState, seqnum); + } else if (toktype == TOK_TYPE_DELETE_CONTEXT) { + if (load_uint16_be(ptr) != TOK_TYPE_DELETE_CONTEXT) + goto defective; + goto verify_mic_1; + } else { + goto defective; + } + + if (conf_state != NULL) + *conf_state = conf_flag; + + code = 0; + major = GSS_S_COMPLETE; + goto cleanup; + +defective: + code = GSSEAP_BAD_WRAP_TOKEN; + major = GSS_S_DEFECTIVE_TOKEN; + +cleanup: + *minor = code; +#ifdef HAVE_HEIMDAL_VERSION + if (freeCrypto && krbCrypto != NULL) + krb5_crypto_destroy(krbContext, krbCrypto); +#endif + + return major; +} + +int +rotateLeft(void *ptr, size_t bufsiz, size_t rc) +{ + void *tbuf; + + if (bufsiz == 0) + return 0; + rc = rc % bufsiz; + if (rc == 0) + return 0; + + tbuf = GSSEAP_MALLOC(rc); + if (tbuf == NULL) + return ENOMEM; + + memcpy(tbuf, ptr, rc); + memmove(ptr, (char *)ptr + rc, bufsiz - rc); + memcpy((char *)ptr + bufsiz - rc, tbuf, rc); + GSSEAP_FREE(tbuf); + + return 0; +} + +/* + * Split a STREAM | SIGN_DATA | DATA into + * HEADER | SIGN_DATA | DATA | PADDING | TRAILER + */ +static OM_uint32 +unwrapStream(OM_uint32 *minor, + gss_ctx_id_t ctx, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + enum gss_eap_token_type toktype) +{ + unsigned char *ptr; + OM_uint32 code = 0, major = GSS_S_FAILURE; + krb5_context krbContext; + 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; +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto = NULL; +#endif + + GSSEAP_KRB_INIT(&krbContext); + + assert(toktype == TOK_TYPE_WRAP); + + if (toktype != TOK_TYPE_WRAP) { + code = GSSEAP_WRONG_TOK_ID; + goto cleanup; + } + + stream = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM); + assert(stream != NULL); + + if (stream->buffer.length < 16) { + major = GSS_S_DEFECTIVE_TOKEN; + goto cleanup; + } + + ptr = (unsigned char *)stream->buffer.value; + toktype2 = load_uint16_be(ptr); + ptr += 2; + + tiov = (gss_iov_buffer_desc *)GSSEAP_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 = 16; + + /* 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 = GSSEAP_BAD_STREAM_IOV; + 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 = GSSEAP_BAD_STREAM_IOV; + 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; + +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto); + if (code != 0) + goto cleanup; +#endif + + { + size_t ec, rrc; + size_t krbHeaderLen = 0; + size_t krbTrailerLen = 0; + + conf_req_flag = ((ptr[0] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0); + ec = conf_req_flag ? load_uint16_be(ptr + 2) : 0; + rrc = load_uint16_be(ptr + 4); + + if (rrc != 0) { + code = rotateLeft((unsigned char *)stream->buffer.value + 16, + stream->buffer.length - 16, rrc); + if (code != 0) + goto cleanup; + store_uint16_be(0, ptr + 4); /* set RRC to zero */ + } + + if (conf_req_flag) { + code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen); + if (code != 0) + goto cleanup; + theader->buffer.length += krbHeaderLen; /* length validated later */ + } + + /* no PADDING for CFX, EC is used instead */ + code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + conf_req_flag + ? KRB5_CRYPTO_TYPE_TRAILER + : KRB5_CRYPTO_TYPE_CHECKSUM, + &krbTrailerLen); + if (code != 0) + goto cleanup; + + ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) + + krbTrailerLen; + ttrailer->buffer.value = (unsigned char *)stream->buffer.value + + stream->buffer.length - ttrailer->buffer.length; + } + + /* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/ + /* 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) { + major = GSS_S_DEFECTIVE_TOKEN; + code = GSSEAP_TOK_TRUNC; + 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 = gssEapAllocIov(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 = unwrapToken(&code, ctx, KRB_CRYPTO_CONTEXT(ctx), + conf_state, qop_state, tiov, i, toktype); + if (major == GSS_S_COMPLETE) { + *data = *tdata; + } else if (tdata->type & GSS_IOV_BUFFER_FLAG_ALLOCATED) { + OM_uint32 tmp; + + gss_release_buffer(&tmp, &tdata->buffer); + tdata->type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED); + } + +cleanup: + if (tiov != NULL) + GSSEAP_FREE(tiov); +#ifdef HAVE_HEIMDAL_VERSION + if (krbCrypto != NULL) + krb5_crypto_destroy(krbContext, krbCrypto); +#endif + + *minor = code; + + return major; +} + +OM_uint32 +gssEapUnwrapOrVerifyMIC(OM_uint32 *minor, + gss_ctx_id_t ctx, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count, + enum gss_eap_token_type toktype) +{ + OM_uint32 major; + + if (ctx->encryptionType == ENCTYPE_NULL) { + *minor = GSSEAP_KEY_UNAVAILABLE; + return GSS_S_UNAVAILABLE; + } + + if (gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM) != NULL) { + major = unwrapStream(minor, ctx, conf_state, qop_state, + iov, iov_count, toktype); + } else { + major = unwrapToken(minor, ctx, + NULL, /* krbCrypto */ + conf_state, qop_state, + iov, iov_count, toktype); + } + + return major; +} + +OM_uint32 +gss_unwrap_iov(OM_uint32 *minor, + gss_ctx_id_t ctx, + int *conf_state, + gss_qop_t *qop_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major; + + if (ctx == GSS_C_NO_CONTEXT) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + *minor = 0; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (!CTX_IS_ESTABLISHED(ctx)) { + major = GSS_S_NO_CONTEXT; + *minor = GSSEAP_CONTEXT_INCOMPLETE; + goto cleanup; + } + + major = gssEapUnwrapOrVerifyMIC(minor, ctx, conf_state, qop_state, + iov, iov_count, TOK_TYPE_WRAP); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return major; +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..49c7c91 --- /dev/null +++ b/util.h @@ -0,0 +1,775 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ +/* + * Portions Copyright 2003-2010 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. + * + */ + +/* + * Utility functions. + */ + +#ifndef _UTIL_H_ +#define _UTIL_H_ 1 + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MIN +#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#endif + +/* util_buffer.c */ +OM_uint32 +makeStringBuffer(OM_uint32 *minor, + const char *string, + gss_buffer_t buffer); + +OM_uint32 +bufferToString(OM_uint32 *minor, + const gss_buffer_t buffer, + char **pString); + +OM_uint32 +duplicateBuffer(OM_uint32 *minor, + const gss_buffer_t src, + gss_buffer_t dst); + +static inline int +bufferEqual(const gss_buffer_t b1, const gss_buffer_t b2) +{ + return (b1->length == b2->length && + memcmp(b1->value, b2->value, b2->length) == 0); +} + +static inline int +bufferEqualString(const gss_buffer_t b1, const char *s) +{ + gss_buffer_desc b2; + + b2.length = strlen(s); + b2.value = (char *)s; + + return bufferEqual(b1, &b2); +} + +/* util_cksum.c */ +int +gssEapSign(krb5_context context, + krb5_cksumtype type, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *key, +#endif + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count); + +int +gssEapVerify(krb5_context context, + krb5_cksumtype type, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *key, +#endif + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int *valid); + +#if 0 +OM_uint32 +gssEapEncodeGssChannelBindings(OM_uint32 *minor, + gss_channel_bindings_t chanBindings, + gss_buffer_t encodedBindings); +#endif + +/* util_context.c */ +#define EAP_EXPORT_CONTEXT_V1 1 + +enum gss_eap_token_type { + TOK_TYPE_NONE = 0x0000, /* no token */ + TOK_TYPE_MIC = 0x0404, /* RFC 4121 MIC token */ + TOK_TYPE_WRAP = 0x0504, /* RFC 4121 wrap token */ + TOK_TYPE_EXPORT_NAME = 0x0401, /* RFC 2743 exported name */ + TOK_TYPE_EXPORT_NAME_COMPOSITE = 0x0402, /* exported composite name */ + TOK_TYPE_DELETE_CONTEXT = 0x0405, /* RFC 2743 delete context */ + TOK_TYPE_EAP_RESP = 0x0601, /* EAP response */ + TOK_TYPE_EAP_REQ = 0x0602, /* EAP request */ + TOK_TYPE_EXT_REQ = 0x0603, /* GSS EAP extensions request */ + TOK_TYPE_EXT_RESP = 0x0604, /* GSS EAP extensions response */ + TOK_TYPE_GSS_REAUTH = 0x0605, /* GSS EAP fast reauthentication token */ + TOK_TYPE_CONTEXT_ERR = 0x0606, /* context error */ +}; + +OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx); +OM_uint32 gssEapReleaseContext(OM_uint32 *minor, gss_ctx_id_t *pCtx); + +OM_uint32 +gssEapMakeToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t innerToken, + enum gss_eap_token_type tokenType, + gss_buffer_t outputToken); + +OM_uint32 +gssEapVerifyToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t inputToken, + enum gss_eap_token_type *tokenType, + gss_buffer_t innerInputToken); + +OM_uint32 +gssEapContextTime(OM_uint32 *minor, + gss_ctx_id_t context_handle, + OM_uint32 *time_rec); + +OM_uint32 +gssEapDisplayName(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t output_name_buffer, + gss_OID *output_name_type); + +/* util_cred.c */ +OM_uint32 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred); +OM_uint32 gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred); + +OM_uint32 +gssEapAcquireCred(OM_uint32 *minor, + const gss_name_t desiredName, + const gss_buffer_t password, + OM_uint32 timeReq, + const gss_OID_set desiredMechs, + int cred_usage, + gss_cred_id_t *pCred, + gss_OID_set *pActualMechs, + OM_uint32 *timeRec); + +int gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech); + +/* util_crypt.c */ +int +gssEapEncrypt(krb5_context context, int dce_style, size_t ec, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *key, +#endif + int usage, + gss_iov_buffer_desc *iov, int iov_count); + +int +gssEapDecrypt(krb5_context context, int dce_style, size_t ec, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *key, +#endif + int usage, + gss_iov_buffer_desc *iov, int iov_count); + +int +gssEapMapCryptoFlag(OM_uint32 type); + +gss_iov_buffer_t +gssEapLocateIov(gss_iov_buffer_desc *iov, + int iov_count, + OM_uint32 type); + +void +gssEapIovMessageLength(gss_iov_buffer_desc *iov, + int iov_count, + size_t *data_length, + size_t *assoc_data_length); + +void +gssEapReleaseIov(gss_iov_buffer_desc *iov, int iov_count); + +int +gssEapIsIntegrityOnly(gss_iov_buffer_desc *iov, int iov_count); + +int +gssEapAllocIov(gss_iov_buffer_t iov, size_t size); + +OM_uint32 +gssEapDeriveRfc3961Key(OM_uint32 *minor, + const unsigned char *key, + size_t keyLength, + krb5_enctype enctype, + krb5_keyblock *pKey); + +/* util_exts.c */ +#define EXT_FLAG_CRITICAL 0x80000000 /* critical, wire flag */ +#define EXT_FLAG_VERIFIED 0x40000000 /* verified, API flag */ + +#define EXT_TYPE_GSS_CHANNEL_BINDINGS 0x00000000 +#define EXT_TYPE_REAUTH_CREDS 0x00000001 +#define EXT_TYPE_MASK (~(EXT_FLAG_CRITICAL | EXT_FLAG_VERIFIED)) + +struct gss_eap_extension_provider { + OM_uint32 type; + int critical; /* client */ + int required; /* server */ + OM_uint32 (*make)(OM_uint32 *, + gss_cred_id_t, + gss_ctx_id_t, + gss_channel_bindings_t, + gss_buffer_t); + OM_uint32 (*verify)(OM_uint32 *, + gss_cred_id_t, + gss_ctx_id_t, + gss_channel_bindings_t, + const gss_buffer_t); +}; + +OM_uint32 +gssEapMakeExtensions(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_channel_bindings_t chanBindings, + gss_buffer_t buffer); + +OM_uint32 +gssEapVerifyExtensions(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_channel_bindings_t chanBindings, + const gss_buffer_t buffer); + +/* util_krb.c */ +#ifdef HAVE_HEIMDAL_VERSION + +#define KRB_TIME_FOREVER ((time_t)~0L) + +#define KRB_KEY_TYPE(key) ((key)->keytype) +#define KRB_KEY_DATA(key) ((key)->keyvalue.data) +#define KRB_KEY_LENGTH(key) ((key)->keyvalue.length) + +#define KRB_PRINC_LENGTH(princ) ((princ)->name.name_string.len) +#define KRB_PRINC_TYPE(princ) ((princ)->name.name_type) +#define KRB_PRINC_NAME(princ) ((princ)->name.name_string.val) +#define KRB_PRINC_REALM(princ) ((princ)->realm) + +#define KRB_KT_ENT_KEYBLOCK(e) (&(e)->keyblock) +#define KRB_KT_ENT_FREE(c, e) krb5_kt_free_entry((c), (e)) + +#define KRB_CRYPTO_CONTEXT(ctx) (krbCrypto) + +#else + +#define KRB_TIME_FOREVER KRB5_INT32_MAX + +#define KRB_KEY_TYPE(key) ((key)->enctype) +#define KRB_KEY_DATA(key) ((key)->contents) +#define KRB_KEY_LENGTH(key) ((key)->length) + +#define KRB_PRINC_LENGTH(princ) (krb5_princ_size(NULL, (princ))) +#define KRB_PRINC_TYPE(princ) (krb5_princ_type(NULL, (princ))) +#define KRB_PRINC_NAME(princ) (krb5_princ_name(NULL, (princ))) +#define KRB_PRINC_REALM(princ) (krb5_princ_realm(NULL, (princ))) + +#define KRB_KT_ENT_KEYBLOCK(e) (&(e)->key) +#define KRB_KT_ENT_FREE(c, e) krb5_free_keytab_entry_contents((c), (e)) + +#define KRB_CRYPTO_CONTEXT(ctx) (&(ctx)->rfc3961Key) + +#endif /* HAVE_HEIMDAL_VERSION */ + +#define KRB_KEY_INIT(key) do { \ + KRB_KEY_TYPE(key) = ENCTYPE_NULL; \ + KRB_KEY_DATA(key) = NULL; \ + KRB_KEY_LENGTH(key) = 0; \ + } while (0) + +#ifdef HAVE_HEIMDAL_VERSION +#define GSS_IOV_BUFFER_FLAG_ALLOCATE GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE +#define GSS_IOV_BUFFER_FLAG_ALLOCATED GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATED + +#define GSS_S_CRED_UNAVAIL GSS_S_FAILURE +#endif + +#define GSSEAP_KRB_INIT(ctx) do { \ + OM_uint32 tmpMajor; \ + \ + tmpMajor = gssEapKerberosInit(minor, ctx); \ + if (GSS_ERROR(tmpMajor)) { \ + return tmpMajor; \ + } \ + } while (0) + +OM_uint32 +gssEapKerberosInit(OM_uint32 *minor, krb5_context *context); + +OM_uint32 +rfc3961ChecksumTypeForKey(OM_uint32 *minor, + krb5_keyblock *key, + krb5_cksumtype *cksumtype); + +krb5_const_principal +krbAnonymousPrincipal(void); + +krb5_error_code +krbCryptoLength(krb5_context krbContext, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *key, +#endif + int type, + size_t *length); + +krb5_error_code +krbPaddingLength(krb5_context krbContext, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *key, +#endif + size_t dataLength, + size_t *padLength); + +krb5_error_code +krbBlockSize(krb5_context krbContext, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *key, +#endif + size_t *blockSize); + +krb5_error_code +krbEnctypeToString(krb5_context krbContext, + krb5_enctype enctype, + const char *prefix, + gss_buffer_t string); + +krb5_error_code +krbMakeAuthDataKdcIssued(krb5_context context, + const krb5_keyblock *key, + krb5_const_principal issuer, +#ifdef HAVE_HEIMDAL_VERSION + const AuthorizationData *authdata, + AuthorizationData *adKdcIssued +#else + krb5_authdata *const *authdata, + krb5_authdata ***adKdcIssued +#endif + ); + +krb5_error_code +krbMakeCred(krb5_context context, + krb5_auth_context authcontext, + krb5_creds *creds, + krb5_data *data); + +/* util_lucid.c */ +OM_uint32 +gssEapExportLucidSecContext(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_OID desiredObject, + gss_buffer_set_t *data_set); + +/* util_mech.c */ +extern gss_OID GSS_EAP_MECHANISM; + +int +gssEapInternalizeOid(const gss_OID oid, + gss_OID *const pInternalizedOid); + +OM_uint32 +gssEapReleaseOid(OM_uint32 *minor, gss_OID *oid); + +OM_uint32 +gssEapDefaultMech(OM_uint32 *minor, + gss_OID *oid); + +OM_uint32 +gssEapIndicateMechs(OM_uint32 *minor, + gss_OID_set *mechs); + +OM_uint32 +gssEapEnctypeToOid(OM_uint32 *minor, + krb5_enctype enctype, + gss_OID *pOid); + +OM_uint32 +gssEapOidToEnctype(OM_uint32 *minor, + const gss_OID oid, + krb5_enctype *enctype); + +int +gssEapIsMechanismOid(const gss_OID oid); + +int +gssEapIsConcreteMechanismOid(const gss_OID oid); + +OM_uint32 +gssEapValidateMechs(OM_uint32 *minor, + const gss_OID_set mechs); + +gss_buffer_t +gssEapOidToSaslName(const gss_OID oid); + +gss_OID +gssEapSaslNameToOid(const gss_buffer_t name); + +/* util_name.c */ +#define EXPORT_NAME_FLAG_OID 0x1 +#define EXPORT_NAME_FLAG_COMPOSITE 0x2 + +OM_uint32 gssEapAllocName(OM_uint32 *minor, gss_name_t *pName); +OM_uint32 gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName); +OM_uint32 gssEapExportName(OM_uint32 *minor, + const gss_name_t name, + gss_buffer_t exportedName); +OM_uint32 gssEapExportNameInternal(OM_uint32 *minor, + const gss_name_t name, + gss_buffer_t exportedName, + unsigned int flags); +OM_uint32 gssEapImportName(OM_uint32 *minor, + const gss_buffer_t input_name_buffer, + gss_OID input_name_type, + gss_name_t *output_name); +OM_uint32 gssEapImportNameInternal(OM_uint32 *minor, + const gss_buffer_t input_name_buffer, + gss_name_t *output_name, + unsigned int flags); +OM_uint32 +gssEapDuplicateName(OM_uint32 *minor, + const gss_name_t input_name, + gss_name_t *dest_name); + +/* util_oid.c */ +OM_uint32 +composeOid(OM_uint32 *minor_status, + const char *prefix, + size_t prefix_len, + int suffix, + gss_OID_desc *oid); + +OM_uint32 +decomposeOid(OM_uint32 *minor_status, + const char *prefix, + size_t prefix_len, + gss_OID_desc *oid, + int *suffix) ; + +OM_uint32 +duplicateOid(OM_uint32 *minor_status, + const gss_OID_desc * const oid, + gss_OID *new_oid); + +OM_uint32 +duplicateOidSet(OM_uint32 *minor, + const gss_OID_set src, + gss_OID_set *dst); + +static inline int +oidEqual(const gss_OID_desc *o1, const gss_OID_desc *o2) +{ + if (o1 == GSS_C_NO_OID) + return (o2 == GSS_C_NO_OID); + else if (o2 == GSS_C_NO_OID) + return (o1 == GSS_C_NO_OID); + else + return (o1->length == o2->length && + memcmp(o1->elements, o2->elements, o1->length) == 0); +} + +/* util_ordering.c */ +OM_uint32 +sequenceInternalize(OM_uint32 *minor, + void **vqueue, + unsigned char **buf, + size_t *lenremain); + +OM_uint32 +sequenceExternalize(OM_uint32 *minor, + void *vqueue, + unsigned char **buf, + size_t *lenremain); + +size_t +sequenceSize(void *vqueue); + +OM_uint32 +sequenceFree(OM_uint32 *minor, void **vqueue); + +OM_uint32 +sequenceCheck(OM_uint32 *minor, void **vqueue, uint64_t seqnum); + +OM_uint32 +sequenceInit(OM_uint32 *minor, void **vqueue, uint64_t seqnum, + int do_replay, int do_sequence, int wide_nums); + +/* util_token.c */ +size_t +tokenSize(const gss_OID_desc *mech, size_t body_size); + +void +makeTokenHeader(const gss_OID_desc *mech, + size_t body_size, + unsigned char **buf, + enum gss_eap_token_type tok_type); + +OM_uint32 +verifyTokenHeader(OM_uint32 *minor, + gss_OID mech, + size_t *body_size, + unsigned char **buf_in, + size_t toksize_in, + enum gss_eap_token_type *ret_tok_type); + +/* Helper macros */ + +#define GSSEAP_CALLOC calloc +#define GSSEAP_MALLOC malloc +#define GSSEAP_FREE free +#define GSSEAP_REALLOC realloc + +#define GSSEAP_NOT_IMPLEMENTED do { \ + assert(0 && "not implemented"); \ + *minor = ENOSYS; \ + return GSS_S_FAILURE; \ + } while (0) + +#include + +#define GSSEAP_MUTEX pthread_mutex_t +#define GSSEAP_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +#define GSSEAP_MUTEX_INIT(m) pthread_mutex_init((m), NULL) +#define GSSEAP_MUTEX_DESTROY(m) pthread_mutex_destroy((m)) +#define GSSEAP_MUTEX_LOCK(m) pthread_mutex_lock((m)) +#define GSSEAP_MUTEX_UNLOCK(m) pthread_mutex_unlock((m)) + +#define GSSEAP_THREAD_KEY pthread_key_t +#define GSSEAP_KEY_CREATE(k, d) pthread_key_create((k), (d)) +#define GSSEAP_GETSPECIFIC(k) pthread_getspecific((k)) +#define GSSEAP_SETSPECIFIC(k, d) pthread_setspecific((k), (d)) + +#define GSSEAP_THREAD_ONCE pthread_once_t +#define GSSEAP_ONCE(o, i) pthread_once((o), (i)) +#define GSSEAP_ONCE_INITIALIZER PTHREAD_ONCE_INIT + +/* Helper functions */ +static inline void +store_uint16_be(uint16_t val, void *vp) +{ + unsigned char *p = (unsigned char *)vp; + + p[0] = (val >> 8) & 0xff; + p[1] = (val ) & 0xff; +} + +static inline uint16_t +load_uint16_be(const void *cvp) +{ + const unsigned char *p = (const unsigned char *)cvp; + + return (p[1] | (p[0] << 8)); +} + +static inline void +store_uint32_be(uint32_t val, void *vp) +{ + unsigned char *p = (unsigned char *)vp; + + p[0] = (val >> 24) & 0xff; + p[1] = (val >> 16) & 0xff; + p[2] = (val >> 8) & 0xff; + p[3] = (val ) & 0xff; +} + +static inline uint32_t +load_uint32_be(const void *cvp) +{ + const unsigned char *p = (const unsigned char *)cvp; + + return (p[3] | (p[2] << 8) + | ((uint32_t) p[1] << 16) + | ((uint32_t) p[0] << 24)); +} + +static inline void +store_uint64_be(uint64_t val, void *vp) +{ + unsigned char *p = (unsigned char *)vp; + + p[0] = (unsigned char)((val >> 56) & 0xff); + p[1] = (unsigned char)((val >> 48) & 0xff); + p[2] = (unsigned char)((val >> 40) & 0xff); + p[3] = (unsigned char)((val >> 32) & 0xff); + p[4] = (unsigned char)((val >> 24) & 0xff); + p[5] = (unsigned char)((val >> 16) & 0xff); + p[6] = (unsigned char)((val >> 8) & 0xff); + p[7] = (unsigned char)((val ) & 0xff); +} + +static inline uint64_t +load_uint64_be(const void *cvp) +{ + const unsigned char *p = (const unsigned char *)cvp; + + return ((uint64_t)load_uint32_be(p) << 32) | load_uint32_be(p + 4); +} + +static inline unsigned char * +store_buffer(gss_buffer_t buffer, void *vp, int wide_nums) +{ + unsigned char *p = (unsigned char *)vp; + + if (wide_nums) { + store_uint64_be(buffer->length, p); + p += 8; + } else { + store_uint32_be(buffer->length, p); + p += 4; + } + + if (buffer->value != NULL) { + memcpy(p, buffer->value, buffer->length); + p += buffer->length; + } + + return p; +} + +static inline unsigned char * +load_buffer(const void *cvp, size_t length, gss_buffer_t buffer) +{ + buffer->length = 0; + buffer->value = GSSEAP_MALLOC(length); + if (buffer->value == NULL) + return NULL; + buffer->length = length; + memcpy(buffer->value, cvp, length); + return (unsigned char *)cvp + length; +} + +static inline unsigned char * +store_oid(gss_OID oid, void *vp) +{ + gss_buffer_desc buf; + + if (oid != GSS_C_NO_OID) { + buf.length = oid->length; + buf.value = oid->elements; + } else { + buf.length = 0; + buf.value = NULL; + } + + return store_buffer(&buf, vp, FALSE); +} + +static inline void +krbDataToGssBuffer(krb5_data *data, gss_buffer_t buffer) +{ + buffer->value = (void *)data->data; + buffer->length = data->length; +} + +static inline void +krbPrincComponentToGssBuffer(krb5_principal krbPrinc, + int index, gss_buffer_t buffer) +{ +#ifdef HAVE_HEIMDAL_VERSION + buffer->value = (void *)KRB_PRINC_NAME(krbPrinc)[index]; + buffer->length = strlen((char *)buffer->value); +#else + buffer->value = (void *)krb5_princ_component(NULL, krbPrinc, index)->data; + buffer->length = krb5_princ_component(NULL, krbPrinc, index)->length; +#endif /* HAVE_HEIMDAL_VERSION */ +} + +static inline void +krbPrincRealmToGssBuffer(krb5_principal krbPrinc, gss_buffer_t buffer) +{ +#ifdef HAVE_HEIMDAL_VERSION + buffer->value = (void *)KRB_PRINC_REALM(krbPrinc); + buffer->length = strlen((char *)buffer->value); +#else + krbDataToGssBuffer(KRB_PRINC_REALM(krbPrinc), buffer); +#endif +} + +static inline void +gssBufferToKrbData(gss_buffer_t buffer, krb5_data *data) +{ + data->data = (char *)buffer->value; + data->length = buffer->length; +} + +#ifdef __cplusplus +} +#endif + +#include "util_attr.h" +#ifdef GSSEAP_ENABLE_REAUTH +#include "util_reauth.h" +#endif + +#endif /* _UTIL_H_ */ diff --git a/util_adshim.c b/util_adshim.c new file mode 100644 index 0000000..e743042 --- /dev/null +++ b/util_adshim.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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 "gssapiP_eap.h" +#include "authdata_plugin.h" + +/* + * This rubbish is necessary because MIT doesn't provide another way + * to access verified AD-KDCIssued elements. We can't verify them + * ourselves because they're signed in the ticket session key, which + * is destroyed immediately after the AP-REQ is processed. + */ + +struct radius_ad_context { + krb5_data avpdata; + krb5_boolean verified; +}; + +static krb5_data radius_ad_attr = { + KV5M_DATA, sizeof("urn:authdata-radius-avp") - 1, "urn:authdata-radius-avp" }; + +static krb5_error_code +radius_ad_init(krb5_context kcontext, void **plugin_context) +{ + *plugin_context = 0; + return 0; +} + +static void +radius_ad_flags(krb5_context kcontext, + void *plugin_context, + krb5_authdatatype ad_type, + krb5_flags *flags) +{ + *flags = AD_USAGE_KDC_ISSUED | AD_INFORMATIONAL; +} + +static void +radius_ad_fini(krb5_context kcontext, void *plugin_context) +{ + return; +} + +static krb5_error_code +radius_ad_request_init(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void **request_context) +{ + struct radius_ad_context *ctx; + + ctx = GSSEAP_CALLOC(1, sizeof(*ctx)); + if (ctx == NULL) + return ENOMEM; + + *request_context = ctx; + + return 0; +} + +static krb5_error_code +radius_ad_export_authdata(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_flags usage, + krb5_authdata ***out_authdata) +{ + struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context; + krb5_authdata *data[2]; + krb5_authdata datum; + + datum.ad_type = KRB5_AUTHDATA_RADIUS_AVP; + datum.length = radius_ad->avpdata.length; + datum.contents = (krb5_octet *)radius_ad->avpdata.data; + + data[0] = &datum; + data[1] = NULL; + + return krb5_copy_authdata(kcontext, data, out_authdata); +} + +static krb5_error_code +radius_ad_import_authdata(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + krb5_authdata **authdata, + krb5_boolean kdc_issued_flag, + krb5_const_principal issuer) +{ + struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context; + + krb5_free_data_contents(kcontext, &radius_ad->avpdata); + radius_ad->verified = FALSE; + + assert(authdata[0] != NULL); + + radius_ad->avpdata.data = GSSEAP_MALLOC(authdata[0]->length); + if (radius_ad->avpdata.data == NULL) + return ENOMEM; + + memcpy(radius_ad->avpdata.data, authdata[0]->contents, + authdata[0]->length); + radius_ad->avpdata.length = authdata[0]->length; + + radius_ad->verified = kdc_issued_flag; + + return 0; +} + +static void +radius_ad_request_fini(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context) +{ + struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context; + + if (radius_ad != NULL) { + krb5_free_data_contents(kcontext, &radius_ad->avpdata); + GSSEAP_FREE(radius_ad); + } +} + +static krb5_error_code +radius_ad_get_attribute(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + const krb5_data *attribute, + krb5_boolean *authenticated, + krb5_boolean *complete, + krb5_data *value, + krb5_data *display_value, + int *more) +{ + struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context; + + if (attribute->length != radius_ad_attr.length || + memcmp(attribute->data, radius_ad_attr.data, + radius_ad_attr.length) != 0) + return ENOENT; + + if (radius_ad->avpdata.length == 0) + return ENOENT; + + *authenticated = radius_ad->verified; + *complete = TRUE; + *more = 0; + + value->data = GSSEAP_MALLOC(radius_ad->avpdata.length); + if (value->data == NULL) + return ENOMEM; + + memcpy(value->data, radius_ad->avpdata.data, radius_ad->avpdata.length); + value->length = radius_ad->avpdata.length; + + return 0; +} + +static krb5_error_code +radius_ad_copy(krb5_context kcontext, + struct _krb5_authdata_context *context, + void *plugin_context, + void *request_context, + void *dst_plugin_context, + void *dst_request_context) +{ + struct radius_ad_context *radius_ad_src = + (struct radius_ad_context *)request_context; + struct radius_ad_context *radius_ad_dst = + (struct radius_ad_context *)dst_request_context; + + radius_ad_dst->avpdata.data = GSSEAP_MALLOC(radius_ad_src->avpdata.length); + if (radius_ad_dst->avpdata.data == NULL) + return ENOMEM; + + memcpy(radius_ad_dst->avpdata.data, radius_ad_src->avpdata.data, + radius_ad_src->avpdata.length); + radius_ad_dst->avpdata.length = radius_ad_src->avpdata.length; + radius_ad_dst->verified = radius_ad_src->verified; + + return 0; +} + +static krb5_authdatatype radius_ad_ad_types[] = + { KRB5_AUTHDATA_RADIUS_AVP, 0 }; + +krb5plugin_authdata_client_ftable_v0 authdata_client_0 = { + "radius_ad", + radius_ad_ad_types, + radius_ad_init, + radius_ad_fini, + radius_ad_flags, + radius_ad_request_init, + radius_ad_request_fini, + NULL, + radius_ad_get_attribute, + NULL, + NULL, + radius_ad_export_authdata, + radius_ad_import_authdata, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + radius_ad_copy +}; diff --git a/util_attr.cpp b/util_attr.cpp new file mode 100644 index 0000000..52da7cf --- /dev/null +++ b/util_attr.cpp @@ -0,0 +1,1073 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Attribute provider mechanism. + */ + +#include "gssapiP_eap.h" + +#include +#include +#include +#include + +/* lazy initialisation */ +static GSSEAP_THREAD_ONCE gssEapAttrProvidersInitOnce = GSSEAP_ONCE_INITIALIZER; +static OM_uint32 gssEapAttrProvidersInitStatus = GSS_S_UNAVAILABLE; + +static void +gssEapAttrProvidersInitInternal(void) +{ + OM_uint32 major, minor; + + assert(gssEapAttrProvidersInitStatus == GSS_S_UNAVAILABLE); + + major = gssEapRadiusAttrProviderInit(&minor); + if (major == GSS_S_COMPLETE) + major = gssEapSamlAttrProvidersInit(&minor); + if (major == GSS_S_COMPLETE) + major = gssEapLocalAttrProviderInit(&minor); + +#ifdef GSSEAP_DEBUG + assert(major == GSS_S_COMPLETE); +#endif + + gssEapAttrProvidersInitStatus = major; +} + +static OM_uint32 +gssEapAttrProvidersInit(OM_uint32 *minor) +{ + GSSEAP_ONCE(&gssEapAttrProvidersInitOnce, gssEapAttrProvidersInitInternal); + + if (GSS_ERROR(gssEapAttrProvidersInitStatus)) + *minor = GSSEAP_NO_ATTR_PROVIDERS; + + return gssEapAttrProvidersInitStatus; +} + +OM_uint32 +gssEapAttrProvidersFinalize(OM_uint32 *minor) +{ + OM_uint32 major = GSS_S_COMPLETE; + + if (gssEapAttrProvidersInitStatus == GSS_S_COMPLETE) { + major = gssEapLocalAttrProviderFinalize(minor); + if (major == GSS_S_COMPLETE) + major = gssEapSamlAttrProvidersFinalize(minor); + if (major == GSS_S_COMPLETE) + major = gssEapRadiusAttrProviderFinalize(minor); + + gssEapAttrProvidersInitStatus = GSS_S_UNAVAILABLE; + } + + return major; +} + +static gss_eap_attr_create_provider gssEapAttrFactories[ATTR_TYPE_MAX + 1]; +static gss_buffer_desc gssEapAttrPrefixes[ATTR_TYPE_MAX + 1]; + +/* + * Register a provider for a particular type and prefix + */ +void +gss_eap_attr_ctx::registerProvider(unsigned int type, + const char *prefix, + gss_eap_attr_create_provider factory) +{ + assert(type <= ATTR_TYPE_MAX); + + assert(gssEapAttrFactories[type] == NULL); + + gssEapAttrFactories[type] = factory; + if (prefix != NULL) { + gssEapAttrPrefixes[type].value = (void *)prefix; + gssEapAttrPrefixes[type].length = strlen(prefix); + } else { + gssEapAttrPrefixes[type].value = NULL; + gssEapAttrPrefixes[type].length = 0; + } +} + +/* + * Unregister a provider + */ +void +gss_eap_attr_ctx::unregisterProvider(unsigned int type) +{ + assert(type <= ATTR_TYPE_MAX); + + gssEapAttrFactories[type] = NULL; + gssEapAttrPrefixes[type].value = NULL; + gssEapAttrPrefixes[type].length = 0; +} + +/* + * Create an attribute context, that manages instances of providers + */ +gss_eap_attr_ctx::gss_eap_attr_ctx(void) +{ + m_flags = 0; + + for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) { + gss_eap_attr_provider *provider; + + if (gssEapAttrFactories[i] != NULL) { + provider = (gssEapAttrFactories[i])(); + } else { + provider = NULL; + } + + m_providers[i] = provider; + } +} + +/* + * Convert an attribute prefix to a type + */ +unsigned int +gss_eap_attr_ctx::attributePrefixToType(const gss_buffer_t prefix) +{ + unsigned int i; + + for (i = ATTR_TYPE_MIN; i < ATTR_TYPE_MAX; i++) { + if (bufferEqual(&gssEapAttrPrefixes[i], prefix)) + return i; + } + + return ATTR_TYPE_LOCAL; +} + +/* + * Convert a type to an attribute prefix + */ +const gss_buffer_t +gss_eap_attr_ctx::attributeTypeToPrefix(unsigned int type) +{ + if (type < ATTR_TYPE_MIN || type >= ATTR_TYPE_MAX) + return GSS_C_NO_BUFFER; + + return &gssEapAttrPrefixes[type]; +} + +bool +gss_eap_attr_ctx::providerEnabled(unsigned int type) const +{ + if (type == ATTR_TYPE_LOCAL && + (m_flags & ATTR_FLAG_DISABLE_LOCAL)) + return false; + + if (m_providers[type] == NULL) + return false; + + return true; +} + +void +gss_eap_attr_ctx::releaseProvider(unsigned int type) +{ + delete m_providers[type]; + m_providers[type] = NULL; +} + +/* + * Initialize a context from an existing context. + */ +bool +gss_eap_attr_ctx::initFromExistingContext(const gss_eap_attr_ctx *manager) +{ + bool ret = true; + + m_flags = manager->m_flags; + + for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) { + gss_eap_attr_provider *provider; + + if (!providerEnabled(i)) { + releaseProvider(i); + continue; + } + + provider = m_providers[i]; + + ret = provider->initFromExistingContext(this, + manager->m_providers[i]); + if (ret == false) { + releaseProvider(i); + break; + } + } + + return ret; +} + +/* + * Initialize a context from a GSS credential and context. + */ +bool +gss_eap_attr_ctx::initFromGssContext(const gss_cred_id_t cred, + const gss_ctx_id_t ctx) +{ + bool ret = true; + + if (cred != GSS_C_NO_CREDENTIAL && + (cred->flags & GSS_EAP_DISABLE_LOCAL_ATTRS_FLAG)) { + m_flags |= ATTR_FLAG_DISABLE_LOCAL; + } + + for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) { + gss_eap_attr_provider *provider; + + if (!providerEnabled(i)) { + releaseProvider(i); + continue; + } + + provider = m_providers[i]; + + ret = provider->initFromGssContext(this, cred, ctx); + if (ret == false) { + releaseProvider(i); + break; + } + } + + return ret; +} + +/* + * Initialize a context from an exported context or name token + */ +bool +gss_eap_attr_ctx::initFromBuffer(const gss_buffer_t buffer) +{ + bool ret; + gss_eap_attr_provider *primaryProvider = getPrimaryProvider(); + gss_buffer_desc primaryBuf; + + if (buffer->length < 4) + return false; + + m_flags = load_uint32_be(buffer->value); + + primaryBuf.length = buffer->length - 4; + primaryBuf.value = (char *)buffer->value + 4; + + ret = primaryProvider->initFromBuffer(this, &primaryBuf); + if (ret == false) + return ret; + + for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) { + gss_eap_attr_provider *provider; + + if (!providerEnabled(i)) { + releaseProvider(i); + continue; + } + + provider = m_providers[i]; + if (provider == primaryProvider) + continue; + + ret = provider->initFromGssContext(this, + GSS_C_NO_CREDENTIAL, + GSS_C_NO_CONTEXT); + if (ret == false) { + releaseProvider(i); + break; + } + } + + return ret; +} + +gss_eap_attr_ctx::~gss_eap_attr_ctx(void) +{ + for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) + delete m_providers[i]; +} + +/* + * Locate provider for a given type + */ +gss_eap_attr_provider * +gss_eap_attr_ctx::getProvider(unsigned int type) const +{ + assert(type >= ATTR_TYPE_MIN && type <= ATTR_TYPE_MAX); + return m_providers[type]; +} + +/* + * Locate provider for a given prefix + */ +gss_eap_attr_provider * +gss_eap_attr_ctx::getProvider(const gss_buffer_t prefix) const +{ + unsigned int type; + + type = attributePrefixToType(prefix); + + return m_providers[type]; +} + +/* + * Get primary provider. Only the primary provider is serialised when + * gss_export_sec_context() or gss_export_name_composite() is called. + */ +gss_eap_attr_provider * +gss_eap_attr_ctx::getPrimaryProvider(void) const +{ + return m_providers[ATTR_TYPE_MIN]; +} + +/* + * Set an attribute + */ +bool +gss_eap_attr_ctx::setAttribute(int complete, + const gss_buffer_t attr, + const gss_buffer_t value) +{ + gss_buffer_desc suffix = GSS_C_EMPTY_BUFFER; + unsigned int type; + gss_eap_attr_provider *provider; + bool ret = false; + + decomposeAttributeName(attr, &type, &suffix); + + provider = m_providers[type]; + if (provider != NULL) { + ret = provider->setAttribute(complete, + (type == ATTR_TYPE_LOCAL) ? attr : &suffix, + value); + } + + return ret; +} + +/* + * Delete an attrbiute + */ +bool +gss_eap_attr_ctx::deleteAttribute(const gss_buffer_t attr) +{ + gss_buffer_desc suffix = GSS_C_EMPTY_BUFFER; + unsigned int type; + gss_eap_attr_provider *provider; + bool ret = false; + + decomposeAttributeName(attr, &type, &suffix); + + provider = m_providers[type]; + if (provider != NULL) { + ret = provider->deleteAttribute(type == ATTR_TYPE_LOCAL ? attr : &suffix); + } + + return ret; +} + +/* + * Enumerate attribute types with callback + */ +bool +gss_eap_attr_ctx::getAttributeTypes(gss_eap_attr_enumeration_cb cb, void *data) const +{ + bool ret = false; + size_t i; + + for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) { + gss_eap_attr_provider *provider = m_providers[i]; + + if (provider == NULL) + continue; + + ret = provider->getAttributeTypes(cb, data); + if (ret == false) + break; + } + + return ret; +} + +struct eap_gss_get_attr_types_args { + unsigned int type; + gss_buffer_set_t attrs; +}; + +static bool +addAttribute(const gss_eap_attr_provider *provider, + const gss_buffer_t attribute, + void *data) +{ + eap_gss_get_attr_types_args *args = (eap_gss_get_attr_types_args *)data; + gss_buffer_desc qualified; + OM_uint32 major, minor; + + if (args->type != ATTR_TYPE_LOCAL) { + gss_eap_attr_ctx::composeAttributeName(args->type, attribute, &qualified); + major = gss_add_buffer_set_member(&minor, &qualified, &args->attrs); + gss_release_buffer(&minor, &qualified); + } else { + major = gss_add_buffer_set_member(&minor, attribute, &args->attrs); + } + + return GSS_ERROR(major) == false; +} + +/* + * Enumerate attribute types, output is buffer set + */ +bool +gss_eap_attr_ctx::getAttributeTypes(gss_buffer_set_t *attrs) +{ + eap_gss_get_attr_types_args args; + OM_uint32 major, minor; + bool ret = false; + unsigned int i; + + major = gss_create_empty_buffer_set(&minor, attrs); + if (GSS_ERROR(major)) { + throw new std::bad_alloc; + return false; + } + + args.attrs = *attrs; + + for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) { + gss_eap_attr_provider *provider = m_providers[i]; + + args.type = i; + + if (provider == NULL) + continue; + + ret = provider->getAttributeTypes(addAttribute, (void *)&args); + if (ret == false) + break; + } + + if (ret == false) + gss_release_buffer_set(&minor, attrs); + + return ret; +} + +/* + * Get attribute with given name + */ +bool +gss_eap_attr_ctx::getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const +{ + gss_buffer_desc suffix = GSS_C_EMPTY_BUFFER; + unsigned int type; + gss_eap_attr_provider *provider; + bool ret; + + decomposeAttributeName(attr, &type, &suffix); + + provider = m_providers[type]; + if (provider == NULL) + return false; + + ret = provider->getAttribute(type == ATTR_TYPE_LOCAL ? attr : &suffix, + authenticated, complete, + value, display_value, more); + + return ret; +} + +/* + * Map attribute context to C++ object + */ +gss_any_t +gss_eap_attr_ctx::mapToAny(int authenticated, + gss_buffer_t type_id) const +{ + unsigned int type; + gss_eap_attr_provider *provider; + gss_buffer_desc suffix; + + decomposeAttributeName(type_id, &type, &suffix); + + provider = m_providers[type]; + if (provider == NULL) + return (gss_any_t)NULL; + + return provider->mapToAny(authenticated, &suffix); +} + +/* + * Release mapped context + */ +void +gss_eap_attr_ctx::releaseAnyNameMapping(gss_buffer_t type_id, + gss_any_t input) const +{ + unsigned int type; + gss_eap_attr_provider *provider; + gss_buffer_desc suffix; + + decomposeAttributeName(type_id, &type, &suffix); + + provider = m_providers[type]; + if (provider != NULL) + provider->releaseAnyNameMapping(&suffix, input); +} + +/* + * Export attribute context to buffer + */ +void +gss_eap_attr_ctx::exportToBuffer(gss_buffer_t buffer) const +{ + const gss_eap_attr_provider *primaryProvider = getPrimaryProvider(); + gss_buffer_desc tmp; + unsigned char *p; + OM_uint32 tmpMinor; + + primaryProvider->exportToBuffer(&tmp); + + buffer->length = 4 + tmp.length; + buffer->value = GSSEAP_MALLOC(buffer->length); + if (buffer->value == NULL) + throw new std::bad_alloc; + + p = (unsigned char *)buffer->value; + store_uint32_be(m_flags, p); + memcpy(p + 4, tmp.value, tmp.length); + + gss_release_buffer(&tmpMinor, &tmp); +} + +/* + * Return soonest expiry time of providers + */ +time_t +gss_eap_attr_ctx::getExpiryTime(void) const +{ + unsigned int i; + time_t expiryTime = 0; + + for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) { + gss_eap_attr_provider *provider = m_providers[i]; + time_t providerExpiryTime; + + if (provider == NULL) + continue; + + providerExpiryTime = provider->getExpiryTime(); + if (providerExpiryTime == 0) + continue; + + if (expiryTime == 0 || providerExpiryTime < expiryTime) + expiryTime = providerExpiryTime; + } + + return expiryTime; +} + +OM_uint32 +gss_eap_attr_ctx::mapException(OM_uint32 *minor, std::exception &e) const +{ + unsigned int i; + OM_uint32 major; + + /* Errors we handle ourselves */ + major = GSS_S_FAILURE; + + if (typeid(e) == typeid(std::bad_alloc)) { + *minor = ENOMEM; + goto cleanup; + } + + /* Errors we delegate to providers */ + major = GSS_S_CONTINUE_NEEDED; + + for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) { + gss_eap_attr_provider *provider = m_providers[i]; + + if (provider == NULL) + continue; + + major = provider->mapException(minor, e); + if (major != GSS_S_CONTINUE_NEEDED) + break; + } + + if (major == GSS_S_CONTINUE_NEEDED) { + *minor = GSSEAP_ATTR_CONTEXT_FAILURE; + major = GSS_S_FAILURE; + } + +cleanup: +#if 0 + /* rethrow for now for debugging */ + throw e; +#endif + + assert(GSS_ERROR(major)); + + return major; +} + +/* + * Decompose attribute name into prefix and suffix + */ +void +gss_eap_attr_ctx::decomposeAttributeName(const gss_buffer_t attribute, + gss_buffer_t prefix, + gss_buffer_t suffix) +{ + char *p = NULL; + size_t i; + + for (i = 0; i < attribute->length; i++) { + if (((char *)attribute->value)[i] == ' ') { + p = (char *)attribute->value + i + 1; + break; + } + } + + prefix->value = attribute->value; + prefix->length = i; + + if (p != NULL && *p != '\0') { + suffix->length = attribute->length - 1 - prefix->length; + suffix->value = p; + } else { + suffix->length = 0; + suffix->value = NULL; + } +} + +/* + * Decompose attribute name into type and suffix + */ +void +gss_eap_attr_ctx::decomposeAttributeName(const gss_buffer_t attribute, + unsigned int *type, + gss_buffer_t suffix) +{ + gss_buffer_desc prefix = GSS_C_EMPTY_BUFFER; + + decomposeAttributeName(attribute, &prefix, suffix); + *type = attributePrefixToType(&prefix); +} + +/* + * Compose attribute name from prefix, suffix; returns C++ string + */ +std::string +gss_eap_attr_ctx::composeAttributeName(const gss_buffer_t prefix, + const gss_buffer_t suffix) +{ + std::string str; + + if (prefix == GSS_C_NO_BUFFER || prefix->length == 0) + return str; + + str.append((const char *)prefix->value, prefix->length); + + if (suffix != GSS_C_NO_BUFFER) { + str.append(" "); + str.append((const char *)suffix->value, suffix->length); + } + + return str; +} + +/* + * Compose attribute name from type, suffix; returns C++ string + */ +std::string +gss_eap_attr_ctx::composeAttributeName(unsigned int type, + const gss_buffer_t suffix) +{ + const gss_buffer_t prefix = attributeTypeToPrefix(type); + + return composeAttributeName(prefix, suffix); +} + +/* + * Compose attribute name from prefix, suffix; returns GSS buffer + */ +void +gss_eap_attr_ctx::composeAttributeName(const gss_buffer_t prefix, + const gss_buffer_t suffix, + gss_buffer_t attribute) +{ + std::string str = composeAttributeName(prefix, suffix); + + if (str.length() != 0) { + return duplicateBuffer(str, attribute); + } else { + attribute->length = 0; + attribute->value = NULL; + } +} + +/* + * Compose attribute name from type, suffix; returns GSS buffer + */ +void +gss_eap_attr_ctx::composeAttributeName(unsigned int type, + const gss_buffer_t suffix, + gss_buffer_t attribute) +{ + gss_buffer_t prefix = attributeTypeToPrefix(type); + + return composeAttributeName(prefix, suffix, attribute); +} + +/* + * C wrappers + */ +OM_uint32 +gssEapInquireName(OM_uint32 *minor, + gss_name_t name, + int *name_is_MN, + gss_OID *MN_mech, + gss_buffer_set_t *attrs) +{ + if (name->attrCtx == NULL) { + *minor = GSSEAP_NO_ATTR_CONTEXT; + return GSS_S_UNAVAILABLE; + } + + if (GSS_ERROR(gssEapAttrProvidersInit(minor))) { + return GSS_S_UNAVAILABLE; + } + + try { + if (!name->attrCtx->getAttributeTypes(attrs)) { + *minor = GSSEAP_NO_ATTR_CONTEXT; + return GSS_S_UNAVAILABLE; + } + } catch (std::exception &e) { + return name->attrCtx->mapException(minor, e); + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapGetNameAttribute(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) +{ + *authenticated = 0; + *complete = 0; + + if (value != NULL) { + value->length = 0; + value->value = NULL; + } + + if (display_value != NULL) { + display_value->length = 0; + display_value->value = NULL; + } + + if (name->attrCtx == NULL) { + *minor = GSSEAP_NO_ATTR_CONTEXT; + return GSS_S_UNAVAILABLE; + } + + if (GSS_ERROR(gssEapAttrProvidersInit(minor))) { + return GSS_S_UNAVAILABLE; + } + + try { + if (!name->attrCtx->getAttribute(attr, authenticated, complete, + value, display_value, more)) { + *minor = GSSEAP_NO_SUCH_ATTR; + gssEapSaveStatusInfo(*minor, "Unknown naming attribute %.*s", + (int)attr->length, (char *)attr->value); + return GSS_S_UNAVAILABLE; + } + } catch (std::exception &e) { + return name->attrCtx->mapException(minor, e); + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapDeleteNameAttribute(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t attr) +{ + if (name->attrCtx == NULL) { + *minor = GSSEAP_NO_ATTR_CONTEXT; + return GSS_S_UNAVAILABLE; + } + + if (GSS_ERROR(gssEapAttrProvidersInit(minor))) + return GSS_S_UNAVAILABLE; + + try { + if (!name->attrCtx->deleteAttribute(attr)) { + *minor = GSSEAP_NO_SUCH_ATTR; + gssEapSaveStatusInfo(*minor, "Unknown naming attribute %.*s", + (int)attr->length, (char *)attr->value); + return GSS_S_UNAVAILABLE; + } + } catch (std::exception &e) { + return name->attrCtx->mapException(minor, e); + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapSetNameAttribute(OM_uint32 *minor, + gss_name_t name, + int complete, + gss_buffer_t attr, + gss_buffer_t value) +{ + if (name->attrCtx == NULL) { + *minor = GSSEAP_NO_ATTR_CONTEXT; + return GSS_S_UNAVAILABLE; + } + + if (GSS_ERROR(gssEapAttrProvidersInit(minor))) + return GSS_S_UNAVAILABLE; + + try { + if (!name->attrCtx->setAttribute(complete, attr, value)) { + *minor = GSSEAP_NO_SUCH_ATTR; + gssEapSaveStatusInfo(*minor, "Unknown naming attribute %.*s", + (int)attr->length, (char *)attr->value); + return GSS_S_UNAVAILABLE; + } + } catch (std::exception &e) { + return name->attrCtx->mapException(minor, e); + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapExportAttrContext(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t buffer) +{ + if (name->attrCtx == NULL) { + buffer->length = 0; + buffer->value = NULL; + + return GSS_S_COMPLETE; + } + + if (GSS_ERROR(gssEapAttrProvidersInit(minor))) + return GSS_S_UNAVAILABLE; + + try { + name->attrCtx->exportToBuffer(buffer); + } catch (std::exception &e) { + return name->attrCtx->mapException(minor, e); + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapImportAttrContext(OM_uint32 *minor, + gss_buffer_t buffer, + gss_name_t name) +{ + gss_eap_attr_ctx *ctx = NULL; + + assert(name->attrCtx == NULL); + + if (GSS_ERROR(gssEapAttrProvidersInit(minor))) + return GSS_S_UNAVAILABLE; + + if (buffer->length != 0) { + try { + ctx = new gss_eap_attr_ctx(); + + if (!ctx->initFromBuffer(buffer)) { + delete ctx; + *minor = GSSEAP_BAD_ATTR_TOKEN; + return GSS_S_DEFECTIVE_TOKEN; + } + name->attrCtx = ctx; + } catch (std::exception &e) { + delete ctx; + return name->attrCtx->mapException(minor, e); + } + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapDuplicateAttrContext(OM_uint32 *minor, + gss_name_t in, + gss_name_t out) +{ + gss_eap_attr_ctx *ctx = NULL; + + assert(out->attrCtx == NULL); + + if (GSS_ERROR(gssEapAttrProvidersInit(minor))) + return GSS_S_UNAVAILABLE; + + try { + if (in->attrCtx != NULL) { + ctx = new gss_eap_attr_ctx(); + if (!ctx->initFromExistingContext(in->attrCtx)) { + delete ctx; + *minor = GSSEAP_ATTR_CONTEXT_FAILURE; + return GSS_S_FAILURE; + } + out->attrCtx = ctx; + } + } catch (std::exception &e) { + delete ctx; + return in->attrCtx->mapException(minor, e); + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapMapNameToAny(OM_uint32 *minor, + gss_name_t name, + int authenticated, + gss_buffer_t type_id, + gss_any_t *output) +{ + if (name->attrCtx == NULL) { + *minor = GSSEAP_NO_ATTR_CONTEXT; + return GSS_S_UNAVAILABLE; + } + + if (GSS_ERROR(gssEapAttrProvidersInit(minor))) + return GSS_S_UNAVAILABLE; + + try { + *output = name->attrCtx->mapToAny(authenticated, type_id); + } catch (std::exception &e) { + return name->attrCtx->mapException(minor, e); + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapReleaseAnyNameMapping(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t type_id, + gss_any_t *input) +{ + if (name->attrCtx == NULL) { + *minor = GSSEAP_NO_ATTR_CONTEXT; + return GSS_S_UNAVAILABLE; + } + + if (GSS_ERROR(gssEapAttrProvidersInit(minor))) + return GSS_S_UNAVAILABLE; + + try { + if (*input != NULL) + name->attrCtx->releaseAnyNameMapping(type_id, *input); + *input = NULL; + } catch (std::exception &e) { + return name->attrCtx->mapException(minor, e); + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapReleaseAttrContext(OM_uint32 *minor, + gss_name_t name) +{ + if (name->attrCtx != NULL) + delete name->attrCtx; + + return GSS_S_COMPLETE; +} + +/* + * Public accessor for initialisng a context from a GSS context. Also + * sets expiry time on GSS context as a side-effect. + */ +OM_uint32 +gssEapCreateAttrContext(OM_uint32 *minor, + gss_cred_id_t gssCred, + gss_ctx_id_t gssCtx, + struct gss_eap_attr_ctx **pAttrContext, + time_t *pExpiryTime) +{ + gss_eap_attr_ctx *ctx; + OM_uint32 major; + + assert(gssCtx != GSS_C_NO_CONTEXT); + + major = gssEapAttrProvidersInit(minor); + if (GSS_ERROR(major)) + return major; + + try { + ctx = new gss_eap_attr_ctx(); + if (!ctx->initFromGssContext(gssCred, gssCtx)) { + delete ctx; + *minor = GSSEAP_ATTR_CONTEXT_FAILURE; + return GSS_S_FAILURE; + } + } catch (std::exception &e) { + major = ctx->mapException(minor, e); + delete ctx; + return major; + } + + *pAttrContext = ctx; + *pExpiryTime = ctx->getExpiryTime(); + + *minor = 0; + return GSS_S_COMPLETE; +} diff --git a/util_attr.h b/util_attr.h new file mode 100644 index 0000000..01f8e2f --- /dev/null +++ b/util_attr.h @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Attribute provider interface. + */ + +#ifndef _UTIL_ATTR_H_ +#define _UTIL_ATTR_H_ 1 + +#ifdef HAVE_HEIMDAL_VERSION +/* Removed in draft-ietf-kitten-gssapi-naming-exts-08 */ +typedef struct gss_any *gss_any_t; +#endif + +#ifdef __cplusplus +#include +#include + +struct gss_eap_attr_provider; +struct gss_eap_attr_ctx; + +typedef bool +(*gss_eap_attr_enumeration_cb)(const gss_eap_attr_provider *source, + const gss_buffer_t attribute, + void *data); + +#define ATTR_TYPE_RADIUS 0U /* RADIUS AVPs */ +#define ATTR_TYPE_SAML_ASSERTION 1U /* SAML assertion */ +#define ATTR_TYPE_SAML 2U /* SAML attributes */ +#define ATTR_TYPE_LOCAL 3U /* Local attributes */ +#define ATTR_TYPE_MIN ATTR_TYPE_RADIUS +#define ATTR_TYPE_MAX ATTR_TYPE_LOCAL + +#define ATTR_FLAG_DISABLE_LOCAL 0x00000001 + +/* + * Attribute provider: this represents a source of attributes derived + * from the security context. + */ +struct gss_eap_attr_provider +{ +public: + gss_eap_attr_provider(void) {} + virtual ~gss_eap_attr_provider(void) {} + + bool initWithManager(const gss_eap_attr_ctx *manager) + { + m_manager = manager; + return true; + } + + virtual bool initFromExistingContext(const gss_eap_attr_ctx *manager, + const gss_eap_attr_provider *ctx) + { + return initWithManager(manager); + } + + virtual bool initFromGssContext(const gss_eap_attr_ctx *manager, + const gss_cred_id_t cred, + const gss_ctx_id_t ctx) + { + return initWithManager(manager); + } + + virtual bool getAttributeTypes(gss_eap_attr_enumeration_cb, void *data) const + { + return false; + } + + virtual bool setAttribute(int complete, + const gss_buffer_t attr, + const gss_buffer_t value) { return false; } + virtual bool deleteAttribute(const gss_buffer_t value) { return false; } + virtual bool getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const { return false; } + + virtual gss_any_t mapToAny(int authenticated, + gss_buffer_t type_id) const { return NULL; } + virtual void releaseAnyNameMapping(gss_buffer_t type_id, + gss_any_t input) const {} + + virtual void exportToBuffer(gss_buffer_t buffer) const {} + virtual bool initFromBuffer(const gss_eap_attr_ctx *manager, + const gss_buffer_t buffer) + { + return initWithManager(manager); + } + + virtual time_t getExpiryTime(void) const { return 0; } + + virtual OM_uint32 mapException(OM_uint32 *minor, std::exception &e) const + { return GSS_S_CONTINUE_NEEDED; } + + static bool init(void) { return true; } + static void finalize(void) {} + + static gss_eap_attr_provider *createAttrContext(void) { return NULL; } + +protected: + const gss_eap_attr_ctx *m_manager; + +private: + /* make non-copyable */ + gss_eap_attr_provider(const gss_eap_attr_provider&); + gss_eap_attr_provider& operator=(const gss_eap_attr_provider&); +}; + +typedef gss_eap_attr_provider *(*gss_eap_attr_create_provider)(void); + +/* + * Attribute context: this manages a set of providers for a given + * security context. + */ +struct gss_eap_attr_ctx +{ +public: + gss_eap_attr_ctx(void); + ~gss_eap_attr_ctx(void); + + bool initFromExistingContext(const gss_eap_attr_ctx *manager); + bool initFromGssContext(const gss_cred_id_t cred, + const gss_ctx_id_t ctx); + + bool getAttributeTypes(gss_eap_attr_enumeration_cb, void *data) const; + bool getAttributeTypes(gss_buffer_set_t *attrs); + + bool setAttribute(int complete, + const gss_buffer_t attr, + const gss_buffer_t value); + bool deleteAttribute(const gss_buffer_t value); + bool getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const; + gss_any_t mapToAny(int authenticated, + gss_buffer_t type_id) const; + void releaseAnyNameMapping(gss_buffer_t type_id, + gss_any_t input) const; + + void exportToBuffer(gss_buffer_t buffer) const; + bool initFromBuffer(const gss_buffer_t buffer); + + static unsigned int + attributePrefixToType(const gss_buffer_t prefix); + + static const gss_buffer_t + attributeTypeToPrefix(unsigned int type); + + static void + decomposeAttributeName(const gss_buffer_t attribute, + gss_buffer_t prefix, + gss_buffer_t suffix); + static void + composeAttributeName(const gss_buffer_t prefix, + const gss_buffer_t suffix, + gss_buffer_t attribute); + static void + decomposeAttributeName(const gss_buffer_t attribute, + unsigned int *type, + gss_buffer_t suffix); + static void + composeAttributeName(unsigned int type, + const gss_buffer_t suffix, + gss_buffer_t attribute); + + static std::string + composeAttributeName(const gss_buffer_t prefix, + const gss_buffer_t suffix); + static std::string + composeAttributeName(unsigned int type, + const gss_buffer_t suffix); + + gss_eap_attr_provider *getProvider(unsigned int type) const; + gss_eap_attr_provider *getProvider(const gss_buffer_t prefix) const; + + static void + registerProvider(unsigned int type, + const char *prefix, + gss_eap_attr_create_provider factory); + static void + unregisterProvider(unsigned int type); + + time_t getExpiryTime(void) const; + OM_uint32 mapException(OM_uint32 *minor, std::exception &e) const; + +private: + bool providerEnabled(unsigned int type) const; + void releaseProvider(unsigned int type); + + gss_eap_attr_provider *getPrimaryProvider(void) const; + + /* make non-copyable */ + gss_eap_attr_ctx(const gss_eap_attr_ctx&); + gss_eap_attr_ctx& operator=(const gss_eap_attr_ctx&); + + uint32_t m_flags; + gss_eap_attr_provider *m_providers[ATTR_TYPE_MAX + 1]; +}; + +#endif /* __cplusplus */ + +#include "util_radius.h" +#include "util_saml.h" +#include "util_shib.h" + +#ifdef __cplusplus + +static inline void +duplicateBuffer(gss_buffer_desc &src, gss_buffer_t dst) +{ + OM_uint32 minor; + + if (GSS_ERROR(duplicateBuffer(&minor, &src, dst))) + throw new std::bad_alloc(); +} + +static inline void +duplicateBuffer(std::string &str, gss_buffer_t buffer) +{ + gss_buffer_desc tmp; + + tmp.length = str.length(); + tmp.value = (char *)str.c_str(); + + duplicateBuffer(tmp, buffer); +} + +#else +struct gss_eap_attr_ctx; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * C wrappers for attribute context functions. These match their + * GSS naming extension equivalents. The caller is required to + * obtain the name mutex. + */ + +OM_uint32 +gssEapCreateAttrContext(OM_uint32 *minor, + gss_cred_id_t acceptorCred, + gss_ctx_id_t acceptorCtx, + struct gss_eap_attr_ctx **pAttrCtx, + time_t *pExpiryTime); + +OM_uint32 +gssEapInquireName(OM_uint32 *minor, + gss_name_t name, + int *name_is_MN, + gss_OID *MN_mech, + gss_buffer_set_t *attrs); + +OM_uint32 +gssEapGetNameAttribute(OM_uint32 *minor, + 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 +gssEapDeleteNameAttribute(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t attr); + +OM_uint32 +gssEapSetNameAttribute(OM_uint32 *minor, + gss_name_t name, + int complete, + gss_buffer_t attr, + gss_buffer_t value); + +OM_uint32 +gssEapExportAttrContext(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t buffer); + +OM_uint32 +gssEapImportAttrContext(OM_uint32 *minor, + gss_buffer_t buffer, + gss_name_t name); + +OM_uint32 +gssEapDuplicateAttrContext(OM_uint32 *minor, + gss_name_t in, + gss_name_t out); + +OM_uint32 +gssEapMapNameToAny(OM_uint32 *minor, + gss_name_t name, + int authenticated, + gss_buffer_t type_id, + gss_any_t *output); + +OM_uint32 +gssEapReleaseAnyNameMapping(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t type_id, + gss_any_t *input); + +OM_uint32 +gssEapReleaseAttrContext(OM_uint32 *minor, + gss_name_t name); + +OM_uint32 +gssEapAttrProvidersFinalize(OM_uint32 *minor); + +#ifdef __cplusplus +} +#endif + +#endif /* _UTIL_ATTR_H_ */ diff --git a/util_buffer.c b/util_buffer.c new file mode 100644 index 0000000..a553882 --- /dev/null +++ b/util_buffer.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Buffer handling helpers. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +makeStringBuffer(OM_uint32 *minor, + const char *string, + gss_buffer_t buffer) +{ + size_t len = strlen(string); + + buffer->value = GSSEAP_MALLOC(len + 1); + if (buffer->value == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + memcpy(buffer->value, string, len + 1); + buffer->length = len; + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +bufferToString(OM_uint32 *minor, + const gss_buffer_t buffer, + char **pString) +{ + char *s; + + s = GSSEAP_MALLOC(buffer->length + 1); + if (s == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + memcpy(s, buffer->value, buffer->length); + s[buffer->length] = '\0'; + + *pString = s; + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +duplicateBuffer(OM_uint32 *minor, + const gss_buffer_t src, + gss_buffer_t dst) +{ + dst->length = 0; + dst->value = NULL; + + if (src == GSS_C_NO_BUFFER) + return GSS_S_COMPLETE; + + dst->value = GSSEAP_MALLOC(src->length + 1); + if (dst->value == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + dst->length = src->length; + memcpy(dst->value, src->value, dst->length); + + ((unsigned char *)dst->value)[dst->length] = '\0'; + + *minor = 0; + return GSS_S_COMPLETE; +} diff --git a/util_cksum.c b/util_cksum.c new file mode 100644 index 0000000..cbd531d --- /dev/null +++ b/util_cksum.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ +/* + * 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. + */ + +/* + * Message protection services: checksum helpers. + */ + +#include "gssapiP_eap.h" + +static int +gssEapChecksum(krb5_context context, + krb5_cksumtype type, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *crypto, +#endif + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int verify, + int *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; + size_t k5_checksumlen; +#ifdef HAVE_HEIMDAL_VERSION + krb5_cksumtype cksumtype; +#endif + + if (verify) + *valid = FALSE; + + code = krbCryptoLength(context, crypto, KRB5_CRYPTO_TYPE_CHECKSUM, &k5_checksumlen); + if (code != 0) + return code; + + header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); + assert(header != NULL); + + trailer = gssEapLocateIov(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 *)GSSEAP_MALLOC(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 = gssEapMapCryptoFlag(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++; + +#ifdef HAVE_HEIMDAL_VERSION + if (verify) { + code = krb5_verify_checksum_iov(context, crypto, sign_usage, + kiov, kiov_count, &cksumtype); + *valid = (code == 0); + } else { + code = krb5_create_checksum_iov(context, crypto, sign_usage, + kiov, kiov_count, &cksumtype); + } +#else + if (verify) { + krb5_boolean kvalid = FALSE; + + code = krb5_c_verify_checksum_iov(context, type, crypto, + sign_usage, kiov, kiov_count, &kvalid); + + *valid = kvalid; + } else { + code = krb5_c_make_checksum_iov(context, type, crypto, + sign_usage, kiov, kiov_count); + } +#endif /* HAVE_HEIMDAL_VERSION */ + + GSSEAP_FREE(kiov); + + return code; +} + +int +gssEapSign(krb5_context context, + krb5_cksumtype type, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *crypto, +#endif + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count) +{ + return gssEapChecksum(context, type, rrc, crypto, + sign_usage, iov, iov_count, 0, NULL); +} + +int +gssEapVerify(krb5_context context, + krb5_cksumtype type, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *crypto, +#endif + krb5_keyusage sign_usage, + gss_iov_buffer_desc *iov, + int iov_count, + int *valid) +{ + return gssEapChecksum(context, type, rrc, crypto, + sign_usage, iov, iov_count, 1, valid); +} + +#if 0 +OM_uint32 +gssEapEncodeGssChannelBindings(OM_uint32 *minor, + gss_channel_bindings_t chanBindings, + gss_buffer_t encodedBindings) +{ + OM_uint32 major, tmpMinor; + size_t length; + unsigned char *p; + + if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS) { + length = 24; + length += chanBindings->initiator_address.length; + length += chanBindings->acceptor_address.length; + length += chanBindings->application_data.length; + + encodedBindings->value = GSSEAP_MALLOC(length); + if (encodedBindings->value == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + encodedBindings->length = length; + p = (unsigned char *)encodedBindings->value; + + store_uint32_be(chanBindings->initiator_addrtype, p); + store_buffer(&chanBindings->initiator_address, p + 4, 0); + p += 4 + chanBindings->initiator_address.length; + + store_uint32_be(chanBindings->acceptor_addrtype, p); + store_buffer(&chanBindings->acceptor_address, p + 4, 0); + p += 4 + chanBindings->acceptor_address.length; + + store_buffer(&chanBindings->application_data, p, 1); + p += chanBindings->application_data.length; + } else { + encodedBindings->length = 0; + encodedBindings->value = NULL; + } + + *minor = 0; + return GSS_S_COMPLETE; +} +#endif diff --git a/util_context.c b/util_context.c new file mode 100644 index 0000000..0020ef6 --- /dev/null +++ b/util_context.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Utility routines for context handles. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gssEapAllocContext(OM_uint32 *minor, + gss_ctx_id_t *pCtx) +{ + OM_uint32 tmpMinor; + gss_ctx_id_t ctx; + + assert(*pCtx == GSS_C_NO_CONTEXT); + + ctx = (gss_ctx_id_t)GSSEAP_CALLOC(1, sizeof(*ctx)); + if (ctx == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + if (GSSEAP_MUTEX_INIT(&ctx->mutex) != 0) { + *minor = errno; + gssEapReleaseContext(&tmpMinor, &ctx); + return GSS_S_FAILURE; + } + + ctx->state = GSSEAP_STATE_IDENTITY; + + /* + * Integrity, confidentiality, sequencing and replay detection are + * always available. Regardless of what flags are requested in + * GSS_Init_sec_context, implementations MUST set the flag corresponding + * to these services in the output of GSS_Init_sec_context and + * GSS_Accept_sec_context. + */ + ctx->gssFlags = GSS_C_TRANS_FLAG | /* exporting contexts */ + GSS_C_INTEG_FLAG | /* integrity */ + GSS_C_CONF_FLAG | /* confidentiality */ + GSS_C_SEQUENCE_FLAG | /* sequencing */ + GSS_C_REPLAY_FLAG; /* replay detection */ + + *pCtx = ctx; + + return GSS_S_COMPLETE; +} + +static void +releaseInitiatorContext(struct gss_eap_initiator_ctx *ctx) +{ + eap_peer_sm_deinit(ctx->eap); +} + +static void +releaseAcceptorContext(struct gss_eap_acceptor_ctx *ctx) +{ + OM_uint32 tmpMinor; + + if (ctx->radConn != NULL) + rs_conn_destroy(ctx->radConn); + if (ctx->radContext != NULL) + rs_context_destroy(ctx->radContext); + if (ctx->radServer != NULL) + GSSEAP_FREE(ctx->radServer); + gss_release_buffer(&tmpMinor, &ctx->state); + if (ctx->vps != NULL) + gssEapRadiusFreeAvps(&tmpMinor, &ctx->vps); +} + +OM_uint32 +gssEapReleaseContext(OM_uint32 *minor, + gss_ctx_id_t *pCtx) +{ + OM_uint32 tmpMinor; + gss_ctx_id_t ctx = *pCtx; + krb5_context krbContext = NULL; + + if (ctx == GSS_C_NO_CONTEXT) { + return GSS_S_COMPLETE; + } + + gssEapKerberosInit(&tmpMinor, &krbContext); + +#ifdef GSSEAP_ENABLE_REAUTH + if (ctx->flags & CTX_FLAG_KRB_REAUTH) { + gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER); + } else +#endif + if (CTX_IS_INITIATOR(ctx)) { + releaseInitiatorContext(&ctx->initiatorCtx); + } else { + releaseAcceptorContext(&ctx->acceptorCtx); + } + + krb5_free_keyblock_contents(krbContext, &ctx->rfc3961Key); + gssEapReleaseName(&tmpMinor, &ctx->initiatorName); + gssEapReleaseName(&tmpMinor, &ctx->acceptorName); + gssEapReleaseOid(&tmpMinor, &ctx->mechanismUsed); + sequenceFree(&tmpMinor, &ctx->seqState); + gssEapReleaseCred(&tmpMinor, &ctx->defaultCred); + + GSSEAP_MUTEX_DESTROY(&ctx->mutex); + + memset(ctx, 0, sizeof(*ctx)); + GSSEAP_FREE(ctx); + *pCtx = GSS_C_NO_CONTEXT; + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapMakeToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t innerToken, + enum gss_eap_token_type tokenType, + gss_buffer_t outputToken) +{ + unsigned char *p; + + outputToken->length = tokenSize(ctx->mechanismUsed, innerToken->length); + outputToken->value = GSSEAP_MALLOC(outputToken->length); + if (outputToken->value == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + p = (unsigned char *)outputToken->value; + makeTokenHeader(ctx->mechanismUsed, innerToken->length, &p, tokenType); + memcpy(p, innerToken->value, innerToken->length); + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapVerifyToken(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_buffer_t inputToken, + enum gss_eap_token_type *actualToken, + gss_buffer_t innerInputToken) +{ + OM_uint32 major; + size_t bodySize; + unsigned char *p = (unsigned char *)inputToken->value; + gss_OID_desc oidBuf; + gss_OID oid; + + if (ctx->mechanismUsed != GSS_C_NO_OID) { + oid = ctx->mechanismUsed; + } else { + oidBuf.elements = NULL; + oidBuf.length = 0; + oid = &oidBuf; + } + + major = verifyTokenHeader(minor, oid, &bodySize, &p, + inputToken->length, actualToken); + if (GSS_ERROR(major)) + return major; + + if (ctx->mechanismUsed == GSS_C_NO_OID) { + if (!gssEapIsConcreteMechanismOid(oid)) { + *minor = GSSEAP_WRONG_MECH; + return GSS_S_BAD_MECH; + } + + if (!gssEapInternalizeOid(oid, &ctx->mechanismUsed)) { + major = duplicateOid(minor, oid, &ctx->mechanismUsed); + if (GSS_ERROR(major)) + return major; + } + } + + innerInputToken->length = bodySize; + innerInputToken->value = p; + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapContextTime(OM_uint32 *minor, + gss_ctx_id_t context_handle, + OM_uint32 *time_rec) +{ + if (context_handle->expiryTime == 0) { + *time_rec = GSS_C_INDEFINITE; + } else { + time_t now, lifetime; + + time(&now); + lifetime = context_handle->expiryTime - now; + if (lifetime <= 0) { + *time_rec = 0; + return GSS_S_CONTEXT_EXPIRED; + } + *time_rec = lifetime; + } + + return GSS_S_COMPLETE; +} diff --git a/util_cred.c b/util_cred.c new file mode 100644 index 0000000..c74cd24 --- /dev/null +++ b/util_cred.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Utility routines for credential handles. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred) +{ + OM_uint32 tmpMinor; + gss_cred_id_t cred; + + *pCred = GSS_C_NO_CREDENTIAL; + + cred = (gss_cred_id_t)GSSEAP_CALLOC(1, sizeof(*cred)); + if (cred == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + if (GSSEAP_MUTEX_INIT(&cred->mutex) != 0) { + *minor = errno; + gssEapReleaseCred(&tmpMinor, &cred); + return GSS_S_FAILURE; + } + + *pCred = cred; + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred) +{ + OM_uint32 tmpMinor; + gss_cred_id_t cred = *pCred; + krb5_context krbContext = NULL; + + if (cred == GSS_C_NO_CREDENTIAL) { + return GSS_S_COMPLETE; + } + + GSSEAP_KRB_INIT(&krbContext); + + gssEapReleaseName(&tmpMinor, &cred->name); + + if (cred->password.value != NULL) { + memset(cred->password.value, 0, cred->password.length); + GSSEAP_FREE(cred->password.value); + } + + if (cred->radiusConfigFile != NULL) + GSSEAP_FREE(cred->radiusConfigFile); + if (cred->radiusConfigStanza != NULL) + GSSEAP_FREE(cred->radiusConfigStanza); + +#ifdef GSSEAP_ENABLE_REAUTH + if (cred->krbCredCache != NULL) { + if (cred->flags & CRED_FLAG_DEFAULT_CCACHE) + krb5_cc_close(krbContext, cred->krbCredCache); + else + krb5_cc_destroy(krbContext, cred->krbCredCache); + } + if (cred->krbCred != GSS_C_NO_CREDENTIAL) + gssReleaseCred(&tmpMinor, &cred->krbCred); +#endif + + GSSEAP_MUTEX_DESTROY(&cred->mutex); + memset(cred, 0, sizeof(*cred)); + GSSEAP_FREE(cred); + *pCred = NULL; + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapAcquireCred(OM_uint32 *minor, + const gss_name_t desiredName, + const gss_buffer_t password, + OM_uint32 timeReq, + const gss_OID_set desiredMechs, + int credUsage, + gss_cred_id_t *pCred, + gss_OID_set *pActualMechs, + OM_uint32 *timeRec) +{ + OM_uint32 major, tmpMinor; + gss_cred_id_t cred; + + /* XXX TODO validate with changed set_cred_option API */ + *pCred = GSS_C_NO_CREDENTIAL; + + major = gssEapAllocCred(minor, &cred); + if (GSS_ERROR(major)) + goto cleanup; + + switch (credUsage) { + case GSS_C_BOTH: + cred->flags |= CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT; + break; + case GSS_C_INITIATE: + cred->flags |= CRED_FLAG_INITIATE; + break; + case GSS_C_ACCEPT: + cred->flags |= CRED_FLAG_ACCEPT; + break; + default: + major = GSS_S_FAILURE; + *minor = GSSEAP_BAD_USAGE; + goto cleanup; + break; + } + + if (desiredName != GSS_C_NO_NAME) { + GSSEAP_MUTEX_LOCK(&desiredName->mutex); + + major = gssEapDuplicateName(minor, desiredName, &cred->name); + if (GSS_ERROR(major)) { + GSSEAP_MUTEX_UNLOCK(&desiredName->mutex); + goto cleanup; + } + + GSSEAP_MUTEX_UNLOCK(&desiredName->mutex); + } else { + gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER; + gss_OID nameType = GSS_C_NO_OID; + + if (cred->flags & CRED_FLAG_ACCEPT) { + char serviceName[5 + MAXHOSTNAMELEN] = "host@"; + + /* default host-based service is host@localhost */ + if (gethostname(&serviceName[5], MAXHOSTNAMELEN) != 0) { + major = GSS_S_FAILURE; + *minor = GSSEAP_NO_HOSTNAME; + goto cleanup; + } + + nameBuf.value = serviceName; + nameBuf.length = strlen((char *)nameBuf.value); + + nameType = GSS_C_NT_HOSTBASED_SERVICE; + } else if (cred->flags & CRED_FLAG_INITIATE) { + nameBuf.value = getlogin(); /* XXX */ + nameBuf.length = strlen((char *)nameBuf.value); + + nameType = GSS_C_NT_USER_NAME; + } + + if (nameBuf.length != 0) { + major = gssEapImportName(minor, &nameBuf, nameType, &cred->name); + if (GSS_ERROR(major)) + goto cleanup; + } + + cred->flags |= CRED_FLAG_DEFAULT_IDENTITY; + } + + if (password != GSS_C_NO_BUFFER) { + major = duplicateBuffer(minor, password, &cred->password); + if (GSS_ERROR(major)) + goto cleanup; + + cred->flags |= CRED_FLAG_PASSWORD; + } else if (cred->flags & CRED_FLAG_INITIATE) { + /* + * OK, here we need to ask the supplicant if we have creds or it + * will acquire them, so GS2 can know whether to prompt for a + * password or not. + */ +#if 0 + && !gssEapCanReauthP(cred, GSS_C_NO_NAME, timeReq) +#endif + major = GSS_S_CRED_UNAVAIL; + *minor = GSSEAP_MISSING_PASSWORD; + goto cleanup; + } + + major = gssEapValidateMechs(minor, desiredMechs); + if (GSS_ERROR(major)) + goto cleanup; + + major = duplicateOidSet(minor, desiredMechs, &cred->mechanisms); + if (GSS_ERROR(major)) + goto cleanup; + + if (pActualMechs != NULL) { + major = duplicateOidSet(minor, cred->mechanisms, pActualMechs); + if (GSS_ERROR(major)) + goto cleanup; + } + + if (timeRec != NULL) + *timeRec = GSS_C_INDEFINITE; + + *pCred = cred; + + major = GSS_S_COMPLETE; + *minor = 0; + +cleanup: + if (GSS_ERROR(major)) + gssEapReleaseCred(&tmpMinor, &cred); + + return major; +} + +/* + * Return TRUE if cred available for mechanism. Caller need no acquire + * lock because mechanisms list is immutable. + */ +int +gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech) +{ + OM_uint32 minor; + int present = 0; + + assert(mech != GSS_C_NO_OID); + + if (cred == GSS_C_NO_CREDENTIAL || cred->mechanisms == GSS_C_NO_OID_SET) + return TRUE; + + gss_test_oid_set_member(&minor, mech, cred->mechanisms, &present); + + return present; +} diff --git a/util_crypt.c b/util_crypt.c new file mode 100644 index 0000000..9841c9d --- /dev/null +++ b/util_crypt.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ +/* + * 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. + */ + +/* + * Message protection services: cryptography helpers. + */ + +#include "gssapiP_eap.h" + +/* + * 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 +mapIov(krb5_context context, int dce_style, size_t ec, size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *crypto, +#endif + 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; + size_t k5_headerlen = 0, k5_trailerlen = 0; + size_t gss_headerlen, gss_trailerlen; + krb5_error_code code; + + *pkiov = NULL; + *pkiov_count = 0; + + header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); + assert(header != NULL); + + trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + assert(trailer == NULL || rrc == 0); + + code = krbCryptoLength(context, crypto, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen); + if (code != 0) + return code; + + code = krbCryptoLength(context, crypto, 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 *)GSSEAP_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 = gssEapMapCryptoFlag(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; +} + +int +gssEapEncrypt(krb5_context context, + int dce_style, + size_t ec, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *crypto, +#endif + int usage, + gss_iov_buffer_desc *iov, + int iov_count) +{ + krb5_error_code code; + size_t kiov_count; + krb5_crypto_iov *kiov = NULL; + + code = mapIov(context, dce_style, ec, rrc, crypto, + iov, iov_count, &kiov, &kiov_count); + if (code != 0) + goto cleanup; + +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_encrypt_iov_ivec(context, crypto, usage, kiov, kiov_count, NULL); +#else + code = krb5_c_encrypt_iov(context, crypto, usage, NULL, kiov, kiov_count); +#endif + if (code != 0) + goto cleanup; + +cleanup: + if (kiov != NULL) + GSSEAP_FREE(kiov); + + return code; +} + +int +gssEapDecrypt(krb5_context context, + int dce_style, + size_t ec, + size_t rrc, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto crypto, +#else + krb5_keyblock *crypto, +#endif + int usage, + gss_iov_buffer_desc *iov, + int iov_count) +{ + krb5_error_code code; + size_t kiov_count; + krb5_crypto_iov *kiov; + + code = mapIov(context, dce_style, ec, rrc, crypto, + iov, iov_count, &kiov, &kiov_count); + if (code != 0) + goto cleanup; + +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_decrypt_iov_ivec(context, crypto, usage, kiov, kiov_count, NULL); +#else + code = krb5_c_decrypt_iov(context, crypto, usage, NULL, kiov, kiov_count); +#endif + +cleanup: + if (kiov != NULL) + GSSEAP_FREE(kiov); + + return code; +} + +int +gssEapMapCryptoFlag(OM_uint32 type) +{ + int 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 +gssEapLocateIov(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; +} + +void +gssEapIovMessageLength(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 +gssEapReleaseIov(gss_iov_buffer_desc *iov, int iov_count) +{ + int i; + OM_uint32 min_stat; + + assert(iov != GSS_C_NO_IOV_BUFFER); + + for (i = 0; i < iov_count; i++) { + if (iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATED) { + gss_release_buffer(&min_stat, &iov[i].buffer); + iov[i].type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED); + } + } +} + +int +gssEapIsIntegrityOnly(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); +} + +int +gssEapAllocIov(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 = GSSEAP_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/util_exts.c b/util_exts.c new file mode 100644 index 0000000..46534d9 --- /dev/null +++ b/util_exts.c @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Extension token support. + */ + +#include "gssapiP_eap.h" + +static OM_uint32 +encodeExtensions(OM_uint32 *minor, + gss_buffer_set_t extensions, + OM_uint32 *types, + gss_buffer_t buffer); + +static OM_uint32 +decodeExtensions(OM_uint32 *minor, + const gss_buffer_t buffer, + gss_buffer_set_t *pExtensions, + OM_uint32 **pTypes); + +/* + * Initiator extensions + */ +static OM_uint32 +makeGssChannelBindings(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_channel_bindings_t chanBindings, + gss_buffer_t outputToken) +{ + OM_uint32 major; + gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER; + + if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS) + buffer = chanBindings->application_data; + + major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT, + &buffer, NULL, outputToken); + if (GSS_ERROR(major)) + return major; + + return GSS_S_COMPLETE; +} + +static OM_uint32 +verifyGssChannelBindings(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken) +{ + OM_uint32 major, tmpMinor; + gss_iov_buffer_desc iov[2]; + + iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE; + iov[0].buffer.length = 0; + iov[0].buffer.value = NULL; + + iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM; + iov[1].buffer = *inputToken; + + major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL, + iov, 2, TOK_TYPE_WRAP); + if (GSS_ERROR(major)) + return GSS_S_BAD_BINDINGS; + + if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS && + !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) { + major = GSS_S_BAD_BINDINGS; + *minor = GSSEAP_BINDINGS_MISMATCH; + } else { + major = GSS_S_COMPLETE; + } + + gss_release_buffer(&tmpMinor, &iov[0].buffer); + + return major; +} + +static struct gss_eap_extension_provider +eapGssInitExtensions[] = { + { + EXT_TYPE_GSS_CHANNEL_BINDINGS, + 1, /* critical */ + 1, /* required */ + makeGssChannelBindings, + verifyGssChannelBindings + }, +}; + +/* + * Acceptor extensions + */ +static OM_uint32 +makeReauthCreds(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_channel_bindings_t chanBindings, + gss_buffer_t outputToken) +{ + OM_uint32 major = GSS_S_UNAVAILABLE; + +#ifdef GSSEAP_ENABLE_REAUTH + /* + * If we're built with fast reauthentication enabled, then + * fabricate a ticket from the initiator to ourselves. + */ + major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken); +#endif + + return major; +} + +static OM_uint32 +verifyReauthCreds(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_channel_bindings_t chanBindings, + gss_buffer_t inputToken) +{ + OM_uint32 major = GSS_S_UNAVAILABLE; + +#ifdef GSSEAP_ENABLE_REAUTH + major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken); +#endif + + return major; +} + +static struct gss_eap_extension_provider +eapGssAcceptExtensions[] = { + { + EXT_TYPE_REAUTH_CREDS, + 0, /* critical */ + 0, /* required */ + makeReauthCreds, + verifyReauthCreds + }, +}; + +OM_uint32 +makeExtensions(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + const struct gss_eap_extension_provider *exts, + size_t nexts, + gss_channel_bindings_t chanBindings, + gss_buffer_t buffer) +{ + OM_uint32 major, tmpMinor; + size_t i, j; + gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET; + OM_uint32 *types; + + assert(buffer != GSS_C_NO_BUFFER); + + buffer->length = 0; + buffer->value = NULL; + + types = GSSEAP_CALLOC(nexts, sizeof(OM_uint32)); + if (types == NULL) { + major = GSS_S_FAILURE; + *minor = ENOMEM; + goto cleanup; + } + + for (i = 0, j = 0; i < nexts; i++) { + const struct gss_eap_extension_provider *ext = &exts[i]; + gss_buffer_desc extension = GSS_C_EMPTY_BUFFER; + + types[j] = ext->type; + if (ext->critical) + types[j] |= EXT_FLAG_CRITICAL; + + major = ext->make(minor, cred, ctx, chanBindings, &extension); + if (GSS_ERROR(major)) { + if (ext->critical) + goto cleanup; + else + continue; + } + + major = gss_add_buffer_set_member(minor, &extension, &extensions); + if (GSS_ERROR(major)) + goto cleanup; + + j++; + } + + assert(j == (extensions == GSS_C_NO_BUFFER_SET ? 0 : extensions->count)); + + major = encodeExtensions(minor, extensions, types, buffer); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + gss_release_buffer_set(&tmpMinor, &extensions); + if (types != NULL) + GSSEAP_FREE(types); + + return major; +} + +OM_uint32 +gssEapMakeExtensions(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_channel_bindings_t chanBindings, + gss_buffer_t buffer) +{ + size_t nexts; + const struct gss_eap_extension_provider *exts; + + if (CTX_IS_INITIATOR(ctx)) { + exts = eapGssInitExtensions; + nexts = sizeof(eapGssInitExtensions) / sizeof(eapGssInitExtensions[0]); + } else { + exts = eapGssAcceptExtensions; + nexts = sizeof(eapGssAcceptExtensions) / sizeof(eapGssAcceptExtensions[0]); + } + + return makeExtensions(minor, cred, ctx, exts, nexts, chanBindings, buffer); +} + +static OM_uint32 +verifyExtensions(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + const struct gss_eap_extension_provider *exts, + size_t nexts, + gss_channel_bindings_t chanBindings, + const gss_buffer_t buffer) +{ + OM_uint32 major, tmpMinor; + gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET; + OM_uint32 *types = NULL; + size_t i; + + major = decodeExtensions(minor, buffer, &extensions, &types); + if (GSS_ERROR(major)) + goto cleanup; + + for (i = 0; i < nexts; i++) { + const struct gss_eap_extension_provider *ext = &exts[i]; + gss_buffer_t extension = GSS_C_NO_BUFFER; + size_t j; + + for (j = 0; j < extensions->count; j++) { + if ((types[j] & EXT_TYPE_MASK) == ext->type) { + extension = &extensions->elements[j]; + break; + } + } + + if (extension != GSS_C_NO_BUFFER) { + /* Process extension and mark as verified */ + major = ext->verify(minor, cred, ctx, chanBindings, + &extensions->elements[j]); + if (GSS_ERROR(major)) + goto cleanup; + + types[j] |= EXT_FLAG_VERIFIED; + } else if (ext->required) { + /* Required extension missing */ + major = GSS_S_UNAVAILABLE; + *minor = GSSEAP_MISSING_REQUIRED_EXT; + goto cleanup; + } + } + + /* Check we processed all critical extensions */ + for (i = 0; i < extensions->count; i++) { + if ((types[i] & EXT_FLAG_CRITICAL) && + (types[i] & EXT_FLAG_VERIFIED) == 0) { + major = GSS_S_UNAVAILABLE; + *minor = GSSEAP_CRIT_EXT_UNAVAILABLE; + goto cleanup; + } + } + + major = GSS_S_COMPLETE; + *minor = 0; + +cleanup: + gss_release_buffer_set(&tmpMinor, &extensions); + if (types != NULL) + GSSEAP_FREE(types); + + return major; +} + +OM_uint32 +gssEapVerifyExtensions(OM_uint32 *minor, + gss_cred_id_t cred, + gss_ctx_id_t ctx, + gss_channel_bindings_t chanBindings, + const gss_buffer_t buffer) +{ + size_t nexts; + const struct gss_eap_extension_provider *exts; + + if (CTX_IS_INITIATOR(ctx)) { + exts = eapGssAcceptExtensions; + nexts = sizeof(eapGssAcceptExtensions) / sizeof(eapGssAcceptExtensions[0]); + } else { + exts = eapGssInitExtensions; + nexts = sizeof(eapGssInitExtensions) / sizeof(eapGssInitExtensions[0]); + } + + return verifyExtensions(minor, cred, ctx, exts, nexts, chanBindings, buffer); +} + +static OM_uint32 +encodeExtensions(OM_uint32 *minor, + gss_buffer_set_t extensions, + OM_uint32 *types, + gss_buffer_t buffer) +{ + OM_uint32 major, tmpMinor; + size_t required = 0, i; + unsigned char *p; + + buffer->value = NULL; + buffer->length = 0; + + if (extensions != GSS_C_NO_BUFFER_SET) { + for (i = 0; i < extensions->count; i++) { + required += 8 + extensions->elements[i].length; + } + } + + /* + * We must always return a non-NULL token otherwise the calling state + * machine assumes we are finished. Hence care in case malloc(0) does + * return NULL. + */ + buffer->value = GSSEAP_MALLOC(required ? required : 1); + if (buffer->value == NULL) { + major = GSS_S_FAILURE; + *minor = ENOMEM; + goto cleanup; + } + + buffer->length = required; + p = (unsigned char *)buffer->value; + + if (extensions != GSS_C_NO_BUFFER_SET) { + for (i = 0; i < extensions->count; i++) { + gss_buffer_t extension = &extensions->elements[i]; + + assert((types[i] & EXT_FLAG_VERIFIED) == 0); /* private flag */ + + /* + * Extensions are encoded as type-length-value, where the upper + * bit of the type indicates criticality. + */ + store_uint32_be(types[i], &p[0]); + store_uint32_be(extension->length, &p[4]); + memcpy(&p[8], extension->value, extension->length); + + p += 8 + extension->length; + } + } + + assert(p == (unsigned char *)buffer->value + required); + assert(buffer->value != NULL); + + major = GSS_S_COMPLETE; + *minor = 0; + +cleanup: + if (GSS_ERROR(major)) { + gss_release_buffer(&tmpMinor, buffer); + } + + return major; +} + +static OM_uint32 +decodeExtensions(OM_uint32 *minor, + const gss_buffer_t buffer, + gss_buffer_set_t *pExtensions, + OM_uint32 **pTypes) +{ + OM_uint32 major, tmpMinor; + gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET; + OM_uint32 *types = NULL; + unsigned char *p; + size_t remain; + + *pExtensions = GSS_C_NO_BUFFER_SET; + *pTypes = NULL; + + major = gss_create_empty_buffer_set(minor, &extensions); + if (GSS_ERROR(major)) + goto cleanup; + + if (buffer->length == 0) { + major = GSS_S_COMPLETE; + goto cleanup; + } + + p = (unsigned char *)buffer->value; + remain = buffer->length; + + do { + OM_uint32 *ntypes; + gss_buffer_desc extension; + + if (remain < 8) { + major = GSS_S_DEFECTIVE_TOKEN; + *minor = GSSEAP_TOK_TRUNC; + goto cleanup; + } + + ntypes = GSSEAP_REALLOC(types, + (extensions->count + 1) * sizeof(OM_uint32)); + if (ntypes == NULL) { + major = GSS_S_FAILURE; + *minor = ENOMEM; + goto cleanup; + } + types = ntypes; + + types[extensions->count] = load_uint32_be(&p[0]); + extension.length = load_uint32_be(&p[4]); + + if (remain < 8 + extension.length) { + major = GSS_S_DEFECTIVE_TOKEN; + *minor = GSSEAP_TOK_TRUNC; + goto cleanup; + } + extension.value = &p[8]; + + major = gss_add_buffer_set_member(minor, &extension, &extensions); + if (GSS_ERROR(major)) + goto cleanup; + + p += 8 + extension.length; + remain -= 8 + extension.length; + } while (remain != 0); + +cleanup: + if (GSS_ERROR(major)) { + gss_release_buffer_set(&tmpMinor, &extensions); + if (types != NULL) + GSSEAP_FREE(types); + } else { + *pExtensions = extensions; + *pTypes = types; + } + + return major; +} diff --git a/util_krb.c b/util_krb.c new file mode 100644 index 0000000..7143685 --- /dev/null +++ b/util_krb.c @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Kerberos 5 helpers. + */ + +#include "gssapiP_eap.h" + +static GSSEAP_THREAD_ONCE krbContextKeyOnce = GSSEAP_ONCE_INITIALIZER; +static GSSEAP_THREAD_KEY krbContextKey; + +static void +destroyKrbContext(void *arg) +{ + krb5_context context = (krb5_context)arg; + + if (context != NULL) + krb5_free_context(context); +} + +static void +createKrbContextKey(void) +{ + GSSEAP_KEY_CREATE(&krbContextKey, destroyKrbContext); +} + +OM_uint32 +gssEapKerberosInit(OM_uint32 *minor, krb5_context *context) +{ + *minor = 0; + + GSSEAP_ONCE(&krbContextKeyOnce, createKrbContextKey); + + *context = GSSEAP_GETSPECIFIC(krbContextKey); + if (*context == NULL) { + *minor = krb5_init_context(context); + if (*minor == 0) { + if (GSSEAP_SETSPECIFIC(krbContextKey, *context) != 0) { + *minor = errno; + krb5_free_context(*context); + *context = NULL; + } + } + } + + return *minor == 0 ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + +/* + * Derive a key K for RFC 4121 use by using the following + * derivation function (based on RFC 4402); + * + * KMSK = random-to-key(MSK) + * Tn = pseudo-random(KMSK, n || "rfc4121-gss-eap") + * L = output key size + * K = truncate(L, T1 || T2 || .. || Tn) + */ +OM_uint32 +gssEapDeriveRfc3961Key(OM_uint32 *minor, + const unsigned char *inputKey, + size_t inputKeyLength, + krb5_enctype encryptionType, + krb5_keyblock *pKey) +{ + krb5_context krbContext; +#ifndef HAVE_HEIMDAL_VERSION + krb5_data data; +#endif + krb5_data ns, t, prfOut; + krb5_keyblock kd; + krb5_error_code code; + size_t randomLength, keyLength, prfLength; + unsigned char constant[4 + sizeof("rfc4121-gss-eap") - 1], *p; + ssize_t i, remain; + + assert(encryptionType != ENCTYPE_NULL); + + memset(pKey, 0, sizeof(*pKey)); + + GSSEAP_KRB_INIT(&krbContext); + + KRB_KEY_INIT(&kd); + KRB_KEY_TYPE(&kd) = encryptionType; + + t.data = NULL; + t.length = 0; + + prfOut.data = NULL; + prfOut.length = 0; + + code = krb5_c_keylengths(krbContext, encryptionType, + &randomLength, &keyLength); + if (code != 0) + goto cleanup; + + KRB_KEY_DATA(&kd) = GSSEAP_MALLOC(keyLength); + if (KRB_KEY_DATA(&kd) == NULL) { + code = ENOMEM; + goto cleanup; + } + KRB_KEY_LENGTH(&kd) = keyLength; + + /* Convert MSK into a Kerberos key */ +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_random_to_key(krbContext, encryptionType, inputKey, + MIN(inputKeyLength, randomLength), &kd); +#else + data.length = MIN(inputKeyLength, randomLength); + data.data = (char *)inputKey; + + code = krb5_c_random_to_key(krbContext, encryptionType, &data, &kd); +#endif + if (code != 0) + goto cleanup; + + memset(&constant[0], 0, 4); + memcpy(&constant[4], "rfc4121-gss-eap", sizeof("rfc4121-gss-eap") - 1); + + ns.length = sizeof(constant); + ns.data = (char *)constant; + + /* Plug derivation constant and key into PRF */ + code = krb5_c_prf_length(krbContext, encryptionType, &prfLength); + if (code != 0) + goto cleanup; + + t.length = prfLength; + t.data = GSSEAP_MALLOC(t.length); + if (t.data == NULL) { + code = ENOMEM; + goto cleanup; + } + + prfOut.length = randomLength; + prfOut.data = GSSEAP_MALLOC(prfOut.length); + if (prfOut.data == NULL) { + code = ENOMEM; + goto cleanup; + } + + for (i = 0, p = (unsigned char *)prfOut.data, remain = randomLength; + remain > 0; + p += t.length, remain -= t.length, i++) + { + store_uint32_be(i, ns.data); + + code = krb5_c_prf(krbContext, &kd, &ns, &t); + if (code != 0) + goto cleanup; + + memcpy(p, t.data, MIN(t.length, remain)); + } + + /* Finally, convert PRF output into a new key which we will return */ +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_random_to_key(krbContext, encryptionType, + prfOut.data, prfOut.length, &kd); +#else + code = krb5_c_random_to_key(krbContext, encryptionType, &prfOut, &kd); +#endif + if (code != 0) + goto cleanup; + + *pKey = kd; + KRB_KEY_DATA(&kd) = NULL; + +cleanup: + if (KRB_KEY_DATA(&kd) != NULL) { + memset(KRB_KEY_DATA(&kd), 0, KRB_KEY_LENGTH(&kd)); + GSSEAP_FREE(KRB_KEY_DATA(&kd)); + } + if (t.data != NULL) { + memset(t.data, 0, t.length); + GSSEAP_FREE(t.data); + } + if (prfOut.data != NULL) { + memset(prfOut.data, 0, prfOut.length); + GSSEAP_FREE(prfOut.data); + } + *minor = code; + return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + +#ifdef HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE +extern krb5_error_code +krb5int_c_mandatory_cksumtype(krb5_context, krb5_enctype, krb5_cksumtype *); +#endif + +OM_uint32 +rfc3961ChecksumTypeForKey(OM_uint32 *minor, + krb5_keyblock *key, + krb5_cksumtype *cksumtype) +{ + krb5_context krbContext; +#ifndef HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE + krb5_data data; + krb5_checksum cksum; +#endif + + GSSEAP_KRB_INIT(&krbContext); + +#ifdef HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE + *minor = krb5int_c_mandatory_cksumtype(krbContext, KRB_KEY_TYPE(key), + cksumtype); + if (*minor != 0) + return GSS_S_FAILURE; +#else + data.length = 0; + data.data = NULL; + + memset(&cksum, 0, sizeof(cksum)); + + /* + * This is a complete hack but it's the only way to work with + * MIT Kerberos pre-1.9 without using private API, as it does + * not support passing in zero as the checksum type. + */ + *minor = krb5_c_make_checksum(krbContext, 0, key, 0, &data, &cksum); + if (*minor != 0) + return GSS_S_FAILURE; + +#ifdef HAVE_HEIMDAL_VERSION + *cksumtype = cksum.cksumtype; +#else + *cksumtype = cksum.checksum_type; +#endif + + krb5_free_checksum_contents(krbContext, &cksum); +#endif /* HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE */ + + if (!krb5_c_is_keyed_cksum(*cksumtype)) { + *minor = KRB5KRB_AP_ERR_INAPP_CKSUM; + return GSS_S_FAILURE; + } + + return GSS_S_COMPLETE; +} + +#ifdef HAVE_HEIMDAL_VERSION +static heim_general_string krbAnonymousPrincipalComponents[] = + { KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME }; + +static const Principal krbAnonymousPrincipalData = { + { KRB5_NT_WELLKNOWN, { 2, krbAnonymousPrincipalComponents } }, + "WELLKNOWN:ANONYMOUS" +}; +#endif + +krb5_const_principal +krbAnonymousPrincipal(void) +{ +#ifdef HAVE_HEIMDAL_VERSION + return &krbAnonymousPrincipalData; +#else + return krb5_anonymous_principal(); +#endif +} + +krb5_error_code +krbCryptoLength(krb5_context krbContext, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *key, +#endif + int type, + size_t *length) +{ +#ifdef HAVE_HEIMDAL_VERSION + return krb5_crypto_length(krbContext, krbCrypto, type, length); +#else + unsigned int len; + krb5_error_code code; + + code = krb5_c_crypto_length(krbContext, KRB_KEY_TYPE(key), type, &len); + if (code == 0) + *length = (size_t)len; + + return code; +#endif +} + +krb5_error_code +krbPaddingLength(krb5_context krbContext, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *key, +#endif + size_t dataLength, + size_t *padLength) +{ + krb5_error_code code; +#ifdef HAVE_HEIMDAL_VERSION + size_t headerLength, paddingLength; + + code = krbCryptoLength(krbContext, krbCrypto, + KRB5_CRYPTO_TYPE_HEADER, &headerLength); + if (code != 0) + return code; + + dataLength += headerLength; + + code = krb5_crypto_length(krbContext, krbCrypto, + KRB5_CRYPTO_TYPE_PADDING, &paddingLength); + if (code != 0) + return code; + + if (paddingLength != 0 && (dataLength % paddingLength) != 0) + *padLength = paddingLength - (dataLength % paddingLength); + else + *padLength = 0; + + return 0; +#else + unsigned int pad; + + code = krb5_c_padding_length(krbContext, KRB_KEY_TYPE(key), dataLength, &pad); + if (code == 0) + *padLength = (size_t)pad; + + return code; +#endif /* HAVE_HEIMDAL_VERSION */ +} + +krb5_error_code +krbBlockSize(krb5_context krbContext, +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto, +#else + krb5_keyblock *key, +#endif + size_t *blockSize) +{ +#ifdef HAVE_HEIMDAL_VERSION + return krb5_crypto_getblocksize(krbContext, krbCrypto, blockSize); +#else + return krb5_c_block_size(krbContext, KRB_KEY_TYPE(key), blockSize); +#endif +} + +krb5_error_code +krbEnctypeToString(krb5_context krbContext, + krb5_enctype enctype, + const char *prefix, + gss_buffer_t string) +{ + krb5_error_code code; +#ifdef HAVE_HEIMDAL_VERSION + char *enctypeBuf = NULL; +#else + char enctypeBuf[128]; +#endif + size_t prefixLength, enctypeLength; + +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_enctype_to_string(krbContext, enctype, &enctypeBuf); +#else + code = krb5_enctype_to_name(enctype, 0, enctypeBuf, sizeof(enctypeBuf)); +#endif + if (code != 0) + return code; + + prefixLength = (prefix != NULL) ? strlen(prefix) : 0; + enctypeLength = strlen(enctypeBuf); + + string->value = GSSEAP_MALLOC(prefixLength + enctypeLength + 1); + if (string->value == NULL) { +#ifdef HAVE_HEIMDAL_VERSION + krb5_xfree(enctypeBuf); +#endif + return ENOMEM; + } + + if (prefixLength != 0) + memcpy(string->value, prefix, prefixLength); + memcpy((char *)string->value + prefixLength, enctypeBuf, enctypeLength); + + string->length = prefixLength + enctypeLength; + ((char *)string->value)[string->length] = '\0'; + +#ifdef HAVE_HEIMDAL_VERSION + krb5_xfree(enctypeBuf); +#endif + + return 0; +} + +krb5_error_code +krbMakeAuthDataKdcIssued(krb5_context context, + const krb5_keyblock *key, + krb5_const_principal issuer, +#ifdef HAVE_HEIMDAL_VERSION + const AuthorizationData *authdata, + AuthorizationData *adKdcIssued +#else + krb5_authdata *const *authdata, + krb5_authdata ***adKdcIssued +#endif + ) +{ +#ifdef HAVE_HEIMDAL_VERSION + krb5_error_code code; + AD_KDCIssued kdcIssued; + AuthorizationDataElement adDatum; + unsigned char *buf; + size_t buf_size, len; + krb5_crypto crypto = NULL; + + memset(&kdcIssued, 0, sizeof(kdcIssued)); + memset(adKdcIssued, 0, sizeof(*adKdcIssued)); + + kdcIssued.i_realm = issuer->realm != NULL ? (Realm *)&issuer->realm : NULL; + kdcIssued.i_sname = (PrincipalName *)&issuer->name; + kdcIssued.elements = *authdata; + + ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata, &len, code); + if (code != 0) + goto cleanup; + + code = krb5_crypto_init(context, key, 0, &crypto); + if (code != 0) + goto cleanup; + + code = krb5_create_checksum(context, crypto, KRB5_KU_AD_KDC_ISSUED, + 0, buf, buf_size, &kdcIssued.ad_checksum); + if (code != 0) + goto cleanup; + + GSSEAP_FREE(buf); + buf = NULL; + + ASN1_MALLOC_ENCODE(AD_KDCIssued, buf, buf_size, &kdcIssued, &len, code); + if (code != 0) + goto cleanup; + + adDatum.ad_type = KRB5_AUTHDATA_KDC_ISSUED; + adDatum.ad_data.length = buf_size; + adDatum.ad_data.data = buf; + + code = add_AuthorizationData(adKdcIssued, &adDatum); + if (code != 0) + goto cleanup; + +cleanup: + if (buf != NULL) + GSSEAP_FREE(buf); + if (crypto != NULL) + krb5_crypto_destroy(context, crypto); + free_Checksum(&kdcIssued.ad_checksum); + + return code; +#else + return krb5_make_authdata_kdc_issued(context, key, issuer, authdata, + adKdcIssued); +#endif /* HAVE_HEIMDAL_VERSION */ +} + +krb5_error_code +krbMakeCred(krb5_context krbContext, + krb5_auth_context authContext, + krb5_creds *creds, + krb5_data *data) +{ + krb5_error_code code; +#ifdef HAVE_HEIMDAL_VERSION + KRB_CRED krbCred; + KrbCredInfo krbCredInfo; + krb5_keyblock *key; + krb5_crypto krbCrypto = NULL; + krb5_data credInfoData = { 0 }; + size_t len; +#else + krb5_data *d = NULL; +#endif + + memset(data, 0, sizeof(*data)); +#ifdef HAVE_HEIMDAL_VERSION + memset(&krbCred, 0, sizeof(krbCred)); + memset(&krbCredInfo, 0, sizeof(krbCredInfo)); + + key = (authContext->local_subkey != NULL) + ? authContext->local_subkey + : authContext->keyblock; + + krbCred.pvno = 5; + krbCred.msg_type = krb_cred; + krbCred.tickets.val = (Ticket *)GSSEAP_CALLOC(1, sizeof(Ticket)); + if (krbCred.tickets.val == NULL) { + code = ENOMEM; + goto cleanup; + } + krbCred.tickets.len = 1; + + code = decode_Ticket(creds->ticket.data, + creds->ticket.length, + krbCred.tickets.val, &len); + if (code != 0) + goto cleanup; + + krbCredInfo.key = creds->session; + krbCredInfo.prealm = &creds->client->realm; + krbCredInfo.pname = &creds->client->name; + krbCredInfo.flags = &creds->flags.b; + krbCredInfo.authtime = &creds->times.authtime; + krbCredInfo.starttime = &creds->times.starttime; + krbCredInfo.endtime = &creds->times.endtime; + krbCredInfo.renew_till = &creds->times.renew_till; + krbCredInfo.srealm = &creds->server->realm; + krbCredInfo.sname = &creds->server->name; + krbCredInfo.caddr = creds->addresses.len ? &creds->addresses : NULL; + + ASN1_MALLOC_ENCODE(KrbCredInfo, credInfoData.data, credInfoData.length, + &krbCredInfo, &len, code); + if (code != 0) + goto cleanup; + + code = krb5_crypto_init(krbContext, key, 0, &krbCrypto); + if (code != 0) + goto cleanup; + + code = krb5_encrypt_EncryptedData(krbContext, + krbCrypto, + KRB5_KU_KRB_CRED, + credInfoData.data, + credInfoData.length, + 0, + &krbCred.enc_part); + if (code != 0) + goto cleanup; + + ASN1_MALLOC_ENCODE(KRB_CRED, data->data, data->length, + &krbCred, &len, code); + if (code != 0) + goto cleanup; + +cleanup: + if (krbCrypto != NULL) + krb5_crypto_destroy(krbContext, krbCrypto); + free_KRB_CRED(&krbCred); + krb5_data_free(&credInfoData); + + return code; +#else + code = krb5_mk_1cred(krbContext, authContext, creds, &d, NULL); + if (code == 0) { + *data = *d; + GSSEAP_FREE(d); + } + + return code; +#endif /* HAVE_HEIMDAL_VERSION */ +} diff --git a/util_lucid.c b/util_lucid.c new file mode 100644 index 0000000..e10074b --- /dev/null +++ b/util_lucid.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * "Lucid" security context export routine (called by MIT Kerberos mechanism). + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gssEapExportLucidSecContext(OM_uint32 *minor, + gss_ctx_id_t ctx, + const gss_OID desiredObject, + gss_buffer_set_t *data_set) +{ + OM_uint32 major = GSS_S_COMPLETE; + int haveAcceptorSubkey = + ((rfc4121Flags(ctx, 0) & TOK_FLAG_ACCEPTOR_SUBKEY) != 0); + gss_buffer_desc rep; +#ifdef HAVE_HEIMDAL_VERSION + krb5_error_code code; + krb5_storage *sp; + krb5_data data = { 0 }; + + sp = krb5_storage_emem(); + if (sp == NULL) { + code = ENOMEM; + goto cleanup; + } + + code = krb5_store_int32(sp, 1); /* version */ + if (code != 0) + goto cleanup; + + code = krb5_store_int32(sp, CTX_IS_INITIATOR(ctx)); + if (code != 0) + goto cleanup; + + code = krb5_store_int32(sp, ctx->expiryTime); + if (code != 0) + goto cleanup; + + code = krb5_store_int32(sp, 0); + if (code != 0) + goto cleanup; + + code = krb5_store_int32(sp, ctx->sendSeq); + if (code != 0) + goto cleanup; + + code = krb5_store_int32(sp, 0); + if (code != 0) + goto cleanup; + + code = krb5_store_int32(sp, ctx->recvSeq); + if (code != 0) + goto cleanup; + + code = krb5_store_int32(sp, 1); /* is_cfx */ + if (code != 0) + goto cleanup; + + code = krb5_store_int32(sp, haveAcceptorSubkey); + if (code != 0) + goto cleanup; + + code = krb5_store_keyblock(sp, ctx->rfc3961Key); + if (code != 0) + goto cleanup; + + if (haveAcceptorSubkey) { + code = krb5_store_keyblock(sp, ctx->rfc3961Key); + if (code != 0) + goto cleanup; + } + + code = krb5_storage_to_data(sp, &data); + if (code != 0) + goto cleanup; + + rep.length = data.length; + rep.value = data.data; + + major = gss_add_buffer_set_member(minor, &rep, data_set); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + krb5_data_free(&data); + + if (major == GSS_S_COMPLETE) { + *minor = code; + major = (code != 0) ? GSS_S_FAILURE : GSS_S_COMPLETE; + } + + return major; +#else + gss_krb5_lucid_context_v1_t *lctx; + gss_krb5_lucid_key_t *lkey = NULL; + + lctx = (gss_krb5_lucid_context_v1_t *)GSSEAP_CALLOC(1, sizeof(*lctx)); + if (lctx == NULL) { + major = GSS_S_FAILURE; + *minor = ENOMEM; + goto cleanup; + } + + lctx->version = 1; + lctx->initiate = CTX_IS_INITIATOR(ctx); + lctx->endtime = ctx->expiryTime; + lctx->send_seq = ctx->sendSeq; + lctx->recv_seq = ctx->recvSeq; + lctx->protocol = 1; + + lctx->cfx_kd.have_acceptor_subkey = haveAcceptorSubkey; + + lkey = haveAcceptorSubkey + ? &lctx->cfx_kd.ctx_key + : &lctx->cfx_kd.acceptor_subkey; + + lkey->type = KRB_KEY_TYPE(&ctx->rfc3961Key); + lkey->data = GSSEAP_MALLOC(KRB_KEY_LENGTH(&ctx->rfc3961Key)); + if (lkey->data == NULL) { + major = GSS_S_FAILURE; + *minor = ENOMEM; + goto cleanup; + } + lkey->length = KRB_KEY_LENGTH(&ctx->rfc3961Key); + memcpy(lkey->data, KRB_KEY_DATA(&ctx->rfc3961Key), lkey->length); + + rep.value = &lctx; + rep.length = sizeof(void *); + + major = gss_add_buffer_set_member(minor, &rep, data_set); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + if (GSS_ERROR(major)) { + if (lctx != NULL) { + if (lkey != NULL && lkey->data != NULL) { + memset(lkey->data, 0, lkey->length); + GSSEAP_FREE(lkey->data); + } + GSSEAP_FREE(lctx); + } + } + + return major; +#endif /* HAVE_HEIMDAL_VERSION */ +} diff --git a/util_mech.c b/util_mech.c new file mode 100644 index 0000000..7343b31 --- /dev/null +++ b/util_mech.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * General mechanism utility routines. + */ + +#include "gssapiP_eap.h" + +/* + * 1.3.6.1.4.1.5322(padl) + * gssEap(22) + * mechanisms(1) + * eap-aes128-cts-hmac-sha1-96(17) + * eap-aes256-cts-hmac-sha1-96(18) + * nameTypes(2) + * apiExtensions(3) + * inquireSecContextByOid(1) + * inquireCredByOid(2) + * setSecContextOption(3) + * setCredOption(4) + * mechInvoke(5) + */ + +/* + * Note: the enctype-less OID is used as the mechanism OID in exported + * names. There is no public symbol for it. This is consistent with + * the krb5 mechanism which, whilst known by many OIDs, always uses a + * canonical OID for exported names. (This OID is also returned by + * gss_inquire_name.) + */ +static gss_OID_desc gssEapMechOids[] = { + /* 1.3.6.1.4.1.5322.22.1 */ + { 9, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01" }, + /* 1.3.6.1.4.1.5322.22.1.17 */ + { 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01\x11" }, + /* 1.3.6.1.4.1.5322.22.1.18 */ + { 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01\x12" } +}; + +gss_OID GSS_EAP_MECHANISM = &gssEapMechOids[0]; +gss_OID GSS_EAP_AES128_CTS_HMAC_SHA1_96_MECHANISM = &gssEapMechOids[1]; +gss_OID GSS_EAP_AES256_CTS_HMAC_SHA1_96_MECHANISM = &gssEapMechOids[2]; + +/* + * Returns TRUE is the OID is a concrete mechanism OID, that is, one + * with a Kerberos enctype as the last element. + */ +int +gssEapIsConcreteMechanismOid(const gss_OID oid) +{ + return oid->length > GSS_EAP_MECHANISM->length && + memcmp(oid->elements, GSS_EAP_MECHANISM->elements, + GSS_EAP_MECHANISM->length) == 0; +} + +int +gssEapIsMechanismOid(const gss_OID oid) +{ + return oid == GSS_C_NO_OID || + oidEqual(oid, GSS_EAP_MECHANISM) || + gssEapIsConcreteMechanismOid(oid); +} + +/* + * Validate that all elements are concrete mechanism OIDs. + */ +OM_uint32 +gssEapValidateMechs(OM_uint32 *minor, + const gss_OID_set mechs) +{ + int i; + + *minor = 0; + + if (mechs == GSS_C_NO_OID_SET) { + return GSS_S_COMPLETE; + } + + for (i = 0; i < mechs->count; i++) { + gss_OID oid = &mechs->elements[i]; + + if (!gssEapIsConcreteMechanismOid(oid)) { + *minor = GSSEAP_WRONG_MECH; + return GSS_S_BAD_MECH; + } + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapOidToEnctype(OM_uint32 *minor, + const gss_OID oid, + krb5_enctype *enctype) +{ + OM_uint32 major; + int suffix; + + major = decomposeOid(minor, + GSS_EAP_MECHANISM->elements, + GSS_EAP_MECHANISM->length, + oid, + &suffix); + if (major == GSS_S_COMPLETE) + *enctype = suffix; + + return major; +} + +OM_uint32 +gssEapEnctypeToOid(OM_uint32 *minor, + krb5_enctype enctype, + gss_OID *pOid) +{ + OM_uint32 major; + gss_OID oid; + + *pOid = NULL; + + oid = (gss_OID)GSSEAP_MALLOC(sizeof(*oid)); + if (oid == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + oid->length = GSS_EAP_MECHANISM->length + 1; + oid->elements = GSSEAP_MALLOC(oid->length); + if (oid->elements == NULL) { + *minor = ENOMEM; + GSSEAP_FREE(oid); + return GSS_S_FAILURE; + } + + major = composeOid(minor, + GSS_EAP_MECHANISM->elements, + GSS_EAP_MECHANISM->length, + enctype, + oid); + if (major == GSS_S_COMPLETE) { + gssEapInternalizeOid(oid, pOid); + *pOid = oid; + } else { + GSSEAP_FREE(oid->elements); + GSSEAP_FREE(oid); + } + + return major; +} + +OM_uint32 +gssEapIndicateMechs(OM_uint32 *minor, + gss_OID_set *mechs) +{ + krb5_context krbContext; + OM_uint32 major, tmpMinor; + krb5_enctype *etypes; + int i; + + GSSEAP_KRB_INIT(&krbContext); + + *minor = krb5_get_permitted_enctypes(krbContext, &etypes); + if (*minor != 0) { + return GSS_S_FAILURE; + } + + major = gss_create_empty_oid_set(minor, mechs); + if (GSS_ERROR(major)) { + GSSEAP_FREE(etypes); + return major; + } + + for (i = 0; etypes[i] != ENCTYPE_NULL; i++) { + gss_OID mechOid; + + /* XXX currently we aren't equipped to encode these enctypes */ + if (etypes[i] < 0 || etypes[i] > 127) + continue; + + major = gssEapEnctypeToOid(minor, etypes[i], &mechOid); + if (GSS_ERROR(major)) + break; + + major = gss_add_oid_set_member(minor, mechOid, mechs); + if (GSS_ERROR(major)) + break; + + gss_release_oid(&tmpMinor, &mechOid); + } + + GSSEAP_FREE(etypes); + + *minor = 0; + return major; +} + +OM_uint32 +gssEapDefaultMech(OM_uint32 *minor, + gss_OID *oid) +{ + gss_OID_set mechs; + OM_uint32 major, tmpMinor; + + major = gssEapIndicateMechs(minor, &mechs); + if (GSS_ERROR(major)) { + return major; + } + + if (mechs->count == 0) { + gss_release_oid_set(&tmpMinor, &mechs); + return GSS_S_BAD_MECH; + } + + if (!gssEapInternalizeOid(&mechs->elements[0], oid)) { + /* don't double-free if we didn't internalize it */ + mechs->elements[0].length = 0; + mechs->elements[0].elements = NULL; + } + + gss_release_oid_set(&tmpMinor, &mechs); + + *minor = 0; + return GSS_S_COMPLETE; +} + +int +gssEapInternalizeOid(const gss_OID oid, + gss_OID *const pInternalizedOid) +{ + int i; + + *pInternalizedOid = GSS_C_NO_OID; + + for (i = 0; + i < sizeof(gssEapMechOids) / sizeof(gssEapMechOids[0]); + i++) { + if (oidEqual(oid, &gssEapMechOids[i])) { + *pInternalizedOid = (const gss_OID)&gssEapMechOids[i]; + break; + } + } + + if (*pInternalizedOid == GSS_C_NO_OID) { + if (oidEqual(oid, GSS_EAP_NT_PRINCIPAL_NAME)) + *pInternalizedOid = (const gss_OID)GSS_EAP_NT_PRINCIPAL_NAME; + } + + if (*pInternalizedOid == GSS_C_NO_OID) { + *pInternalizedOid = oid; + return 0; + } + + return 1; +} + +OM_uint32 +gssEapReleaseOid(OM_uint32 *minor, gss_OID *oid) +{ + gss_OID internalizedOid = GSS_C_NO_OID; + + *minor = 0; + + if (gssEapInternalizeOid(*oid, &internalizedOid)) { + /* OID was internalized, so we can mark it as "freed" */ + *oid = GSS_C_NO_OID; + return GSS_S_COMPLETE; + } + + /* we don't know about this OID */ + return GSS_S_CONTINUE_NEEDED; +} + +static gss_buffer_desc gssEapSaslMechs[] = { + { sizeof("EAP") - 1, "EAP", }, /* not used */ + { sizeof("EAP-AES128") - 1, "EAP-AES128" }, + { sizeof("EAP-AES256") - 1, "EAP-AES256" }, +}; + +gss_buffer_t +gssEapOidToSaslName(const gss_OID oid) +{ + size_t i; + + for (i = 1; i < sizeof(gssEapMechOids)/sizeof(gssEapMechOids[0]); i++) { + if (oidEqual(&gssEapMechOids[i], oid)) + return &gssEapSaslMechs[i]; + } + + return GSS_C_NO_BUFFER; +} + +gss_OID +gssEapSaslNameToOid(const gss_buffer_t name) +{ + size_t i; + + for (i = 1; i < sizeof(gssEapSaslMechs)/sizeof(gssEapSaslMechs[0]); i++) { + if (bufferEqual(&gssEapSaslMechs[i], name)) + return &gssEapMechOids[i]; + } + + return GSS_C_NO_OID; +} diff --git a/util_name.c b/util_name.c new file mode 100644 index 0000000..b1475f8 --- /dev/null +++ b/util_name.c @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ +/* + * Portions 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. + */ + +/* + * Name utility routines. + */ + +#include "gssapiP_eap.h" + +static gss_OID_desc gssEapNtPrincipalName = { + /* 1.3.6.1.4.1.5322.22.2.1 */ + 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x02\x01" +}; + +gss_OID GSS_EAP_NT_PRINCIPAL_NAME = &gssEapNtPrincipalName; + +OM_uint32 +gssEapAllocName(OM_uint32 *minor, gss_name_t *pName) +{ + OM_uint32 tmpMinor; + gss_name_t name; + + *pName = GSS_C_NO_NAME; + + name = (gss_name_t)GSSEAP_CALLOC(1, sizeof(*name)); + if (name == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + if (GSSEAP_MUTEX_INIT(&name->mutex) != 0) { + *minor = errno; + gssEapReleaseName(&tmpMinor, &name); + return GSS_S_FAILURE; + } + + *pName = name; + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName) +{ + gss_name_t name; + krb5_context krbContext = NULL; + OM_uint32 tmpMinor; + + *minor = 0; + + if (pName == NULL) { + return GSS_S_COMPLETE; + } + + name = *pName; + if (name == GSS_C_NO_NAME) { + return GSS_S_COMPLETE; + } + + GSSEAP_KRB_INIT(&krbContext); + krb5_free_principal(krbContext, name->krbPrincipal); + + gssEapReleaseAttrContext(&tmpMinor, name); + + GSSEAP_MUTEX_DESTROY(&name->mutex); + GSSEAP_FREE(name); + *pName = NULL; + + return GSS_S_COMPLETE; +} + +static OM_uint32 +krbPrincipalToName(OM_uint32 *minor, + krb5_principal *principal, + gss_name_t *pName) +{ + OM_uint32 major; + gss_name_t name; + + major = gssEapAllocName(minor, &name); + if (GSS_ERROR(major)) + return major; + + name->krbPrincipal = *principal; + *principal = NULL; + + if (KRB_PRINC_LENGTH(name->krbPrincipal) > 1) { + name->flags |= NAME_FLAG_SERVICE; + } else { + name->flags |= NAME_FLAG_NAI; + } + + *pName = name; + *minor = 0; + + return GSS_S_COMPLETE; +} + +static OM_uint32 +importServiceName(OM_uint32 *minor, + const gss_buffer_t nameBuffer, + gss_name_t *pName) +{ + OM_uint32 major; + krb5_context krbContext; + krb5_principal krbPrinc; + char *service, *host; + + GSSEAP_KRB_INIT(&krbContext); + + major = bufferToString(minor, nameBuffer, &service); + if (GSS_ERROR(major)) + return major; + + host = strchr(service, '@'); + if (host != NULL) { + *host = '\0'; + host++; + } + + /* XXX this is probably NOT what we want to be doing */ + if (krb5_sname_to_principal(krbContext, host, service, + KRB5_NT_SRV_HST, &krbPrinc) != 0) { + GSSEAP_FREE(service); + *minor = GSSEAP_BAD_SERVICE_NAME; + return GSS_S_FAILURE; + } + + major = krbPrincipalToName(minor, &krbPrinc, pName); + if (GSS_ERROR(major)) { + krb5_free_principal(krbContext, krbPrinc); + } + + GSSEAP_FREE(service); + return major; +} + +static OM_uint32 +importUserName(OM_uint32 *minor, + const gss_buffer_t nameBuffer, + gss_name_t *pName) +{ + OM_uint32 major; + krb5_context krbContext; + krb5_principal krbPrinc; + char *nameString; + + GSSEAP_KRB_INIT(&krbContext); + + if (nameBuffer == GSS_C_NO_BUFFER) { + *minor = krb5_copy_principal(krbContext, + krbAnonymousPrincipal(), &krbPrinc); + if (*minor != 0) + return GSS_S_FAILURE; + } else { + major = bufferToString(minor, nameBuffer, &nameString); + if (GSS_ERROR(major)) + return major; + + *minor = krb5_parse_name(krbContext, nameString, &krbPrinc); + if (*minor != 0) { + GSSEAP_FREE(nameString); + return GSS_S_FAILURE; + } + } + + major = krbPrincipalToName(minor, &krbPrinc, pName); + if (GSS_ERROR(major)) { + krb5_free_principal(krbContext, krbPrinc); + } + + GSSEAP_FREE(nameString); + return major; +} + +static OM_uint32 +importAnonymousName(OM_uint32 *minor, + const gss_buffer_t nameBuffer, + gss_name_t *pName) +{ + OM_uint32 major; + krb5_context krbContext; + krb5_principal krbPrinc; + + GSSEAP_KRB_INIT(&krbContext); + + *minor = krb5_copy_principal(krbContext, krbAnonymousPrincipal(), + &krbPrinc); + if (*minor != 0) + return GSS_S_FAILURE; + + major = krbPrincipalToName(minor, &krbPrinc, pName); + if (GSS_ERROR(major)) { + krb5_free_principal(krbContext, krbPrinc); + } + + return major; +} + +#define UPDATE_REMAIN(n) do { \ + p += (n); \ + remain -= (n); \ + } while (0) + +#define CHECK_REMAIN(n) do { \ + if (remain < (n)) { \ + major = GSS_S_BAD_NAME; \ + *minor = GSSEAP_TOK_TRUNC; \ + goto cleanup; \ + } \ + } while (0) + +OM_uint32 +gssEapImportNameInternal(OM_uint32 *minor, + const gss_buffer_t nameBuffer, + gss_name_t *pName, + unsigned int flags) +{ + OM_uint32 major, tmpMinor; + krb5_context krbContext; + unsigned char *p; + size_t len, remain; + gss_buffer_desc buf; + enum gss_eap_token_type tokType; + gss_name_t name = GSS_C_NO_NAME; + + GSSEAP_KRB_INIT(&krbContext); + + p = (unsigned char *)nameBuffer->value; + remain = nameBuffer->length; + + if (flags & EXPORT_NAME_FLAG_OID) { + if (remain < 6 + GSS_EAP_MECHANISM->length + 4) + return GSS_S_BAD_NAME; + + if (flags & EXPORT_NAME_FLAG_COMPOSITE) + tokType = TOK_TYPE_EXPORT_NAME_COMPOSITE; + else + tokType = TOK_TYPE_EXPORT_NAME; + + /* TOK_ID */ + if (load_uint16_be(p) != tokType) + return GSS_S_BAD_NAME; + UPDATE_REMAIN(2); + + /* MECH_OID_LEN */ + len = load_uint16_be(p); + if (len != 2 + GSS_EAP_MECHANISM->length) + return GSS_S_BAD_NAME; + UPDATE_REMAIN(2); + + /* MECH_OID */ + if (p[0] != 0x06) + return GSS_S_BAD_NAME; + if (p[1] != GSS_EAP_MECHANISM->length) + return GSS_S_BAD_MECH; + if (memcmp(&p[2], GSS_EAP_MECHANISM->elements, GSS_EAP_MECHANISM->length)) + return GSS_S_BAD_MECH; + UPDATE_REMAIN(2 + GSS_EAP_MECHANISM->length); + } + + /* NAME_LEN */ + len = load_uint32_be(p); + UPDATE_REMAIN(4); + + /* NAME */ + CHECK_REMAIN(len); + buf.length = len; + buf.value = p; + UPDATE_REMAIN(len); + + major = importUserName(minor, &buf, &name); + if (GSS_ERROR(major)) + goto cleanup; + + if (flags & EXPORT_NAME_FLAG_COMPOSITE) { + gss_buffer_desc buf; + + buf.length = remain; + buf.value = p; + + major = gssEapImportAttrContext(minor, &buf, name); + if (GSS_ERROR(major)) + goto cleanup; + } + + major = GSS_S_COMPLETE; + *minor = 0; + +cleanup: + if (GSS_ERROR(major)) + gssEapReleaseName(&tmpMinor, &name); + else + *pName = name; + + return major; +} + +static OM_uint32 +importExportName(OM_uint32 *minor, + const gss_buffer_t nameBuffer, + gss_name_t *name) +{ + return gssEapImportNameInternal(minor, nameBuffer, name, + EXPORT_NAME_FLAG_OID); +} + +#ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT +static OM_uint32 +importCompositeExportName(OM_uint32 *minor, + const gss_buffer_t nameBuffer, + gss_name_t *name) +{ + return gssEapImportNameInternal(minor, nameBuffer, name, + EXPORT_NAME_FLAG_OID | + EXPORT_NAME_FLAG_COMPOSITE); +} +#endif + +struct gss_eap_name_import_provider { + gss_OID oid; + OM_uint32 (*import)(OM_uint32 *, const gss_buffer_t, gss_name_t *); +}; + +OM_uint32 +gssEapImportName(OM_uint32 *minor, + const gss_buffer_t nameBuffer, + gss_OID nameType, + gss_name_t *name) +{ + struct gss_eap_name_import_provider nameTypes[] = { + { GSS_C_NT_USER_NAME, importUserName }, + { GSS_EAP_NT_PRINCIPAL_NAME, importUserName }, + { GSS_C_NT_HOSTBASED_SERVICE, importServiceName }, + { GSS_C_NT_HOSTBASED_SERVICE_X, importServiceName }, + { GSS_C_NT_ANONYMOUS, importAnonymousName }, + { GSS_C_NT_EXPORT_NAME, importExportName }, +#ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT + { GSS_C_NT_COMPOSITE_EXPORT, importCompositeExportName }, +#endif + }; + size_t i; + + *name = GSS_C_NO_NAME; + + if (nameType == GSS_C_NO_OID) + nameType = nameTypes[0].oid; + + for (i = 0; i < sizeof(nameTypes) / sizeof(nameTypes[0]); i++) { + if (oidEqual(nameTypes[i].oid, nameType)) + return nameTypes[i].import(minor, nameBuffer, name); + } + + return GSS_S_BAD_NAMETYPE; +} + +OM_uint32 +gssEapExportName(OM_uint32 *minor, + const gss_name_t name, + gss_buffer_t exportedName) +{ + return gssEapExportNameInternal(minor, name, exportedName, + EXPORT_NAME_FLAG_OID); +} + +OM_uint32 +gssEapExportNameInternal(OM_uint32 *minor, + const gss_name_t name, + gss_buffer_t exportedName, + unsigned int flags) +{ + OM_uint32 major = GSS_S_FAILURE, tmpMinor; + krb5_context krbContext; + char *krbName = NULL; + size_t krbNameLen, exportedNameLen; + unsigned char *p; + gss_buffer_desc attrs = GSS_C_EMPTY_BUFFER; + + exportedName->length = 0; + exportedName->value = NULL; + + GSSEAP_KRB_INIT(&krbContext); + + *minor = krb5_unparse_name(krbContext, name->krbPrincipal, &krbName); + if (*minor != 0) { + major = GSS_S_FAILURE; + goto cleanup; + } + krbNameLen = strlen(krbName); + + exportedNameLen = 0; + if (flags & EXPORT_NAME_FLAG_OID) { + exportedNameLen += 6 + GSS_EAP_MECHANISM->length; + } + exportedNameLen += 4 + krbNameLen; + if (flags & EXPORT_NAME_FLAG_COMPOSITE) { + major = gssEapExportAttrContext(minor, name, &attrs); + if (GSS_ERROR(major)) + goto cleanup; + exportedNameLen += attrs.length; + } + + exportedName->value = GSSEAP_MALLOC(exportedNameLen); + if (exportedName->value == NULL) { + major = GSS_S_FAILURE; + *minor = ENOMEM; + goto cleanup; + } + exportedName->length = exportedNameLen; + + p = (unsigned char *)exportedName->value; + + if (flags & EXPORT_NAME_FLAG_OID) { + /* TOK | MECH_OID_LEN */ + store_uint16_be((flags & EXPORT_NAME_FLAG_COMPOSITE) + ? TOK_TYPE_EXPORT_NAME_COMPOSITE + : TOK_TYPE_EXPORT_NAME, + p); + p += 2; + store_uint16_be(GSS_EAP_MECHANISM->length + 2, p); + p += 2; + + /* MECH_OID */ + *p++ = 0x06; + *p++ = GSS_EAP_MECHANISM->length & 0xff; + memcpy(p, GSS_EAP_MECHANISM->elements, GSS_EAP_MECHANISM->length); + p += GSS_EAP_MECHANISM->length; + } + + /* NAME_LEN */ + store_uint32_be(krbNameLen, p); + p += 4; + + /* NAME */ + memcpy(p, krbName, krbNameLen); + p += krbNameLen; + + if (flags & EXPORT_NAME_FLAG_COMPOSITE) { + memcpy(p, attrs.value, attrs.length); + p += attrs.length; + } + + assert(p == (unsigned char *)exportedName->value + exportedNameLen); + + major = GSS_S_COMPLETE; + *minor = 0; + +cleanup: + gss_release_buffer(&tmpMinor, &attrs); + if (GSS_ERROR(major)) + gss_release_buffer(&tmpMinor, exportedName); + krb5_free_unparsed_name(krbContext, krbName); + + return major; +} + +OM_uint32 +gssEapDuplicateName(OM_uint32 *minor, + const gss_name_t input_name, + gss_name_t *dest_name) +{ + OM_uint32 major, tmpMinor; + krb5_context krbContext; + gss_name_t name; + + if (input_name == GSS_C_NO_NAME) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + } + + GSSEAP_KRB_INIT(&krbContext); + + major = gssEapAllocName(minor, &name); + if (GSS_ERROR(major)) { + return major; + } + + name->flags = input_name->flags; + + *minor = krb5_copy_principal(krbContext, input_name->krbPrincipal, + &name->krbPrincipal); + if (*minor != 0) { + major = GSS_S_FAILURE; + goto cleanup; + } + + if (input_name->attrCtx != NULL) { + major = gssEapDuplicateAttrContext(minor, input_name, name); + if (GSS_ERROR(major)) + goto cleanup; + } + + *dest_name = name; + +cleanup: + if (GSS_ERROR(major)) { + gssEapReleaseName(&tmpMinor, &name); + } + + return major; +} + +OM_uint32 +gssEapDisplayName(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t output_name_buffer, + gss_OID *output_name_type) +{ + OM_uint32 major; + krb5_context krbContext; + char *krbName; + gss_OID name_type; + + GSSEAP_KRB_INIT(&krbContext); + + output_name_buffer->length = 0; + output_name_buffer->value = NULL; + + if (name == GSS_C_NO_NAME) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME; + } + + *minor = krb5_unparse_name(krbContext, name->krbPrincipal, &krbName); + if (*minor != 0) { + return GSS_S_FAILURE; + } + + major = makeStringBuffer(minor, krbName, output_name_buffer); + if (GSS_ERROR(major)) { + krb5_free_unparsed_name(krbContext, krbName); + return major; + } + + krb5_free_unparsed_name(krbContext, krbName); + + if (KRB_PRINC_TYPE(name->krbPrincipal) == KRB5_NT_WELLKNOWN && + krb5_principal_compare(krbContext, + name->krbPrincipal, krbAnonymousPrincipal())) { + name_type = GSS_C_NT_ANONYMOUS; + } else { + name_type = GSS_EAP_NT_PRINCIPAL_NAME; + } + + if (output_name_type != NULL) + *output_name_type = name_type; + + return GSS_S_COMPLETE; +} diff --git a/util_oid.c b/util_oid.c new file mode 100644 index 0000000..40f5f6e --- /dev/null +++ b/util_oid.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ +/* + * Copyright 1995-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. + * + */ + +/* + * OID utility routines. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +duplicateOid(OM_uint32 *minor, + const gss_OID_desc * const oid, + gss_OID *newOid) +{ + gss_OID p; + + *newOid = GSS_C_NO_OID; + + p = (gss_OID)GSSEAP_MALLOC(sizeof(*p)); + if (p == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + p->length = oid->length; + p->elements = GSSEAP_MALLOC(p->length); + if (p->elements == NULL) { + GSSEAP_FREE(p); + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + memcpy(p->elements, oid->elements, p->length); + *newOid = p; + + *minor = 0; + return GSS_S_COMPLETE; +} + +/* Compose an OID of a prefix and an integer suffix */ +OM_uint32 +composeOid(OM_uint32 *minor, + const char *prefix, + size_t prefix_len, + int suffix, + gss_OID_desc *oid) +{ + int osuffix, i; + size_t nbytes; + unsigned char *op; + + if (oid == GSS_C_NO_OID) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_FAILURE; + } + + if (oid->length < prefix_len) { + *minor = GSSEAP_WRONG_SIZE; + return GSS_S_FAILURE; + } + + memcpy(oid->elements, prefix, prefix_len); + + nbytes = 0; + osuffix = suffix; + while (suffix) { + nbytes++; + suffix >>= 7; + } + suffix = osuffix; + + if (oid->length < prefix_len + nbytes) { + *minor = GSSEAP_WRONG_SIZE; + return GSS_S_FAILURE; + } + + op = (unsigned char *) oid->elements + prefix_len + nbytes; + i = -1; + while (suffix) { + op[i] = (unsigned char)suffix & 0x7f; + if (i != -1) + op[i] |= 0x80; + i--; + suffix >>= 7; + } + + oid->length = prefix_len + nbytes; + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +decomposeOid(OM_uint32 *minor, + const char *prefix, + size_t prefix_len, + gss_OID_desc *oid, + int *suffix) +{ + size_t i, slen; + unsigned char *op; + + if (oid->length < prefix_len || + memcmp(oid->elements, prefix, prefix_len) != 0) { + return GSS_S_BAD_MECH; + } + + op = (unsigned char *) oid->elements + prefix_len; + + *suffix = 0; + + slen = oid->length - prefix_len; + + for (i = 0; i < slen; i++) { + *suffix = (*suffix << 7) | (op[i] & 0x7f); + if (i + 1 != slen && (op[i] & 0x80) == 0) { + *minor = GSSEAP_WRONG_SIZE; + return GSS_S_FAILURE; + } + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +duplicateOidSet(OM_uint32 *minor, + const gss_OID_set src, + gss_OID_set *dst) +{ + OM_uint32 major, tmpMinor; + int i; + + if (src == GSS_C_NO_OID_SET) { + *dst = GSS_C_NO_OID_SET; + return GSS_S_COMPLETE; + } + + major = gss_create_empty_oid_set(minor, dst); + if (GSS_ERROR(major)) + return major; + + for (i = 0; i < src->count; i++) { + gss_OID oid = &src->elements[i]; + + major = gss_add_oid_set_member(minor, oid, dst); + if (GSS_ERROR(major)) + break; + } + + if (GSS_ERROR(major)) + gss_release_oid_set(&tmpMinor, dst); + + return major; +} diff --git a/util_ordering.c b/util_ordering.c new file mode 100644 index 0000000..9d74697 --- /dev/null +++ b/util_ordering.c @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ +/* + * 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. + */ + +/* + * Functions to check sequence numbers for replay and sequencing + */ + +#include "gssapiP_eap.h" + +#define QUEUE_LENGTH 20 + +typedef struct _queue { + int do_replay; + int do_sequence; + int start; + int length; + uint64_t firstnum; + /* Stored as deltas from firstnum. This way, the high bit won't + overflow unless we've actually gone through 2**n messages, or + gotten something *way* out of sequence. */ + uint64_t elem[QUEUE_LENGTH]; + /* All ones for 64-bit sequence numbers; 32 ones for 32-bit + sequence numbers. */ + uint64_t mask; +} queue; + +/* rep invariant: + * - the queue is a circular queue. The first element (q->elem[q->start]) + * is the oldest. The last element is the newest. + */ + +#define QSIZE(q) (sizeof((q)->elem)/sizeof((q)->elem[0])) +#define QELEM(q,i) ((q)->elem[(i)%QSIZE(q)]) + +static void +queue_insert(queue *q, int after, uint64_t seqnum) +{ + /* insert. this is not the fastest way, but it's easy, and it's + optimized for insert at end, which is the common case */ + int i; + + /* common case: at end, after == q->start+q->length-1 */ + + /* move all the elements (after,last] up one slot */ + + for (i = q->start + q->length - 1; i > after; i--) + QELEM(q,i+1) = QELEM(q,i); + + /* fill in slot after+1 */ + + QELEM(q,after+1) = seqnum; + + /* Either increase the length by one, or move the starting point up + one (deleting the first element, which got bashed above), as + appropriate. */ + + if (q->length == QSIZE(q)) { + q->start++; + if (q->start == QSIZE(q)) + q->start = 0; + } else { + q->length++; + } +} + +OM_uint32 +sequenceInit(OM_uint32 *minor, + void **vqueue, + uint64_t seqnum, + int do_replay, + int do_sequence, + int wide_nums) +{ + queue *q; + + q = (queue *)GSSEAP_CALLOC(1, sizeof(queue)); + if (q == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + q->do_replay = do_replay; + q->do_sequence = do_sequence; + q->mask = wide_nums ? ~(uint64_t)0 : 0xffffffffUL; + + q->start = 0; + q->length = 1; + q->firstnum = seqnum; + q->elem[q->start] = ((uint64_t)0 - 1) & q->mask; + + *vqueue = (void *)q; + + return GSS_S_COMPLETE; +} + +OM_uint32 +sequenceCheck(OM_uint32 *minor, + void **vqueue, + uint64_t seqnum) +{ + queue *q; + int i; + uint64_t expected; + + q = (queue *) (*vqueue); + + if (!q->do_replay && !q->do_sequence) + return GSS_S_COMPLETE; + + /* All checks are done relative to the initial sequence number, to + avoid (or at least put off) the pain of wrapping. */ + seqnum -= q->firstnum; + /* If we're only doing 32-bit values, adjust for that again. + + Note that this will probably be the wrong thing to if we get + 2**32 messages sent with 32-bit sequence numbers. */ + seqnum &= q->mask; + + /* rule 1: expected sequence number */ + + expected = (QELEM(q,q->start+q->length-1)+1) & q->mask; + if (seqnum == expected) { + queue_insert(q, q->start+q->length-1, seqnum); + return GSS_S_COMPLETE; + } + + /* rule 2: > expected sequence number */ + + if ((seqnum > expected)) { + queue_insert(q, q->start+q->length-1, seqnum); + if (q->do_replay && !q->do_sequence) + return GSS_S_COMPLETE; + else + return GSS_S_GAP_TOKEN; + } + + /* rule 3: seqnum < seqnum(first) */ + + if ((seqnum < QELEM(q,q->start)) && + /* Is top bit of whatever width we're using set? + + We used to check for greater than or equal to firstnum, but + (1) we've since switched to compute values relative to + firstnum, so the lowest we can have is 0, and (2) the effect + of the original scheme was highly dependent on whether + firstnum was close to either side of 0. (Consider + firstnum==0xFFFFFFFE and we miss three packets; the next + packet is *new* but would look old.) + + This check should give us 2**31 or 2**63 messages "new", and + just as many "old". That's not quite right either. */ + (seqnum & (1 + (q->mask >> 1))) + ) { + if (q->do_replay && !q->do_sequence) + return GSS_S_OLD_TOKEN; + else + return GSS_S_UNSEQ_TOKEN; + } + + /* rule 4+5: seqnum in [seqnum(first),seqnum(last)] */ + + else { + if (seqnum == QELEM(q,q->start+q->length - 1)) + return GSS_S_DUPLICATE_TOKEN; + + for (i = q->start; i < q->start + q->length - 1; i++) { + if (seqnum == QELEM(q,i)) + return GSS_S_DUPLICATE_TOKEN; + if ((seqnum > QELEM(q,i)) && (seqnum < QELEM(q,i+1))) { + queue_insert(q, i, seqnum); + if (q->do_replay && !q->do_sequence) + return GSS_S_COMPLETE; + else + return GSS_S_UNSEQ_TOKEN; + } + } + } + + /* this should never happen */ + return GSS_S_FAILURE; +} + +OM_uint32 +sequenceFree(OM_uint32 *minor, void **vqueue) +{ + queue *q; + + q = (queue *) (*vqueue); + + GSSEAP_FREE(q); + + *vqueue = NULL; + + return GSS_S_COMPLETE; +} + +/* + * These support functions are for the serialization routines + */ +size_t +sequenceSize(void *vqueue) +{ + return sizeof(queue); +} + +OM_uint32 +sequenceExternalize(OM_uint32 *minor, + void *vqueue, + unsigned char **buf, + size_t *lenremain) +{ + if (*lenremain < sizeof(queue)) { + *minor = GSSEAP_WRONG_SIZE; + return GSS_S_FAILURE; + } + memcpy(*buf, vqueue, sizeof(queue)); + *buf += sizeof(queue); + *lenremain -= sizeof(queue); + + return 0; +} + +OM_uint32 +sequenceInternalize(OM_uint32 *minor, + void **vqueue, + unsigned char **buf, + size_t *lenremain) +{ + void *q; + + if (*lenremain < sizeof(queue)) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_DEFECTIVE_TOKEN; + } + + q = GSSEAP_MALLOC(sizeof(queue)); + if (q == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + memcpy(q, *buf, sizeof(queue)); + *buf += sizeof(queue); + *lenremain -= sizeof(queue); + *vqueue = q; + + *minor = 0; + return GSS_S_COMPLETE; +} diff --git a/util_radius.cpp b/util_radius.cpp new file mode 100644 index 0000000..4edb056 --- /dev/null +++ b/util_radius.cpp @@ -0,0 +1,799 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * RADIUS attribute provider implementation. + */ + +#include "gssapiP_eap.h" + +/* stuff that should be provided by libradsec/libfreeradius-radius */ +#define VENDORATTR(vendor, attr) (((vendor) << 16) | (attr)) + +#ifndef ATTRID +#define ATTRID(attr) ((attr) & 0xFFFF) +#endif + +static gss_buffer_desc radiusUrnPrefix = { + sizeof("urn:x-radius:") - 1, + (void *)"urn:x-radius:" +}; + +static VALUE_PAIR *copyAvps(const VALUE_PAIR *src); + +gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void) +{ + m_vps = NULL; + m_authenticated = false; +} + +gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void) +{ + if (m_vps != NULL) + pairfree(&m_vps); +} + +bool +gss_eap_radius_attr_provider::initFromExistingContext(const gss_eap_attr_ctx *manager, + const gss_eap_attr_provider *ctx) +{ + const gss_eap_radius_attr_provider *radius; + + if (!gss_eap_attr_provider::initFromExistingContext(manager, ctx)) + return false; + + radius = static_cast(ctx); + + if (radius->m_vps != NULL) + m_vps = copyAvps(const_cast(radius->getAvps())); + + m_authenticated = radius->m_authenticated; + + return true; +} + +bool +gss_eap_radius_attr_provider::initFromGssContext(const gss_eap_attr_ctx *manager, + const gss_cred_id_t gssCred, + const gss_ctx_id_t gssCtx) +{ + if (!gss_eap_attr_provider::initFromGssContext(manager, gssCred, gssCtx)) + return false; + + if (gssCtx != GSS_C_NO_CONTEXT) { + if (gssCtx->acceptorCtx.vps != NULL) { + m_vps = copyAvps(gssCtx->acceptorCtx.vps); + if (m_vps == NULL) + return false; + + /* We assume libradsec validated this for us */ + assert(pairfind(m_vps, PW_MESSAGE_AUTHENTICATOR) != NULL); + m_authenticated = true; + } + } + + return true; +} + +static bool +alreadyAddedAttributeP(std::vector &attrs, VALUE_PAIR *vp) +{ + for (std::vector::const_iterator a = attrs.begin(); + a != attrs.end(); + ++a) { + if (strcmp(vp->name, (*a).c_str()) == 0) + return true; + } + + return false; +} + +static bool +isSecretAttributeP(uint16_t attrid, uint16_t vendor) +{ + bool bSecretAttribute = false; + + switch (vendor) { + case VENDORPEC_MS: + switch (attrid) { + case PW_MS_MPPE_SEND_KEY: + case PW_MS_MPPE_RECV_KEY: + bSecretAttribute = true; + break; + default: + break; + } + default: + break; + } + + return bSecretAttribute; +} + +static bool +isSecretAttributeP(uint32_t attribute) +{ + return isSecretAttributeP(ATTRID(attribute), VENDOR(attribute)); +} + +static bool +isInternalAttributeP(uint16_t attrid, uint16_t vendor) +{ + bool bInternalAttribute = false; + + /* should have been filtered */ + assert(!isSecretAttributeP(attrid, vendor)); + + switch (vendor) { + case VENDORPEC_UKERNA: + bInternalAttribute = true; + break; + default: + break; + } + + return bInternalAttribute; +} + +static bool +isInternalAttributeP(uint32_t attribute) +{ + return isInternalAttributeP(ATTRID(attribute), VENDOR(attribute)); +} + +/* + * Copy AVP list, same as paircopy except it filters out attributes + * containing keys. + */ +static VALUE_PAIR * +copyAvps(const VALUE_PAIR *src) +{ + const VALUE_PAIR *vp; + VALUE_PAIR *dst = NULL, **pDst = &dst; + + for (vp = src; vp != NULL; vp = vp->next) { + VALUE_PAIR *vpcopy; + + if (isSecretAttributeP(vp->attribute)) + continue; + + vpcopy = paircopyvp(vp); + if (vpcopy == NULL) { + pairfree(&dst); + throw new std::bad_alloc; + return NULL; + } + *pDst = vpcopy; + pDst = &vpcopy->next; + } + + return dst; +} + +bool +gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute, + void *data) const +{ + VALUE_PAIR *vp; + std::vector seen; + + for (vp = m_vps; vp != NULL; vp = vp->next) { + gss_buffer_desc attribute; + char attrid[64]; + + /* Don't advertise attributes that are internal to the GSS-EAP mechanism */ + if (isInternalAttributeP(vp->attribute)) + continue; + + if (alreadyAddedAttributeP(seen, vp)) + continue; + + snprintf(attrid, sizeof(attrid), "%s%d", + (char *)radiusUrnPrefix.value, vp->attribute); + + attribute.value = attrid; + attribute.length = strlen(attrid); + + if (!addAttribute(this, &attribute, data)) + return false; + + seen.push_back(std::string(vp->name)); + } + + return true; +} + +uint32_t +getAttributeId(const gss_buffer_t attr) +{ + OM_uint32 tmpMinor; + gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER; + DICT_ATTR *da; + char *s; + uint32_t attrid = 0; + + if (attr->length < radiusUrnPrefix.length || + memcmp(attr->value, radiusUrnPrefix.value, radiusUrnPrefix.length) != 0) + return 0; + + /* need to duplicate because attr may not be NUL terminated */ + duplicateBuffer(*attr, &strAttr); + s = (char *)strAttr.value + radiusUrnPrefix.length; + + if (isdigit(*s)) { + attrid = strtoul(s, NULL, 10); + } else { + da = dict_attrbyname(s); + if (da != NULL) + attrid = da->attr; + } + + gss_release_buffer(&tmpMinor, &strAttr); + + return attrid; +} + +bool +gss_eap_radius_attr_provider::setAttribute(int complete, + uint32_t attrid, + const gss_buffer_t value) +{ + OM_uint32 major = GSS_S_UNAVAILABLE, minor; + + if (!isSecretAttributeP(attrid) && + !isInternalAttributeP(attrid)) { + deleteAttribute(attrid); + + major = gssEapRadiusAddAvp(&minor, &m_vps, + ATTRID(attrid), VENDOR(attrid), + value); + } + + return !GSS_ERROR(major); +} + +bool +gss_eap_radius_attr_provider::setAttribute(int complete, + const gss_buffer_t attr, + const gss_buffer_t value) +{ + uint32_t attrid = getAttributeId(attr); + + if (!attrid) + return false; + + return setAttribute(complete, attrid, value); +} + +bool +gss_eap_radius_attr_provider::deleteAttribute(uint32_t attrid) +{ + if (isSecretAttributeP(attrid) || isInternalAttributeP(attrid) || + pairfind(m_vps, attrid) == NULL) + return false; + + pairdelete(&m_vps, attrid); + + return true; +} + +bool +gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t attr) +{ + uint32_t attrid = getAttributeId(attr); + + if (!attrid) + return false; + + return deleteAttribute(attrid); +} + +bool +gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const +{ + uint32_t attrid; + + attrid = getAttributeId(attr); + if (!attrid) + return false; + + return getAttribute(attrid, authenticated, complete, + value, display_value, more); +} + +bool +gss_eap_radius_attr_provider::getAttribute(uint32_t attrid, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const +{ + VALUE_PAIR *vp; + int i = *more, count = 0; + + *more = 0; + + if (i == -1) + i = 0; + + for (vp = pairfind(m_vps, attrid); + vp != NULL; + vp = pairfind(vp->next, attrid)) { + if (count++ == i) { + if (pairfind(vp->next, attrid) != NULL) + *more = count; + break; + } + } + + if (vp == NULL && *more == 0) + return false; + + if (value != GSS_C_NO_BUFFER) { + gss_buffer_desc valueBuf; + + valueBuf.value = (void *)vp->vp_octets; + valueBuf.length = vp->length; + + duplicateBuffer(valueBuf, value); + } + + if (display_value != GSS_C_NO_BUFFER) { + char displayString[MAX_STRING_LEN]; + gss_buffer_desc displayBuf; + + displayBuf.length = vp_prints_value(displayString, + sizeof(displayString), vp, 0); + displayBuf.value = (void *)displayString; + + duplicateBuffer(displayBuf, display_value); + } + + if (authenticated != NULL) + *authenticated = m_authenticated; + if (complete != NULL) + *complete = true; + + return true; +} + +bool +gss_eap_radius_attr_provider::getFragmentedAttribute(uint16_t attribute, + uint16_t vendor, + int *authenticated, + int *complete, + gss_buffer_t value) const +{ + OM_uint32 major, minor; + + major = gssEapRadiusGetAvp(&minor, m_vps, attribute, vendor, value, TRUE); + + if (authenticated != NULL) + *authenticated = m_authenticated; + if (complete != NULL) + *complete = true; + + return !GSS_ERROR(major); +} + +bool +gss_eap_radius_attr_provider::getAttribute(uint16_t attribute, + uint16_t vendor, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const +{ + + return getAttribute(VENDORATTR(attribute, vendor), + authenticated, complete, + value, display_value, more); +} + +gss_any_t +gss_eap_radius_attr_provider::mapToAny(int authenticated, + gss_buffer_t type_id) const +{ + if (authenticated && !m_authenticated) + return (gss_any_t)NULL; + + return (gss_any_t)copyAvps(m_vps); +} + +void +gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id, + gss_any_t input) const +{ + pairfree((VALUE_PAIR **)&input); +} + +bool +gss_eap_radius_attr_provider::init(void) +{ + gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS, + "urn:ietf:params:gss-eap:radius-avp", + createAttrContext); + return true; +} + +void +gss_eap_radius_attr_provider::finalize(void) +{ + gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_RADIUS); +} + +gss_eap_attr_provider * +gss_eap_radius_attr_provider::createAttrContext(void) +{ + return new gss_eap_radius_attr_provider; +} + +OM_uint32 +gssEapRadiusAddAvp(OM_uint32 *minor, + VALUE_PAIR **vps, + uint16_t attribute, + uint16_t vendor, + const gss_buffer_t buffer) +{ + uint32_t attrid = VENDORATTR(vendor, attribute); + unsigned char *p = (unsigned char *)buffer->value; + size_t remain = buffer->length; + + do { + VALUE_PAIR *vp; + size_t n = remain; + + if (n > MAX_STRING_LEN) + n = MAX_STRING_LEN; + + vp = paircreate(attrid, PW_TYPE_OCTETS); + if (vp == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + memcpy(vp->vp_octets, p, n); + vp->length = n; + + pairadd(vps, vp); + + p += n; + remain -= n; + } while (remain != 0); + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapRadiusGetRawAvp(OM_uint32 *minor, + VALUE_PAIR *vps, + uint16_t attribute, + uint16_t vendor, + VALUE_PAIR **vp) +{ + uint32_t attr = VENDORATTR(vendor, attribute); + + *vp = pairfind(vps, attr); + if (*vp == NULL) { + *minor = GSSEAP_NO_SUCH_ATTR; + return GSS_S_UNAVAILABLE; + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapRadiusGetAvp(OM_uint32 *minor, + VALUE_PAIR *vps, + uint16_t attribute, + uint16_t vendor, + gss_buffer_t buffer, + int concat) +{ + VALUE_PAIR *vp; + unsigned char *p; + uint32_t attr = VENDORATTR(vendor, attribute); + + buffer->length = 0; + buffer->value = NULL; + + vp = pairfind(vps, attr); + if (vp == NULL) { + *minor = GSSEAP_NO_SUCH_ATTR; + return GSS_S_UNAVAILABLE; + } + + do { + buffer->length += vp->length; + } while (concat && (vp = pairfind(vp->next, attr)) != NULL); + + buffer->value = GSSEAP_MALLOC(buffer->length); + if (buffer->value == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + p = (unsigned char *)buffer->value; + + for (vp = pairfind(vps, attr); + concat && vp != NULL; + vp = pairfind(vp->next, attr)) { + memcpy(p, vp->vp_octets, vp->length); + p += vp->length; + } + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapRadiusFreeAvps(OM_uint32 *minor, + VALUE_PAIR **vps) +{ + pairfree(vps); + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapRadiusAttrProviderInit(OM_uint32 *minor) +{ + if (!gss_eap_radius_attr_provider::init()) { + *minor = GSSEAP_RADSEC_INIT_FAILURE; + return GSS_S_FAILURE; + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapRadiusAttrProviderFinalize(OM_uint32 *minor) +{ + gss_eap_radius_attr_provider::finalize(); + return GSS_S_COMPLETE; +} + +/* + * Encoding is: + * 4 octet NBO attribute ID | 4 octet attribute length | attribute data + */ +static size_t +avpSize(const VALUE_PAIR *vp) +{ + size_t size = 4 + 1; + + if (vp != NULL) + size += vp->length; + + return size; +} + +static bool +avpExport(const VALUE_PAIR *vp, + unsigned char **pBuffer, + size_t *pRemain) +{ + unsigned char *p = *pBuffer; + size_t remain = *pRemain; + + assert(remain >= avpSize(vp)); + + store_uint32_be(vp->attribute, p); + + switch (vp->type) { + case PW_TYPE_INTEGER: + case PW_TYPE_IPADDR: + case PW_TYPE_DATE: + p[4] = 4; + store_uint32_be(vp->lvalue, p + 5); + break; + default: + assert(vp->length <= MAX_STRING_LEN); + p[4] = (uint8_t)vp->length; + memcpy(p + 5, vp->vp_octets, vp->length); + break; + } + + *pBuffer += 5 + p[4]; + *pRemain -= 5 + p[4]; + + return true; + +} + +static bool +avpImport(VALUE_PAIR **pVp, + unsigned char **pBuffer, + size_t *pRemain) +{ + unsigned char *p = *pBuffer; + size_t remain = *pRemain; + VALUE_PAIR *vp = NULL; + DICT_ATTR *da; + uint32_t attrid; + + if (remain < avpSize(NULL)) + goto fail; + + attrid = load_uint32_be(p); + p += 4; + remain -= 4; + + da = dict_attrbyvalue(attrid); + if (da == NULL) + goto fail; + + vp = pairalloc(da); + if (vp == NULL) { + throw new std::bad_alloc; + goto fail; + } + + if (remain < p[0]) + goto fail; + + switch (vp->type) { + case PW_TYPE_INTEGER: + case PW_TYPE_IPADDR: + case PW_TYPE_DATE: + if (p[0] != 4) + goto fail; + + vp->length = 4; + vp->lvalue = load_uint32_be(p + 1); + p += 5; + remain -= 5; + break; + case PW_TYPE_STRING: + /* check enough room to NUL terminate */ + if (p[0] == MAX_STRING_LEN) + goto fail; + else + /* fallthrough */ + default: + if (p[0] > MAX_STRING_LEN) + goto fail; + + vp->length = (uint32_t)p[0]; + memcpy(vp->vp_octets, p + 1, vp->length); + + if (vp->type == PW_TYPE_STRING) + vp->vp_strvalue[vp->length] = '\0'; + + p += 1 + vp->length; + remain -= 1 + vp->length; + break; + } + + *pVp = vp; + *pBuffer = p; + *pRemain = remain; + + return true; + +fail: + pairbasicfree(vp); + return false; +} + +bool +gss_eap_radius_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx, + const gss_buffer_t buffer) +{ + unsigned char *p = (unsigned char *)buffer->value; + size_t remain = buffer->length; + VALUE_PAIR **pNext = &m_vps; + + if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer)) + return false; + + do { + VALUE_PAIR *attr; + + if (!avpImport(&attr, &p, &remain)) + return false; + + *pNext = attr; + pNext = &attr->next; + } while (remain != 0); + + return true; +} + +void +gss_eap_radius_attr_provider::exportToBuffer(gss_buffer_t buffer) const +{ + VALUE_PAIR *vp; + unsigned char *p; + size_t remain = 0; + + for (vp = m_vps; vp != NULL; vp = vp->next) { + remain += avpSize(vp); + } + + buffer->value = GSSEAP_MALLOC(remain); + if (buffer->value == NULL) { + throw new std::bad_alloc; + return; + } + buffer->length = remain; + + p = (unsigned char *)buffer->value; + + for (vp = m_vps; vp != NULL; vp = vp->next) { + avpExport(vp, &p, &remain); + } + + assert(remain == 0); +} + +time_t +gss_eap_radius_attr_provider::getExpiryTime(void) const +{ + VALUE_PAIR *vp; + + vp = pairfind(m_vps, PW_SESSION_TIMEOUT); + if (vp == NULL || vp->lvalue == 0) + return 0; + + return time(NULL) + vp->lvalue; +} + +OM_uint32 +gssEapRadiusMapError(OM_uint32 *minor, + struct rs_error *err) +{ + int code; + + assert(err != NULL); + + code = rs_err_code(err, 0); + + if (code == RSE_OK) { + *minor = 0; + return GSS_S_COMPLETE; + } + + *minor = ERROR_TABLE_BASE_rse + code; + + gssEapSaveStatusInfo(*minor, "%s", rs_err_msg(err, 0)); + rs_err_free(err); + + return GSS_S_FAILURE; +} diff --git a/util_radius.h b/util_radius.h new file mode 100644 index 0000000..d6e4cf5 --- /dev/null +++ b/util_radius.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * RADIUS attribute provider. + */ + +#ifndef _UTIL_RADIUS_H_ +#define _UTIL_RADIUS_H_ 1 + +#ifdef __cplusplus + +struct gss_eap_radius_attr_provider : gss_eap_attr_provider { +public: + gss_eap_radius_attr_provider(void); + ~gss_eap_radius_attr_provider(void); + + bool initFromExistingContext(const gss_eap_attr_ctx *source, + const gss_eap_attr_provider *ctx); + bool initFromGssContext(const gss_eap_attr_ctx *source, + const gss_cred_id_t cred, + const gss_ctx_id_t ctx); + + bool getAttributeTypes(gss_eap_attr_enumeration_cb, void *data) const; + bool setAttribute(int complete, + const gss_buffer_t attr, + const gss_buffer_t value); + bool deleteAttribute(const gss_buffer_t attr); + bool getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const; + gss_any_t mapToAny(int authenticated, + gss_buffer_t type_id) const; + void releaseAnyNameMapping(gss_buffer_t type_id, + gss_any_t input) const; + + void exportToBuffer(gss_buffer_t buffer) const; + bool initFromBuffer(const gss_eap_attr_ctx *ctx, + const gss_buffer_t buffer); + + bool getAttribute(uint32_t attribute, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const; + bool getAttribute(uint16_t attribute, + uint16_t vendor, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const; + bool setAttribute(int complete, + uint32_t attribute, + const gss_buffer_t value); + bool deleteAttribute(uint32_t attribute); + + bool getFragmentedAttribute(uint16_t attribute, + uint16_t vendor, + int *authenticated, + int *complete, + gss_buffer_t value) const; + + bool authenticated(void) const { return m_authenticated; } + + time_t getExpiryTime(void) const; + + static bool init(void); + static void finalize(void); + + static gss_eap_attr_provider *createAttrContext(void); + +private: + const VALUE_PAIR *getAvps(void) const { + return m_vps; + } + + VALUE_PAIR *m_vps; + bool m_authenticated; +}; + +/* For now */ +extern "C" { +#endif + +OM_uint32 +gssEapRadiusAddAvp(OM_uint32 *minor, + VALUE_PAIR **vp, + uint16_t type, + uint16_t vendor, + const gss_buffer_t buffer); + +OM_uint32 +gssEapRadiusGetAvp(OM_uint32 *minor, + VALUE_PAIR *vps, + uint16_t type, + uint16_t vendor, + gss_buffer_t buffer, + int concat); + +OM_uint32 +gssEapRadiusGetRawAvp(OM_uint32 *minor, + VALUE_PAIR *vps, + uint16_t type, + uint16_t vendor, + VALUE_PAIR **vp); +OM_uint32 +gssEapRadiusFreeAvps(OM_uint32 *minor, + VALUE_PAIR **vps); + +OM_uint32 gssEapRadiusAttrProviderInit(OM_uint32 *minor); +OM_uint32 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor); + +OM_uint32 +gssEapRadiusMapError(OM_uint32 *minor, + struct rs_error *err); + +#define RS_CONFIG_FILE SYSCONFDIR "/radsec.conf" +#define RS_DICT_FILE DATAROOTDIR "/freeradius/dictionary" + +#define VENDORPEC_MS 311 /* RFC 2548 */ + +#define PW_MS_MPPE_SEND_KEY 16 +#define PW_MS_MPPE_RECV_KEY 17 + +#define VENDORPEC_UKERNA 25622 + +#define PW_GSS_ACCEPTOR_SERVICE_NAME 128 +#define PW_GSS_ACCEPTOR_HOST_NAME 129 +#define PW_GSS_ACCEPTOR_SERVICE_SPECIFIC 130 +#define PW_GSS_ACCEPTOR_REALM_NAME 131 +#define PW_SAML_AAA_ASSERTION 132 + +#define IS_RADIUS_ERROR(code) ((code) >= ERROR_TABLE_BASE_rse && \ + (code) <= ERROR_TABLE_BASE_rse + RSE_SOME_ERROR) + +#ifdef __cplusplus +} +#endif + +#endif /* _UTIL_RADIUS_H_ */ diff --git a/util_reauth.c b/util_reauth.c new file mode 100644 index 0000000..8b853cb --- /dev/null +++ b/util_reauth.c @@ -0,0 +1,1184 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Fast reauthentication support. + */ + +#include "gssapiP_eap.h" + +#include + +/* + * Fast reauthentication support for EAP GSS. + */ + +krb5_error_code +krb5_encrypt_tkt_part(krb5_context, const krb5_keyblock *, krb5_ticket *); + +krb5_error_code +encode_krb5_ticket(const krb5_ticket *rep, krb5_data **code); + +static OM_uint32 +gssDisplayName(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t buffer, + gss_OID *name_type); + +static OM_uint32 +gssImportName(OM_uint32 *minor, + gss_buffer_t buffer, + gss_OID name_type, + gss_name_t *name); + +static krb5_error_code +getAcceptorKey(krb5_context krbContext, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + krb5_principal *princ, + krb5_keyblock *key) +{ + krb5_error_code code; + krb5_keytab keytab = NULL; + krb5_keytab_entry ktent = { 0 }; + krb5_kt_cursor cursor; + + *princ = NULL; + memset(key, 0, sizeof(*key)); + memset(&cursor, 0, sizeof(cursor)); + + code = krb5_kt_default(krbContext, &keytab); + if (code != 0) + goto cleanup; + + if (cred != GSS_C_NO_CREDENTIAL && cred->name != GSS_C_NO_NAME) { + code = krb5_kt_get_entry(krbContext, keytab, + cred->name->krbPrincipal, 0, + ctx->encryptionType, &ktent); + if (code != 0) + goto cleanup; + } else { + /* + * It's not clear that looking encrypting the ticket in the + * requested EAP enctype provides any value. + */ + code = krb5_kt_start_seq_get(krbContext, keytab, &cursor); + if (code != 0) + goto cleanup; + + while ((code = krb5_kt_next_entry(krbContext, keytab, + &ktent, &cursor)) == 0) { + if (KRB_KEY_TYPE(KRB_KT_ENT_KEYBLOCK(&ktent)) == ctx->encryptionType) + break; + else + KRB_KT_ENT_FREE(krbContext, &ktent); + } + } + + if (code == 0) { + *princ = ktent.principal; + *key = *KRB_KT_ENT_KEYBLOCK(&ktent); + } + +cleanup: + if (cred == GSS_C_NO_CREDENTIAL || cred->name == GSS_C_NO_NAME) + krb5_kt_end_seq_get(krbContext, keytab, &cursor); + krb5_kt_close(krbContext, keytab); + if (code != 0) + KRB_KT_ENT_FREE(krbContext, &ktent); + + return code; +} + +static OM_uint32 +freezeAttrContext(OM_uint32 *minor, + gss_name_t initiatorName, + krb5_const_principal acceptorPrinc, + krb5_keyblock *session, +#ifdef HAVE_HEIMDAL_VERSION + krb5_authdata *kdcIssuedAuthData +#else + krb5_authdata ***kdcIssuedAuthData +#endif + ) +{ + OM_uint32 major, tmpMinor; + krb5_error_code code; + krb5_context krbContext; + gss_buffer_desc attrBuf = GSS_C_EMPTY_BUFFER; +#ifdef HAVE_HEIMDAL_VERSION + krb5_authdata authDataBuf, *authData = &authDataBuf; + AuthorizationDataElement authDatum = { 0 }; +#else + krb5_authdata *authData[2], authDatum = { 0 }; +#endif + + memset(kdcIssuedAuthData, 0, sizeof(*kdcIssuedAuthData)); + + GSSEAP_KRB_INIT(&krbContext); + + major = gssEapExportAttrContext(minor, initiatorName, &attrBuf); + if (GSS_ERROR(major)) + return major; + + authDatum.ad_type = KRB5_AUTHDATA_RADIUS_AVP; +#ifdef HAVE_HEIMDAL_VERSION + authDatum.ad_data.length = attrBuf.length; + authDatum.ad_data.data = attrBuf.value; + authData->len = 1; + authData->val = &authDatum; +#else + authDatum.length = attrBuf.length; + authDatum.contents = attrBuf.value; + authData[0] = &authDatum; + authData[1] = NULL; +#endif + + code = krbMakeAuthDataKdcIssued(krbContext, session, acceptorPrinc, + authData, kdcIssuedAuthData); + if (code != 0) { + major = GSS_S_FAILURE; + *minor = code; + } else { + major = GSS_S_COMPLETE; + } + + gss_release_buffer(&tmpMinor, &attrBuf); + + return major; +} + +/* + * Fabricate a ticket to ourselves given a GSS EAP context. + */ +OM_uint32 +gssEapMakeReauthCreds(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t credBuf) +{ + OM_uint32 major = GSS_S_COMPLETE; + krb5_error_code code; + krb5_context krbContext = NULL; + krb5_keyblock session = { 0 }, acceptorKey = { 0 }; + krb5_principal server = NULL; +#ifdef HAVE_HEIMDAL_VERSION + Ticket ticket; + EncTicketPart enc_part; + AuthorizationData authData = { 0 }; + krb5_crypto krbCrypto = NULL; + krb5_data ticketData = { 0 }; + krb5_data encPartData = { 0 }; + size_t len; +#else + krb5_ticket ticket; + krb5_enc_tkt_part enc_part; + krb5_data *ticketData = NULL; +#endif + krb5_data credsData = { 0 }; + krb5_creds creds = { 0 }; + krb5_auth_context authContext = NULL; + + memset(&ticket, 0, sizeof(ticket)); + memset(&enc_part, 0, sizeof(enc_part)); + + credBuf->length = 0; + credBuf->value = NULL; + + GSSEAP_KRB_INIT(&krbContext); + + code = getAcceptorKey(krbContext, ctx, cred, &server, &acceptorKey); + if (code == KRB5_KT_NOTFOUND) { + *minor = code; + return GSS_S_UNAVAILABLE; + } else if (code != 0) + goto cleanup; + +#ifdef HAVE_HEIMDAL_VERSION + ticket.realm = server->realm; + ticket.sname = server->name; +#else + ticket.server = server; +#endif + + /* + * Generate a random session key to place in the ticket and + * sign the "KDC-Issued" authorization data element. + */ +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_generate_random_keyblock(krbContext, ctx->encryptionType, + &session); + if (code != 0) + goto cleanup; + + enc_part.flags.initial = 1; + enc_part.key = session; + enc_part.crealm = ctx->initiatorName->krbPrincipal->realm; + enc_part.cname = ctx->initiatorName->krbPrincipal->name; + enc_part.authtime = time(NULL); + enc_part.starttime = &enc_part.authtime; + enc_part.endtime = (ctx->expiryTime != 0) + ? ctx->expiryTime : KRB_TIME_FOREVER; + enc_part.renew_till = NULL; + enc_part.authorization_data = &authData; + + major = freezeAttrContext(minor, ctx->initiatorName, server, + &session, &authData); + if (GSS_ERROR(major)) + goto cleanup; + + ASN1_MALLOC_ENCODE(EncTicketPart, encPartData.data, encPartData.length, + &enc_part, &len, code); + if (code != 0) + goto cleanup; + + code = krb5_crypto_init(krbContext, &acceptorKey, 0, &krbCrypto); + if (code != 0) + goto cleanup; + + code = krb5_encrypt_EncryptedData(krbContext, + krbCrypto, + KRB5_KU_TICKET, + encPartData.data, + encPartData.length, + 0, + &ticket.enc_part); + if (code != 0) + goto cleanup; + + ASN1_MALLOC_ENCODE(Ticket, ticketData.data, ticketData.length, + &ticket, &len, code); + if (code != 0) + goto cleanup; +#else + code = krb5_c_make_random_key(krbContext, ctx->encryptionType, + &session); + if (code != 0) + goto cleanup; + + enc_part.flags = TKT_FLG_INITIAL; + enc_part.session = &session; + enc_part.client = ctx->initiatorName->krbPrincipal; + enc_part.times.authtime = time(NULL); + enc_part.times.starttime = enc_part.times.authtime; + enc_part.times.endtime = (ctx->expiryTime != 0) + ? ctx->expiryTime + : KRB_TIME_FOREVER; + enc_part.times.renew_till = 0; + + major = freezeAttrContext(minor, ctx->initiatorName, server, + &session, &enc_part.authorization_data); + if (GSS_ERROR(major)) + goto cleanup; + + ticket.enc_part2 = &enc_part; + + code = krb5_encrypt_tkt_part(krbContext, &acceptorKey, &ticket); + if (code != 0) + goto cleanup; + + code = encode_krb5_ticket(&ticket, &ticketData); + if (code != 0) + goto cleanup; +#endif /* HAVE_HEIMDAL_VERSION */ + + creds.client = ctx->initiatorName->krbPrincipal; + creds.server = server; +#ifdef HAVE_HEIMDAL_VERSION + creds.session = session; + creds.times.authtime = enc_part.authtime; + creds.times.starttime = *enc_part.starttime; + creds.times.endtime = enc_part.endtime; + creds.times.renew_till = 0; + creds.flags.b = enc_part.flags; + creds.ticket = ticketData; + creds.authdata = authData; +#else + creds.keyblock = session; + creds.times = enc_part.times; + creds.ticket_flags = enc_part.flags; + creds.ticket = *ticketData; + creds.authdata = enc_part.authorization_data; +#endif + + code = krb5_auth_con_init(krbContext, &authContext); + if (code != 0) + goto cleanup; + + code = krb5_auth_con_setflags(krbContext, authContext, 0); + if (code != 0) + goto cleanup; + + code = krb5_auth_con_setsendsubkey(krbContext, authContext, + &ctx->rfc3961Key); + if (code != 0) + goto cleanup; + + code = krbMakeCred(krbContext, authContext, &creds, &credsData); + if (code != 0) + goto cleanup; + + krbDataToGssBuffer(&credsData, credBuf); + +cleanup: +#ifdef HAVE_HEIMDAL_VERSION + if (krbCrypto != NULL) + krb5_crypto_destroy(krbContext, krbCrypto); + free_AuthorizationData(&authData); + free_EncryptedData(&ticket.enc_part); + krb5_data_free(&ticketData); + krb5_data_free(&encPartData); +#else + krb5_free_authdata(krbContext, enc_part.authorization_data); + if (ticket.enc_part.ciphertext.data != NULL) + GSSEAP_FREE(ticket.enc_part.ciphertext.data); + krb5_free_data(krbContext, ticketData); +#endif + krb5_free_keyblock_contents(krbContext, &session); + krb5_free_principal(krbContext, server); + krb5_free_keyblock_contents(krbContext, &acceptorKey); + krb5_auth_con_free(krbContext, authContext); + + if (major == GSS_S_COMPLETE) { + *minor = code; + major = (code != 0) ? GSS_S_FAILURE : GSS_S_COMPLETE; + } + + return major; +} + +static int +isTicketGrantingServiceP(krb5_context krbContext, + krb5_const_principal principal) +{ + if (KRB_PRINC_LENGTH(principal) == 2 && +#ifdef HAVE_HEIMDAL_VERSION + strcmp(KRB_PRINC_NAME(principal)[0], "krbtgt") == 0 +#else + krb5_princ_component(krbContext, principal, 0)->length == 6 && + memcmp(krb5_princ_component(krbContext, + principal, 0)->data, "krbtgt", 6) == 0 +#endif + ) + return TRUE; + + return FALSE; +} + +/* + * Returns TRUE if the configuration variable reauth_use_ccache is + * set in krb5.conf for the eap_gss application and the client realm. + */ +static int +reauthUseCredsCache(krb5_context krbContext, + krb5_principal principal) +{ + int reauthUseCCache; + + /* if reauth_use_ccache, use default credentials cache if ticket is for us */ + krb5_appdefault_boolean(krbContext, "eap_gss", + KRB_PRINC_REALM(principal), + "reauth_use_ccache", 0, &reauthUseCCache); + + return reauthUseCCache; +} + +/* + * Look in default credentials cache for reauthentication credentials, + * if policy allows. + */ +static OM_uint32 +getDefaultReauthCredentials(OM_uint32 *minor, + gss_cred_id_t cred, + gss_name_t target, + time_t now, + OM_uint32 timeReq) +{ + OM_uint32 major = GSS_S_CRED_UNAVAIL; + krb5_context krbContext = NULL; + krb5_error_code code = 0; + krb5_ccache ccache = NULL; + krb5_creds match = { 0 }; + krb5_creds creds = { 0 }; + + GSSEAP_KRB_INIT(&krbContext); + + assert(cred != GSS_C_NO_CREDENTIAL); + assert(target != GSS_C_NO_NAME); + + if (cred->name == GSS_C_NO_NAME || + !reauthUseCredsCache(krbContext, cred->name->krbPrincipal)) + goto cleanup; + + match.client = cred->name->krbPrincipal; + match.server = target->krbPrincipal; + if (timeReq != 0 && timeReq != GSS_C_INDEFINITE) + match.times.endtime = now + timeReq; + + code = krb5_cc_default(krbContext, &ccache); + if (code != 0) + goto cleanup; + + code = krb5_cc_retrieve_cred(krbContext, ccache, 0, &match, &creds); + if (code != 0) + goto cleanup; + + cred->flags |= CRED_FLAG_DEFAULT_CCACHE; + cred->krbCredCache = ccache; + ccache = NULL; + + major = gss_krb5_import_cred(minor, cred->krbCredCache, NULL, NULL, + &cred->krbCred); + +cleanup: + if (major == GSS_S_CRED_UNAVAIL) + *minor = code; + + if (ccache != NULL) + krb5_cc_close(krbContext, ccache); + krb5_free_cred_contents(krbContext, &creds); + + return major; +} + +/* + * Returns TRUE if the credential handle's reauth credentials are + * valid or if we can use the default credentials cache. Credentials + * handle must be locked. + */ +int +gssEapCanReauthP(gss_cred_id_t cred, + gss_name_t target, + OM_uint32 timeReq) +{ + time_t now, expiryReq; + OM_uint32 minor; + + assert(cred != GSS_C_NO_CREDENTIAL); + + now = time(NULL); + expiryReq = now; + if (timeReq != GSS_C_INDEFINITE) + expiryReq += timeReq; + + if (cred->krbCredCache != NULL && cred->expiryTime > expiryReq) + return TRUE; + + if (getDefaultReauthCredentials(&minor, cred, target, + now, timeReq) == GSS_S_COMPLETE) + return TRUE; + + return FALSE; +} + +/* + * Store re-authentication (Kerberos) credentials in a credential handle. + * Credentials handle must be locked. + */ +OM_uint32 +gssEapStoreReauthCreds(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t credBuf) +{ + OM_uint32 major = GSS_S_COMPLETE; + krb5_error_code code; + krb5_context krbContext = NULL; + krb5_auth_context authContext = NULL; + krb5_data credData = { 0 }; + krb5_creds **creds = NULL; + krb5_principal canonPrinc; + krb5_principal ccPrinc = NULL; + int i; + + if (credBuf->length == 0 || cred == GSS_C_NO_CREDENTIAL) + return GSS_S_COMPLETE; + + GSSEAP_KRB_INIT(&krbContext); + + code = krb5_auth_con_init(krbContext, &authContext); + if (code != 0) + goto cleanup; + + code = krb5_auth_con_setflags(krbContext, authContext, 0); + if (code != 0) + goto cleanup; + + code = krb5_auth_con_setrecvsubkey(krbContext, authContext, + &ctx->rfc3961Key); + if (code != 0) + goto cleanup; + + gssBufferToKrbData(credBuf, &credData); + + code = krb5_rd_cred(krbContext, authContext, &credData, &creds, NULL); + if (code != 0) + goto cleanup; + + if (creds == NULL || creds[0] == NULL) + goto cleanup; + + code = krb5_copy_principal(krbContext, creds[0]->client, &canonPrinc); + if (code != 0) + goto cleanup; + + krb5_free_principal(krbContext, cred->name->krbPrincipal); + cred->name->krbPrincipal = canonPrinc; + + if (creds[0]->times.endtime == KRB_TIME_FOREVER) + cred->expiryTime = 0; + else + cred->expiryTime = creds[0]->times.endtime; + + if (cred->krbCredCache == NULL) { + if (reauthUseCredsCache(krbContext, creds[0]->client) && + krb5_cc_default(krbContext, &cred->krbCredCache) == 0) + cred->flags |= CRED_FLAG_DEFAULT_CCACHE; + } else { + /* + * If we already have an associated credentials cache, possibly from + * the last time we stored a reauthentication credential, then we + * need to clear it out and release the associated GSS credential. + */ + if (cred->flags & CRED_FLAG_DEFAULT_CCACHE) { + krb5_cc_remove_cred(krbContext, cred->krbCredCache, 0, creds[0]); + } else { + krb5_cc_destroy(krbContext, cred->krbCredCache); + cred->krbCredCache = NULL; + } + gssReleaseCred(minor, &cred->krbCred); + } + + if (cred->krbCredCache == NULL) { + code = krb5_cc_new_unique(krbContext, "MEMORY", NULL, &cred->krbCredCache); + if (code != 0) + goto cleanup; + } + + if ((cred->flags & CRED_FLAG_DEFAULT_CCACHE) == 0 || + krb5_cc_get_principal(krbContext, cred->krbCredCache, &ccPrinc) != 0) { + code = krb5_cc_initialize(krbContext, cred->krbCredCache, + creds[0]->client); + if (code != 0) + goto cleanup; + } + + for (i = 0; creds[i] != NULL; i++) { + krb5_creds kcred = *(creds[i]); + + /* + * Swap in the acceptor name the client asked for so + * get_credentials() works. We're making the assumption that + * any service tickets returned are for us. We'll need to + * reflect some more on whether that is a safe assumption. + */ + if (!isTicketGrantingServiceP(krbContext, kcred.server)) + kcred.server = ctx->acceptorName->krbPrincipal; + + code = krb5_cc_store_cred(krbContext, cred->krbCredCache, &kcred); + if (code != 0) + goto cleanup; + } + + major = gss_krb5_import_cred(minor, cred->krbCredCache, NULL, NULL, + &cred->krbCred); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + *minor = code; + + krb5_free_principal(krbContext, ccPrinc); + krb5_auth_con_free(krbContext, authContext); + if (creds != NULL) { + for (i = 0; creds[i] != NULL; i++) + krb5_free_creds(krbContext, creds[i]); + GSSEAP_FREE(creds); + } + if (major == GSS_S_COMPLETE) + major = *minor ? GSS_S_FAILURE : GSS_S_COMPLETE; + + return major; +} + +#ifndef HAVE_HEIMDAL_VERSION +static gss_buffer_desc radiusAvpKrbAttr = { + sizeof("urn:authdata-radius-avp") - 1, "urn:authdata-radius-avp" +}; +#endif + +/* + * Unfortunately extracting an AD-KDCIssued authorization data element + * is pretty implementation-dependent. It's not possible to verify the + * signature ourselves because the ticket session key is not exposed + * outside GSS. In an ideal world, all AD-KDCIssued elements would be + * verified by the Kerberos library and authentication would fail if + * verification failed. We're not quite there yet and as a result have + * to go through some hoops to get this to work. The alternative would + * be to sign the authorization data with our long-term key, but it + * seems a pity to compromise the design because of current implementation + * limitations. + * + * (Specifically, the hoops involve a libkrb5 authorisation data plugin + * that exposes the verified and serialised attribute context through + * the Kerberos GSS mechanism's naming extensions API.) + */ +static OM_uint32 +defrostAttrContext(OM_uint32 *minor, + gss_ctx_id_t glueContext, + gss_name_t glueName, + gss_name_t mechName) +{ + OM_uint32 major, tmpMinor; +#ifdef HAVE_HEIMDAL_VERSION + gss_OID_desc oid = { 0 }; + gss_buffer_set_t authData = GSS_C_NO_BUFFER_SET; +#else + gss_buffer_desc authData = GSS_C_EMPTY_BUFFER; + gss_buffer_desc authDataDisplay = GSS_C_EMPTY_BUFFER; + int more = -1; + int authenticated, complete; +#endif + +#ifdef HAVE_HEIMDAL_VERSION + major = composeOid(minor, + GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements, + GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length, + KRB5_AUTHDATA_RADIUS_AVP, &oid); + if (GSS_ERROR(major)) + return major; + + /* XXX we are assuming that this verifies AD-KDCIssued signature */ + major = gssInquireSecContextByOid(minor, glueContext, + &oid, &authData); + if (major == GSS_S_COMPLETE) { + if (authData == GSS_C_NO_BUFFER_SET || authData->count != 1) + major = GSS_S_FAILURE; + else + major = gssEapImportAttrContext(minor, authData->elements, mechName); + } else if (major == GSS_S_FAILURE && *minor == ENOENT) { + /* This is the equivalent of GSS_S_UNAVAILABLE for MIT attr APIs */ + *minor = 0; + major = GSS_S_COMPLETE; + } + + gss_release_buffer_set(&tmpMinor, &authData); + GSSEAP_FREE(oid.elements); +#else + major = gssGetNameAttribute(minor, glueName, &radiusAvpKrbAttr, + &authenticated, &complete, + &authData, &authDataDisplay, &more); + if (major == GSS_S_COMPLETE) { + if (authenticated == 0) + major = GSS_S_BAD_NAME; + else + major = gssEapImportAttrContext(minor, &authData, mechName); + } else if (major == GSS_S_UNAVAILABLE) { + major = GSS_S_COMPLETE; + } + + gss_release_buffer(&tmpMinor, &authData); + gss_release_buffer(&tmpMinor, &authDataDisplay); +#endif /* HAVE_HEIMDAL_VERSION */ + + return major; +} + +/* + * Convert a mechanism glue to an EAP mechanism name by displaying and + * importing it. This also handles the RADIUS attributes. + */ +OM_uint32 +gssEapGlueToMechName(OM_uint32 *minor, + gss_ctx_id_t glueContext, + gss_name_t glueName, + gss_name_t *pMechName) +{ + OM_uint32 major, tmpMinor; + gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER; + + *pMechName = GSS_C_NO_NAME; + + major = gssDisplayName(minor, glueName, &nameBuf, NULL); + if (GSS_ERROR(major)) + goto cleanup; + + major = gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME, + pMechName); + if (GSS_ERROR(major)) + goto cleanup; + + major = defrostAttrContext(minor, glueContext, glueName, *pMechName); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + if (GSS_ERROR(major)) { + gssReleaseName(&tmpMinor, pMechName); + *pMechName = GSS_C_NO_NAME; + } + + gss_release_buffer(&tmpMinor, &nameBuf); + + return major; +} + +/* + * Convert an EAP mechanism name to a mechanism glue name by displaying + * and importing it. + */ +OM_uint32 +gssEapMechToGlueName(OM_uint32 *minor, + gss_name_t mechName, + gss_name_t *pGlueName) +{ + OM_uint32 major, tmpMinor; + gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER; + + *pGlueName = GSS_C_NO_NAME; + + major = gssEapDisplayName(minor, mechName, &nameBuf, NULL); + if (GSS_ERROR(major)) + goto cleanup; + + major = gssImportName(minor, &nameBuf, GSS_C_NT_USER_NAME, + pGlueName); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + gss_release_buffer(&tmpMinor, &nameBuf); + + return major; +} + +/* + * Suck out the analgous elements of a Kerberos GSS context into an EAP + * one so that the application doesn't know the difference. + */ +OM_uint32 +gssEapReauthComplete(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + const gss_OID mech, + OM_uint32 timeRec) +{ + OM_uint32 major, tmpMinor; + gss_buffer_set_t keyData = GSS_C_NO_BUFFER_SET; + krb5_context krbContext = NULL; +#ifdef HAVE_HEIMDAL_VERSION + krb5_storage *sp = NULL; +#endif + + GSSEAP_KRB_INIT(&krbContext); + + if (!oidEqual(mech, gss_mech_krb5)) { + major = GSS_S_BAD_MECH; + goto cleanup; + } + + /* Get the raw subsession key and encryption type */ +#ifdef HAVE_HEIMDAL_VERSION +#define KRB_GSS_SUBKEY_COUNT 1 /* encoded session key */ + major = gssInquireSecContextByOid(minor, ctx->kerberosCtx, + GSS_KRB5_GET_SUBKEY_X, &keyData); +#else +#define KRB_GSS_SUBKEY_COUNT 2 /* raw session key, enctype OID */ + major = gssInquireSecContextByOid(minor, ctx->kerberosCtx, + GSS_C_INQ_SSPI_SESSION_KEY, &keyData); +#endif + if (GSS_ERROR(major)) + goto cleanup; + + if (keyData == GSS_C_NO_BUFFER_SET || keyData->count < KRB_GSS_SUBKEY_COUNT) { + *minor = GSSEAP_KEY_UNAVAILABLE; + major = GSS_S_FAILURE; + goto cleanup; + } + +#ifdef HAVE_HEIMDAL_VERSION + sp = krb5_storage_from_mem(keyData->elements[0].value, + keyData->elements[0].length); + if (sp == NULL) { + *minor = ENOMEM; + major = GSS_S_FAILURE; + goto cleanup; + } + + *minor = krb5_ret_keyblock(sp, &ctx->rfc3961Key); + if (*minor != 0) { + major = GSS_S_FAILURE; + goto cleanup; + } +#else + { + gss_OID_desc oid; + int suffix; + + oid.length = keyData->elements[1].length; + oid.elements = keyData->elements[1].value; + + /* GSS_KRB5_SESSION_KEY_ENCTYPE_OID */ + major = decomposeOid(minor, + "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04", + 10, &oid, &suffix); + if (GSS_ERROR(major)) + goto cleanup; + + ctx->encryptionType = suffix; + } + + { + krb5_keyblock key; + + KRB_KEY_LENGTH(&key) = keyData->elements[0].length; + KRB_KEY_DATA(&key) = keyData->elements[0].value; + KRB_KEY_TYPE(&key) = ctx->encryptionType; + + *minor = krb5_copy_keyblock_contents(krbContext, + &key, &ctx->rfc3961Key); + if (*minor != 0) { + major = GSS_S_FAILURE; + goto cleanup; + } + } +#endif /* HAVE_HEIMDAL_VERSION */ + + major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key, + &ctx->checksumType); + if (GSS_ERROR(major)) + goto cleanup; + + if (timeRec != GSS_C_INDEFINITE) + ctx->expiryTime = time(NULL) + timeRec; + + /* Initialize our sequence state */ + major = sequenceInit(minor, + &ctx->seqState, ctx->recvSeq, + ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0), + ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0), + TRUE); + if (GSS_ERROR(major)) + goto cleanup; + + major = GSS_S_COMPLETE; + +cleanup: +#ifdef HAVE_HEIMDAL_VERSION + if (sp != NULL) + krb5_storage_free(sp); +#endif + gss_release_buffer_set(&tmpMinor, &keyData); + + return major; +} + +/* + * The remainder of this file consists of wrappers so we can call into the + * mechanism glue without calling ourselves. + */ +static OM_uint32 +(*gssInitSecContextNext)(OM_uint32 *, + gss_cred_id_t, + gss_ctx_id_t *, + gss_name_t, + gss_OID, + OM_uint32, + OM_uint32, + gss_channel_bindings_t, + gss_buffer_t, + gss_OID *, + gss_buffer_t, + OM_uint32 *, + OM_uint32 *); + +static OM_uint32 +(*gssAcceptSecContextNext)(OM_uint32 *, + gss_ctx_id_t *, + gss_cred_id_t, + gss_buffer_t, + gss_channel_bindings_t, + gss_name_t *, + gss_OID *, + gss_buffer_t, + OM_uint32 *, + OM_uint32 *, + gss_cred_id_t *); + +static OM_uint32 +(*gssReleaseCredNext)(OM_uint32 *, gss_cred_id_t *); + +static OM_uint32 +(*gssReleaseNameNext)(OM_uint32 *, gss_name_t *); + +static OM_uint32 +(*gssInquireSecContextByOidNext)(OM_uint32 *, + const gss_ctx_id_t, + const gss_OID, + gss_buffer_set_t *); + +static OM_uint32 +(*gssDeleteSecContextNext)(OM_uint32 *, + gss_ctx_id_t *, + gss_buffer_t); + +static OM_uint32 +(*gssDisplayNameNext)(OM_uint32 *, + gss_name_t, + gss_buffer_t, + gss_OID *); + +static OM_uint32 +(*gssImportNameNext)(OM_uint32 *, + gss_buffer_t, + gss_OID, + gss_name_t *); + +static OM_uint32 +(*gssStoreCredNext)(OM_uint32 *, + const gss_cred_id_t, + gss_cred_usage_t, + const gss_OID, + OM_uint32, + OM_uint32, + gss_OID_set *, + gss_cred_usage_t *); + +static OM_uint32 +(*gssGetNameAttributeNext)(OM_uint32 *, + gss_name_t, + gss_buffer_t, + int *, + int *, + gss_buffer_t, + gss_buffer_t, + int *); + +#define NEXT_SYMBOL(local, global) do { \ + ((local) = dlsym(RTLD_NEXT, (global))); \ + if ((local) == NULL) { \ + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; \ + major = GSS_S_UNAVAILABLE; \ + /* but continue */ \ + } \ + } while (0) + +OM_uint32 +gssEapReauthInitialize(OM_uint32 *minor) +{ + OM_uint32 major = GSS_S_COMPLETE; + + NEXT_SYMBOL(gssInitSecContextNext, "gss_init_sec_context"); + NEXT_SYMBOL(gssAcceptSecContextNext, "gss_accept_sec_context"); + NEXT_SYMBOL(gssReleaseCredNext, "gss_release_cred"); + NEXT_SYMBOL(gssReleaseNameNext, "gss_release_name"); + NEXT_SYMBOL(gssInquireSecContextByOidNext, "gss_inquire_sec_context_by_oid"); + NEXT_SYMBOL(gssDeleteSecContextNext, "gss_delete_sec_context"); + NEXT_SYMBOL(gssDisplayNameNext, "gss_display_name"); + NEXT_SYMBOL(gssImportNameNext, "gss_import_name"); + NEXT_SYMBOL(gssStoreCredNext, "gss_store_cred"); +#ifndef HAVE_HEIMDAL_VERSION + NEXT_SYMBOL(gssGetNameAttributeNext, "gss_get_name_attribute"); +#endif + + return major; +} + +OM_uint32 +gssInitSecContext(OM_uint32 *minor, + 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) +{ + if (gssInitSecContextNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssInitSecContextNext(minor, 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); +} + +OM_uint32 +gssAcceptSecContext(OM_uint32 *minor, + gss_ctx_id_t *context_handle, + gss_cred_id_t cred, + 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) +{ + if (gssAcceptSecContextNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssAcceptSecContextNext(minor, context_handle, cred, + input_token, input_chan_bindings, + src_name, mech_type, output_token, + ret_flags, time_rec, delegated_cred_handle); +} + +OM_uint32 +gssReleaseCred(OM_uint32 *minor, + gss_cred_id_t *cred_handle) +{ + if (gssReleaseCredNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssReleaseCredNext(minor, cred_handle); +} + +OM_uint32 +gssReleaseName(OM_uint32 *minor, + gss_name_t *name) +{ + if (gssReleaseName == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssReleaseNameNext(minor, name); +} + +OM_uint32 +gssDeleteSecContext(OM_uint32 *minor, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) +{ + if (gssDeleteSecContextNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssDeleteSecContextNext(minor, context_handle, output_token); +} + +static OM_uint32 +gssDisplayName(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t buffer, + gss_OID *name_type) +{ + if (gssDisplayNameNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssDisplayNameNext(minor, name, buffer, name_type); +} + +static OM_uint32 +gssImportName(OM_uint32 *minor, + gss_buffer_t buffer, + gss_OID name_type, + gss_name_t *name) +{ + if (gssImportNameNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssImportNameNext(minor, buffer, name_type, name); +} + +OM_uint32 +gssInquireSecContextByOid(OM_uint32 *minor, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + if (gssInquireSecContextByOidNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssInquireSecContextByOidNext(minor, context_handle, + desired_object, data_set); +} + +OM_uint32 +gssStoreCred(OM_uint32 *minor, + const 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_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored) +{ + if (gssStoreCredNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssStoreCredNext(minor, input_cred_handle, input_usage, + desired_mech, overwrite_cred, default_cred, + elements_stored, cred_usage_stored); +} + +OM_uint32 +gssGetNameAttribute(OM_uint32 *minor, + gss_name_t name, + gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) +{ + if (gssGetNameAttributeNext == NULL) { + *minor = GSSEAP_NO_MECHGLUE_SYMBOL; + return GSS_S_UNAVAILABLE; + } + + return gssGetNameAttributeNext(minor, name, attr, authenticated, complete, + value, display_value, more); +} diff --git a/util_reauth.h b/util_reauth.h new file mode 100644 index 0000000..29f2f2e --- /dev/null +++ b/util_reauth.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Fast reauthentication support. + */ + +#include "gssapiP_eap.h" + +#ifndef _UTIL_REAUTH_H_ +#define _UTIL_REAUTH_H_ 1 + +/* AD element containing serialised AVPs. */ +#define KRB5_AUTHDATA_RADIUS_AVP 513 + +OM_uint32 +gssInitSecContext(OM_uint32 *minor, + 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); + +OM_uint32 +gssAcceptSecContext(OM_uint32 *minor, + gss_ctx_id_t *context_handle, + gss_cred_id_t cred, + 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 +gssReleaseCred(OM_uint32 *minor, + gss_cred_id_t *cred_handle); + +OM_uint32 +gssReleaseName(OM_uint32 *minor, + gss_name_t *name); + +OM_uint32 +gssDeleteSecContext(OM_uint32 *minor, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token); + +OM_uint32 +gssInquireSecContextByOid(OM_uint32 *minor, + const gss_ctx_id_t context_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set); + +OM_uint32 +gssStoreCred(OM_uint32 *minor, + const 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_OID_set *elements_stored, + gss_cred_usage_t *cred_usage_stored); + +OM_uint32 +gssGetNameAttribute(OM_uint32 *minor, + 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 +gssEapMakeReauthCreds(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t credBuf); + +OM_uint32 +gssEapStoreReauthCreds(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + gss_buffer_t credBuf); + + +OM_uint32 +gssEapGlueToMechName(OM_uint32 *minor, + gss_ctx_id_t glueContext, + gss_name_t glueName, + gss_name_t *pMechName); + +OM_uint32 +gssEapMechToGlueName(OM_uint32 *minor, + gss_name_t mechName, + gss_name_t *pGlueName); + +OM_uint32 +gssEapReauthComplete(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_cred_id_t cred, + const gss_OID mech, + OM_uint32 timeRec); + +OM_uint32 +gssEapReauthInitialize(OM_uint32 *minor); + +int +gssEapCanReauthP(gss_cred_id_t cred, + gss_name_t target, + OM_uint32 timeReq); + +#endif /* _UTIL_REAUTH_H_ */ diff --git a/util_saml.cpp b/util_saml.cpp new file mode 100644 index 0000000..69d0379 --- /dev/null +++ b/util_saml.cpp @@ -0,0 +1,751 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * SAML attribute provider implementation. + */ + +#include "gssapiP_eap.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace xmltooling; +using namespace opensaml::saml2md; +using namespace opensaml; +using namespace xercesc; +using namespace std; + +/* + * gss_eap_saml_assertion_provider is for retrieving the underlying + * assertion. + */ +gss_eap_saml_assertion_provider::gss_eap_saml_assertion_provider(void) +{ + m_assertion = NULL; + m_authenticated = false; +} + +gss_eap_saml_assertion_provider::~gss_eap_saml_assertion_provider(void) +{ + delete m_assertion; +} + +bool +gss_eap_saml_assertion_provider::initFromExistingContext(const gss_eap_attr_ctx *manager, + const gss_eap_attr_provider *ctx) +{ + /* Then we may be creating from an existing attribute context */ + const gss_eap_saml_assertion_provider *saml; + + assert(m_assertion == NULL); + + if (!gss_eap_attr_provider::initFromExistingContext(manager, ctx)) + return false; + + saml = static_cast(ctx); + setAssertion(saml->getAssertion(), saml->authenticated()); + + return true; +} + +bool +gss_eap_saml_assertion_provider::initFromGssContext(const gss_eap_attr_ctx *manager, + const gss_cred_id_t gssCred, + const gss_ctx_id_t gssCtx) +{ + const gss_eap_radius_attr_provider *radius; + gss_buffer_desc value = GSS_C_EMPTY_BUFFER; + int authenticated, complete; + OM_uint32 minor; + + assert(m_assertion == NULL); + + if (!gss_eap_attr_provider::initFromGssContext(manager, gssCred, gssCtx)) + return false; + + /* + * XXX TODO we need to support draft-howlett-radius-saml-attr-00 + */ + radius = static_cast + (m_manager->getProvider(ATTR_TYPE_RADIUS)); + if (radius != NULL && + radius->getFragmentedAttribute(PW_SAML_AAA_ASSERTION, + VENDORPEC_UKERNA, + &authenticated, &complete, &value)) { + setAssertion(&value, authenticated); + gss_release_buffer(&minor, &value); + } else { + m_assertion = NULL; + } + + return true; +} + +void +gss_eap_saml_assertion_provider::setAssertion(const saml2::Assertion *assertion, + bool authenticated) +{ + + delete m_assertion; + + if (assertion != NULL) { +#ifdef __APPLE__ + m_assertion = (saml2::Assertion *)((void *)assertion->clone()); +#else + m_assertion = dynamic_cast(assertion->clone()); +#endif + m_authenticated = authenticated; + } else { + m_assertion = NULL; + m_authenticated = false; + } +} + +void +gss_eap_saml_assertion_provider::setAssertion(const gss_buffer_t buffer, + bool authenticated) +{ + delete m_assertion; + + m_assertion = parseAssertion(buffer); + m_authenticated = (m_assertion != NULL && authenticated); +} + +saml2::Assertion * +gss_eap_saml_assertion_provider::parseAssertion(const gss_buffer_t buffer) +{ + string str((char *)buffer->value, buffer->length); + istringstream istream(str); + DOMDocument *doc; + const XMLObjectBuilder *b; + + try { + doc = XMLToolingConfig::getConfig().getParser().parse(istream); + if (doc == NULL) + return NULL; + + b = XMLObjectBuilder::getBuilder(doc->getDocumentElement()); + +#ifdef __APPLE__ + return (saml2::Assertion *)((void *)b->buildFromDocument(doc)); +#else + return dynamic_cast(b->buildFromDocument(doc)); +#endif + } catch (exception &e) { + return NULL; + } +} + +bool +gss_eap_saml_assertion_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute, + void *data) const +{ + bool ret; + + /* just add the prefix */ + if (m_assertion != NULL) + ret = addAttribute(this, GSS_C_NO_BUFFER, data); + else + ret = true; + + return ret; +} + +bool +gss_eap_saml_assertion_provider::setAttribute(int complete, + const gss_buffer_t attr, + const gss_buffer_t value) +{ + if (attr == GSS_C_NO_BUFFER || attr->length == 0) { + setAssertion(value); + return true; + } + + return false; +} + +bool +gss_eap_saml_assertion_provider::deleteAttribute(const gss_buffer_t value) +{ + delete m_assertion; + m_assertion = NULL; + m_authenticated = false; + + return true; +} + +time_t +gss_eap_saml_assertion_provider::getExpiryTime(void) const +{ + saml2::Conditions *conditions; + time_t expiryTime = 0; + + if (m_assertion == NULL) + return 0; + + conditions = m_assertion->getConditions(); + + if (conditions != NULL && conditions->getNotOnOrAfter() != NULL) + expiryTime = conditions->getNotOnOrAfter()->getEpoch(); + + return expiryTime; +} + +OM_uint32 +gss_eap_saml_assertion_provider::mapException(OM_uint32 *minor, + std::exception &e) const +{ + if (typeid(e) == typeid(SecurityPolicyException)) + *minor = GSSEAP_SAML_SEC_POLICY_FAILURE; + else if (typeid(e) == typeid(BindingException)) + *minor = GSSEAP_SAML_BINDING_FAILURE; + else if (typeid(e) == typeid(ProfileException)) + *minor = GSSEAP_SAML_PROFILE_FAILURE; + else if (typeid(e) == typeid(FatalProfileException)) + *minor = GSSEAP_SAML_FATAL_PROFILE_FAILURE; + else if (typeid(e) == typeid(RetryableProfileException)) + *minor = GSSEAP_SAML_RETRY_PROFILE_FAILURE; + else if (typeid(e) == typeid(MetadataException)) + *minor = GSSEAP_SAML_METADATA_FAILURE; + else + return GSS_S_CONTINUE_NEEDED; + + return GSS_S_FAILURE; +} + +bool +gss_eap_saml_assertion_provider::getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const +{ + string str; + + if (attr != GSS_C_NO_BUFFER && attr->length != 0) + return false; + + if (m_assertion == NULL) + return false; + + if (*more != -1) + return false; + + if (authenticated != NULL) + *authenticated = m_authenticated; + if (complete != NULL) + *complete = true; + + XMLHelper::serialize(m_assertion->marshall((DOMDocument *)NULL), str); + + duplicateBuffer(str, value); + *more = 0; + + return true; +} + +gss_any_t +gss_eap_saml_assertion_provider::mapToAny(int authenticated, + gss_buffer_t type_id) const +{ + if (authenticated && !m_authenticated) + return (gss_any_t)NULL; + + return (gss_any_t)m_assertion; +} + +void +gss_eap_saml_assertion_provider::releaseAnyNameMapping(gss_buffer_t type_id, + gss_any_t input) const +{ + delete ((saml2::Assertion *)input); +} + +void +gss_eap_saml_assertion_provider::exportToBuffer(gss_buffer_t buffer) const +{ + ostringstream sink; + string str; + + buffer->length = 0; + buffer->value = NULL; + + if (m_assertion == NULL) + return; + + sink << *m_assertion; + str = sink.str(); + + duplicateBuffer(str, buffer); +} + +bool +gss_eap_saml_assertion_provider::initFromBuffer(const gss_eap_attr_ctx *ctx, + const gss_buffer_t buffer) +{ + if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer)) + return false; + + if (buffer->length == 0) + return true; + + assert(m_assertion == NULL); + + setAssertion(buffer); + /* TODO XXX how to propagate authenticated flag? */ + + return true; +} + +bool +gss_eap_saml_assertion_provider::init(void) +{ + gss_eap_attr_ctx::registerProvider(ATTR_TYPE_SAML_ASSERTION, + "urn:ietf:params:gss-eap:saml-aaa-assertion", + createAttrContext); + return true; +} + +void +gss_eap_saml_assertion_provider::finalize(void) +{ + gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_SAML_ASSERTION); +} + +gss_eap_attr_provider * +gss_eap_saml_assertion_provider::createAttrContext(void) +{ + return new gss_eap_saml_assertion_provider; +} + +saml2::Assertion * +gss_eap_saml_assertion_provider::initAssertion(void) +{ + delete m_assertion; + m_assertion = saml2::AssertionBuilder::buildAssertion(); + m_authenticated = false; + + return m_assertion; +} + +/* + * gss_eap_saml_attr_provider is for retrieving the underlying attributes. + */ +bool +gss_eap_saml_attr_provider::getAssertion(int *authenticated, + saml2::Assertion **pAssertion, + bool createIfAbsent) const +{ + gss_eap_saml_assertion_provider *saml; + + if (authenticated != NULL) + *authenticated = false; + if (pAssertion != NULL) + *pAssertion = NULL; + + saml = static_cast + (m_manager->getProvider(ATTR_TYPE_SAML_ASSERTION)); + if (saml == NULL) + return false; + + if (authenticated != NULL) + *authenticated = saml->authenticated(); + if (pAssertion != NULL) + *pAssertion = saml->getAssertion(); + + if (saml->getAssertion() == NULL) { + if (createIfAbsent) { + if (authenticated != NULL) + *authenticated = false; + if (pAssertion != NULL) + *pAssertion = saml->initAssertion(); + } else + return false; + } + + return true; +} + +bool +gss_eap_saml_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute, + void *data) const +{ + saml2::Assertion *assertion; + int authenticated; + + if (!getAssertion(&authenticated, &assertion)) + return true; + + /* + * Note: the first prefix is added by the attribute provider manager + * + * From draft-hartman-gss-eap-naming-00: + * + * Each attribute carried in the assertion SHOULD also be a GSS name + * attribute. The name of this attribute has three parts, all separated + * by an ASCII space character. The first part is + * urn:ietf:params:gss-eap:saml-attr. The second part is the URI for + * the SAML attribute name format. The final part is the name of the + * SAML attribute. If the mechanism performs an additional attribute + * query, the retrieved attributes SHOULD be GSS-API name attributes + * using the same name syntax. + */ + /* For each attribute statement, look for an attribute match */ + const vector &statements = + const_cast(assertion)->getAttributeStatements(); + + for (vector::const_iterator s = statements.begin(); + s != statements.end(); + ++s) { + const vector &attrs = + const_cast(*s)->getAttributes(); + + for (vector::const_iterator a = attrs.begin(); a != attrs.end(); ++a) { + const XMLCh *attributeName = (*a)->getName(); + const XMLCh *attributeNameFormat = (*a)->getNameFormat(); + XMLCh *qualifiedName; + XMLCh space[2] = { ' ', 0 }; + gss_buffer_desc utf8; + bool ret; + + qualifiedName = new XMLCh[XMLString::stringLen(attributeNameFormat) + 1 + + XMLString::stringLen(attributeName) + 1]; + XMLString::copyString(qualifiedName, attributeNameFormat); + XMLString::catString(qualifiedName, space); + XMLString::catString(qualifiedName, attributeName); + + utf8.value = (void *)toUTF8(qualifiedName); + utf8.length = strlen((char *)utf8.value); + + ret = addAttribute(this, &utf8, data); + + delete qualifiedName; + + if (!ret) + return ret; + } + } + + return true; +} + +static BaseRefVectorOf * +decomposeAttributeName(const gss_buffer_t attr) +{ + XMLCh *qualifiedAttr = new XMLCh[attr->length + 1]; + XMLString::transcode((const char *)attr->value, qualifiedAttr, attr->length); + + BaseRefVectorOf *components = XMLString::tokenizeString(qualifiedAttr); + + delete qualifiedAttr; + + if (components->size() != 2) { + delete components; + components = NULL; + } + + return components; +} + +bool +gss_eap_saml_attr_provider::setAttribute(int complete, + const gss_buffer_t attr, + const gss_buffer_t value) +{ + saml2::Assertion *assertion; + saml2::Attribute *attribute; + saml2::AttributeValue *attributeValue; + saml2::AttributeStatement *attributeStatement; + + if (!getAssertion(NULL, &assertion, true)) + return false; + + if (assertion->getAttributeStatements().size() != 0) { + attributeStatement = assertion->getAttributeStatements().front(); + } else { + attributeStatement = saml2::AttributeStatementBuilder::buildAttributeStatement(); + assertion->getAttributeStatements().push_back(attributeStatement); + } + + /* Check the attribute name consists of name format | whsp | name */ + BaseRefVectorOf *components = decomposeAttributeName(attr); + if (components == NULL) + return false; + + attribute = saml2::AttributeBuilder::buildAttribute(); + attribute->setNameFormat(components->elementAt(0)); + attribute->setName(components->elementAt(1)); + + XMLCh *xmlValue = new XMLCh[value->length + 1]; + XMLString::transcode((const char *)value->value, xmlValue, attr->length); + + attributeValue = saml2::AttributeValueBuilder::buildAttributeValue(); + attributeValue->setTextContent(xmlValue); + + attribute->getAttributeValues().push_back(attributeValue); + + assert(attributeStatement != NULL); + attributeStatement->getAttributes().push_back(attribute); + + delete components; + delete xmlValue; + + return true; +} + +bool +gss_eap_saml_attr_provider::deleteAttribute(const gss_buffer_t attr) +{ + saml2::Assertion *assertion; + bool ret = false; + + if (!getAssertion(NULL, &assertion) || + assertion->getAttributeStatements().size() == 0) + return false; + + /* Check the attribute name consists of name format | whsp | name */ + BaseRefVectorOf *components = decomposeAttributeName(attr); + if (components == NULL) + return false; + + /* For each attribute statement, look for an attribute match */ + const vector &statements = + const_cast(assertion)->getAttributeStatements(); + + for (vector::const_iterator s = statements.begin(); + s != statements.end(); + ++s) { + const vector &attrs = + const_cast(*s)->getAttributes(); + ssize_t index = -1, i = 0; + + /* There's got to be an easier way to do this */ + for (vector::const_iterator a = attrs.begin(); + a != attrs.end(); + ++a) { + if (XMLString::equals((*a)->getNameFormat(), components->elementAt(0)) && + XMLString::equals((*a)->getName(), components->elementAt(1))) { + index = i; + break; + } + ++i; + } + if (index != -1) { + (*s)->getAttributes().erase((*s)->getAttributes().begin() + index); + ret = true; + } + } + + delete components; + + return ret; +} + +bool +gss_eap_saml_attr_provider::getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + const saml2::Attribute **pAttribute) const +{ + saml2::Assertion *assertion; + + if (authenticated != NULL) + *authenticated = false; + if (complete != NULL) + *complete = true; + *pAttribute = NULL; + + if (!getAssertion(authenticated, &assertion) || + assertion->getAttributeStatements().size() == 0) + return false; + + /* Check the attribute name consists of name format | whsp | name */ + BaseRefVectorOf *components = decomposeAttributeName(attr); + if (components == NULL) + return false; + + /* For each attribute statement, look for an attribute match */ + const vector &statements = + const_cast(assertion)->getAttributeStatements(); + const saml2::Attribute *ret = NULL; + + for (vector::const_iterator s = statements.begin(); + s != statements.end(); + ++s) { + const vector &attrs = + const_cast(*s)->getAttributes(); + + for (vector::const_iterator a = attrs.begin(); a != attrs.end(); ++a) { + if (XMLString::equals((*a)->getNameFormat(), components->elementAt(0)) && + XMLString::equals((*a)->getName(), components->elementAt(1))) { + ret = *a; + break; + } + } + + if (ret != NULL) + break; + } + + delete components; + + *pAttribute = ret; + + return (ret != NULL); +} + +bool +gss_eap_saml_attr_provider::getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const +{ + const saml2::Attribute *a; + const saml2::AttributeValue *av; + int nvalues, i = *more; + + *more = 0; + + if (!getAttribute(attr, authenticated, complete, &a)) + return false; + + nvalues = a->getAttributeValues().size(); + + if (i == -1) + i = 0; + else if (i >= nvalues) + return false; +#ifdef __APPLE__ + av = (const saml2::AttributeValue *)((void *)(a->getAttributeValues().at(i))); +#else + av = dynamic_cast(a->getAttributeValues().at(i)); +#endif + if (av != NULL) { + if (value != NULL) { + value->value = toUTF8(av->getTextContent(), true); + value->length = strlen((char *)value->value); + } + if (display_value != NULL) { + display_value->value = toUTF8(av->getTextContent(), true); + display_value->length = strlen((char *)value->value); + } + } + + if (nvalues > ++i) + *more = i; + + return true; +} + +gss_any_t +gss_eap_saml_attr_provider::mapToAny(int authenticated, + gss_buffer_t type_id) const +{ + return (gss_any_t)NULL; +} + +void +gss_eap_saml_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id, + gss_any_t input) const +{ +} + +void +gss_eap_saml_attr_provider::exportToBuffer(gss_buffer_t buffer) const +{ + buffer->length = 0; + buffer->value = NULL; +} + +bool +gss_eap_saml_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx, + const gss_buffer_t buffer) +{ + return gss_eap_attr_provider::initFromBuffer(ctx, buffer); +} + +bool +gss_eap_saml_attr_provider::init(void) +{ + gss_eap_attr_ctx::registerProvider(ATTR_TYPE_SAML, + "urn:ietf:params:gss-eap:saml-attr", + createAttrContext); + return true; +} + +void +gss_eap_saml_attr_provider::finalize(void) +{ + gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_SAML); +} + +gss_eap_attr_provider * +gss_eap_saml_attr_provider::createAttrContext(void) +{ + return new gss_eap_saml_attr_provider; +} + +OM_uint32 +gssEapSamlAttrProvidersInit(OM_uint32 *minor) +{ + if (!gss_eap_saml_assertion_provider::init() || + !gss_eap_saml_attr_provider::init()) { + *minor = GSSEAP_SAML_INIT_FAILURE; + return GSS_S_FAILURE; + } + + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapSamlAttrProvidersFinalize(OM_uint32 *minor) +{ + gss_eap_saml_attr_provider::finalize(); + gss_eap_saml_assertion_provider::finalize(); + return GSS_S_COMPLETE; +} diff --git a/util_saml.h b/util_saml.h new file mode 100644 index 0000000..f3cb3cd --- /dev/null +++ b/util_saml.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * SAML attribute provider. + */ + +#ifndef _UTIL_SAML_H_ +#define _UTIL_SAML_H_ 1 + +#ifdef __cplusplus + +namespace opensaml { + namespace saml2 { + class Attribute; + class Assertion; + class NameID; + }; +}; + +struct gss_eap_saml_assertion_provider : gss_eap_attr_provider { +public: + gss_eap_saml_assertion_provider(void); + ~gss_eap_saml_assertion_provider(void); + + bool initFromExistingContext(const gss_eap_attr_ctx *source, + const gss_eap_attr_provider *ctx); + bool initFromGssContext(const gss_eap_attr_ctx *source, + const gss_cred_id_t cred, + const gss_ctx_id_t ctx); + + bool getAttributeTypes(gss_eap_attr_enumeration_cb, void *data) const; + bool setAttribute(int complete, + const gss_buffer_t attr, + const gss_buffer_t value); + bool deleteAttribute(const gss_buffer_t value); + bool getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const; + gss_any_t mapToAny(int authenticated, + gss_buffer_t type_id) const; + void releaseAnyNameMapping(gss_buffer_t type_id, + gss_any_t input) const; + + void exportToBuffer(gss_buffer_t buffer) const; + bool initFromBuffer(const gss_eap_attr_ctx *ctx, + const gss_buffer_t buffer); + + opensaml::saml2::Assertion *initAssertion(void); + + opensaml::saml2::Assertion *getAssertion(void) const { + return m_assertion; + } + bool authenticated(void) const { + return m_authenticated; + } + + time_t getExpiryTime(void) const; + OM_uint32 mapException(OM_uint32 *minor, std::exception &e) const; + + static bool init(void); + static void finalize(void); + + static gss_eap_attr_provider *createAttrContext(void); + +private: + static opensaml::saml2::Assertion * + parseAssertion(const gss_buffer_t buffer); + + void setAssertion(const opensaml::saml2::Assertion *assertion, + bool authenticated = false); + void setAssertion(const gss_buffer_t buffer, + bool authenticated = false); + + opensaml::saml2::Assertion *m_assertion; + bool m_authenticated; +}; + +struct gss_eap_saml_attr_provider : gss_eap_attr_provider { +public: + gss_eap_saml_attr_provider(void) {} + ~gss_eap_saml_attr_provider(void) {} + + bool getAttributeTypes(gss_eap_attr_enumeration_cb, void *data) const; + bool setAttribute(int complete, + const gss_buffer_t attr, + const gss_buffer_t value); + bool deleteAttribute(const gss_buffer_t value); + bool getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const; + gss_any_t mapToAny(int authenticated, + gss_buffer_t type_id) const; + void releaseAnyNameMapping(gss_buffer_t type_id, + gss_any_t input) const; + + void exportToBuffer(gss_buffer_t buffer) const; + bool initFromBuffer(const gss_eap_attr_ctx *ctx, + const gss_buffer_t buffer); + + bool getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + const opensaml::saml2::Attribute **pAttribute) const; + bool getAssertion(int *authenticated, + opensaml::saml2::Assertion **pAssertion, + bool createIfAbsent = false) const; + + static bool init(void); + static void finalize(void); + + static gss_eap_attr_provider *createAttrContext(void); + +private: +}; + +extern "C" { +#endif + +OM_uint32 gssEapSamlAttrProvidersInit(OM_uint32 *minor); +OM_uint32 gssEapSamlAttrProvidersFinalize(OM_uint32 *minor); + +#ifdef __cplusplus +} +#endif + +#endif /* _UTIL_SAML_H_ */ diff --git a/util_shib.cpp b/util_shib.cpp new file mode 100644 index 0000000..7080e24 --- /dev/null +++ b/util_shib.cpp @@ -0,0 +1,529 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ +/* + * Copyright 2001-2009 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Local attribute provider implementation. + */ + +#include + +#include + +#include +#include +#include + +#include + +#include "gssapiP_eap.h" + +using namespace shibsp; +using namespace shibresolver; +using namespace opensaml::saml2md; +using namespace opensaml; +using namespace xmltooling; +using namespace std; + +gss_eap_shib_attr_provider::gss_eap_shib_attr_provider(void) +{ + m_authenticated = false; +} + +gss_eap_shib_attr_provider::~gss_eap_shib_attr_provider(void) +{ + for_each(m_attributes.begin(), + m_attributes.end(), + xmltooling::cleanup()) + ; +} + +bool +gss_eap_shib_attr_provider::initFromExistingContext(const gss_eap_attr_ctx *manager, + const gss_eap_attr_provider *ctx) +{ + const gss_eap_shib_attr_provider *shib; + + if (!gss_eap_attr_provider::initFromExistingContext(manager, ctx)) { + return false; + } + + m_authenticated = false; + + shib = static_cast(ctx); + if (shib != NULL) { + m_attributes = duplicateAttributes(shib->getAttributes()); + m_authenticated = shib->authenticated(); + } + + return true; +} + +bool +addRadiusAttribute(const gss_eap_attr_provider *provider, + const gss_buffer_t attribute, + void *data) +{ + const gss_eap_shib_attr_provider *shib; + const gss_eap_radius_attr_provider *radius; + int authenticated, complete, more = -1; + vector attributeIds(1); + SimpleAttribute *a; + + radius = static_cast(provider); + shib = static_cast(data); + + assert(radius != NULL && shib != NULL); + + string attributeName = + gss_eap_attr_ctx::composeAttributeName(ATTR_TYPE_RADIUS, attribute); + + attributeIds.push_back(attributeName); + a = new SimpleAttribute(attributeIds); + if (a == NULL) + return false; + + while (more != 0) { + gss_buffer_desc value = GSS_C_EMPTY_BUFFER; + OM_uint32 minor; + + if (!radius->getAttribute(attribute, + &authenticated, + &complete, + &value, + NULL, + &more)) + return false; + + string attributeValue((char *)value.value, value.length); + a->getValues().push_back(attributeValue); + + gss_release_buffer(&minor, &value); + } + + shib->getAttributes().push_back(a); + + return true; +} + +bool +gss_eap_shib_attr_provider::initFromGssContext(const gss_eap_attr_ctx *manager, + const gss_cred_id_t gssCred, + const gss_ctx_id_t gssCtx) +{ + const gss_eap_saml_assertion_provider *saml; + const gss_eap_radius_attr_provider *radius; + gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER; + ShibbolethResolver *resolver; + OM_uint32 minor; + + if (!gss_eap_attr_provider::initFromGssContext(manager, gssCred, gssCtx)) + return false; + + saml = static_cast + (m_manager->getProvider(ATTR_TYPE_SAML_ASSERTION)); + radius = static_cast + (m_manager->getProvider(ATTR_TYPE_RADIUS)); + + resolver = ShibbolethResolver::create(); + + if (gssCred != GSS_C_NO_CREDENTIAL && + gssEapDisplayName(&minor, gssCred->name, &nameBuf, NULL) == GSS_S_COMPLETE) { + resolver->setApplicationID((const char *)nameBuf.value); + gss_release_buffer(&minor, &nameBuf); + } + + m_authenticated = false; + + if (radius != NULL) { + radius->getAttributeTypes(addRadiusAttribute, (void *)this); + m_authenticated = radius->authenticated(); + } + + if (saml != NULL && saml->getAssertion() != NULL) { + resolver->addToken(saml->getAssertion()); + if (m_authenticated) + m_authenticated = saml->authenticated(); + } + + try { + resolver->resolve(); + m_attributes = resolver->getResolvedAttributes(); + resolver->getResolvedAttributes().clear(); + } catch (exception &e) { +#if 0 + delete resolver; + throw e; +#endif + } + + delete resolver; + return true; +} + +ssize_t +gss_eap_shib_attr_provider::getAttributeIndex(const gss_buffer_t attr) const +{ + int i = 0; + + for (vector::const_iterator a = m_attributes.begin(); + a != m_attributes.end(); + ++a) + { + for (vector::const_iterator s = (*a)->getAliases().begin(); + s != (*a)->getAliases().end(); + ++s) { + if (attr->length == (*s).length() && + memcmp((*s).c_str(), attr->value, attr->length) == 0) { + return i; + } + } + } + + return -1; +} + +bool +gss_eap_shib_attr_provider::setAttribute(int complete, + const gss_buffer_t attr, + const gss_buffer_t value) +{ + string attrStr((char *)attr->value, attr->length); + vector ids(1, attrStr); + SimpleAttribute *a = new SimpleAttribute(ids); + + if (value->length != 0) { + string valueStr((char *)value->value, value->length); + + a->getValues().push_back(valueStr); + } + + m_attributes.push_back(a); + m_authenticated = false; + + return true; +} + +bool +gss_eap_shib_attr_provider::deleteAttribute(const gss_buffer_t attr) +{ + int i; + + i = getAttributeIndex(attr); + if (i >= 0) + m_attributes.erase(m_attributes.begin() + i); + + m_authenticated = false; + + return true; +} + +bool +gss_eap_shib_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute, + void *data) const +{ + for (vector::const_iterator a = m_attributes.begin(); + a != m_attributes.end(); + ++a) + { + gss_buffer_desc attribute; + + attribute.value = (void *)((*a)->getId()); + attribute.length = strlen((char *)attribute.value); + + if (!addAttribute(this, &attribute, data)) + return false; + } + + return true; +} + +const Attribute * +gss_eap_shib_attr_provider::getAttribute(const gss_buffer_t attr) const +{ + const Attribute *ret = NULL; + + for (vector::const_iterator a = m_attributes.begin(); + a != m_attributes.end(); + ++a) + { + for (vector::const_iterator s = (*a)->getAliases().begin(); + s != (*a)->getAliases().end(); + ++s) { + if (attr->length == (*s).length() && + memcmp((*s).c_str(), attr->value, attr->length) == 0) { + ret = *a; + break; + } + } + if (ret != NULL) + break; + } + + return ret; +} + +bool +gss_eap_shib_attr_provider::getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const +{ + const Attribute *shibAttr = NULL; + gss_buffer_desc buf; + int nvalues, i = *more; + + *more = 0; + + shibAttr = getAttribute(attr); + if (shibAttr == NULL) + return false; + + nvalues = shibAttr->valueCount(); + + if (i == -1) + i = 0; + else if (i >= nvalues) + return false; + + buf.value = (void *)shibAttr->getString(*more); + buf.length = strlen((char *)buf.value); + + if (buf.length != 0) { + if (value != NULL) + duplicateBuffer(buf, value); + + if (display_value != NULL) + duplicateBuffer(buf, display_value); + } + + if (authenticated != NULL) + *authenticated = m_authenticated; + if (complete != NULL) + *complete = false; + + if (nvalues > ++i) + *more = i; + + return true; +} + +gss_any_t +gss_eap_shib_attr_provider::mapToAny(int authenticated, + gss_buffer_t type_id) const +{ + gss_any_t output; + + if (authenticated && !m_authenticated) + return (gss_any_t)NULL; + + vector v = duplicateAttributes(m_attributes); + + output = (gss_any_t)new vector (v); + + return output; +} + +void +gss_eap_shib_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id, + gss_any_t input) const +{ + vector *v = ((vector *)input); + delete v; +} + +void +gss_eap_shib_attr_provider::exportToBuffer(gss_buffer_t buffer) const +{ + DDF obj(NULL); + DDF attrs(NULL); + + buffer->length = 0; + buffer->value = NULL; + + obj.addmember("version").integer(1); + obj.addmember("authenticated").integer(m_authenticated); + + attrs = obj.addmember("attributes").list(); + for (vector::const_iterator a = m_attributes.begin(); + a != m_attributes.end(); ++a) { + DDF attr = (*a)->marshall(); + attrs.add(attr); + } + + ostringstream sink; + sink << attrs; + string str = sink.str(); + + duplicateBuffer(str, buffer); + + attrs.destroy(); +} + +bool +gss_eap_shib_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx, + const gss_buffer_t buffer) +{ + if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer)) + return false; + + if (buffer->length == 0) + return true; + + assert(m_authenticated == false); + assert(m_attributes.size() == 0); + + DDF obj(NULL); + string str((const char *)buffer->value, buffer->length); + istringstream source(str); + + source >> obj; + + if (obj["version"].integer() != 1) + return false; + + m_authenticated = (obj["authenticated"].integer() != 0); + + DDF attrs = obj["attributes"]; + DDF attr = attrs.first(); + while (!attr.isnull()) { + Attribute *attribute = Attribute::unmarshall(attr); + m_attributes.push_back(attribute); + attr = attrs.next(); + } + + attrs.destroy(); + + return true; +} + +bool +gss_eap_shib_attr_provider::init(void) +{ + if (!ShibbolethResolver::init()) + return false; + + gss_eap_attr_ctx::registerProvider(ATTR_TYPE_LOCAL, NULL, createAttrContext); + + return true; +} + +void +gss_eap_shib_attr_provider::finalize(void) +{ + gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_LOCAL); + ShibbolethResolver::term(); +} + +OM_uint32 +gss_eap_shib_attr_provider::mapException(OM_uint32 *minor, + std::exception &e) const +{ + if (typeid(e) == typeid(AttributeException)) + *minor = GSSEAP_SHIB_ATTR_FAILURE; + else if (typeid(e) == typeid(AttributeExtractionException)) + *minor = GSSEAP_SHIB_ATTR_EXTRACT_FAILURE; + else if (typeid(e) == typeid(AttributeFilteringException)) + *minor = GSSEAP_SHIB_ATTR_FILTER_FAILURE; + else if (typeid(e) == typeid(AttributeResolutionException)) + *minor = GSSEAP_SHIB_ATTR_RESOLVE_FAILURE; + else if (typeid(e) == typeid(ConfigurationException)) + *minor = GSSEAP_SHIB_CONFIG_FAILURE; + else if (typeid(e) == typeid(ListenerException)) + *minor = GSSEAP_SHIB_LISTENER_FAILURE; + else + return GSS_S_CONTINUE_NEEDED; + + return GSS_S_FAILURE; +} + +gss_eap_attr_provider * +gss_eap_shib_attr_provider::createAttrContext(void) +{ + return new gss_eap_shib_attr_provider; +} + +Attribute * +gss_eap_shib_attr_provider::duplicateAttribute(const Attribute *src) +{ + DDF obj = src->marshall(); + Attribute *attribute = Attribute::unmarshall(obj); + obj.destroy(); + + return attribute; +} + +vector +gss_eap_shib_attr_provider::duplicateAttributes(const vector src) +{ + vector dst; + + for (vector::const_iterator a = src.begin(); + a != src.end(); + ++a) + dst.push_back(duplicateAttribute(*a)); + + return dst; +} + +OM_uint32 +gssEapLocalAttrProviderInit(OM_uint32 *minor) +{ + if (!gss_eap_shib_attr_provider::init()) { + *minor = GSSEAP_SHIB_INIT_FAILURE; + return GSS_S_FAILURE; + } + return GSS_S_COMPLETE; +} + +OM_uint32 +gssEapLocalAttrProviderFinalize(OM_uint32 *minor) +{ + gss_eap_shib_attr_provider::finalize(); + return GSS_S_COMPLETE; +} diff --git a/util_shib.h b/util_shib.h new file mode 100644 index 0000000..f9ede20 --- /dev/null +++ b/util_shib.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Local attribute provider. + */ + +#ifndef _UTIL_SHIB_H_ +#define _UTIL_SHIB_H_ 1 + +#ifdef __cplusplus + +#include + +namespace shibsp { + class Attribute; +}; + +namespace shibresolver { + class ShibbolethResolver; +}; + +struct gss_eap_shib_attr_provider : gss_eap_attr_provider { +public: + gss_eap_shib_attr_provider(void); + ~gss_eap_shib_attr_provider(void); + + bool initFromExistingContext(const gss_eap_attr_ctx *source, + const gss_eap_attr_provider *ctx); + bool initFromGssContext(const gss_eap_attr_ctx *source, + const gss_cred_id_t cred, + const gss_ctx_id_t ctx); + + bool setAttribute(int complete, + const gss_buffer_t attr, + const gss_buffer_t value); + bool deleteAttribute(const gss_buffer_t value); + bool getAttributeTypes(gss_eap_attr_enumeration_cb, void *data) const; + bool getAttribute(const gss_buffer_t attr, + int *authenticated, + int *complete, + gss_buffer_t value, + gss_buffer_t display_value, + int *more) const; + gss_any_t mapToAny(int authenticated, + gss_buffer_t type_id) const; + void releaseAnyNameMapping(gss_buffer_t type_id, + gss_any_t input) const; + + void exportToBuffer(gss_buffer_t buffer) const; + bool initFromBuffer(const gss_eap_attr_ctx *ctx, + const gss_buffer_t buffer); + + static bool init(void); + static void finalize(void); + + OM_uint32 mapException(OM_uint32 *minor, std::exception &e) const; + + static gss_eap_attr_provider *createAttrContext(void); + +private: + static shibsp::Attribute * + duplicateAttribute(const shibsp::Attribute *src); + static std::vector + duplicateAttributes(const std::vector src); + + ssize_t getAttributeIndex(const gss_buffer_t attr) const; + const shibsp::Attribute *getAttribute(const gss_buffer_t attr) const; + + std::vector getAttributes(void) const { + return m_attributes; + } + + bool authenticated(void) const { return m_authenticated; } + + friend bool + addRadiusAttribute(const gss_eap_attr_provider *source, + const gss_buffer_t attribute, + void *data); + + std::vector m_attributes; + int m_authenticated; +}; + + +extern "C" { +#endif + +OM_uint32 gssEapLocalAttrProviderInit(OM_uint32 *minor); +OM_uint32 gssEapLocalAttrProviderFinalize(OM_uint32 *minor); + +#ifdef __cplusplus +} +#endif + +#endif /* _UTIL_SHIB_H_ */ diff --git a/util_token.c b/util_token.c new file mode 100644 index 0000000..1ce51f9 --- /dev/null +++ b/util_token.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ +/* + * 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. + */ + +/* + * Utility routines for GSS tokens. + */ + +#include "gssapiP_eap.h" + +/* + * $Id: util_token.c 23457 2009-12-08 00:04:48Z tlyu $ + */ + +/* XXXX this code currently makes the assumption that a mech oid will + never be longer than 127 bytes. This assumption is not inherent in + the interfaces, so the code can be fixed if the OSI namespace + balloons unexpectedly. */ + +/* + * Each token looks like this: + * 0x60 tag for APPLICATION 0, SEQUENCE + * (constructed, definite-length) + * possible multiple bytes, need to parse/generate + * 0x06 tag for OBJECT IDENTIFIER + * compile-time constant string (assume 1 byte) + * compile-time constant string + * the ANY containing the application token + * bytes 0,1 are the token type + * bytes 2,n are the token data + * + * Note that the token type field is a feature of RFC 1964 mechanisms and + * is not used by other GSSAPI mechanisms. As such, a token type of -1 + * is interpreted to mean that no token type should be expected or + * generated. + * + * For the purposes of this abstraction, the token "header" consists of + * the sequence tag and length octets, the mech OID DER encoding, and the + * first two inner bytes, which indicate the token type. The token + * "body" consists of everything else. + */ + +static size_t +der_length_size(size_t length) +{ + if (length < (1<<7)) + return 1; + else if (length < (1<<8)) + return 2; +#if INT_MAX == 0x7fff + else + return 3; +#else + else if (length < (1<<16)) + return 3; + else if (length < (1<<24)) + return 4; + else + return 5; +#endif +} + +static void +der_write_length(unsigned char **buf, size_t length) +{ + if (length < (1<<7)) { + *(*buf)++ = (unsigned char)length; + } else { + *(*buf)++ = (unsigned char)(der_length_size(length)+127); +#if INT_MAX > 0x7fff + if (length >= (1<<24)) + *(*buf)++ = (unsigned char)(length>>24); + if (length >= (1<<16)) + *(*buf)++ = (unsigned char)((length>>16)&0xff); +#endif + if (length >= (1<<8)) + *(*buf)++ = (unsigned char)((length>>8)&0xff); + *(*buf)++ = (unsigned char)(length&0xff); + } +} + +/* returns decoded length, or < 0 on failure. Advances buf and + decrements bufsize */ + +static int +der_read_length(unsigned char **buf, ssize_t *bufsize) +{ + unsigned char sf; + int ret; + + if (*bufsize < 1) + return -1; + + sf = *(*buf)++; + (*bufsize)--; + if (sf & 0x80) { + if ((sf &= 0x7f) > ((*bufsize)-1)) + return -1; + if (sf > sizeof(int)) + return -1; + ret = 0; + for (; sf; sf--) { + ret = (ret<<8) + (*(*buf)++); + (*bufsize)--; + } + } else { + ret = sf; + } + + return ret; +} + +/* returns the length of a token, given the mech oid and the body size */ + +size_t +tokenSize(const gss_OID_desc *mech, size_t body_size) +{ + /* set body_size to sequence contents size */ + body_size += 4 + (size_t) mech->length; /* NEED overflow check */ + return 1 + der_length_size(body_size) + body_size; +} + +/* fills in a buffer with the token header. The buffer is assumed to + be the right size. buf is advanced past the token header */ + +void +makeTokenHeader( + const gss_OID_desc *mech, + size_t body_size, + unsigned char **buf, + enum gss_eap_token_type tok_type) +{ + *(*buf)++ = 0x60; + der_write_length(buf, (tok_type == -1) ?2:4 + mech->length + body_size); + *(*buf)++ = 0x06; + *(*buf)++ = (unsigned char)mech->length; + memcpy(*buf, mech->elements, mech->length); + *buf += mech->length; + assert(tok_type != TOK_TYPE_NONE); + *(*buf)++ = (unsigned char)((tok_type>>8) & 0xff); + *(*buf)++ = (unsigned char)(tok_type & 0xff); +} + +/* + * Given a buffer containing a token, reads and verifies the token, + * leaving buf advanced past the token header, and setting body_size + * to the number of remaining bytes. Returns 0 on success, + * G_BAD_TOK_HEADER for a variety of errors, and G_WRONG_MECH if the + * mechanism in the token does not match the mech argument. buf and + * *body_size are left unmodified on error. + */ + +OM_uint32 +verifyTokenHeader(OM_uint32 *minor, + gss_OID mech, + size_t *body_size, + unsigned char **buf_in, + size_t toksize_in, + enum gss_eap_token_type *ret_tok_type) +{ + unsigned char *buf = *buf_in; + ssize_t seqsize; + gss_OID_desc toid; + ssize_t toksize = (ssize_t)toksize_in; + + *minor = GSSEAP_BAD_TOK_HEADER; + + if (ret_tok_type != NULL) + *ret_tok_type = TOK_TYPE_NONE; + + if ((toksize -= 1) < 0) + return GSS_S_DEFECTIVE_TOKEN; + + if (*buf++ != 0x60) + return GSS_S_DEFECTIVE_TOKEN; + + seqsize = der_read_length(&buf, &toksize); + if (seqsize < 0) + return GSS_S_DEFECTIVE_TOKEN; + + if (seqsize != toksize) + return GSS_S_DEFECTIVE_TOKEN; + + if ((toksize -= 1) < 0) + return GSS_S_DEFECTIVE_TOKEN; + + if (*buf++ != 0x06) + return GSS_S_DEFECTIVE_TOKEN; + + if ((toksize -= 1) < 0) + return GSS_S_DEFECTIVE_TOKEN; + + toid.length = *buf++; + + if ((toksize -= toid.length) < 0) + return GSS_S_DEFECTIVE_TOKEN; + + toid.elements = buf; + buf += toid.length; + + if (mech->elements == NULL) { + *mech = toid; + if (toid.length == 0) + return GSS_S_BAD_MECH; + } else if (!oidEqual(&toid, mech)) { + *minor = GSSEAP_WRONG_MECH; + return GSS_S_BAD_MECH; + } + + if (ret_tok_type != NULL) { + if ((toksize -= 2) < 0) + return GSS_S_DEFECTIVE_TOKEN; + + *ret_tok_type = load_uint16_be(buf); + buf += 2; + } + + *buf_in = buf; + *body_size = toksize; + + *minor = 0; + return GSS_S_COMPLETE; +} diff --git a/verify_mic.c b/verify_mic.c new file mode 100644 index 0000000..5df6b52 --- /dev/null +++ b/verify_mic.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Message protection services: verify a message integrity check. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_verify_mic(OM_uint32 *minor, + gss_ctx_id_t ctx, + gss_buffer_t message_buffer, + gss_buffer_t message_token, + gss_qop_t *qop_state) +{ + OM_uint32 major; + gss_iov_buffer_desc iov[3]; + int conf_state; + + if (message_token->length < 16) { + *minor = GSSEAP_TOK_TRUNC; + return GSS_S_BAD_SIG; + } + + *minor = 0; + + iov[0].type = GSS_IOV_BUFFER_TYPE_DATA; + iov[0].buffer = *message_buffer; + + iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER; + iov[1].buffer.length = 16; + iov[1].buffer.value = message_token->value; + + iov[2].type = GSS_IOV_BUFFER_TYPE_TRAILER; + iov[2].buffer.length = message_token->length - 16; + iov[2].buffer.value = (unsigned char *)message_token->value + 16; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + major = gssEapUnwrapOrVerifyMIC(minor, ctx, &conf_state, qop_state, + iov, 3, TOK_TYPE_MIC); + + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return major; +} diff --git a/wrap.c b/wrap.c new file mode 100644 index 0000000..408f983 --- /dev/null +++ b/wrap.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Message protection services: wrap. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_wrap(OM_uint32 *minor, + gss_ctx_id_t ctx, + 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 major; + + if (ctx == GSS_C_NO_CONTEXT) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + *minor = 0; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (!CTX_IS_ESTABLISHED(ctx)) { + major = GSS_S_NO_CONTEXT; + *minor = GSSEAP_CONTEXT_INCOMPLETE; + goto cleanup; + } + + major = gssEapWrap(minor, ctx, conf_req_flag, qop_req, + input_message_buffer, + conf_state, output_message_buffer); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return major; +} + +OM_uint32 +gssEapWrap(OM_uint32 *minor, + gss_ctx_id_t ctx, + 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 major, tmpMinor; + gss_iov_buffer_desc iov[4]; + unsigned char *p; + int i; + + iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; + iov[0].buffer.value = NULL; + iov[0].buffer.length = 0; + + iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; + iov[1].buffer = *input_message_buffer; + + iov[2].type = GSS_IOV_BUFFER_TYPE_PADDING; + iov[2].buffer.value = NULL; + iov[2].buffer.length = 0; + + iov[3].type = GSS_IOV_BUFFER_TYPE_TRAILER; + iov[3].buffer.value = NULL; + iov[3].buffer.length = 0; + + major = gssEapWrapIovLength(minor, ctx, conf_req_flag, qop_req, + NULL, iov, 4); + if (GSS_ERROR(major)) { + return major; + } + + for (i = 0, output_message_buffer->length = 0; i < 4; i++) { + output_message_buffer->length += iov[i].buffer.length; + } + + output_message_buffer->value = GSSEAP_MALLOC(output_message_buffer->length); + if (output_message_buffer->value == NULL) { + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + for (i = 0, p = output_message_buffer->value; i < 4; i++) { + if (iov[i].type == GSS_IOV_BUFFER_TYPE_DATA) { + memcpy(p, input_message_buffer->value, input_message_buffer->length); + } + iov[i].buffer.value = p; + p += iov[i].buffer.length; + } + + major = gssEapWrapOrGetMIC(minor, ctx, conf_req_flag, conf_state, + iov, 4, TOK_TYPE_WRAP); + if (GSS_ERROR(major)) { + gss_release_buffer(&tmpMinor, output_message_buffer); + } + + return major; +} diff --git a/wrap_iov.c b/wrap_iov.c new file mode 100644 index 0000000..19f263c --- /dev/null +++ b/wrap_iov.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ +/* + * 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. + */ + +/* + * Message protection services: wrap with scatter-gather API. + */ + +#include "gssapiP_eap.h" + +unsigned char +rfc4121Flags(gss_ctx_id_t ctx, int receiving) +{ + unsigned char flags; + int isAcceptor; + + isAcceptor = !CTX_IS_INITIATOR(ctx); + if (receiving) + isAcceptor = !isAcceptor; + + flags = 0; + if (isAcceptor) + flags |= TOK_FLAG_SENDER_IS_ACCEPTOR; + + if ((ctx->flags & CTX_FLAG_KRB_REAUTH) && + (ctx->gssFlags & GSS_C_MUTUAL_FLAG)) + flags |= TOK_FLAG_ACCEPTOR_SUBKEY; + + return flags; +} + +OM_uint32 +gssEapWrapOrGetMIC(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count, + enum gss_eap_token_type toktype) +{ + krb5_error_code code = 0; + gss_iov_buffer_t header; + gss_iov_buffer_t padding; + gss_iov_buffer_t trailer; + unsigned char flags; + unsigned char *outbuf = NULL; + unsigned char *tbuf = NULL; + int keyUsage; + size_t rrc = 0; + size_t gssHeaderLen, gssTrailerLen; + size_t dataLen, assocDataLen; + krb5_context krbContext; +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto = NULL; +#endif + + if (ctx->encryptionType == ENCTYPE_NULL) { + *minor = GSSEAP_KEY_UNAVAILABLE; + return GSS_S_UNAVAILABLE; + } + + GSSEAP_KRB_INIT(&krbContext); + + flags = rfc4121Flags(ctx, FALSE); + + if (toktype == TOK_TYPE_WRAP) { + keyUsage = CTX_IS_INITIATOR(ctx) + ? KEY_USAGE_INITIATOR_SEAL + : KEY_USAGE_ACCEPTOR_SEAL; + } else { + keyUsage = CTX_IS_INITIATOR(ctx) + ? KEY_USAGE_INITIATOR_SIGN + : KEY_USAGE_ACCEPTOR_SIGN; + } + + gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen); + + header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); + if (header == NULL) { + *minor = GSSEAP_MISSING_IOV; + return GSS_S_FAILURE; + } + + padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding != NULL) + padding->buffer.length = 0; + + trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto); + if (code != 0) + goto cleanup; +#endif + + if (toktype == TOK_TYPE_WRAP && conf_req_flag) { + size_t krbHeaderLen, krbTrailerLen, krbPadLen; + size_t ec = 0, confDataLen = dataLen - assocDataLen; + + code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen); + if (code != 0) + goto cleanup; + + code = krbPaddingLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + confDataLen + 16 /* E(Header) */, + &krbPadLen); + if (code != 0) + goto cleanup; + + if (krbPadLen == 0 && (ctx->gssFlags & GSS_C_DCE_STYLE)) { + /* Windows rejects AEAD tokens with non-zero EC */ + code = krbBlockSize(krbContext, KRB_CRYPTO_CONTEXT(ctx), &ec); + if (code != 0) + goto cleanup; + } else + ec = krbPadLen; + + code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + KRB5_CRYPTO_TYPE_TRAILER, &krbTrailerLen); + if (code != 0) + goto cleanup; + + gssHeaderLen = 16 /* Header */ + krbHeaderLen; + gssTrailerLen = ec + 16 /* E(Header) */ + krbTrailerLen; + + if (trailer == NULL) { + rrc = gssTrailerLen; + /* Workaround for Windows bug where it rotates by EC + RRC */ + if (ctx->gssFlags & GSS_C_DCE_STYLE) + rrc -= ec; + gssHeaderLen += gssTrailerLen; + } + + if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) { + code = gssEapAllocIov(header, (size_t)gssHeaderLen); + } else if (header->buffer.length < gssHeaderLen) + code = GSSEAP_WRONG_SIZE; + if (code != 0) + goto cleanup; + outbuf = (unsigned char *)header->buffer.value; + header->buffer.length = (size_t)gssHeaderLen; + + if (trailer != NULL) { + if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = gssEapAllocIov(trailer, (size_t)gssTrailerLen); + else if (trailer->buffer.length < gssTrailerLen) + code = GSSEAP_WRONG_SIZE; + if (code != 0) + goto cleanup; + trailer->buffer.length = (size_t)gssTrailerLen; + } + + /* TOK_ID */ + store_uint16_be((uint16_t)toktype, outbuf); + /* flags */ + outbuf[2] = flags + | (conf_req_flag ? TOK_FLAG_WRAP_CONFIDENTIAL : 0); + /* filler */ + outbuf[3] = 0xFF; + /* EC */ + store_uint16_be(ec, outbuf + 4); + /* RRC */ + store_uint16_be(0, outbuf + 6); + store_uint64_be(ctx->sendSeq, 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 = gssEapEncrypt(krbContext, + ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0), + ec, rrc, KRB_CRYPTO_CONTEXT(ctx), + keyUsage, iov, iov_count); + if (code != 0) + goto cleanup; + + /* RRC */ + store_uint16_be(rrc, outbuf + 6); + + ctx->sendSeq++; + } else if (toktype == TOK_TYPE_WRAP && !conf_req_flag) { + wrap_with_checksum: + + gssHeaderLen = 16; + + code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + KRB5_CRYPTO_TYPE_CHECKSUM, &gssTrailerLen); + if (code != 0) + goto cleanup; + + assert(gssTrailerLen <= 0xFFFF); + + if (trailer == NULL) { + rrc = gssTrailerLen; + gssHeaderLen += gssTrailerLen; + } + + if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = gssEapAllocIov(header, (size_t)gssHeaderLen); + else if (header->buffer.length < gssHeaderLen) + code = GSSEAP_WRONG_SIZE; + if (code != 0) + goto cleanup; + outbuf = (unsigned char *)header->buffer.value; + header->buffer.length = (size_t)gssHeaderLen; + + if (trailer != NULL) { + if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) + code = gssEapAllocIov(trailer, (size_t)gssTrailerLen); + else if (trailer->buffer.length < gssTrailerLen) + code = GSSEAP_WRONG_SIZE; + if (code != 0) + goto cleanup; + trailer->buffer.length = (size_t)gssTrailerLen; + } + + /* TOK_ID */ + store_uint16_be((uint16_t)toktype, outbuf); + /* flags */ + outbuf[2] = flags; + /* filler */ + outbuf[3] = 0xFF; + if (toktype == TOK_TYPE_WRAP) { + /* Use 0 for checksum calculation, substitute + * checksum length later. + */ + /* EC */ + store_uint16_be(0, outbuf + 4); + /* RRC */ + store_uint16_be(0, outbuf + 6); + } else { + /* MIC and DEL store 0xFF in EC and RRC */ + store_uint16_be(0xFFFF, outbuf + 4); + store_uint16_be(0xFFFF, outbuf + 6); + } + store_uint64_be(ctx->sendSeq, outbuf + 8); + + code = gssEapSign(krbContext, ctx->checksumType, rrc, + KRB_CRYPTO_CONTEXT(ctx), keyUsage, + iov, iov_count); + if (code != 0) + goto cleanup; + + ctx->sendSeq++; + + if (toktype == TOK_TYPE_WRAP) { + /* Fix up EC field */ + store_uint16_be(gssTrailerLen, outbuf + 4); + /* Fix up RRC field */ + store_uint16_be(rrc, outbuf + 6); + } + } else if (toktype == TOK_TYPE_MIC) { + trailer = NULL; + goto wrap_with_checksum; + } else if (toktype == TOK_TYPE_DELETE_CONTEXT) { + trailer = NULL; + goto wrap_with_checksum; + } else { + abort(); + } + + code = 0; + if (conf_state != NULL) + *conf_state = conf_req_flag; + +cleanup: + if (code != 0) + gssEapReleaseIov(iov, iov_count); +#ifdef HAVE_HEIMDAL_VERSION + if (krbCrypto != NULL) + krb5_crypto_destroy(krbContext, krbCrypto); +#endif + + *minor = code; + + return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE; +} + +OM_uint32 +gss_wrap_iov(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major; + + if (ctx == GSS_C_NO_CONTEXT) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + if (qop_req != GSS_C_QOP_DEFAULT) { + *minor = GSSEAP_UNKNOWN_QOP; + return GSS_S_UNAVAILABLE; + } + + *minor = 0; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (!CTX_IS_ESTABLISHED(ctx)) { + major = GSS_S_NO_CONTEXT; + *minor = GSSEAP_CONTEXT_INCOMPLETE; + goto cleanup; + } + + major = gssEapWrapOrGetMIC(minor, ctx, conf_req_flag, conf_state, + iov, iov_count, TOK_TYPE_WRAP); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return major; +} diff --git a/wrap_iov_length.c b/wrap_iov_length.c new file mode 100644 index 0000000..134ecc8 --- /dev/null +++ b/wrap_iov_length.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ +/* + * 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. + */ + +/* + * Message protection services: determine protected message size. + */ + +#include "gssapiP_eap.h" + +#define INIT_IOV_DATA(_iov) do { (_iov)->buffer.value = NULL; \ + (_iov)->buffer.length = 0; } \ + while (0) + +OM_uint32 +gssEapWrapIovLength(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + gss_iov_buffer_t header, trailer, padding; + size_t dataLength, assocDataLength; + size_t gssHeaderLen, gssPadLen, gssTrailerLen; + size_t krbHeaderLen = 0, krbTrailerLen = 0, krbPadLen = 0; + krb5_error_code code; + krb5_context krbContext; + int dce_style; + size_t ec; +#ifdef HAVE_HEIMDAL_VERSION + krb5_crypto krbCrypto = NULL; +#endif + + if (qop_req != GSS_C_QOP_DEFAULT) { + *minor = GSSEAP_UNKNOWN_QOP; + return GSS_S_UNAVAILABLE; + } + + if (ctx->encryptionType == ENCTYPE_NULL) { + *minor = GSSEAP_KEY_UNAVAILABLE; + return GSS_S_UNAVAILABLE; + } + + GSSEAP_KRB_INIT(&krbContext); + + header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER); + if (header == NULL) { + *minor = GSSEAP_MISSING_IOV; + return GSS_S_FAILURE; + } + INIT_IOV_DATA(header); + + trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER); + if (trailer != NULL) { + INIT_IOV_DATA(trailer); + } + + dce_style = ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0); + + /* For CFX, EC is used instead of padding, and is placed in header or trailer */ + padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING); + if (padding != NULL) { + INIT_IOV_DATA(padding); + } + + gssEapIovMessageLength(iov, iov_count, &dataLength, &assocDataLength); + + if (conf_req_flag && gssEapIsIntegrityOnly(iov, iov_count)) + conf_req_flag = FALSE; + + gssHeaderLen = gssPadLen = gssTrailerLen = 0; + +#ifdef HAVE_HEIMDAL_VERSION + code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto); + if (code != 0) + return code; +#endif + + code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + conf_req_flag ? + KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM, + &krbTrailerLen); + if (code != 0) { + *minor = code; + return GSS_S_FAILURE; + } + + if (conf_req_flag) { + code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen); + if (code != 0) { + *minor = code; + return GSS_S_FAILURE; + } + } + + gssHeaderLen = 16; /* Header */ + if (conf_req_flag) { + gssHeaderLen += krbHeaderLen; /* Kerb-Header */ + gssTrailerLen = 16 /* E(Header) */ + krbTrailerLen; /* Kerb-Trailer */ + + code = krbPaddingLength(krbContext, KRB_CRYPTO_CONTEXT(ctx), + dataLength - assocDataLength + 16 /* E(Header) */, + &krbPadLen); + if (code != 0) { + *minor = code; + return GSS_S_FAILURE; + } + + if (krbPadLen == 0 && dce_style) { + /* Windows rejects AEAD tokens with non-zero EC */ + code = krbBlockSize(krbContext, KRB_CRYPTO_CONTEXT(ctx), &ec); + if (code != 0) { + *minor = code; + return GSS_S_FAILURE; + } + } else + ec = krbPadLen; + + gssTrailerLen += ec; + } else { + gssTrailerLen = krbTrailerLen; /* Kerb-Checksum */ + } + + dataLength += gssPadLen; + + if (trailer == NULL) + gssHeaderLen += gssTrailerLen; + else + trailer->buffer.length = gssTrailerLen; + + assert(gssPadLen == 0 || padding != NULL); + + if (padding != NULL) + padding->buffer.length = gssPadLen; + + header->buffer.length = gssHeaderLen; + + if (conf_state != NULL) + *conf_state = conf_req_flag; + + *minor = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gss_wrap_iov_length(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + gss_qop_t qop_req, + int *conf_state, + gss_iov_buffer_desc *iov, + int iov_count) +{ + OM_uint32 major; + + if (ctx == GSS_C_NO_CONTEXT) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + *minor = 0; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (!CTX_IS_ESTABLISHED(ctx)) { + major = GSS_S_NO_CONTEXT; + *minor = GSSEAP_CONTEXT_INCOMPLETE; + goto cleanup; + } + + major = gssEapWrapIovLength(minor, ctx, conf_req_flag, qop_req, + conf_state, iov, iov_count); + if (GSS_ERROR(major)) + goto cleanup; + +cleanup: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return major; +} diff --git a/wrap_size_limit.c b/wrap_size_limit.c new file mode 100644 index 0000000..37356ce --- /dev/null +++ b/wrap_size_limit.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2010, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be 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 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. + */ + +/* + * Message protection services: determine maximum input size. + */ + +#include "gssapiP_eap.h" + +OM_uint32 +gss_wrap_size_limit(OM_uint32 *minor, + gss_ctx_id_t ctx, + int conf_req_flag, + gss_qop_t qop_req, + OM_uint32 req_output_size, + OM_uint32 *max_input_size) +{ + gss_iov_buffer_desc iov[4]; + OM_uint32 major, overhead; + + if (ctx == GSS_C_NO_CONTEXT) { + *minor = EINVAL; + return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT; + } + + *minor = 0; + + GSSEAP_MUTEX_LOCK(&ctx->mutex); + + if (!CTX_IS_ESTABLISHED(ctx)) { + major = GSS_S_NO_CONTEXT; + *minor = GSSEAP_CONTEXT_INCOMPLETE; + goto cleanup; + } + + iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; + iov[0].buffer.value = NULL; + iov[0].buffer.length = 0; + + iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; + iov[1].buffer.length = req_output_size; + iov[1].buffer.value = NULL; + + iov[2].type = GSS_IOV_BUFFER_TYPE_PADDING; + iov[2].buffer.value = NULL; + iov[2].buffer.length = 0; + + iov[3].type = GSS_IOV_BUFFER_TYPE_TRAILER; + iov[3].buffer.value = NULL; + iov[3].buffer.length = 0; + + major = gssEapWrapIovLength(minor, ctx, conf_req_flag, qop_req, + NULL, iov, 4); + if (GSS_ERROR(major)) + goto cleanup; + + overhead = iov[0].buffer.length + iov[3].buffer.length; + + if (iov[2].buffer.length == 0 && overhead < req_output_size) + *max_input_size = req_output_size - overhead; + else + *max_input_size = 0; + +cleanup: + GSSEAP_MUTEX_UNLOCK(&ctx->mutex); + + return major; +}