1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00

r1198: Merge the Samba 3.0 ntlm_auth, including the kerberos and SPENGO parts.

I have moved the SPNEGO and Kerberos code into libcli/auth, and intend
to refactor them into the same format as NTLMSSP.

Andrew Bartlett
This commit is contained in:
Andrew Bartlett 2004-06-19 08:15:41 +00:00 committed by Gerald (Jerry) Carter
parent 57ca89eab3
commit 58da78a746
19 changed files with 1999 additions and 1124 deletions

View File

@ -1,34 +1,33 @@
/*
/*
Unix SMB/CIFS implementation.
header for ads (active directory) library routines
basically this is a wrapper around ldap
Copyright (C) Andrew Tridgell 2001-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 _ADS_H
#define _ADS_H
typedef struct {
void *ld; /* the active ldap structure */
struct in_addr ldap_ip; /* the ip of the active connection, if any */
time_t last_attempt; /* last attempt to reconnect */
int ldap_port;
int is_mine; /* do I own this structure's memory? */
/* info needed to find the server */
struct {
char *realm;
@ -46,6 +45,7 @@ typedef struct {
char *kdc_server;
uint_t flags;
int time_offset;
time_t expire;
} auth;
/* info derived from the servers config */
@ -226,9 +226,6 @@ typedef void **ADS_MODLIST;
#define ADS_AUTH_SIMPLE_BIND 0x08
#define ADS_AUTH_ALLOW_NTLMSSP 0x10
/***************************************
Some krb5 compat stuff
***************************************/
/* Kerberos environment variable names */
#define KRB5_ENV_CCNAME "KRB5CCNAME"
@ -243,29 +240,3 @@ typedef void **ADS_MODLIST;
#ifndef HAVE_AP_OPTS_USE_SUBKEY
#define AP_OPTS_USE_SUBKEY 0
#endif
#if defined(HAVE_KRB5)
#ifndef HAVE_KRB5_SET_REAL_TIME
krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds);
#endif
#ifndef HAVE_KRB5_SET_DEFAULT_TGS_KTYPES
krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc);
#endif
#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock);
#endif
/* Samba wrapper function for krb5 functionality. */
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr);
int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
void get_auth_data_from_tkt(DATA_BLOB *auth_data, krb5_ticket *tkt);
krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt);
krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters);
krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes);
void free_kerberos_etypes(krb5_context context, krb5_enctype *enctypes);
BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote);
#endif /* HAVE_KRB5 */
#endif /* _ADS_H */

View File

@ -649,9 +649,11 @@ extern int errno;
#include "md5.h"
#include "hmacmd5.h"
#include "libcli/auth/spnego.h"
#include "libcli/auth/ntlmssp.h"
#include "libcli/auth/credentials.h"
#include "libcli/auth/schannel.h"
#include "libcli/auth/kerberos.h"
#include "auth/auth.h"

View File

@ -50,7 +50,8 @@ ADD_OBJ_FILES = \
lib/crypto/md5.o \
lib/crypto/hmacmd5.o \
lib/crypto/md4.o \
lib/tdb_helper.o
lib/tdb_helper.o \
lib/server_mutex.o
REQUIRED_SUBSYSTEMS = \
LIBTDB CHARSET
# End SUBSYSTEM LIBBASIC

View File

