/*
* 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
*/
/*
*/
/*
* 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 "smbfs_lib.h"
#include <smb/ntstatus.h>
#include "smbfs_private.h"
#include "smbfs_charsets.h"
#include "smbfs_derparse.h"
#include "smbfs_ssp.h"
/*
* smbfs_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.
*/
static 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 NTLMSSP?
*/
spnego_mech_oid_NTLMSSP, &indx)) {
/*
* OK, we'll use NTLMSSP
*/
if (err == 0) {
DPRINT("using NTLMSSP");
return (0);
}
}
/* No supported mechanisms! */
return (err);
}
/*
* smbfs_ssp_ctx_destroy
*
* Dispatch to the mechanism-specific destroy.
*/
static void
{
return;
}
/*
* smbfs_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()
*/
static 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);
}
/*
* smbfs_ssp_init_sec
*
* Establishes security context using security provider glue, roughly modeled
* on Microsoft SSPI. It will negotiate NTLMSSP authentication mechanism.
*/
int
{
int err;
if (err)
goto out;
/* NULL input indicates first call. */
if (err)
goto out;
for (;;) {
if (err)
goto out;
if (ntstatus == 0)
break;
if (ntstatus != NT_STATUS_MORE_PROCESSING_REQUIRED) {
goto out;
}
/* middle calls get both in, out */
if (err)
goto out;
}
/* NULL output indicates last call. */
out:
return (err);
}