From b2edf254eda92f775e7d3d9b6793b4d77f9000b6 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Sat, 17 Aug 2002 17:00:51 +0000 Subject: sync 3.0 branch with head (This used to be commit 3928578b52cfc949be5e0ef444fce1558d75f290) --- source3/nsswitch/pam_winbind.c | 18 ++-- source3/nsswitch/wb_client.c | 6 +- source3/nsswitch/wb_common.c | 17 ---- source3/nsswitch/wbinfo.c | 7 +- source3/nsswitch/winbindd.c | 7 +- source3/nsswitch/winbindd.h | 8 +- source3/nsswitch/winbindd_ads.c | 119 ++++++++++------------- source3/nsswitch/winbindd_cache.c | 15 ++- source3/nsswitch/winbindd_cm.c | 196 ++++++++++++++++++++++++++------------ source3/nsswitch/winbindd_group.c | 6 ++ source3/nsswitch/winbindd_nss.h | 12 ++- source3/nsswitch/winbindd_pam.c | 139 +++++++++++++++++++++------ source3/nsswitch/winbindd_rpc.c | 15 ++- source3/nsswitch/winbindd_sid.c | 15 +++ source3/nsswitch/winbindd_user.c | 3 + source3/nsswitch/winbindd_util.c | 118 +++++++++++++++-------- source3/nsswitch/winbindd_wins.c | 6 ++ 17 files changed, 467 insertions(+), 240 deletions(-) (limited to 'source3/nsswitch') diff --git a/source3/nsswitch/pam_winbind.c b/source3/nsswitch/pam_winbind.c index 4739cfbf7a..29ceca4e79 100644 --- a/source3/nsswitch/pam_winbind.c +++ b/source3/nsswitch/pam_winbind.c @@ -79,7 +79,7 @@ static int converse(pam_handle_t *pamh, int nargs, } -int _make_remark(pam_handle_t * pamh, int type, const char *text) +static int _make_remark(pam_handle_t * pamh, int type, const char *text) { int retval = PAM_SUCCESS; @@ -163,6 +163,10 @@ static int winbind_auth_request(const char *user, const char *pass, int ctrl) /* password expired */ _pam_log(LOG_WARNING, "user `%s' password expired", user); return retval; + case PAM_NEW_AUTHTOK_REQD: + /* password expired */ + _pam_log(LOG_WARNING, "user `%s' new password required", user); + return retval; case PAM_USER_UNKNOWN: /* the user does not exist */ if (ctrl & WINBIND_DEBUG_ARG) @@ -241,12 +245,12 @@ static char *_pam_delete(register char *xx) * obtain a password from the user */ -int _winbind_read_password(pam_handle_t * pamh - ,unsigned int ctrl - ,const char *comment - ,const char *prompt1 - ,const char *prompt2 - ,const char **pass) +static int _winbind_read_password(pam_handle_t * pamh + ,unsigned int ctrl + ,const char *comment + ,const char *prompt1 + ,const char *prompt2 + ,const char **pass) { int authtok_flag; int retval; diff --git a/source3/nsswitch/wb_client.c b/source3/nsswitch/wb_client.c index bcb339864a..9ac1515d7d 100644 --- a/source3/nsswitch/wb_client.c +++ b/source3/nsswitch/wb_client.c @@ -65,7 +65,7 @@ BOOL winbind_lookup_name(const char *dom_name, const char *name, DOM_SID *sid, /* Call winbindd to convert sid to name */ -BOOL winbind_lookup_sid(DOM_SID *sid, +BOOL winbind_lookup_sid(const DOM_SID *sid, fstring dom_name, fstring name, enum SID_NAME_USE *name_type) { @@ -102,7 +102,7 @@ BOOL winbind_lookup_sid(DOM_SID *sid, /* Call winbindd to convert SID to uid */ -BOOL winbind_sid_to_uid(uid_t *puid, DOM_SID *sid) +BOOL winbind_sid_to_uid(uid_t *puid, const DOM_SID *sid) { struct winbindd_request request; struct winbindd_response response; @@ -168,7 +168,7 @@ BOOL winbind_uid_to_sid(DOM_SID *sid, uid_t uid) /* Call winbindd to convert SID to gid */ -BOOL winbind_sid_to_gid(gid_t *pgid, DOM_SID *sid) +BOOL winbind_sid_to_gid(gid_t *pgid, const DOM_SID *sid) { struct winbindd_request request; struct winbindd_response response; diff --git a/source3/nsswitch/wb_common.c b/source3/nsswitch/wb_common.c index 89dd625241..9bc9faafb5 100644 --- a/source3/nsswitch/wb_common.c +++ b/source3/nsswitch/wb_common.c @@ -28,7 +28,6 @@ /* Global variables. These are effectively the client state information */ int winbindd_fd = -1; /* fd for winbindd socket */ -static char *excluded_domain; /* Free a response structure */ @@ -40,16 +39,6 @@ void free_response(struct winbindd_response *response) SAFE_FREE(response->extra_data); } -/* - smbd needs to be able to exclude lookups for its own domain -*/ -void winbind_exclude_domain(const char *domain) -{ - SAFE_FREE(excluded_domain); - excluded_domain = strdup(domain); -} - - /* Initialise a request structure */ void init_request(struct winbindd_request *request, int request_type) @@ -325,12 +314,6 @@ NSS_STATUS winbindd_send_request(int req_type, struct winbindd_request *request) return NSS_STATUS_NOTFOUND; } - /* smbd may have excluded this domain */ - if (excluded_domain && - strcasecmp(excluded_domain, request->domain) == 0) { - return NSS_STATUS_NOTFOUND; - } - if (!request) { ZERO_STRUCT(lrequest); request = &lrequest; diff --git a/source3/nsswitch/wbinfo.c b/source3/nsswitch/wbinfo.c index d0af10a0e6..4d36acc51b 100644 --- a/source3/nsswitch/wbinfo.c +++ b/source3/nsswitch/wbinfo.c @@ -255,8 +255,7 @@ static BOOL wbinfo_check_secret(void) ZERO_STRUCT(response); - result = winbindd_request(WINBINDD_CHECK_MACHACC, NULL, &response) == - NSS_STATUS_SUCCESS; + result = winbindd_request(WINBINDD_CHECK_MACHACC, NULL, &response); d_printf("checking the trust secret via RPC calls %s\n", (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed"); @@ -490,9 +489,9 @@ static BOOL wbinfo_auth_crap(char *username) generate_random_buffer(request.data.auth_crap.chal, 8, False); - SMBencrypt((uchar *)pass, request.data.auth_crap.chal, + SMBencrypt(pass, request.data.auth_crap.chal, (uchar *)request.data.auth_crap.lm_resp); - SMBNTencrypt((uchar *)pass, request.data.auth_crap.chal, + SMBNTencrypt(pass, request.data.auth_crap.chal, (uchar *)request.data.auth_crap.nt_resp); request.data.auth_crap.lm_resp_len = 24; diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c index 358d9add3a..256c0203c0 100644 --- a/source3/nsswitch/winbindd.c +++ b/source3/nsswitch/winbindd.c @@ -25,7 +25,7 @@ /* List of all connected clients */ -struct winbindd_cli_state *client_list; +static struct winbindd_cli_state *client_list; static int num_clients; BOOL opt_nocache = False; BOOL opt_dual_daemon = False; @@ -375,6 +375,9 @@ void winbind_process_packet(struct winbindd_cli_state *state) { /* Process request */ + /* Ensure null termination of entire request */ + state->request.domain[sizeof(state->request.domain)-1]='\0'; + state->pid = state->request.pid; process_request(state); @@ -688,6 +691,8 @@ int winbind_setup_common(void) } + namecache_enable(); /* Enable netbios namecache */ + /* Get list of domains we look up requests for. This includes the domain which we are a member of as well as any trusted domains. */ diff --git a/source3/nsswitch/winbindd.h b/source3/nsswitch/winbindd.h index 11d399be49..dd92ecefe6 100644 --- a/source3/nsswitch/winbindd.h +++ b/source3/nsswitch/winbindd.h @@ -88,7 +88,7 @@ typedef struct { struct winbindd_domain { fstring name; /* Domain name */ - fstring full_name; /* full Domain name (realm) */ + fstring alt_name; /* alt Domain name (if any) */ DOM_SID sid; /* SID for this domain */ /* Lookup methods for this domain (LDAP or RPC) */ @@ -170,11 +170,15 @@ struct winbindd_methods { TALLOC_CTX *mem_ctx, uint32 *num_domains, char ***names, + char ***alt_names, DOM_SID **dom_sids); /* find the domain sid */ NTSTATUS (*domain_sid)(struct winbindd_domain *domain, DOM_SID *sid); + + /* setup the list of alternate names for the domain, if any */ + NTSTATUS (*alternate_name)(struct winbindd_domain *domain); }; /* Used to glue a policy handle and cli_state together */ @@ -190,6 +194,8 @@ typedef struct { #include "rpc_client.h" #define WINBINDD_ESTABLISH_LOOP 30 +#define WINBINDD_RESCAN_FREQ 300 + #define DOM_SEQUENCE_NONE ((uint32)-1) /* SETENV */ diff --git a/source3/nsswitch/winbindd_ads.c b/source3/nsswitch/winbindd_ads.c index b61348adfe..b0b70178a4 100644 --- a/source3/nsswitch/winbindd_ads.c +++ b/source3/nsswitch/winbindd_ads.c @@ -61,8 +61,8 @@ ADS_STATUS ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope if (*res) ads_msgfree(ads, *res); *res = NULL; - DEBUG(3,("Reopening ads connection to %s after error %s\n", - ads->ldap_server, ads_errstr(status))); + DEBUG(3,("Reopening ads connection to realm '%s' after error %s\n", + ads->config.realm, ads_errstr(status))); if (ads->ld) { ldap_unbind(ads->ld); } @@ -87,7 +87,7 @@ ADS_STATUS ads_search_retry(ADS_STRUCT *ads, void **res, const char *exp, const char **attrs) { - return ads_do_search_retry(ads, ads->bind_path, LDAP_SCOPE_SUBTREE, + return ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, exp, attrs, res); } @@ -108,8 +108,6 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) ADS_STRUCT *ads; ADS_STATUS status; char *ccache; - struct in_addr server_ip; - char *sname; if (domain->private) { return (ADS_STRUCT *)domain->private; @@ -120,30 +118,23 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) SETENV("KRB5CCNAME", ccache, 1); unlink(ccache); - if (resolve_name(domain->name, &server_ip, 0x1b)) { - sname = inet_ntoa(server_ip); - } else if (resolve_name(domain->name, &server_ip, 0x1c)) { - sname = inet_ntoa(server_ip); - } else { - if (strcasecmp(domain->name, lp_workgroup()) != 0) { - DEBUG(1,("can't find domain controller for %s\n", domain->name)); - return NULL; - } - sname = NULL; - } - - ads = ads_init(primary_realm, domain->name, NULL, NULL, NULL); + ads = ads_init(domain->alt_name, domain->name, NULL); if (!ads) { DEBUG(1,("ads_init for domain %s failed\n", domain->name)); return NULL; } /* the machine acct password might have change - fetch it every time */ - SAFE_FREE(ads->password); - ads->password = secrets_fetch_machine_password(); + SAFE_FREE(ads->auth.password); + ads->auth.password = secrets_fetch_machine_password(); + + if (primary_realm) { + SAFE_FREE(ads->auth.realm); + ads->auth.realm = strdup(primary_realm); + } status = ads_connect(ads); - if (!ADS_ERR_OK(status) || !ads->realm) { + if (!ADS_ERR_OK(status) || !ads->config.realm) { extern struct winbindd_methods msrpc_methods; DEBUG(1,("ads_connect for domain %s failed: %s\n", domain->name, ads_errstr(status))); @@ -161,11 +152,9 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) /* remember our primary realm for trusted domain support */ if (!primary_realm) { - primary_realm = strdup(ads->realm); + primary_realm = strdup(ads->config.realm); } - fstrcpy(domain->full_name, ads->server_realm); - domain->private = (void *)ads; return ads; } @@ -405,7 +394,7 @@ static NTSTATUS name_to_sid(struct winbindd_domain *domain, /* accept either the win2000 or the pre-win2000 username */ asprintf(&exp, "(|(sAMAccountName=%s)(userPrincipalName=%s@%s))", - name, name, ads->realm); + name, name, ads->config.realm); rc = ads_search_retry(ads, &res, exp, attrs); free(exp); if (!ADS_ERR_OK(rc)) { @@ -535,49 +524,6 @@ failed: return False; } - -/* convert a sid to a distnguished name */ -static NTSTATUS sid_to_distinguished_name(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - DOM_SID *sid, - char **dn) -{ - ADS_STRUCT *ads = NULL; - const char *attrs[] = {"distinguishedName", NULL}; - ADS_STATUS rc; - void *msg = NULL; - char *exp; - char *sidstr; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - - DEBUG(3,("ads: sid_to_distinguished_name\n")); - - ads = ads_cached_connection(domain); - if (!ads) goto done; - - sidstr = sid_binstring(sid); - asprintf(&exp, "(objectSid=%s)", sidstr); - rc = ads_search_retry(ads, &msg, exp, attrs); - free(exp); - free(sidstr); - if (!ADS_ERR_OK(rc)) { - DEBUG(1,("sid_to_distinguished_name ads_search: %s\n", ads_errstr(rc))); - goto done; - } - - *dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName"); - - status = NT_STATUS_OK; - - DEBUG(3,("ads sid_to_distinguished_name mapped %s\n", *dn)); - -done: - if (msg) ads_msgfree(ads, msg); - - return status; -} - - /* Lookup user information from a rid */ static NTSTATUS query_user(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, @@ -831,6 +777,7 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, uint32 *num_domains, char ***names, + char ***alt_names, DOM_SID **dom_sids) { ADS_STRUCT *ads; @@ -842,7 +789,7 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, ads = ads_cached_connection(domain); if (!ads) return NT_STATUS_UNSUCCESSFUL; - rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, dom_sids); + rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, alt_names, dom_sids); return ads_ntstatus(rc); } @@ -867,6 +814,37 @@ static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid) return ads_ntstatus(rc); } + +/* find alternate names list for the domain - for ADS this is the + netbios name */ +static NTSTATUS alternate_name(struct winbindd_domain *domain) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + TALLOC_CTX *ctx; + char *workgroup; + + ads = ads_cached_connection(domain); + if (!ads) return NT_STATUS_UNSUCCESSFUL; + + if (!(ctx = talloc_init_named("alternate_name"))) { + return NT_STATUS_NO_MEMORY; + } + + rc = ads_workgroup_name(ads, ctx, &workgroup); + + if (ADS_ERR_OK(rc)) { + fstrcpy(domain->name, workgroup); + fstrcpy(domain->alt_name, ads->config.realm); + strupper(domain->alt_name); + strupper(domain->name); + } + + talloc_destroy(ctx); + + return ads_ntstatus(rc); +} + /* the ADS backend methods are exposed via this structure */ struct winbindd_methods ads_methods = { True, @@ -879,7 +857,8 @@ struct winbindd_methods ads_methods = { lookup_groupmem, sequence_number, trusted_domains, - domain_sid + domain_sid, + alternate_name }; #endif diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c index a607727867..060139af3e 100644 --- a/source3/nsswitch/winbindd_cache.c +++ b/source3/nsswitch/winbindd_cache.c @@ -873,13 +873,14 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, uint32 *num_domains, char ***names, + char ***alt_names, DOM_SID **dom_sids) { struct winbind_cache *cache = get_cache(domain); /* we don't cache this call */ return cache->backend->trusted_domains(domain, mem_ctx, num_domains, - names, dom_sids); + names, alt_names, dom_sids); } /* find the domain sid */ @@ -891,6 +892,15 @@ static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid) return cache->backend->domain_sid(domain, sid); } +/* find the alternate names for the domain, if any */ +static NTSTATUS alternate_name(struct winbindd_domain *domain) +{ + struct winbind_cache *cache = get_cache(domain); + + /* we don't cache this call */ + return cache->backend->alternate_name(domain); +} + /* the ADS backend methods are exposed via this structure */ struct winbindd_methods cache_methods = { True, @@ -903,5 +913,6 @@ struct winbindd_methods cache_methods = { lookup_groupmem, sequence_number, trusted_domains, - domain_sid + domain_sid, + alternate_name }; diff --git a/source3/nsswitch/winbindd_cm.c b/source3/nsswitch/winbindd_cm.c index 3ab97ed408..2dec9f0558 100644 --- a/source3/nsswitch/winbindd_cm.c +++ b/source3/nsswitch/winbindd_cm.c @@ -90,12 +90,122 @@ struct get_dc_name_cache { struct get_dc_name_cache *prev, *next; }; + +/* + find the DC for a domain using methods appropriate for a ADS domain +*/ +static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name) +{ + ADS_STRUCT *ads; + const char *realm = domain; + + if (strcasecmp(realm, lp_workgroup()) == 0) { + realm = lp_realm(); + } + + ads = ads_init(realm, domain, NULL); + if (!ads) { + return False; + } + + /* we don't need to bind, just connect */ + ads->auth.no_bind = 1; + + DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain)); + +#ifdef HAVE_ADS + /* a full ads_connect() is actually overkill, as we don't srictly need + to do the SASL auth in order to get the info we need, but libads + doesn't offer a better way right now */ + ads_connect(ads); +#endif + + if (!ads->config.realm) { + return False; + } + + fstrcpy(srv_name, ads->config.ldap_server_name); + strupper(srv_name); + *dc_ip = ads->ldap_ip; + ads_destroy(&ads); + + DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n", + srv_name, inet_ntoa(*dc_ip))); + + return True; +} + +/* + find the DC for a domain using methods appropriate for a RPC domain +*/ +static BOOL cm_rpc_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name) +{ + struct in_addr *ip_list = NULL; + int count, i; + + /* Lookup domain controller name. Try the real PDC first to avoid + SAM sync delays */ + if (!get_dc_list(True, domain, &ip_list, &count)) { + if (!get_dc_list(False, domain, &ip_list, &count)) { + DEBUG(3, ("Could not look up dc's for domain %s\n", domain)); + return False; + } + } + + /* Pick a nice close server */ + /* Look for DC on local net */ + for (i = 0; i < count; i++) { + if (!is_local_net(ip_list[i])) + continue; + + if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) { + *dc_ip = ip_list[i]; + SAFE_FREE(ip_list); + return True; + } + zero_ip(&ip_list[i]); + } + + /* + * Secondly try and contact a random PDC/BDC. + */ + + i = (sys_random() % count); + + if (!is_zero_ip(ip_list[i]) && + name_status_find(domain, 0x1c, 0x20, + ip_list[i], srv_name)) { + *dc_ip = ip_list[i]; + SAFE_FREE(ip_list); + return True; + } + zero_ip(&ip_list[i]); /* Tried and failed. */ + + /* Finally return first DC that we can contact using a node + status */ + for (i = 0; i < count; i++) { + if (is_zero_ip(ip_list[i])) + continue; + + if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) { + *dc_ip = ip_list[i]; + SAFE_FREE(ip_list); + return True; + } + } + + SAFE_FREE(ip_list); + + return False; +} + + static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out) { static struct get_dc_name_cache *get_dc_name_cache; struct get_dc_name_cache *dcc; - struct in_addr *ip_list, dc_ip; - int count, i; + struct in_addr dc_ip; + BOOL ret; /* Check the cache for previous lookups */ @@ -144,66 +254,22 @@ static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr DLIST_ADD(get_dc_name_cache, dcc); - /* Lookup domain controller name. Try the real PDC first to avoid - SAM sync delays */ - if (!get_dc_list(True, domain, &ip_list, &count)) { - if (!get_dc_list(False, domain, &ip_list, &count)) { - DEBUG(3, ("Could not look up dc's for domain %s\n", domain)); - return False; - } - } + zero_ip(&dc_ip); - /* Pick a nice close server */ - /* Look for DC on local net */ - - for (i = 0; i < count; i++) { - if (!is_local_net(ip_list[i])) - continue; - - if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) { - dc_ip = ip_list[i]; - goto done; - } - zero_ip(&ip_list[i]); + ret = False; + if (lp_security() == SEC_ADS) { + ret = cm_ads_find_dc(domain, &dc_ip, srv_name); } - - /* - * Secondly try and contact a random PDC/BDC. - */ - - i = (sys_random() % count); - - if (!is_zero_ip(ip_list[i]) && - name_status_find(domain, 0x1c, 0x20, - ip_list[i], srv_name)) { - dc_ip = ip_list[i]; - goto done; + if (!ret) { + /* fall back on rpc methods if the ADS methods fail */ + ret = cm_rpc_find_dc(domain, &dc_ip, srv_name); } - zero_ip(&ip_list[i]); /* Tried and failed. */ - /* Finally return first DC that we can contact */ - - for (i = 0; i < count; i++) { - if (is_zero_ip(ip_list[i])) - continue; - - if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) { - dc_ip = ip_list[i]; - goto done; - } + if (!ret) { + return False; } - /* No-one to talk to )-: */ - return False; /* Boo-hoo */ - - done: - /* We have the netbios name and IP address of a domain controller. - Ideally we should sent a SAMLOGON request to determine whether - the DC is alive and kicking. If we can catch a dead DC before - performing a cli_connect() we can avoid a 30-second timeout. */ - /* We have a name so make the cache entry positive now */ - fstrcpy(dcc->srv_name, srv_name); DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name, @@ -211,8 +277,6 @@ static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out = dc_ip; - SAFE_FREE(ip_list); - return True; } @@ -262,11 +326,23 @@ static struct failed_connection_cache *failed_connection_cache; /* Add an entry to the failed conneciton cache */ -static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn, NTSTATUS result) { +static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn, + NTSTATUS result) +{ struct failed_connection_cache *fcc; SMB_ASSERT(!NT_STATUS_IS_OK(result)); + /* Check we already aren't in the cache */ + + for (fcc = failed_connection_cache; fcc; fcc = fcc->next) { + if (strequal(fcc->domain_name, new_conn->domain)) { + DEBUG(10, ("domain %s already tried and failed\n", + fcc->domain_name)); + return; + } + } + /* Create negative lookup cache entry for this domain and controller */ if (!(fcc = (struct failed_connection_cache *) @@ -794,7 +870,7 @@ NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd, return result; } - result = new_cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ? + result = cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ? SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd); if (!NT_STATUS_IS_OK(result)) { @@ -808,7 +884,7 @@ NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd, } /* Try again */ - result = new_cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ? + result = cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ? SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd); } diff --git a/source3/nsswitch/winbindd_group.c b/source3/nsswitch/winbindd_group.c index 20563ba7bd..abb6b9da75 100644 --- a/source3/nsswitch/winbindd_group.c +++ b/source3/nsswitch/winbindd_group.c @@ -196,6 +196,9 @@ enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state) gid_t gid; int gr_mem_len; + /* Ensure null termination */ + state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0'; + DEBUG(3, ("[%5d]: getgrnam %s\n", state->pid, state->request.data.groupname)); @@ -783,6 +786,9 @@ enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state) int i; TALLOC_CTX *mem_ctx; + /* Ensure null termination */ + state->request.data.username[sizeof(state->request.data.username)-1]='\0'; + DEBUG(3, ("[%5d]: getgroups %s\n", state->pid, state->request.data.username)); diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h index 0f0e40a2ec..9eea94e7c0 100644 --- a/source3/nsswitch/winbindd_nss.h +++ b/source3/nsswitch/winbindd_nss.h @@ -36,7 +36,7 @@ /* Update this when you change the interface. */ -#define WINBIND_INTERFACE_VERSION 4 +#define WINBIND_INTERFACE_VERSION 5 /* Socket commands */ @@ -107,6 +107,12 @@ enum winbindd_cmd { WINBINDD_NUM_CMDS }; +#define WINBIND_PAM_INFO3_NDR 0x0001 +#define WINBIND_PAM_INFO3_TEXT 0x0002 +#define WINBIND_PAM_NTKEY 0x0004 +#define WINBIND_PAM_LMKEY 0x0008 +#define WINBIND_PAM_CONTACT_TRUSTDOM 0x0010 + /* Winbind request structure */ struct winbindd_request { @@ -132,6 +138,8 @@ struct winbindd_request { uint16 lm_resp_len; fstring nt_resp; uint16 nt_resp_len; + fstring workstation; + uint32 flags; } auth_crap; struct { fstring user; @@ -216,6 +224,8 @@ struct winbindd_response { fstring nt_status_string; fstring error_string; int pam_error; + char nt_session_key[16]; + char first_8_lm_hash[8]; } auth; } data; diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c index e608f826c9..a8b508a49c 100644 --- a/source3/nsswitch/winbindd_pam.c +++ b/source3/nsswitch/winbindd_pam.c @@ -23,17 +23,41 @@ */ #include "winbindd.h" - #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND + +static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx, + struct winbindd_cli_state *state, + NET_USER_INFO_3 *info3) +{ + prs_struct ps; + uint32 size; + if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) { + return NT_STATUS_NO_MEMORY; + } + if (!net_io_user_info3("", info3, &ps, 1, 3)) { + prs_mem_free(&ps); + return NT_STATUS_UNSUCCESSFUL; + } + + size = prs_data_size(&ps); + state->response.extra_data = memdup(prs_data_p(&ps), size); + if (!state->response.extra_data) { + prs_mem_free(&ps); + return NT_STATUS_NO_MEMORY; + } + state->response.length += size; + prs_mem_free(&ps); + return NT_STATUS_OK; +} + /* Return a password structure from a username. */ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) { NTSTATUS result; fstring name_domain, name_user; - int passlen; unsigned char trust_passwd[16]; time_t last_change_time; uint32 smb_uid_low; @@ -46,6 +70,12 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) extern pstring global_myname; + /* Ensure null termination */ + state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0'; + + /* Ensure null termination */ + state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0'; + DEBUG(3, ("[%5d]: pam auth %s\n", state->pid, state->request.data.auth.user)); @@ -64,16 +94,14 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) goto done; } - passlen = strlen(state->request.data.auth.pass); - { unsigned char local_lm_response[24]; unsigned char local_nt_response[24]; generate_random_buffer(chal, 8, False); - SMBencrypt( (const uchar *)state->request.data.auth.pass, chal, local_lm_response); + SMBencrypt(state->request.data.auth.pass, chal, local_lm_response); - SMBNTencrypt((const uchar *)state->request.data.auth.pass, chal, local_nt_response); + SMBNTencrypt(state->request.data.auth.pass, chal, local_nt_response); lm_resp = data_blob_talloc(mem_ctx, local_lm_response, sizeof(local_lm_response)); nt_resp = data_blob_talloc(mem_ctx, local_nt_response, sizeof(local_nt_response)); @@ -140,34 +168,67 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) NET_USER_INFO_3 info3; struct cli_state *cli = NULL; TALLOC_CTX *mem_ctx = NULL; - const char *domain = NULL; + char *user = NULL; + char *domain = NULL; + char *contact_domain; + char *workstation; DATA_BLOB lm_resp, nt_resp; extern pstring global_myname; - DEBUG(3, ("[%5d]: pam auth crap domain: %s user: %s\n", state->pid, - state->request.data.auth_crap.domain, state->request.data.auth_crap.user)); + /* Ensure null termination */ + state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]='\0'; + + /* Ensure null termination */ + state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]='\0'; - if (!(mem_ctx = talloc_init_named("winbind pam auth crap for %s", state->request.data.auth.user))) { + if (!(mem_ctx = talloc_init_named("winbind pam auth crap for (utf8) %s", state->request.data.auth.user))) { DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n")); result = NT_STATUS_NO_MEMORY; goto done; } + if (pull_utf8_talloc(mem_ctx, &user, state->request.data.auth_crap.user) < 0) { + DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n")); + } + if (*state->request.data.auth_crap.domain) { - domain = talloc_strdup(mem_ctx, state->request.data.auth_crap.domain); + if (pull_utf8_talloc(mem_ctx, &domain, state->request.data.auth_crap.domain) < 0) { + DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n")); + } } else if (lp_winbind_use_default_domain()) { - domain = talloc_strdup(mem_ctx, lp_workgroup()); + domain = lp_workgroup(); } else { - DEBUG(5,("no domain specified with username (%s) - failing auth\n", state->request.data.auth.user)); + DEBUG(5,("no domain specified with username (%s) - failing auth\n", + user)); result = NT_STATUS_INVALID_PARAMETER; goto done; } - if (!domain) { - DEBUG(0,("winbindd_pam_auth_crap: talloc_strdup failed!\n")); - result = NT_STATUS_NO_MEMORY; + DEBUG(3, ("[%5d]: pam auth crap domain: %s user: %s\n", state->pid, + domain, user)); + + if (lp_allow_trusted_domains() && (state->request.data.auth_crap.flags & WINBIND_PAM_CONTACT_TRUSTDOM)) { + contact_domain = domain; + } else { + contact_domain = lp_workgroup(); + } + + if (*state->request.data.auth_crap.workstation) { + if (pull_utf8_talloc(mem_ctx, &workstation, state->request.data.auth_crap.workstation) < 0) { + DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n")); + } + } else { + workstation = global_myname; + } + + if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp) + || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) { + DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n", + state->request.data.auth_crap.lm_resp_len, + state->request.data.auth_crap.nt_resp_len)); + result = NT_STATUS_INVALID_PARAMETER; goto done; } @@ -175,13 +236,15 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) nt_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.nt_resp, state->request.data.auth_crap.nt_resp_len); /* - * Get the machine account password for our primary domain + * Get the machine account password for the domain to contact. + * This is either our own domain for a workstation, or possibly + * any domain for a PDC with trusted domains. */ - if (!secrets_fetch_trust_account_password( - lp_workgroup(), trust_passwd, &last_change_time)) { + if (!secrets_fetch_trust_account_password ( + contact_domain, trust_passwd, &last_change_time)) { DEBUG(0, ("winbindd_pam_auth: could not fetch trust account " - "password for domain %s\n", lp_workgroup())); + "password for domain %s\n", contact_domain)); result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; goto done; } @@ -189,7 +252,7 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) ZERO_STRUCT(info3); /* Don't shut this down - it belongs to the connection cache code */ - result = cm_get_netlogon_cli(lp_workgroup(), trust_passwd, &cli); + result = cm_get_netlogon_cli(contact_domain, trust_passwd, &cli); if (!NT_STATUS_IS_OK(result)) { DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n", nt_errstr(result))); @@ -197,27 +260,43 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) } result = cli_netlogon_sam_network_logon(cli, mem_ctx, - state->request.data.auth_crap.user, domain, - global_myname, state->request.data.auth_crap.chal, + user, domain, + workstation, state->request.data.auth_crap.chal, lm_resp, nt_resp, &info3); if (NT_STATUS_IS_OK(result)) { uni_group_cache_store_netlogon(mem_ctx, &info3); + if (state->request.data.auth_crap.flags & WINBIND_PAM_INFO3_NDR) { + result = append_info3_as_ndr(mem_ctx, state, &info3); + } + +#if 0 + /* we don't currently do this stuff right */ + if (state->request.data.auth_crap.flags & WINBIND_PAM_NTKEY) { + SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) == sizeof(info3.user_sess_key)); + memcpy(state->response.data.auth.nt_session_key, info3.user_sess_key, sizeof(state->response.data.auth.nt_session_key) /* 16 */); + } + if (state->request.data.auth_crap.flags & WINBIND_PAM_LMKEY) { + SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) <= sizeof(info3.user_sess_key)); + memcpy(state->response.data.auth.first_8_lm_hash, info3.padding, sizeof(state->response.data.auth.nt_session_key) /* 16 */); + } +#endif } done: state->response.data.auth.nt_status = NT_STATUS_V(result); - fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); - fstrcpy(state->response.data.auth.error_string, nt_errstr(result)); + push_utf8_fstring(state->response.data.auth.nt_status_string, nt_errstr(result)); + push_utf8_fstring(state->response.data.auth.error_string, nt_errstr(result)); state->response.data.auth.pam_error = nt_status_to_pam(result); - DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("NTLM CRAP authenticaion for user [%s]\\[%s] returned %s (PAM: %d)\n", - state->request.data.auth_crap.domain, - state->request.data.auth_crap.user, - state->response.data.auth.nt_status_string, - state->response.data.auth.pam_error)); + DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, + ("NTLM CRAP authenticaion for user [%s]\\[%s] returned %s (PAM: %d)\n", + domain, + user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); if (mem_ctx) talloc_destroy(mem_ctx); diff --git a/source3/nsswitch/winbindd_rpc.c b/source3/nsswitch/winbindd_rpc.c index 2bb0e8c49f..5ec34f663d 100644 --- a/source3/nsswitch/winbindd_rpc.c +++ b/source3/nsswitch/winbindd_rpc.c @@ -575,22 +575,23 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, uint32 *num_domains, char ***names, + char ***alt_names, DOM_SID **dom_sids) { CLI_POLICY_HND *hnd; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; uint32 enum_ctx = 0; - uint32 pref_num_domains = 5; DEBUG(3,("rpc: trusted_domains\n")); *num_domains = 0; + *alt_names = NULL; if (!(hnd = cm_get_lsa_handle(lp_workgroup()))) goto done; result = cli_lsa_enum_trust_dom(hnd->cli, mem_ctx, - &hnd->pol, &enum_ctx, &pref_num_domains, + &hnd->pol, &enum_ctx, num_domains, names, dom_sids); done: return result; @@ -621,6 +622,13 @@ done: return status; } +/* find alternate names list for the domain - none for rpc */ +static NTSTATUS alternate_name(struct winbindd_domain *domain) +{ + return NT_STATUS_OK; +} + + /* the rpc backend methods are exposed via this structure */ struct winbindd_methods msrpc_methods = { False, @@ -633,5 +641,6 @@ struct winbindd_methods msrpc_methods = { lookup_groupmem, sequence_number, trusted_domains, - domain_sid + domain_sid, + alternate_name }; diff --git a/source3/nsswitch/winbindd_sid.c b/source3/nsswitch/winbindd_sid.c index 372898a08a..44f857d6be 100644 --- a/source3/nsswitch/winbindd_sid.c +++ b/source3/nsswitch/winbindd_sid.c @@ -36,6 +36,9 @@ enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state) fstring name; fstring dom_name; + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + DEBUG(3, ("[%5d]: lookupsid %s\n", state->pid, state->request.data.sid)); @@ -79,6 +82,12 @@ enum winbindd_result winbindd_lookupname(struct winbindd_cli_state *state) DOM_SID sid; struct winbindd_domain *domain; + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.name.dom_name)-1]='\0'; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.name.name)-1]='\0'; + DEBUG(3, ("[%5d]: lookupname %s%s%s\n", state->pid, state->request.data.name.dom_name, lp_winbind_separator(), @@ -112,6 +121,9 @@ enum winbindd_result winbindd_sid_to_uid(struct winbindd_cli_state *state) { DOM_SID sid; + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + DEBUG(3, ("[%5d]: sid to uid %s\n", state->pid, state->request.data.sid)); @@ -139,6 +151,9 @@ enum winbindd_result winbindd_sid_to_gid(struct winbindd_cli_state *state) { DOM_SID sid; + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + DEBUG(3, ("[%5d]: sid to gid %s\n", state->pid, state->request.data.sid)); diff --git a/source3/nsswitch/winbindd_user.c b/source3/nsswitch/winbindd_user.c index 55593d6ae5..4f57fd2c72 100644 --- a/source3/nsswitch/winbindd_user.c +++ b/source3/nsswitch/winbindd_user.c @@ -103,6 +103,9 @@ enum winbindd_result winbindd_getpwnam(struct winbindd_cli_state *state) struct winbindd_domain *domain; TALLOC_CTX *mem_ctx; + /* Ensure null termination */ + state->request.data.username[sizeof(state->request.data.username)-1]='\0'; + DEBUG(3, ("[%5d]: getpwnam %s\n", state->pid, state->request.data.username)); diff --git a/source3/nsswitch/winbindd_util.c b/source3/nsswitch/winbindd_util.c index d5668a2bb6..daa3abb340 100644 --- a/source3/nsswitch/winbindd_util.c +++ b/source3/nsswitch/winbindd_util.c @@ -74,19 +74,17 @@ void free_domain_list(void) } /* Add a trusted domain to our list of domains */ - -static struct winbindd_domain *add_trusted_domain(char *domain_name, - struct winbindd_methods *methods) +static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name, + struct winbindd_methods *methods, + DOM_SID *sid) { struct winbindd_domain *domain; /* We can't call domain_list() as this function is called from init_domain_list() and we'll get stuck in a loop. */ - for (domain = _domain_list; domain; domain = domain->next) { - if (strcmp(domain_name, domain->name) == 0) { - DEBUG(3, ("domain %s already in domain list\n", - domain_name)); + if (strcmp(domain_name, domain->name) == 0 || + strcmp(domain_name, domain->alt_name) == 0) { return domain; } } @@ -101,40 +99,95 @@ static struct winbindd_domain *add_trusted_domain(char *domain_name, ZERO_STRUCTP(domain); + /* prioritise the short name */ + if (strchr_m(domain_name, '.') && alt_name && *alt_name) { + fstrcpy(domain->name, alt_name); + fstrcpy(domain->alt_name, domain_name); + } else { fstrcpy(domain->name, domain_name); + if (alt_name) { + fstrcpy(domain->alt_name, alt_name); + } + } + domain->methods = methods; domain->sequence_number = DOM_SEQUENCE_NONE; domain->last_seq_check = 0; + if (sid) { + sid_copy(&domain->sid, sid); + } /* Link to domain list */ - DLIST_ADD(_domain_list, domain); + DEBUG(1,("Added domain %s %s %s\n", + domain->name, domain->alt_name, + sid?sid_string_static(&domain->sid):"")); + return domain; } -/* Look up global info for the winbind daemon */ +/* + rescan our domains looking for new trusted domains + */ +void rescan_trusted_domains(void) +{ + struct winbindd_domain *domain; + TALLOC_CTX *mem_ctx; + static time_t last_scan; + time_t t = time(NULL); + + /* ony rescan every few minutes */ + if ((unsigned)(t - last_scan) < WINBINDD_RESCAN_FREQ) { + return; + } + last_scan = time(NULL); + + DEBUG(1, ("scanning trusted domain list\n")); + + if (!(mem_ctx = talloc_init_named("init_domain_list"))) + return; + + for (domain = _domain_list; domain; domain = domain->next) { + NTSTATUS result; + char **names; + char **alt_names; + int num_domains = 0; + DOM_SID *dom_sids; + int i; + + result = domain->methods->trusted_domains(domain, mem_ctx, &num_domains, + &names, &alt_names, &dom_sids); + if (!NT_STATUS_IS_OK(result)) { + continue; + } + + /* Add each domain to the trusted domain list. Each domain inherits + the access methods of its parent */ + for(i = 0; i < num_domains; i++) { + DEBUG(10,("Found domain %s\n", names[i])); + add_trusted_domain(names[i], + alt_names?alt_names[i]:NULL, + domain->methods, &dom_sids[i]); + } + } + + talloc_destroy(mem_ctx); +} + +/* Look up global info for the winbind daemon */ BOOL init_domain_list(void) { NTSTATUS result; - TALLOC_CTX *mem_ctx; extern struct winbindd_methods cache_methods; struct winbindd_domain *domain; - DOM_SID *dom_sids; - char **names; - int num_domains = 0; - - if (!(mem_ctx = talloc_init_named("init_domain_list"))) - return False; /* Free existing list */ - free_domain_list(); /* Add ourselves as the first entry */ - - domain = add_trusted_domain(lp_workgroup(), &cache_methods); + domain = add_trusted_domain(lp_workgroup(), NULL, &cache_methods, NULL); /* Now we *must* get the domain sid for our primary domain. Go into a holding pattern until that is available */ @@ -147,29 +200,12 @@ BOOL init_domain_list(void) 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")); + /* get any alternate name for the primary domain */ + cache_methods.alternate_name(domain); - result = cache_methods.trusted_domains(domain, mem_ctx, &num_domains, - &names, &dom_sids); + /* do an initial scan for trusted domains */ + rescan_trusted_domains(); - /* Add each domain to the trusted domain list */ - if (NT_STATUS_IS_OK(result)) { - int i; - for(i = 0; i < num_domains; i++) { - domain = add_trusted_domain(names[i], &cache_methods); - if (!domain) continue; - sid_copy(&domain->sid, &dom_sids[i]); - DEBUG(1,("Added domain %s (%s)\n", - domain->name, - sid_string_static(&domain->sid))); - } - } - - talloc_destroy(mem_ctx); return True; } @@ -184,7 +220,7 @@ struct winbindd_domain *find_domain_from_name(const char *domain_name) for (domain = domain_list(); domain != NULL; domain = domain->next) { if (strequal(domain_name, domain->name) || - strequal(domain_name, domain->full_name)) + (domain->alt_name[0] && strequal(domain_name, domain->alt_name))) return domain; } diff --git a/source3/nsswitch/winbindd_wins.c b/source3/nsswitch/winbindd_wins.c index 8f9a7414bd..8ddd5dc10d 100644 --- a/source3/nsswitch/winbindd_wins.c +++ b/source3/nsswitch/winbindd_wins.c @@ -122,6 +122,9 @@ enum winbindd_result winbindd_wins_byip(struct winbindd_cli_state *state) int i, count, maxlen, size; struct node_status *status; + /* Ensure null termination */ + state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0'; + DEBUG(3, ("[%5d]: wins_byip %s\n", state->pid, state->request.data.winsreq)); @@ -166,6 +169,9 @@ enum winbindd_result winbindd_wins_byname(struct winbindd_cli_state *state) fstring response; char * addr; + /* Ensure null termination */ + state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0'; + DEBUG(3, ("[%5d]: wins_byname %s\n", state->pid, state->request.data.winsreq)); -- cgit