summaryrefslogtreecommitdiffstats
path: root/source3/libads
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2001-12-08 11:18:56 +0000
committerAndrew Tridgell <tridge@samba.org>2001-12-08 11:18:56 +0000
commit5d378a280f74405fccbadbfb28e1066613c76fd8 (patch)
tree3b039a256136b760164c9357dc0ce4fb3986b735 /source3/libads
parent22a76a063213bdc514816440d3838e145c4ec340 (diff)
downloadsamba-5d378a280f74405fccbadbfb28e1066613c76fd8.tar.gz
samba-5d378a280f74405fccbadbfb28e1066613c76fd8.tar.xz
samba-5d378a280f74405fccbadbfb28e1066613c76fd8.zip
added internal sasl/gssapi code. This means we are no longer dependent on cyrus-sasl which makes the code much less fragile. Also added code to auto-determine the server name or realm
(This used to be commit 435fdf276a79c2a517adcd7726933aeef3fa924b)
Diffstat (limited to 'source3/libads')
-rw-r--r--source3/libads/ads_struct.c49
-rw-r--r--source3/libads/kerberos.c24
-rw-r--r--source3/libads/ldap.c138
-rw-r--r--source3/libads/sasl.c186
4 files changed, 279 insertions, 118 deletions
diff --git a/source3/libads/ads_struct.c b/source3/libads/ads_struct.c
index 15cbb328e8..72f2a32e64 100644
--- a/source3/libads/ads_struct.c
+++ b/source3/libads/ads_struct.c
@@ -22,7 +22,7 @@
#include "includes.h"
-static char *ads_build_dn(const char *realm)
+char *ads_build_dn(const char *realm)
{
char *p, *r;
int numdots = 0;
@@ -54,46 +54,6 @@ static char *ads_build_dn(const char *realm)
return ret;
}
-#ifdef HAVE_KRB5
-
-/*
- get the default relm from krb5.conf
-*/
-static char *get_default_realm(ADS_STRUCT *ads)
-{
- BOOL ret;
- krb5_context context;
- char *realm;
-
- ret = krb5_init_context(&context);
- if (ret) {
- DEBUG(1,("krb5_init_context failed (%s)\n", error_message(ret)));
- return NULL;
- }
-
- ret = krb5_get_default_realm(context, &realm);
- if (ret) {
- DEBUG(1,("krb5_get_default_realm failed (%s)\n", error_message(ret)));
- krb5_free_context(context);
- return NULL;
- } else {
- DEBUG(5,("krb5_get_default_realm got (%s)\n", realm));
- }
- krb5_free_context(context);
-
- return realm;
-}
-
-#else
-static char *get_default_realm(ADS_STRUCT *ads)
-{
- /* We can't do this if we don't have krb5,
- but save linking nightmares */
- DEBUG(5,("get_default_realm: not compiled with krb5.\n"));
- return NULL;
-}
-
-#endif
#ifdef HAVE_LDAP
/*
@@ -151,11 +111,10 @@ ADS_STRUCT *ads_init(const char *realm,
if (!ads->realm) {
ads->realm = strdup(lp_realm());
if (!ads->realm[0]) {
- ads->realm = get_default_realm(ads);
+ SAFE_FREE(ads->realm);
}
- if (!ads->realm) ads->realm = strdup("");
}
- if (!ads->bind_path) {
+ if (!ads->bind_path && ads->realm) {
ads->bind_path = ads_build_dn(ads->realm);
}
if (!ads->ldap_server) {
@@ -183,9 +142,11 @@ void ads_destroy(ADS_STRUCT **ads)
#endif
SAFE_FREE((*ads)->realm);
SAFE_FREE((*ads)->ldap_server);
+ SAFE_FREE((*ads)->ldap_server_name);
SAFE_FREE((*ads)->kdc_server);
SAFE_FREE((*ads)->bind_path);
SAFE_FREE((*ads)->password);
+ SAFE_FREE((*ads)->user_name);
ZERO_STRUCTP(*ads);
SAFE_FREE(*ads);
}
diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c
index 19e8ffdc00..521fe0d5eb 100644
--- a/source3/libads/kerberos.c
+++ b/source3/libads/kerberos.c
@@ -85,19 +85,29 @@ int ads_kinit_password(ADS_STRUCT *ads)
{
char *s;
int ret;
- extern pstring global_myname;
- fstring myname;
+ char *ccache;
+
+ ccache = lock_path("winbindd_ccache");
/* we don't want this to affect the users ccache */
- setenv("KRB5CCNAME", lock_path("winbindd_ccache"), 1);
+ setenv("KRB5CCNAME", ccache, 1);
- fstrcpy(myname, global_myname);
- strlower(myname);
- asprintf(&s, "HOST/%s@%s", global_myname, ads->realm);
+ unlink(ccache);
+
+ if (!ads->user_name) {
+ /* by default use the machine account */
+ extern pstring global_myname;
+ fstring myname;
+ fstrcpy(myname, global_myname);
+ strlower(myname);
+ asprintf(&ads->user_name, "HOST/%s", global_myname);
+ }
+ asprintf(&s, "%s@%s", ads->user_name, ads->realm);
ret = kerberos_kinit_password(s, ads->password);
free(s);
if (ret) {
- DEBUG(1,("kerberos_kinit_password failed: %s\n", error_message(ret)));
+ DEBUG(1,("kerberos_kinit_password %s failed: %s\n",
+ s, error_message(ret)));
}
return ret;
}
diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c
index a7c9265b18..b716966c1b 100644
--- a/source3/libads/ldap.c
+++ b/source3/libads/ldap.c
@@ -36,25 +36,6 @@ char *ads_errstr(int rc)
}
/*
- 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
-*/
-static int sasl_interact(LDAP *ld,unsigned flags,void *defaults,void *in)
-{
- sasl_interact_t *interact = in;
-
- while (interact->id != SASL_CB_LIST_END) {
- interact->result = strdup("");
- interact->len = strlen(interact->result);
- interact++;
- }
-
- return LDAP_SUCCESS;
-}
-
-/*
connect to the LDAP server
*/
int ads_connect(ADS_STRUCT *ads)
@@ -68,67 +49,38 @@ int ads_connect(ADS_STRUCT *ads)
if (!ads->ld) {
return LDAP_SERVER_DOWN;
}
+ if (!ads_server_info(ads)) {
+ DEBUG(1,("Failed to get ldap server info\n"));
+ return LDAP_SERVER_DOWN;
+ }
+
ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
if (ads->password) {
- /* the machine acct password might have changed */
- free(ads->password);
- ads->password = secrets_fetch_machine_password();
ads_kinit_password(ads);
}
- rc = ldap_sasl_interactive_bind_s(ads->ld, NULL, NULL, NULL, NULL,
- LDAP_SASL_QUIET,
- sasl_interact, NULL);
-
+ rc = ads_sasl_bind(ads);
return rc;
}
/*
- a wrapper around ldap_search_s that retries depending on the error code
- this is supposed to catch dropped connections and auto-reconnect
+ do a search with a timeout
*/
-int ads_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope, const char *exp,
- const char **attrs, void **res)
+int ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, const char *exp,
+ const char **attrs, void **res)
{
struct timeval timeout;
- int rc = -1, rc2;
- int count = 3;
-
- if (!ads->ld &&
- time(NULL) - ads->last_attempt < ADS_RECONNECT_TIME) {
- return LDAP_SERVER_DOWN;
- }
- while (count--) {
- *res = NULL;
- timeout.tv_sec = ADS_SEARCH_TIMEOUT;
- timeout.tv_usec = 0;
- if (ads->ld) {
- rc = ldap_search_ext_s(ads->ld, bind_path, scope, exp, attrs, 0, NULL, NULL,
- &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
- if (rc == 0) return rc;
- }
+ timeout.tv_sec = ADS_SEARCH_TIMEOUT;
+ timeout.tv_usec = 0;
+ *res = NULL;
- if (*res) ads_msgfree(ads, *res);
- *res = NULL;
- DEBUG(1,("Reopening ads connection after error %s\n", ads_errstr(rc)));
- if (ads->ld) {
- /* we should unbind here, but that seems to trigger openldap bugs :(
- ldap_unbind(ads->ld);
- */
- }
- ads->ld = NULL;
- rc2 = ads_connect(ads);
- if (rc2) {
- DEBUG(1,("ads_search_retry: failed to reconnect (%s)\n", ads_errstr(rc)));
- return rc2;
- }
- }
- DEBUG(1,("ads reopen failed after error %s\n", ads_errstr(rc)));
- return rc;
+ return ldap_search_ext_s(ads->ld,
+ bind_path, scope,
+ exp, attrs, 0, NULL, NULL,
+ &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
}
-
/*
do a general ADS search
*/
@@ -136,7 +88,8 @@ int ads_search(ADS_STRUCT *ads, void **res,
const char *exp,
const char **attrs)
{
- return ads_search_retry(ads, ads->bind_path, LDAP_SCOPE_SUBTREE, exp, attrs, res);
+ return ads_do_search(ads, ads->bind_path, LDAP_SCOPE_SUBTREE,
+ exp, attrs, res);
}
/*
@@ -146,7 +99,7 @@ int ads_search_dn(ADS_STRUCT *ads, void **res,
const char *dn,
const char **attrs)
{
- return ads_search_retry(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
+ return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
}
/*
@@ -586,7 +539,7 @@ BOOL ads_USN(ADS_STRUCT *ads, uint32 *usn)
void *res;
BOOL ret;
- rc = ads_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+ rc = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
if (rc || ads_count_replies(ads, res) != 1) return False;
ret = ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
ads_msgfree(ads, res);
@@ -594,5 +547,56 @@ BOOL ads_USN(ADS_STRUCT *ads, uint32 *usn)
}
+/* find the servers name and realm - this can be done before authentication
+ The ldapServiceName field on w2k looks like this:
+ vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
+*/
+BOOL ads_server_info(ADS_STRUCT *ads)
+{
+ const char *attrs[] = {"ldapServiceName", NULL};
+ int rc;
+ void *res;
+ char **values;
+ char *ret, *p;
+
+ rc = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+ if (rc || ads_count_replies(ads, res) != 1) return False;
+
+ values = ldap_get_values(ads->ld, res, "ldapServiceName");
+
+ if (!values || !values[0]) return False;
+
+ p = strchr(values[0], ':');
+ if (!p) {
+ ldap_value_free(values);
+ ldap_msgfree(res);
+ return False;
+ }
+
+ SAFE_FREE(ads->ldap_server_name);
+
+ ads->ldap_server_name = strdup(p+1);
+ p = strchr(ads->ldap_server_name, '$');
+ if (!p || p[1] != '@') {
+ ldap_value_free(values);
+ ldap_msgfree(res);
+ SAFE_FREE(ads->ldap_server_name);
+ return False;
+ }
+
+ *p = 0;
+
+ /* in case the realm isn't configured in smb.conf */
+ if (!ads->realm || !ads->realm[0]) {
+ SAFE_FREE(ads->realm);
+ SAFE_FREE(ads->bind_path);
+ ads->realm = strdup(p+2);
+ ads->bind_path = ads_build_dn(ads->realm);
+ }
+
+ DEBUG(3,("got ldap server name %s@%s\n", ret, ads->realm));
+
+ return True;
+}
#endif
diff --git a/source3/libads/sasl.c b/source3/libads/sasl.c
new file mode 100644
index 0000000000..dd948b5d4d
--- /dev/null
+++ b/source3/libads/sasl.c
@@ -0,0 +1,186 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ ads sasl code
+ Copyright (C) Andrew Tridgell 2001
+
+ 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"
+
+#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
+*/
+static int sasl_interact(LDAP *ld,unsigned flags,void *defaults,void *in)
+{
+ sasl_interact_t *interact = in;
+
+ while (interact->id != SASL_CB_LIST_END) {
+ interact->result = strdup("");
+ interact->len = strlen(interact->result);
+ interact++;
+ }
+
+ return LDAP_SUCCESS;
+}
+#endif
+
+
+#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
+*/
+int ads_sasl_gssapi_bind(ADS_STRUCT *ads)
+{
+ int rc, minor_status;
+ gss_name_t serv_name;
+ gss_buffer_desc input_name;
+ gss_ctx_id_t context_handle;
+ gss_OID mech_type = GSS_C_NULL_OID;
+ gss_buffer_desc output_token, input_token;
+ OM_uint32 ret_flags, conf_state;
+ struct berval cred;
+ struct berval *scred;
+ int i=0;
+ int gss_rc;
+ uint8 *p;
+ uint32 max_msg_size;
+ char *sname;
+
+ asprintf(&sname, "ldap@%s.%s", ads->ldap_server_name, ads->realm);
+
+ input_name.value = sname;
+ input_name.length = strlen(input_name.value);
+
+ rc = gss_import_name(&minor_status,&input_name,gss_nt_service_name, &serv_name);
+
+ free(sname);
+
+ context_handle = GSS_C_NO_CONTEXT;
+
+ input_token.value = NULL;
+ input_token.length = 0;
+
+ for (i=0; i < MAX_GSS_PASSES; i++) {
+ gss_rc = gss_init_sec_context(&minor_status,
+ GSS_C_NO_CREDENTIAL,
+ &context_handle,
+ serv_name,
+ mech_type,
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
+ 0,
+ NULL,
+ &input_token,
+ NULL,
+ &output_token,
+ &ret_flags,
+ NULL);
+
+ if (input_token.value) {
+ gss_release_buffer(&minor_status, &input_token);
+ }
+
+ if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) goto failed;
+
+ cred.bv_val = output_token.value;
+ cred.bv_len = output_token.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
+ &scred);
+
+ if (output_token.value) {
+ gss_release_buffer(&minor_status, &output_token);
+ }
+
+ if (scred) {
+ input_token.value = scred->bv_val;
+ input_token.length = scred->bv_len;
+ } else {
+ input_token.value = NULL;
+ input_token.length = 0;
+ }
+
+ if (gss_rc != GSS_S_CONTINUE_NEEDED) break;
+ }
+
+ gss_release_name(&minor_status, &serv_name);
+
+ gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
+ &conf_state,NULL);
+
+ gss_release_buffer(&minor_status, &input_token);
+
+ p = (uint8 *)output_token.value;
+
+ max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
+
+ gss_release_buffer(&minor_status, &output_token);
+
+ output_token.value = malloc(strlen(ads->bind_path) + 8);
+ p = output_token.value;
+
+ *p++ = 1; /* no sign or seal */
+ /* 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->bind_path)+1, "dn:%s", ads->bind_path);
+ p += strlen(ads->bind_path);
+
+ output_token.length = strlen(ads->bind_path) + 8;
+
+ gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
+ &output_token, &conf_state,
+ &input_token);
+
+ free(output_token.value);
+
+ cred.bv_val = input_token.value;
+ cred.bv_len = input_token.length;
+
+ rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
+ &scred);
+
+ gss_release_buffer(&minor_status, &input_token);
+ return rc;
+
+failed:
+ return gss_rc;
+}
+
+int ads_sasl_bind(ADS_STRUCT *ads)
+{
+#if USE_CYRUS_SASL
+ return ldap_sasl_interactive_bind_s(ads->ld, NULL, NULL, NULL, NULL,
+ LDAP_SASL_QUIET,
+ sasl_interact, NULL);
+#else
+ return ads_sasl_gssapi_bind(ads);
+#endif
+}
+
+#endif
+