static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, const char *name, struct drsuapi_DsNameInfo1 *info1) { krb5_error_code ret; krb5_principal principal; /* perhaps it's a principal with a realm, so return the right 'domain only' response */ char *realm; ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal); if (ret) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } realm = smb_krb5_principal_get_realm(smb_krb5_context->krb5_context, principal); info1->dns_domain_name = talloc_strdup(mem_ctx, realm); krb5_free_principal(smb_krb5_context->krb5_context, principal); free(realm); W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name); info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; return WERR_OK; }
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; const char *realm = 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); 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); DEBUG(1,("ads_krb5_chg_password: asprintf fail\n")); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } 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); } aret = do_krb5_kpasswd_request(context, kdc_host, KRB5_KPASSWD_VERS_CHANGEPW, &creds, principal, newpw); krb5_free_principal(context, princ); krb5_free_context(context); return aret; }
ADS_STATUS ads_krb5_set_password(const char *kdc_host, const char *princ, const char *newpw, int time_offset) { ADS_STATUS aret; krb5_error_code ret = 0; krb5_context context = NULL; const char *realm = NULL; unsigned int realm_len = 0; krb5_creds creds, *credsp = NULL; krb5_ccache ccache = NULL; ZERO_STRUCT(creds); 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 (time_offset != 0) { krb5_set_real_time(context, time(NULL) + time_offset, 0); } ret = krb5_cc_default(context, &ccache); if (ret) { krb5_free_context(context); DEBUG(1,("Failed to get default creds (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } ret = krb5_cc_get_principal(context, ccache, &creds.client); if (ret) { krb5_cc_close(context, ccache); krb5_free_context(context); DEBUG(1,("Failed to get principal from ccache (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } realm = smb_krb5_principal_get_realm(context, creds.client); realm_len = strlen(realm); ret = krb5_build_principal(context, &creds.server, realm_len, realm, "kadmin", "changepw", NULL); ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp); if (ret) { krb5_cc_close(context, ccache); krb5_free_principal(context, creds.client); krb5_free_principal(context, creds.server); krb5_free_context(context); DEBUG(1,("krb5_build_prinipal_ext (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp); if (ret) { krb5_cc_close(context, ccache); krb5_free_principal(context, creds.client); krb5_free_principal(context, creds.server); krb5_free_context(context); DEBUG(1,("krb5_get_credentials failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } /* we might have to call krb5_free_creds(...) from now on ... */ aret = do_krb5_kpasswd_request(context, kdc_host, KRB5_KPASSWD_VERS_SETPW, credsp, princ, newpw); krb5_free_creds(context, credsp); krb5_free_principal(context, creds.client); krb5_free_principal(context, creds.server); 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 WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, uint32_t format_flags, enum drsuapi_DsNameFormat format_offered, enum drsuapi_DsNameFormat format_desired, const char *name, struct drsuapi_DsNameInfo1 *info1) { int ldb_ret; WERROR status; const char *domain_filter = NULL; const char *result_filter = NULL; krb5_error_code ret; krb5_principal principal; char *realm; char *unparsed_name_short; const char *domain_attrs[] = { NULL }; struct ldb_result *domain_res = NULL; /* Prevent recursion */ if (!name) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal); if (ret) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } realm = smb_krb5_principal_get_realm(smb_krb5_context->krb5_context, principal); ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res, samdb_partitions_dn(sam_ctx, mem_ctx), LDB_SCOPE_ONELEVEL, domain_attrs, "(&(objectClass=crossRef)(|(dnsRoot=%s)(netbiosName=%s))(systemFlags:%s:=%u))", ldb_binary_encode_string(mem_ctx, realm), ldb_binary_encode_string(mem_ctx, realm), LDB_OID_COMPARATOR_AND, SYSTEM_FLAG_CR_NTDS_DOMAIN); free(realm); if (ldb_ret != LDB_SUCCESS) { DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s\n", ldb_errstring(sam_ctx))); info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_OK; } switch (domain_res->count) { case 1: break; case 0: krb5_free_principal(smb_krb5_context->krb5_context, principal); return dns_domain_from_principal(mem_ctx, smb_krb5_context, name, info1); default: info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE; krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_OK; } ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short); krb5_free_principal(smb_krb5_context->krb5_context, principal); if (ret) { free(unparsed_name_short); return WERR_NOMEM; } /* This may need to be extended for more userPrincipalName variations */ result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))", ldb_binary_encode_string(mem_ctx, unparsed_name_short)); domain_filter = talloc_asprintf(mem_ctx, "(distinguishedName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn)); if (!result_filter || !domain_filter) { free(unparsed_name_short); return WERR_NOMEM; } status = DsCrackNameOneFilter(sam_ctx, mem_ctx, smb_krb5_context, format_flags, format_offered, format_desired, NULL, unparsed_name_short, domain_filter, result_filter, info1, LDB_SCOPE_SUBTREE, NULL); free(unparsed_name_short); return status; }
static krb5_error_code kpasswd_set_password(struct kdc_server *kdc, TALLOC_CTX *mem_ctx, struct auth_session_info *session_info, DATA_BLOB *decoded_data, DATA_BLOB *kpasswd_reply, const char **error_string) { krb5_context context = kdc->smb_krb5_context->krb5_context; krb5_data k_dec_data; krb5_data *k_clear_data; krb5_principal target_principal; krb5_error_code code; DATA_BLOB password; char *target_realm = NULL; char *target_name = NULL; char *target_principal_string = NULL; bool is_service_principal = false; bool ok; size_t num_components; enum samPwdChangeReason reject_reason = SAM_PWD_CHANGE_NO_ERROR; struct samr_DomInfo1 *dominfo = NULL; NTSTATUS status; k_dec_data.length = decoded_data->length; k_dec_data.data = (char *)decoded_data->data; code = decode_krb5_setpw_req(&k_dec_data, &k_clear_data, &target_principal); if (code != 0) { DBG_WARNING("decode_krb5_setpw_req failed: %s\n", error_message(code)); ok = kpasswd_make_error_reply(mem_ctx, KRB5_KPASSWD_MALFORMED, "Failed to decode packet", kpasswd_reply); if (!ok) { *error_string = "Failed to create reply"; return KRB5_KPASSWD_HARDERROR; } return 0; } ok = convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(kdc->task->lp_ctx), CH_UTF8, CH_UTF16, (const char *)k_clear_data->data, k_clear_data->length, (void **)&password.data, &password.length); krb5_free_data(context, k_clear_data); if (!ok) { DBG_WARNING("String conversion failed\n"); *error_string = "String conversion failed"; return KRB5_KPASSWD_HARDERROR; } target_realm = smb_krb5_principal_get_realm(context, target_principal); code = krb5_unparse_name_flags(context, target_principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &target_name); if (code != 0) { DBG_WARNING("Failed to parse principal\n"); *error_string = "String conversion failed"; return KRB5_KPASSWD_HARDERROR; } if ((target_name != NULL && target_realm == NULL) || (target_name == NULL && target_realm != NULL)) { krb5_free_principal(context, target_principal); SAFE_FREE(target_realm); SAFE_FREE(target_name); ok = kpasswd_make_error_reply(mem_ctx, KRB5_KPASSWD_MALFORMED, "Realm and principal must be " "both present, or neither " "present", kpasswd_reply); if (!ok) { *error_string = "Failed to create reply"; return KRB5_KPASSWD_HARDERROR; } return 0; } if (target_name != NULL && target_realm != NULL) { SAFE_FREE(target_realm); SAFE_FREE(target_name); } else { krb5_free_principal(context, target_principal); SAFE_FREE(target_realm); SAFE_FREE(target_name); return kpasswd_change_password(kdc, mem_ctx, session_info, &password, kpasswd_reply, error_string); } num_components = krb5_princ_size(context, target_principal); if (num_components >= 2) { is_service_principal = true; code = krb5_unparse_name_flags(context, target_principal, KRB5_PRINCIPAL_UNPARSE_SHORT, &target_principal_string); } else { code = krb5_unparse_name(context, target_principal, &target_principal_string); } krb5_free_principal(context, target_principal); if (code != 0) { ok = kpasswd_make_error_reply(mem_ctx, KRB5_KPASSWD_MALFORMED, "Failed to parse principal", kpasswd_reply); if (!ok) { *error_string = "Failed to create reply"; return KRB5_KPASSWD_HARDERROR; } } status = kpasswd_samdb_set_password(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, session_info, is_service_principal, target_principal_string, &password, &reject_reason, &dominfo); if (!NT_STATUS_IS_OK(status)) { DBG_ERR("kpasswd_samdb_set_password failed - %s\n", nt_errstr(status)); } ok = kpasswd_make_pwchange_reply(mem_ctx, status, reject_reason, dominfo, kpasswd_reply); if (!ok) { *error_string = "Failed to create reply"; return KRB5_KPASSWD_HARDERROR; } return 0; }
/* does the ccache have a valid TGT? */ static time_t get_tgt_time(const char *ccname) { krb5_context context; krb5_ccache ccache; krb5_cc_cursor cur; krb5_creds creds; krb5_principal principal; time_t credtime = 0; char *realm = NULL; TALLOC_CTX *mem_ctx; if (krb5_init_context(&context)) { syslog(LOG_DEBUG, "%s: unable to init krb5 context", __func__); return 0; } if (krb5_cc_resolve(context, ccname, &ccache)) { syslog(LOG_DEBUG, "%s: unable to resolve krb5 cache", __func__); goto err_cache; } if (krb5_cc_set_flags(context, ccache, 0)) { syslog(LOG_DEBUG, "%s: unable to set flags", __func__); goto err_cache; } if (krb5_cc_get_principal(context, ccache, &principal)) { syslog(LOG_DEBUG, "%s: unable to get principal", __func__); goto err_princ; } if (krb5_cc_start_seq_get(context, ccache, &cur)) { syslog(LOG_DEBUG, "%s: unable to seq start", __func__); goto err_ccstart; } if ((realm = smb_krb5_principal_get_realm(context, principal)) == NULL) { syslog(LOG_DEBUG, "%s: unable to get realm", __func__); goto err_ccstart; } mem_ctx = talloc_init("cifs.upcall"); while (!credtime && !krb5_cc_next_cred(context, ccache, &cur, &creds)) { char *name; if (smb_krb5_unparse_name(mem_ctx, context, creds.server, &name)) { syslog(LOG_DEBUG, "%s: unable to unparse name", __func__); goto err_endseq; } if (krb5_realm_compare(context, creds.server, principal) && strnequal(name, KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE) && strnequal(name+KRB5_TGS_NAME_SIZE+1, realm, strlen(realm)) && creds.times.endtime > time(NULL)) credtime = creds.times.endtime; krb5_free_cred_contents(context, &creds); TALLOC_FREE(name); } err_endseq: TALLOC_FREE(mem_ctx); krb5_cc_end_seq_get(context, ccache, &cur); err_ccstart: krb5_free_principal(context, principal); err_princ: #if defined(KRB5_TC_OPENCLOSE) krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); #endif krb5_cc_close(context, ccache); err_cache: krb5_free_context(context); return credtime; }