1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-26 10:04:02 +03:00

This adds gss-spnego to ntlm_auth. It contains some new spnego support

from Jim McDonough. It is to enable cyrus sasl to provide the
gss-spnego support. For a preliminary patch to cyrus sasl see

http://samba.sernet.de/cyrus-gss-spnego.diff

Volker
This commit is contained in:
Volker Lendecke -
parent a4e342c20c
commit 45cef8f66e
5 changed files with 586 additions and 2 deletions

View File

@ -615,7 +615,8 @@ POPT_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
TDBBACKUP_OBJ = tdb/tdbbackup.o tdb/tdbback.o $(TDBBASE_OBJ)
NTLM_AUTH_OBJ = utils/ntlm_auth.o $(LIBSAMBA_OBJ) $(POPT_LIB_OBJ)
NTLM_AUTH_OBJ = utils/ntlm_auth.o $(LIBSAMBA_OBJ) $(POPT_LIB_OBJ) \
libsmb/asn1.o libsmb/spnego.o
######################################################################
# now the rules...

View File

@ -835,6 +835,8 @@ extern int errno;
#include "nsswitch/winbind_client.h"
#include "spnego.h"
/*
* Type for wide character dirent structure.
* Only d_name is defined by POSIX.

65
source/include/spnego.h Normal file
View File

@ -0,0 +1,65 @@
/*
Unix SMB/CIFS implementation.
RFC2478 Compliant SPNEGO implementation
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef SAMBA_SPNEGO_H
#define SAMBA_SPNEGO_H
#define SPNEGO_DELEG_FLAG 0x01
#define SPNEGO_MUTUAL_FLAG 0x02
#define SPNEGO_REPLAY_FLAG 0x04
#define SPNEGO_SEQUENCE_FLAG 0x08
#define SPNEGO_ANON_FLAG 0x10
#define SPNEGO_CONF_FLAG 0x20
#define SPNEGO_INTEG_FLAG 0x40
#define SPNEGO_REQ_FLAG 0x80
#define SPNEGO_NEG_TOKEN_INIT 0
#define SPNEGO_NEG_TOKEN_TARG 1
typedef enum _spnego_negResult {
SPNEGO_ACCEPT_COMPLETED = 0,
SPNEGO_ACCEPT_INCOMPLETE = 1,
SPNEGO_REJECT = 2
} negResult_t;
typedef struct spnego_negTokenInit {
char **mechTypes;
int reqFlags;
DATA_BLOB mechToken;
DATA_BLOB mechListMIC;
} negTokenInit_t;
typedef struct spnego_negTokenTarg {
uint8 negResult;
char *supportedMech;
DATA_BLOB responseToken;
DATA_BLOB mechListMIC;
} negTokenTarg_t;
typedef struct spnego_spnego {
int type;
negTokenInit_t negTokenInit;
negTokenTarg_t negTokenTarg;
} SPNEGO_DATA;
#endif

292
source/libsmb/spnego.c Normal file
View File

@ -0,0 +1,292 @@
/*
Unix SMB/CIFS implementation.
RFC2478 Compliant SPNEGO implementation
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_AUTH
static BOOL read_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
{
ZERO_STRUCTP(token);
asn1_start_tag(asn1, ASN1_CONTEXT(0));
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
int i;
switch (asn1->data[asn1->ofs]) {
/* Read mechTypes */
case ASN1_CONTEXT(0):
asn1_start_tag(asn1, ASN1_CONTEXT(0));
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
token->mechTypes = malloc(sizeof(*token->mechTypes));
for (i = 0; !asn1->has_error &&
0 < asn1_tag_remaining(asn1); i++) {
token->mechTypes =
realloc(token->mechTypes, (i + 1) *
sizeof(*token->mechTypes));
asn1_read_OID(asn1, token->mechTypes + i);
}
token->mechTypes[i] = NULL;
asn1_end_tag(asn1);
asn1_end_tag(asn1);
break;
/* Read reqFlags */
case ASN1_CONTEXT(1):
asn1_start_tag(asn1, ASN1_CONTEXT(1));
asn1_read_Integer(asn1, &token->reqFlags);
token->reqFlags |= SPNEGO_REQ_FLAG;
asn1_end_tag(asn1);
break;
/* Read mechToken */
case ASN1_CONTEXT(2):
asn1_start_tag(asn1, ASN1_CONTEXT(2));
asn1_read_OctetString(asn1, &token->mechToken);
asn1_end_tag(asn1);
break;
/* Read mecListMIC */
case ASN1_CONTEXT(3):
asn1_start_tag(asn1, ASN1_CONTEXT(3));
if (!asn1_read_OctetString(asn1, &token->mechListMIC)) {
char *mechListMIC;
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_read_GeneralString(asn1, &mechListMIC);
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
token->mechListMIC =
data_blob(mechListMIC, strlen(mechListMIC));
SAFE_FREE(mechListMIC);
}
asn1_end_tag(asn1);
break;
default:
asn1->has_error = True;
break;
}
}
asn1_end_tag(asn1);
asn1_end_tag(asn1);
return !asn1->has_error;
}
static BOOL write_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
{
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
/* Write mechTypes */
if (token->mechTypes && *token->mechTypes) {
int i;
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
for (i = 0; token->mechTypes[i]; i++) {
asn1_write_OID(asn1, token->mechTypes[i]);
}
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
}
/* write reqFlags */
if (token->reqFlags & SPNEGO_REQ_FLAG) {
int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
asn1_push_tag(asn1, ASN1_CONTEXT(1));
asn1_write_Integer(asn1, flags);
asn1_pop_tag(asn1);
}
/* write mechToken */
if (token->mechToken.data) {
asn1_push_tag(asn1, ASN1_CONTEXT(2));
asn1_write_OctetString(asn1, token->mechToken.data,
token->mechToken.length);
asn1_pop_tag(asn1);
}
/* write mechListMIC */
if (token->mechListMIC.data) {
asn1_push_tag(asn1, ASN1_CONTEXT(3));
asn1_write_OctetString(asn1, token->mechListMIC.data,
token->mechListMIC.length);
asn1_pop_tag(asn1);
}
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
return !asn1->has_error;
}
static BOOL read_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
{
ZERO_STRUCTP(token);
asn1_start_tag(asn1, ASN1_CONTEXT(1));
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
switch (asn1->data[asn1->ofs]) {
case ASN1_CONTEXT(0):
/* this is listed as being non-optional by RFC2478 but
Windows doesn't always send it... */
asn1_start_tag(asn1, ASN1_CONTEXT(0));
asn1_start_tag(asn1, ASN1_ENUMERATED);
asn1_read_uint8(asn1, &token->negResult);
asn1_end_tag(asn1);
asn1_end_tag(asn1);
break;
case ASN1_CONTEXT(1):
asn1_start_tag(asn1, ASN1_CONTEXT(1));
asn1_read_OID(asn1, &token->supportedMech);
asn1_end_tag(asn1);
break;
case ASN1_CONTEXT(2):
asn1_start_tag(asn1, ASN1_CONTEXT(2));
asn1_read_OctetString(asn1, &token->responseToken);
asn1_end_tag(asn1);
break;
case ASN1_CONTEXT(3):
asn1_start_tag(asn1, ASN1_CONTEXT(3));
asn1_read_OctetString(asn1, &token->mechListMIC);
asn1_end_tag(asn1);
break;
default:
asn1->has_error = True;
break;
}
}
asn1_end_tag(asn1);
asn1_end_tag(asn1);
return !asn1->has_error;
}
static BOOL write_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
{
asn1_push_tag(asn1, ASN1_CONTEXT(1));
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_write_enumerated(asn1, token->negResult);
asn1_pop_tag(asn1);
if (token->supportedMech) {
asn1_push_tag(asn1, ASN1_CONTEXT(1));
asn1_write_OID(asn1, token->supportedMech);
asn1_pop_tag(asn1);
}
if (token->responseToken.data) {
asn1_push_tag(asn1, ASN1_CONTEXT(2));
asn1_write_OctetString(asn1, token->responseToken.data,
token->responseToken.length);
asn1_pop_tag(asn1);
}
if (token->mechListMIC.data) {
asn1_push_tag(asn1, ASN1_CONTEXT(3));
asn1_write_OctetString(asn1, token->mechListMIC.data,
token->mechListMIC.length);
asn1_pop_tag(asn1);
}
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
return !asn1->has_error;
}
ssize_t read_spnego_data(DATA_BLOB data, SPNEGO_DATA *token)
{
ASN1_DATA asn1;
ssize_t ret = -1;
ZERO_STRUCTP(token);
ZERO_STRUCT(asn1);
asn1_load(&asn1, data);
switch (asn1.data[asn1.ofs]) {
case ASN1_APPLICATION(0):
asn1_start_tag(&asn1, ASN1_APPLICATION(0));
asn1_check_OID(&asn1, OID_SPNEGO);
if (read_negTokenInit(&asn1, &token->negTokenInit)) {
token->type = SPNEGO_NEG_TOKEN_INIT;
}
asn1_end_tag(&asn1);
break;
case ASN1_CONTEXT(1):
if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
token->type = SPNEGO_NEG_TOKEN_TARG;
}
break;
default:
break;
}
if (!asn1.has_error) ret = asn1.ofs;
asn1_free(&asn1);
return ret;
}
ssize_t write_spnego_data(DATA_BLOB *blob, SPNEGO_DATA *spnego)
{
ASN1_DATA asn1;
ssize_t ret = -1;
ZERO_STRUCT(asn1);
switch (spnego->type) {
case SPNEGO_NEG_TOKEN_INIT:
asn1_push_tag(&asn1, ASN1_APPLICATION(0));
asn1_write_OID(&asn1, OID_SPNEGO);
write_negTokenInit(&asn1, &spnego->negTokenInit);
asn1_pop_tag(&asn1);
break;
case SPNEGO_NEG_TOKEN_TARG:
write_negTokenTarg(&asn1, &spnego->negTokenTarg);
break;
default:
asn1.has_error = True;
break;
}
if (!asn1.has_error) {
*blob = data_blob(asn1.data, asn1.length);
ret = asn1.ofs;
}
asn1_free(&asn1);
return ret;
}

