📄 util.c
字号:
return ret; } if (res->count != 1) { *errstring = talloc_asprintf(mem_ctx, "Invalid dn (%s), not child of a domain object", ldb_dn_get_linearized(dn)); talloc_free(local_ctx); return LDB_ERR_CONSTRAINT_VIOLATION; } *parent_dn = talloc_steal(mem_ctx, res->msgs[0]->dn); talloc_free(local_ctx); return ret;}/* check that a password is sufficiently complex*/static bool samdb_password_complexity_ok(const char *pass){ return check_password_quality(pass);}/* set the user password using plaintext, obeying any user or domain password restrictions note that this function doesn't actually store the result in the database, it just fills in the "mod" structure with ldb modify elements to setup the correct change when samdb_replace() is called. This allows the caller to combine the change with other changes (as is needed by some of the set user info levels) The caller should probably have a transaction wrapping this*/NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, struct ldb_dn *user_dn, struct ldb_dn *domain_dn, struct ldb_message *mod, const char *new_pass, struct samr_Password *lmNewHash, struct samr_Password *ntNewHash, bool user_change, enum samr_RejectReason *reject_reason, struct samr_DomInfo1 **_dominfo){ const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory", "ntPwdHistory", "dBCSPwd", "unicodePwd", "objectSid", "pwdLastSet", NULL }; const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength", "maxPwdAge", "minPwdAge", "minPwdLength", NULL }; NTTIME pwdLastSet; int64_t minPwdAge; uint_t minPwdLength, pwdProperties, pwdHistoryLength; uint_t userAccountControl; struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash; struct samr_Password local_lmNewHash, local_ntNewHash; int sambaLMPwdHistory_len, sambaNTPwdHistory_len; struct dom_sid *domain_sid; struct ldb_message **res; bool restrictions; int count; time_t now = time(NULL); NTTIME now_nt; int i; /* we need to know the time to compute password age */ unix_to_nt_time(&now_nt, now); /* pull all the user parameters */ count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs); if (count != 1) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0); sambaLMPwdHistory_len = samdb_result_hashes(mem_ctx, res[0], "lmPwdHistory", &sambaLMPwdHistory); sambaNTPwdHistory_len = samdb_result_hashes(mem_ctx, res[0], "ntPwdHistory", &sambaNTPwdHistory); lmPwdHash = samdb_result_hash(mem_ctx, res[0], "dBCSPwd"); ntPwdHash = samdb_result_hash(mem_ctx, res[0], "unicodePwd"); pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0); /* Only non-trust accounts have restrictions (possibly this * test is the wrong way around, but I like to be restrictive * if possible */ restrictions = !(userAccountControl & (UF_INTERDOMAIN_TRUST_ACCOUNT |UF_WORKSTATION_TRUST_ACCOUNT |UF_SERVER_TRUST_ACCOUNT)); if (domain_dn) { /* pull the domain parameters */ count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs); if (count != 1) { DEBUG(2, ("samdb_set_password: Domain DN %s is invalid, for user %s\n", ldb_dn_get_linearized(domain_dn), ldb_dn_get_linearized(user_dn))); return NT_STATUS_NO_SUCH_DOMAIN; } } else { /* work out the domain sid, and pull the domain from there */ domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid"); if (domain_sid == NULL) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs, "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, domain_sid)); if (count != 1) { DEBUG(2, ("samdb_set_password: Could not find domain to match SID: %s, for user %s\n", dom_sid_string(mem_ctx, domain_sid), ldb_dn_get_linearized(user_dn))); return NT_STATUS_NO_SUCH_DOMAIN; } } pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0); pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0); minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0); minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0); if (_dominfo) { struct samr_DomInfo1 *dominfo; /* on failure we need to fill in the reject reasons */ dominfo = talloc(mem_ctx, struct samr_DomInfo1); if (dominfo == NULL) { return NT_STATUS_NO_MEMORY; } dominfo->min_password_length = minPwdLength; dominfo->password_properties = pwdProperties; dominfo->password_history_length = pwdHistoryLength; dominfo->max_password_age = minPwdAge; dominfo->min_password_age = minPwdAge; *_dominfo = dominfo; } if (restrictions && new_pass) { /* check the various password restrictions */ if (restrictions && minPwdLength > strlen_m(new_pass)) { if (reject_reason) { *reject_reason = SAMR_REJECT_TOO_SHORT; } return NT_STATUS_PASSWORD_RESTRICTION; } /* possibly check password complexity */ if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX && !samdb_password_complexity_ok(new_pass)) { if (reject_reason) { *reject_reason = SAMR_REJECT_COMPLEXITY; } return NT_STATUS_PASSWORD_RESTRICTION; } /* compute the new nt and lm hashes */ if (E_deshash(new_pass, local_lmNewHash.hash)) { lmNewHash = &local_lmNewHash; } if (!E_md4hash(new_pass, local_ntNewHash.hash)) { /* If we can't convert this password to UCS2, then we should not accept it */ if (reject_reason) { *reject_reason = SAMR_REJECT_OTHER; } return NT_STATUS_PASSWORD_RESTRICTION; } ntNewHash = &local_ntNewHash; } if (user_change) { /* are all password changes disallowed? */ if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) { if (reject_reason) { *reject_reason = SAMR_REJECT_OTHER; } return NT_STATUS_PASSWORD_RESTRICTION; } /* can this user change password? */ if (userAccountControl & UF_PASSWD_CANT_CHANGE) { if (reject_reason) { *reject_reason = SAMR_REJECT_OTHER; } return NT_STATUS_PASSWORD_RESTRICTION; } /* yes, this is a minus. The ages are in negative 100nsec units! */ if (pwdLastSet - minPwdAge > now_nt) { if (reject_reason) { *reject_reason = SAMR_REJECT_OTHER; } return NT_STATUS_PASSWORD_RESTRICTION; } /* check the immediately past password */ if (pwdHistoryLength > 0) { if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) { if (reject_reason) { *reject_reason = SAMR_REJECT_IN_HISTORY; } return NT_STATUS_PASSWORD_RESTRICTION; } if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) { if (reject_reason) { *reject_reason = SAMR_REJECT_IN_HISTORY; } return NT_STATUS_PASSWORD_RESTRICTION; } } /* check the password history */ sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength); sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength); for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) { if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) { if (reject_reason) { *reject_reason = SAMR_REJECT_IN_HISTORY; } return NT_STATUS_PASSWORD_RESTRICTION; } } for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) { if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) { if (reject_reason) { *reject_reason = SAMR_REJECT_IN_HISTORY; } return NT_STATUS_PASSWORD_RESTRICTION; } } }#define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0) /* the password is acceptable. Start forming the new fields */ if (new_pass) { /* if we know the cleartext, then only set it. * Modules in ldb will set all the appropriate * hashes */ CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod, "sambaPassword", new_pass)); } else { /* We don't have the cleartext, so delete the old one * and set what we have of the hashes */ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "sambaPassword")); if (lmNewHash) { CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "dBCSPwd", lmNewHash)); } else { CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "dBCSPwd")); } if (ntNewHash) { CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "unicodePwd", ntNewHash)); } else { CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd")); } } return NT_STATUS_OK;}/* set the user password using plaintext, obeying any user or domain password restrictions This wrapper function takes a SID as input, rather than a user DN, and actually performs the password change*/NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid, const char *new_pass, struct samr_Password *lmNewHash, struct samr_Password *ntNewHash, bool user_change, enum samr_RejectReason *reject_reason, struct samr_DomInfo1 **_dominfo) { NTSTATUS nt_status; struct ldb_dn *user_dn; struct ldb_message *msg; int ret; ret = ldb_transaction_start(ctx); if (ret) { DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx))); return NT_STATUS_TRANSACTION_ABORTED; } user_dn = samdb_search_dn(ctx, mem_ctx, NULL, "(&(objectSid=%s)(objectClass=user))", ldap_encode_ndr_dom_sid(mem_ctx, user_sid)); if (!user_dn) { ldb_transaction_cancel(ctx); DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n", dom_sid_string(mem_ctx, user_sid))); return NT_STATUS_NO_SUCH_USER; } msg = ldb_msg_new(mem_ctx); if (msg == NULL) { ldb_transaction_cancel(ctx); return NT_STATUS_NO_MEMORY; } msg->dn = ldb_dn_copy(msg, user_dn); if (!msg->dn) { ldb_transaction_cancel(ctx); return NT_STATUS_NO_MEMORY; } nt_status = samdb_set_password(ctx, mem_ctx, user_dn, NULL, msg, new_pass, lmNewHash, ntNewHash, user_change, /* This is a password set, not change */ reject_reason, _dominfo); if (!NT_STATUS_IS_OK(nt_status)) { ldb_transaction_cancel(ctx); return nt_status; } /* modify the samdb record */ ret = samdb_replace(ctx, mem_ctx, msg); if (ret != 0) { ldb_transaction_cancel(ctx); return NT_STATUS_ACCESS_DENIED; } ret = ldb_transaction_commit(ctx); if (ret != 0) { DEBUG(0,("Failed to commit transaction to change password on %s: %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(ctx))); return NT_STATUS_TRANSACTION_ABORTED; } return NT_STATUS_OK;}NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct dom_sid *sid, struct ldb_dn **ret_dn) { struct ldb_message *msg; struct ldb_dn *basedn; const char *sidstr; int ret; sidstr = dom_sid_string(mem_ctx, sid); NT_STATUS_HAVE_NO_MEMORY(sidstr); /* We might have to create a ForeignSecurityPrincipal, even if this user * is in our own domain */ msg = ldb_msg_new(mem_ctx); if (msg == NULL) { return NT_STATUS_NO_MEMORY; } /* TODO: Hmmm. This feels wrong. How do I find the base dn to * put the ForeignSecurityPrincipals? d_state->domain_dn does * not work, this is wrong for the Builtin domain, there's no * cn=For...,cn=Builtin,dc={BASEDN}. -- vl */ basedn = samdb_search_dn(sam_ctx, mem_ctx, NULL, "(&(objectClass=container)(cn=ForeignSecurityPrincipals))"); if (basedn == NULL) { DEBUG(0, ("Failed to find DN for " "ForeignSecurityPrincipal container\n")); return NT_STATUS_INTERNAL_DB_CORRUPTION; } /* add core elements to the ldb_message for the alias */ msg->dn = ldb_dn_copy(mem_ctx, basedn); if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr)) return NT_STATUS_NO_MEMORY; samdb_msg_add_string(sam_ctx, mem_ctx, msg, "objectClass", "foreignSecurityPrincipal"); /* create the alias */ ret = ldb_add(sam_ctx, msg); if (ret != 0) { DEBUG(0,("Failed to create foreignSecurityPrincipal " "record %s: %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(sam_ctx))); return NT_STATUS_INTERNAL_DB_CORRUPTION; } *ret_dn = msg->dn; return NT_STATUS_OK;}/* Find the DN of a domain, assuming it to be a dotted.dns name*/struct ldb_dn *samdb_dns_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *dns_domain) { int i; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); const char *binary_encoded; const char **split_realm; struct ldb_dn *dn; if (!tmp_ctx) { return NULL; } split_realm = str_list_make(tmp_ctx, dns_domain, "."); if (!split_realm) { talloc_free(tmp_ctx); return NULL; } dn = ldb_dn_new(mem_ctx, ldb, NULL); for (i=0; split_realm[i]; i++) { binary_encoded = ldb_binary_encode_string(tmp_ctx, split_realm[i]); if (!ldb_dn_add_base_fmt(dn, "dc=%s", binary_encoded)) { DEBUG(2, ("Failed to add dc=%s element to DN %s\n", binary_encoded, ldb_dn_get_linearized(dn))); talloc_free(tmp_ctx); return NULL; } } if (!ldb_dn_validate(dn)) { DEBUG(2, ("Failed to validated DN %s\n", ldb_dn_get_linearized(dn))); return NULL; } return dn;}/* Find the DN of a domain, be it the netbios or DNS name */struct ldb_dn *samdb_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *domain_name) { const char * const domain_ref_attrs[] = { "ncName", NULL }; const char * const domain_ref2_attrs[] = { NULL }; struct ldb_result *res_domain_ref; char *escaped_domain = ldb_binary_encode_string(mem_ctx, domain_name); /* find the domain's DN */ int ret_domain = ldb_search_exp_fmt(ldb, mem_ctx, &res_domain_ref, samdb_partitions_dn(ldb, mem_ctx), LDB_SCOPE_ONELEVEL, domain_ref_attrs, "(&(nETBIOSName=%s)(objectclass=crossRef))", escaped_domain); if (ret_domain != 0) { return NULL; } if (res_domain_ref->count == 0) { ret_domain = ldb_search_exp_fmt(ldb, mem_ctx, &res_domain_ref, samdb_dns_domain_to_dn(ldb, mem_ctx, domain_name), LDB_SCOPE_BASE, domain_ref2_attrs, "(objectclass=domain)"); if (ret_domain != 0) { return NULL; } if (res_domain_ref->count == 1) { return res_domain_ref->msgs[0]->dn; } return NULL; } if (res_domain_ref->count > 1) { DEBUG(0,("Found %d records matching domain [%s]\n", ret_domain, domain_name)); return NULL; } return samdb_result_dn(ldb, mem_ctx, res_domain_ref->msgs[0], "nCName", NULL);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -