mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +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:
parent
57ca89eab3
commit
58da78a746
@ -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 */
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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}])
|
||||
|
||||
|
@ -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)
|
||||
{
|
@ -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
|
50
source/libcli/auth/kerberos.h
Normal file
50
source/libcli/auth/kerberos.h
Normal 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 */
|
||||
|
@ -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
343
source/libcli/auth/spnego.c
Normal 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;
|
||||
}
|
||||
|
65
source/libcli/auth/spnego.h
Normal file
65
source/libcli/auth/spnego.h
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 = \
|
||||
|
@ -1,3 +1,4 @@
|
||||
dnl # utils subsystem
|
||||
|
||||
SMB_BINARY_MK(ndrdump, utils/config.mk)
|
||||
SMB_BINARY_MK(ntlm_auth, utils/config.mk)
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user