📄 password_hash.c
字号:
/* ldb database module Copyright (C) Simo Sorce 2004-2006 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006 Copyright (C) Andrew Tridgell 2004 Copyright (C) Stefan Metzmacher 2007 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.*//* * Name: ldb * * Component: ldb password_hash module * * Description: correctly update hash values based on changes to sambaPassword and friends * * Author: Andrew Bartlett * Author: Stefan Metzmacher */#include "includes.h"#include "libcli/ldap/ldap_ndr.h"#include "ldb/include/ldb_errors.h"#include "ldb/include/ldb.h"#include "ldb/include/ldb_private.h"#include "librpc/gen_ndr/misc.h"#include "librpc/gen_ndr/samr.h"#include "libcli/auth/libcli_auth.h"#include "libcli/security/security.h"#include "system/kerberos.h"#include "auth/kerberos/kerberos.h"#include "system/time.h"#include "dsdb/samdb/samdb.h"#include "dsdb/common/flags.h"#include "dsdb/samdb/ldb_modules/password_modules.h"#include "librpc/ndr/libndr.h"#include "librpc/gen_ndr/ndr_drsblobs.h"#include "lib/crypto/crypto.h"#include "param/param.h"/* If we have decided there is reason to work on this request, then * setup all the password hash types correctly. * * If the administrator doesn't want the sambaPassword stored (set in the * domain and per-account policies) then we must strip that out before * we do the first operation. * * Once this is done (which could update anything at all), we * calculate the password hashes. * * This function must not only update the unicodePwd, dBCSPwd and * supplementalCredentials fields, it must also atomicly increment the * msDS-KeyVersionNumber. We should be in a transaction, so all this * should be quite safe... * * Finally, if the administrator has requested that a password history * be maintained, then this should also be written out. * */struct ph_context { enum ph_type {PH_ADD, PH_MOD} type; enum ph_step {PH_ADD_SEARCH_DOM, PH_ADD_DO_ADD, PH_MOD_DO_REQ, PH_MOD_SEARCH_SELF, PH_MOD_SEARCH_DOM, PH_MOD_DO_MOD} step; struct ldb_module *module; struct ldb_request *orig_req; struct ldb_request *dom_req; struct ldb_reply *dom_res; struct ldb_request *down_req; struct ldb_request *search_req; struct ldb_reply *search_res; struct ldb_request *mod_req; struct dom_sid *domain_sid;};struct domain_data { bool store_cleartext; uint_t pwdProperties; uint_t pwdHistoryLength; char *netbios_domain; char *dns_domain; char *realm;};struct setup_password_fields_io { struct ph_context *ac; struct domain_data *domain; struct smb_krb5_context *smb_krb5_context; /* infos about the user account */ struct { uint32_t user_account_control; const char *sAMAccountName; const char *user_principal_name; bool is_computer; } u; /* new credentials */ struct { const char *cleartext; struct samr_Password *nt_hash; struct samr_Password *lm_hash; } n; /* old credentials */ struct { uint32_t nt_history_len; struct samr_Password *nt_history; uint32_t lm_history_len; struct samr_Password *lm_history; const struct ldb_val *supplemental; struct supplementalCredentialsBlob scb; uint32_t kvno; } o; /* generated credentials */ struct { struct samr_Password *nt_hash; struct samr_Password *lm_hash; uint32_t nt_history_len; struct samr_Password *nt_history; uint32_t lm_history_len; struct samr_Password *lm_history; struct ldb_val supplemental; NTTIME last_set; uint32_t kvno; } g;};static int setup_nt_fields(struct setup_password_fields_io *io){ uint32_t i; io->g.nt_hash = io->n.nt_hash; if (io->domain->pwdHistoryLength == 0) { return LDB_SUCCESS; } /* We might not have an old NT password */ io->g.nt_history = talloc_array(io->ac, struct samr_Password, io->domain->pwdHistoryLength); if (!io->g.nt_history) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } for (i = 0; i < MIN(io->domain->pwdHistoryLength-1, io->o.nt_history_len); i++) { io->g.nt_history[i+1] = io->o.nt_history[i]; } io->g.nt_history_len = i + 1; if (io->g.nt_hash) { io->g.nt_history[0] = *io->g.nt_hash; } else { /* * TODO: is this correct? * the simular behavior is correct for the lm history case */ E_md4hash("", io->g.nt_history[0].hash); } return LDB_SUCCESS;}static int setup_lm_fields(struct setup_password_fields_io *io){ uint32_t i; io->g.lm_hash = io->n.lm_hash; if (io->domain->pwdHistoryLength == 0) { return LDB_SUCCESS; } /* We might not have an old NT password */ io->g.lm_history = talloc_array(io->ac, struct samr_Password, io->domain->pwdHistoryLength); if (!io->g.lm_history) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } for (i = 0; i < MIN(io->domain->pwdHistoryLength-1, io->o.lm_history_len); i++) { io->g.lm_history[i+1] = io->o.lm_history[i]; } io->g.lm_history_len = i + 1; if (io->g.lm_hash) { io->g.lm_history[0] = *io->g.lm_hash; } else { E_deshash("", io->g.lm_history[0].hash); } return LDB_SUCCESS;}static int setup_primary_kerberos(struct setup_password_fields_io *io, const struct supplementalCredentialsBlob *old_scb, struct package_PrimaryKerberosBlob *pkb){ krb5_error_code krb5_ret; Principal *salt_principal; krb5_salt salt; krb5_keyblock key; uint32_t k=0; struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3; struct supplementalCredentialsPackage *old_scp = NULL; struct package_PrimaryKerberosBlob _old_pkb; struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL; uint32_t i; enum ndr_err_code ndr_err; /* Many, many thanks to lukeh@padl.com for this * algorithm, described in his Nov 10 2004 mail to * samba-technical@samba.org */ /* * Determine a salting principal */ if (io->u.is_computer) { char *name; char *saltbody; name = talloc_strdup(io->ac, io->u.sAMAccountName); if (!name) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } if (name[strlen(name)-1] == '$') { name[strlen(name)-1] = '\0'; } saltbody = talloc_asprintf(io->ac, "%s.%s", name, io->domain->dns_domain); if (!saltbody) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context, &salt_principal, io->domain->realm, "host", saltbody, NULL); } else if (io->u.user_principal_name) { char *user_principal_name; char *p; user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name); if (!user_principal_name) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } p = strchr(user_principal_name, '@'); if (p) { p[0] = '\0'; } krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context, &salt_principal, io->domain->realm, user_principal_name, NULL); } else { krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context, &salt_principal, io->domain->realm, io->u.sAMAccountName, NULL); } if (krb5_ret) { ldb_asprintf_errstring(io->ac->module->ldb, "setup_primary_kerberos: " "generation of a salting principal failed: %s", smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac)); return LDB_ERR_OPERATIONS_ERROR; } /* * create salt from salt_principal */ krb5_ret = krb5_get_pw_salt(io->smb_krb5_context->krb5_context, salt_principal, &salt); krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal); if (krb5_ret) { ldb_asprintf_errstring(io->ac->module->ldb, "setup_primary_kerberos: " "generation of krb5_salt failed: %s", smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac)); return LDB_ERR_OPERATIONS_ERROR; } /* create a talloc copy */ pkb3->salt.string = talloc_strndup(io->ac, salt.saltvalue.data, salt.saltvalue.length); krb5_free_salt(io->smb_krb5_context->krb5_context, salt); if (!pkb3->salt.string) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } salt.saltvalue.data = discard_const(pkb3->salt.string); salt.saltvalue.length = strlen(pkb3->salt.string); /* * prepare generation of keys * * ENCTYPE_AES256_CTS_HMAC_SHA1_96 (disabled by default) * ENCTYPE_DES_CBC_MD5 * ENCTYPE_DES_CBC_CRC * * NOTE: update num_keys when you add another enctype! */ pkb3->num_keys = 3; pkb3->keys = talloc_array(io->ac, struct package_PrimaryKerberosKey, pkb3->num_keys); if (!pkb3->keys) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } pkb3->unknown3 = talloc_zero_array(io->ac, uint64_t, pkb3->num_keys); if (!pkb3->unknown3) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } if (lp_parm_bool(ldb_get_opaque(io->ac->module->ldb, "loadparm"), NULL, "password_hash", "create_aes_key", false)) { /* * TODO: * * w2k and w2k3 doesn't support AES, so we'll not include * the AES key here yet. * * Also we don't have an example supplementalCredentials blob * from Windows Longhorn Server with AES support * */ /* * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of * the salt and the cleartext password */ krb5_ret = krb5_string_to_key_salt(io->smb_krb5_context->krb5_context, ENCTYPE_AES256_CTS_HMAC_SHA1_96, io->n.cleartext, salt, &key); pkb3->keys[k].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96; pkb3->keys[k].value = talloc(pkb3->keys, DATA_BLOB); if (!pkb3->keys[k].value) { krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key); ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } *pkb3->keys[k].value = data_blob_talloc(pkb3->keys[k].value, key.keyvalue.data, key.keyvalue.length); krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key); if (!pkb3->keys[k].value->data) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } k++;} /* * create ENCTYPE_DES_CBC_MD5 key out of * the salt and the cleartext password */ krb5_ret = krb5_string_to_key_salt(io->smb_krb5_context->krb5_context, ENCTYPE_DES_CBC_MD5, io->n.cleartext, salt, &key); pkb3->keys[k].keytype = ENCTYPE_DES_CBC_MD5; pkb3->keys[k].value = talloc(pkb3->keys, DATA_BLOB); if (!pkb3->keys[k].value) { krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key); ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } *pkb3->keys[k].value = data_blob_talloc(pkb3->keys[k].value, key.keyvalue.data, key.keyvalue.length); krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key); if (!pkb3->keys[k].value->data) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } k++; /* * create ENCTYPE_DES_CBC_CRC key out of * the salt and the cleartext password */ krb5_ret = krb5_string_to_key_salt(io->smb_krb5_context->krb5_context, ENCTYPE_DES_CBC_CRC, io->n.cleartext, salt, &key); pkb3->keys[k].keytype = ENCTYPE_DES_CBC_CRC; pkb3->keys[k].value = talloc(pkb3->keys, DATA_BLOB); if (!pkb3->keys[k].value) { krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key); ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } *pkb3->keys[k].value = data_blob_talloc(pkb3->keys[k].value, key.keyvalue.data, key.keyvalue.length); krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key); if (!pkb3->keys[k].value->data) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } k++; /* fix up key number */ pkb3->num_keys = k; /* initialize the old keys to zero */ pkb3->num_old_keys = 0; pkb3->old_keys = NULL; pkb3->unknown3_old = NULL; /* if there're no old keys, then we're done */ if (!old_scb) { return LDB_SUCCESS; } for (i=0; i < old_scb->sub.num_packages; i++) { if (old_scb->sub.packages[i].unknown1 != 0x00000001) { continue; } if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) { continue; } if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) { continue; } old_scp = &old_scb->sub.packages[i]; break; } /* Primary:Kerberos element of supplementalCredentials */ if (old_scp) { DATA_BLOB blob; blob = strhex_to_data_blob(old_scp->data); if (!blob.data) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } talloc_steal(io->ac, blob.data); /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */ ndr_err = ndr_pull_struct_blob(&blob, io->ac, lp_iconv_convenience(ldb_get_opaque(io->ac->module->ldb, "loadparm")), &_old_pkb, (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { NTSTATUS status = ndr_map_error2ntstatus(ndr_err); ldb_asprintf_errstring(io->ac->module->ldb, "setup_primary_kerberos: " "failed to pull old package_PrimaryKerberosBlob: %s", nt_errstr(status)); return LDB_ERR_OPERATIONS_ERROR; } if (_old_pkb.version != 3) { ldb_asprintf_errstring(io->ac->module->ldb, "setup_primary_kerberos: " "package_PrimaryKerberosBlob version[%u] expected[3]", _old_pkb.version); return LDB_ERR_OPERATIONS_ERROR; } old_pkb3 = &_old_pkb.ctr.ctr3; } /* if we didn't found the old keys we're done */ if (!old_pkb3) { return LDB_SUCCESS; } /* fill in the old keys */ pkb3->num_old_keys = old_pkb3->num_keys; pkb3->old_keys = old_pkb3->keys; pkb3->unknown3_old = old_pkb3->unknown3; return LDB_SUCCESS;}static int setup_primary_wdigest(struct setup_password_fields_io *io, const struct supplementalCredentialsBlob *old_scb, struct package_PrimaryWDigestBlob *pdb){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -