2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. 2N/A * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology. 2N/A * All rights reserved. 2N/A * Export of this software from the United States of America may 2N/A * require a specific license from the United States Government. 2N/A * It is the responsibility of any person or organization contemplating 2N/A * export to obtain such a license before exporting. 2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 2N/A * distribute this software and its documentation for any purpose and 2N/A * without fee is hereby granted, provided that the above copyright 2N/A * notice appear in all copies and that both that copyright notice and 2N/A * this permission notice appear in supporting documentation, and that 2N/A * the name of M.I.T. not be used in advertising or publicity pertaining 2N/A * to distribution of the software without specific, written prior 2N/A * permission. Furthermore if you modify this software you must label 2N/A * your software as modified software and not distribute it in such a 2N/A * fashion that it might be confused with the original M.I.T. software. 2N/A * M.I.T. makes no representations about the suitability of 2N/A * this software for any purpose. It is provided "as is" without express 2N/A * or implied warranty. 2N/A * A module that implements the spnego security mechanism. 2N/A * It is used to negotiate the security mechanism between 2N/A * peers using the GSS-API. 2N/A * Copyright (c) 2006-2008, Novell, Inc. 2N/A * All rights reserved. 2N/A * Redistribution and use in source and binary forms, with or without 2N/A * modification, are permitted provided that the following conditions are met: 2N/A * * Redistributions of source code must retain the above copyright notice, 2N/A * this list of conditions and the following disclaimer. 2N/A * * Redistributions in binary form must reproduce the above copyright 2N/A * notice, this list of conditions and the following disclaimer in the 2N/A * documentation and/or other materials provided with the distribution. 2N/A * * The copyright holder's name is not used to endorse or promote products 2N/A * derived from this software without specific prior written permission. 2N/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2N/A * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2N/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2N/A * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 2N/A * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2N/A * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2N/A * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2N/A * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2N/A * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2N/A * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2N/A * POSSIBILITY OF SUCH DAMAGE. 2N/A * MIT has diff names for these GSS utilities. Solaris needs to change 2N/A * them globally to get in sync w/MIT. 2N/A * Revisit for full 1.7 resync. 2N/A/* der routines defined in libgss */ 2N/A/* private routines for spnego_mechanism */ 2N/A unsigned char **,
unsigned int);
2N/A/* SPNEGO oid structure */ 2N/A/* encoded OID octet string for NTLMSSP security mechanism */ 2N/A#
endif /* _GSS_STATIC_LINK */ 2N/A * The Mech OID for SPNEGO: 2N/A * { iso(1) org(3) dod(6) internet(1) security(5) 2N/A * mechanism(5) spnego(2) } 2N/A#
endif /* LEAN_CLIENT */ 2N/A NULL,
/* gss_process_context_token */ 2N/A/* EXPORT DELETE START */ /* CRYPT DELETE START */ 2N/A/* EXPORT DELETE END */ /* CRYPT DELETE END */ 2N/A/* EXPORT DELETE START */ /* CRYPT DELETE START */ 2N/A/* EXPORT DELETE END */ /* CRYPT DELETE END */ 2N/A#
endif /* LEAN_CLIENT */ 2N/A NULL,
/* gss_inquire_cred_by_mech */ 2N/A NULL,
/* gss_internal_release_oid */ 2N/A/* EXPORT DELETE START */ 2N/A/* CRYPT DELETE START */ 2N/A/* CRYPT DELETE END */ 2N/A/* CRYPT DELETE START */ 2N/A/* CRYPT DELETE END */ 2N/A/* EXPORT DELETE END */ 2N/A NULL,
/* gss_inquire_cred_by_oid */ 2N/A NULL,
/* gss_set_sec_context_option */ 2N/A/* EXPORT DELETE START */ /* CRYPT DELETE START */ 2N/A/* EXPORT DELETE END */ /* CRYPT DELETE END */ 2N/A NULL,
/* gss_acquire_cred_impersonate_name */ 2N/A NULL,
/* gss_delete_name_attribute */ 2N/A NULL,
/* gss_export_name_composite */ 2N/A NULL,
/* gss_release_any_name_mapping */ 2N/A/* Entry point for libgss */ 2N/A "SPNEGO gss_mech_initialize: error message TSD key register fail");
2N/A#
if 0
/* SUNW17PACresync */ 2N/A#
endif /* _GSS_STATIC_LINK */ 2N/A "SPNEGO gss_mech_initialize: error message TSD key register fail: err=%d",
2N/A * If the user did not specify a list of mechs, 2N/A * use get_available_mechs to collect a list of 2N/A * mechs for which creds are available. 2N/A * The caller gave a specific list of mechanisms, 2N/A * so just get whatever creds are available. 2N/A * gss_acquire_creds will return the subset of mechs for 2N/A * which the given 'output_cred_handle' is valid. 2N/A * Both initiator and acceptor call here to verify and/or create 2N/A * mechListMIC, and to consistency-check the MIC state. 2N/A /* Reject MIC if we've already received a MIC. */ 2N/A * If the peer sends the final mechanism token, it 2N/A * must send the MIC with that token if the 2N/A * negotiation requires MICs. 2N/A * We sent a MIC on the previous pass; we 2N/A * shouldn't be sending a mechanism token. 2N/A * Perform the actual verification and/or generation of mechListMIC. 2N/A /* If we got a MIC, we must send a MIC. */ 2N/A * Initial call to spnego_gss_init_sec_context(). 2N/A /* determine negotiation mech set */ 2N/A * Use the list of mechs included in the cred that we 2N/A * need to pull the first mech from mechSet to do first 2N/A * gss_init_sec_context() 2N/A * The actual context is not yet determined, set the output 2N/A * context handle to refer to the spnego context itself. 2N/A * Called by second and later calls to spnego_gss_init_sec_context() 2N/A * to decode reply and update state. 2N/A /* Reject "empty" token. */ 2N/A /* Solaris SPNEGO */ 2N/A "SPNEGO failed to negotiate a mechanism: server rejected request"));
2N/A * nego_done is false for the first call to init_ctx_cont() 2N/A * mech not finished and mech token missing 2N/A * Consistency checking and mechanism negotiation handling for second 2N/A * call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to 2N/A * update internal state if acceptor has counter-proposed. 2N/A * Both supportedMech and negState must be present in first 2N/A /* Solaris SPNEGO */ 2N/A "SPNEGO failed to negotiate a mechanism: defective token"));
2N/A * If the mechanism we sent is not the mechanism returned from 2N/A * the server, we need to handle the server's counter 2N/A * proposal. There is a bug in SAMBA servers that always send 2N/A * the old Kerberos mech OID, even though we sent the new one. 2N/A * So we will treat all the Kerberos mech OIDS as the same. 2N/A * Mech completed on first call to its 2N/A * init_sec_context(). Acceptor sends no mech 2N/A * Reject missing mech token when optimistic 2N/A /* Reject spurious mech token. */ 2N/A * Handle acceptor's counter-proposal of an alternative mechanism. 2N/A /* Reject spurious mech token. */ 2N/A * Windows 2003 and earlier don't correctly send a 2N/A * negState of request-mic when counter-proposing a 2N/A * mechanism. They probably don't handle mechListMICs 2N/A * Wrap call to mechanism gss_init_sec_context() and update state 2N/A * If this isn't the first time we've been called, 2N/A * we're done unless a MIC needs to be 2N/A /* Don't output token on error if first call. */ 2N/A * send_token is used to indicate in later steps 2N/A * what type of token, if any should be sent or processed. 2N/A * NO_TOKEN_SEND = no token should be sent 2N/A * INIT_TOKEN_SEND = initial token will be sent 2N/A * CONT_TOKEN_SEND = continuing tokens to be sent 2N/A * CHECK_MIC = no token to be sent, but have a MIC to check. 2N/A /* Solaris SPNEGO */ 2N/A * Now, switch the output context to refer to the 2N/A * negotiated mechanism's context. 2N/A}
/* init_sec_context */ 2N/A/* We don't want to import KRB5 headers here */ 2N/A {
9,
"\052\206\110\206\367\022\001\002\002" };
2N/A {
9,
"\052\206\110\202\367\022\001\002\002" };
2N/A * verify that the input token length is not 0. If it is, just return. 2N/A * If the token length is greater than 0, der encode as a sequence 2N/A * and place in buf_out, advancing buf_out. 2N/A /* if token length is 0, we do not want to send */ 2N/A * NegHints ::= SEQUENCE { 2N/A * hintName [0] GeneralString OPTIONAL, 2N/A * hintAddress [1] OCTET STRING OPTIONAL 2N/A /* this breaks mutual authentication but Samba relies on it */ 2N/A /* Solaris SPNEGO */ 2N/A * Now encode the name hint into a NegHints ASN.1 type 2N/A /* Length of DER encoded GeneralString */ 2N/A /* Length of DER encoded hintName */ 2N/A /* Solaris SPNEGO */ 2N/A * Select the best match between the list of mechs 2N/A * that the initiator requested and the list that 2N/A * the acceptor will support. 2N/A * Input an OID set of mechs and output a string like so: 2N/A * '{ x y z } (mechname0), { a b c } (mechname1) ...'. 2N/A * On error return NULL. 2N/A * Caller needs to free returned string. 2N/A /* No need to free mech_name. */ 2N/A /* Add '{ x y x ... }'. */ 2N/A /* Add '(mech name)'. */ 2N/A /* Even if we have buf overflow, let's output what we got so far. */ 2N/A * Set negState to REJECT if the token is defective, else 2N/A * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's 2N/A * preferred mechanism is supported. 2N/A * Select the best match between the list of mechs 2N/A * that the initiator requested and the list that 2N/A * the acceptor will support. 2N/A /* Solaris SPNEGO: Spruce-up error msg */ 2N/A "SPNEGO failed to negotiate a mechanism: client requested mech set '%s'"),
2N/A * We save error here cuz the tmp ctx goes away (very) soon. 2N/A * So callers of acc_ctx_new() should NOT call it again. 2N/A * Attempt to work with old Sun SPNEGO. 2N/A * Verify that mech OID is either exactly the same as the negotiated 2N/A * mech OID, or is a mech OID supported by the negotiated mech. MS 2N/A * implementations can list a most preferred mech using an incorrect 2N/A * krb5 OID while emitting a krb5 initiator mech token having the 2N/A * correct krb5 mech OID. 2N/A * If both mechs are kerb, we are done. 2N/A * Spruce-up error msg. 2N/A /* No need to free mnamestr. */ 2N/A "SPNEGO failed to negotiate a mechanism: unsupported mech OID ('%s') in the token. Negotiated mech OID is '%s' (%s)"),
2N/A * Spruce-up error msg. 2N/A /* No need to free mnamestr. */ 2N/A "SPNEGO failed to negotiate a mechanism: negotiated mech OID '%s' (%s) not found in mechset ('%s') of token mech"),
2N/A * Wrap call to gss_accept_sec_context() and update state 2N/A * mechoid is an alias; don't free it. 2N/A * Force MIC to be not required even if we previously 2N/A /* Can set negState to REQUEST_MIC */ 2N/A /* Can set negState to ACCEPT_INCOMPLETE */ 2N/A * Handle mechtok_in and mic_in only if they are 2N/A * present in input_token. If neither is present, whether 2N/A * this is an error depends on whether this is the first 2N/A * round-trip. RET is set to a default value according to 2N/A * whether it is the first round-trip. 2N/A /* Solaris SPNEGO */ 2N/A#
endif /* LEAN_CLIENT */ 2N/A * If mech_spnego calls mech_krb5 (via libgss) and an 2N/A * error occurs there, give it a shot. 2N/A * We only know how to handle our own mechanism. 2N/A /* Now add our members. */ 2N/A * If this is still an SPNEGO mech, release it locally. 2N/A /* SUNW17PACresync - MIT 1.7 bug (and our fix) */ 2N/A#
endif /* LEAN_CLIENT */ 2N/A * These GSS funcs not needed yet, so disable them. 2N/A * Revisit for full 1.7 resync. 2N/A * We will release everything but the ctx_handle so that it 2N/A * not be called until after the ctx_handle memory is assigned to 2N/A /* Solaris SPNEGO */ 2N/A * Can't use gss_indicate_mechs by itself to get available mechs for 2N/A * SPNEGO because it will also return the SPNEGO mech and we do not 2N/A * want to consider SPNEGO as an available security mech for 2N/A * negotiation. For this reason, get_available_mechs will return 2N/A * all available mechs except SPNEGO. 2N/A * If a ptr to a creds list is given, this function will attempt 2N/A * to acquire creds for the creds given and trim the list of 2N/A * returned mechanisms to only those for which creds are valid. 2N/A * Solaris SPNEGO: gss_indicate_mechs is stupid as 2N/A * it never inferences any of the related OIDs of the 2N/A * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG. 2N/A * We add KRB5_WRONG here so that old MS clients can 2N/A * negotiate this mechanism, which allows extensions 2N/A * in Kerberos (clock skew adjustment, refresh ccache). 2N/A * Add NTLMSSP OID to the mech OID set only if MS_INTEROP env var 2N/A * This is a requirement until NTLMSSP is implemented as a GSS-API 2N/A * If the caller wanted a list of creds returned, 2N/A * trim the list of mechanisms down to only those 2N/A * for which the creds are valid. 2N/A * Drop the old list in favor of the new 2N/A/* following are token creation and reading routines */ 2N/A * If buff_in is not pointing to a MECH_OID, then return NULL and do not 2N/A * advance the buffer, otherwise, decode the mech_oid from the buffer and 2N/A * der encode the given mechanism oid into buf_out, advancing the 2N/A * verify that buff_in points to an octet string, if it does not, 2N/A * return NULL and don't advance the pointer. If it is an octet string 2N/A * decode buff_in into a gss_buffer_t and return it, advancing the 2N/A * verify that the input token length is not 0. If it is, just return. 2N/A * If the token length is greater than 0, der encode as an octet string 2N/A * and place in buf_out, advancing buf_out. 2N/A /* if token length is 0, we do not want to send */ 2N/A * verify that buff_in points to a sequence of der encoding. The mech 2N/A * set is the only sequence of encoded object in the token, so if it is 2N/A * a sequence of encoding, decode the mechset into a gss_OID_set and 2N/A * return it, advancing the buffer pointer. 2N/A if (
length < 0)
/* SUNW17PACresync - MIT17 lacks this check */ 2N/A * Encode mechSet into buf. 2N/A * 0x06 [DER LEN] [OID] 2N/A * Verify that buff_in is pointing to a BIT_STRING with the correct 2N/A * length and padding for the req_flags. If it is, decode req_flags 2N/A * and return them, otherwise, return NULL. 2N/A /* alias into input_token */ 2N/A * der encode the passed negResults as an ENUMERATED type and 2N/A * place it in buf_out, advancing the buffer. 2N/A * This routine compares the recieved mechset to the mechset that 2N/A * this server can support. It looks sequentially through the mechset 2N/A * and the first one that matches what the server can support is 2N/A * chosen as the negotiated mechanism. If one is found, negResult 2N/A * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if 2N/A * it's not the first mech, otherwise we return NULL and negResult 2N/A * NOTE: There is currently no way to specify a preference order of 2N/A * mechanisms supported by the acceptor. 2N/A * Solaris SPNEGO: MIT compares against MS' wrong OID, but 2N/A * we actually want to select it if the client supports, as this 2N/A * will enable features on MS clients that allow credential 2N/A * refresh on rekeying and caching system times from servers. 2N/A /* Accept wrong mechanism OID from MS clients */ 2N/A /* Solaris SPNEGO */ 2N/A * the next two routines make a token buffer suitable for 2N/A * spnego_gss_display_status. These currently take the string 2N/A * in name and place it in the token. Eventually, if 2N/A * spnego_gss_display_status returns valid error messages, 2N/A * these routines will be changes to return the error string. 2N/A * Create the client side spnego token passed back to gss_init_sec_context 2N/A * and eventually up to the application program and over to the server. 2N/A * Use DER rules, definite length method per RFC 2478 2N/A /* calculate the data length */ 2N/A * 0xa0 [DER LEN] [mechTypes] 2N/A * If a token from gss_init_sec_context exists, 2N/A * add the length of the token + the ASN.1 overhead 2N/A * Encoded in final output as: 2N/A * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA] 2N/A * -----s--------|--------s2---------- 2N/A * Encoded in final output as: 2N/A * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA] 2N/A * --s-- -----tlen------------ 2N/A * Add size of DER encoding 2N/A * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ] 2N/A * 0x30 [DER_LEN] [data] 2N/A * negTokenInitSize indicates the bytes needed to 2N/A * hold the ASN.1 encoding of the entire NegTokenInit 2N/A * 0xa0 [DER_LEN] + data 2N/A /* create the message */ 2N/A /* We already encoded the MechSetList */ 2N/A * create the server side spnego token passed back to 2N/A * gss_accept_sec_context and eventually up to the application program 2N/A * and over to the client. 2N/A * ASN.1 encoding of the negResult 2N/A * ENUMERATED type is 3 bytes 2N/A * ENUMERATED TAG, Length, Value, 2N/A * Plus 2 bytes for the CONTEXT id and length. 2N/A * calculate data length 2N/A * If this is the initial token, include length of 2N/A * mech_type and the negotiation result fields. 2N/A * 1 byte for the CONTEXT ID(0xa0), 2N/A * 1 byte for the OID ID(0x06) 2N/A * 1 byte for OID Length field 2N/A * Plus the rest... (OID Length, OID value) 2N/A /* Length of the inner token */ 2N/A /* Length of the outer token */ 2N/A /* Length of the inner token */ 2N/A /* Length of the outer token */ 2N/A * Add size of DER encoded: 2N/A * NegTokenTarg [ SEQUENCE ] of 2N/A * NegResult[0] ENUMERATED { 2N/A * accept_completed(0), 2N/A * accept_incomplete(1), 2N/A * supportedMech [1] MechType OPTIONAL, 2N/A * responseToken [2] OCTET STRING OPTIONAL, 2N/A * mechListMIC [3] OCTET STRING OPTIONAL 2N/A * size = data->length + MechListMic + SupportedMech len + 2N/A * Result Length + ASN.1 overhead 2N/A * NegotiationToken [ CHOICE ]{ 2N/A * negTokenInit [0] NegTokenInit, 2N/A * negTokenTarg [1] NegTokenTarg } 2N/A * Indicate that we are sending CHOICE 1 2N/A * First field of the NegTokenTarg SEQUENCE 2N/A * is the ENUMERATED NegResult. 2N/A * Next, is the Supported MechType 2N/A/* determine size of token */ 2N/A * Initialize the header size to the 2N/A * MECH_OID byte + the bytes needed to indicate the 2N/A * length of the OID + the OID itself. 2N/A * 0x06 [MECHLENFIELD] MECHDATA 2N/A * Now add the bytes needed for the initial header 2N/A * 0x60 + [DER_LEN] + HDRSIZE 2N/A * generate token header. 2N/A * Use DER Definite Length method per RFC2478 2N/A * Use of indefinite length encoding will not be compatible 2N/A * with Microsoft or others that actually follow the spec. 2N/A * NOTE: This checks that the length returned by 2N/A * gssint_get_der_length() is not greater than the number of octets 2N/A * remaining, even though gssint_get_der_length() already checks, in 2N/A int ret = -
1;
/* pessimists, assume failure ! */ 2N/A * Verify this is a NegotiationToken type token 2N/A * - check for a0(context specific identifier) 2N/A * - get length and verify that enoughd ata exists 2N/A * Verify the next piece, it should identify this as 2N/A * a strucure of type NegTokenInit. 2N/A * Make sure we have the entire buffer as described 2N/A * Verify that the first blob is a sequence of mechTypes 2N/A * Make sure we have the entire buffer as described 2N/A * At this point, *buf should be at the beginning of the 2N/A * DER encoded list of mech types that are to be negotiated. 2N/A/* verify token header. */ 2N/A * G_WRONG_MECH is not returned immediately because it's more important 2N/A * to return G_BAD_TOK_HEADER if the token header is in fact bad 2N/A * Return non-zero if the oid is one of the kerberos mech oids, 2N/A * otherwise return zero. 2N/A * N.B. There are 3 oids that represent the kerberos mech: 2N/A * RFC-specified GSS_MECH_KRB5_OID, 2N/A * Old pre-RFC GSS_MECH_KRB5_OLD_OID, 2N/A * Incorrect MS GSS_MECH_KRB5_WRONG_OID