Example #1
0
/**
 * Check if cached connection can be reused. If the connection cannot
 * be reused the ADS_STRUCT is freed and the pointer is set to NULL.
 */
static void ads_cached_connection_reuse(ADS_STRUCT **adsp)
{

	ADS_STRUCT *ads = *adsp;

	if (ads != NULL) {
		time_t expire;
		time_t now = 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_t)expire - (uint32_t)now,
			  (uint32_t) expire, (uint32_t) now));

		if ( ads->config.realm && (expire > now)) {
			return;
		} 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);
			*adsp = NULL;
		}
	}
}
Example #2
0
NTSTATUS remove_ccache(const char *username)
{
	struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
	NTSTATUS status = NT_STATUS_OK;
#ifdef HAVE_KRB5
	krb5_error_code ret;
#endif

	if (!entry) {
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	if (entry->ref_count <= 0) {
		DEBUG(0,("remove_ccache: logic error. "
			"ref count for user %s = %d\n",
			username, entry->ref_count));
		return NT_STATUS_INTERNAL_DB_CORRUPTION;
	}

	entry->ref_count--;

	if (entry->ref_count > 0) {
		DEBUG(10,("remove_ccache: entry %s ref count now %d\n",
			username, entry->ref_count));
		return NT_STATUS_OK;
	}

	/* no references any more */

	DLIST_REMOVE(ccache_list, entry);
	TALLOC_FREE(entry->event); /* unregisters events */

#ifdef HAVE_KRB5
	ret = ads_kdestroy(entry->ccname);

	/* we ignore the error when there has been no credential cache */
	if (ret == KRB5_FCC_NOFILE) {
		ret = 0;
	} else if (ret) {
		DEBUG(0,("remove_ccache: "
			"failed to destroy user krb5 ccache %s with: %s\n",
			entry->ccname, error_message(ret)));
	} else {
		DEBUG(10,("remove_ccache: "
			"successfully destroyed krb5 ccache %s for user %s\n",
			entry->ccname, username));
	}
	status = krb5_to_nt_status(ret);
#endif

	TALLOC_FREE(entry);
 	DEBUG(10,("remove_ccache: removed ccache for user %s\n", username));

	return status;
}
Example #3
0
WERROR nt_printer_guid_retrieve(TALLOC_CTX *mem_ctx, const char *printer,
				struct GUID *pguid)
{
	ADS_STRUCT *ads = NULL;
	char *old_krb5ccname = NULL;
	char *printer_dn;
	WERROR result;
	ADS_STATUS ads_status;
	TALLOC_CTX *tmp_ctx;

	tmp_ctx = talloc_new(mem_ctx);
	if (tmp_ctx == NULL) {
		return WERR_NOMEM;
	}

	ads = ads_init(lp_realm(), lp_workgroup(), NULL);
	if (ads == NULL) {
		result = WERR_SERVER_UNAVAILABLE;
		goto out;
	}

	old_krb5ccname = getenv(KRB5_ENV_CCNAME);
	setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
	SAFE_FREE(ads->auth.password);
	ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
							    NULL, NULL);

	ads_status = ads_connect(ads);
	if (!ADS_ERR_OK(ads_status)) {
		DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_status)));
		result = WERR_ACCESS_DENIED;
		goto out;
	}

	result = nt_printer_dn_lookup(tmp_ctx, ads, printer, &printer_dn);
	if (!W_ERROR_IS_OK(result)) {
		goto out;
	}

	result = nt_printer_guid_retrieve_internal(ads, printer_dn, pguid);
