1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-11 05:18:09 +03:00

CVE-2020-25722 dsdb: Add restrictions on computer accounts without a trailing $

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
This commit is contained in:
Andrew Bartlett 2021-09-22 11:29:02 +12:00 committed by Jule Anger
parent adf628000f
commit 53d0e5d31e
3 changed files with 154 additions and 30 deletions
selftest/knownfail.d
source4/dsdb/samdb/ldb_modules

View File

@ -1 +0,0 @@
^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_cc_plain

View File

@ -20,21 +20,9 @@
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_priv_user_UF_NORMAL_ACCOUNT_to_computer_UF_NORMAL_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_priv_user_UF_NORMAL_ACCOUNT_to_computer_UF_WORKSTATION_TRUST_ACCOUNT_keep_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_priv_user_UF_NORMAL_ACCOUNT_to_computer_UF_WORKSTATION_TRUST_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_NORMAL_ACCOUNT_to_None_UF_NORMAL_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_NORMAL_ACCOUNT_to_computer_UF_NORMAL_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_NORMAL_ACCOUNT_to_user_UF_NORMAL_ACCOUNT_keep_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_NORMAL_ACCOUNT_to_user_UF_NORMAL_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_SERVER_TRUST_ACCOUNT_to_None_UF_SERVER_TRUST_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_SERVER_TRUST_ACCOUNT_to_None_UF_WORKSTATION_TRUST_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_SERVER_TRUST_ACCOUNT_to_computer_UF_SERVER_TRUST_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_SERVER_TRUST_ACCOUNT_to_computer_UF_WORKSTATION_TRUST_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_SERVER_TRUST_ACCOUNT_to_user_UF_SERVER_TRUST_ACCOUNT_keep_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_SERVER_TRUST_ACCOUNT_to_user_UF_SERVER_TRUST_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_SERVER_TRUST_ACCOUNT_to_user_UF_WORKSTATION_TRUST_ACCOUNT_keep_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_SERVER_TRUST_ACCOUNT_to_user_UF_WORKSTATION_TRUST_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_WORKSTATION_TRUST_ACCOUNT_to_None_UF_WORKSTATION_TRUST_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_WORKSTATION_TRUST_ACCOUNT_to_computer_UF_WORKSTATION_TRUST_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_WORKSTATION_TRUST_ACCOUNT_to_user_UF_WORKSTATION_TRUST_ACCOUNT_keep_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_computer_UF_WORKSTATION_TRUST_ACCOUNT_to_user_UF_WORKSTATION_TRUST_ACCOUNT_remove_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_user_UF_NORMAL_ACCOUNT_to_computer_UF_NORMAL_ACCOUNT_keep_dollar
^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_lock_wp_user_UF_NORMAL_ACCOUNT_to_computer_UF_NORMAL_ACCOUNT_remove_dollar

View File

