Example #1
0
/*
  do a rough conversion between ads error codes and NT status codes
  we'll need to fill this in more
*/
NTSTATUS ads_ntstatus(ADS_STATUS status)
{
	switch (status.error_type) {
	case ENUM_ADS_ERROR_NT:
		return status.err.nt_status;	
	case ENUM_ADS_ERROR_SYSTEM:
		return map_nt_error_from_unix(status.err.rc);
#ifdef HAVE_LDAP
	case ENUM_ADS_ERROR_LDAP:
		if (status.err.rc == LDAP_SUCCESS) {
			return NT_STATUS_OK;
		}
		return NT_STATUS_LDAP(status.err.rc);
#endif
#ifdef HAVE_KRB5
	case ENUM_ADS_ERROR_KRB5:
		return krb5_to_nt_status(status.err.rc);
#endif
	default:
		break;
	}

	if (ADS_ERR_OK(status)) {
		return NT_STATUS_OK;
	}
	return NT_STATUS_UNSUCCESSFUL;
}
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
static NTSTATUS keytab_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
			      struct replUpToDateVectorBlob *new_utdv)
{
	NTSTATUS status = NT_STATUS_OK;
	krb5_error_code ret = 0;
	struct libnet_keytab_context *keytab_ctx =
		(struct libnet_keytab_context *)ctx->private_data;

	if (new_utdv) {
		enum ndr_err_code ndr_err;
		DATA_BLOB blob;

		if (DEBUGLEVEL >= 10) {
			NDR_PRINT_DEBUG(replUpToDateVectorBlob, new_utdv);
		}

		ndr_err = ndr_push_struct_blob(&blob, mem_ctx, new_utdv,
				(ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			status = ndr_map_error2ntstatus(ndr_err);
			ctx->error_message = talloc_asprintf(ctx,
					"Failed to push UpToDateVector: %s",
					nt_errstr(status));
			goto done;
		}

		status = libnet_keytab_add_to_keytab_entries(mem_ctx, keytab_ctx, 0,
							     ctx->nc_dn, "UTDV",
							     ENCTYPE_NULL,
							     blob);
		if (!NT_STATUS_IS_OK(status)) {
			goto done;
		}
	}

	ret = libnet_keytab_add(keytab_ctx);
	if (ret) {
		status = krb5_to_nt_status(ret);
		ctx->error_message = talloc_asprintf(ctx,
			"Failed to add entries to keytab %s: %s",
			keytab_ctx->keytab_name, error_message(ret));
		goto done;
	}

	ctx->result_message = talloc_asprintf(ctx,
		"Vampired %d accounts to keytab %s",
		keytab_ctx->count,
		keytab_ctx->keytab_name);

done:
	TALLOC_FREE(keytab_ctx);
	return status;
}
Example #4
0
static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
			       struct replUpToDateVectorBlob **pold_utdv)
{
	krb5_error_code ret = 0;
	struct libnet_keytab_context *keytab_ctx;
	struct libnet_keytab_entry *entry;
	struct replUpToDateVectorBlob *old_utdv = NULL;
	char *principal;

	ret = libnet_keytab_init(mem_ctx, ctx->output_filename, &keytab_ctx);
	if (ret) {
		return krb5_to_nt_status(ret);
	}

	keytab_ctx->dns_domain_name = ctx->dns_domain_name;
	keytab_ctx->clean_old_entries = ctx->clean_old_entries;
	ctx->private_data = keytab_ctx;

	principal = talloc_asprintf(mem_ctx, "UTDV/%s@%s",
				    ctx->nc_dn, ctx->dns_domain_name);
	NT_STATUS_HAVE_NO_MEMORY(principal);

