s4:dsdb/password_hash: implement DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID
[nivanova/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / password_hash.c
index 7717b03621a8f8fe809c68b7f9cccff36b18dc1b..505f2c26f74bc8663b1c74bfe51f23dbecf2e538 100644 (file)
@@ -215,7 +215,7 @@ static int setup_lm_fields(struct setup_password_fields_io *io)
                return LDB_SUCCESS;
        }
 
-       /* We might not have an old NT password */
+       /* We might not have an old LM password */
        io->g.lm_history = talloc_array(io->ac,
                                        struct samr_Password,
                                        io->ac->status->domain_data.pwdHistoryLength);
@@ -519,7 +519,7 @@ static int setup_primary_kerberos(struct setup_password_fields_io *io,
                }
 
                /* 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(ldb, "loadparm")), &_old_pkb,
+               ndr_err = ndr_pull_struct_blob(&blob, io->ac, &_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);
@@ -636,7 +636,6 @@ static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
 
                /* 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(ldb, "loadparm")),
                                               &_old_pkb,
                                               (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
                if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -1068,7 +1067,6 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
        /* if there's an old supplementaCredentials blob then parse it */
        if (io->o.supplemental) {
                ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
-                                                  lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
                                                   &_old_scb,
                                                   (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
                if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -1148,7 +1146,6 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
                }
 
                ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
-                                              lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
                                               &pknb,
                                               (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
                if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -1180,7 +1177,6 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
        }
 
        ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac, 
-                                      lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
                                       &pkb,
                                       (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -1211,7 +1207,6 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
        }
 
        ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac, 
-                                      lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
                                       &pdb,
                                       (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -1240,7 +1235,6 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
                pcb.cleartext   = *io->n.cleartext_utf16;
 
                ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac, 
-                                              lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
                                               &pcb,
                                               (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
                if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -1266,7 +1260,6 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
         */
        pb.names = names;
        ndr_err = ndr_push_struct_blob(&pb_blob, io->ac, 
-                                      lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), 
                                       &pb,
                                       (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -1294,7 +1287,6 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
        scb.sub.packages        = packages;
 
        ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac, 
-                                      lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
                                       &scb,
                                       (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -1335,8 +1327,7 @@ static int setup_given_passwords(struct setup_password_fields_io *io,
                        ldb_oom(ldb);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
-               if (!convert_string_talloc_convenience(io->ac,
-                                                      lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+               if (!convert_string_talloc(io->ac,
                                                       CH_UTF8, CH_UTF16,
                                                       g->cleartext_utf8->data,
                                                       g->cleartext_utf8->length,
@@ -1360,8 +1351,7 @@ static int setup_given_passwords(struct setup_password_fields_io *io,
                        ldb_oom(ldb);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
-               if (!convert_string_talloc_convenience(io->ac,
-                                                      lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+               if (!convert_string_talloc(io->ac,
                                                       CH_UTF16MUNGED, CH_UTF8,
                                                       g->cleartext_utf16->data,
                                                       g->cleartext_utf16->length,
@@ -1392,8 +1382,7 @@ static int setup_given_passwords(struct setup_password_fields_io *io,
                       g->cleartext_utf16->length);
        }
 
-       if (g->cleartext_utf8 &&
-           lp_lanman_auth(ldb_get_opaque(ldb, "loadparm"))) {
+       if (g->cleartext_utf8) {
                struct samr_Password *lm_hash;
 
                lm_hash = talloc(io->ac, struct samr_Password);
@@ -1416,11 +1405,12 @@ static int setup_given_passwords(struct setup_password_fields_io *io,
 
 static int setup_password_fields(struct setup_password_fields_io *io)
 {
-       struct ldb_context *ldb;
+       struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
+       struct loadparm_context *lp_ctx =
+               lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
+                                        struct loadparm_context);
        int ret;
 
-       ldb = ldb_module_get_ctx(io->ac->module);
-
        /* transform the old password (for password changes) */
        ret = setup_given_passwords(io, &io->og);
        if (ret != LDB_SUCCESS) {
@@ -1445,9 +1435,14 @@ static int setup_password_fields(struct setup_password_fields_io *io)
                return ret;
        }
 
-       ret = setup_lm_fields(io);
-       if (ret != LDB_SUCCESS) {
-               return ret;
+       if (lp_lanman_auth(lp_ctx)) {
+               ret = setup_lm_fields(io);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       } else {
+               io->g.lm_hash = NULL;
+               io->g.lm_history_len = 0;
        }
 
        ret = setup_supplemental_field(io);
@@ -1500,16 +1495,6 @@ static int check_password_restrictions(struct setup_password_fields_io *io)
                                return LDB_ERR_UNWILLING_TO_PERFORM;
                        }
                } else if (io->og.lm_hash) {
-                       struct loadparm_context *lp_ctx =
-                               (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm");
-
-                       if (!lp_lanman_auth(lp_ctx)) {
-                               ldb_asprintf_errstring(ldb,
-                                       "check_password_restrictions: "
-                                       "The password change through the LM hash is deactivated!");
-                               return LDB_ERR_UNWILLING_TO_PERFORM;
-                       }
-
                        if (!io->o.lm_hash) {
                                ldb_asprintf_errstring(ldb,
                                        "check_password_restrictions: "
@@ -1533,40 +1518,43 @@ static int check_password_restrictions(struct setup_password_fields_io *io)
        }
 
        /*
-        * Fundamental password checks done by the call "samdb_check_password".
+        * Fundamental password checks done by the call
+        * "samdb_check_password".
         * It is also in use by "dcesrv_samr_ValidatePassword".
         */
-       stat = samdb_check_password(io->n.cleartext_utf8,
-                                   io->ac->status->domain_data.pwdProperties,
-                                   io->ac->status->domain_data.minPwdLength);
-       switch (stat) {
-       case SAMR_VALIDATION_STATUS_SUCCESS:
-               /* perfect -> proceed! */
-               break;
+       if (io->n.cleartext_utf8 != NULL) {
+               stat = samdb_check_password(io->n.cleartext_utf8,
+                                           io->ac->status->domain_data.pwdProperties,
+                                           io->ac->status->domain_data.minPwdLength);
+               switch (stat) {
+               case SAMR_VALIDATION_STATUS_SUCCESS:
+                               /* perfect -> proceed! */
+                       break;
 
-       case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
-               ldb_asprintf_errstring(ldb,
-                       "check_password_restrictions: "
-                       "the password is too short. It should be equal or longer than %i characters!",
-                       io->ac->status->domain_data.minPwdLength);
+               case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
+                       ldb_asprintf_errstring(ldb,
+                               "check_password_restrictions: "
+                               "the password is too short. It should be equal or longer than %i characters!",
+                               io->ac->status->domain_data.minPwdLength);
 
-               io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
-               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
+                       return LDB_ERR_CONSTRAINT_VIOLATION;
 
-       case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
-               ldb_asprintf_errstring(ldb,
-                       "check_password_restrictions: "
-                       "the password does not meet the complexity criterias!");
-               io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
+               case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
+                       ldb_asprintf_errstring(ldb,
+                               "check_password_restrictions: "
+                               "the password does not meet the complexity criterias!");
+                       io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
 
-               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       return LDB_ERR_CONSTRAINT_VIOLATION;
 
-       default:
-               ldb_asprintf_errstring(ldb,
-                       "check_password_restrictions: "
-                       "the password doesn't fit by a certain reason!");
+               default:
+                       ldb_asprintf_errstring(ldb,
+                               "check_password_restrictions: "
+                               "the password doesn't fit by a certain reason!");
 
-               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       return LDB_ERR_CONSTRAINT_VIOLATION;
+               }
        }
 
        if (io->ac->pwd_reset) {
@@ -1643,6 +1631,9 @@ static int setup_io(struct ph_context *ac,
 { 
        const struct ldb_val *quoted_utf16, *old_quoted_utf16, *lm_hash, *old_lm_hash;
        struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
+       struct loadparm_context *lp_ctx =
+               lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
+                                        struct loadparm_context);
        int ret;
 
        ZERO_STRUCTP(io);
@@ -1672,9 +1663,6 @@ static int setup_io(struct ph_context *ac,
                return LDB_ERR_CONSTRAINT_VIOLATION;
        }
 
-       /* FIXME: fix to don't break provision */
-       io->ac->hash_values = true;
-
        /* Only non-trust accounts have restrictions (possibly this test is the
         * wrong way around, but we like to be restrictive if possible */
        io->u.restrictions = !(io->u.userAccountControl
@@ -1759,7 +1747,7 @@ static int setup_io(struct ph_context *ac,
                io->n.cleartext_utf16 = quoted_utf16_2;
                io->n.nt_hash = NULL;
 
-       } else {
+       } else if (quoted_utf16) {
                /* We have only the hash available -> so no plaintext here */
                if (!ac->hash_values) {
                        /* refuse the change if someone wants to change
@@ -1778,11 +1766,9 @@ static int setup_io(struct ph_context *ac,
                        return LDB_ERR_CONSTRAINT_VIOLATION;
                }
 
-               if (quoted_utf16 != NULL) {
-                       io->n.nt_hash = talloc(io->ac, struct samr_Password);
-                       memcpy(io->n.nt_hash->hash, quoted_utf16->data,
-                              MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
-               }
+               io->n.nt_hash = talloc(io->ac, struct samr_Password);
+               memcpy(io->n.nt_hash->hash, quoted_utf16->data,
+                      MIN(quoted_utf16->length, sizeof(io->n.nt_hash->hash)));
        }
 
        /* Checks and converts the previous "unicodePwd" attribute */
@@ -1818,7 +1804,7 @@ static int setup_io(struct ph_context *ac,
 
                io->og.cleartext_utf16 = old_quoted_utf16_2;
                io->og.nt_hash = NULL;
-       } else {
+       } else if (old_quoted_utf16) {
                /* We have only the hash available -> so no plaintext here */
                if (!ac->hash_values) {
                        /* refuse the change if someone wants to change
@@ -1829,11 +1815,9 @@ static int setup_io(struct ph_context *ac,
                        return LDB_ERR_UNWILLING_TO_PERFORM;
                }
 
-               if (old_quoted_utf16 != NULL) {
-                       io->og.nt_hash = talloc(io->ac, struct samr_Password);
-                       memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
-                              MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
-               }
+               io->og.nt_hash = talloc(io->ac, struct samr_Password);
+               memcpy(io->og.nt_hash->hash, old_quoted_utf16->data,
+                      MIN(old_quoted_utf16->length, sizeof(io->og.nt_hash->hash)));
        }
 
        /* Handles the "dBCSPwd" attribute (LM hash) */
@@ -1855,13 +1839,13 @@ static int setup_io(struct ph_context *ac,
                        "it's not allowed to set the LM hash password directly'");
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
-       if (lm_hash != NULL) {
+
+       if (lp_lanman_auth(lp_ctx) && (lm_hash != NULL)) {
                io->n.lm_hash = talloc(io->ac, struct samr_Password);
                memcpy(io->n.lm_hash->hash, lm_hash->data, MIN(lm_hash->length,
                       sizeof(io->n.lm_hash->hash)));
        }
-
-       if (old_lm_hash != NULL) {
+       if (lp_lanman_auth(lp_ctx) && (old_lm_hash != NULL)) {
                io->og.lm_hash = talloc(io->ac, struct samr_Password);
                memcpy(io->og.lm_hash->hash, old_lm_hash->data, MIN(old_lm_hash->length,
                       sizeof(io->og.lm_hash->hash)));
@@ -1886,6 +1870,17 @@ static int setup_io(struct ph_context *ac,
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
 
+       /* refuse the change if someone tries to set/change the password by
+        * the lanman hash alone and we've deactivated that mechanism. This
+        * would end in an account without any password! */
+       if ((!io->n.cleartext_utf8) && (!io->n.cleartext_utf16)
+           && (!io->n.nt_hash) && (!io->n.lm_hash)) {
+               ldb_asprintf_errstring(ldb,
+                       "setup_io: "
+                       "The password change/set operations performed using the LAN Manager hash alone are deactivated!");
+               return LDB_ERR_UNWILLING_TO_PERFORM;
+       }
+
        /* refuse the change if someone wants to compare against a plaintext
           or hash at the same time for a "password modify" operation... */
        if ((io->og.cleartext_utf8 || io->og.cleartext_utf16)
@@ -2192,6 +2187,7 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
        struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr,
                *ntAttr, *lmAttr;
        int ret;
+       struct ldb_control *bypass = NULL;
 
        ldb = ldb_module_get_ctx(module);
 
@@ -2207,6 +2203,15 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
                return ldb_next_request(module, req);
        }
 
+       bypass = ldb_request_get_control(req,
+                                        DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
+       if (bypass != NULL) {
+               /* Mark the "bypass" control as uncritical (done) */
+               bypass->critical = false;
+               ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add (bypassing)\n");
+               return ldb_next_request(module, req);
+       }
+
        /* nobody must touch password histories and 'supplementalCredentials' */
        if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
                return LDB_ERR_UNWILLING_TO_PERFORM;
@@ -2280,22 +2285,11 @@ static int password_hash_add_do_add(struct ph_context *ac)
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* remove attributes that we just read into 'io' (handle also superfluous
-        * "password modify" trials - multiple attributes with the same name -
-        * on add operations) */
-       while (ldb_msg_find_element(msg, "userPassword") != NULL) {
-               ldb_msg_remove_attr(msg, "userPassword");
-       }
-       while (ldb_msg_find_element(msg, "clearTextPassword") != NULL) {
-               ldb_msg_remove_attr(msg, "clearTextPassword");
-       }
-       while (ldb_msg_find_element(msg, "unicodePwd") != NULL) {
-               ldb_msg_remove_attr(msg, "unicodePwd");
-       }
-       while (ldb_msg_find_element(msg, "dBCSPwd") != NULL) {
-               ldb_msg_remove_attr(msg, "dBCSPwd");
-       }
-
+       /* remove attributes that we just read into 'io' */
+       ldb_msg_remove_attr(msg, "userPassword");
+       ldb_msg_remove_attr(msg, "clearTextPassword");
+       ldb_msg_remove_attr(msg, "unicodePwd");
+       ldb_msg_remove_attr(msg, "dBCSPwd");
        ldb_msg_remove_attr(msg, "pwdLastSet");
 
        ldb = ldb_module_get_ctx(ac->module);
@@ -2379,6 +2373,7 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
        struct ldb_message *msg;
        struct ldb_request *down_req;
        int ret;
+       struct ldb_control *bypass = NULL;
 
        ldb = ldb_module_get_ctx(module);
 
@@ -2394,6 +2389,15 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
                return ldb_next_request(module, req);
        }
 
+       bypass = ldb_request_get_control(req,
+                                        DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
+       if (bypass != NULL) {
+               /* Mark the "bypass" control as uncritical (done) */
+               bypass->critical = false;
+               ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify (bypassing)\n");
+               return ldb_next_request(module, req);
+       }
+
        /* nobody must touch password histories and 'supplementalCredentials' */
        if (ldb_msg_find_element(req->op.mod.message, "ntPwdHistory")) {
                return LDB_ERR_UNWILLING_TO_PERFORM;
@@ -2466,7 +2470,7 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
                                                       *l);
                                return LDB_ERR_CONSTRAINT_VIOLATION;
                        }
-                       ldb_msg_remove_attr(msg, *l);
+                       ldb_msg_remove_element(msg, passwordAttr);
                }
        }
        if ((del_attr_cnt > 0) && (add_attr_cnt == 0)) {
@@ -2659,7 +2663,10 @@ static int password_hash_mod_search_self(struct ph_context *ac)
 
 static int password_hash_mod_do_mod(struct ph_context *ac)
 {
-       struct ldb_context *ldb;
+       struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
+       struct loadparm_context *lp_ctx =
+               lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
+                                        struct loadparm_context);
        struct ldb_request *mod_req;
        struct ldb_message *msg;
        const struct ldb_message *orig_msg, *searched_msg;
@@ -2667,8 +2674,6 @@ static int password_hash_mod_do_mod(struct ph_context *ac)
        int ret;
        NTSTATUS status;
 
-       ldb = ldb_module_get_ctx(ac->module);
-
        /* use a new message structure so that we can modify it */
        msg = ldb_msg_new(ac);
        if (msg == NULL) {
@@ -2689,8 +2694,8 @@ static int password_hash_mod_do_mod(struct ph_context *ac)
        
        /* Get the old password from the database */
        status = samdb_result_passwords(io.ac,
-                                       ldb_get_opaque(ldb, "loadparm"),
-                                       searched_msg,
+                                       lp_ctx,
+                                       discard_const_p(struct ldb_message, searched_msg),
                                        &io.o.lm_hash, &io.o.nt_hash);
        if (!NT_STATUS_IS_OK(status)) {
                return LDB_ERR_OPERATIONS_ERROR;