@ -1252,6 +1252,22 @@ DATA_BLOB strhex_to_data_blob(const char *strhex)
return ret_blob;
}
/**
* Routine to print a buffer as HEX digits, into an allocated string.
*/
void hex_encode(const unsigned char *buff_in, size_t len, char **out_hex_buffer)
{
int i;
char *hex_buffer;
*out_hex_buffer = smb_xmalloc((len*2)+1);
hex_buffer = *out_hex_buffer;
for (i = 0; i < len; i++)
slprintf(&hex_buffer[i*2], 3, "%02X", buff_in[i]);
}
/**
Unescape a URL encoded string, in place.

View File

@ -443,3 +443,4 @@ fi
SMB_EXT_LIB(LDAP,[${LDAP_LIBS}],[${LDAP_CFLAGS}],[${LDAP_CPPFLAGS}],[${LDAP_LDFLAGS}])
SMB_EXT_LIB(KRB5,[${KRB5_LIBS}],[${KRB5_CFLAGS}],[${KRB5_CPPFLAGS}],[${KRB5_LDFLAGS}])

View File

@ -234,6 +234,55 @@ krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
}
#endif
#if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
void krb5_free_unparsed_name(krb5_context context, char *val)
{
SAFE_FREE(val);
}
#endif
static BOOL ads_cleanup_expired_creds(krb5_context context,
krb5_ccache ccache,
krb5_creds *credsp)
{
krb5_error_code retval;
TALLOC_CTX *mem_ctx = talloc_init("ticket expied time");
if (!mem_ctx) {
return False;
}
DEBUG(3, ("Ticket in ccache[%s] expiration %s\n",
krb5_cc_default_name(context),
http_timestring(mem_ctx, credsp->times.endtime)));
talloc_destroy(mem_ctx);
/* we will probably need new tickets if the current ones
will expire within 10 seconds.
*/
if (credsp->times.endtime >= (time(NULL) + 10))
return False;
/* heimdal won't remove creds from a file ccache, and
perhaps we shouldn't anyway, since internally we
use memory ccaches, and a FILE one probably means that
we're using creds obtained outside of our exectuable
*/
if (StrCaseCmp(krb5_cc_get_type(context, ccache), "FILE") == 0) {
DEBUG(5, ("We do not remove creds from a FILE ccache\n"));
return False;
}
retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
if (retval) {
DEBUG(1, ("krb5_cc_remove_cred failed, err %s\n",
error_message(retval)));
/* If we have an error in this, we want to display it,
but continue as though we deleted it */
}
return True;
}
/*
we can't use krb5_mk_req because w2k wants the service to be in a particular format
*/
@ -249,7 +298,10 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context,
krb5_creds * credsp;
krb5_creds creds;
krb5_data in_data;
BOOL creds_ready = False;
TALLOC_CTX *mem_ctx;
retval = krb5_parse_name(context, principal, &server);
if (retval) {
DEBUG(1,("Failed to parse principal %s\n", principal));
@ -270,20 +322,36 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context,
goto cleanup_creds;
}
if ((retval = krb5_get_credentials(context, 0,
ccache, &creds, &credsp))) {
DEBUG(1,("krb5_get_credentials failed for %s (%s)\n",
principal, error_message(retval)));
goto cleanup_creds;
while(!creds_ready) {
if ((retval = krb5_get_credentials(context, 0, ccache,
&creds, &credsp))) {
DEBUG(1,("krb5_get_credentials failed for %s (%s)\n",
principal, error_message(retval)));
goto cleanup_creds;
}
/* cope with ticket being in the future due to clock skew */
if ((unsigned)credsp->times.starttime > time(NULL)) {
time_t t = time(NULL);
int time_offset =(unsigned)credsp->times.starttime-t;
DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
krb5_set_real_time(context, t + time_offset + 1, 0);
}
if (!ads_cleanup_expired_creds(context, ccache, credsp))
creds_ready = True;
}
/* cope with the ticket being in the future due to clock skew */
if ((uint_t)credsp->times.starttime > time(NULL)) {
time_t t = time(NULL);
int time_offset = (uint_t)credsp->times.starttime - t;
DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
krb5_set_real_time(context, t + time_offset + 1, 0);
mem_ctx = talloc_init("ticket expied time");
if (!mem_ctx) {
retval = ENOMEM;
goto cleanup_creds;
}
DEBUG(10,("Ticket (%s) in ccache (%s) is valid until: (%s - %d)\n",
principal, krb5_cc_default_name(context),
http_timestring(mem_ctx, (unsigned)credsp->times.endtime),
(unsigned)credsp->times.endtime));
in_data.length = 0;
retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
@ -312,8 +380,8 @@ int cli_krb5_get_ticket(const char *principal, time_t time_offset,
{
krb5_error_code retval;
krb5_data packet;
krb5_ccache ccdef;
krb5_context context;
krb5_context context = NULL;
krb5_ccache ccdef = NULL;
krb5_auth_context auth_context = NULL;
krb5_enctype enc_types[] = {
#ifdef ENCTYPE_ARCFOUR_HMAC
@ -364,8 +432,17 @@ int cli_krb5_get_ticket(const char *principal, time_t time_offset,
#endif
failed:
if ( context )
if ( context ) {
#if 0 /* JERRY -- disabled since it causes heimdal 0.6.1rc3 to die
SuSE 9.1 Pro */
if (ccdef)
krb5_cc_close(context, ccdef);
#endif
if (auth_context)
krb5_auth_con_free(context, auth_context);
krb5_free_context(context);
}
return retval;
}
@ -410,7 +487,7 @@ failed:
#endif
#else /* HAVE_KRB5 */
/* this saves a few linking headaches */
/* this saves a few linking headaches */
int cli_krb5_get_ticket(const char *principal, time_t time_offset,
DATA_BLOB *ticket, DATA_BLOB *session_key_krb5)
{

View File

@ -54,11 +54,11 @@ kerb_prompter(krb5_context ctx, void *data,
simulate a kinit, putting the tgt in the default cache location
remus@snapserver.com
*/
int kerberos_kinit_password(const char *principal, const char *password, int time_offset)
int kerberos_kinit_password(const char *principal, const char *password, int time_offset, time_t *expire_time)
{
krb5_context ctx;
krb5_context ctx = NULL;
krb5_error_code code = 0;
krb5_ccache cc;
krb5_ccache cc = NULL;
krb5_principal me;
krb5_creds my_creds;
@ -102,6 +102,9 @@ int kerberos_kinit_password(const char *principal, const char *password, int tim
return code;
}
if (expire_time)
*expire_time = (time_t) my_creds.times.endtime;
krb5_cc_close(ctx, cc);
krb5_free_cred_contents(ctx, &my_creds);
krb5_free_principal(ctx, me);
@ -126,7 +129,7 @@ int ads_kinit_password(ADS_STRUCT *ads)
return KRB5_LIBOS_CANTREADPWD;
}
ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset);
ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset, &ads->auth.expire);
if (ret) {
DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
@ -136,5 +139,37 @@ int ads_kinit_password(ADS_STRUCT *ads)
return ret;
}
int ads_kdestroy(const char *cc_name)
{
krb5_error_code code;
krb5_context ctx = NULL;
krb5_ccache cc = NULL;
if ((code = krb5_init_context (&ctx))) {
DEBUG(3, ("ads_kdestroy: kdb5_init_context rc=%d\n", code));
return code;
}
if (!cc_name) {
if ((code = krb5_cc_default(ctx, &cc))) {
krb5_free_context(ctx);
return code;
}
} else {
if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
DEBUG(3, ("ads_kdestroy: krb5_cc_resolve rc=%d\n",
code));
krb5_free_context(ctx);
return code;
}
}
if ((code = krb5_cc_destroy (ctx, cc))) {
DEBUG(3, ("ads_kdestroy: krb5_cc_destroy rc=%d\n", code));
}
krb5_free_context (ctx);
return code;
}
#endif

View File

@ -0,0 +1,50 @@
/*
Unix SMB/CIFS implementation.
simple kerberos5 routines for active directory
Copyright (C) Andrew Tridgell 2001
Copyright (C) Luke Howard 2002-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.
*/
#if defined(HAVE_KRB5)
#ifndef HAVE_KRB5_SET_REAL_TIME
krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds);
#endif
#ifndef HAVE_KRB5_SET_DEFAULT_TGS_KTYPES
krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc);
#endif
#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock);
#endif
#ifndef HAVE_KRB5_FREE_UNPARSED_NAME
void krb5_free_unparsed_name(krb5_context ctx, char *val);
#endif
/* Samba wrapper function for krb5 functionality. */
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr);
int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
void get_auth_data_from_tkt(DATA_BLOB *auth_data, krb5_ticket *tkt);
krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt);
krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters);
krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes);
void free_kerberos_etypes(krb5_context context, krb5_enctype *enctypes);
BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote);
#endif /* HAVE_KRB5 */

