s4: Use same function signature for convert_* as s3.
[garming/samba-autobuild/.git] / source4 / dsdb / common / util.c
index efc1b20ae053e2ce2a161869c9b60faae36725bd..19eb3433a92b55b9b3689b6676e78f6adde79895 100644 (file)
 */
 
 #include "includes.h"
+#include "events/events.h"
 #include "ldb.h"
 #include "ldb_errors.h"
-#include "lib/util/util_ldb.h"
+#include "../lib/util/util_ldb.h"
+#include "../lib/crypto/crypto.h"
 #include "dsdb/samdb/samdb.h"
 #include "libcli/security/security.h"
 #include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
 #include "dsdb/common/flags.h"
 #include "dsdb/common/proto.h"
 #include "libcli/ldap/ldap_ndr.h"
+#include "param/param.h"
 #include "libcli/auth/libcli_auth.h"
 
 /*
@@ -375,7 +379,7 @@ struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, const struct ldb_messa
        if (sid == NULL) {
                return NULL;
        }
-       ndr_err = ndr_pull_struct_blob(v, sid, sid,
+       ndr_err = ndr_pull_struct_blob(v, sid, NULL, sid,
                                       (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                talloc_free(sid);
@@ -401,7 +405,7 @@ struct GUID samdb_result_guid(const struct ldb_message *msg, const char *attr)
 
        mem_ctx = talloc_named_const(NULL, 0, "samdb_result_guid");
        if (!mem_ctx) return guid;
-       ndr_err = ndr_pull_struct_blob(v, mem_ctx, &guid,
+       ndr_err = ndr_pull_struct_blob(v, mem_ctx, NULL, &guid,
                                       (ndr_pull_flags_fn_t)ndr_pull_GUID);
        talloc_free(mem_ctx);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -432,6 +436,29 @@ NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, NTTIME def
        return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
 }
 
+/*
+ * Windows uses both 0 and 9223372036854775807 (0x7FFFFFFFFFFFFFFFULL) to
+ * indicate an account doesn't expire.
+ *
+ * When Windows initially creates an account, it sets
+ * accountExpires = 9223372036854775807 (0x7FFFFFFFFFFFFFFF).  However,
+ * when changing from an account having a specific expiration date to
+ * that account never expiring, it sets accountExpires = 0.
+ *
+ * Consolidate that logic here to allow clearer logic for account expiry in
+ * the rest of the code.
+ */
+NTTIME samdb_result_account_expires(struct ldb_message *msg)
+{
+       NTTIME ret = ldb_msg_find_attr_as_uint64(msg, "accountExpires",
+                                                0);
+
+       if (ret == 0)
+               ret = 0x7FFFFFFFFFFFFFFFULL;
+
+       return ret;
+}
+
 /*
   pull a uint64_t from a result set. 
 */
@@ -468,8 +495,8 @@ NTTIME samdb_result_allow_password_change(struct ldb_context *sam_ldb,
 }
 
 /*
-  construct the force_password_change field from the PwdLastSet attribute and the 
-  domain password settings
+  construct the force_password_change field from the PwdLastSet
+  attribute, the userAccountControl and the domain password settings
 */
 NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb, 
                                          TALLOC_CTX *mem_ctx, 
@@ -477,10 +504,12 @@ NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb,
                                          struct ldb_message *msg)
 {
        uint64_t attr_time = samdb_result_uint64(msg, "pwdLastSet", 0);
-       uint32_t user_flags = samdb_result_uint64(msg, "userAccountControl", 0);
+       uint32_t userAccountControl = samdb_result_uint64(msg, "userAccountControl", 0);
        int64_t maxPwdAge;
 
-       if (user_flags & UF_DONT_EXPIRE_PASSWD) {
+       /* Machine accounts don't expire, and there is a flag for 'no expiry' */
+       if (!(userAccountControl & UF_NORMAL_ACCOUNT)
+           || (userAccountControl & UF_DONT_EXPIRE_PASSWD)) {
                return 0x7FFFFFFFFFFFFFFFULL;
        }
 
@@ -490,7 +519,7 @@ NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb,
 
        maxPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "maxPwdAge", NULL);
        if (maxPwdAge == 0) {
-               return 0;
+               return 0x7FFFFFFFFFFFFFFFULL;
        } else {
                attr_time -= maxPwdAge;
        }
@@ -543,7 +572,7 @@ uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
        return count;
 }
 
-NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
+NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, struct ldb_message *msg, 
                                struct samr_Password **lm_pwd, struct samr_Password **nt_pwd) 
 {
        struct samr_Password *lmPwdHash, *ntPwdHash;
@@ -559,14 +588,21 @@ NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
                }
        }
        if (lm_pwd) {
-               int num_lm;
-               num_lm = samdb_result_hashes(mem_ctx, msg, "dBCSPwd", &lmPwdHash);
-               if (num_lm == 0) {
-                       *lm_pwd = NULL;
-               } else if (num_lm > 1) {
-                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               /* Ensure that if we have turned off LM
+                * authentication, that we never use the LM hash, even
+                * if we store it */
+               if (lp_lanman_auth(lp_ctx)) {
+                       int num_lm;
+                       num_lm = samdb_result_hashes(mem_ctx, msg, "dBCSPwd", &lmPwdHash);
+                       if (num_lm == 0) {
+                               *lm_pwd = NULL;
+                       } else if (num_lm > 1) {
+                               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+                       } else {
+                               *lm_pwd = &lmPwdHash[0];
+                       }
                } else {
-                       *lm_pwd = &lmPwdHash[0];
+                       *lm_pwd = NULL;
                }
        }
        return NT_STATUS_OK;
@@ -595,13 +631,54 @@ struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_
 
 /*
   pull a set of account_flags from a result set. 
+
+  This requires that the attributes: 
+   pwdLastSet
+   userAccountControl
+  be included in 'msg'
 */
-uint16_t samdb_result_acct_flags(struct ldb_message *msg, const char *attr)
+uint32_t samdb_result_acct_flags(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 
+                                struct ldb_message *msg, struct ldb_dn *domain_dn)
 {
-       uint_t userAccountControl = ldb_msg_find_attr_as_uint(msg, attr, 0);
-       return samdb_uf2acb(userAccountControl);
+       uint32_t userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
+       uint32_t acct_flags = samdb_uf2acb(userAccountControl); 
+       NTTIME must_change_time;
+       NTTIME now;
+       
+       must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx, 
+                                                             domain_dn, msg);
+       
+       /* Test account expire time */
+       unix_to_nt_time(&now, time(NULL));
+       /* check for expired password */
+       if (must_change_time < now) {
+               acct_flags |= ACB_PW_EXPIRED;
+       }
+       return acct_flags;
 }
 
+struct lsa_BinaryString samdb_result_parameters(TALLOC_CTX *mem_ctx,
+                                               struct ldb_message *msg,
+                                               const char *attr)
+{
+       struct lsa_BinaryString s;
+       const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
+
+       ZERO_STRUCT(s);
+
+       if (!val) {
+               return s;
+       }
+
+       s.array = talloc_array(mem_ctx, uint16_t, val->length/2);
+       if (!s.array) {
+               return s;
+       }
+       s.length = s.size = val->length/2;
+       memcpy(s.array, val->data, val->length);
+
+       return s;
+}
 
 /* Find an attribute, with a particular value */
 
@@ -674,7 +751,9 @@ int samdb_msg_add_dom_sid(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, stru
        struct ldb_val v;
        enum ndr_err_code ndr_err;
 
-       ndr_err = ndr_push_struct_blob(&v, mem_ctx, sid,
+       ndr_err = ndr_push_struct_blob(&v, mem_ctx, 
+                                      lp_iconv_convenience(ldb_get_opaque(sam_ldb, "loadparm")),
+                                      sid,
                                       (ndr_push_flags_fn_t)ndr_push_dom_sid);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                return -1;
@@ -839,6 +918,17 @@ int samdb_msg_add_logon_hours(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx,
        return ldb_msg_add_value(msg, attr_name, &val, NULL);
 }
 
+/*
+  add a parameters element to a message
+*/
+int samdb_msg_add_parameters(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+                            const char *attr_name, struct lsa_BinaryString *parameters)
+{
+       struct ldb_val val;
+       val.length = parameters->length * 2;
+       val.data = (uint8_t *)parameters->array;
+       return ldb_msg_add_value(msg, attr_name, &val, NULL);
+}
 /*
   add a general value element to a message
 */
@@ -956,7 +1046,13 @@ struct ldb_dn *samdb_sites_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx)
 const struct dom_sid *samdb_domain_sid(struct ldb_context *ldb)
 {
        TALLOC_CTX *tmp_ctx;
-       struct dom_sid *domain_sid;
+       const struct dom_sid *domain_sid;
+       const char *attrs[] = {
+               "objectSid",
+               NULL
+       };
+       struct ldb_result *res;
+       int ret;
 
        /* see if we have a cached copy */
        domain_sid = (struct dom_sid *)ldb_get_opaque(ldb, "cache.domain_sid");
@@ -969,15 +1065,23 @@ const struct dom_sid *samdb_domain_sid(struct ldb_context *ldb)
                goto failed;
        }
 
-       /* find the domain_sid */
-       domain_sid = samdb_search_dom_sid(ldb, tmp_ctx, ldb_get_default_basedn(ldb),
-                                         "objectSid", "objectClass=domainDNS");
+       ret = ldb_search(ldb, tmp_ctx, &res, ldb_get_default_basedn(ldb), LDB_SCOPE_BASE, attrs, "objectSid=*");
+
+       if (ret != LDB_SUCCESS) {
+               goto failed;
+       }
+       
+       if (res->count != 1) {
+               goto failed;
+       }
+
+       domain_sid = samdb_result_dom_sid(tmp_ctx, res->msgs[0], "objectSid");
        if (domain_sid == NULL) {
                goto failed;
        }
 
        /* cache the domain_sid in the ldb */
-       if (ldb_set_opaque(ldb, "cache.domain_sid", domain_sid) != LDB_SUCCESS) {
+       if (ldb_set_opaque(ldb, "cache.domain_sid", discard_const_p(struct dom_sid, domain_sid)) != LDB_SUCCESS) {
                goto failed;
        }
 
@@ -1074,13 +1178,12 @@ struct ldb_dn *samdb_ntds_settings_dn(struct ldb_context *ldb)
        }
        
 
-       ret = ldb_search(ldb, ldb_dn_new(tmp_ctx, ldb, ""), LDB_SCOPE_BASE, NULL, root_attrs, &root_res);
+       ret = ldb_search(ldb, tmp_ctx, &root_res, ldb_dn_new(tmp_ctx, ldb, ""), LDB_SCOPE_BASE, root_attrs, NULL);
        if (ret) {
                DEBUG(1,("Searching for dsServiceName in rootDSE failed: %s\n", 
                         ldb_errstring(ldb)));
                goto failed;
        }
-       talloc_steal(tmp_ctx, root_res);
 
        if (root_res->count != 1) {
                goto failed;
@@ -1126,11 +1229,10 @@ const struct GUID *samdb_ntds_invocation_id(struct ldb_context *ldb)
                goto failed;
        }
 
-       ret = ldb_search(ldb, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, NULL, attrs, &res);
+       ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, attrs, NULL);
        if (ret) {
                goto failed;
        }
