r21240: Fix longstanding Bug #4009.
authorGünther Deschner <gd@samba.org>
Thu, 8 Feb 2007 17:02:39 +0000 (17:02 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:17:50 +0000 (12:17 -0500)
For the winbind cached ADS LDAP connection handling
(ads_cached_connection()) we were (incorrectly) assuming that the
service ticket lifetime equaled the tgt lifetime. For setups where the
service ticket just lives 10 minutes, we were leaving hundreds of LDAP
connections in CLOSE_WAIT state, until we fail to service entirely with
"Too many open files".

Also sequence_number() in winbindd_ads.c needs to delete the cached LDAP
connection after the ads_do_search_retry() has failed to submit the
search request (although the bind succeeded (returning an expired
service ticket that we cannot delete from the memory cred cache - this
will get fixed later)).

Guenther
(This used to be commit 7e1a84b7226fb8dcd5d34c64a3478a6d886a9a91)

13 files changed:
source3/include/ads.h
source3/include/includes.h
source3/libads/kerberos.c
source3/libads/ldap_utils.c
source3/libads/sasl.c
source3/libsmb/cliconnect.c
source3/libsmb/clikrb5.c
source3/libsmb/clispnego.c
source3/nsswitch/idmap_ad.c
source3/nsswitch/winbindd_ads.c
source3/nsswitch/winbindd_pam.c
source3/rpc_client/cli_pipe.c
source3/utils/ntlm_auth.c

index d97ae1531f956f27b87a9c2b086a1518cdc20894..29df0d2f3532abb5c3597053ca937015ed7b67b9 100644 (file)
@@ -42,7 +42,8 @@ typedef struct {
                char *kdc_server;
                unsigned flags;
                int time_offset;
-               time_t expire;
+               time_t tgt_expire;
+               time_t tgs_expire;
                time_t renewable;
        } auth;
 
index 8aaaba979936452919fc6385c45a25589d362d39..3864faddb94983e93b185167904f13475d88de39 100644 (file)
@@ -1165,7 +1165,7 @@ BOOL smb_krb5_principal_compare_any_realm(krb5_context context,
                                          krb5_const_principal princ1, 
                                          krb5_const_principal princ2);
 int cli_krb5_get_ticket(const char *principal, time_t time_offset, 
-                       DATA_BLOB *ticket, DATA_BLOB *session_key_krb5, uint32 extra_ap_opts, const char *ccname);
+                       DATA_BLOB *ticket, DATA_BLOB *session_key_krb5, uint32 extra_ap_opts, const char *ccname, time_t *tgs_expire);
 PAC_LOGON_INFO *get_logon_info_from_pac(PAC_DATA *pac_data);
 krb5_error_code smb_krb5_renew_ticket(const char *ccache_string, const char *client_string, const char *service_string, time_t *new_start_time);
 krb5_error_code kpasswd_err_to_krb5_err(krb5_error_code res_code);
index 92461bd9c1e37f1d7b706d0bf251987d24dfb0c8..8e8297b07e5490980faf0d7fa88611177ca1b231 100644 (file)
@@ -110,6 +110,10 @@ int kerberos_kinit_password_ext(const char *principal,
 
        krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
        krb5_get_init_creds_opt_set_forwardable(opt, True);
+#if 0
+       /* insane testing */
+       krb5_get_init_creds_opt_set_tkt_life(opt, 60);
+#endif
 
 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
        if (request_pac) {
@@ -216,7 +220,7 @@ int ads_kinit_password(ADS_STRUCT *ads)
        }
        
        ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
-                       &ads->auth.expire, NULL, NULL, False, False, ads->auth.renewable);
+                       &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable);
 
        if (ret) {
                DEBUG(0,("kerberos_kinit_password %s failed: %s\n", 
index af9e9af2b8c59a61c2a9fa10148f68f1e89eef7a..1da51b3c5c9c594c128ccf6f93a0c0582bf26555 100644 (file)
@@ -110,10 +110,10 @@ static ADS_STATUS ads_do_search_retry_internal(ADS_STRUCT *ads, const char *bind
        }
         SAFE_FREE(bp);
 
-       if (!ADS_ERR_OK(status))
+       if (!ADS_ERR_OK(status)) {
                DEBUG(1,("ads reopen failed after error %s\n", 
                         ads_errstr(status)));
-
+       }
        return status;
 }
 
index 7d1fd0d1a839749ef2160d1762c33de5a8a8a4a9..61fd54da1da79a0f910170c5540689d0be6de369 100644 (file)
@@ -147,7 +147,8 @@ static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *princip
        DATA_BLOB session_key = data_blob(NULL, 0);
        int rc;
 
-       rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0);
+       rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
+                                    &ads->auth.tgs_expire);
 
        if (rc) {
                return ADS_ERROR_KRB5(rc);
@@ -218,7 +219,7 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
 #endif
                free(OIDs[i]);
        }