View File

@ -26,135 +26,6 @@
#ifdef HAVE_KRB5
static void free_keytab(krb5_context context, krb5_keytab keytab)
{
int ret=0;
if (keytab)
ret = krb5_kt_close(context, keytab);
if (ret) {
DEBUG(3, ("krb5_kt_close failed (%s)\n",
error_message(ret)));
}
}
#ifdef HAVE_MEMORY_KEYTAB
static krb5_error_code create_keytab(krb5_context context,
krb5_principal host_princ,
char *host_princ_s,
krb5_data password,
krb5_enctype *enctypes,
krb5_keytab *keytab,
char *keytab_name)
{
krb5_keytab_entry entry;
krb5_kvno kvno = 1;
krb5_error_code ret;
krb5_keyblock *key;
int i;
DEBUG(10,("creating keytab: %s\n", keytab_name));
ret = krb5_kt_resolve(context, keytab_name, keytab);
if (ret)
return ret;
if (!(key = (krb5_keyblock *)malloc(sizeof(*key)))) {
return ENOMEM;
}
/* add keytab entries for all encryption types */
for ( i=0; enctypes[i]; i++ ) {
if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i])) {
continue;
}
entry.principal = host_princ;
entry.vno = kvno;
#if !defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) && !defined(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK)
#error krb5_keytab_entry has no key or keyblock member
#endif
#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEY /* MIT */
entry.key = *key;
#endif
#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK /* Heimdal */
entry.keyblock = *key;
#endif
DEBUG(10,("adding keytab-entry for (%s) with encryption type (%d)\n",
host_princ_s, enctypes[i]));
ret = krb5_kt_add_entry(context, *keytab, &entry);
if (ret) {
DEBUG(1,("adding entry to keytab failed (%s)\n",
error_message(ret)));
free_keytab(context, *keytab);
return ret;
}
}
krb5_free_keyblock(context, key);
return 0;
}
#endif
static BOOL setup_keytab(krb5_context context,
krb5_principal host_princ,
char *host_princ_s,
krb5_data password,
krb5_enctype *enctypes,
krb5_keytab *keytab)
{
char *keytab_name = NULL;
krb5_error_code ret;
/* check if we have to setup a keytab - not currently enabled
I've put this in so that the else block below functions
the same way that it will when this code is turned on */
if (0 /* will later be *lp_keytab() */) {
/* use a file-keytab */
asprintf(&keytab_name, "%s:%s",
""
/* KRB5_KT_FILE_PREFIX, "FILE" or
"WRFILE" depending on HEeimdal or MIT */,
"" /* will later be lp_keytab() */);
DEBUG(10,("will use filebased keytab: %s\n", keytab_name));
ret = krb5_kt_resolve(context, keytab_name, keytab);
if (ret) {
DEBUG(3,("cannot resolve keytab name %s (%s)\n",
keytab_name,
error_message(ret)));
SAFE_FREE(keytab_name);
return False;
}
}
#if defined(HAVE_MEMORY_KEYTAB)
else {
/* setup a in-memory-keytab */
asprintf(&keytab_name, "MEMORY:");
ret = create_keytab(context, host_princ, host_princ_s, password, enctypes,
keytab, keytab_name);
if (ret) {
DEBUG(3,("unable to create MEMORY: keytab (%s)\n",
error_message(ret)));
SAFE_FREE(keytab_name);
return False;
}
}
#endif
SAFE_FREE(keytab_name);
return True;
}
/*
verify an incoming ticket and parse out the principal name and
authorization_data if available
@ -167,7 +38,6 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket,
NTSTATUS sret = NT_STATUS_LOGON_FAILURE;
krb5_context context = NULL;
krb5_auth_context auth_context = NULL;
krb5_keytab keytab = NULL;
krb5_data packet;
krb5_ticket *tkt = NULL;
krb5_rcache rcache = NULL;
@ -177,6 +47,7 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket,
krb5_principal host_princ;
char *host_princ_s = NULL;
BOOL free_host_princ = False;
BOOL got_replay_mutex = False;
fstring myname;
char *password_s = NULL;
@ -198,7 +69,7 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket,
return NT_STATUS_LOGON_FAILURE;
}
password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
password_s = secrets_fetch_machine_password(lp_workgroup());
if (!password_s) {
DEBUG(1,("ads_verify_ticket: failed to fetch machine password\n"));
return NT_STATUS_LOGON_FAILURE;
@ -280,13 +151,8 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket,
goto out;
}
if (!setup_keytab(context, host_princ, host_princ_s, password,
enctypes, &keytab)) {
DEBUG(3,("ads_verify_ticket: unable to setup keytab\n"));
sret = NT_STATUS_LOGON_FAILURE;
goto out;
}
got_replay_mutex = True;
/* We need to setup a auth context with each possible encoding type in turn. */
for (i=0;enctypes[i];i++) {
if (!(key = (krb5_keyblock *)malloc(sizeof(*key)))) {
@ -306,24 +172,21 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket,
packet.data = (krb5_pointer)ticket->data;
if (!(ret = krb5_rd_req(context, &auth_context, &packet,
#ifdef HAVE_MEMORY_KEYTAB
host_princ,
#else
NULL,
#endif
keytab, NULL, &tkt))) {
NULL, NULL, &tkt))) {
DEBUG(10,("ads_verify_ticket: enc type [%u] decrypted message !\n",
(uint_t)enctypes[i] ));
(unsigned int)enctypes[i] ));
auth_ok = True;
break;
}
DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10,
("ads_verify_ticket: enc type [%u] failed to decrypt with error %s\n",
(uint_t)enctypes[i], error_message(ret)));
(unsigned int)enctypes[i], error_message(ret)));
}
release_server_mutex();
got_replay_mutex = False;
if (!auth_ok) {
DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n",
@ -350,16 +213,9 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket,
file_save("/tmp/ticket.dat", ticket->data, ticket->length);
#endif
/* auth_data is the PAC */
get_auth_data_from_tkt(auth_data, tkt);
#if 0
{
TALLOC_CTX *ctx = talloc_init("pac data");
decode_pac_data(auth_data, ctx);
talloc_destroy(ctx);
}
#endif
#if 0
if (tkt->enc_part2) {
file_save("/tmp/authdata.dat",
@ -368,10 +224,6 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket,
}
#endif
/* get rid of all resources associated with the keytab */
if (keytab) free_keytab(context, keytab);
if ((ret = krb5_unparse_name(context, get_principal_from_tkt(tkt),
principal))) {
DEBUG(3,("ads_verify_ticket: krb5_unparse_name failed (%s)\n",
@ -384,6 +236,9 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket,
out:
if (got_replay_mutex)
release_server_mutex();
if (!NT_STATUS_IS_OK(sret))
data_blob_free(auth_data);

343
source/libcli/auth/spnego.c Normal file
View File

@ -0,0 +1,343 @@
/*
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, struct spnego_negTokenInit *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 + 2) *
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->data[asn1->ofs] == ASN1_OCTET_STRING) {
asn1_read_OctetString(asn1,
&token->mechListMIC);
} else {
/* RFC 2478 says we have an Octet String here,
but W2k sends something different... */
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, struct spnego_negTokenInit *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));
#if 0
/* This is what RFC 2478 says ... */
asn1_write_OctetString(asn1, token->mechListMIC.data,
token->mechListMIC.length);
#else
/* ... but unfortunately this is what Windows
sends/expects */
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_push_tag(asn1, ASN1_GENERAL_STRING);
asn1_write(asn1, token->mechListMIC.data,
token->mechListMIC.length);
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
#endif
asn1_pop_tag(asn1);
}
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
return !asn1->has_error;
}
static BOOL read_negTokenTarg(ASN1_DATA *asn1, struct spnego_negTokenTarg *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):
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, struct spnego_negTokenTarg *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, struct 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, struct 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;
}
BOOL free_spnego_data(struct spnego_data *spnego)
{
BOOL ret = True;
if (!spnego) goto out;
switch(spnego->type) {
case SPNEGO_NEG_TOKEN_INIT:
if (spnego->negTokenInit.mechTypes) {
int i;
for (i = 0; spnego->negTokenInit.mechTypes[i]; i++) {
free(spnego->negTokenInit.mechTypes[i]);
}
free(spnego->negTokenInit.mechTypes);
}
data_blob_free(&spnego->negTokenInit.mechToken);
data_blob_free(&spnego->negTokenInit.mechListMIC);
break;
case SPNEGO_NEG_TOKEN_TARG:
if (spnego->negTokenTarg.supportedMech) {
free(spnego->negTokenTarg.supportedMech);
}
data_blob_free(&spnego->negTokenTarg.responseToken);
data_blob_free(&spnego->negTokenTarg.mechListMIC);
break;
default:
ret = False;
break;
}
ZERO_STRUCTP(spnego);
out:
return ret;
}

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;
struct spnego_negTokenInit {
char **mechTypes;
int reqFlags;
DATA_BLOB mechToken;
DATA_BLOB mechListMIC;
};
struct spnego_negTokenTarg {
uint8 negResult;
const char *supportedMech;
DATA_BLOB responseToken;
DATA_BLOB mechListMIC;
};
struct spnego_data {
int type;
struct spnego_negTokenInit negTokenInit;
struct spnego_negTokenTarg negTokenTarg;
};
#endif