out:
	TALLOC_FREE(tmp_ctx);
	ads_destroy(&ads);
	ads_kdestroy("MEMORY:prtpub_cache");
	unsetenv(KRB5_ENV_CCNAME);
	if (old_krb5ccname != NULL) {
		setenv(KRB5_ENV_CCNAME, old_krb5ccname, 0);
	}

	return result;
}
Example #4
0
/*
  return our ads connections structure for a domain. We keep the connection
  open to make things faster
*/
static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
{
	ADS_STRUCT *ads;
	ADS_STATUS status;
	fstring dc_name;
	struct sockaddr_storage dc_ss;

	DEBUG(10,("ads_cached_connection\n"));

	if (domain->private_data) {

		time_t expire;
		time_t now = time(NULL);

		/* check for a valid structure */
		ads = (ADS_STRUCT *)domain->private_data;

		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 {
			/* 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");
			domain->private_data = NULL;
		}
	}

	/* we don't want this to affect the users ccache */
	setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);

	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->auth.password);
	SAFE_FREE(ads->auth.realm);

	if ( IS_DC ) {

		if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, NULL, NULL ) ) {
			ads_destroy( &ads );
			return NULL;
		}
		ads->auth.realm = SMB_STRDUP( ads->server.realm );
		strupper_m( ads->auth.realm );
	}
	else {
		struct winbindd_domain *our_domain = domain;

		ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);

		/* always give preference to the alt_name in our
		   primary domain if possible */

		if ( !domain->primary )
			our_domain = find_our_domain();

		if ( our_domain->alt_name[0] != '\0' ) {
			ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
			strupper_m( ads->auth.realm );
		}
		else
			ads->auth.realm = SMB_STRDUP( lp_realm() );
	}

	ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME;

	/* Setup the server affinity cache.  We don't reaally care
	   about the name.  Just setup affinity and the KRB5_CONFIG
	   file. */

	get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ss );

	status = ads_connect(ads);
	if (!ADS_ERR_OK(status) || !ads->config.realm) {
		DEBUG(1,("ads_connect for domain %s failed: %s\n",
			 domain->name, ads_errstr(status)));
		ads_destroy(&ads);

		/* if we get ECONNREFUSED then it might be a NT4
                   server, fall back to MSRPC */
		if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
		    status.err.rc == ECONNREFUSED) {
			/* 'reconnect_methods' is the MS-RPC backend. */
			DEBUG(1,("Trying MSRPC methods\n"));
			domain->backend = &reconnect_methods;
		}
		return NULL;
	}

	/* set the flag that says we don't own the memory even
	   though we do so that ads_destroy() won't destroy the
	   structure we pass back by reference */

	ads->is_mine = False;

	domain->private_data = (void *)ads;
	return ads;
}
Example #5
0
WERROR check_published_printers(struct messaging_context *msg_ctx)
{
	ADS_STATUS ads_rc;
	ADS_STRUCT *ads = NULL;
	int snum;
	int n_services = lp_numservices();
	TALLOC_CTX *tmp_ctx = NULL;
	struct auth_serversupplied_info *session_info = NULL;
	struct spoolss_PrinterInfo2 *pinfo2;
	NTSTATUS status;
	WERROR result;

	tmp_ctx = talloc_new(NULL);
	if (!tmp_ctx) return WERR_NOMEM;

	ads = ads_init(lp_realm(), lp_workgroup(), NULL);
	if (!ads) {
		DEBUG(3, ("ads_init() failed\n"));
		return WERR_SERVER_UNAVAILABLE;
	}
	setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
	SAFE_FREE(ads->auth.password);
	ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
		NULL, NULL);

	/* ads_connect() will find the DC for us */
	ads_rc = ads_connect(ads);
	if (!ADS_ERR_OK(ads_rc)) {
		DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
		result = WERR_ACCESS_DENIED;
		goto done;
	}

	status = make_session_info_system(tmp_ctx, &session_info);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(0, ("check_published_printers: "
			  "Could not create system session_info\n"));
		result = WERR_ACCESS_DENIED;
		goto done;
	}

	for (snum = 0; snum < n_services; snum++) {
		if (!lp_snum_ok(snum) || !lp_print_ok(snum)) {
			continue;
		}

		result = winreg_get_printer(tmp_ctx, session_info, msg_ctx,
					    lp_servicename(snum),
					    &pinfo2);
		if (!W_ERROR_IS_OK(result)) {
			continue;
		}

		if (pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
			nt_printer_publish_ads(msg_ctx, ads, pinfo2);
		}

		TALLOC_FREE(pinfo2);
	}

	result = WERR_OK;