@ -71,6 +71,13 @@ struct samldb_ctx {
/* used for add operations */
enum samldb_add_type type;
/*
* should we apply the need_trailing_dollar restriction to
* samAccountName
*/
bool need_trailing_dollar;
/* the resulting message */
struct ldb_message *msg;
@ -235,12 +242,86 @@ static int samldb_unique_attr_check(struct samldb_ctx *ac, const char *attr,
static int samldb_sam_accountname_valid_check(struct samldb_ctx *ac)
{
int ret = samldb_unique_attr_check(ac, "samAccountName", NULL,
ldb_get_default_basedn(
ldb_module_get_ctx(ac->module)));
if (ret == LDB_ERR_OBJECT_CLASS_VIOLATION) {
int ret = 0;
bool is_admin;
struct security_token *user_token = NULL;
struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
struct ldb_message_element *el = dsdb_get_single_valued_attr(ac->msg, "samAccountName",
ac->req->operation);
if (el == NULL || el->num_values == 0) {
ldb_asprintf_errstring(ldb,
"%08X: samldb: 'samAccountName' can't be deleted/empty!",
W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
if (ac->req->operation == LDB_ADD) {
return LDB_ERR_CONSTRAINT_VIOLATION;
} else {
return LDB_ERR_UNWILLING_TO_PERFORM;
}
}
ret = samldb_unique_attr_check(ac, "samAccountName", NULL,
ldb_get_default_basedn(
ldb_module_get_ctx(ac->module)));
/*
* Error code munging to try and match what must be some quite
* strange code-paths in Windows
*/
if (ret == LDB_ERR_CONSTRAINT_VIOLATION
&& ac->req->operation == LDB_MODIFY) {
ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
} else if (ret == LDB_ERR_OBJECT_CLASS_VIOLATION) {
ret = LDB_ERR_CONSTRAINT_VIOLATION;
}
if (ret != LDB_SUCCESS) {
return ret;
}
if (!ac->need_trailing_dollar) {
return LDB_SUCCESS;
}
/* This does not permit a single $ */
if (el->values[0].length < 2) {
ldb_asprintf_errstring(ldb,
"%08X: samldb: 'samAccountName' "
"can't just be one character!",
W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
return LDB_ERR_UNWILLING_TO_PERFORM;
}
user_token = acl_user_token(ac->module);
if (user_token == NULL) {
return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
}
is_admin
= security_token_has_builtin_administrators(user_token);
if (is_admin) {
/*
* Administrators are allowed to select strange names.
* This is poor practice but not prevented.
*/
return false;
}
if (el->values[0].data[el->values[0].length - 1] != '$') {
ldb_asprintf_errstring(ldb,
"%08X: samldb: 'samAccountName' "
"must have a trailing $!",
W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
return LDB_ERR_UNWILLING_TO_PERFORM;
}
if (el->values[0].data[el->values[0].length - 2] == '$') {
ldb_asprintf_errstring(ldb,
"%08X: samldb: 'samAccountName' "
"must not have a double trailing $!",
W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION));
return LDB_ERR_UNWILLING_TO_PERFORM;
}
return ret;
}
@ -557,17 +638,31 @@ static int samldb_schema_add_handle_mapiid(struct samldb_ctx *ac)
}
/* sAMAccountName handling */
static int samldb_generate_sAMAccountName(struct ldb_context *ldb,
static int samldb_generate_sAMAccountName(struct samldb_ctx *ac,
struct ldb_message *msg)
{
struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
char *name;
/* Format: $000000-000000000000 */
/*
* This is currently a Samba-only behaviour, to add a trailing
* $ even for the generated accounts.
*/
name = talloc_asprintf(msg, "$%.6X-%.6X%.6X",
(unsigned int)generate_random(),
(unsigned int)generate_random(),
(unsigned int)generate_random());
if (ac->need_trailing_dollar) {
/* Format: $000000-00000000000$ */
name = talloc_asprintf(msg, "$%.6X-%.6X%.5X$",
(unsigned int)generate_random(),
(unsigned int)generate_random(),
(unsigned int)generate_random());
} else {
/* Format: $000000-000000000000 */
name = talloc_asprintf(msg, "$%.6X-%.6X%.6X",
(unsigned int)generate_random(),
(unsigned int)generate_random(),
(unsigned int)generate_random());
}
if (name == NULL) {
return ldb_oom(ldb);
}
@ -576,11 +671,10 @@ static int samldb_generate_sAMAccountName(struct ldb_context *ldb,
static int samldb_check_sAMAccountName(struct samldb_ctx *ac)
{
struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
int ret;
if (ldb_msg_find_element(ac->msg, "sAMAccountName") == NULL) {
ret = samldb_generate_sAMAccountName(ldb, ac->msg);
ret = samldb_generate_sAMAccountName(ac, ac->msg);
if (ret != LDB_SUCCESS) {
return ret;
}
@ -1495,6 +1589,20 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac)
return ret;
}
/*
* Require, for non-admin modifications, a trailing $
* for either objectclass=computer or a trust account
* type in userAccountControl
*/
if ((user_account_control
& UF_TRUST_ACCOUNT_MASK) != 0) {
ac->need_trailing_dollar = true;
}
if (is_computer_objectclass) {
ac->need_trailing_dollar = true;
}
/* add "sAMAccountType" attribute */
ret = dsdb_user_obj_set_account_type(ldb, ac->msg, user_account_control, NULL);
if (ret != LDB_SUCCESS) {
@ -4010,12 +4118,41 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
el = ldb_msg_find_element(ac->msg, "sAMAccountName");
if (el != NULL) {
uint32_t user_account_control;
struct ldb_result *res = NULL;
const char * const attrs[] = { "userAccountControl",
"objectclass",
NULL };
ret = dsdb_module_search_dn(ac->module,
ac,
&res,
ac->msg->dn,
attrs,
DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
ac->req);
if (ret != LDB_SUCCESS) {
return ret;
}
user_account_control
= ldb_msg_find_attr_as_uint(res->msgs[0],
"userAccountControl",
0);
if ((user_account_control
& UF_TRUST_ACCOUNT_MASK) != 0) {
ac->need_trailing_dollar = true;
} else if (samdb_find_attribute(ldb,
res->msgs[0],
"objectclass",
"computer")
!= NULL) {
ac->need_trailing_dollar = true;
}
ret = samldb_sam_accountname_valid_check(ac);
/*
* Other errors are checked for elsewhere, we just
* want to prevent duplicates
*/
if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
if (ret != LDB_SUCCESS) {
return ret;
}
}