-       talloc_steal(tmp_ctx, res);
 
        if (res->count != 1) {
                goto failed;
@@ -1220,11 +1322,10 @@ const struct GUID *samdb_ntds_objectGUID(struct ldb_context *ldb)
                goto failed;
        }
 
-       ret = ldb_search(ldb, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, NULL, attrs, &res);
+       ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, attrs, NULL);
        if (ret) {
                goto failed;
        }
-       talloc_steal(tmp_ctx, res);
 
        if (res->count != 1) {
                goto failed;
@@ -1334,14 +1435,13 @@ bool samdb_is_pdc(struct ldb_context *ldb)
                return false;
        }
 
-       ret = ldb_search(ldb, ldb_get_default_basedn(ldb), LDB_SCOPE_BASE, NULL, dom_attrs, &dom_res);
+       ret = ldb_search(ldb, tmp_ctx, &dom_res, ldb_get_default_basedn(ldb), LDB_SCOPE_BASE, dom_attrs, NULL);
        if (ret) {
                DEBUG(1,("Searching for fSMORoleOwner in %s failed: %s\n", 
                         ldb_dn_get_linearized(ldb_get_default_basedn(ldb)), 
                         ldb_errstring(ldb)));
                goto failed;
        }
-       talloc_steal(tmp_ctx, dom_res);
        if (dom_res->count != 1) {
                goto failed;
        }
@@ -1364,6 +1464,42 @@ failed:
        return false;
 }
 