-       DEBUG(3,("ads_sasl_spnego_bind: got server principal name =%s\n", principal));
+       DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", principal));
 
 #ifdef HAVE_KRB5
        if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
@@ -229,6 +230,9 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
                        return status;
                }
 
+               DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
+                         "calling kinit\n", ads_errstr(status)));
+
                status = ADS_ERROR_KRB5(ads_kinit_password(ads)); 
 
                if (ADS_ERR_OK(status)) {
index f29449cfb22253f0613e5b2f4f7c51a28112e51f..2742d70194b4949d5445cac9567724ba83ce0055 100644 (file)
@@ -554,7 +554,7 @@ static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char *
        DEBUG(2,("Doing kerberos session setup\n"));
 
        /* generate the encapsulated kerberos5 ticket */
-       rc = spnego_gen_negTokenTarg(principal, 0, &negTokenTarg, &session_key_krb5, 0);
+       rc = spnego_gen_negTokenTarg(principal, 0, &negTokenTarg, &session_key_krb5, 0, NULL);
 
        if (rc) {
                DEBUG(1, ("spnego_gen_negTokenTarg failed: %s\n", error_message(rc)));
index 305139e1f4752b611f8909eed2f2900e52fbeb32..f06a19b345c2b9b58cc5924fba24be97a80b2676 100644 (file)
@@ -551,7 +551,8 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context,
                                       const krb5_flags ap_req_options,
                                       const char *principal,
                                       krb5_ccache ccache, 
-                                      krb5_data *outbuf)
+                                      krb5_data *outbuf, 
+                                      time_t *expire_time)
 {
        krb5_error_code           retval;
        krb5_principal    server;
@@ -584,6 +585,7 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context,
        }
 
        while (!creds_ready && (i < maxtries)) {
+
                if ((retval = krb5_get_credentials(context, 0, ccache, 
                                                   &creds, &credsp))) {
                        DEBUG(1,("ads_krb5_mk_req: krb5_get_credentials failed for %s (%s)\n",
@@ -599,8 +601,9 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context,
                        krb5_set_real_time(context, t + time_offset + 1, 0);
                }
 
-               if (!ads_cleanup_expired_creds(context, ccache, credsp))
+               if (!ads_cleanup_expired_creds(context, ccache, credsp)) {
                        creds_ready = True;
+               }
 
                i++;
        }
@@ -610,6 +613,10 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context,
                  http_timestring((unsigned)credsp->times.endtime), 
                  (unsigned)credsp->times.endtime));
 
+       if (expire_time) {
+               *expire_time = (time_t)credsp->times.endtime;
+       }
+
        in_data.length = 0;
        retval = krb5_mk_req_extended(context, auth_context, ap_req_options, 
                                      &in_data, credsp, outbuf);
@@ -634,7 +641,9 @@ cleanup_princ:
 */
 int cli_krb5_get_ticket(const char *principal, time_t time_offset, 
                        DATA_BLOB *ticket, DATA_BLOB *session_key_krb5, 
-                       uint32 extra_ap_opts, const char *ccname)
+                       uint32 extra_ap_opts, const char *ccname, 
+                       time_t *tgs_expire)
+
 {
        krb5_error_code retval;
        krb5_data packet;
@@ -678,7 +687,8 @@ int cli_krb5_get_ticket(const char *principal, time_t time_offset,
                                        &auth_context, 
                                        AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
                                        principal,
-                                       ccdef, &packet))) {
+                                       ccdef, &packet,
+                                       tgs_expire))) {
                goto failed;
        }
 
