/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Security Provider glue
*
* Modeled after SSPI for now, only because we're currently
* using the Microsoft sample spnego code.
*
* ToDo: Port all of this to GSS-API plugins.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <netdb.h>
#include <libintl.h>
#include <xti.h>
#include <assert.h>
#include <sys/byteorder.h>
#include "private.h"
#include "charsets.h"
#include "spnego.h"
#include "derparse.h"
#include "ssp.h"
/*
* ssp_ctx_create_client
*
* This is the first function called for SMB "extended security".
* Here we select a security support provider (SSP), or mechanism,
* and build the security context used throughout authentication.
*
* Note that we receive a "hint" in the SMB Negotiate response
* that contains the list of mechanisms supported by the server.
* We use this to help us select a mechanism.
*
* With SSPI this would call:
* ssp->InitSecurityInterface()
* ssp->AcquireCredentialsHandle()
* ssp->InitializeSecurityContext()
* With GSS-API this will become:
* gss_import_name(... service_principal_name)
* gss_init_sec_context(), etc.
*/
int
{
mbuf_t *m;
return (ENOMEM);
/*
* Parse the SPNEGO "hint" to get the server's list of
* supported mechanisms. If the "hint" is empty,
* assume NTLMSSP. (Or could use "raw NTLMSSP")
*/
if (m == NULL)
goto use_ntlm;
if (rc) {
goto use_ntlm;
}
/*
* Did the server offer Kerberos?
* Either spec. OID or legacy is OK,
* but have to remember what we got.
*/
if (oid != spnego_mech_oid_NotUsed) {
/*
* Yes! Server offers Kerberos.
* Try to init our krb5 mechanism.
* It will fail if the calling user
* does not have krb5 credentials.
*/
if (err == 0) {
DPRINT("using Kerberos");
return (0);
}
/* else fall back to NTLMSSP */
}
/*
* Did the server offer NTLMSSP?
*/
spnego_mech_oid_NTLMSSP, &indx)) {
/*
* OK, we'll use NTLMSSP
*/
if (err == 0) {
DPRINT("using NTLMSSP");
return (0);
}
}
/* No supported mechanisms! */
return (err);
}
/*
* ssp_ctx_destroy
*
* Dispatch to the mechanism-specific destroy.
*/
void
{
return;
}
/*
* ssp_ctx_next_token
*
* This is the function called to generate the next token to send,
* given a token just received, using the selected back-end method.
* The back-end method is called a security service provider (SSP).
*
* This is also called to generate the first token to send
* (when called with caller_in == NULL) and to handle the last
* token received (when called with caller_out == NULL).
* See caller: smb_ssnsetup_spnego
*
* Note that if the back-end SSP "next token" function ever
* returns an error, the conversation ends, and there are
* no further calls to this function for this context.
*
* General outline of this funcion:
* if (caller_in)
* Unwrap caller_in spnego blob,
* store payload in body_in
* Call back-end SSP "next token" method (body_in, body_out)
* if (caller_out)
* Wrap returned body_out in spnego,
* store in caller_out
*
* With SSPI this would call:
* ssp->InitializeSecurityContext()
* With GSS-API this will become:
* gss_init_sec_context()
*/
int
struct mbdata *caller_out)
{
struct mbuf *m;
/*
* If we have an spnego input token, parse it,
* extract the payload for the back-end SSP.
*/
/*
* Let the spnego code parse it.
*/
if (rc) {
goto out;
}
/* Note: Allocated stok_in */
/*
* Now get the payload. Two calls:
* first gets the size, 2nd the data.
*
* Expect SPNEGO_E_BUFFER_TOO_SMALL here,
* but if the payload is missing, we'll
* get SPNEGO_E_ELEMENT_UNAVAILABLE.
*/
switch (rc) {
toklen = 0;
break;
/* have toklen */
break;
default:
goto out;
}
if (err)
goto out;
if (toklen > 0) {
if (rc) {
goto out;
}
}
}
/*
* Call the back-end security provider (SSP) to
* handle the received token (if present) and
* generate an output token (if requested).
*/
if (err)
goto out;
/*
* Wrap the outgoing body if requested,
* either negTokenInit on first call, or
* negTokenTarg on subsequent calls.
*/
if (caller_out != NULL) {
/*
* This is the first call, so create a
* negTokenInit.
*/
/* Note: allocated stok_out */
} else {
/*
* Note: must pass spnego_mech_oid_NotUsed,
* instead of sp->sp_mech so that the spnego
* code will not marshal a mech OID list.
* The mechanism is determined at this point,
* and some servers won't parse an unexpected
* mech. OID list in a negTokenTarg
*/
/* Note: allocated stok_out */
}
if (rc) {
goto out;
}
/*
* Copy binary from stok_out to caller_out
* Two calls: get the size, get the data.
*/
if (rc != SPNEGO_E_BUFFER_TOO_SMALL) {
goto out;
}
if (err)
goto out;
m = caller_out->mb_top;
if (rc) {
goto out;
}
} else {
/*
* caller_out == NULL, so this is the "final" call.
* Get final SPNEGO result from the INPUT token.
*/
if (rc) {
goto out;
}
if (result != spnego_negresult_success) {
goto out;
}
}
err = 0;
out:
return (err);
}