View File

@ -32,7 +32,8 @@
enum squid_mode {
SQUID_2_4_BASIC,
SQUID_2_5_BASIC,
SQUID_2_5_NTLMSSP
SQUID_2_5_NTLMSSP,
GSS_SPNEGO
};
@ -342,6 +343,225 @@ static void manage_squid_basic_request(enum squid_mode squid_mode,
}
}
static void offer_gss_spnego_mechs(void) {
DATA_BLOB token;
ASN1_DATA asn1;
SPNEGO_DATA spnego;
const char *OIDs[] = {OID_NTLMSSP, NULL};
ssize_t len;
char *reply_base64;
/* Server negTokenInit (mech offerings) */
ZERO_STRUCT(spnego);
spnego.type = SPNEGO_NEG_TOKEN_INIT;
spnego.negTokenInit.mechTypes = OIDs;
ZERO_STRUCT(asn1);
asn1_push_tag(&asn1, ASN1_SEQUENCE(0));
asn1_push_tag(&asn1, ASN1_CONTEXT(0));
asn1_write_GeneralString(&asn1, "NONE");
asn1_pop_tag(&asn1);
asn1_pop_tag(&asn1);
spnego.negTokenInit.mechListMIC = data_blob(asn1.data, asn1.length);
asn1_free(&asn1);
len = write_spnego_data(&token, &spnego);
data_blob_free(&spnego.negTokenInit.mechListMIC);
if (len == -1) {
DEBUG(1, ("Could not write SPNEGO data blob\n"));
x_fprintf(x_stdout, "BH\n");
return;
}
reply_base64 = base64_encode_data_blob(token);
x_fprintf(x_stdout, "TT %s\n", reply_base64);
SAFE_FREE(reply_base64);
data_blob_free(&token);
DEBUG(10, ("sent SPNEGO negTokenInit\n"));
return;
}
static void manage_gss_spnego_request(enum squid_mode squid_mode,
char *buf, int length)
{
static NTLMSSP_STATE *ntlmssp_state = NULL;
SPNEGO_DATA spnego;
DATA_BLOB request, token;
NTSTATUS status;
char *reply_base64;
pstring reply;
ssize_t len;
if (strlen(buf) < 2) {
if (ntlmssp_state != NULL) {
DEBUG(1, ("Request for initial SPNEGO request where "
"we already have a state\n"));
x_fprintf(x_stdout, "BH\n");
return;
}
DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
x_fprintf(x_stdout, "BH\n");
return;
}
if ( (strlen(buf) == 2) && (strcmp(buf, "YR") == 0) ) {
/* Initial request, get the negTokenInit offering
mechanisms */
offer_gss_spnego_mechs();
return;
}
/* All subsequent requests are "KK" (Knock, Knock ;)) and have
a blob. This might be negTokenInit or negTokenTarg */
if ( (strlen(buf) <= 3) || (strncmp(buf, "KK", 2) != 0) ) {
DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
x_fprintf(x_stdout, "BH\n");
return;
}
request = base64_decode_data_blob(buf + 3);
len = read_spnego_data(request, &spnego);
if (len == -1) {
DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
x_fprintf(x_stdout, "BH\n");
return;
}
if ( (spnego.type != SPNEGO_NEG_TOKEN_INIT) &&
(spnego.type != SPNEGO_NEG_TOKEN_TARG) ) {
DEBUG(1, ("Got an invalid SPNEGO token!\n"));
x_fprintf(x_stdout, "BH\n");
return;
}
if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
/* Second request from Client. This is where the
client offers its mechanism to use. We currently
only support NTLMSSP, the decision for Kerberos
would be taken here. */
if ( (spnego.negTokenInit.mechTypes == NULL) ||
(spnego.negTokenInit.mechTypes[0] == NULL) ) {
DEBUG(1, ("Client did not offer any mechanism"));
x_fprintf(x_stdout, "BH\n");
return;
}
if ( strcmp(spnego.negTokenInit.mechTypes[0], OID_NTLMSSP) != 0 ) {
DEBUG(1, ("Client did not choose NTLMSSP but %s\n",
spnego.negTokenInit.mechTypes[0]));
x_fprintf(x_stdout, "BH\n");
return;
}
if ( spnego.negTokenInit.mechToken.data == NULL ) {
DEBUG(1, ("Client did not provide NTLMSSP data\n"));
x_fprintf(x_stdout, "BH\n");
return;
}
if ( ntlmssp_state != NULL ) {
DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
"already got one\n"));
x_fprintf(x_stdout, "BH\n");
ntlmssp_server_end(&ntlmssp_state);
return;
}
ntlmssp_server_start(&ntlmssp_state);
ntlmssp_state->check_password = winbind_pw_check;
ntlmssp_state->get_domain = get_winbind_domain;
ntlmssp_state->get_global_myname = get_winbind_netbios_name;
DEBUG(10, ("got NTLMSSP packet:\n"));
dump_data(10, spnego.negTokenInit.mechToken.data,
spnego.negTokenInit.mechToken.length);
ZERO_STRUCT(spnego);
spnego.type = SPNEGO_NEG_TOKEN_TARG;
spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
spnego.negTokenTarg.supportedMech = OID_NTLMSSP;
status = ntlmssp_server_update(ntlmssp_state,
spnego.negTokenInit.mechToken,
&spnego.negTokenTarg.responseToken);
} else {
/* spnego.type == SPNEGO_NEG_TOKEN_TARG */
DATA_BLOB response;
if (spnego.negTokenTarg.responseToken.data == NULL) {
DEBUG(1, ("Got a negTokenArg without a responseToken!\n"));
x_fprintf(x_stdout, "BH\n");
return;
}
status = ntlmssp_server_update(ntlmssp_state,
spnego.negTokenTarg.responseToken,
&response);
data_blob_free(&spnego.negTokenTarg.responseToken);
spnego.negTokenTarg.responseToken = response;
}
if ( !NT_STATUS_IS_OK(status) &&
!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ) {
/* Neither ok nor more work to do, so reject */
x_fprintf(x_stdout, "NA %s\n", nt_errstr(status));
DEBUG(10, ("NTLMSSP %s\n", nt_errstr(status)));
return;
}
pstr_sprintf(reply, "TT");
if (NT_STATUS_IS_OK(status)) {
pstr_sprintf(reply, "AF %s\\%s",
ntlmssp_state->domain, ntlmssp_state->user);
spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
DEBUG(10, ("NTLMSSP OK!\n"));
}
len = write_spnego_data(&token, &spnego);
if (len == -1) {
DEBUG(1, ("Could not write SPNEGO data blob\n"));
x_fprintf(x_stdout, "BH\n");
return;
}
reply_base64 = base64_encode_data_blob(token);
x_fprintf(x_stdout, "%s %s\n", reply, reply_base64);
SAFE_FREE(reply_base64);
data_blob_free(&token);
if (NT_STATUS_IS_OK(status)) {
ntlmssp_server_end(&ntlmssp_state);
}
return;
}
static void manage_squid_request(enum squid_mode squid_mode)
{
char buf[SQUID_BUFFER_SIZE+1];
@ -383,6 +603,8 @@ static void manage_squid_request(enum squid_mode squid_mode)
manage_squid_basic_request(squid_mode, buf, length);
} else if (squid_mode == SQUID_2_5_NTLMSSP) {
manage_squid_ntlmssp_request(squid_mode, buf, length);
} else if (squid_mode == GSS_SPNEGO) {
manage_gss_spnego_request(squid_mode, buf, length);
}
}
@ -1334,6 +1556,8 @@ enum {
squid_stream(SQUID_2_5_BASIC);
} else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) {
squid_stream(SQUID_2_4_BASIC);
} else if (strcmp(helper_protocol, "gss-spnego")== 0) {
squid_stream(GSS_SPNEGO);
} else {
x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol);
exit(1);