summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2004-06-19 08:15:41 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:56:44 -0500
commitbf598954f75bfd924b9aa22649975b372c74a49e (patch)
tree10395f5e51d75c68b2adb667dc00a1931a44705b
parentbc2fd488f1ad6116ba71fe793cc4444b8cd3c7a2 (diff)
downloadsamba-bf598954f75bfd924b9aa22649975b372c74a49e.tar.gz
samba-bf598954f75bfd924b9aa22649975b372c74a49e.tar.bz2
samba-bf598954f75bfd924b9aa22649975b372c74a49e.zip
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 used to be commit 58da78a7460d5d0a4abee7d7b84799c228e6bc0b)
-rw-r--r--source4/include/ads.h45
-rw-r--r--source4/include/includes.h2
-rw-r--r--source4/lib/basic.mk3
-rw-r--r--source4/lib/util_str.c16
-rw-r--r--source4/libads/config.m41
-rw-r--r--source4/libcli/auth/clikrb5.c (renamed from source4/libcli/raw/clikrb5.c)107
-rw-r--r--source4/libcli/auth/kerberos.c (renamed from source4/libads/kerberos.c)43
-rw-r--r--source4/libcli/auth/kerberos.h50
-rw-r--r--source4/libcli/auth/kerberos_verify.c (renamed from source4/libads/kerberos_verify.c)169
-rw-r--r--source4/libcli/auth/spnego.c343
-rw-r--r--source4/libcli/auth/spnego.h65
-rw-r--r--source4/libcli/config.m410
-rw-r--r--source4/libcli/raw/clispnego.c539
-rw-r--r--source4/libcli/util/asn1.c35
-rw-r--r--source4/passdb/secrets.c4
-rw-r--r--source4/smbd/config.mk1
-rw-r--r--source4/utils/config.m41
-rw-r--r--source4/utils/config.mk14
-rw-r--r--source4/utils/ntlm_auth.c1611
19 files changed, 1967 insertions, 1092 deletions
diff --git a/source4/include/ads.h b/source4/include/ads.h
index 36f898b2b0..27a2e41c52 100644
--- a/source4/include/ads.h
+++ b/source4/include/ads.h
@@ -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 */
diff --git a/source4/include/includes.h b/source4/include/includes.h
index b3cd402e9f..25725bdf4a 100644
--- a/source4/include/includes.h
+++ b/source4/include/includes.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"
diff --git a/source4/lib/basic.mk b/source4/lib/basic.mk
index 754458fec1..ccd0935407 100644
--- a/source4/lib/basic.mk
+++ b/source4/lib/basic.mk
@@ -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
diff --git a/source4/lib/util_str.c b/source4/lib/util_str.c
index bb94d3fce6..70011cf80d 100644
--- a/source4/lib/util_str.c
+++ b/source4/lib/util_str.c
@@ -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.
diff --git a/source4/libads/config.m4 b/source4/libads/config.m4
index 99d138cd09..c6029a4d80 100644
--- a/source4/libads/config.m4
+++ b/source4/libads/config.m4
@@ -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}])
+
diff --git a/source4/libcli/raw/clikrb5.c b/source4/libcli/auth/clikrb5.c
index ba3e9ccd17..6e19d4dc18 100644
--- a/source4/libcli/raw/clikrb5.c
+++ b/source4/libcli/auth/clikrb5.c
@@ -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)
{
diff --git a/source4/libads/kerberos.c b/source4/libcli/auth/kerberos.c
index bef2febaef..e8bf4b0846 100644
--- a/source4/libads/kerberos.c
+++ b/source4/libcli/auth/kerberos.c
@@ -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
diff --git a/source4/libcli/auth/kerberos.h b/source4/libcli/auth/kerberos.h
new file mode 100644
index 0000000000..6f63f6eef2
--- /dev/null
+++ b/source4/libcli/auth/kerberos.h
@@ -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 */
+
diff --git a/source4/libads/kerberos_verify.c b/source4/libcli/auth/kerberos_verify.c
index 48a8378d36..805a3f570f 100644
--- a/source4/libads/kerberos_verify.c
+++ b/source4/libcli/auth/kerberos_verify.c
@@ -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,17 +213,10 @@ 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",
tkt->enc_part2->authorization_data[0]->contents,
@@ -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);
diff --git a/source4/libcli/auth/spnego.c b/source4/libcli/auth/spnego.c
new file mode 100644
index 0000000000..ddc98f883b
--- /dev/null
+++ b/source4/libcli/auth/spnego.c
@@ -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;
+}
+
diff --git a/source4/libcli/auth/spnego.h b/source4/libcli/auth/spnego.h
new file mode 100644
index 0000000000..e30fa13d26
--- /dev/null
+++ b/source4/libcli/auth/spnego.h
@@ -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
diff --git a/source4/libcli/config.m4 b/source4/libcli/config.m4
index 82f629b280..49c690c944 100644
--- a/source4/libcli/config.m4
+++ b/source4/libcli/config.m4
@@ -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
diff --git a/source4/libcli/raw/clispnego.c b/source4/libcli/raw/clispnego.c
deleted file mode 100644
index ff7d45c8c1..0000000000
--- a/source4/libcli/raw/clispnego.c
+++ /dev/null
@@ -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;
-}
-
diff --git a/source4/libcli/util/asn1.c b/source4/libcli/util/asn1.c
index ddefe0baa6..05bc5eace8 100644
--- a/source4/libcli/util/asn1.c
+++ b/source4/libcli/util/asn1.c
@@ -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);
diff --git a/source4/passdb/secrets.c b/source4/passdb/secrets.c
index 6fad9ac228..b5bae614b6 100644
--- a/source4/passdb/secrets.c
+++ b/source4/passdb/secrets.c
@@ -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);
diff --git a/source4/smbd/config.mk b/source4/smbd/config.mk
index 1711a86a97..d5fd9700b9 100644
--- a/source4/smbd/config.mk
+++ b/source4/smbd/config.mk
@@ -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 = \
diff --git a/source4/utils/config.m4 b/source4/utils/config.m4
index a4730e42b9..846d599af5 100644
--- a/source4/utils/config.m4
+++ b/source4/utils/config.m4
@@ -1,3 +1,4 @@
dnl # utils subsystem
SMB_BINARY_MK(ndrdump, utils/config.mk)
+SMB_BINARY_MK(ntlm_auth, utils/config.mk)
diff --git a/source4/utils/config.mk b/source4/utils/config.mk
index 405640deb7..c8e5c0b491 100644
--- a/source4/utils/config.mk
+++ b/source4/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
+#################################
diff --git a/source4/utils/ntlm_auth.c b/source4/utils/ntlm_auth.c
index a966a0461d..d70b0b5d2b 100644
--- a/source4/utils/ntlm_auth.c
+++ b/source4/utils/ntlm_auth.c
@@ -3,8 +3,8 @@
Winbind status program.
- Copyright (C) Tim Potter 2000-2002
- Copyright (C) Andrew Bartlett 2003
+ Copyright (C) Tim Potter 2000-2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000
This program is free software; you can redistribute it and/or modify
@@ -29,232 +29,441 @@
#define SQUID_BUFFER_SIZE 2010
-enum squid_mode {
+enum stdio_helper_mode {
SQUID_2_4_BASIC,
SQUID_2_5_BASIC,
- SQUID_2_5_NTLMSSP
+ SQUID_2_5_NTLMSSP,
+ NTLMSSP_CLIENT_1,
+ GSS_SPNEGO,
+ GSS_SPNEGO_CLIENT,
+ NTLM_SERVER_1,
+ NUM_HELPER_MODES
};
-
-extern int winbindd_fd;
+#define NTLM_AUTH_FLAG_USER_SESSION_KEY 0x0004
+#define NTLM_AUTH_FLAG_LMKEY 0x0008
-static const char *helper_protocol;
-static const char *username;
-static const char *domain;
-static const char *workstation;
-static const char *hex_challenge;
-static const char *hex_lm_response;
-static const char *hex_nt_response;
-static uint8_t *challenge;
-static size_t challenge_len;
-static uint8_t *lm_response;
-static size_t lm_response_len;
-static uint8_t *nt_response;
-static size_t nt_response_len;
-
-static char *password;
-
-static char winbind_separator(void)
-{
- struct winbindd_response response;
- static BOOL got_sep;
- static char sep;
- if (got_sep)
- return sep;
+typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
- ZERO_STRUCT(response);
+static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
- /* Send off request */
+static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
- if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
- NSS_STATUS_SUCCESS) {
- d_printf("could not obtain winbind separator!\n");
- return '\\';
- }
+static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
- sep = response.data.info.winbind_separator;
- got_sep = True;
+static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
- if (!sep) {
- d_printf("winbind separator was NULL!\n");
- return '\\';
- }
-
- return sep;
-}
+static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
-static const char *get_winbind_domain(void)
-{
- struct winbindd_response response;
+static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
- static fstring winbind_domain;
- if (*winbind_domain) {
- return winbind_domain;
- }
+static const struct {
+ enum stdio_helper_mode mode;
+ const char *name;
+ stdio_helper_function fn;
+} stdio_helper_protocols[] = {
+ { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
+ { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
+ { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
+ { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
+ { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request},
+ { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
+ { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
+ { NUM_HELPER_MODES, NULL, NULL}
+};
+
+extern int winbindd_fd;
- ZERO_STRUCT(response);
+const char *opt_username;
+const char *opt_domain;
+const char *opt_workstation;
+const char *opt_password;
- /* Send off request */
- if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
- NSS_STATUS_SUCCESS) {
- d_printf("could not obtain winbind domain name!\n");
- return NULL;
+/* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
+ form DOMAIN/user into a domain and a user */
+
+static BOOL parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
+ fstring user)
+{
+
+ char *p = strchr(domuser,*lp_winbind_separator());
+
+ if (!p) {
+ return False;
}
+
+ fstrcpy(user, p+1);
+ fstrcpy(domain, domuser);
+ domain[PTR_DIFF(p, domuser)] = 0;
+ strupper_m(domain);
- fstrcpy(winbind_domain, response.data.domain_name);
+ return True;
+}
- return winbind_domain;
+/* Authenticate a user with a plaintext password */
+
+static BOOL check_plaintext_auth(const char *user, const char *pass,
+ BOOL stdout_diagnostics)
+{
+ return (strcmp(pass, opt_password) == 0);
+}
+/* authenticate a user with an encrypted username/password */
+
+static NTSTATUS local_pw_check_specified(const char *username,
+ const char *domain,
+ const char *workstation,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ uint32 flags,
+ DATA_BLOB *lm_session_key,
+ DATA_BLOB *user_session_key,
+ char **error_string,
+ char **unix_name)
+{
+ NTSTATUS nt_status;
+ uint8_t lm_pw[16], nt_pw[16];
+ uint8_t *lm_pwd, *nt_pwd;
+ TALLOC_CTX *mem_ctx = talloc_init("local_pw_check_specified");
+ if (!mem_ctx) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ } else {
+
+ E_md4hash(opt_password, nt_pw);
+ if (E_deshash(opt_password, lm_pw)) {
+ lm_pwd = lm_pw;
+ } else {
+ lm_pwd = NULL;
+ }
+ nt_pwd = nt_pw;
+
+
+ nt_status = ntlm_password_check(mem_ctx,
+ challenge,
+ lm_response,
+ nt_response,
+ NULL, NULL,
+ username,
+ username,
+ domain,
+ lm_pwd, nt_pwd, user_session_key, lm_session_key);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ if (unix_name) {
+ asprintf(unix_name,
+ "%s%c%s", domain,
+ *lp_winbind_separator(),
+ username);
+ }
+ } else {
+ DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
+ domain, username, workstation,
+ nt_errstr(nt_status)));
+ }
+ talloc_destroy(mem_ctx);
+ }
+ if (error_string) {
+ *error_string = strdup(nt_errstr(nt_status));
+ }
+ return nt_status;
+
+
}
-static const char *get_winbind_netbios_name(void)
+static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
{
- struct winbindd_response response;
+ NTSTATUS nt_status;
+ uint8 lm_pw[16], nt_pw[16];
+ uint8_t *lm_pwd, *nt_pwd;
- static fstring winbind_netbios_name;
+ E_md4hash(opt_password, nt_pw);
+ if (E_deshash(opt_password, lm_pw)) {
+ lm_pwd = lm_pw;
+ } else {
+ lm_pwd = NULL;
+ }
+ nt_pwd = nt_pw;
+
+ nt_status = ntlm_password_check(ntlmssp_state->mem_ctx,
+ &ntlmssp_state->chal,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ NULL, NULL,
+ ntlmssp_state->user,
+ ntlmssp_state->user,
+ ntlmssp_state->domain,
+ lm_pwd, nt_pwd, user_session_key, lm_session_key);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ ntlmssp_state->auth_context = talloc_asprintf(ntlmssp_state->mem_ctx,
+ "%s%c%s", ntlmssp_state->domain,
+ *lp_winbind_separator(),
+ ntlmssp_state->user);
+ } else {
+ DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
+ ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation,
+ nt_errstr(nt_status)));
+ ntlmssp_state->auth_context = NULL;
+ }
+ return nt_status;
+}
- if (*winbind_netbios_name) {
- return winbind_netbios_name;
+static NTSTATUS ntlm_auth_start_ntlmssp_client(struct ntlmssp_state **client_ntlmssp_state)
+{
+ NTSTATUS status;
+ if ( (opt_username == NULL) || (opt_domain == NULL) ) {
+ DEBUG(1, ("Need username and domain for NTLMSSP\n"));
+ return status;
}
- ZERO_STRUCT(response);
+ status = ntlmssp_client_start(client_ntlmssp_state);
- /* Send off request */
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not start NTLMSSP client: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
- if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) !=
- NSS_STATUS_SUCCESS) {
- d_printf("could not obtain winbind netbios name!\n");
- return NULL;
+ status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set username: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
}
- fstrcpy(winbind_netbios_name, response.data.netbios_name);
+ status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
- return winbind_netbios_name;
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set domain: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+ status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set password: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+ return NT_STATUS_OK;
}
-/* Authenticate a user with a plaintext password */
+static NTSTATUS ntlm_auth_start_ntlmssp_server(struct ntlmssp_state **ntlmssp_state)
+{
+ NTSTATUS status = ntlmssp_server_start(ntlmssp_state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not start NTLMSSP client: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ /* Have we been given a local password, or should we ask winbind? */
+ if (opt_password) {
+ (*ntlmssp_state)->check_password = local_pw_check;
+ (*ntlmssp_state)->get_domain = lp_workgroup;
+ (*ntlmssp_state)->get_global_myname = global_myname;
+ } else {
+ DEBUG(0, ("Winbind not supported in Samba4 ntlm_auth yet, specify --password\n"));
+ exit(1);
+ }
+ return NT_STATUS_OK;
+}
-static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout_diagnostics)
+static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
{
- struct winbindd_request request;
- struct winbindd_response response;
- NSS_STATUS result;
+ static struct ntlmssp_state *ntlmssp_state = NULL;
+ DATA_BLOB request, reply;
+ NTSTATUS nt_status;
- /* Send off request */
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
- ZERO_STRUCT(request);
- ZERO_STRUCT(response);
+ if (strlen(buf) > 3) {
+ request = base64_decode_data_blob(buf + 3);
+ } else {
+ request = data_blob(NULL, 0);
+ }
- fstrcpy(request.data.auth.user, user);
- fstrcpy(request.data.auth.pass, pass);
+ if ((strncmp(buf, "PW ", 3) == 0)) {
+ /* The calling application wants us to use a local password (rather than winbindd) */
- result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+ opt_password = strndup((const char *)request.data, request.length);
- /* Display response */
-
- if (stdout_diagnostics) {
- if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
- d_printf("Reading winbind reply failed! (0x01)\n");
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
}
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
+ } else if (strncmp(buf, "KK", 2) == 0) {
- d_printf("%s (0x%x)\n",
- response.data.auth.nt_status_string,
- response.data.auth.nt_status);
} else {
- if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
- DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
- }
-
- DEBUG(3, ("%s (0x%x)\n",
- response.data.auth.nt_status_string,
- response.data.auth.nt_status));
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
}
-
- return (result == NSS_STATUS_SUCCESS);
-}
-static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state)
-{
- struct winbindd_request request;
- struct winbindd_response response;
- NSS_STATUS result;
- /* Send off request */
-
- ZERO_STRUCT(request);
- ZERO_STRUCT(response);
+ if (!ntlmssp_state) {
+ if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ return;
+ }
+ }
- fstrcpy(request.data.auth_crap.user, ntlmssp_state->user);
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, (const char *)request.data, request.length);
- fstrcpy(request.data.auth_crap.domain, ntlmssp_state->domain);
- fstrcpy(request.data.auth_crap.workstation, ntlmssp_state->workstation);
+ nt_status = ntlmssp_update(ntlmssp_state, NULL, request, &reply);
- memcpy(request.data.auth_crap.chal, ntlmssp_state->chal.data,
- MIN(ntlmssp_state->chal.length, 8));
-
- memcpy(request.data.auth_crap.lm_resp, ntlmssp_state->lm_resp.data,
- MIN(ntlmssp_state->lm_resp.length, sizeof(request.data.auth_crap.lm_resp)));
-
- memcpy(request.data.auth_crap.nt_resp, ntlmssp_state->lm_resp.data,
- MIN(ntlmssp_state->nt_resp.length, sizeof(request.data.auth_crap.nt_resp)));
-
- request.data.auth_crap.lm_resp_len = ntlmssp_state->lm_resp.length;
- request.data.auth_crap.nt_resp_len = ntlmssp_state->nt_resp.length;
-
- result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
-
- /* Display response */
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ char *reply_base64 = base64_encode_data_blob(reply);
+ x_fprintf(x_stdout, "TT %s\n", reply_base64);
+ SAFE_FREE(reply_base64);
+ data_blob_free(&reply);
+ DEBUG(10, ("NTLMSSP challenge\n"));
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
- if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
- return NT_STATUS_UNSUCCESSFUL;
+ ntlmssp_end(&ntlmssp_state);
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
+ DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
+ } else {
+ x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context);
+ DEBUG(10, ("NTLMSSP OK!\n"));
}
- return NT_STATUS(response.data.auth.nt_status);
+ data_blob_free(&request);
}
-static void manage_squid_ntlmssp_request(enum squid_mode squid_mode,
+static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
char *buf, int length)
{
- static NTLMSSP_STATE *ntlmssp_state;
+ static struct ntlmssp_state *ntlmssp_state = NULL;
DATA_BLOB request, reply;
NTSTATUS nt_status;
+ BOOL first = False;
+
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
- if (!ntlmssp_state) {
- ntlmssp_server_start(&ntlmssp_state);
- ntlmssp_state->check_password = winbind_pw_check;
- ntlmssp_state->get_domain = get_winbind_domain;
- ntlmssp_state->get_global_myname = get_winbind_netbios_name;
+ if (strlen(buf) > 3) {
+ request = base64_decode_data_blob(buf + 3);
+ } else {
+ request = data_blob(NULL, 0);
+ }
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+ /* We asked for a password and obviously got it :-) */
+
+ opt_password = strndup((const char *)request.data, request.length);
+
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if (opt_password == NULL) {
+
+ /* Request a password from the calling process. After
+ sending it, the calling process should retry asking for the negotiate. */
+
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return;
}
- if (strlen(buf) < 3) {
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
+ } else if (strncmp(buf, "TT", 2) == 0) {
+
+ } else {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
x_fprintf(x_stdout, "BH\n");
return;
}
-
- request = base64_decode_data_blob(buf + 3);
-
- DEBUG(0, ("got NTLMSSP packet:\n"));
- dump_data(0, request.data, request.length);
- nt_status = ntlmssp_server_update(ntlmssp_state, request, &reply);
+ if (!ntlmssp_state) {
+ if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_client(&ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ return;
+ }
+ first = True;
+ }
+
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, (const char *)request.data, request.length);
+
+ nt_status = ntlmssp_update(ntlmssp_state, NULL, request, &reply);
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
char *reply_base64 = base64_encode_data_blob(reply);
- x_fprintf(x_stdout, "TT %s\n", reply_base64);
+ if (first) {
+ x_fprintf(x_stdout, "YR %s\n", reply_base64);
+ } else {
+ x_fprintf(x_stdout, "KK %s\n", reply_base64);
+ }
SAFE_FREE(reply_base64);
data_blob_free(&reply);
- } else if (!NT_STATUS_IS_OK(nt_status)) {
- x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
+ DEBUG(10, ("NTLMSSP challenge\n"));
+ } else if (NT_STATUS_IS_OK(nt_status)) {
+ x_fprintf(x_stdout, "AF\n");
+ DEBUG(10, ("NTLMSSP OK!\n"));
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
} else {
- x_fprintf(x_stdout, "AF %s\\%s\n", ntlmssp_state->domain, ntlmssp_state->user);
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
}
data_blob_free(&request);
}
-static void manage_squid_basic_request(enum squid_mode squid_mode,
+static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
char *buf, int length)
{
char *user, *pass;
@@ -263,13 +472,13 @@ static void manage_squid_basic_request(enum squid_mode squid_mode,
pass=memchr(buf,' ',length);
if (!pass) {
DEBUG(2, ("Password not found. Denying access\n"));
- x_fprintf(x_stderr, "ERR\n");
+ x_fprintf(x_stdout, "ERR\n");
return;
}
*pass='\0';
pass++;
- if (squid_mode == SQUID_2_5_BASIC) {
+ if (stdio_helper_mode == SQUID_2_5_BASIC) {
rfc1738_unescape(user);
rfc1738_unescape(pass);
}
@@ -281,17 +490,868 @@ static void manage_squid_basic_request(enum squid_mode squid_mode,
}
}
-static void manage_squid_request(enum squid_mode squid_mode)
+static void offer_gss_spnego_mechs(void) {
+
+ DATA_BLOB token;
+ struct spnego_data spnego;
+ ssize_t len;
+ char *reply_base64;
+
+ pstring principal;
+ pstring myname_lower;
+
+ ZERO_STRUCT(spnego);
+
+ pstrcpy(myname_lower, global_myname());
+ strlower_m(myname_lower);
+
+ pstr_sprintf(principal, "%s$@%s", myname_lower, lp_realm());
+
+ /* Server negTokenInit (mech offerings) */
+ spnego.type = SPNEGO_NEG_TOKEN_INIT;
+ spnego.negTokenInit.mechTypes = smb_xmalloc(sizeof(char *) * 3);
+#ifdef HAVE_KRB5
+ spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
+ spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
+ spnego.negTokenInit.mechTypes[2] = NULL;
+#else
+ spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP);
+ spnego.negTokenInit.mechTypes[1] = NULL;
+#endif
+
+
+ spnego.negTokenInit.mechListMIC = data_blob(principal,
+ strlen(principal));
+
+ len = write_spnego_data(&token, &spnego);
+ free_spnego_data(&spnego);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ reply_base64 = base64_encode_data_blob(token);
+ x_fprintf(x_stdout, "TT %s *\n", reply_base64);
+
+ SAFE_FREE(reply_base64);
+ data_blob_free(&token);
+ DEBUG(10, ("sent SPNEGO negTokenInit\n"));
+ return;
+}
+
+static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ static struct ntlmssp_state *ntlmssp_state = NULL;
+ struct spnego_data request, response;
+ DATA_BLOB token;
+ NTSTATUS status;
+ ssize_t len;
+
+ char *user = NULL;
+ char *domain = NULL;
+
+ const char *reply_code;
+ char *reply_base64;
+ pstring reply_argument;
+
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("SPENGO query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
+ } else if (strncmp(buf, "KK", 2) == 0) {
+
+ } else {
+ DEBUG(1, ("SPENGO query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if ( (strlen(buf) == 2)) {
+
+ /* no client data, get the negTokenInit offering
+ mechanisms */
+
+ offer_gss_spnego_mechs();
+ return;
+ }
+
+ /* All subsequent requests have a blob. This might be negTokenInit or negTokenTarg */
+
+ if (strlen(buf) <= 3) {
+ DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ token = base64_decode_data_blob(buf + 3);
+ len = read_spnego_data(token, &request);
+ data_blob_free(&token);
+
+ if (len == -1) {
+ DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (request.type == SPNEGO_NEG_TOKEN_INIT) {
+
+ /* Second request from Client. This is where the
+ client offers its mechanism to use. */
+
+ if ( (request.negTokenInit.mechTypes == NULL) ||
+ (request.negTokenInit.mechTypes[0] == NULL) ) {
+ DEBUG(1, ("Client did not offer any mechanism"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
+
+ if ( request.negTokenInit.mechToken.data == NULL ) {
+ DEBUG(1, ("Client did not provide NTLMSSP data\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if ( ntlmssp_state != NULL ) {
+ DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
+ "already got one\n"));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_end(&ntlmssp_state);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
+ return;
+ }
+
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, (const char *)request.negTokenInit.mechToken.data,
+ request.negTokenInit.mechToken.length);
+
+ response.type = SPNEGO_NEG_TOKEN_TARG;
+ response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
+ response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
+
+ status = ntlmssp_update(ntlmssp_state,
+ NULL,
+ request.negTokenInit.mechToken,
+ &response.negTokenTarg.responseToken);
+ }
+
+#ifdef HAVE_KRB5
+ if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
+
+ char *principal;
+ DATA_BLOB auth_data;
+ DATA_BLOB ap_rep;
+ DATA_BLOB session_key;
+
+ if ( request.negTokenInit.mechToken.data == NULL ) {
+ DEBUG(1, ("Client did not provide Kerberos data\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ response.type = SPNEGO_NEG_TOKEN_TARG;
+ response.negTokenTarg.supportedMech = strdup(OID_KERBEROS5_OLD);
+ response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
+ response.negTokenTarg.responseToken = data_blob(NULL, 0);
+
+ status = ads_verify_ticket(lp_realm(),
+ &request.negTokenInit.mechToken,
+ &principal, &auth_data, &ap_rep,
+ &session_key);
+
+ /* Now in "principal" we have the name we are
+ authenticated as. */
+
+ if (NT_STATUS_IS_OK(status)) {
+
+ domain = strchr(principal, '@');
+
+ if (domain == NULL) {
+ DEBUG(1, ("Did not get a valid principal "
+ "from ads_verify_ticket\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ *domain++ = '\0';
+ domain = strdup(domain);
+ user = strdup(principal);
+
+ data_blob_free(&ap_rep);
+ data_blob_free(&auth_data);
+
+ SAFE_FREE(principal);
+ }
+ }
+#endif
+
+ } else {
+
+ if ( (request.negTokenTarg.supportedMech == NULL) ||
+ ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) {
+ /* Kerberos should never send a negTokenTarg, OID_NTLMSSP
+ is the only one we support that sends this stuff */
+ DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n",
+ request.negTokenTarg.supportedMech));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (request.negTokenTarg.responseToken.data == NULL) {
+ DEBUG(1, ("Got a negTokenTarg without a responseToken!\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ status = ntlmssp_update(ntlmssp_state,
+ NULL,
+ request.negTokenTarg.responseToken,
+ &response.negTokenTarg.responseToken);
+
+ response.type = SPNEGO_NEG_TOKEN_TARG;
+ response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
+ response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
+
+ if (NT_STATUS_IS_OK(status)) {
+ user = strdup(ntlmssp_state->user);
+ domain = strdup(ntlmssp_state->domain);
+ ntlmssp_end(&ntlmssp_state);
+ }
+ }
+
+ free_spnego_data(&request);
+
+ if (NT_STATUS_IS_OK(status)) {
+ response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
+ reply_code = "AF";
+ pstr_sprintf(reply_argument, "%s\\%s", domain, user);
+ } else if (NT_STATUS_EQUAL(status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ reply_code = "TT";
+ pstr_sprintf(reply_argument, "*");
+ } else {
+ response.negTokenTarg.negResult = SPNEGO_REJECT;
+ reply_code = "NA";
+ pstrcpy(reply_argument, nt_errstr(status));
+ }
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+
+ len = write_spnego_data(&token, &response);
+ free_spnego_data(&response);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ reply_base64 = base64_encode_data_blob(token);
+
+ x_fprintf(x_stdout, "%s %s %s\n",
+ reply_code, reply_base64, reply_argument);
+
+ SAFE_FREE(reply_base64);
+ data_blob_free(&token);
+
+ return;
+}
+
+static struct ntlmssp_state *client_ntlmssp_state = NULL;
+
+static BOOL manage_client_ntlmssp_init(struct spnego_data spnego)
+{
+ NTSTATUS status;
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+ DATA_BLOB to_server;
+ char *to_server_base64;
+ const char *my_mechs[] = {OID_NTLMSSP, NULL};
+
+ DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
+
+ if (client_ntlmssp_state != NULL) {
+ DEBUG(1, ("Request for initial SPNEGO request where "
+ "we already have a state\n"));
+ return False;
+ }
+
+ if (!client_ntlmssp_state) {
+ if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
+ return False;
+ }
+ }
+
+
+ if (opt_password == NULL) {
+
+ /* Request a password from the calling process. After
+ sending it, the calling process should retry with
+ the negTokenInit. */
+
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return True;
+ }
+
+ spnego.type = SPNEGO_NEG_TOKEN_INIT;
+ spnego.negTokenInit.mechTypes = my_mechs;
+ spnego.negTokenInit.reqFlags = 0;
+ spnego.negTokenInit.mechListMIC = null_blob;
+
+ status = ntlmssp_update(client_ntlmssp_state,
+ NULL,
+ null_blob,
+ &spnego.negTokenInit.mechToken);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED, got: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(&client_ntlmssp_state);
+ return False;
+ }
+
+ write_spnego_data(&to_server, &spnego);
+ data_blob_free(&spnego.negTokenInit.mechToken);
+
+ to_server_base64 = base64_encode_data_blob(to_server);
+ data_blob_free(&to_server);
+ x_fprintf(x_stdout, "KK %s\n", to_server_base64);
+ SAFE_FREE(to_server_base64);
+ return True;
+}
+
+static void manage_client_ntlmssp_targ(struct spnego_data spnego)
+{
+ NTSTATUS status;
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+ DATA_BLOB request;
+ DATA_BLOB to_server;
+ char *to_server_base64;
+
+ DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
+
+ if (client_ntlmssp_state == NULL) {
+ DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
+ x_fprintf(x_stdout, "NA\n");
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
+ x_fprintf(x_stdout, "AF\n");
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ status = ntlmssp_update(client_ntlmssp_state,
+ NULL,
+ spnego.negTokenTarg.responseToken,
+ &request);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
+ "ntlmssp_update, got: %s\n",
+ nt_errstr(status)));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ spnego.type = SPNEGO_NEG_TOKEN_TARG;
+ spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ spnego.negTokenTarg.supportedMech = OID_NTLMSSP;
+ spnego.negTokenTarg.responseToken = request;
+ spnego.negTokenTarg.mechListMIC = null_blob;
+
+ write_spnego_data(&to_server, &spnego);
+ data_blob_free(&request);
+
+ to_server_base64 = base64_encode_data_blob(to_server);
+ data_blob_free(&to_server);
+ x_fprintf(x_stdout, "KK %s\n", to_server_base64);
+ SAFE_FREE(to_server_base64);
+ return;
+}
+
+#ifdef HAVE_KRB5
+
+static BOOL manage_client_krb5_init(struct spnego_data spnego)
+{
+ char *principal;
+ DATA_BLOB tkt, to_server;
+ DATA_BLOB session_key_krb5 = data_blob(NULL, 0);
+ struct spnego_data reply;
+ char *reply_base64;
+ int retval;
+
+ const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
+ ssize_t len;
+
+ if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
+ (spnego.negTokenInit.mechListMIC.length == 0) ) {
+ DEBUG(1, ("Did not get a principal for krb5\n"));
+ return False;
+ }
+
+ principal = malloc(spnego.negTokenInit.mechListMIC.length+1);
+
+ if (principal == NULL) {
+ DEBUG(1, ("Could not malloc principal\n"));
+ return False;
+ }
+
+ memcpy(principal, spnego.negTokenInit.mechListMIC.data,
+ spnego.negTokenInit.mechListMIC.length);
+ principal[spnego.negTokenInit.mechListMIC.length] = '\0';
+
+ retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
+
+ if (retval) {
+
+ pstring user;
+
+ /* Let's try to first get the TGT, for that we need a
+ password. */
+
+ if (opt_password == NULL) {
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return True;
+ }
+
+ pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
+
+ if ((retval = kerberos_kinit_password(user, opt_password,
+ 0, NULL))) {
+ DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
+ return False;
+ }
+
+ retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
+
+ if (retval) {
+ DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
+ return False;
+ }
+ }
+
+ data_blob_free(&session_key_krb5);
+
+ ZERO_STRUCT(reply);
+
+ reply.type = SPNEGO_NEG_TOKEN_INIT;
+ reply.negTokenInit.mechTypes = my_mechs;
+ reply.negTokenInit.reqFlags = 0;
+ reply.negTokenInit.mechToken = tkt;
+ reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
+
+ len = write_spnego_data(&to_server, &reply);
+ data_blob_free(&tkt);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ return False;
+ }
+
+ reply_base64 = base64_encode_data_blob(to_server);
+ x_fprintf(x_stdout, "KK %s *\n", reply_base64);
+
+ SAFE_FREE(reply_base64);
+ data_blob_free(&to_server);
+ DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
+ return True;
+}
+
+static void manage_client_krb5_targ(struct spnego_data spnego)
+{
+ switch (spnego.negTokenTarg.negResult) {
+ case SPNEGO_ACCEPT_INCOMPLETE:
+ DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
+ x_fprintf(x_stdout, "BH\n");
+ break;
+ case SPNEGO_ACCEPT_COMPLETED:
+ DEBUG(10, ("Accept completed\n"));
+ x_fprintf(x_stdout, "AF\n");
+ break;
+ case SPNEGO_REJECT:
+ DEBUG(10, ("Rejected\n"));
+ x_fprintf(x_stdout, "NA\n");
+ break;
+ default:
+ DEBUG(1, ("Got an invalid negTokenTarg\n"));
+ x_fprintf(x_stdout, "AF\n");
+ }
+}
+
+#endif
+
+static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ DATA_BLOB request;
+ struct spnego_data spnego;
+ ssize_t len;
+
+ if (strlen(buf) <= 3) {
+ DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ request = base64_decode_data_blob(buf+3);
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+
+ /* We asked for a password and obviously got it :-) */
+
+ opt_password = strndup((const char *)request.data, request.length);
+
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if ( (strncmp(buf, "TT ", 3) != 0) &&
+ (strncmp(buf, "AF ", 3) != 0) &&
+ (strncmp(buf, "NA ", 3) != 0) ) {
+ DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ /* So we got a server challenge to generate a SPNEGO
+ client-to-server request... */
+
+ len = read_spnego_data(request, &spnego);
+ data_blob_free(&request);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
+
+ /* The server offers a list of mechanisms */
+
+ char **mechType = spnego.negTokenInit.mechTypes;
+
+ while (*mechType != NULL) {
+
+#ifdef HAVE_KRB5
+ if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
+ (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
+ if (manage_client_krb5_init(spnego))
+ goto out;
+ }
+#endif
+
+ if (strcmp(*mechType, OID_NTLMSSP) == 0) {
+ if (manage_client_ntlmssp_init(spnego))
+ goto out;
+ }
+
+ mechType++;
+ }
+
+ DEBUG(1, ("Server offered no compatible mechanism\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
+
+ if (spnego.negTokenTarg.supportedMech == NULL) {
+ /* On accept/reject Windows does not send the
+ mechanism anymore. Handle that here and
+ shut down the mechanisms. */
+
+ switch (spnego.negTokenTarg.negResult) {
+ case SPNEGO_ACCEPT_COMPLETED:
+ x_fprintf(x_stdout, "AF\n");
+ break;
+ case SPNEGO_REJECT:
+ x_fprintf(x_stdout, "NA\n");
+ break;
+ default:
+ DEBUG(1, ("Got a negTokenTarg with no mech and an "
+ "unknown negResult: %d\n",
+ spnego.negTokenTarg.negResult));
+ x_fprintf(x_stdout, "BH\n");
+ }
+
+ ntlmssp_end(&client_ntlmssp_state);
+ goto out;
+ }
+
+ if (strcmp(spnego.negTokenTarg.supportedMech,
+ OID_NTLMSSP) == 0) {
+ manage_client_ntlmssp_targ(spnego);
+ goto out;
+ }
+
+#if HAVE_KRB5
+ if (strcmp(spnego.negTokenTarg.supportedMech,
+ OID_KERBEROS5_OLD) == 0) {
+ manage_client_krb5_targ(spnego);
+ goto out;
+ }
+#endif
+
+ }
+
+ DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+
+ out:
+ free_spnego_data(&spnego);
+ return;
+}
+
+static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
+{
+ char *request, *parameter;
+ static DATA_BLOB challenge;
+ static DATA_BLOB lm_response;
+ static DATA_BLOB nt_response;
+ static char *full_username;
+ static char *username;
+ static char *domain;
+ static char *plaintext_password;
+ static BOOL ntlm_server_1_user_session_key;
+ static BOOL ntlm_server_1_lm_session_key;
+
+ if (strequal(buf, ".")) {
+ if (!full_username && !username) {
+ x_fprintf(x_stdout, "Error: No username supplied!\n");
+ } else if (plaintext_password) {
+ /* handle this request as plaintext */
+ if (!full_username) {
+ if (asprintf(&full_username, "%s%c%s", domain, *lp_winbind_separator(), username) == -1) {
+ x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
+ return;
+ }
+ }
+ if (check_plaintext_auth(full_username, plaintext_password, False)) {
+ x_fprintf(x_stdout, "Authenticated: Yes\n");
+ } else {
+ x_fprintf(x_stdout, "Authenticated: No\n");
+ }
+ } else if (!lm_response.data && !nt_response.data) {
+ x_fprintf(x_stdout, "Error: No password supplied!\n");
+ } else if (!challenge.data) {
+ x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
+ } else {
+ char *error_string = NULL;
+ DATA_BLOB lm_key;
+ DATA_BLOB user_session_key;
+ uint32 flags = 0;
+
+ if (full_username && !username) {
+ fstring fstr_user;
+ fstring fstr_domain;
+
+ if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
+ /* username might be 'tainted', don't print into our new-line deleimianted stream */
+ x_fprintf(x_stdout, "Error: Could not parse into domain and username\n");
+ }
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ username = smb_xstrdup(fstr_user);
+ domain = smb_xstrdup(fstr_domain);
+ }
+
+ if (!domain) {
+ domain = smb_xstrdup(lp_workgroup());
+ }
+
+ if (ntlm_server_1_lm_session_key)
+ flags |= NTLM_AUTH_FLAG_LMKEY;
+
+ if (ntlm_server_1_user_session_key)
+ flags |= NTLM_AUTH_FLAG_USER_SESSION_KEY;
+
+ if (!NT_STATUS_IS_OK(
+ local_pw_check_specified(username,
+ domain,
+ global_myname(),
+ &challenge,
+ &lm_response,
+ &nt_response,
+ flags,
+ &lm_key,
+ &user_session_key,
+ &error_string,
+ NULL))) {
+
+ x_fprintf(x_stdout, "Authenticated: No\n");
+ x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
+ SAFE_FREE(error_string);
+ } else {
+ static char zeros[16];
+ char *hex_lm_key;
+ char *hex_user_session_key;
+
+ x_fprintf(x_stdout, "Authenticated: Yes\n");
+
+ if (ntlm_server_1_lm_session_key
+ && lm_key.length
+ && (memcmp(zeros, lm_key.data,
+ lm_key.length) != 0)) {
+ hex_encode(lm_key.data,
+ lm_key.length,
+ &hex_lm_key);
+ x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
+ SAFE_FREE(hex_lm_key);
+ }
+
+ if (ntlm_server_1_user_session_key
+ && user_session_key.length
+ && (memcmp(zeros, user_session_key.data,
+ user_session_key.length) != 0)) {
+ hex_encode(user_session_key.data,
+ user_session_key.length,
+ &hex_user_session_key);
+ x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
+ SAFE_FREE(hex_user_session_key);
+ }
+ }
+ }
+ /* clear out the state */
+ challenge = data_blob(NULL, 0);
+ nt_response = data_blob(NULL, 0);
+ lm_response = data_blob(NULL, 0);
+ SAFE_FREE(full_username);
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ SAFE_FREE(plaintext_password);
+ ntlm_server_1_user_session_key = False;
+ ntlm_server_1_lm_session_key = False;
+ x_fprintf(x_stdout, ".\n");
+
+ return;
+ }
+
+ request = buf;
+
+ /* Indicates a base64 encoded structure */
+ parameter = strstr(request, ":: ");
+ if (!parameter) {
+ parameter = strstr(request, ": ");
+
+ if (!parameter) {
+ DEBUG(0, ("Parameter not found!\n"));
+ x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
+ return;
+ }
+
+ parameter[0] ='\0';
+ parameter++;
+ parameter[0] ='\0';
+ parameter++;
+
+ } else {
+ parameter[0] ='\0';
+ parameter++;
+ parameter[0] ='\0';
+ parameter++;
+ parameter[0] ='\0';
+ parameter++;
+
+ base64_decode_inplace(parameter);
+ }
+
+ if (strequal(request, "LANMAN-Challenge")) {
+ challenge = strhex_to_data_blob(parameter);
+ if (challenge.length != 8) {
+ x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
+ parameter,
+ (int)challenge.length);
+ challenge = data_blob(NULL, 0);
+ }
+ } else if (strequal(request, "NT-Response")) {
+ nt_response = strhex_to_data_blob(parameter);
+ if (nt_response.length < 24) {
+ x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
+ parameter,
+ (int)nt_response.length);
+ nt_response = data_blob(NULL, 0);
+ }
+ } else if (strequal(request, "LANMAN-Response")) {
+ lm_response = strhex_to_data_blob(parameter);
+ if (lm_response.length != 24) {
+ x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
+ parameter,
+ (int)lm_response.length);
+ lm_response = data_blob(NULL, 0);
+ }
+ } else if (strequal(request, "Password")) {
+ plaintext_password = smb_xstrdup(parameter);
+ } else if (strequal(request, "NT-Domain")) {
+ domain = smb_xstrdup(parameter);
+ } else if (strequal(request, "Username")) {
+ username = smb_xstrdup(parameter);
+ } else if (strequal(request, "Full-Username")) {
+ full_username = smb_xstrdup(parameter);
+ } else if (strequal(request, "Request-User-Session-Key")) {
+ ntlm_server_1_user_session_key = strequal(parameter, "Yes");
+ } else if (strequal(request, "Request-LanMan-Session-Key")) {
+ ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
+ } else {
+ x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
+ }
+}
+
+static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn)
{
char buf[SQUID_BUFFER_SIZE+1];
int length;
char *c;
static BOOL err;
-
- if (x_fgets(buf, sizeof(buf)-1, x_stdin) == NULL) {
- DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", errno,
- strerror(errno)));
- exit(1); /* BIIG buffer */
+
+ /* this is not a typo - x_fgets doesn't work too well under squid */
+ if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
+ if (ferror(stdin)) {
+ DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
+ strerror(ferror(stdin))));
+
+ exit(1); /* BIIG buffer */
+ }
+ exit(0);
}
c=memchr(buf,'\n',sizeof(buf)-1);
@@ -317,65 +1377,20 @@ static void manage_squid_request(enum squid_mode squid_mode)
return;
}
- if (squid_mode == SQUID_2_5_BASIC || squid_mode == SQUID_2_4_BASIC) {
- manage_squid_basic_request(squid_mode, buf, length);
- } else if (squid_mode == SQUID_2_5_NTLMSSP) {
- manage_squid_ntlmssp_request(squid_mode, buf, length);
- }
+ fn(helper_mode, buf, length);
}
-static void squid_stream(enum squid_mode squid_mode) {
+static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
/* initialize FDescs */
x_setbuf(x_stdout, NULL);
x_setbuf(x_stderr, NULL);
while(1) {
- manage_squid_request(squid_mode);
+ manage_squid_request(stdio_mode, fn);
}
}
-/* Authenticate a user with a challenge/response */
-
-static BOOL check_auth_crap(void)
-{
- struct winbindd_request request;
- struct winbindd_response response;
- NSS_STATUS result;
- /* Send off request */
-
- ZERO_STRUCT(request);
- ZERO_STRUCT(response);
-
- fstrcpy(request.data.auth_crap.user, username);
-
- fstrcpy(request.data.auth_crap.domain, domain);
- fstrcpy(request.data.auth_crap.workstation, workstation);
-
- memcpy(request.data.auth_crap.chal, challenge, MIN(challenge_len, 8));
-
- memcpy(request.data.auth_crap.lm_resp, lm_response, MIN(lm_response_len, sizeof(request.data.auth_crap.lm_resp)));
-
- memcpy(request.data.auth_crap.nt_resp, nt_response, MIN(nt_response_len, sizeof(request.data.auth_crap.nt_resp)));
-
- request.data.auth_crap.lm_resp_len = lm_response_len;
- request.data.auth_crap.nt_resp_len = nt_response_len;
-
- result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
-
- /* Display response */
-
- if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
- d_printf("Reading winbind reply failed! (0x01)\n");
- }
-
- d_printf("%s (0x%x)\n",
- response.data.auth.nt_status_string,
- response.data.auth.nt_status);
-
- return result == NSS_STATUS_SUCCESS;
-}
-
/* Main program */
enum {
@@ -386,94 +1401,48 @@ enum {
OPT_RESPONSE,
OPT_LM,
OPT_NT,
- OPT_PASSWORD
+ OPT_PASSWORD,
+ OPT_LM_KEY,
+ OPT_USER_SESSION_KEY,
+ OPT_DIAGNOSTICS,
+ OPT_REQUIRE_MEMBERSHIP
};
-/*************************************************************
- Routine to set hex password characters into an allocated array.
-**************************************************************/
-
-static void hex_encode(const uint8_t *buff_in, size_t len, char **out_hex_buffer)
+ int main(int argc, const char **argv)
{
- 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]);
-}
-
-/*************************************************************
- Routine to get the 32 hex characters and turn them
- into a 16 byte array.
-**************************************************************/
-
-static BOOL hex_decode(const char *hex_buf_in, uint8_t **out_buffer, size_t *size)
-{
- int i;
- size_t hex_buf_in_len = strlen(hex_buf_in);
- uint8_t partial_byte_hex;
- uint8_t partial_byte;
- const char *hexchars = "0123456789ABCDEF";
- char *p;
- BOOL high = True;
-
- if (!hex_buf_in)
- return (False);
-
- *size = (hex_buf_in_len + 1) / 2;
-
- *out_buffer = smb_xmalloc(*size);
-
- for (i = 0; i < hex_buf_in_len; i++) {
- partial_byte_hex = toupper(hex_buf_in[i]);
-
- p = strchr(hexchars, partial_byte_hex);
-
- if (!p)
- return (False);
-
- partial_byte = PTR_DIFF(p, hexchars);
+ static const char *helper_protocol;
+ int opt;
- if (high) {
- (*out_buffer)[i / 2] = (partial_byte << 4);
- } else {
- (*out_buffer)[i / 2] |= partial_byte;
- }
- high = !high;
- }
- return (True);
-}
+ poptContext pc;
+ /* NOTE: DO NOT change this interface without considering the implications!
+ This is an external interface, which other programs will use to interact
+ with this helper.
+ */
-int main(int argc, const char **argv)
-{
- int opt;
+ /* We do not use single-letter command abbreviations, because they harm future
+ interface stability. */
- poptContext pc;
struct poptOption long_options[] = {
POPT_AUTOHELP
-
{ "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
- { "username", 0, POPT_ARG_STRING, &username, OPT_USERNAME, "username"},
- { "domain", 0, POPT_ARG_STRING, &domain, OPT_DOMAIN, "domain name"},
- { "workstation", 0, POPT_ARG_STRING, &domain, OPT_WORKSTATION, "workstation"},
- { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
- { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
- { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
- { "password", 0, POPT_ARG_STRING, &password, OPT_PASSWORD, "User's plaintext password"},
- { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug },
- { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_configfile },
- { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version},
- { 0, 0, 0, 0 }
+ { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
+ { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
+ { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
};
/* Samba client initialisation */
- dbf = x_stderr;
-
+ setup_logging("ntlm_auth", DEBUG_STDOUT);
+
+ if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
+ d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
+ dyn_CONFIGFILE, strerror(errno));
+ exit(1);
+ }
+
/* Parse options */
pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
@@ -489,58 +1458,58 @@ int main(int argc, const char **argv)
POPT_CONTEXT_KEEP_FIRST);
while((opt = poptGetNextOpt(pc)) != -1) {
- switch (opt) {
- case OPT_CHALLENGE:
- if (!hex_decode(hex_challenge, &challenge, &challenge_len)) {
- fprintf(stderr, "hex decode of %s failed!\n", hex_challenge);
- exit(1);
- }
- break;
- case OPT_LM:
- if (!hex_decode(hex_lm_response, &lm_response, &lm_response_len)) {
- fprintf(stderr, "hex decode of %s failed!\n", lm_response);
- exit(1);
- }
- break;
- case OPT_NT:
- if (!hex_decode(hex_lm_response, &lm_response, &lm_response_len)) {
- fprintf(stderr, "hex decode of %s failed!\n", lm_response);
- exit(1);
- }
+ if (opt < -1) {
break;
}
}
+ if (opt < -1) {
+ fprintf(stderr, "%s: %s\n",
+ poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+ poptStrerror(opt));
+ return 1;
+ }
if (helper_protocol) {
- if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) {
- squid_stream(SQUID_2_5_NTLMSSP);
- } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) {
- squid_stream(SQUID_2_5_BASIC);
- } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) {
- squid_stream(SQUID_2_4_BASIC);
- } else {
- fprintf(stderr, "unknown helper protocol [%s]\n", helper_protocol);
- exit(1);
+ int i;
+ for (i=0; i<NUM_HELPER_MODES; i++) {
+ if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
+ squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
+ exit(0);
+ }
}
+ x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
+
+ for (i=0; i<NUM_HELPER_MODES; i++) {
+ x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
+ }
+
+ exit(1);
}
- if (domain == NULL) {
- domain = get_winbind_domain();
+ if (!opt_username) {
+ x_fprintf(x_stderr, "username must be specified!\n\n");
+ poptPrintHelp(pc, stderr, 0);
+ exit(1);
}
- if (workstation == NULL) {
- workstation = "";
+ if (opt_domain == NULL) {
+ opt_domain = lp_workgroup();
}
- if (challenge) {
- if (!check_auth_crap()) {
- exit(1);
- }
- } else if (password) {
- fstring user;
- snprintf(user, sizeof(user)-1, "%s%c%s", domain, winbind_separator(), username);
- if (!check_plaintext_auth(user, password, True)) {
- exit(1);
+ if (opt_workstation == NULL) {
+ opt_workstation = lp_netbios_name();
+ }
+
+ if (!opt_password) {
+ opt_password = getpass("password: ");
+ }
+
+ {
+ char *user;
+
+ asprintf(&user, "%s%c%s", opt_domain, *lp_winbind_separator(), opt_username);
+ if (!check_plaintext_auth(user, opt_password, True)) {
+ return 1;
}
}