static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state) { if (!gensec_krb5_state->smb_krb5_context) { /* We can't clean anything else up unless we started up this far */ return 0; } if (gensec_krb5_state->enc_ticket.length) { kerberos_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->enc_ticket); } if (gensec_krb5_state->ticket) { krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context, gensec_krb5_state->ticket); } /* ccache freed in a child destructor */ krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context, gensec_krb5_state->keyblock); if (gensec_krb5_state->auth_context) { krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context, gensec_krb5_state->auth_context); } return 0; }
static BOOL make_krb5_skew_error(DATA_BLOB *pblob_out) { krb5_context context = NULL; krb5_error_code kerr = 0; krb5_data reply; krb5_principal host_princ = NULL; char *host_princ_s = NULL; BOOL ret = False; *pblob_out = data_blob(NULL,0); initialize_krb5_error_table(); kerr = krb5_init_context(&context); if (kerr) { return False; } /* Create server principal. */ asprintf(&host_princ_s, "%s$@%s", global_myname(), lp_realm()); if (!host_princ_s) { goto out; } strlower_m(host_princ_s); kerr = smb_krb5_parse_name(context, host_princ_s, &host_princ); if (kerr) { DEBUG(10,("make_krb5_skew_error: smb_krb5_parse_name failed for name %s: Error %s\n", host_princ_s, error_message(kerr) )); goto out; } kerr = smb_krb5_mk_error(context, KRB5KRB_AP_ERR_SKEW, host_princ, &reply); if (kerr) { DEBUG(10,("make_krb5_skew_error: smb_krb5_mk_error failed: Error %s\n", error_message(kerr) )); goto out; } *pblob_out = data_blob(reply.data, reply.length); kerberos_free_data_contents(context,&reply); ret = True; out: if (host_princ_s) { SAFE_FREE(host_princ_s); } if (host_princ) { krb5_free_principal(context, host_princ); } krb5_free_context(context); return ret; }
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; }
ADS_STATUS ads_krb5_set_password(const char *kdc_host, const char *principal, const char *newpw, int time_offset) { ADS_STATUS aret; krb5_error_code ret = 0; krb5_context context = NULL; krb5_principal princ = NULL; krb5_ccache ccache = NULL; int result_code; krb5_data result_code_string = { 0 }; krb5_data result_string = { 0 }; initialize_krb5_error_table(); ret = krb5_init_context(&context); if (ret) { DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } if (principal) { ret = smb_krb5_parse_name(context, principal, &princ); if (ret) { krb5_free_context(context); DEBUG(1, ("Failed to parse %s (%s)\n", principal, error_message(ret))); return ADS_ERROR_KRB5(ret); } } if (time_offset != 0) { krb5_set_real_time(context, time(NULL) + time_offset, 0); } ret = krb5_cc_default(context, &ccache); if (ret) { krb5_free_principal(context, princ); krb5_free_context(context); DEBUG(1,("Failed to get default creds (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } ret = krb5_set_password_using_ccache(context, ccache, discard_const_p(char, newpw), princ, &result_code, &result_code_string, &result_string); if (ret) { DEBUG(1, ("krb5_set_password failed (%s)\n", error_message(ret))); aret = ADS_ERROR_KRB5(ret); goto done; } if (result_code != KRB5_KPASSWD_SUCCESS) { ret = kpasswd_err_to_krb5_err(result_code); DEBUG(1, ("krb5_set_password failed (%s)\n", error_message(ret))); aret = ADS_ERROR_KRB5(ret); goto done; } aret = ADS_SUCCESS; done: kerberos_free_data_contents(context, &result_code_string); kerberos_free_data_contents(context, &result_string); krb5_free_principal(context, princ); krb5_cc_close(context, ccache); krb5_free_context(context); return aret; }
static ADS_STATUS ads_krb5_chg_password(const char *kdc_host, const char *principal, const char *oldpw, const char *newpw, int time_offset) { ADS_STATUS aret; krb5_error_code ret; krb5_context context = NULL; krb5_principal princ; krb5_get_init_creds_opt opts; krb5_creds creds; char *chpw_princ = NULL, *password; char *realm = NULL; int result_code; krb5_data result_code_string = { 0 }; krb5_data result_string = { 0 }; smb_krb5_addresses *addr = NULL; initialize_krb5_error_table(); ret = krb5_init_context(&context); if (ret) { DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } if ((ret = smb_krb5_parse_name(context, principal, &princ))) { krb5_free_context(context); DEBUG(1,("Failed to parse %s (%s)\n", principal, error_message(ret))); return ADS_ERROR_KRB5(ret); } krb5_get_init_creds_opt_init(&opts); krb5_get_init_creds_opt_set_tkt_life(&opts, 5*60); krb5_get_init_creds_opt_set_renew_life(&opts, 0); krb5_get_init_creds_opt_set_forwardable(&opts, 0); krb5_get_init_creds_opt_set_proxiable(&opts, 0); /* note that heimdal will fill in the local addresses if the addresses * in the creds_init_opt are all empty and then later fail with invalid * address, sending our local netbios krb5 address - just like windows * - avoids this - gd */ ret = smb_krb5_gen_netbios_krb5_address(&addr, lp_netbios_name()); if (ret) { krb5_free_principal(context, princ); krb5_free_context(context); return ADS_ERROR_KRB5(ret); } krb5_get_init_creds_opt_set_address_list(&opts, addr->addrs); realm = smb_krb5_principal_get_realm(context, princ); /* We have to obtain an INITIAL changepw ticket for changing password */ if (asprintf(&chpw_princ, "kadmin/changepw@%s", realm) == -1) { krb5_free_context(context); free(realm); DEBUG(1,("ads_krb5_chg_password: asprintf fail\n")); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } free(realm); password = SMB_STRDUP(oldpw); ret = krb5_get_init_creds_password(context, &creds, princ, password, kerb_prompter, NULL, 0, chpw_princ, &opts); SAFE_FREE(chpw_princ); SAFE_FREE(password); if (ret) { if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) DEBUG(1,("Password incorrect while getting initial ticket")); else DEBUG(1,("krb5_get_init_creds_password failed (%s)\n", error_message(ret))); krb5_free_principal(context, princ); krb5_free_context(context); return ADS_ERROR_KRB5(ret); } ret = krb5_change_password(context, &creds, discard_const_p(char, newpw), &result_code, &result_code_string, &result_string); if (ret) { DEBUG(1, ("krb5_change_password failed (%s)\n", error_message(ret))); aret = ADS_ERROR_KRB5(ret); goto done; } if (result_code != KRB5_KPASSWD_SUCCESS) { ret = kpasswd_err_to_krb5_err(result_code); DEBUG(1, ("krb5_change_password failed (%s)\n", error_message(ret))); aret = ADS_ERROR_KRB5(ret); goto done; } aret = ADS_SUCCESS; done: kerberos_free_data_contents(context, &result_code_string); kerberos_free_data_contents(context, &result_string); krb5_free_principal(context, princ); krb5_free_context(context); return aret; }
static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, struct auth_session_info **_session_info) { NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context; struct auth_session_info *session_info = NULL; krb5_principal client_principal; char *principal_string; DATA_BLOB pac_blob, *pac_blob_ptr = NULL; krb5_data pac_data; krb5_error_code ret; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); if (!tmp_ctx) { return NT_STATUS_NO_MEMORY; } ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal); if (ret) { DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n", smb_get_krb5_error_message(context, ret, tmp_ctx))); talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context, client_principal, &principal_string); if (ret) { DEBUG(1, ("Unable to parse client principal: %s\n", smb_get_krb5_error_message(context, ret, tmp_ctx))); krb5_free_principal(context, client_principal); talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket, KRB5_AUTHDATA_WIN2K_PAC, &pac_data); if (ret) { /* NO pac */ DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n", smb_get_krb5_error_message(context, ret, tmp_ctx))); } else { /* Found pac */ pac_blob = data_blob_talloc(tmp_ctx, pac_data.data, pac_data.length); kerberos_free_data_contents(context, &pac_data); if (!pac_blob.data) { free(principal_string); krb5_free_principal(context, client_principal); talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } /* decode and verify the pac */ nt_status = kerberos_decode_pac(gensec_krb5_state, pac_blob, gensec_krb5_state->smb_krb5_context->krb5_context, NULL, gensec_krb5_state->keyblock, client_principal, gensec_krb5_state->ticket->ticket.authtime, NULL); if (!NT_STATUS_IS_OK(nt_status)) { free(principal_string); krb5_free_principal(context, client_principal); talloc_free(tmp_ctx); return nt_status; } pac_blob_ptr = &pac_blob; } nt_status = gensec_generate_session_info_pac(tmp_ctx, gensec_security, gensec_krb5_state->smb_krb5_context, pac_blob_ptr, principal_string, gensec_get_remote_address(gensec_security), &session_info); free(principal_string); krb5_free_principal(context, client_principal); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return nt_status; } nt_status = gensec_krb5_session_key(gensec_security, session_info, &session_info->session_key); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return nt_status; } *_session_info = talloc_steal(mem_ctx, session_info); talloc_free(tmp_ctx); return NT_STATUS_OK; }
krb5_error_code kerberos_pac_to_user_info_dc(TALLOC_CTX *mem_ctx, krb5_pac pac, krb5_context context, struct auth_user_info_dc **user_info_dc, struct PAC_SIGNATURE_DATA *pac_srv_sig, struct PAC_SIGNATURE_DATA *pac_kdc_sig) { NTSTATUS nt_status; enum ndr_err_code ndr_err; krb5_error_code ret; DATA_BLOB pac_logon_info_in, pac_srv_checksum_in, pac_kdc_checksum_in; krb5_data k5pac_logon_info_in, k5pac_srv_checksum_in, k5pac_kdc_checksum_in; union PAC_INFO info; struct auth_user_info_dc *user_info_dc_out; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); if (!tmp_ctx) { return ENOMEM; } ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_LOGON_INFO, &k5pac_logon_info_in); if (ret != 0) { talloc_free(tmp_ctx); return EINVAL; } pac_logon_info_in = data_blob_const(k5pac_logon_info_in.data, k5pac_logon_info_in.length); ndr_err = ndr_pull_union_blob(&pac_logon_info_in, tmp_ctx, &info, PAC_TYPE_LOGON_INFO, (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO); kerberos_free_data_contents(context, &k5pac_logon_info_in); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err) || !info.logon_info.info) { nt_status = ndr_map_error2ntstatus(ndr_err); DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status))); talloc_free(tmp_ctx); return EINVAL; } /* Pull this right into the normal auth sysstem structures */ nt_status = make_user_info_dc_pac(mem_ctx, info.logon_info.info, &user_info_dc_out); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return EINVAL; } if (pac_srv_sig) { ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_SRV_CHECKSUM, &k5pac_srv_checksum_in); if (ret != 0) { talloc_free(tmp_ctx); return ret; } pac_srv_checksum_in = data_blob_const(k5pac_srv_checksum_in.data, k5pac_srv_checksum_in.length); ndr_err = ndr_pull_struct_blob(&pac_srv_checksum_in, pac_srv_sig, pac_srv_sig, (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA); kerberos_free_data_contents(context, &k5pac_srv_checksum_in); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { nt_status = ndr_map_error2ntstatus(ndr_err); DEBUG(0,("can't parse the KDC signature: %s\n", nt_errstr(nt_status))); return EINVAL; } } if (pac_kdc_sig) { ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_KDC_CHECKSUM, &k5pac_kdc_checksum_in); if (ret != 0) { talloc_free(tmp_ctx); return ret; } pac_kdc_checksum_in = data_blob_const(k5pac_kdc_checksum_in.data, k5pac_kdc_checksum_in.length); ndr_err = ndr_pull_struct_blob(&pac_kdc_checksum_in, pac_kdc_sig, pac_kdc_sig, (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA); kerberos_free_data_contents(context, &k5pac_kdc_checksum_in); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { nt_status = ndr_map_error2ntstatus(ndr_err); DEBUG(0,("can't parse the KDC signature: %s\n", nt_errstr(nt_status))); return EINVAL; } } *user_info_dc = user_info_dc_out; return 0; }
/* get a kerberos5 ticket for the given service */ 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, time_t *tgs_expire) { krb5_error_code retval; krb5_data packet; krb5_context context = NULL; krb5_ccache ccdef = NULL; krb5_auth_context auth_context = NULL; krb5_enctype enc_types[] = { #ifdef ENCTYPE_ARCFOUR_HMAC ENCTYPE_ARCFOUR_HMAC, #endif ENCTYPE_DES_CBC_MD5, ENCTYPE_DES_CBC_CRC, ENCTYPE_NULL}; initialize_krb5_error_table(); retval = krb5_init_context(&context); if (retval) { DEBUG(1,("cli_krb5_get_ticket: krb5_init_context failed (%s)\n", error_message(retval))); goto failed; } if (time_offset != 0) { krb5_set_real_time(context, time(NULL) + time_offset, 0); } if ((retval = krb5_cc_resolve(context, ccname ? ccname : krb5_cc_default_name(context), &ccdef))) { DEBUG(1,("cli_krb5_get_ticket: krb5_cc_default failed (%s)\n", error_message(retval))); goto failed; } if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) { DEBUG(1,("cli_krb5_get_ticket: krb5_set_default_tgs_ktypes failed (%s)\n", error_message(retval))); goto failed; } if ((retval = ads_krb5_mk_req(context, &auth_context, AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts, principal, ccdef, &packet, tgs_expire))) { goto failed; } get_krb5_smb_session_key(context, auth_context, session_key_krb5, False); *ticket = data_blob(packet.data, packet.length); kerberos_free_data_contents(context, &packet); failed: if ( context ) { if (ccdef) krb5_cc_close(context, ccdef); if (auth_context) krb5_auth_con_free(context, auth_context); krb5_free_context(context); } return retval; }