View File

@ -12,8 +12,6 @@ SMB_SUBSYSTEM(LIBCLI_RAW,[],
libcli/raw/clitransport.o
libcli/raw/clisession.o
libcli/raw/clitree.o
libcli/raw/clikrb5.o
libcli/raw/clispnego.o
libcli/raw/rawrequest.o
libcli/raw/rawreadwrite.o
libcli/raw/rawsearch.o
@ -43,13 +41,17 @@ SMB_SUBSYSTEM(LIBCLI_UTILS,[],
libcli/util/dom_sid.o])
SMB_SUBSYSTEM(LIBCLI_AUTH,[],
[libcli/auth/ntlmssp.o
[libcli/auth/spnego.o
libcli/auth/ntlmssp.o
libcli/auth/ntlmssp_parse.o
libcli/auth/ntlmssp_sign.o
libcli/auth/schannel.o
libcli/auth/credentials.o
libcli/auth/session.o
libcli/auth/ntlm_check.o])
libcli/auth/ntlm_check.o
libcli/auth/kerberos.o
libcli/auth/kerberos_verify.o
libcli/auth/clikrb5.o])
SMB_SUBSYSTEM(LIBCLI_NMB,[],
[libcli/unexpected.o

View File

@ -1,539 +0,0 @@
/*
Unix SMB/CIFS implementation.
simple kerberos5/SPNEGO routines
Copyright (C) Andrew Tridgell 2001
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
Copyright (C) Luke Howard 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"
/*
generate a negTokenInit packet given a GUID, a list of supported
OIDs (the mechanisms) and a principal name string
*/
DATA_BLOB spnego_gen_negTokenInit(uint8_t guid[16],
const char *OIDs[],
const char *principal)
{
int i;
ASN1_DATA data;
DATA_BLOB ret;
memset(&data, 0, sizeof(data));
asn1_write(&data, guid, 16);
asn1_push_tag(&data,ASN1_APPLICATION(0));
asn1_write_OID(&data,OID_SPNEGO);
asn1_push_tag(&data,ASN1_CONTEXT(0));
asn1_push_tag(&data,ASN1_SEQUENCE(0));
asn1_push_tag(&data,ASN1_CONTEXT(0));
asn1_push_tag(&data,ASN1_SEQUENCE(0));
for (i=0; OIDs[i]; i++) {
asn1_write_OID(&data,OIDs[i]);
}
asn1_pop_tag(&data);
asn1_pop_tag(&data);
asn1_push_tag(&data, ASN1_CONTEXT(3));
asn1_push_tag(&data, ASN1_SEQUENCE(0));
asn1_push_tag(&data, ASN1_CONTEXT(0));
asn1_write_GeneralString(&data,principal);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
if (data.has_error) {
DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
asn1_free(&data);
}
ret = data_blob(data.data, data.length);
asn1_free(&data);
return ret;
}
/*
Generate a negTokenInit as used by the client side ... It has a mechType
(OID), and a mechToken (a security blob) ...
Really, we need to break out the NTLMSSP stuff as well, because it could be
raw in the packets!
*/
DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob)
{
ASN1_DATA data;
DATA_BLOB ret;
memset(&data, 0, sizeof(data));
asn1_push_tag(&data, ASN1_APPLICATION(0));
asn1_write_OID(&data,OID_SPNEGO);
asn1_push_tag(&data, ASN1_CONTEXT(0));
asn1_push_tag(&data, ASN1_SEQUENCE(0));
asn1_push_tag(&data, ASN1_CONTEXT(0));
asn1_push_tag(&data, ASN1_SEQUENCE(0));
asn1_write_OID(&data, OID);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
asn1_push_tag(&data, ASN1_CONTEXT(2));
asn1_write_OctetString(&data,blob.data,blob.length);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
if (data.has_error) {
DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
asn1_free(&data);
}
ret = data_blob(data.data, data.length);
asn1_free(&data);
return ret;
}
/*
parse a negTokenInit packet giving a GUID, a list of supported
OIDs (the mechanisms) and a principal name string
*/
BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
char *OIDs[ASN1_MAX_OIDS],
char **principal)
{
int i;
BOOL ret;
ASN1_DATA data;
asn1_load(&data, blob);
asn1_start_tag(&data,ASN1_APPLICATION(0));
asn1_check_OID(&data,OID_SPNEGO);
asn1_start_tag(&data,ASN1_CONTEXT(0));
asn1_start_tag(&data,ASN1_SEQUENCE(0));
asn1_start_tag(&data,ASN1_CONTEXT(0));
asn1_start_tag(&data,ASN1_SEQUENCE(0));
for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
char *aoid = NULL;
asn1_read_OID(&data,&aoid);
OIDs[i] = aoid;
}
OIDs[i] = NULL;
asn1_end_tag(&data);
asn1_end_tag(&data);
asn1_start_tag(&data, ASN1_CONTEXT(3));
asn1_start_tag(&data, ASN1_SEQUENCE(0));
asn1_start_tag(&data, ASN1_CONTEXT(0));
asn1_read_GeneralString(&data,principal);
asn1_end_tag(&data);
asn1_end_tag(&data);
asn1_end_tag(&data);
asn1_end_tag(&data);
asn1_end_tag(&data);
asn1_end_tag(&data);
ret = !data.has_error;
asn1_free(&data);
return ret;
}
/*
generate a negTokenTarg packet given a list of OIDs and a security blob
*/
DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
{
int i;
ASN1_DATA data;
DATA_BLOB ret;
memset(&data, 0, sizeof(data));
asn1_push_tag(&data, ASN1_APPLICATION(0));
asn1_write_OID(&data,OID_SPNEGO);
asn1_push_tag(&data, ASN1_CONTEXT(0));
asn1_push_tag(&data, ASN1_SEQUENCE(0));
asn1_push_tag(&data, ASN1_CONTEXT(0));
asn1_push_tag(&data, ASN1_SEQUENCE(0));
for (i=0; OIDs[i]; i++) {
asn1_write_OID(&data,OIDs[i]);
}
asn1_pop_tag(&data);
asn1_pop_tag(&data);
asn1_push_tag(&data, ASN1_CONTEXT(2));
asn1_write_OctetString(&data,blob.data,blob.length);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
if (data.has_error) {
DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
asn1_free(&data);
}
ret = data_blob(data.data, data.length);
asn1_free(&data);
return ret;
}
/*
parse a negTokenTarg packet giving a list of OIDs and a security blob
*/
BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
{
int i;
ASN1_DATA data;
asn1_load(&data, blob);
asn1_start_tag(&data, ASN1_APPLICATION(0));
asn1_check_OID(&data,OID_SPNEGO);
asn1_start_tag(&data, ASN1_CONTEXT(0));
asn1_start_tag(&data, ASN1_SEQUENCE(0));
asn1_start_tag(&data, ASN1_CONTEXT(0));
asn1_start_tag(&data, ASN1_SEQUENCE(0));
for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
char *aoid = NULL;
asn1_read_OID(&data,&aoid);
OIDs[i] = aoid;
}
OIDs[i] = NULL;
asn1_end_tag(&data);
asn1_end_tag(&data);
asn1_start_tag(&data, ASN1_CONTEXT(2));
asn1_read_OctetString(&data,secblob);
asn1_end_tag(&data);
asn1_end_tag(&data);
asn1_end_tag(&data);
asn1_end_tag(&data);
if (data.has_error) {
DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
asn1_free(&data);
return False;
}
asn1_free(&data);
return True;
}
/*
generate a krb5 GSS-API wrapper packet given a ticket
*/
DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket, const uint8_t tok_id[2])
{
ASN1_DATA data;
DATA_BLOB ret;
memset(&data, 0, sizeof(data));
asn1_push_tag(&data, ASN1_APPLICATION(0));
asn1_write_OID(&data, OID_KERBEROS5);
asn1_write(&data, tok_id, 2);
asn1_write(&data, ticket.data, ticket.length);
asn1_pop_tag(&data);
if (data.has_error) {
DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
asn1_free(&data);
}
ret = data_blob(data.data, data.length);
asn1_free(&data);
return ret;
}
/*
parse a krb5 GSS-API wrapper packet giving a ticket
*/
BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8_t tok_id[2])
{
BOOL ret;
ASN1_DATA data;
int data_remaining;
asn1_load(&data, blob);
asn1_start_tag(&data, ASN1_APPLICATION(0));
asn1_check_OID(&data, OID_KERBEROS5);
data_remaining = asn1_tag_remaining(&data);
if (data_remaining < 3) {
data.has_error = True;
} else {
asn1_read(&data, tok_id, 2);
data_remaining -= 2;
*ticket = data_blob(NULL, data_remaining);
asn1_read(&data, ticket->data, ticket->length);
}
asn1_end_tag(&data);
ret = !data.has_error;
asn1_free(&data);
return ret;
}
/*
generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
kerberos session setup
*/
int spnego_gen_negTokenTarg(const char *principal, int time_offset,
DATA_BLOB *targ,
DATA_BLOB *session_key_krb5)
{
int retval;
DATA_BLOB tkt, tkt_wrapped;
const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
/* get a kerberos ticket for the service and extract the session key */
retval = cli_krb5_get_ticket(principal, time_offset, &tkt, session_key_krb5);
if (retval)
return retval;
/* wrap that up in a nice GSS-API wrapping */
tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
/* and wrap that in a shiny SPNEGO wrapper */
*targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
data_blob_free(&tkt_wrapped);
data_blob_free(&tkt);
return retval;
}
/*
parse a spnego NTLMSSP challenge packet giving two security blobs
*/
BOOL spnego_parse_challenge(const DATA_BLOB blob,
DATA_BLOB *chal1, DATA_BLOB *chal2)
{
BOOL ret;
ASN1_DATA data;
ZERO_STRUCTP(chal1);
ZERO_STRUCTP(chal2);
asn1_load(&data, blob);
asn1_start_tag(&data,ASN1_CONTEXT(1));
asn1_start_tag(&data,ASN1_SEQUENCE(0));
asn1_start_tag(&data,ASN1_CONTEXT(0));
asn1_check_enumerated(&data,1);
asn1_end_tag(&data);
asn1_start_tag(&data,ASN1_CONTEXT(1));
asn1_check_OID(&data, OID_NTLMSSP);
asn1_end_tag(&data);
asn1_start_tag(&data,ASN1_CONTEXT(2));
asn1_read_OctetString(&data, chal1);
asn1_end_tag(&data);
/* the second challenge is optional (XP doesn't send it) */
if (asn1_tag_remaining(&data)) {
asn1_start_tag(&data,ASN1_CONTEXT(3));
asn1_read_OctetString(&data, chal2);
asn1_end_tag(&data);
}
asn1_end_tag(&data);
asn1_end_tag(&data);
ret = !data.has_error;
asn1_free(&data);
return ret;
}
/*
generate a SPNEGO auth packet. This will contain the encrypted passwords
*/
DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
{
ASN1_DATA data;
DATA_BLOB ret;
memset(&data, 0, sizeof(data));
asn1_push_tag(&data, ASN1_CONTEXT(1));
asn1_push_tag(&data, ASN1_SEQUENCE(0));
asn1_push_tag(&data, ASN1_CONTEXT(2));
asn1_write_OctetString(&data,blob.data,blob.length);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
asn1_pop_tag(&data);
ret = data_blob(data.data, data.length);
asn1_free(&data);
return ret;
}
/*
parse a SPNEGO auth packet. This contains the encrypted passwords
*/
BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
{
ASN1_DATA data;
asn1_load(&data, blob);
asn1_start_tag(&data, ASN1_CONTEXT(1));
asn1_start_tag(&data, ASN1_SEQUENCE(0));
asn1_start_tag(&data, ASN1_CONTEXT(2));
asn1_read_OctetString(&data,auth);
asn1_end_tag(&data);
asn1_end_tag(&data);
asn1_end_tag(&data);
if (data.has_error) {
DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
asn1_free(&data);
return False;
}
asn1_free(&data);
return True;
}
/*
generate a minimal SPNEGO response packet. Doesn't contain much.
*/
DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
const char *mechOID)
{
ASN1_DATA data;
DATA_BLOB ret;
uint8_t negResult;
if (NT_STATUS_IS_OK(nt_status)) {
negResult = SPNEGO_NEG_RESULT_ACCEPT;
} else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
} else {
negResult = SPNEGO_NEG_RESULT_REJECT;
}
ZERO_STRUCT(data);
asn1_push_tag(&data, ASN1_CONTEXT(1));
asn1_push_tag(&data, ASN1_SEQUENCE(0));
asn1_push_tag(&data, ASN1_CONTEXT(0));
asn1_write_enumerated(&data, negResult);
asn1_pop_tag(&data);
if (reply->data != NULL) {
asn1_push_tag(&data,ASN1_CONTEXT(1));
asn1_write_OID(&data, mechOID);
asn1_pop_tag(&data);
asn1_push_tag(&data,ASN1_CONTEXT(2));
asn1_write_OctetString(&data, reply->data, reply->length);
asn1_pop_tag(&data);
}
asn1_pop_tag(&data);
asn1_pop_tag(&data);
ret = data_blob(data.data, data.length);
asn1_free(&data);
return ret;
}
/*
parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
*/
BOOL spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status,
DATA_BLOB *auth)
{
ASN1_DATA data;
uint8_t negResult;
if (NT_STATUS_IS_OK(nt_status)) {
negResult = SPNEGO_NEG_RESULT_ACCEPT;
} else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
} else {
negResult = SPNEGO_NEG_RESULT_REJECT;
}
asn1_load(&data, blob);
asn1_start_tag(&data, ASN1_CONTEXT(1));
asn1_start_tag(&data, ASN1_SEQUENCE(0));
asn1_start_tag(&data, ASN1_CONTEXT(0));
asn1_check_enumerated(&data, negResult);
asn1_end_tag(&data);
if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
asn1_start_tag(&data,ASN1_CONTEXT(1));
asn1_check_OID(&data, OID_NTLMSSP);
asn1_end_tag(&data);
asn1_start_tag(&data,ASN1_CONTEXT(2));
asn1_read_OctetString(&data, auth);
asn1_end_tag(&data);
}
asn1_end_tag(&data);
asn1_end_tag(&data);
if (data.has_error) {
DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data.ofs));
asn1_free(&data);
data_blob_free(auth);
return False;
}
asn1_free(&data);
return True;
}

