diff options
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/password_hash.c | 120 |
1 files changed, 71 insertions, 49 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c index 52c7a43426..c7f6465e97 100644 --- a/source4/dsdb/samdb/ldb_modules/password_hash.c +++ b/source4/dsdb/samdb/ldb_modules/password_hash.c @@ -1748,23 +1748,6 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req) return ldb_next_request(module, req); } - if (userPasswordAttr && (userPasswordAttr->num_values != 1)) { - ldb_set_errstring(ldb, "'userPassword' must have exactly one value!"); - return LDB_ERR_CONSTRAINT_VIOLATION; - } - if (clearTextPasswordAttr && (clearTextPasswordAttr->num_values != 1)) { - ldb_set_errstring(ldb, "'clearTextPassword' must have exactly one value!"); - return LDB_ERR_CONSTRAINT_VIOLATION; - } - if (ntAttr && (ntAttr->num_values != 1)) { - ldb_set_errstring(ldb, "'unicodePwd' must have exactly one value!"); - return LDB_ERR_CONSTRAINT_VIOLATION; - } - if (lmAttr && (lmAttr->num_values != 1)) { - ldb_set_errstring(ldb, "'dBCSPwd' must have exactly one value!"); - return LDB_ERR_CONSTRAINT_VIOLATION; - } - /* Make sure we are performing the password set action on a (for us) * valid object. Those are instances of either "user" and/or * "inetOrgPerson". Otherwise continue with the submodules. */ @@ -1890,8 +1873,10 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r { struct ldb_context *ldb; struct ph_context *ac; - struct ldb_message_element *userPasswordAttr, *clearTextPasswordAttr, - *ntAttr, *lmAttr; + const char *passwordAttrs[] = { "userPassword", "clearTextPassword", + "unicodePwd", "dBCSPwd", NULL }, **l; + unsigned int attr_cnt, del_attr_cnt, add_attr_cnt, rep_attr_cnt; + struct ldb_message_element *passwordAttr; struct ldb_message *msg; struct ldb_request *down_req; int ret; @@ -1925,31 +1910,14 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r * OR 'unicodePwd' OR 'dBCSPwd' we don't need to make any changes. * For password changes/set there should be a 'delete' or a 'modify' * on these attributes. */ - - userPasswordAttr = ldb_msg_find_element(req->op.mod.message, "userPassword"); - clearTextPasswordAttr = ldb_msg_find_element(req->op.mod.message, "clearTextPassword"); - ntAttr = ldb_msg_find_element(req->op.mod.message, "unicodePwd"); - lmAttr = ldb_msg_find_element(req->op.mod.message, "dBCSPwd"); - - if ((!userPasswordAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) { - return ldb_next_request(module, req); - } - - if (userPasswordAttr && (userPasswordAttr->num_values != 1)) { - ldb_set_errstring(ldb, "'userPassword' must have exactly one value!"); - return LDB_ERR_CONSTRAINT_VIOLATION; - } - if (clearTextPasswordAttr && (clearTextPasswordAttr->num_values != 1)) { - ldb_set_errstring(ldb, "'clearTextPassword' must have exactly one value!"); - return LDB_ERR_CONSTRAINT_VIOLATION; - } - if (ntAttr && (ntAttr->num_values != 1)) { - ldb_set_errstring(ldb, "'unicodePwd' must have exactly one value!"); - return LDB_ERR_CONSTRAINT_VIOLATION; + attr_cnt = 0; + for (l = passwordAttrs; *l != NULL; l++) { + if (ldb_msg_find_element(req->op.mod.message, *l) != NULL) { + ++attr_cnt; + } } - if (lmAttr && (lmAttr->num_values != 1)) { - ldb_set_errstring(ldb, "'dBCSPwd' must have exactly one value!"); - return LDB_ERR_CONSTRAINT_VIOLATION; + if (attr_cnt == 0) { + return ldb_next_request(module, req); } ac = ph_init_context(module, req); @@ -1965,12 +1933,66 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r return LDB_ERR_OPERATIONS_ERROR; } - /* - remove any modification to the password from the first commit - * we will make the real modification later */ - if (userPasswordAttr) ldb_msg_remove_attr(msg, "userPassword"); - if (clearTextPasswordAttr) ldb_msg_remove_attr(msg, "clearTextPassword"); - if (ntAttr) ldb_msg_remove_attr(msg, "unicodePwd"); - if (lmAttr) ldb_msg_remove_attr(msg, "dBCSPwd"); + /* - check for single-valued password attributes + * (if not return "CONSTRAINT_VIOLATION") + * - check that for a password change operation one add and one delete + * operation exists + * (if not return "CONSTRAINT_VIOLATION" or "UNWILLING_TO_PERFORM") + * - check that a password change and a password set operation cannot + * be mixed + * (if not return "UNWILLING_TO_PERFORM") + * - remove all password attributes modifications from the first change + * operation (anything without the passwords) - we will make the real + * modification later */ + del_attr_cnt = 0; + add_attr_cnt = 0; + rep_attr_cnt = 0; + for (l = passwordAttrs; *l != NULL; l++) { + while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) { + if (passwordAttr->flags == LDB_FLAG_MOD_DELETE) { + ++del_attr_cnt; + } + if (passwordAttr->flags == LDB_FLAG_MOD_ADD) { + ++add_attr_cnt; + } + if (passwordAttr->flags == LDB_FLAG_MOD_REPLACE) { + ++rep_attr_cnt; + } + if ((passwordAttr->num_values != 1) && + (passwordAttr->flags != LDB_FLAG_MOD_REPLACE)) { + talloc_free(ac); + ldb_asprintf_errstring(ldb, + "'%s' attributes must have exactly one value!", + *l); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + ldb_msg_remove_attr(msg, *l); + } + } + if ((del_attr_cnt > 0) && (add_attr_cnt == 0)) { + talloc_free(ac); + ldb_set_errstring(ldb, + "Only the delete action for a password change specified!"); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + if ((del_attr_cnt == 0) && (add_attr_cnt > 0)) { + talloc_free(ac); + ldb_set_errstring(ldb, + "Only the add action for a password change specified!"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + if ((del_attr_cnt > 1) || (add_attr_cnt > 1)) { + talloc_free(ac); + ldb_set_errstring(ldb, + "Only one delete and one add action for a password change allowed!"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + if ((rep_attr_cnt > 0) && ((del_attr_cnt > 0) || (add_attr_cnt > 0))) { + talloc_free(ac); + ldb_set_errstring(ldb, + "Either a password change or a password set operation is allowed!"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } /* if there was nothing else to be modified skip to next step */ if (msg->num_elements == 0) { |