done:
	ads_destroy(&ads);
	ads_kdestroy("MEMORY:prtpub_cache");
	talloc_free(tmp_ctx);
	return result;
}
Example #6
0
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;
}
Example #7
0
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
}
Example #8
0
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
}
Example #9
0
WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
			  const struct auth_session_info *session_info,
			  struct messaging_context *msg_ctx,
			  struct spoolss_PrinterInfo2 *pinfo2,
			  int action)
{
	uint32_t info2_mask = SPOOLSS_PRINTER_INFO_ATTRIBUTES;
	struct spoolss_SetPrinterInfo2 *sinfo2;
	ADS_STATUS ads_rc;
	ADS_STRUCT *ads = NULL;
	WERROR win_rc;
	char *old_krb5ccname = NULL;

	sinfo2 = talloc_zero(mem_ctx, struct spoolss_SetPrinterInfo2);
	if (!sinfo2) {
		return WERR_NOMEM;
	}

	switch (action) {
	case DSPRINT_PUBLISH:
	case DSPRINT_UPDATE:
		pinfo2->attributes |= PRINTER_ATTRIBUTE_PUBLISHED;
		break;
	case DSPRINT_UNPUBLISH:
		pinfo2->attributes &= (~PRINTER_ATTRIBUTE_PUBLISHED);
		break;
	default:
		win_rc = WERR_NOT_SUPPORTED;
		goto done;
	}

	sinfo2->attributes = pinfo2->attributes;

	win_rc = winreg_update_printer_internal(mem_ctx, session_info, msg_ctx,
					pinfo2->sharename, info2_mask,
					sinfo2, NULL, NULL);
	if (!W_ERROR_IS_OK(win_rc)) {
		DEBUG(3, ("err %d saving data\n", W_ERROR_V(win_rc)));
		goto done;
	}

	TALLOC_FREE(sinfo2);

	ads = ads_init(lp_realm(), lp_workgroup(), NULL);
	if (!ads) {
		DEBUG(3, ("ads_init() failed\n"));
		win_rc = WERR_SERVER_UNAVAILABLE;
		goto done;
	}
	old_krb5ccname = getenv(KRB5_ENV_CCNAME);
	setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
	SAFE_FREE(ads->auth.password);
	ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
		NULL, NULL);

	/* ads_connect() will find the DC for us */
	ads_rc = ads_connect(ads);
	if (!ADS_ERR_OK(ads_rc)) {
		DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
		win_rc = WERR_ACCESS_DENIED;
		goto done;
	}

	switch (action) {
	case DSPRINT_PUBLISH:
	case DSPRINT_UPDATE:
		win_rc = nt_printer_publish_ads(msg_ctx, ads, pinfo2);
		break;
	case DSPRINT_UNPUBLISH:
		win_rc = nt_printer_unpublish_ads(ads, pinfo2->sharename);
		break;
	}

done:
	ads_destroy(&ads);
	ads_kdestroy("MEMORY:prtpub_cache");
	unsetenv(KRB5_ENV_CCNAME);
	if (old_krb5ccname) {
		setenv(KRB5_ENV_CCNAME, old_krb5ccname, 0);
	}
	return win_rc;
}
Example #10
0
/*
 * Given the username/password, do a kinit, store the ticket in
 * cache_name if specified, and return the PAC_LOGON_INFO (the
 * structure containing the important user information such as
 * groups).
 */
NTSTATUS kerberos_return_pac(TALLOC_CTX *mem_ctx,
			     const char *name,
			     const char *pass,
			     time_t time_offset,
			     time_t *expire_time,
			     time_t *renew_till_time,
			     const char *cache_name,
			     bool request_pac,
			     bool add_netbios_addr,
			     time_t renewable_time,
			     const char *impersonate_princ_s,
			     struct PAC_LOGON_INFO **_logon_info)
{
	krb5_error_code ret;
	NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
	DATA_BLOB tkt, tkt_wrapped, ap_rep, sesskey1;
	const char *auth_princ = NULL;
	const char *local_service = NULL;
	const char *cc = "MEMORY:kerberos_return_pac";
	struct auth_session_info *session_info;
	struct gensec_security *gensec_server_context;

	struct gensec_settings *gensec_settings;
	size_t idx = 0;
	struct auth4_context *auth_context;
	struct loadparm_context *lp_ctx;
	struct PAC_LOGON_INFO *logon_info = NULL;

	TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
	NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);

	ZERO_STRUCT(tkt);
	ZERO_STRUCT(ap_rep);
	ZERO_STRUCT(sesskey1);

	if (!name || !pass) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (cache_name) {
		cc = cache_name;
	}

	if (!strchr_m(name, '@')) {
		auth_princ = talloc_asprintf(mem_ctx, "%s@%s", name,
			lp_realm());
	} else {
		auth_princ = name;
	}
	NT_STATUS_HAVE_NO_MEMORY(auth_princ);

	local_service = talloc_asprintf(mem_ctx, "%s$@%s",
					lp_netbios_name(), lp_realm());
	NT_STATUS_HAVE_NO_MEMORY(local_service);

	ret = kerberos_kinit_password_ext(auth_princ,
					  pass,
					  time_offset,
					  expire_time,
					  renew_till_time,
					  cc,
					  request_pac,
					  add_netbios_addr,
					  renewable_time,
					  &status);
	if (ret) {
		DEBUG(1,("kinit failed for '%s' with: %s (%d)\n",
			auth_princ, error_message(ret), ret));
		/* status already set */
		goto out;
	}

	DEBUG(10,("got TGT for %s in %s\n", auth_princ, cc));
	if (expire_time) {
		DEBUGADD(10,("\tvalid until: %s (%d)\n",
			http_timestring(talloc_tos(), *expire_time),
			(int)*expire_time));
	}
	if (renew_till_time) {
		DEBUGADD(10,("\trenewable till: %s (%d)\n",
			http_timestring(talloc_tos(), *renew_till_time),
			(int)*renew_till_time));
	}

	/* we cannot continue with krb5 when UF_DONT_REQUIRE_PREAUTH is set,
	 * in that case fallback to NTLM - gd */

	if (expire_time && renew_till_time &&
	    (*expire_time == 0) && (*renew_till_time == 0)) {
		return NT_STATUS_INVALID_LOGON_TYPE;
	}

	ret = cli_krb5_get_ticket(mem_ctx,
				  local_service,
				  time_offset,
				  &tkt,
				  &sesskey1,
				  0,
				  cc,
				  NULL,
				  impersonate_princ_s);
	if (ret) {
		DEBUG(1,("failed to get ticket for %s: %s\n",
			local_service, error_message(ret)));
		if (impersonate_princ_s) {
			DEBUGADD(1,("tried S4U2SELF impersonation as: %s\n",
				impersonate_princ_s));
		}
		status = krb5_to_nt_status(ret);
		goto out;
	}

	/* wrap that up in a nice GSS-API wrapping */
	tkt_wrapped = spnego_gen_krb5_wrap(tmp_ctx, tkt, TOK_ID_KRB_AP_REQ);
	if (tkt_wrapped.data == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	auth_context = talloc_zero(tmp_ctx, struct auth4_context);
	if (auth_context == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}
	auth_context->generate_session_info_pac = kerberos_fetch_pac;

	lp_ctx = loadparm_init_s3(tmp_ctx, loadparm_s3_context());
	if (lp_ctx == NULL) {
		status = NT_STATUS_INVALID_SERVER_STATE;
		DEBUG(10, ("loadparm_init_s3 failed\n"));
		goto out;
	}

	gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
	if (lp_ctx == NULL) {
		status = NT_STATUS_NO_MEMORY;
		DEBUG(10, ("lpcfg_gensec_settings failed\n"));
		goto out;
	}

	gensec_settings->backends = talloc_zero_array(gensec_settings,
						      struct gensec_security_ops *, 2);
	if (gensec_settings->backends == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	gensec_init();

	gensec_settings->backends[idx++] = &gensec_gse_krb5_security_ops;

	status = gensec_server_start(tmp_ctx, gensec_settings,
					auth_context, &gensec_server_context);

	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, (__location__ "Failed to start server-side GENSEC to validate a Kerberos ticket: %s\n", nt_errstr(status)));
		goto out;
	}

	talloc_unlink(tmp_ctx, lp_ctx);
	talloc_unlink(tmp_ctx, gensec_settings);
	talloc_unlink(tmp_ctx, auth_context);

	status = gensec_start_mech_by_oid(gensec_server_context, GENSEC_OID_KERBEROS5);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, (__location__ "Failed to start server-side GENSEC krb5 to validate a Kerberos ticket: %s\n", nt_errstr(status)));
		goto out;
	}

	/* Do a client-server update dance */
	status = gensec_update(gensec_server_context, tmp_ctx, NULL, tkt_wrapped, &ap_rep);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, ("gensec_update() failed: %s\n", nt_errstr(status)));
		goto out;
	}

	/* Now return the PAC information to the callers.  We ingore
	 * the session_info and instead pick out the PAC via the
	 * private_data on the auth_context */
	status = gensec_session_info(gensec_server_context, tmp_ctx, &session_info);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, ("Unable to obtain PAC via gensec_session_info\n"));
		goto out;
	}

	logon_info = talloc_get_type_abort(gensec_server_context->auth_context->private_data,
					   struct PAC_LOGON_INFO);
	if (logon_info == NULL) {
		DEBUG(1,("no PAC\n"));
		status = NT_STATUS_INVALID_PARAMETER;
		goto out;
	}

	*_logon_info = talloc_move(mem_ctx, &logon_info);

out:
	talloc_free(tmp_ctx);
	if (cc != cache_name) {
		ads_kdestroy(cc);
	}

	data_blob_free(&tkt);
	data_blob_free(&ap_rep);
	data_blob_free(&sesskey1);

	return status;
}