View File

@ -219,6 +219,11 @@ BOOL asn1_load(ASN1_DATA *data, DATA_BLOB blob)
/* read from a ASN1 buffer, advancing the buffer pointer */
BOOL asn1_read(ASN1_DATA *data, void *p, int len)
{
if (len < 0 || data->ofs + len < data->ofs || data->ofs + len < len) {
data->has_error = True;
return False;
}
if (data->ofs + len > data->length) {
data->has_error = True;
return False;
@ -315,17 +320,17 @@ int asn1_tag_remaining(ASN1_DATA *data)
BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
{
uint8_t b;
pstring aoid;
fstring el;
char *oid = NULL;
TALLOC_CTX *mem_ctx = talloc_init("asn1_read_OID");
if (!mem_ctx) {
return False;
}
if (!asn1_start_tag(data, ASN1_OID)) return False;
asn1_read_uint8(data, &b);
aoid[0] = 0;
snprintf(el, sizeof(el), "%u", b/40);
pstrcat(aoid, el);
snprintf(el, sizeof(el), " %u", b%40);
pstrcat(aoid, el);
oid = talloc_asprintf_append(mem_ctx, oid, "%u", b/40);
oid = talloc_asprintf_append(mem_ctx, oid, " %u", b%40);
while (asn1_tag_remaining(data) > 0) {
uint_t v = 0;
@ -333,15 +338,15 @@ BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
asn1_read_uint8(data, &b);
v = (v<<7) | (b&0x7f);
} while (!data->has_error && b & 0x80);
snprintf(el, sizeof(el), " %u", v);
pstrcat(aoid, el);
oid = talloc_asprintf_append(mem_ctx, oid, " %u", v);
}
asn1_end_tag(data);
*OID = strdup(aoid);
*OID = strdup(oid);
talloc_destroy(mem_ctx);
return !data->has_error;
return (*OID && !data->has_error);
}
/* check that the next object ID is correct */
@ -365,6 +370,10 @@ BOOL asn1_read_GeneralString(ASN1_DATA *data, char **s)
int len;
if (!asn1_start_tag(data, ASN1_GENERAL_STRING)) return False;
len = asn1_tag_remaining(data);
if (len < 0) {
data->has_error = True;
return False;
}
*s = malloc(len+1);
if (! *s) {
data->has_error = True;
@ -383,6 +392,10 @@ BOOL asn1_read_OctetString(ASN1_DATA *data, DATA_BLOB *blob)
ZERO_STRUCTP(blob);
if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return False;
len = asn1_tag_remaining(data);
if (len < 0) {
data->has_error = True;
return False;
}
*blob = data_blob(NULL, len);
asn1_read(data, blob->data, len);
asn1_end_tag(data);

View File

@ -71,11 +71,11 @@ static void *secrets_fetch(const char *key, size_t *size)
Routine to fetch the plaintext machine account password for a realm
the password is assumed to be a null terminated ascii string
************************************************************************/
char *secrets_fetch_machine_password(void)
char *secrets_fetch_machine_password(const char *domain)
{
char *key;
char *ret;
asprintf(&key, "%s/%s", SECRETS_MACHINE_PASSWORD, lp_workgroup());
asprintf(&key, "%s/%s", SECRETS_MACHINE_PASSWORD, domain);
strupper(key);
ret = (char *)secrets_fetch(key, NULL);
free(key);

View File

@ -31,7 +31,6 @@ INIT_OBJ_FILES = \
smbd/server.o
ADD_OBJ_FILES = \
smbd/process.o \
lib/server_mutex.o \
smbd/build_options.o \
smbd/rewrite.o
REQUIRED_SUBSYSTEMS = \

View File

@ -1,3 +1,4 @@
dnl # utils subsystem
SMB_BINARY_MK(ndrdump, utils/config.mk)
SMB_BINARY_MK(ntlm_auth, utils/config.mk)

View File

@ -12,3 +12,17 @@ REQUIRED_SUBSYSTEMS = \
LIBSMB
# End BINARY ndrdump
#################################
#################################
# Start BINARY ntlm_auth
[BINARY::ntlm_auth]
OBJ_FILES = \
utils/ntlm_auth.o
REQUIRED_SUBSYSTEMS = \
CONFIG \
LIBCMDLINE \
LIBBASIC \
LIBSMB \
LIBRPC
# End BINARY ntlm_auth
#################################

File diff suppressed because it is too large Load Diff