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

samba: tag release samba-4.5.16

-----BEGIN PGP SIGNATURE-----
 
 iEYEABECAAYFAlqmbowACgkQbzORW2Vot+rCbQCeN6rCkAERSaaYU4l2SCRwQXI9
 93QAn3yfFmREstPdM0xHWvswIB6A0DRS
 =Deny
 -----END PGP SIGNATURE-----

Merge tag 'samba-4.5.16' into v4-5-test

samba: tag release samba-4.5.16
This commit is contained in:
Stefan Metzmacher 2018-03-13 11:00:06 +01:00
commit 6e98de0158
8 changed files with 324 additions and 29 deletions

View File

@ -1,3 +1,79 @@
==============================
Release Notes for Samba 4.5.16
March 13, 2018
==============================
This is a security release in order to address the following defects:
o CVE-2018-1050 (Denial of Service Attack on external print server.)
o CVE-2018-1057 (Authenticated users can change other users' password.)
=======
Details
=======
o CVE-2018-1050:
All versions of Samba from 4.0.0 onwards are vulnerable to a denial of
service attack when the RPC spoolss service is configured to be run as
an external daemon. Missing input sanitization checks on some of the
input parameters to spoolss RPC calls could cause the print spooler
service to crash.
There is no known vulnerability associated with this error, merely a
denial of service. If the RPC spoolss service is left by default as an
internal service, all a client can do is crash its own authenticated
connection.
o CVE-2018-1057:
On a Samba 4 AD DC the LDAP server in all versions of Samba from
4.0.0 onwards incorrectly validates permissions to modify passwords
over LDAP allowing authenticated users to change any other users'
passwords, including administrative users.
Possible workarounds are described at a dedicated page in the Samba wiki:
https://wiki.samba.org/index.php/CVE-2018-1057
Changes since 4.5.15:
---------------------
o Jeremy Allison <jra@samba.org>
* BUG 11343: CVE-2018-1050: Codenomicon crashes in spoolss server code.
o Ralph Boehme <slow@samba.org>
* BUG 13272: CVE-2018-1057: Unprivileged user can change any user (and admin)
password.
o Stefan Metzmacher <metze@samba.org>
* BUG 13272: CVE-2018-1057: Unprivileged user can change any user (and admin)
password.
#######################################
Reporting bugs & Development Discussion
#######################################
Please discuss this release on the samba-technical mailing list or by
joining the #samba-technical IRC channel on irc.freenode.net.
If you do report problems then please try to send high quality
feedback. If you don't provide vital information to help us track down
the problem then you will probably be ignored. All bug reports should
be filed under the "Samba 4.1 and newer" product in the project's Bugzilla
database (https://bugzilla.samba.org/).
======================================================================
== Our Code, Our Bugs, Our Responsibility.
== The Samba Team
======================================================================
Release notes for older releases follow:
----------------------------------------
==============================
Release Notes for Samba 4.5.15
November 21, 2017
@ -66,8 +142,8 @@ database (https://bugzilla.samba.org/).
======================================================================
Release notes for older releases follow:
----------------------------------------
----------------------------------------------------------------------
==============================
Release Notes for Samba 4.5.14

View File

@ -178,6 +178,11 @@ static void prune_printername_cache(void);
static const char *canon_servername(const char *servername)
{
const char *pservername = servername;
if (servername == NULL) {
return "";
}
while (*pservername == '\\') {
pservername++;
}
@ -2073,6 +2078,10 @@ WERROR _spoolss_DeletePrinterDriver(struct pipes_struct *p,
return WERR_ACCESS_DENIED;
}
if (r->in.architecture == NULL || r->in.driver == NULL) {
return WERR_INVALID_ENVIRONMENT;
}
/* check that we have a valid driver name first */
if ((version = get_version_id(r->in.architecture)) == -1) {
@ -2212,6 +2221,10 @@ WERROR _spoolss_DeletePrinterDriverEx(struct pipes_struct *p,
return WERR_ACCESS_DENIED;
}
if (r->in.architecture == NULL || r->in.driver == NULL) {
return WERR_INVALID_ENVIRONMENT;
}
/* check that we have a valid driver name first */
if (get_version_id(r->in.architecture) == -1) {
/* this is what NT returns */

View File

@ -966,11 +966,79 @@ static int acl_check_password_rights(TALLOC_CTX *mem_ctx,
{
int ret = LDB_SUCCESS;
unsigned int del_attr_cnt = 0, add_attr_cnt = 0, rep_attr_cnt = 0;
unsigned int del_val_cnt = 0, add_val_cnt = 0, rep_val_cnt = 0;
struct ldb_message_element *el;
struct ldb_message *msg;
struct ldb_control *c = NULL;
const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
"unicodePwd", "dBCSPwd", NULL }, **l;
"unicodePwd", NULL }, **l;
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
struct dsdb_control_password_acl_validation *pav = NULL;
if (tmp_ctx == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
pav = talloc_zero(req, struct dsdb_control_password_acl_validation);
if (pav == NULL) {
talloc_free(tmp_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
c = ldb_request_get_control(req, DSDB_CONTROL_PASSWORD_CHANGE_OID);
if (c != NULL) {
pav->pwd_reset = false;
/*
* 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.
*
* This control can't be used by real LDAP clients,
* the only caller is samdb_set_password_internal(),
* so we don't have to strict verification of the input.
*/
ret = acl_check_extended_right(tmp_ctx,
sd,
acl_user_token(module),
GUID_DRS_USER_CHANGE_PASSWORD,
SEC_ADS_CONTROL_ACCESS,
sid);
goto checked;
}
c = ldb_request_get_control(req, DSDB_CONTROL_PASSWORD_HASH_VALUES_OID);
if (c != NULL) {
pav->pwd_reset = true;
/*
* The "DSDB_CONTROL_PASSWORD_HASH_VALUES_OID" control, without
* "DSDB_CONTROL_PASSWORD_CHANGE_OID" control means that we
* have a force password set.
* This control is used by the SAMR/NETLOGON/LSA password
* reset mechanisms.
*
* This control can't be used by real LDAP clients,
* the only caller is samdb_set_password_internal(),
* so we don't have to strict verification of the input.
*/
ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
GUID_DRS_FORCE_CHANGE_PASSWORD,
SEC_ADS_CONTROL_ACCESS,
sid);
goto checked;
}
el = ldb_msg_find_element(req->op.mod.message, "dBCSPwd");
if (el != NULL) {
/*
* dBCSPwd is only allowed with a control.
*/
talloc_free(tmp_ctx);
return LDB_ERR_UNWILLING_TO_PERFORM;
}
msg = ldb_msg_copy_shallow(tmp_ctx, req->op.mod.message);
if (msg == NULL) {
@ -984,12 +1052,15 @@ static int acl_check_password_rights(TALLOC_CTX *mem_ctx,
while ((el = ldb_msg_find_element(msg, *l)) != NULL) {
if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
++del_attr_cnt;
del_val_cnt += el->num_values;
}
if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_ADD) {
++add_attr_cnt;
add_val_cnt += el->num_values;
}
if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_REPLACE) {
++rep_attr_cnt;
rep_val_cnt += el->num_values;
}
ldb_msg_remove_element(msg, el);
}
@ -1002,26 +1073,30 @@ static int acl_check_password_rights(TALLOC_CTX *mem_ctx,
return LDB_SUCCESS;
}
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)) {
if (rep_attr_cnt > 0) {
pav->pwd_reset = true;
ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
GUID_DRS_FORCE_CHANGE_PASSWORD,
SEC_ADS_CONTROL_ACCESS,
sid);
goto checked;
}
else if (add_attr_cnt == 1 && del_attr_cnt == 1) {
if (add_attr_cnt != del_attr_cnt) {
pav->pwd_reset = true;
ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
GUID_DRS_FORCE_CHANGE_PASSWORD,
SEC_ADS_CONTROL_ACCESS,
sid);
goto checked;
}
if (add_val_cnt == 1 && del_val_cnt == 1) {
pav->pwd_reset = false;
ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
GUID_DRS_USER_CHANGE_PASSWORD,
SEC_ADS_CONTROL_ACCESS,
@ -1030,17 +1105,53 @@ static int acl_check_password_rights(TALLOC_CTX *mem_ctx,
if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
ret = LDB_ERR_CONSTRAINT_VIOLATION;
}
goto checked;
}
if (add_val_cnt == 1 && del_val_cnt == 0) {
pav->pwd_reset = true;
ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
GUID_DRS_FORCE_CHANGE_PASSWORD,
SEC_ADS_CONTROL_ACCESS,
sid);
/* Very strange, but we get constraint violation in this case */
if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
ret = LDB_ERR_CONSTRAINT_VIOLATION;
}
goto checked;
}
/*
* Everything else is handled by the password_hash module where it will
* fail, but with the correct error code when the module is again
* checking the attributes. As the change request will lack the
* DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID control, we can be sure that
* any modification attempt that went this way will be rejected.
*/
talloc_free(tmp_ctx);
return LDB_SUCCESS;
checked:
if (ret != LDB_SUCCESS) {
dsdb_acl_debug(sd, acl_user_token(module),
req->op.mod.message->dn,
true,
10);
talloc_free(tmp_ctx);
return ret;
}
talloc_free(tmp_ctx);
return ret;
}
ret = ldb_request_add_control(req,
DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID, false, pav);
if (ret != LDB_SUCCESS) {
ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR,
"Unable to register ACL validation control!\n");
return ret;
}
return LDB_SUCCESS;
}
static int acl_modify(struct ldb_module *module, struct ldb_request *req)
{
@ -1055,6 +1166,7 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req)
struct ldb_control *as_system;
struct ldb_control *is_undelete;
bool userPassword;
bool password_rights_checked = false;
TALLOC_CTX *tmp_ctx;
const struct ldb_message *msg = req->op.mod.message;
static const char *acl_attrs[] = {
@ -1200,6 +1312,9 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req)
} else if (ldb_attr_cmp("unicodePwd", el->name) == 0 ||
(userPassword && ldb_attr_cmp("userPassword", el->name) == 0) ||
ldb_attr_cmp("clearTextPassword", el->name) == 0) {
if (password_rights_checked) {
continue;
}
ret = acl_check_password_rights(tmp_ctx,
module,
req,
@ -1210,6 +1325,7 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req)
if (ret != LDB_SUCCESS) {
goto fail;
}
password_rights_checked = true;
} else if (ldb_attr_cmp("servicePrincipalName", el->name) == 0) {
ret = acl_check_spn(tmp_ctx,
module,

View File

@ -3188,7 +3188,35 @@ static int setup_io(struct ph_context *ac,
/* On "add" we have only "password reset" */
ac->pwd_reset = true;
} else if (ac->req->operation == LDB_MODIFY) {
if (io->og.cleartext_utf8 || io->og.cleartext_utf16
struct ldb_control *pav_ctrl = NULL;
struct dsdb_control_password_acl_validation *pav = NULL;
pav_ctrl = ldb_request_get_control(ac->req,
DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID);
if (pav_ctrl != NULL) {
pav = talloc_get_type_abort(pav_ctrl->data,
struct dsdb_control_password_acl_validation);
}
if (pav == NULL && ac->update_password) {
bool ok;
/*
* If the DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID
* control is missing, we require system access!
*/
ok = dsdb_module_am_system(ac->module);
if (!ok) {
return ldb_module_operr(ac->module);
}
}
if (pav != NULL) {
/*
* We assume what the acl module has validated.
*/
ac->pwd_reset = pav->pwd_reset;
} else if (io->og.cleartext_utf8 || io->og.cleartext_utf16
|| io->og.nt_hash || io->og.lm_hash) {
/* If we have an old password specified then for sure it
* is a user "password change" */
@ -3921,25 +3949,26 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
}
while ((passwordAttr = ldb_msg_find_element(msg, *l)) != NULL) {
if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE) {
unsigned int mtype = LDB_FLAG_MOD_TYPE(passwordAttr->flags);
unsigned int nvalues = passwordAttr->num_values;
if (mtype == LDB_FLAG_MOD_DELETE) {
++del_attr_cnt;
}
if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD) {
if (mtype == LDB_FLAG_MOD_ADD) {
++add_attr_cnt;
}
if (LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_REPLACE) {
if (mtype == LDB_FLAG_MOD_REPLACE) {
++rep_attr_cnt;
}
if ((passwordAttr->num_values != 1) &&
(LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_ADD)) {
if ((nvalues != 1) && (mtype == LDB_FLAG_MOD_ADD)) {
talloc_free(ac);
ldb_asprintf_errstring(ldb,
"'%s' attribute must have exactly one value on add operations!",
*l);
return LDB_ERR_CONSTRAINT_VIOLATION;
}
if ((passwordAttr->num_values > 1) &&
(LDB_FLAG_MOD_TYPE(passwordAttr->flags) == LDB_FLAG_MOD_DELETE)) {
if ((nvalues > 1) && (mtype == LDB_FLAG_MOD_DELETE)) {
talloc_free(ac);
ldb_asprintf_errstring(ldb,
"'%s' attribute must have zero or one value(s) on delete operations!",

View File

@ -182,6 +182,15 @@ struct dsdb_control_password_user_account_control {
/* passed when we want to thoroughly delete linked attributes */
#define DSDB_CONTROL_REPLMD_VANISH_LINKS "1.3.6.1.4.1.7165.4.3.29"
/*
* Used to pass "user password change" vs "password reset" from the ACL to the
* password_hash module, ensuring both modules treat the request identical.
*/
#define DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID "1.3.6.1.4.1.7165.4.3.33"
struct dsdb_control_password_acl_validation {
bool pwd_reset;
};
#define DSDB_EXTENDED_REPLICATED_OBJECTS_OID "1.3.6.1.4.1.7165.4.4.1"
struct dsdb_extended_replicated_object {
struct ldb_message *msg;

View File

@ -1020,6 +1020,55 @@ userPassword: thatsAcomplPASS4
# Reset the "minPwdLength" as it was before
self.ldb.set_minPwdLength(minPwdLength)
def test_pw_change_delete_no_value_userPassword(self):
"""Test password change with userPassword where the delete attribute doesn't have a value"""
try:
self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
add: userPassword
userPassword: thatsAcomplPASS1
""")
except LdbError, (num, msg):
self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
else:
self.fail()
def test_pw_change_delete_no_value_clearTextPassword(self):
"""Test password change with clearTextPassword where the delete attribute doesn't have a value"""
try:
self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: clearTextPassword
add: clearTextPassword
clearTextPassword: thatsAcomplPASS2
""")
except LdbError, (num, msg):
self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
else:
self.fail()
def test_pw_change_delete_no_value_unicodePwd(self):
"""Test password change with unicodePwd where the delete attribute doesn't have a value"""
try:
self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: unicodePwd
add: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
""")
except LdbError, (num, msg):
self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
else:
self.fail()
def tearDown(self):
super(PasswordTests, self).tearDown()
delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)

View File

@ -1262,6 +1262,7 @@ static const struct ldap_control_handler ldap_known_controls[] = {
{ DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID, NULL, NULL },
{ DSDB_CONTROL_PASSWORD_HASH_VALUES_OID, NULL, NULL },
{ DSDB_CONTROL_PASSWORD_CHANGE_OID, NULL, NULL },
{ DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID, NULL, NULL },
{ DSDB_CONTROL_APPLY_LINKS, NULL, NULL },
{ LDB_CONTROL_BYPASS_OPERATIONAL_OID, NULL, NULL },
{ DSDB_CONTROL_CHANGEREPLMETADATA_OID, NULL, NULL },

View File

@ -217,6 +217,8 @@
#Allocated: DSDB_CONTROL_PASSWORD_USER_ACCOUNT_CONTROL_OID 1.3.6.1.4.1.7165.4.3.27
#Allocated: DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID 1.3.6.1.4.1.7165.4.3.28
#Allocated: DSDB_CONTROL_REPLMD_VANISH_LINKS 1.3.6.1.4.1.7165.4.3.29
#Allocated: DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID 1.3.6.1.4.1.7165.4.3.33
# Extended 1.3.6.1.4.1.7165.4.4.x
#Allocated: DSDB_EXTENDED_REPLICATED_OBJECTS_OID 1.3.6.1.4.1.7165.4.4.1