コード例 #1
0
static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
{
	NTSTATUS status;
	unsigned time_diff;
	time_t t = time(NULL);
	unsigned cache_time = lp_winbind_cache_time();

	get_cache( domain );

#if 0	/* JERRY -- disable as the default cache time is now 5 minutes */
	/* trying to reconnect is expensive, don't do it too often */
	if (domain->sequence_number == DOM_SEQUENCE_NONE) {
		cache_time *= 8;
	}
#endif

	time_diff = t - domain->last_seq_check;

	/* see if we have to refetch the domain sequence number */
	if (!force && (time_diff < cache_time)) {
		DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
		goto done;
	}
	
	/* try to get the sequence number from the tdb cache first */
	/* this will update the timestamp as well */
	
	status = fetch_cache_seqnum( domain, t );
	if ( NT_STATUS_IS_OK(status) )
		goto done;	

	status = domain->backend->sequence_number(domain, &domain->sequence_number);

	if (!NT_STATUS_IS_OK(status)) {
		domain->sequence_number = DOM_SEQUENCE_NONE;
	}
	
	domain->last_status = status;
	domain->last_seq_check = time(NULL);
	
	/* save the new sequence number ni the cache */
	store_cache_seqnum( domain );

done:
	DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
		   domain->name, domain->sequence_number));

	return;
}
コード例 #2
0
static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
{
	TDB_DATA data;
	fstring key;
	uint32 time_diff;
	
	if (!wcache->tdb) {
		DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
		return NT_STATUS_UNSUCCESSFUL;
	}
		
	fstr_sprintf( key, "SEQNUM/%s", domain->name );
	
	data = tdb_fetch_bystring( wcache->tdb, key );
	if ( !data.dptr || data.dsize!=8 ) {
		DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
		return NT_STATUS_UNSUCCESSFUL;
	}
	
	domain->sequence_number = IVAL(data.dptr, 0);
	domain->last_seq_check  = IVAL(data.dptr, 4);
	
	/* have we expired? */
	
	time_diff = now - domain->last_seq_check;
	if ( time_diff > lp_winbind_cache_time() ) {
		DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
			domain->name, domain->sequence_number,
			(uint32)domain->last_seq_check));
		return NT_STATUS_UNSUCCESSFUL;
	}

	DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
		domain->name, domain->sequence_number, 
		(uint32)domain->last_seq_check));

	return NT_STATUS_OK;
}
コード例 #3
0
ファイル: winbindd_cred_cache.c プロジェクト: AIdrifter/samba
NTSTATUS add_ccache_to_list(const char *princ_name,
			    const char *ccname,
			    const char *service,
			    const char *username,
			    const char *pass,
			    const char *realm,
			    uid_t uid,
			    time_t create_time,
			    time_t ticket_end,
			    time_t renew_until,
			    bool postponed_request)
{
	struct WINBINDD_CCACHE_ENTRY *entry = NULL;
	struct timeval t;
	NTSTATUS ntret;
#ifdef HAVE_KRB5
	int ret;
#endif

	if ((username == NULL && princ_name == NULL) ||
	    ccname == NULL || uid < 0) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (ccache_entry_count() + 1 > MAX_CCACHES) {
		DEBUG(10,("add_ccache_to_list: "
			"max number of ccaches reached\n"));
		return NT_STATUS_NO_MORE_ENTRIES;
	}

	/* If it is cached login, destroy krb5 ticket
	 * to avoid surprise. */
#ifdef HAVE_KRB5
	if (postponed_request) {
		/* ignore KRB5_FCC_NOFILE error here */
		ret = ads_kdestroy(ccname);
		if (ret == KRB5_FCC_NOFILE) {
			ret = 0;
		}
		if (ret) {
			DEBUG(0, ("add_ccache_to_list: failed to destroy "
				   "user krb5 ccache %s with %s\n", ccname,
				   error_message(ret)));
			return krb5_to_nt_status(ret);
		}
		DEBUG(10, ("add_ccache_to_list: successfully destroyed "
			   "krb5 ccache %s for user %s\n", ccname,
			   username));
	}
#endif

	/* Reference count old entries */
	entry = get_ccache_by_username(username);
	if (entry) {
		/* Check cached entries are identical. */
		if (!ccache_entry_identical(username, uid, ccname)) {
			return NT_STATUS_INVALID_PARAMETER;
		}
		entry->ref_count++;
		DEBUG(10,("add_ccache_to_list: "
			"ref count on entry %s is now %d\n",
			username, entry->ref_count));
		/* FIXME: in this case we still might want to have a krb5 cred
		 * event handler created - gd
		 * Add ticket refresh handler here */

		if (!lp_winbind_refresh_tickets() || renew_until <= 0) {
			return NT_STATUS_OK;
		}

		if (!entry->event) {
			if (postponed_request) {
				t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
				add_krb5_ticket_gain_handler_event(entry, t);
			} else {
				/* Renew at 1/2 the ticket expiration time */
#if defined(DEBUG_KRB5_TKT_RENEWAL)
				t = timeval_set(time(NULL)+30, 0);
#else
				t = timeval_set(krb5_event_refresh_time(ticket_end),
						0);
#endif
				if (!entry->refresh_time) {
					entry->refresh_time = t.tv_sec;
				}
				entry->event = tevent_add_timer(winbind_event_context(),
							       entry,
							       t,
							       krb5_ticket_refresh_handler,
							       entry);
			}

			if (!entry->event) {
				ntret = remove_ccache(username);
				if (!NT_STATUS_IS_OK(ntret)) {
					DEBUG(0, ("add_ccache_to_list: Failed to remove krb5 "
						  "ccache %s for user %s\n", entry->ccname,
						  entry->username));
					DEBUG(0, ("add_ccache_to_list: error is %s\n",
						  nt_errstr(ntret)));
					return ntret;
				}
				return NT_STATUS_NO_MEMORY;
			}

			DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));

		}

		/*
		 * If we're set up to renew our krb5 tickets, we must
		 * cache the credentials in memory for the ticket
		 * renew function (or increase the reference count
		 * if we're logging in more than once). Fix inspired
		 * by patch from Ian Gordon <*****@*****.**>
		 * for bugid #9098.
		 */

		ntret = winbindd_add_memory_creds(username, uid, pass);
		DEBUG(10, ("winbindd_add_memory_creds returned: %s\n",
			nt_errstr(ntret)));

		return NT_STATUS_OK;
	}

	entry = talloc(NULL, struct WINBINDD_CCACHE_ENTRY);
	if (!entry) {
		return NT_STATUS_NO_MEMORY;
	}

	ZERO_STRUCTP(entry);

	if (username) {
		entry->username = talloc_strdup(entry, username);
		if (!entry->username) {
			goto no_mem;
		}
	}
	if (princ_name) {
		entry->principal_name = talloc_strdup(entry, princ_name);
		if (!entry->principal_name) {
			goto no_mem;
		}
	}
	if (service) {
		entry->service = talloc_strdup(entry, service);
		if (!entry->service) {
			goto no_mem;
		}
	}

	entry->ccname = talloc_strdup(entry, ccname);
	if (!entry->ccname) {
		goto no_mem;
	}

	entry->realm = talloc_strdup(entry, realm);
	if (!entry->realm) {
		goto no_mem;
	}

	entry->create_time = create_time;
	entry->renew_until = renew_until;
	entry->uid = uid;
	entry->ref_count = 1;

	if (!lp_winbind_refresh_tickets() || renew_until <= 0) {
		goto add_entry;
	}

	if (postponed_request) {
		t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
		add_krb5_ticket_gain_handler_event(entry, t);
	} else {
		/* Renew at 1/2 the ticket expiration time */
#if defined(DEBUG_KRB5_TKT_RENEWAL)
		t = timeval_set(time(NULL)+30, 0);
#else
		t = timeval_set(krb5_event_refresh_time(ticket_end), 0);
#endif
		if (entry->refresh_time == 0) {
			entry->refresh_time = t.tv_sec;
		}
		entry->event = tevent_add_timer(winbind_event_context(),
					       entry,
					       t,
					       krb5_ticket_refresh_handler,
					       entry);
	}

	if (!entry->event) {
		goto no_mem;
	}

	DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));

 add_entry:

	DLIST_ADD(ccache_list, entry);

	DEBUG(10,("add_ccache_to_list: "
		"added ccache [%s] for user [%s] to the list\n",
		ccname, username));

	if (entry->event) {
		/*
		 * If we're set up to renew our krb5 tickets, we must
		 * cache the credentials in memory for the ticket
		 * renew function. Fix inspired by patch from
		 * Ian Gordon <*****@*****.**> for
		 * bugid #9098.
		 */

		ntret = winbindd_add_memory_creds(username, uid, pass);
		DEBUG(10, ("winbindd_add_memory_creds returned: %s\n",
			nt_errstr(ntret)));
	}

	return NT_STATUS_OK;

 no_mem:

	TALLOC_FREE(entry);
	return NT_STATUS_NO_MEMORY;
}
コード例 #4
0
ファイル: winbindd_cred_cache.c プロジェクト: AIdrifter/samba
static void krb5_ticket_gain_handler(struct tevent_context *event_ctx,
				     struct tevent_timer *te,
				     struct timeval now,
				     void *private_data)
{
	struct WINBINDD_CCACHE_ENTRY *entry =
		talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
#ifdef HAVE_KRB5
	int ret;
	struct timeval t;
	struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
	struct winbindd_domain *domain = NULL;
#endif

	DEBUG(10,("krb5_ticket_gain_handler called\n"));
	DEBUGADD(10,("event called for: %s, %s\n",
		entry->ccname, entry->username));

	TALLOC_FREE(entry->event);

#ifdef HAVE_KRB5

	if (!cred_ptr || !cred_ptr->pass) {
		DEBUG(10,("krb5_ticket_gain_handler: no memory creds\n"));
		return;
	}

	if ((domain = find_domain_from_name(entry->realm)) == NULL) {
		DEBUG(0,("krb5_ticket_gain_handler: unknown domain\n"));
		return;
	}

	if (!domain->online) {
		goto retry_later;
	}

	set_effective_uid(entry->uid);

	ret = kerberos_kinit_password_ext(entry->principal_name,
					  cred_ptr->pass,
					  0, /* hm, can we do time correction here ? */
					  &entry->refresh_time,
					  &entry->renew_until,
					  entry->ccname,
					  False, /* no PAC required anymore */
					  True,
					  WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
					  NULL);
	gain_root_privilege();

	if (ret) {
		DEBUG(3,("krb5_ticket_gain_handler: "
			"could not kinit: %s\n",
			error_message(ret)));
		/* evil. If we cannot do it, destroy any the __maybe__ 
		 * __existing__ ticket */
		ads_kdestroy(entry->ccname);
		goto retry_later;
	}

	DEBUG(10,("krb5_ticket_gain_handler: "
		"successful kinit for: %s in ccache: %s\n",
		entry->principal_name, entry->ccname));

	goto got_ticket;

  retry_later:
 
#if defined(DEBUG_KRB5_TKT_RENEWAL)
 	t = timeval_set(time(NULL) + 30, 0);
#else
	t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
#endif

	add_krb5_ticket_gain_handler_event(entry, t);
	return;

  got_ticket:

#if defined(DEBUG_KRB5_TKT_RENEWAL)
	t = timeval_set(time(NULL) + 30, 0);
#else
	t = timeval_set(krb5_event_refresh_time(entry->refresh_time), 0);
#endif

	if (entry->refresh_time == 0) {
		entry->refresh_time = t.tv_sec;
	}
	entry->event = tevent_add_timer(winbind_event_context(),
				       entry,
				       t,
				       krb5_ticket_refresh_handler,
				       entry);

	return;
#endif
}
コード例 #5
0
ファイル: winbindd_cred_cache.c プロジェクト: AIdrifter/samba
static void krb5_ticket_refresh_handler(struct tevent_context *event_ctx,
					struct tevent_timer *te,
					struct timeval now,
					void *private_data)
{
	struct WINBINDD_CCACHE_ENTRY *entry =
		talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
#ifdef HAVE_KRB5
	int ret;
	time_t new_start;
	time_t expire_time = 0;
	struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
#endif

	DEBUG(10,("krb5_ticket_refresh_handler called\n"));
	DEBUGADD(10,("event called for: %s, %s\n",
		entry->ccname, entry->username));

	TALLOC_FREE(entry->event);

#ifdef HAVE_KRB5

	/* Kinit again if we have the user password and we can't renew the old
	 * tgt anymore 
	 * NB
	 * This happens when machine are put to sleep for a very long time. */

	if (entry->renew_until < time(NULL)) {
rekinit:
		if (cred_ptr && cred_ptr->pass) {

			set_effective_uid(entry->uid);

			ret = kerberos_kinit_password_ext(entry->principal_name,
							  cred_ptr->pass,
							  0, /* hm, can we do time correction here ? */
							  &entry->refresh_time,
							  &entry->renew_until,
							  entry->ccname,
							  False, /* no PAC required anymore */
							  True,
							  WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
							  NULL);
			gain_root_privilege();

			if (ret) {
				DEBUG(3,("krb5_ticket_refresh_handler: "
					"could not re-kinit: %s\n",
					error_message(ret)));
				/* destroy the ticket because we cannot rekinit
				 * it, ignore error here */
				ads_kdestroy(entry->ccname);

				/* Don't break the ticket refresh chain: retry 
				 * refreshing ticket sometime later when KDC is 
				 * unreachable -- BoYang. More error code handling
				 * here? 
				 * */

				if ((ret == KRB5_KDC_UNREACH)
				    || (ret == KRB5_REALM_CANT_RESOLVE)) {
#if defined(DEBUG_KRB5_TKT_RENEWAL)
					new_start = time(NULL) + 30;
#else
					new_start = time(NULL) +
						    MAX(30, lp_winbind_cache_time());
#endif
					add_krb5_ticket_gain_handler_event(entry,
							timeval_set(new_start, 0));
					return;
				}
				TALLOC_FREE(entry->event);
				return;
			}

			DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit "
				"for: %s in ccache: %s\n",
				entry->principal_name, entry->ccname));

#if defined(DEBUG_KRB5_TKT_RENEWAL)
			new_start = time(NULL) + 30;
#else
			/* The tkt should be refreshed at one-half the period
			   from now to the expiration time */
			expire_time = entry->refresh_time;
			new_start = krb5_event_refresh_time(entry->refresh_time);
#endif
			goto done;
		} else {
				/* can this happen? 
				 * No cached credentials
				 * destroy ticket and refresh chain 
				 * */
				ads_kdestroy(entry->ccname);
				TALLOC_FREE(entry->event);
				return;
		}
	}

	set_effective_uid(entry->uid);

	ret = smb_krb5_renew_ticket(entry->ccname,
				    entry->principal_name,
				    entry->service,
				    &new_start);
