1
0
mirror of https://github.com/altlinux/admc.git synced 2025-03-23 02:50:17 +03:00

Merge pull request #284 from altlinux/feature/protect-against-deletion

Feature/protect against deletion
This commit is contained in:
Dmitry Degtyarev 2021-08-20 15:30:07 +03:00 committed by GitHub
commit e22f521f81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 316 additions and 0 deletions

View File

@ -100,6 +100,7 @@ Tests for ADMC
%_bindir/admc_test_delegation_edit
%_bindir/admc_test_string_other_edit
%_bindir/admc_test_account_option_edit
%_bindir/admc_test_protect_deletion_edit
%_bindir/admc_test_gpoptions_edit
%_bindir/admc_test_octet_editor
%_bindir/admc_test_bool_editor

View File

@ -244,6 +244,8 @@ enum AcePermission {
AcePermission_FullControl,
AcePermission_Read,
AcePermission_Write,
AcePermission_Delete,
AcePermission_DeleteSubtree,
AcePermission_CreateChild,
AcePermission_DeleteChild,
AcePermission_AllowedToAuthenticate,

View File

@ -113,6 +113,8 @@ const QHash<AcePermission, uint32_t> ace_permission_to_mask_map = {
{AcePermission_Read, (SEC_STD_READ_CONTROL | SEC_ADS_LIST | SEC_ADS_READ_PROP)},
// {AcePermission_Write, SEC_ADS_GENERIC_WRITE},
{AcePermission_Write, (SEC_ADS_SELF_WRITE | SEC_ADS_WRITE_PROP)},
{AcePermission_Delete, SEC_STD_DELETE},
{AcePermission_DeleteSubtree, SEC_DIR_DELETE_CHILD},
{AcePermission_CreateChild, SEC_ADS_CREATE_CHILD},
{AcePermission_DeleteChild, SEC_ADS_DELETE_CHILD},
{AcePermission_AllowedToAuthenticate, SEC_ADS_CONTROL_ACCESS},
@ -458,6 +460,14 @@ QByteArray dom_sid_to_bytes(const dom_sid &sid) {
return bytes;
}
QByteArray dom_sid_string_to_bytes(const QString &string) {
dom_sid sid;
dom_sid_parse(cstr(string), &sid);
const QByteArray bytes = dom_sid_to_bytes(sid);
return bytes;
}
void ad_security_sort_dacl(security_descriptor *sd) {
qsort(sd->dacl->aces, sd->dacl->num_aces, sizeof(security_ace), ace_compare);
}
@ -600,3 +610,43 @@ QHash<QByteArray, QHash<AcePermission, PermissionState>> ad_security_get_state_f
return out;
}
bool ad_security_get_protected_against_deletion(const AdObject &object, AdConfig *adconfig) {
QHash<QByteArray, QHash<AcePermission, PermissionState>> permissions = object.get_security_state(adconfig);
const QByteArray world_trustee = dom_sid_string_to_bytes(SID_WORLD);
const PermissionState delete_state = permissions[world_trustee][AcePermission_Delete];
const PermissionState delete_subtree_state = permissions[world_trustee][AcePermission_DeleteSubtree];
const bool out = ((delete_state == PermissionState_Denied) && (delete_subtree_state == PermissionState_Denied));
return out;
}
bool ad_security_set_protected_against_deletion(AdInterface &ad, const QString dn, AdConfig *adconfig, const bool enabled) {
const AdObject object = ad.search_object(dn);
const QHash<QByteArray, QHash<AcePermission, PermissionState>> old_permissions = object.get_security_state(adconfig);
const QHash<QByteArray, QHash<AcePermission, PermissionState>> new_permissions = [&]() {
QHash<QByteArray, QHash<AcePermission, PermissionState>> out;
const QByteArray world_trustee = dom_sid_string_to_bytes(SID_WORLD);
const PermissionState state = [&]() {
if (enabled) {
return PermissionState_Denied;
} else {
return PermissionState_None;
}
}();
out = old_permissions;
out = ad_security_modify(out, world_trustee, AcePermission_Delete, state);
out = ad_security_modify(out, world_trustee, AcePermission_DeleteSubtree, state);
return out;
}();
const bool apply_success = attribute_replace_security_descriptor(&ad, dn, new_permissions);
return apply_success;
}

View File

@ -53,6 +53,8 @@ QString ad_security_get_trustee_name(AdInterface &ad, const QByteArray &trustee)
bool attribute_replace_security_descriptor(AdInterface *ad, const QString &dn, const QHash<QByteArray, QHash<AcePermission, PermissionState>> &descriptor_state_arg);
QList<QByteArray> ad_security_get_trustee_list_from_object(const AdObject &object);
QHash<QByteArray, QHash<AcePermission, PermissionState>> ad_security_get_state_from_sd(security_descriptor *sd, AdConfig *adconfig);
bool ad_security_get_protected_against_deletion(const AdObject &object, AdConfig *config);
bool ad_security_set_protected_against_deletion(AdInterface &ad, const QString dn, AdConfig *config, const bool enabled);
// NOTE: have to talloc_free() returned sd
security_descriptor *ad_security_get_sd(const AdObject &object);
@ -60,5 +62,6 @@ security_descriptor *ad_security_get_sd(const AdObject &object);
void ad_security_sort_dacl(security_descriptor *sd);
QByteArray dom_sid_to_bytes(const dom_sid &sid);
QByteArray dom_sid_string_to_bytes(const dom_sid &sid);
#endif /* AD_SECURITY_H */

View File

@ -144,6 +144,7 @@ set(ADMC_SOURCES
edits/delegation_edit.cpp
edits/logon_hours_edit.cpp
edits/logon_computers_edit.cpp
edits/protect_deletion_edit.cpp
console_widget/console_widget.cpp
console_widget/scope_proxy_model.cpp

View File

@ -27,6 +27,7 @@
#include "edits/password_edit.h"
#include "edits/string_edit.h"
#include "edits/upn_edit.h"
#include "edits/protect_deletion_edit.h"
#include "globals.h"
#include "settings.h"
#include "status.h"
@ -188,6 +189,10 @@ CreateObjectDialog::CreateObjectDialog(const QString &parent_dn_arg, const QStri
});
} else if (object_class == CLASS_OU) {
edits_layout->addRow(tr("Name:"), name_edit);
auto protect_deletion_edit = new ProtectDeletionEdit(&all_edits, this);
protect_deletion_edit->set_enabled(true);
protect_deletion_edit->add_to_layout(edits_layout);
} else {
qWarning() << "Class" << object_class << "is unsupported by create dialog";
return;

View File

@ -0,0 +1,68 @@
/*
* ADMC - AD Management Center
*
* Copyright (C) 2020-2021 BaseALT Ltd.
* Copyright (C) 2020-2021 Dmitry Degtyarev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "edits/protect_deletion_edit.h"
#include "adldap.h"
#include "utils.h"
#include "globals.h"
#include <QCheckBox>
#include <QFormLayout>
// Object is protected from deletion if it denies
// permissions for "delete" and "delete subtree" for
// "WORLD"(everyone) trustee
ProtectDeletionEdit::ProtectDeletionEdit(QList<AttributeEdit *> *edits_out, QObject *parent)
: AttributeEdit(edits_out, parent) {
check = new QCheckBox(tr("Protect against deletion"));
connect(
check, &QCheckBox::stateChanged,
[this]() {
emit edited();
});
}
void ProtectDeletionEdit::set_enabled(const bool enabled) {
check->setChecked(enabled);
}
void ProtectDeletionEdit::load_internal(AdInterface &ad, const AdObject &object) {
const bool enabled = ad_security_get_protected_against_deletion(object, g_adconfig);
check->setChecked(enabled);
}
void ProtectDeletionEdit::set_read_only(const bool read_only) {
check->setDisabled(read_only);
}
void ProtectDeletionEdit::add_to_layout(QFormLayout *layout) {
layout->addRow(check);
}
bool ProtectDeletionEdit::apply(AdInterface &ad, const QString &dn) const {
const bool enabled = check->isChecked();
const bool apply_success = ad_security_set_protected_against_deletion(ad, dn, g_adconfig, enabled);
return apply_success;
}

View File

@ -0,0 +1,45 @@
/*
* ADMC - AD Management Center
*
* Copyright (C) 2020-2021 BaseALT Ltd.
* Copyright (C) 2020-2021 Dmitry Degtyarev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROTECT_DELETION_EDIT_H
#define PROTECT_DELETION_EDIT_H
/**
* Checkbox edit that modifies ACL to protect (or not)
* against deletion by denying/allowing deletion.
*/
#include "edits/attribute_edit.h"
class QCheckBox;
class ProtectDeletionEdit final : public AttributeEdit {
Q_OBJECT
public:
ProtectDeletionEdit(QList<AttributeEdit *> *edits_out, QObject *parent);
DECL_ATTRIBUTE_EDIT_VIRTUALS();
void set_enabled(const bool enabled);
private:
QCheckBox *check;
};
#endif /* PROTECT_DELETION_EDIT_H */

View File

@ -23,6 +23,7 @@
#include "adldap.h"
#include "edits/datetime_edit.h"
#include "edits/string_edit.h"
#include "edits/protect_deletion_edit.h"
#include <QFormLayout>
@ -38,6 +39,8 @@ ObjectTab::ObjectTab() {
edits_set_read_only(edits, true);
new ProtectDeletionEdit(&edits, this);
edits_connect_to_tab(edits, this);
const auto layout = new QFormLayout();

View File

@ -47,6 +47,8 @@ const QHash<AcePermission, QString> ace_permission_to_name_map = {
{AcePermission_FullControl, QCoreApplication::translate("Security", "Full control")},
{AcePermission_Read, QCoreApplication::translate("Security", "Read")},
{AcePermission_Write, QCoreApplication::translate("Security", "Write")},
{AcePermission_Delete, QCoreApplication::translate("Security", "Delete")},
{AcePermission_DeleteSubtree, QCoreApplication::translate("Security", "Delete subtree")},
{AcePermission_CreateChild, QCoreApplication::translate("Security", "Create child")},
{AcePermission_DeleteChild, QCoreApplication::translate("Security", "Delete child")},
{AcePermission_AllowedToAuthenticate, QCoreApplication::translate("Security", "Allowed to authenticate")},

View File

@ -59,6 +59,7 @@ set(TEST_TARGETS
admc_test_string_other_edit
admc_test_account_option_edit
admc_test_gpoptions_edit
admc_test_protect_deletion_edit
admc_test_octet_editor
admc_test_bool_editor
admc_test_datetime_editor

View File

@ -0,0 +1,91 @@
/*
* ADMC - AD Management Center
*
* Copyright (C) 2020-2021 BaseALT Ltd.
* Copyright (C) 2020-2021 Dmitry Degtyarev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "admc_test_protect_deletion_edit.h"
#include "edits/protect_deletion_edit.h"
#include <QCheckBox>
#include <QFormLayout>
// NOTE: this doesn't really "lock" accounts. Accounts can
// only be locked by the server and lockout time only
// displays the state. Since unlock edit modifies lockout
// time, test it like this.
#define LOCKOUT_LOCKED_VALUE "1"
void ADMCTestProtectDeletionEdit::init() {
ADMCTest::init();
edit = new ProtectDeletionEdit(&edits, parent_widget);
add_attribute_edit(edit);
checkbox = parent_widget->findChild<QCheckBox *>();
QVERIFY(checkbox != nullptr);
dn = test_object_dn(TEST_OU, CLASS_OU);
const bool create_success = ad.object_add(dn, CLASS_OU);
QVERIFY(create_success);
}
// edited() signal should be emitted when checkbox is toggled
void ADMCTestProtectDeletionEdit::emit_edited_signal() {
bool edited_signal_emitted = false;
connect(
edit, &AttributeEdit::edited,
[&edited_signal_emitted]() {
edited_signal_emitted = true;
});
// Check checkbox
checkbox->setChecked(true);
QVERIFY(edited_signal_emitted);
// Unheck checkbox
edited_signal_emitted = false;
checkbox->setChecked(false);
QVERIFY(edited_signal_emitted);
}
// Edit should unlock locked user if checkbox is checked
void ADMCTestProtectDeletionEdit::apply() {
const AdObject object = ad.search_object(dn);
edit->load(ad, object);
// Enable protection against deletion
checkbox->setChecked(true);
const bool apply_success = edit->apply(ad, dn);
QVERIFY(apply_success);
// Try to delete, should fail
qInfo() << "Error relating to \"Insufficient access\" is part of the test";
const bool delete_success = ad.object_delete(dn);
QCOMPARE(delete_success, false);
// Disable protection against deletion
checkbox->setChecked(false);
const bool apply_2_success = edit->apply(ad, dn);
QVERIFY(apply_2_success);
const bool delete_2_success = ad.object_delete(dn);
QCOMPARE(delete_2_success, true);
}
QTEST_MAIN(ADMCTestProtectDeletionEdit)

View File

@ -0,0 +1,44 @@
/*
* ADMC - AD Management Center
*
* Copyright (C) 2020-2021 BaseALT Ltd.
* Copyright (C) 2020-2021 Dmitry Degtyarev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ADMC_TEST_PROTECT_DELETION_EDIT_H
#define ADMC_TEST_PROTECT_DELETION_EDIT_H
#include "admc_test.h"
class ProtectDeletionEdit;
class QCheckBox;
class ADMCTestProtectDeletionEdit : public ADMCTest {
Q_OBJECT
private slots:
void init() override;
void emit_edited_signal();
void apply();
private:
ProtectDeletionEdit *edit;
QCheckBox *checkbox;
QString dn;
};
#endif /* ADMC_TEST_PROTECT_DELETION_EDIT_H */