	entry = libnet_keytab_search(keytab_ctx, principal, 0, ENCTYPE_NULL,
				     mem_ctx);
	if (entry) {
		enum ndr_err_code ndr_err;
		old_utdv = talloc(mem_ctx, struct replUpToDateVectorBlob);

		ndr_err = ndr_pull_struct_blob(&entry->password, old_utdv, old_utdv,
				(ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
			ctx->error_message = talloc_asprintf(ctx,
					"Failed to pull UpToDateVector: %s",
					nt_errstr(status));
			return status;
		}

		if (DEBUGLEVEL >= 10) {
			NDR_PRINT_DEBUG(replUpToDateVectorBlob, old_utdv);
		}
	}

	if (pold_utdv) {
		*pold_utdv = old_utdv;
	}

	return NT_STATUS_OK;
}
Example #5
0
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
			   const char *realm,
			   time_t time_offset,
			   const DATA_BLOB *ticket,
			   char **principal,
			   struct PAC_DATA **pac_data,
			   DATA_BLOB *ap_rep,
			   DATA_BLOB *session_key,
			   bool use_replay_cache)
{
	NTSTATUS sret = NT_STATUS_LOGON_FAILURE;
	NTSTATUS pac_ret;
	DATA_BLOB auth_data;
	krb5_context context = NULL;
	krb5_auth_context auth_context = NULL;
	krb5_data packet;
	krb5_ticket *tkt = NULL;
	krb5_rcache rcache = NULL;
	krb5_keyblock *keyblock = NULL;
	time_t authtime;
	krb5_error_code ret = 0;
	int flags = 0;	
	krb5_principal host_princ = NULL;
	krb5_const_principal client_principal = NULL;
	char *host_princ_s = NULL;
	bool auth_ok = False;
	bool got_auth_data = False;
	struct named_mutex *mutex = NULL;

	ZERO_STRUCT(packet);
	ZERO_STRUCT(auth_data);

	*principal = NULL;
	*pac_data = NULL;
	*ap_rep = data_blob_null;
	*session_key = data_blob_null;

	initialize_krb5_error_table();
	ret = krb5_init_context(&context);
	if (ret) {
		DEBUG(1,("ads_verify_ticket: krb5_init_context failed (%s)\n", error_message(ret)));
		return NT_STATUS_LOGON_FAILURE;
	}

	if (time_offset != 0) {
		krb5_set_real_time(context, time(NULL) + time_offset, 0);
	}

	ret = krb5_set_default_realm(context, realm);
	if (ret) {
		DEBUG(1,("ads_verify_ticket: krb5_set_default_realm failed (%s)\n", error_message(ret)));
		goto out;
	}

	/* This whole process is far more complex than I would
           like. We have to go through all this to allow us to store
           the secret internally, instead of using /etc/krb5.keytab */

	ret = krb5_auth_con_init(context, &auth_context);
	if (ret) {
		DEBUG(1,("ads_verify_ticket: krb5_auth_con_init failed (%s)\n", error_message(ret)));
		goto out;
	}

	krb5_auth_con_getflags( context, auth_context, &flags );
	if ( !use_replay_cache ) {
		/* Disable default use of a replay cache */
		flags &= ~KRB5_AUTH_CONTEXT_DO_TIME;
		krb5_auth_con_setflags( context, auth_context, flags );
	}

	if (asprintf(&host_princ_s, "%s$", global_myname()) == -1) {
		goto out;
	}

	strlower_m(host_princ_s);
	ret = smb_krb5_parse_name(context, host_princ_s, &host_princ);
	if (ret) {
		DEBUG(1,("ads_verify_ticket: smb_krb5_parse_name(%s) failed (%s)\n",
					host_princ_s, error_message(ret)));
		goto out;
	}


	if ( use_replay_cache ) {
		
		/* Lock a mutex surrounding the replay as there is no 
		   locking in the MIT krb5 code surrounding the replay 
		   cache... */

		mutex = grab_named_mutex(talloc_tos(), "replay cache mutex",
					 10);
		if (mutex == NULL) {
			DEBUG(1,("ads_verify_ticket: unable to protect "
				 "replay cache with mutex.\n"));
			ret = KRB5_CC_IO;
			goto out;
		}

		/* JRA. We must set the rcache here. This will prevent 
		   replay attacks. */
		
		ret = krb5_get_server_rcache(context, 
					     krb5_princ_component(context, host_princ, 0), 
					     &rcache);
		if (ret) {
			DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache "
				 "failed (%s)\n", error_message(ret)));
			goto out;
		}

		ret = krb5_auth_con_setrcache(context, auth_context, rcache);
		if (ret) {
			DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache "
				 "failed (%s)\n", error_message(ret)));
			goto out;
		}
	}

	/* Try secrets.tdb first and fallback to the krb5.keytab if
	   necessary */

	auth_ok = ads_secrets_verify_ticket(context, auth_context, host_princ,
					    ticket, &tkt, &keyblock, &ret);

	if (!auth_ok &&
	    (ret == KRB5KRB_AP_ERR_TKT_NYV ||
	     ret == KRB5KRB_AP_ERR_TKT_EXPIRED ||
	     ret == KRB5KRB_AP_ERR_SKEW)) {
		goto auth_failed;
	}

	if (!auth_ok && lp_use_kerberos_keytab()) {
		auth_ok = ads_keytab_verify_ticket(context, auth_context, 
						   ticket, &tkt, &keyblock, &ret);
	}

	if ( use_replay_cache ) {		
		TALLOC_FREE(mutex);
#if 0
		/* Heimdal leaks here, if we fix the leak, MIT crashes */
		if (rcache) {
			krb5_rc_close(context, rcache);
		}
#endif
	}	

 auth_failed:
	if (!auth_ok) {
		DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n", 
			 error_message(ret)));
		/* Try map the error return in case it's something like
		 * a clock skew error.
		 */
		sret = krb5_to_nt_status(ret);
		if (NT_STATUS_IS_OK(sret) || NT_STATUS_EQUAL(sret,NT_STATUS_UNSUCCESSFUL)) {
			sret = NT_STATUS_LOGON_FAILURE;
		}
		DEBUG(10,("ads_verify_ticket: returning error %s\n",
			nt_errstr(sret) ));
		goto out;
	} 
	
	authtime = get_authtime_from_tkt(tkt);
	client_principal = get_principal_from_tkt(tkt);

	ret = krb5_mk_rep(context, auth_context, &packet);
	if (ret) {
		DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n",
			error_message(ret)));
		goto out;
	}

	*ap_rep = data_blob(packet.data, packet.length);
	if (packet.data) {
		kerberos_free_data_contents(context, &packet);
		ZERO_STRUCT(packet);
	}

	get_krb5_smb_session_key(context, auth_context, session_key, True);
	dump_data_pw("SMB session key (from ticket)\n", session_key->data, session_key->length);

