From a062e58d9e47f95ac7c66668b3cfe1f72386f6e0 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 19 Dec 2001 08:44:23 +0000 Subject: - added initial support for trusted domains in winbindd_ads - gss error code patch from a.bokovoy@sam-solutions.net - better sid dumping in ads_dump - fixed help in wbinfo (This used to be commit ee1c3e1f044b4ef62169ad74c5cac40eef81bfda) --- source3/include/ads.h | 11 ++++++ source3/libads/ads_struct.c | 26 +++++++++++++ source3/libads/ldap.c | 77 +++++++++++++++++++++++++++++++++++---- source3/libads/sasl.c | 53 ++++++++++++++++++--------- source3/nsswitch/wbinfo.c | 6 ++- source3/nsswitch/winbindd_ads.c | 57 ++++++++++++++++++++++++----- source3/nsswitch/winbindd_cache.c | 26 +++++++++++-- source3/nsswitch/winbindd_util.c | 10 +++-- source3/script/mkproto.awk | 2 +- source3/utils/net_ads.c | 9 +++-- 10 files changed, 230 insertions(+), 47 deletions(-) diff --git a/source3/include/ads.h b/source3/include/ads.h index 5ae127ff28..4a20d0e79f 100644 --- a/source3/include/ads.h +++ b/source3/include/ads.h @@ -15,8 +15,19 @@ typedef struct { time_t last_attempt; char *password; char *user_name; + char *server_realm; } ADS_STRUCT; +typedef struct { + /* Type of error returned by ads_connect: */ + /* True corresponds GSS API, False - LDAP */ + int error_type; + /* For error_type = False rc describes LDAP error */ + int rc; + /* For error_type = True rc and minor_status describe GSS API error */ + /* Where rc represents major_status of GSS API error */ + int minor_status; +} ADS_RETURN_CODE; /* time between reconnect attempts */ #define ADS_RECONNECT_TIME 5 diff --git a/source3/libads/ads_struct.c b/source3/libads/ads_struct.c index 4b2ab5b40f..a7c8d1a681 100644 --- a/source3/libads/ads_struct.c +++ b/source3/libads/ads_struct.c @@ -157,3 +157,29 @@ void ads_destroy(ADS_STRUCT **ads) } } + +static void ads_display_status_helper(char *m, OM_uint32 code, int type) +{ + int maj_stat, min_stat; + gss_buffer_desc msg; + int msg_ctx; + + msg_ctx = 0; + while (1) { + maj_stat = gss_display_status(&min_stat, code, + type, GSS_C_NULL_OID, + &msg_ctx, &msg); + DEBUG(1, ("GSS-API error %s: %s\n", m, + (char *)msg.value)); + (void) gss_release_buffer(&min_stat, &msg); + + if (!msg_ctx) + break; + } +} + +void ads_display_status(char * msg, int maj_stat,int min_stat) +{ + ads_display_status_helper(msg, maj_stat, GSS_C_GSS_CODE); + ads_display_status_helper(msg, min_stat, GSS_C_MECH_CODE); +} diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c index 09498b4384..b41a864ae2 100644 --- a/source3/libads/ldap.c +++ b/source3/libads/ldap.c @@ -38,20 +38,24 @@ char *ads_errstr(int rc) /* connect to the LDAP server */ -int ads_connect(ADS_STRUCT *ads) +ADS_RETURN_CODE ads_connect(ADS_STRUCT *ads) { int version = LDAP_VERSION3; - int rc; + ADS_RETURN_CODE rc; + + rc.error_type = False; ads->last_attempt = time(NULL); ads->ld = ldap_open(ads->ldap_server, ads->ldap_port); if (!ads->ld) { - return LDAP_SERVER_DOWN; + rc.rc = LDAP_SERVER_DOWN; + return rc; } if (!ads_server_info(ads)) { DEBUG(1,("Failed to get ldap server info\n")); - return LDAP_SERVER_DOWN; + rc.rc = LDAP_SERVER_DOWN; + return rc; } ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version); @@ -232,6 +236,19 @@ static void dump_binary(const char *field, struct berval **values) } } +/* + dump a sid result from ldap +*/ +static void dump_sid(const char *field, struct berval **values) +{ + int i; + for (i=0; values[i]; i++) { + DOM_SID sid; + sid_parse(values[i]->bv_val, values[i]->bv_len, &sid); + printf("%s: %s\n", field, sid_string_static(&sid)); + } +} + /* dump a string result from ldap */ @@ -257,7 +274,7 @@ void ads_dump(ADS_STRUCT *ads, void *res) void (*handler)(const char *, struct berval **); } handlers[] = { {"objectGUID", dump_binary}, - {"objectSid", dump_binary}, + {"objectSid", dump_sid}, {NULL, NULL} }; @@ -547,12 +564,16 @@ BOOL ads_server_info(ADS_STRUCT *ads) *p = 0; + SAFE_FREE(ads->server_realm); + SAFE_FREE(ads->bind_path); + + ads->server_realm = strdup(p+2); + ads->bind_path = ads_build_dn(ads->server_realm); + /* 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); + ads->realm = strdup(ads->server_realm); } DEBUG(3,("got ldap server name %s@%s\n", @@ -561,4 +582,44 @@ BOOL ads_server_info(ADS_STRUCT *ads) return True; } + +/* + find the list of trusted domains +*/ +BOOL ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, + int *num_trusts, char ***names, DOM_SID **sids) +{ + const char *attrs[] = {"flatName", "securityIdentifier", NULL}; + int rc; + void *res, *msg; + int count, i; + + *num_trusts = 0; + + rc = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs); + if (rc) return False; + + count = ads_count_replies(ads, res); + if (count == 0) { + ads_msgfree(ads, res); + return False; + } + + (*names) = talloc(mem_ctx, sizeof(char *) * count); + (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count); + if (! *names || ! *sids) return False; + + for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { + (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatName"); + ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i]); + i++; + } + + ads_msgfree(ads, res); + + *num_trusts = i; + + return True; +} + #endif diff --git a/source3/libads/sasl.c b/source3/libads/sasl.c index dd948b5d4d..b3610b8fdb 100644 --- a/source3/libads/sasl.c +++ b/source3/libads/sasl.c @@ -53,9 +53,9 @@ static int sasl_interact(LDAP *ld,unsigned flags,void *defaults,void *in) this routine is much less fragile see RFC2078 for details */ -int ads_sasl_gssapi_bind(ADS_STRUCT *ads) +ADS_RETURN_CODE ads_sasl_gssapi_bind(ADS_STRUCT *ads) { - int rc, minor_status; + int minor_status; gss_name_t serv_name; gss_buffer_desc input_name; gss_ctx_id_t context_handle; @@ -69,15 +69,27 @@ int ads_sasl_gssapi_bind(ADS_STRUCT *ads) uint8 *p; uint32 max_msg_size; char *sname; + ADS_RETURN_CODE rc; + krb5_principal principal; + krb5_context ctx; + krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL}; + gss_OID_desc nt_principal = + {10, "\052\206\110\206\367\022\001\002\002\002"}; + + /* we need to fetch a service ticket as the ldap user in the + servers realm, regardless of our realm */ + asprintf(&sname, "ldap/%s@%s", ads->ldap_server_name, ads->server_realm); + krb5_init_context(&ctx); + krb5_set_default_tgs_ktypes(ctx, enc_types); + krb5_parse_name(ctx, sname, &principal); + free(sname); + krb5_free_context(ctx); - 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); + input_name.value = &principal; + input_name.length = sizeof(principal); - free(sname); + rc.rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name); + rc.error_type = False; context_handle = GSS_C_NO_CONTEXT; @@ -103,12 +115,17 @@ int ads_sasl_gssapi_bind(ADS_STRUCT *ads) gss_release_buffer(&minor_status, &input_token); } - if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) goto failed; + if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) { + rc.minor_status = minor_status; + rc.rc = gss_rc; + rc.error_type = True; + 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, + rc.rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, &scred); if (output_token.value) { @@ -152,7 +169,7 @@ int ads_sasl_gssapi_bind(ADS_STRUCT *ads) output_token.length = strlen(ads->bind_path) + 8; - gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT, + rc.rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT, &output_token, &conf_state, &input_token); @@ -161,22 +178,24 @@ int ads_sasl_gssapi_bind(ADS_STRUCT *ads) cred.bv_val = input_token.value; cred.bv_len = input_token.length; - rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, + rc.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; + return rc; } -int ads_sasl_bind(ADS_STRUCT *ads) +ADS_RETURN_CODE ads_sasl_bind(ADS_STRUCT *ads) { #if USE_CYRUS_SASL - return ldap_sasl_interactive_bind_s(ads->ld, NULL, NULL, NULL, NULL, + ADS_RETURN_CODE rc; + rc.error_type = False; + rc.rc = ldap_sasl_interactive_bind_s(ads->ld, NULL, NULL, NULL, NULL, LDAP_SASL_QUIET, sasl_interact, NULL); + return rc; #else return ads_sasl_gssapi_bind(ads); #endif diff --git a/source3/nsswitch/wbinfo.c b/source3/nsswitch/wbinfo.c index 82d483611f..9c012eb85d 100644 --- a/source3/nsswitch/wbinfo.c +++ b/source3/nsswitch/wbinfo.c @@ -486,7 +486,7 @@ int main(int argc, char **argv) struct poptOption long_options[] = { /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - + { "help", 'h', POPT_ARG_NONE, 0, 'h' }, { "domain-users", 'u', POPT_ARG_NONE, 0, 'u' }, { "domain-groups", 'g', POPT_ARG_NONE, 0, 'g' }, { "name-to-sid", 'n', POPT_ARG_STRING, &string_arg, 'n' }, @@ -548,6 +548,9 @@ int main(int argc, char **argv) while((opt = poptGetNextOpt(pc)) != -1) { switch (opt) { + case 'h': + usage(); + exit(0); case 'u': if (!print_domain_users()) { printf("Error looking up domain users\n"); @@ -644,6 +647,7 @@ int main(int argc, char **argv) break; default: fprintf(stderr, "Invalid option\n"); + usage(); return 1; } } diff --git a/source3/nsswitch/winbindd_ads.c b/source3/nsswitch/winbindd_ads.c index e52f448a63..4ce0894ab3 100644 --- a/source3/nsswitch/winbindd_ads.c +++ b/source3/nsswitch/winbindd_ads.c @@ -24,6 +24,9 @@ #ifdef HAVE_ADS +/* the realm of our primary LDAP server */ +static char *primary_realm; + /* a wrapper around ldap_search_s that retries depending on the error code @@ -33,7 +36,8 @@ int ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope, const char *exp, const char **attrs, void **res) { - int rc = -1, rc2; + int rc = -1; + ADS_RETURN_CODE rc2; int count = 3; if (!ads->ld && @@ -59,9 +63,15 @@ int ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope, } ads->ld = NULL; rc2 = ads_connect(ads); - if (rc2) { - DEBUG(1,("ads_search_retry: failed to reconnect (%s)\n", ads_errstr(rc))); - return rc2; + if (rc2.rc) { + DEBUG(1,("ads_search_retry: failed to reconnect:\n")); + if(rc2.error_type) + ads_display_status("", rc2.rc, rc2.minor_status); + else + DEBUG(1,("LDAP error: %s\n", ads_errstr(rc2.rc))); + + ads_destroy(&ads); + return rc2.rc; } } DEBUG(1,("ads reopen failed after error %s\n", ads_errstr(rc))); @@ -92,8 +102,9 @@ int ads_search_retry_dn(ADS_STRUCT *ads, void **res, static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) { ADS_STRUCT *ads; - int rc; + ADS_RETURN_CODE rc; char *ccache; + struct in_addr server_ip; if (domain->private) { return (ADS_STRUCT *)domain->private; @@ -104,7 +115,12 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) SETENV("KRB5CCNAME", ccache, 1); unlink(ccache); - ads = ads_init(NULL, NULL, NULL, NULL); + if (!resolve_name(domain->name, &server_ip, 0x1b)) { + DEBUG(1,("Can't find PDC for domain %s\n", domain->name)); + return NULL; + } + + ads = ads_init(primary_realm, inet_ntoa(server_ip), NULL, NULL); if (!ads) { DEBUG(1,("ads_init for domain %s failed\n", domain->name)); return NULL; @@ -115,12 +131,22 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) ads->password = secrets_fetch_machine_password(); rc = ads_connect(ads); - if (rc) { - DEBUG(1,("ads_connect for domain %s failed: %s\n", domain->name, ads_errstr(rc))); + if (rc.rc) { + DEBUG(1,("ads_connect for domain %s failed:\n", domain->name)); + if(rc.error_type) + ads_display_status("", rc.rc, rc.minor_status); + else + DEBUG(1,("LDAP error: %s\n", ads_errstr(rc.rc))); + ads_destroy(&ads); return NULL; } + /* remember our primary realm for trusted domain support */ + if (!primary_realm) { + primary_realm = strdup(ads->realm); + } + domain->private = (void *)ads; return ads; } @@ -546,7 +572,7 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, } if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group)) { - DEBUG(1,("No primary group for rid=%d !?\n", user_rid)); + DEBUG(1,("%s: No primary group for rid=%d !?\n", domain->name, user_rid)); goto done; } @@ -666,8 +692,19 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, char ***names, DOM_SID **dom_sids) { + ADS_STRUCT *ads = NULL; + *num_domains = 0; - return NT_STATUS_NOT_IMPLEMENTED; + *names = NULL; + + ads = ads_cached_connection(domain); + if (!ads) return NT_STATUS_UNSUCCESSFUL; + + if (!ads_trusted_domains(ads, mem_ctx, num_domains, names, dom_sids)) { + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; } /* find the domain sid for a domain */ diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c index 32f9f0d69f..847ec9e541 100644 --- a/source3/nsswitch/winbindd_cache.c +++ b/source3/nsswitch/winbindd_cache.c @@ -462,8 +462,10 @@ do_cached: return status; do_query: + *num_entries = 0; + *info = NULL; + if (wcache_server_down(domain)) { - *num_entries = 0; return NT_STATUS_SERVER_DISABLED; } @@ -533,8 +535,10 @@ do_cached: return status; do_query: + *num_entries = 0; + *info = NULL; + if (wcache_server_down(domain)) { - *num_entries = 0; return NT_STATUS_SERVER_DISABLED; } @@ -580,6 +584,8 @@ static NTSTATUS name_to_sid(struct winbindd_domain *domain, return status; do_query: + ZERO_STRUCTP(sid); + if (wcache_server_down(domain)) { return NT_STATUS_SERVER_DISABLED; } @@ -619,6 +625,8 @@ static NTSTATUS sid_to_name(struct winbindd_domain *domain, return status; do_query: + *name = NULL; + if (wcache_server_down(domain)) { return NT_STATUS_SERVER_DISABLED; } @@ -656,9 +664,12 @@ static NTSTATUS query_user(struct winbindd_domain *domain, return status; do_query: + ZERO_STRUCTP(info); + if (wcache_server_down(domain)) { return NT_STATUS_SERVER_DISABLED; } + status = cache->backend->query_user(domain, mem_ctx, user_rid, info); /* and save it */ @@ -701,8 +712,10 @@ do_cached: return status; do_query: + (*num_groups) = 0; + (*user_gids) = NULL; + if (wcache_server_down(domain)) { - (*num_groups) = 0; return NT_STATUS_SERVER_DISABLED; } status = cache->backend->lookup_usergroups(domain, mem_ctx, user_rid, num_groups, user_gids); @@ -763,8 +776,13 @@ do_cached: return status; do_query: + (*num_names) = 0; + (*rid_mem) = NULL; + (*names) = NULL; + (*name_types) = NULL; + + if (wcache_server_down(domain)) { - (*num_names) = 0; return NT_STATUS_SERVER_DISABLED; } status = cache->backend->lookup_groupmem(domain, mem_ctx, group_rid, num_names, diff --git a/source3/nsswitch/winbindd_util.c b/source3/nsswitch/winbindd_util.c index 608749b39d..f760b635d6 100644 --- a/source3/nsswitch/winbindd_util.c +++ b/source3/nsswitch/winbindd_util.c @@ -98,10 +98,7 @@ static struct winbindd_domain *add_trusted_domain(char *domain_name, } } - DEBUG(1, ("adding domain %s\n", domain_name)); - /* Create new domain entry */ - if ((domain = (struct winbindd_domain *)malloc(sizeof(*domain))) == NULL) return NULL; @@ -146,6 +143,10 @@ BOOL get_domain_info(void) domain->name)); result = cache_methods.domain_sid(domain, &domain->sid); } + + DEBUG(1,("Added domain %s (%s)\n", + domain->name, + sid_string_static(&domain->sid))); DEBUG(1, ("getting trusted domain list\n")); @@ -160,6 +161,9 @@ BOOL get_domain_info(void) if (domain) { sid_copy(&domain->sid, &dom_sids[i]); } + DEBUG(1,("Added domain %s (%s)\n", + domain->name, + sid_string_static(&domain->sid))); } } diff --git a/source3/script/mkproto.awk b/source3/script/mkproto.awk index f927d273bd..7c8bc3d6cf 100644 --- a/source3/script/mkproto.awk +++ b/source3/script/mkproto.awk @@ -122,7 +122,7 @@ END { gotstart = 1; } - if( $0 ~ /^ADS_STRUCT|^DATA_BLOB|^ASN1_DATA|^TDB_CONTEXT|^TDB_DATA|^smb_ucs2_t|^TALLOC_CTX|^hash_element|^NT_DEVICEMODE|^enum.*\(|^NT_USER_TOKEN|^SAM_ACCOUNT/ ) { + if( $0 ~ /^ADS_STRUCT|^ADS_RETURN_CODE|^DATA_BLOB|^ASN1_DATA|^TDB_CONTEXT|^TDB_DATA|^smb_ucs2_t|^TALLOC_CTX|^hash_element|^NT_DEVICEMODE|^enum.*\(|^NT_USER_TOKEN|^SAM_ACCOUNT/ ) { gotstart = 1; } diff --git a/source3/utils/net_ads.c b/source3/utils/net_ads.c index 8d41c09208..0d7b641771 100644 --- a/source3/utils/net_ads.c +++ b/source3/utils/net_ads.c @@ -68,7 +68,7 @@ static int net_ads_info(int argc, const char **argv) static ADS_STRUCT *ads_startup(void) { ADS_STRUCT *ads; - int rc; + ADS_RETURN_CODE rc; extern char *opt_password; extern char *opt_user_name; @@ -88,8 +88,11 @@ static ADS_STRUCT *ads_startup(void) ads->user_name = strdup(opt_user_name); rc = ads_connect(ads); - if (rc) { - d_printf("ads_connect: %s\n", ads_errstr(rc)); + if (rc.rc) { + if(rc.error_type) + ads_display_status("ads_connect", rc.rc, rc.minor_status); + else + d_printf("ads_connect: %s\n", ads_errstr(rc.rc)); return NULL; } return ads; -- cgit