+/*
+  work out if we are a Global Catalog server for the domain of the current open ldb
+*/
+bool samdb_is_gc(struct ldb_context *ldb)
+{
+       const char *attrs[] = { "options", NULL };
+       int ret, options;
+       struct ldb_result *res;
+       TALLOC_CTX *tmp_ctx;
+
+       tmp_ctx = talloc_new(ldb);
+       if (tmp_ctx == NULL) {
+               DEBUG(1, ("talloc_new failed in samdb_is_pdc"));
+               return false;
+       }
+
+       /* Query cn=ntds settings,.... */
+       ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, attrs, NULL);
+       if (ret) {
+               talloc_free(tmp_ctx);
+               return false;
+       }
+       if (res->count != 1) {
+               talloc_free(tmp_ctx);
+               return false;
+       }
+
+       options = ldb_msg_find_attr_as_int(res->msgs[0], "options", 0);
+       talloc_free(tmp_ctx);
+
+       /* if options attribute has the 0x00000001 flag set, then enable the global catlog */
+       if (options & 0x000000001) {
+               return true;
+       }
+       return false;
+}
 
 /* Find a domain object in the parents of a particular DN.  */
 int samdb_search_for_parent_domain(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn *dn,
@@ -1379,10 +1515,9 @@ int samdb_search_for_parent_domain(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
        if (local_ctx == NULL) return LDB_ERR_OPERATIONS_ERROR;
        
        while ((sdn = ldb_dn_get_parent(local_ctx, sdn))) {
-               ret = ldb_search(ldb, sdn, LDB_SCOPE_BASE, 
-                                "(|(objectClass=domain)(objectClass=builtinDomain))", attrs, &res);
+               ret = ldb_search(ldb, local_ctx, &res, sdn, LDB_SCOPE_BASE, attrs,
+                                "(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain))");
                if (ret == LDB_SUCCESS) {
-                       talloc_steal(local_ctx, res);
                        if (res->count == 1) {
                                break;
                        }
@@ -1433,11 +1568,11 @@ static bool samdb_password_complexity_ok(const char *pass)
 
   The caller should probably have a transaction wrapping this
 */
-_PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
+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,
+                           const DATA_BLOB *new_password,
                            struct samr_Password *lmNewHash, 
                            struct samr_Password *ntNewHash,
                            bool user_change,
@@ -1538,40 +1673,47 @@ _PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ct
                *_dominfo = dominfo;
        }
 
-       if (restrictions && new_pass) {
-
+       if (restrictions && new_password) {
+               char *new_pass;
+               
                /* check the various password restrictions */
-               if (restrictions && minPwdLength > strlen_m(new_pass)) {
+               if (restrictions && minPwdLength > utf16_len_n(new_password->data, new_password->length) / 2) {
                        if (reject_reason) {
                                *reject_reason = SAMR_REJECT_TOO_SHORT;
                        }
                        return NT_STATUS_PASSWORD_RESTRICTION;
                }
+
+               /* Create the NT hash */
+               mdfour(local_ntNewHash.hash, new_password->data, new_password->length);
                
-               /* possibly check password complexity */
-               if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
-                   !samdb_password_complexity_ok(new_pass)) {
-                       if (reject_reason) {
-                               *reject_reason = SAMR_REJECT_COMPLEXITY;
+               ntNewHash = &local_ntNewHash;
+
+               /* Only check complexity if we can convert it at all.  Assuming unconvertable passwords are 'strong' */
+               if (convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(ldb_get_opaque(ctx, "loadparm")), 
+                                         CH_UTF16, CH_UNIX, 
+                                         new_password->data, new_password->length, 
+                                         (void **)&new_pass, NULL, false)) {
+                       
+                       
+                       /* 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;
                        }
-                       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;
+                       
+                       /* compute the new lm hashes (for checking history - case insenitivly!) */
+                       if (E_deshash(new_pass, local_lmNewHash.hash)) {
+                               lmNewHash = &local_lmNewHash;
                        }
-                       return NT_STATUS_PASSWORD_RESTRICTION;
+                       
                }
-               ntNewHash = &local_ntNewHash;
        }
 
-       if (user_change) {
+       if (restrictions && user_change) {
                /* are all password changes disallowed? */
                if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
                        if (reject_reason) {
@@ -1637,16 +1779,15 @@ _PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ct
 #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.
+       if (new_password) {
+               /* if we know the cleartext UTF16 password, then set it.
                 * Modules in ldb will set all the appropriate
                 * hashes */
-               CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod, 
-                                              "sambaPassword", new_pass));
+               CHECK_RET(ldb_msg_add_value(mod, "clearTextPassword", new_password, NULL));
        } 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"));
+               CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "clearTextPassword"));
 
                if (lmNewHash) {
                        CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "dBCSPwd", lmNewHash));
@@ -1673,9 +1814,9 @@ _PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ct
   and actually performs the password change
 
 */
-_PUBLIC_ NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
+NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
                                const struct dom_sid *user_sid,
-                               const char *new_pass,
+                               const DATA_BLOB *new_pass,
                                struct samr_Password *lmNewHash, 
                                struct samr_Password *ntNewHash,
                                bool user_change,
@@ -1818,7 +1959,7 @@ struct ldb_dn *samdb_dns_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_c
                return NULL;
        }
        
-       split_realm = str_list_make(tmp_ctx, dns_domain, ".");
+       split_realm = (const char **)str_list_make(tmp_ctx, dns_domain, ".");
        if (!split_realm) {
                talloc_free(tmp_ctx);
                return NULL;
@@ -1856,7 +1997,7 @@ struct ldb_dn *samdb_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
        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, 
+       int ret_domain = ldb_search(ldb, mem_ctx,
                                            &res_domain_ref, 
                                            samdb_partitions_dn(ldb, mem_ctx), 
                                            LDB_SCOPE_ONELEVEL, 
@@ -1868,7 +2009,7 @@ struct ldb_dn *samdb_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
        }
        
        if (res_domain_ref->count == 0) {
-               ret_domain = ldb_search_exp_fmt(ldb, mem_ctx, 
+               ret_domain = ldb_search(ldb, mem_ctx,
                                                &res_domain_ref, 
                                                samdb_dns_domain_to_dn(ldb, mem_ctx, domain_name),
                                                LDB_SCOPE_BASE,