#if defined(DEBUG_KRB5_TKT_RENEWAL)
	new_start = time(NULL) + 30;
#else
	expire_time = new_start;
	new_start = krb5_event_refresh_time(new_start);
#endif

	gain_root_privilege();

	if (ret) {
		DEBUG(3,("krb5_ticket_refresh_handler: "
			"could not renew tickets: %s\n",
			error_message(ret)));
		/* maybe we are beyond the renewing window */

		/* evil rises here, we refresh ticket failed,
		 * but the ticket might be expired. Therefore,
		 * When we refresh ticket failed, destory the 
		 * ticket */

		ads_kdestroy(entry->ccname);

		/* avoid breaking the renewal chain: retry in
		 * lp_winbind_cache_time() seconds when the KDC was not
		 * available right now. 
		 * the return code can be KRB5_REALM_CANT_RESOLVE. 
		 * More error code handling here? */

		if ((ret == KRB5_KDC_UNREACH) 
		    || (ret == KRB5_REALM_CANT_RESOLVE)) {
#if defined(DEBUG_KRB5_TKT_RENEWAL)
			new_start = time(NULL) + 30;
#else
			new_start = time(NULL) +
				    MAX(30, lp_winbind_cache_time());
#endif
			/* ticket is destroyed here, we have to regain it
			 * if it is possible */
			add_krb5_ticket_gain_handler_event(entry,
						timeval_set(new_start, 0));
			return;
		}

		/* This is evil, if the ticket was already expired.
		 * renew ticket function returns KRB5KRB_AP_ERR_TKT_EXPIRED.
		 * But there is still a chance that we can rekinit it. 
		 *
		 * This happens when user login in online mode, and then network
		 * down or something cause winbind goes offline for a very long time,
		 * and then goes online again. ticket expired, renew failed.
		 * This happens when machine are put to sleep for a long time,
		 * but shorter than entry->renew_util.
		 * NB
		 * Looks like the KDC is reachable, we want to rekinit as soon as
		 * possible instead of waiting some time later. */
		if ((ret == KRB5KRB_AP_ERR_TKT_EXPIRED)
		    || (ret == KRB5_FCC_NOFILE)) goto rekinit;

		return;
	}

done:
	/* in cases that ticket will be unrenewable soon, we don't try to renew ticket 
	 * but try to regain ticket if it is possible */
	if (entry->renew_until && expire_time
	     && (entry->renew_until <= expire_time)) {
		/* try to regain ticket 10 seconds before expiration */
		expire_time -= 10;
		add_krb5_ticket_gain_handler_event(entry,
					timeval_set(expire_time, 0));
		return;
	}

	if (entry->refresh_time == 0) {
		entry->refresh_time = new_start;
	}
	entry->event = tevent_add_timer(winbind_event_context(), entry,
				       timeval_set(new_start, 0),
				       krb5_ticket_refresh_handler,
				       entry);

#endif
}