summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2002-08-30 06:59:57 +0000
committerAndrew Tridgell <tridge@samba.org>2002-08-30 06:59:57 +0000
commitdcd029169424d8846c1fbb0b1527516a4a026b27 (patch)
tree0d1ca640b6c60ebf20458154b19c2f557a0b8f60
parenta6ace770eb9b11271803215f218bf772fa7d9faa (diff)
downloadsamba-dcd029169424d8846c1fbb0b1527516a4a026b27.tar.gz
samba-dcd029169424d8846c1fbb0b1527516a4a026b27.tar.bz2
samba-dcd029169424d8846c1fbb0b1527516a4a026b27.zip
convert the LDAP/SASL code to use GSS-SPNEGO if possible
we now do this: - look for suported SASL mechanisms on the LDAP server - choose GSS-SPNEGO if possible - within GSS-SPNEGO choose KRB5 if we can do a kinit - otherwise use NTLMSSP This change also means that we no longer rely on having a gssapi library to do ADS. todo: - add TLS/SSL support over LDAP - change to using LDAP/SSL for password change in ADS (This used to be commit b04e91f660d3b26d23044075d4a7e707eb41462d)
-rw-r--r--source3/include/includes.h6
-rw-r--r--source3/libads/ldap.c7
-rw-r--r--source3/libads/sasl.c248
-rw-r--r--source3/libsmb/cliconnect.c13
-rw-r--r--source3/libsmb/clikrb5.c8
-rw-r--r--source3/libsmb/clispnego.c4
-rw-r--r--source3/utils/net_ads.c4
7 files changed, 243 insertions, 47 deletions
diff --git a/source3/include/includes.h b/source3/include/includes.h
index f1c8c50df4..544487f273 100644
--- a/source3/include/includes.h
+++ b/source3/include/includes.h
@@ -410,18 +410,14 @@
#if HAVE_GSSAPI_GSSAPI_H
#include <gssapi/gssapi.h>
-#else
-#undef HAVE_KRB5
#endif
#if HAVE_GSSAPI_GSSAPI_GENERIC_H
#include <gssapi/gssapi_generic.h>
-#else
-#undef HAVE_KRB5
#endif
/* we support ADS if we have krb5 and ldap libs */
-#if defined(HAVE_KRB5) && defined(HAVE_LDAP) && defined(HAVE_GSSAPI)
+#if defined(HAVE_KRB5) && defined(HAVE_LDAP)
#define HAVE_ADS
#endif
diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c
index f0c4ad9040..2cfbedc6d4 100644
--- a/source3/libads/ldap.c
+++ b/source3/libads/ldap.c
@@ -226,7 +226,7 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads)
/* try via DNS */
if (ads_try_dns(ads)) {
goto got_connection;
- }
+ }
/* try via netbios lookups */
if (!lp_disable_netbios() && ads_try_netbios(ads)) {
@@ -274,11 +274,6 @@ got_connection:
}
#endif
- if (ads->auth.password) {
- if ((code = ads_kinit_password(ads)))
- return ADS_ERROR_KRB5(code);
- }
-
if (ads->auth.no_bind) {
return ADS_SUCCESS;
}
diff --git a/source3/libads/sasl.c b/source3/libads/sasl.c
index 81dedb0a81..12a5722319 100644
--- a/source3/libads/sasl.c
+++ b/source3/libads/sasl.c
@@ -22,37 +22,192 @@
#ifdef HAVE_ADS
-#if USE_CYRUS_SASL
-/*
- this is a minimal interact function, just enough for SASL to talk
- GSSAPI/kerberos to W2K
- Error handling is a bit of a problem. I can't see how to get Cyrus-sasl
- to give sensible errors
+/*
+ perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
+ we fit on one socket??)
*/
-static int sasl_interact(LDAP *ld,unsigned flags,void *defaults,void *in)
+static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
{
- sasl_interact_t *interact = in;
+ const char *mechs[] = {OID_NTLMSSP, NULL};
+ DATA_BLOB msg1;
+ DATA_BLOB blob, chal1, chal2, auth;
+ uint8 challenge[8];
+ uint8 nthash[24], lmhash[24], sess_key[16];
+ uint32 neg_flags;
+ struct berval cred, *scred;
+ ADS_STATUS status;
+ extern pstring global_myname;
+ int rc;
+
+ neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
+ NTLMSSP_NEGOTIATE_128 |
+ NTLMSSP_NEGOTIATE_NTLM;
+
+ memset(sess_key, 0, 16);
- while (interact->id != SASL_CB_LIST_END) {
- interact->result = strdup("");
- interact->len = strlen(interact->result);
- interact++;
+ /* generate the ntlmssp negotiate packet */
+ msrpc_gen(&blob, "CddB",
+ "NTLMSSP",
+ NTLMSSP_NEGOTIATE,
+ neg_flags,
+ sess_key, 16);
+
+ /* and wrap it in a SPNEGO wrapper */
+ msg1 = gen_negTokenTarg(mechs, blob);
+ data_blob_free(&blob);
+
+ cred.bv_val = msg1.data;
+ cred.bv_len = msg1.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+ if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
+ status = ADS_ERROR(rc);
+ goto failed;
}
-
- return LDAP_SUCCESS;
+
+ blob = data_blob(scred->bv_val, scred->bv_len);
+
+ /* the server gives us back two challenges */
+ if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
+ DEBUG(3,("Failed to parse challenges\n"));
+ status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
+ goto failed;
+ }
+
+ data_blob_free(&blob);
+
+ /* encrypt the password with the challenge */
+ memcpy(challenge, chal1.data + 24, 8);
+ SMBencrypt(ads->auth.password, challenge,lmhash);
+ SMBNTencrypt(ads->auth.password, challenge,nthash);
+
+ data_blob_free(&chal1);
+ data_blob_free(&chal2);
+
+ /* this generates the actual auth packet */
+ msrpc_gen(&blob, "CdBBUUUBd",
+ "NTLMSSP",
+ NTLMSSP_AUTH,
+ lmhash, 24,
+ nthash, 24,
+ lp_workgroup(),
+ ads->auth.user_name,
+ global_myname,
+ sess_key, 16,
+ neg_flags);
+
+ /* wrap it in SPNEGO */
+ auth = spnego_gen_auth(blob);
+
+ data_blob_free(&blob);
+
+ /* now send the auth packet and we should be done */
+ cred.bv_val = auth.data;
+ cred.bv_len = auth.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+
+ return ADS_ERROR(rc);
+
+failed:
+ return status;
+}
+
+/*
+ perform a LDAP/SASL/SPNEGO/KRB5 bind
+*/
+static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
+{
+ DATA_BLOB blob;
+ struct berval cred, *scred;
+ int rc;
+
+ blob = spnego_gen_negTokenTarg(principal);
+
+ if (!blob.data) {
+ return ADS_ERROR(LDAP_OPERATIONS_ERROR);
+ }
+
+ /* now send the auth packet and we should be done */
+ cred.bv_val = blob.data;
+ cred.bv_len = blob.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+
+ data_blob_free(&blob);
+
+ return ADS_ERROR(rc);
}
+
+/*
+ this performs a SASL/SPNEGO bind
+*/
+static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
+{
+ struct berval *scred;
+ int rc, i;
+ ADS_STATUS status;
+ DATA_BLOB blob;
+ char *principal;
+ char *OIDs[ASN1_MAX_OIDS];
+ BOOL got_kerberos_mechanism = False;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
+
+ if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
+ status = ADS_ERROR(rc);
+ goto failed;
+ }
+
+ blob = data_blob(scred->bv_val, scred->bv_len);
+
+#if 0
+ file_save("sasl_spnego.dat", blob.data, blob.length);
#endif
+ /* the server sent us the first part of the SPNEGO exchange in the negprot
+ reply */
+ if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
+ data_blob_free(&blob);
+ status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
+ goto failed;
+ }
+ data_blob_free(&blob);
+
+ /* make sure the server understands kerberos */
+ for (i=0;OIDs[i];i++) {
+ DEBUG(3,("got OID=%s\n", OIDs[i]));
+ if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
+ strcmp(OIDs[i], OID_KERBEROS5) == 0) {
+ got_kerberos_mechanism = True;
+ }
+ free(OIDs[i]);
+ }
+ DEBUG(3,("got principal=%s\n", principal));
+ if (got_kerberos_mechanism && ads_kinit_password(ads) == 0) {
+ return ads_sasl_spnego_krb5_bind(ads, principal);
+ }
+
+ /* lets do NTLMSSP ... this has the big advantage that we don't need
+ to sync clocks, and we don't rely on special versions of the krb5
+ library for HMAC_MD4 encryption */
+ return ads_sasl_spnego_ntlmssp_bind(ads);
+
+failed:
+ return status;
+}
+
+#ifdef HAVE_GSSAPI
#define MAX_GSS_PASSES 3
/* this performs a SASL/gssapi bind
we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
is very dependent on correctly configured DNS whereas
this routine is much less fragile
- see RFC2078 for details
+ see RFC2078 and RFC2222 for details
*/
-ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
+static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
{
int minor_status;
gss_name_t serv_name;
@@ -68,6 +223,7 @@ ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
uint8 *p;
uint32 max_msg_size;
char *sname;
+ unsigned sec_layer;
ADS_STATUS status;
krb5_principal principal;
krb5_context ctx;
@@ -159,22 +315,25 @@ ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
p = (uint8 *)output_token.value;
+ file_save("sasl_gssapi.dat", output_token.value, output_token.length);
+
max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
+ sec_layer = *p;
gss_release_buffer(&minor_status, &output_token);
output_token.value = malloc(strlen(ads->config.bind_path) + 8);
p = output_token.value;
- *p++ = 1; /* no sign or seal */
+ *p++ = 1; /* no sign & seal selection */
/* choose the same size as the server gave us */
*p++ = max_msg_size>>16;
*p++ = max_msg_size>>8;
*p++ = max_msg_size;
snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
- p += strlen(ads->config.bind_path);
+ p += strlen(p);
- output_token.length = strlen(ads->config.bind_path) + 8;
+ output_token.length = PTR_DIFF(p, output_token.value);
gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
&output_token, &conf_state,
@@ -198,18 +357,51 @@ ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
failed:
return status;
}
+#endif
+
+/* mapping between SASL mechanisms and functions */
+static struct {
+ const char *name;
+ ADS_STATUS (*fn)(ADS_STRUCT *);
+} sasl_mechanisms[] = {
+ {"GSS-SPNEGO", ads_sasl_spnego_bind},
+#ifdef HAVE_GSSAPI
+ {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
+#endif
+ {NULL, NULL}
+};
ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
{
-#if USE_CYRUS_SASL
- int rc;
- rc = ldap_sasl_interactive_bind_s(ads->ld, NULL, NULL, NULL, NULL,
- LDAP_SASL_QUIET,
- sasl_interact, NULL);
- return ADS_ERROR(rc);
-#else
- return ads_sasl_gssapi_bind(ads);
-#endif
+ const char *attrs[] = {"supportedSASLMechanisms", NULL};
+ char **values;
+ ADS_STATUS status;
+ int i, j;
+ void *res;
+
+ /* get a list of supported SASL mechanisms */
+ status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+ if (!ADS_ERR_OK(status)) return status;
+
+ values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
+
+ /* try our supported mechanisms in order */
+ for (i=0;sasl_mechanisms[i].name;i++) {
+ /* see if the server supports it */
+ for (j=0;values && values[j];j++) {
+ if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
+ DEBUG(4,("Found SASL mechanism %s\n", values[j]));
+ status = sasl_mechanisms[i].fn(ads);
+ ldap_value_free(values);
+ ldap_msgfree(res);
+ return status;
+ }
+ }
+ }
+
+ ldap_value_free(values);
+ ldap_msgfree(res);
+ return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
}
#endif
diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
index 0d033c9b59..e9b2b7b32e 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -446,7 +446,7 @@ static BOOL cli_session_setup_kerberos(struct cli_state *cli, char *principal, c
DEBUG(2,("Doing kerberos session setup\n"));
/* generate the encapsulated kerberos5 ticket */
- negTokenTarg = spnego_gen_negTokenTarg(cli, principal);
+ negTokenTarg = spnego_gen_negTokenTarg(principal);
if (!negTokenTarg.data) return False;
@@ -572,14 +572,14 @@ static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user,
{
char *principal;
char *OIDs[ASN1_MAX_OIDS];
- uint8 guid[16];
int i;
BOOL got_kerberos_mechanism = False;
+ DATA_BLOB blob;
DEBUG(2,("Doing spnego session setup (blob length=%d)\n", cli->secblob.length));
/* the server might not even do spnego */
- if (cli->secblob.length == 16) {
+ if (cli->secblob.length <= 16) {
DEBUG(3,("server didn't supply a full spnego negprot\n"));
goto ntlmssp;
}
@@ -588,11 +588,16 @@ static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user,
file_save("negprot.dat", cli->secblob.data, cli->secblob.length);
#endif
+ /* there is 16 bytes of GUID before the real spnego packet starts */
+ blob = data_blob(cli->secblob.data+16, cli->secblob.length-16);
+
/* the server sent us the first part of the SPNEGO exchange in the negprot
reply */
- if (!spnego_parse_negTokenInit(cli->secblob, guid, OIDs, &principal)) {
+ if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
+ data_blob_free(&blob);
return False;
}
+ data_blob_free(&blob);
/* make sure the server understands kerberos */
for (i=0;OIDs[i];i++) {
diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c
index 685c4a25e0..955a93285c 100644
--- a/source3/libsmb/clikrb5.c
+++ b/source3/libsmb/clikrb5.c
@@ -20,6 +20,10 @@
#include "includes.h"
+#ifndef ENCTYPE_ARCFOUR_HMAC
+#define ENCTYPE_ARCFOUR_HMAC 0x0017
+#endif
+
#ifdef HAVE_KRB5
/*
we can't use krb5_mk_req because w2k wants the service to be in a particular format
@@ -94,7 +98,9 @@ DATA_BLOB krb5_get_ticket(char *principal)
krb5_context context;
krb5_auth_context auth_context = NULL;
DATA_BLOB ret;
- krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
+ krb5_enctype enc_types[] = {ENCTYPE_ARCFOUR_HMAC,
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_NULL};
retval = krb5_init_context(&context);
if (retval) {
diff --git a/source3/libsmb/clispnego.c b/source3/libsmb/clispnego.c
index bc4d0ca348..1eeae8b171 100644
--- a/source3/libsmb/clispnego.c
+++ b/source3/libsmb/clispnego.c
@@ -79,7 +79,6 @@ DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16],
OIDs (the mechanisms) and a principal name string
*/
BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
- uint8 guid[16],
char *OIDs[ASN1_MAX_OIDS],
char **principal)
{
@@ -89,7 +88,6 @@ BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
asn1_load(&data, blob);
- asn1_read(&data, guid, 16);
asn1_start_tag(&data,ASN1_APPLICATION(0));
asn1_check_OID(&data,OID_SPNEGO);
asn1_start_tag(&data,ASN1_CONTEXT(0));
@@ -279,7 +277,7 @@ BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket)
generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
kerberos session setup
*/
-DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli, char *principal)
+DATA_BLOB spnego_gen_negTokenTarg(const char *principal)
{
DATA_BLOB tkt, tkt_wrapped, targ;
const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
diff --git a/source3/utils/net_ads.c b/source3/utils/net_ads.c
index 16450c5b29..eb1c3fe059 100644
--- a/source3/utils/net_ads.c
+++ b/source3/utils/net_ads.c
@@ -653,6 +653,10 @@ int net_ads_join(int argc, const char **argv)
return -1;
}
+ if (ads_kinit_password(ads)) {
+ return -1;
+ }
+
rc = ads_set_machine_password(ads, global_myname, password);
if (!ADS_ERR_OK(rc)) {
d_printf("ads_set_machine_password: %s\n", ads_errstr(rc));