Skip to content
Commits on Source (20)
samba (2:4.7.4+dfsg-2) UNRELEASED; urgency=medium
samba (2:4.7.4+dfsg-2) unstable; urgency=high
[ Mathieu Parent ]
* This is a security release in order to address the following defects:
- CVE-2018-1050: Codenomicon crashes in spoolss server code
- CVE-2018-1057: Unprivileged user can change any user (and admin) password
* Fix "/etc/dhcp/dhclient-enter-hooks.d/samba returned non-zero exit status 1"
when samba.service is disabled
- Pick patch from Ubuntu for Launchpad #1579597
- Fix systemd check
* Replace override_dh_systemd_start by override_dh_installsystemd (Fixes
ctdb starting on install and restarting on upgrade since update to debhelper
compat 11)
* Replace --no-restart-on-upgrade by --no-stop-on-upgrade in dh_installinit
and dh_installsystemd
* Add missing dh_installsystemd calls to ensure that services are properly
unmasked and enabled in postinst
[ Andreas Hasenack ]
* Add extra DEP8 tests to samba (Closes: #890439):
......@@ -10,13 +26,7 @@ samba (2:4.7.4+dfsg-2) UNRELEASED; urgency=medium
- d/t/control, d/t/smbclient-share-access: create a share and download a
file from it
[ Mathieu Parent ]
* Fix "/etc/dhcp/dhclient-enter-hooks.d/samba returned non-zero exit status 1"
when samba.service is disabled
- Pick patch from Ubuntu for Launchpad #1579597
- Fix systemd check
-- Mathieu Parent <sathieu@debian.org> Thu, 01 Mar 2018 21:51:45 +0100
-- Mathieu Parent <sathieu@debian.org> Fri, 02 Mar 2018 20:55:06 +0100
samba (2:4.7.4+dfsg-1) unstable; urgency=medium
......
From 214bc22c7c0b87cb4e8c7713780f692b730a5509 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Tue, 2 Jan 2018 15:56:03 -0800
Subject: [PATCH] CVE-2018-1050: s3: RPC: spoolss server. Protect against null
pointer derefs.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=11343
Signed-off-by: Jeremy Allison <jra@samba.org>
---
source3/rpc_server/spoolss/srv_spoolss_nt.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/source3/rpc_server/spoolss/srv_spoolss_nt.c b/source3/rpc_server/spoolss/srv_spoolss_nt.c
index fcc491e7662..3d99470b3d2 100644
--- a/source3/rpc_server/spoolss/srv_spoolss_nt.c
+++ b/source3/rpc_server/spoolss/srv_spoolss_nt.c
@@ -142,6 +142,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++;
}
@@ -2042,6 +2047,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) {
@@ -2181,6 +2190,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 */
--
2.11.0
This diff is collapsed.
......@@ -11,3 +11,5 @@ systemd-syslog.target-is-obsolete.patch
Add-documentation-to-systemd-Unit-files.patch
fix_kill_path_in_units.patch
nmbd-requires-a-working-network.patch
CVE-2018-1050-11343-4.7.patch
CVE-2018-1057-v4-7.metze01.patches.txt
......@@ -105,8 +105,16 @@ override_dh_auto_test:
# Running make test requires configuration with --enable-selftest, which
# we don't want to do for production systems.
override_dh_systemd_start:
dh_systemd_start -pctdb --no-start --no-restart-on-upgrade
override_dh_installsystemd:
ifneq (,$(filter samba, $(shell dh_listpackages)))
dh_installsystemd -psamba
endif
ifneq (,$(filter winbind, $(shell dh_listpackages)))
dh_installsystemd -pwinbind
endif
ifneq (,$(filter ctdb, $(shell dh_listpackages)))
dh_installsystemd -pctdb --no-start --no-stop-on-upgrade
endif
override_dh_installdocs-arch:
cp ctdb/config/events.d/README ctdb/README.eventscripts
......@@ -218,7 +226,7 @@ ifneq (,$(filter ctdb, $(shell dh_listpackages)))
mkdir -p $(CURDIR)/debian/ctdb/etc/init.d
install -m755 ctdb/config/ctdb.init $(CURDIR)/debian/ctdb/etc/init.d/ctdb
# Install dh scripts
dh_installinit -pctdb --no-start --no-restart-on-upgrade --onlyscripts
dh_installinit -pctdb --no-start --no-stop-on-upgrade --onlyscripts
endif
override_dh_shlibdeps:
......
......@@ -142,6 +142,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++;
}
......@@ -2042,6 +2047,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) {
......@@ -2181,6 +2190,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 */
......
......@@ -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. */
if (rep_attr_cnt > 0) {
pav->pwd_reset = true;
ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
GUID_DRS_USER_CHANGE_PASSWORD,
GUID_DRS_FORCE_CHANGE_PASSWORD,
SEC_ADS_CONTROL_ACCESS,
sid);
goto checked;
}
else if (rep_attr_cnt > 0 || (add_attr_cnt != del_attr_cnt)) {
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;
}
else if (add_attr_cnt == 1 && del_attr_cnt == 1) {
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,
......
......@@ -3500,7 +3500,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" */
......@@ -4234,25 +4262,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!",
......
......@@ -194,6 +194,15 @@ struct dsdb_control_password_user_account_control {
#define DSDB_CONTROL_INVALID_NOT_IMPLEMENTED "1.3.6.1.4.1.7165.4.3.32"
/*
* 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;
......
......@@ -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)
......
......@@ -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 },
......
......@@ -226,6 +226,7 @@
#Allocated: LDB_CONTROL_RECALCULATE_RDN_OID 1.3.6.1.4.1.7165.4.3.30
#Allocated: DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE 1.3.6.1.4.1.7165.4.3.31
#Allocated: DSDB_CONTROL_INVALID_NOT_IMPLEMENTED 1.3.6.1.4.1.7165.4.3.32
#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
......