#if 0
	file_save("/tmp/ticket.dat", ticket->data, ticket->length);
#endif

	/* continue when no PAC is retrieved or we couldn't decode the PAC 
	   (like accounts that have the UF_NO_AUTH_DATA_REQUIRED flag set, or
	   Kerberos tickets encrypted using a DES key) - Guenther */

	got_auth_data = get_auth_data_from_tkt(mem_ctx, &auth_data, tkt);
	if (!got_auth_data) {
		DEBUG(3,("ads_verify_ticket: did not retrieve auth data. continuing without PAC\n"));
	}

	if (got_auth_data) {
		pac_ret = decode_pac_data(mem_ctx, &auth_data, context, keyblock, client_principal, authtime, pac_data);
		if (!NT_STATUS_IS_OK(pac_ret)) {
			DEBUG(3,("ads_verify_ticket: failed to decode PAC_DATA: %s\n", nt_errstr(pac_ret)));
			*pac_data = NULL;
		}
		data_blob_free(&auth_data);
	}

#if 0
#if defined(HAVE_KRB5_TKT_ENC_PART2)
	/* MIT */
	if (tkt->enc_part2) {
		file_save("/tmp/authdata.dat",
			  tkt->enc_part2->authorization_data[0]->contents,
			  tkt->enc_part2->authorization_data[0]->length);
	}
#else
	/* Heimdal */
	if (tkt->ticket.authorization_data) {
		file_save("/tmp/authdata.dat",
			  tkt->ticket.authorization_data->val->ad_data.data,
			  tkt->ticket.authorization_data->val->ad_data.length);
	}
#endif
#endif

	if ((ret = smb_krb5_unparse_name(context, client_principal, principal))) {
		DEBUG(3,("ads_verify_ticket: smb_krb5_unparse_name failed (%s)\n", 
			 error_message(ret)));
		sret = NT_STATUS_LOGON_FAILURE;
		goto out;
	}

	sret = NT_STATUS_OK;

 out:

	TALLOC_FREE(mutex);

	if (!NT_STATUS_IS_OK(sret)) {
		data_blob_free(&auth_data);
	}

	if (!NT_STATUS_IS_OK(sret)) {
		data_blob_free(ap_rep);
	}

	if (host_princ) {
		krb5_free_principal(context, host_princ);
	}

	if (keyblock) {
		krb5_free_keyblock(context, keyblock);
	}

	if (tkt != NULL) {
		krb5_free_ticket(context, tkt);
	}

	SAFE_FREE(host_princ_s);

	if (auth_context) {
		krb5_auth_con_free(context, auth_context);
	}

	if (context) {
		krb5_free_context(context);
	}

	return sret;
}
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
/*
 * 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;
}