@@ -1409,7 +1419,7 @@ done:
  /* 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, uint32 extra_ap_opts,
-                       const char *ccname) 
+                       const char *ccname, time_t *tgs_expire
 {
         DEBUG(0,("NO KERBEROS SUPPORT\n"));
         return 1;
index a01c009b6e3c3bd9a59505c09581742fbacf67f8..6aca217e259304be67eccd7c6ba484d1874f66b6 100644 (file)
@@ -343,7 +343,8 @@ BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
 */
 int spnego_gen_negTokenTarg(const char *principal, int time_offset, 
                            DATA_BLOB *targ, 
-                           DATA_BLOB *session_key_krb5, uint32 extra_ap_opts)
+                           DATA_BLOB *session_key_krb5, uint32 extra_ap_opts,
+                           time_t *expire_time)
 {
        int retval;
        DATA_BLOB tkt, tkt_wrapped;
@@ -351,7 +352,8 @@ int spnego_gen_negTokenTarg(const char *principal, int time_offset,
 
        /* get a kerberos ticket for the service and extract the session key */
        retval = cli_krb5_get_ticket(principal, time_offset,
-                                       &tkt, session_key_krb5, extra_ap_opts, NULL);
+                                       &tkt, session_key_krb5, extra_ap_opts, NULL, 
+                                       expire_time);
 
        if (retval)
                return retval;
index a0ed0847657bcb52658da074de60783aec89859c..fee53a0539ed7c00c01a079365bc1c6e2fd763e4 100644 (file)
@@ -64,16 +64,23 @@ static ADS_STRUCT *ad_idmap_cached_connection_internal(void)
        struct in_addr dc_ip;   
 
        if (ad_idmap_ads != NULL) {
+
+               time_t expire;
+               time_t now = time(NULL);
+
                ads = ad_idmap_ads;
 
+               expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
+
                /* check for a valid structure */
+               DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
+                         (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
 
-               DEBUG(7, ("Current tickets expire at %d, time is now %d\n",
-                         (uint32) ads->auth.expire, (uint32) time(NULL)));
-               if ( ads->config.realm && (ads->auth.expire > time(NULL))) {
+               if ( ads->config.realm && (expire > time(NULL))) {
                        return ads;
                } else {
                        /* we own this ADS_STRUCT so make sure it goes away */
+                       DEBUG(7,("Deleting expired krb5 credential cache\n"));
                        ads->is_mine = True;
                        ads_destroy( &ads );
                        ads_kdestroy(WINBIND_CCACHE_NAME);
index f572dd08ffa756526293849bfefad52747807f3b..9c8f23b1cf77f0410f8fe08a47aca43f64e92dfe 100644 (file)
@@ -44,17 +44,23 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
        DEBUG(10,("ads_cached_connection\n"));
 
        if (domain->private_data) {
-               ads = (ADS_STRUCT *)domain->private_data;
+
+               time_t expire;
+               time_t now = time(NULL);
 
                /* check for a valid structure */
+               ads = (ADS_STRUCT *)domain->private_data;
 
-               DEBUG(7, ("Current tickets expire at %d, time is now %d\n",
-                         (uint32) ads->auth.expire, (uint32) time(NULL)));
-               if ( ads->config.realm && (ads->auth.expire > time(NULL))) {
+               expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
+
+               DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
+                         (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
+
+               if ( ads->config.realm && (expire > now)) {
                        return ads;
-               }
-               else {
+               } else {
                        /* we own this ADS_STRUCT so make sure it goes away */
+                       DEBUG(7,("Deleting expired krb5 credential cache\n"));
                        ads->is_mine = True;
                        ads_destroy( &ads );
                        ads_kdestroy("MEMORY:winbind_ccache");
@@ -998,11 +1004,15 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
        
        if (!ADS_ERR_OK(rc)) {
        
-               /* its a dead connection ; don't destroy it 
-                  through since ads_USN() has already done 
-                  that indirectly */
-                  
-               domain->private_data = NULL;
+               /* its a dead connection, destroy it */
+
+               if (domain->private_data) {
+                       ads = (ADS_STRUCT *)domain->private_data;
+                       ads->is_mine = True;
+                       ads_destroy(&ads);
+                       ads_kdestroy("MEMORY:winbind_ccache");
+                       domain->private_data = NULL;
+               }
        }
        return ads_ntstatus(rc);
 }
index fcaad1fb1f8d82224e430df1adad7de431670bae..2a5ca40125eb6cee0a998623b3f95d73b034c574 100644 (file)
@@ -598,7 +598,8 @@ static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain,
                                       &tkt, 
                                       &session_key_krb5, 
                                       0, 
-                                      cc);
+                                      cc,
+                                      NULL);
        if (krb5_ret) {
                DEBUG(1,("winbindd_raw_kerberos_login: failed to get ticket for %s: %s\n", 
                        local_service, error_message(krb5_ret)));
index 467e652ca991a45b3e27f6aaf61cb878d87c6874..547f300f3aec8e803b690d1e5e76b41ea555173d 100644 (file)
@@ -927,7 +927,7 @@ static NTSTATUS create_krb5_auth_bind_req( struct rpc_pipe_client *cli,
        /* Create the ticket for the service principal and return it in a gss-api wrapped blob. */
 
        ret = cli_krb5_get_ticket(a->service_principal, 0, &tkt,
-                       &a->session_key, (uint32)AP_OPTS_MUTUAL_REQUIRED, NULL);
+                       &a->session_key, (uint32)AP_OPTS_MUTUAL_REQUIRED, NULL, NULL);
 
        if (ret) {
                DEBUG(1,("create_krb5_auth_bind_req: cli_krb5_get_ticket for principal %s "
index 28de39c4f45107b5dce755cd0445c3c919cb50fe..1e7b361e860ca45faadb8171ecebf31e691b9139 100644 (file)
@@ -1424,7 +1424,7 @@ static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
               spnego.negTokenInit.mechListMIC.length);
        principal[spnego.negTokenInit.mechListMIC.length] = '\0';
 
-       retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL);
+       retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL, NULL);
 
        if (retval) {
 
@@ -1446,7 +1446,7 @@ static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
                        return False;
                }
 
-               retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL);
+               retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL, NULL);
 
                if (retval) {
                        DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));