📄 password_hash.c
字号:
pp->unknown1 = 2; pp->data = pb_hexstr; /* * setup 'supplementalCredentials' value */ scb.sub.num_packages = num_packages; scb.sub.packages = packages; ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac, lp_iconv_convenience(ldb_get_opaque(io->ac->module->ldb, "loadparm")), &scb, (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { NTSTATUS status = ndr_map_error2ntstatus(ndr_err); ldb_asprintf_errstring(io->ac->module->ldb, "setup_supplemental_field: " "failed to push supplementalCredentialsBlob: %s", nt_errstr(status)); return LDB_ERR_OPERATIONS_ERROR; } return LDB_SUCCESS;}static int setup_last_set_field(struct setup_password_fields_io *io){ /* set it as now */ unix_to_nt_time(&io->g.last_set, time(NULL)); return LDB_SUCCESS;}static int setup_kvno_field(struct setup_password_fields_io *io){ /* increment by one */ io->g.kvno = io->o.kvno + 1; return LDB_SUCCESS;}static int setup_password_fields(struct setup_password_fields_io *io){ bool ok; int ret; /* * refuse the change if someone want to change the cleartext * and supply his own hashes at the same time... */ if (io->n.cleartext && (io->n.nt_hash || io->n.lm_hash)) { ldb_asprintf_errstring(io->ac->module->ldb, "setup_password_fields: " "it's only allowed to set the cleartext password or the password hashes"); return LDB_ERR_UNWILLING_TO_PERFORM; } if (io->n.cleartext && !io->n.nt_hash) { struct samr_Password *hash; hash = talloc(io->ac, struct samr_Password); if (!hash) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } /* compute the new nt hash */ ok = E_md4hash(io->n.cleartext, hash->hash); if (ok) { io->n.nt_hash = hash; } else { ldb_asprintf_errstring(io->ac->module->ldb, "setup_password_fields: " "failed to generate nthash from cleartext password"); return LDB_ERR_OPERATIONS_ERROR; } } if (io->n.cleartext && !io->n.lm_hash) { struct samr_Password *hash; hash = talloc(io->ac, struct samr_Password); if (!hash) { ldb_oom(io->ac->module->ldb); return LDB_ERR_OPERATIONS_ERROR; } /* compute the new lm hash */ ok = E_deshash(io->n.cleartext, hash->hash); if (ok) { io->n.lm_hash = hash; } else { talloc_free(hash->hash); } } ret = setup_nt_fields(io); if (ret != 0) { return ret; } ret = setup_lm_fields(io); if (ret != 0) { return ret; } ret = setup_supplemental_field(io); if (ret != 0) { return ret; } ret = setup_last_set_field(io); if (ret != 0) { return ret; } ret = setup_kvno_field(io); if (ret != 0) { return ret; } return LDB_SUCCESS;}static struct ldb_handle *ph_init_handle(struct ldb_request *req, struct ldb_module *module, enum ph_type type){ struct ph_context *ac; struct ldb_handle *h; h = talloc_zero(req, struct ldb_handle); if (h == NULL) { ldb_set_errstring(module->ldb, "Out of Memory"); return NULL; } h->module = module; ac = talloc_zero(h, struct ph_context); if (ac == NULL) { ldb_set_errstring(module->ldb, "Out of Memory"); talloc_free(h); return NULL; } h->private_data = (void *)ac; h->state = LDB_ASYNC_INIT; h->status = LDB_SUCCESS; ac->type = type; ac->module = module; ac->orig_req = req; return h;}static int get_domain_data_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares){ struct ph_context *ac; ac = talloc_get_type(context, struct ph_context); /* we are interested only in the single reply (base search) we receive here */ if (ares->type == LDB_REPLY_ENTRY) { if (ac->dom_res != NULL) { ldb_set_errstring(ldb, "Too many results"); talloc_free(ares); return LDB_ERR_OPERATIONS_ERROR; } ac->dom_res = talloc_steal(ac, ares); } else { talloc_free(ares); } return LDB_SUCCESS;}static int build_domain_data_request(struct ph_context *ac){ /* attrs[] is returned from this function in ac->dom_req->op.search.attrs, so it must be static, as otherwise the compiler can put it on the stack */ static const char * const attrs[] = { "pwdProperties", "pwdHistoryLength", NULL }; char *filter; ac->dom_req = talloc_zero(ac, struct ldb_request); if (ac->dom_req == NULL) { ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n"); return LDB_ERR_OPERATIONS_ERROR; } ac->dom_req->operation = LDB_SEARCH; ac->dom_req->op.search.base = ldb_get_default_basedn(ac->module->ldb); ac->dom_req->op.search.scope = LDB_SCOPE_SUBTREE; filter = talloc_asprintf(ac->dom_req, "(&(objectSid=%s)(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain)))", ldap_encode_ndr_dom_sid(ac->dom_req, ac->domain_sid)); if (filter == NULL) { ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n"); talloc_free(ac->dom_req); return LDB_ERR_OPERATIONS_ERROR; } ac->dom_req->op.search.tree = ldb_parse_tree(ac->dom_req, filter); if (ac->dom_req->op.search.tree == NULL) { ldb_set_errstring(ac->module->ldb, "Invalid search filter"); talloc_free(ac->dom_req); return LDB_ERR_OPERATIONS_ERROR; } ac->dom_req->op.search.attrs = attrs; ac->dom_req->controls = NULL; ac->dom_req->context = ac; ac->dom_req->callback = get_domain_data_callback; ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->dom_req); return LDB_SUCCESS;}static struct domain_data *get_domain_data(struct ldb_module *module, void *ctx, struct ldb_reply *res){ struct domain_data *data; const char *tmp; struct ph_context *ac; char *p; ac = talloc_get_type(ctx, struct ph_context); data = talloc_zero(ac, struct domain_data); if (data == NULL) { return NULL; } if (res == NULL) { ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Could not find this user's domain: %s!\n", dom_sid_string(data, ac->domain_sid)); talloc_free(data); return NULL; } data->pwdProperties= samdb_result_uint(res->message, "pwdProperties", 0); data->store_cleartext = data->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT; data->pwdHistoryLength = samdb_result_uint(res->message, "pwdHistoryLength", 0); /* For a domain DN, this puts things in dotted notation */ /* For builtin domains, this will give details for the host, * but that doesn't really matter, as it's just used for salt * and kerberos principals, which don't exist here */ tmp = ldb_dn_canonical_string(ctx, res->message->dn); if (!tmp) { return NULL; } /* But it puts a trailing (or just before 'builtin') / on things, so kill that */ p = strchr(tmp, '/'); if (p) { p[0] = '\0'; } if (tmp != NULL) { data->dns_domain = strlower_talloc(data, tmp); if (data->dns_domain == NULL) { ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n"); return NULL; } data->realm = strupper_talloc(data, tmp); if (data->realm == NULL) { ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n"); return NULL; } p = strchr(tmp, '.'); if (p) { p[0] = '\0'; } data->netbios_domain = strupper_talloc(data, tmp); if (data->netbios_domain == NULL) { ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Out of memory!\n"); return NULL; } } return data;}static int password_hash_add(struct ldb_module *module, struct ldb_request *req){ struct ldb_handle *h; struct ph_context *ac; struct ldb_message_element *sambaAttr; struct ldb_message_element *ntAttr; struct ldb_message_element *lmAttr; int ret; ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_add\n"); if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */ return ldb_next_request(module, req); } /* If the caller is manipulating the local passwords directly, let them pass */ if (ldb_dn_compare_base(ldb_dn_new(req, module->ldb, LOCAL_BASE), req->op.add.message->dn) == 0) { return ldb_next_request(module, req); } /* nobody must touch this fields */ if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) { return LDB_ERR_UNWILLING_TO_PERFORM; } if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) { return LDB_ERR_UNWILLING_TO_PERFORM; } if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) { return LDB_ERR_UNWILLING_TO_PERFORM; } /* If no part of this ADD touches the sambaPassword, or the NT * or LM hashes, then we don't need to make any changes. */ sambaAttr = ldb_msg_find_element(req->op.mod.message, "sambaPassword"); ntAttr = ldb_msg_find_element(req->op.mod.message, "unicodePwd"); lmAttr = ldb_msg_find_element(req->op.mod.message, "dBCSPwd"); if ((!sambaAttr) && (!ntAttr) && (!lmAttr)) { return ldb_next_request(module, req); } /* if it is not an entry of type person its an error */ /* TODO: remove this when sambaPassword will be in schema */ if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) { ldb_set_errstring(module->ldb, "Cannot set a password on entry that does not have objectClass 'person'"); return LDB_ERR_OBJECT_CLASS_VIOLATION; } /* check sambaPassword is single valued here */ /* TODO: remove this when sambaPassword will be single valued in schema */ if (sambaAttr && sambaAttr->num_values > 1) { ldb_set_errstring(module->ldb, "mupltiple values for sambaPassword not allowed!\n"); return LDB_ERR_CONSTRAINT_VIOLATION; } if (ntAttr && (ntAttr->num_values > 1)) { ldb_set_errstring(module->ldb, "mupltiple values for unicodePwd not allowed!\n"); return LDB_ERR_CONSTRAINT_VIOLATION; } if (lmAttr && (lmAttr->num_values > 1)) { ldb_set_errstring(module->ldb, "mupltiple values for dBCSPwd not allowed!\n"); return LDB_ERR_CONSTRAINT_VIOLATION; } if (sambaAttr && sambaAttr->num_values == 0) { ldb_set_errstring(module->ldb, "sambaPassword must have a value!\n"); return LDB_ERR_CONSTRAINT_VIOLATION; } if (ntAttr && (ntAttr->num_values == 0)) { ldb_set_errstring(module->ldb, "unicodePwd must have a value!\n"); return LDB_ERR_CONSTRAINT_VIOLATION; } if (lmAttr && (lmAttr->num_values == 0)) { ldb_set_errstring(module->ldb, "dBCSPwd must have a value!\n"); return LDB_ERR_CONSTRAINT_VIOLATION; } h = ph_init_handle(req, module, PH_ADD); if (!h) { return LDB_ERR_OPERATIONS_ERROR; } ac = talloc_get_type(h->private_data, struct ph_context); /* get user domain data */ ac->domain_sid = samdb_result_sid_prefix(ac, req->op.add.message, "objectSid"); if (ac->domain_sid == NULL) { ldb_debug(module->ldb, LDB_DEBUG_ERROR, "can't handle entry with missing objectSid!\n"); return LDB_ERR_OPERATIONS_ERROR; } ret = build_domain_data_request(ac); if (ret != LDB_SUCCESS) { return ret; } ac->step = PH_ADD_SEARCH_DOM; req->handle = h; return ldb_next_request(module, ac->dom_req);}static int password_hash_add_do_add(struct ldb_handle *h) { struct ph_context *ac; struct domain_data *domain; struct smb_krb5_context *smb_krb5_context; struct ldb_message *msg; struct setup_password_fields_io io; int ret; ac = talloc_get_type(h->private_data, struct ph_context); domain = get_domain_data(ac->module, ac, ac->dom_res); if (domain == NULL) { return LDB_ERR_OPERATIONS_ERROR; } ac->down_req = talloc(ac, struct ldb_request); if (ac->down_req == NULL) { return LDB_ERR_OPERATIONS_ERROR; } *(ac->down_req) = *(ac->orig_req); ac->down_req->op.add.message = msg = ldb_msg_copy_shallow(ac->down_req, ac->orig_req->op.add.message); if (ac->down_req->op.add.message == NULL) { return LDB_ERR_OPERATIONS_ERROR; } /* Some operations below require kerberos contexts */ if (smb_krb5_init_context(ac->down_req, ldb_get_opaque(h->module->ldb, "EventContext"), (struct loadparm_context *)ldb_get_opaque(h->module->ldb, "loadparm"), &smb_krb5_context) != 0) { return LDB_ERR_OPERATIONS_ERROR; } ZERO_STRUCT(io); io.ac = ac; io.domain = domain; io.smb_krb5_context = smb_krb5_context; io.u.user_account_control = samdb_result_uint(msg, "userAccountControl", 0); io.u.sAMAccountName = samdb_result_string(msg, "samAccountName", NULL); io.u.user_principal_name = samdb_result_string(msg, "userPrincipalName", NULL); io.u.is_computer = ldb_msg_check_string_attribute(msg, "objectClass", "computer"); io.n.cleartext = samdb_result_string(msg, "sambaPassword", NULL); io.n.nt_hash = samdb_result_hash(io.ac, msg, "unicodePwd"); io.n.lm_hash = samdb_result_hash(io.ac, msg, "dBCSPwd"); /* remove attributes */ if (io.n.cleartext) ldb_msg_remove_attr(msg, "sambaPassword"); if (io.n.nt_hash) ldb_msg_remove_attr(msg, "unicodePwd"); if (io.n.lm_hash) ldb_msg_remove_attr(msg, "dBCSPwd"); ldb_msg_remove_attr(msg, "pwdLastSet"); io.o.kvno = samdb_result_uint(msg, "msDs-KeyVersionNumber", 1) - 1; ldb_msg_remove_attr(msg, "msDs-KeyVersionNumber"); ret = setup_password_fields(&io); if (ret != LDB_SUCCESS) { return ret; } if (io.g.nt_hash) { ret = samdb_msg_add_hash(ac->module->ldb, ac, msg, "unicodePwd", io.g.nt_hash); if (ret != LDB_SUCCESS) { return ret; } } if (io.g.lm_hash) { ret = samdb_msg_add_hash(ac->module->ldb, ac, msg, "dBCSPwd", io.g.lm_hash); if (ret != LDB_SUCCESS) { return ret; } } if (io.g.nt_history_len > 0) { ret = samdb_msg_add_hashes(ac, msg, "ntPwdHistory", io.g.nt_history, io.g.nt_history_len); if (ret != LDB_SUCCESS) { return ret; } } if (io.g.lm_history_len > 0) { ret = samdb_msg_add_hashes(ac, msg, "lmPwdHistory", io.g.lm_history, io.g.lm_history_len); if (ret != LDB_SUCCESS) { return ret; } } if (io.g.supplemental.length > 0) { ret = ldb_msg_add_value(msg, "supplementalCredentials", &io.g.supplemental, NULL); if (ret != LDB_SUCCESS) { return ret; } } ret = samdb_msg_add_uint64(ac->module->ldb, ac, msg, "pwdLastSet", io.g.last_set); if (ret != LDB_SUCCESS) { return ret; } ret = samdb_msg_add_uint(ac->module->ldb, ac, msg, "msDs-KeyVersionNumber", io.g.kvno); if (ret != LDB_SUCCESS) { return ret; } h->state = LDB_ASYNC_INIT; h->status = LDB_SUCCESS; ac->step = PH_ADD_DO_ADD; ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->down_req); /* perform the operation */ return ldb_next_request(ac->module, ac->down_req);}static int password_hash_mod_search_self(struct ldb_handle *h);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -