struct dsdb_schema *schema;
};
-bool is_root_base_dn(struct ldb_context *ldb, struct ldb_dn *dn_to_check)
-{
- int result;
- struct ldb_dn *root_base_dn = ldb_get_root_basedn(ldb);
- result = ldb_dn_compare(root_base_dn,dn_to_check);
- return (result==0);
-}
-
static struct security_token *acl_user_token(struct ldb_module *module)
{
struct ldb_context *ldb = ldb_module_get_ctx(module);
struct ldb_context *ldb;
struct acl_private *data;
int ret, i;
- TALLOC_CTX *mem_ctx = talloc_new(module);
+ TALLOC_CTX *mem_ctx;
static const char *attrs[] = { "passwordAttribute", NULL };
struct ldb_result *res;
struct ldb_message *msg;
}
data->password_attrs = NULL;
- data->acl_perform = lp_parm_bool(ldb_get_opaque(ldb, "loadparm"),
+ data->acl_perform = lpcfg_parm_bool(ldb_get_opaque(ldb, "loadparm"),
NULL, "acl", "perform", false);
ldb_module_set_private(module, data);
+ mem_ctx = talloc_new(module);
if (!mem_ctx) {
return ldb_oom(ldb);
}
/* FIXME: this has to be made dynamic at some point */
if ((ldb_dn_compare(req->op.add.message->dn, (ldb_get_schema_basedn(ldb))) == 0) ||
(ldb_dn_compare(req->op.add.message->dn, (ldb_get_config_basedn(ldb))) == 0) ||
- (ldb_dn_compare(req->op.add.message->dn, (ldb_get_default_basedn(ldb))) == 0) ||
- (ldb_dn_compare(req->op.add.message->dn, (ldb_get_root_basedn(ldb))) == 0)) {
+ (ldb_dn_compare(req->op.add.message->dn, (ldb_get_default_basedn(ldb))) == 0)) {
return ldb_next_request(module, req);
}
return LDB_SUCCESS;
}
/* if we are adding/deleting ourselves, check for self membership */
- ret = dsdb_find_dn_by_sid(ldb, mem_ctx, acl_user_token(module)->user_sid, &user_dn);
+ ret = dsdb_find_dn_by_sid(ldb, mem_ctx,
+ &acl_user_token(module)->sids[PRIMARY_USER_SID_INDEX],
+ &user_dn);
if (ret != LDB_SUCCESS) {
return ret;
}
unsigned int del_attr_cnt = 0, add_attr_cnt = 0, rep_attr_cnt = 0;
struct ldb_message_element *el;
struct ldb_message *msg;
- const char *passwordAttrs[] = { "userPassword", "unicodePwd",
- "clearTextPassword", NULL }, **l;
+ const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
+ "unicodePwd", "dBCSPwd", NULL }, **l;
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
msg = ldb_msg_copy_shallow(tmp_ctx, req->op.mod.message);
}
for (l = passwordAttrs; *l != NULL; l++) {
while ((el = ldb_msg_find_element(msg, *l)) != NULL) {
- if (el->flags == LDB_FLAG_MOD_DELETE) {
+ if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
++del_attr_cnt;
}
- if (el->flags == LDB_FLAG_MOD_ADD) {
+ if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_ADD) {
++add_attr_cnt;
}
- if (el->flags == LDB_FLAG_MOD_REPLACE) {
+ if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
++rep_attr_cnt;
}
ldb_msg_remove_element(msg, el);
talloc_free(tmp_ctx);
return LDB_SUCCESS;
}
- if (rep_attr_cnt > 0 || (add_attr_cnt != del_attr_cnt)) {
+
+ if (ldb_request_get_control(req,
+ DSDB_CONTROL_PASSWORD_CHANGE_OID) != NULL) {
+ /* The "DSDB_CONTROL_PASSWORD_CHANGE_OID" control means that we
+ * have a user password change and not a set as the message
+ * looks like. In it's value blob it contains the NT and/or LM
+ * hash of the old password specified by the user.
+ * This control is used by the SAMR and "kpasswd" password
+ * change mechanisms. */
+ ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
+ GUID_DRS_USER_CHANGE_PASSWORD,
+ SEC_ADS_CONTROL_ACCESS,
+ sid);
+ }
+ else if (rep_attr_cnt > 0 || (add_attr_cnt != del_attr_cnt)) {
ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
GUID_DRS_FORCE_CHANGE_PASSWORD,
SEC_ADS_CONTROL_ACCESS,
/* FIXME: this has to be made dynamic at some point */
if ((ldb_dn_compare(req->op.del.dn, (ldb_get_schema_basedn(ldb))) == 0) ||
(ldb_dn_compare(req->op.del.dn, (ldb_get_config_basedn(ldb))) == 0) ||
- (ldb_dn_compare(req->op.del.dn, (ldb_get_default_basedn(ldb))) == 0) ||
- (ldb_dn_compare(req->op.del.dn, (ldb_get_root_basedn(ldb))) == 0)) {
+ (ldb_dn_compare(req->op.del.dn, (ldb_get_default_basedn(ldb))) == 0)) {
DEBUG(10,("acl:deleting an NC\n"));
return ldb_module_done(req, NULL, NULL, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS);
}
/* FIXME: this has to be made dynamic at some point */
if ((ldb_dn_compare(req->op.rename.newdn, (ldb_get_schema_basedn(ldb))) == 0) ||
(ldb_dn_compare(req->op.rename.newdn, (ldb_get_config_basedn(ldb))) == 0) ||
- (ldb_dn_compare(req->op.rename.newdn, (ldb_get_default_basedn(ldb))) == 0) ||
- (ldb_dn_compare(req->op.rename.newdn, (ldb_get_root_basedn(ldb))) == 0)) {
+ (ldb_dn_compare(req->op.rename.newdn, (ldb_get_default_basedn(ldb))) == 0)) {
DEBUG(10,("acl:moving as an NC\n"));
return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
}
return ldb_next_request(module, down_req);
}
+static const char *acl_user_name(TALLOC_CTX *mem_ctx, struct ldb_module *module)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct auth_session_info *session_info
+ = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
+ if (!session_info) {
+ return "UNKNOWN (NULL)";
+ }
+
+ return talloc_asprintf(mem_ctx, "%s\\%s",
+ session_info->server_info->domain_name,
+ session_info->server_info->account_name);
+}
+
+static int acl_extended(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
+
+ /* allow everybody to read the sequence number */
+ if (strcmp(req->op.extended.oid,
+ LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
+ return ldb_next_request(module, req);
+ }
+
+ if (dsdb_module_am_system(module) ||
+ dsdb_module_am_administrator(module) || as_system) {
+ return ldb_next_request(module, req);
+ } else {
+ ldb_asprintf_errstring(ldb,
+ "acl_extended: "
+ "attempted database modify not permitted. "
+ "User %s is not SYSTEM or an administrator",
+ acl_user_name(req, module));
+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+ }
+}
+
_PUBLIC_ const struct ldb_module_ops ldb_acl_module_ops = {
.name = "acl",
.search = acl_search,
.modify = acl_modify,
.del = acl_delete,
.rename = acl_rename,
+ .extended = acl_extended,
.init_context = acl_module_init
};