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