summaryrefslogtreecommitdiff
path: root/source4/dsdb/samdb/ldb_modules
diff options
context:
space:
mode:
authorNadezhda Ivanova <nivanova@samba.org>2010-12-22 12:27:15 +0200
committerNadezhda Ivanova <nivanova@samba.org>2010-12-22 12:29:14 +0200
commit3ba42be7c178062c2e865d5197a5f3346f6b9a17 (patch)
tree9c7d6c7f841f0813dabe1d33653c1ab0673d41ec /source4/dsdb/samdb/ldb_modules
parent9aaacee6cd19adf615f941771fe9d490b7dae3c4 (diff)
downloadsamba-3ba42be7c178062c2e865d5197a5f3346f6b9a17.tar.gz
samba-3ba42be7c178062c2e865d5197a5f3346f6b9a17.tar.bz2
samba-3ba42be7c178062c2e865d5197a5f3346f6b9a17.zip
s4-acl: Implementation of Validated-SPN validated write
If this right is granted to a user, they may modify the SPN of an object with some value restrictions serviceName can be set only if the object is a DC, and then only to the default domain and netbios name, or ntds_guid._msdsc_.forest_domain. If the serviceType is GC, only to the forest root domain. If the serviceType is ldap, then to forest_domain or netbiosname. InstanceType can be samAccountName or dnsHostName.
Diffstat (limited to 'source4/dsdb/samdb/ldb_modules')
-rw-r--r--source4/dsdb/samdb/ldb_modules/acl.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c
index 7fc626ad51..42e08cd0aa 100644
--- a/source4/dsdb/samdb/ldb_modules/acl.c
+++ b/source4/dsdb/samdb/ldb_modules/acl.c
@@ -41,6 +41,8 @@
#include "dsdb/samdb/ldb_modules/util.h"
#include "dsdb/samdb/ldb_modules/schema.h"
#include "lib/util/tsort.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
struct extended_access_check_attribute {
const char *oa_name;
@@ -431,6 +433,208 @@ static int acl_sDRightsEffective(struct ldb_module *module,
"sDRightsEffective", flags);
}
+static int acl_validate_spn_value(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *spn_value,
+ int userAccountControl,
+ const char *samAccountName,
+ const char *dnsHostName,
+ const char *netbios_name,
+ const char *ntds_guid)
+{
+ int ret;
+ krb5_context krb_ctx;
+ krb5_error_code kerr;
+ krb5_principal principal;
+ char *instanceName;
+ char *serviceType;
+ char *serviceName;
+ const char *realm;
+ const char *guid_str;
+ const char *forest_name = samdb_forest_name(ldb, mem_ctx);
+ const char *base_domain = samdb_default_domain_name(ldb, mem_ctx);
+ struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
+ struct loadparm_context);
+ bool is_dc = (userAccountControl & UF_SERVER_TRUST_ACCOUNT) ||
+ (userAccountControl & UF_PARTIAL_SECRETS_ACCOUNT);
+
+ kerr = smb_krb5_init_context_basic(mem_ctx,
+ lp_ctx,
+ &krb_ctx);
+ if (kerr != 0) {
+ return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
+ "Could not initialize kerberos context.");
+ }
+
+ ret = krb5_parse_name(krb_ctx, spn_value, &principal);
+ if (ret) {
+ krb5_free_context(krb_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ instanceName = principal->name.name_string.val[1];
+ serviceType = principal->name.name_string.val[0];
+ realm = krb5_principal_get_realm(krb_ctx, principal);
+ guid_str = talloc_asprintf(mem_ctx,"%s._msdcs.%s",
+ ntds_guid,
+ forest_name);
+ if (principal->name.name_string.len == 3) {
+ serviceName = principal->name.name_string.val[2];
+ } else {
+ serviceName = NULL;
+ }
+
+ if (serviceName) {
+ if (!is_dc) {
+ goto fail;
+ }
+ if (strcasecmp(serviceType, "ldap") == 0) {
+ if (strcasecmp(serviceName, netbios_name) != 0 &&
+ strcasecmp(serviceName, forest_name) != 0) {
+ goto fail;
+ }
+
+ } else if (strcasecmp(serviceType, "gc") == 0) {
+ if (strcasecmp(serviceName, forest_name) != 0) {
+ goto fail;
+ }
+ } else {
+ if (strcasecmp(serviceName, base_domain) != 0 &&
+ strcasecmp(serviceName, netbios_name) != 0) {
+ goto fail;
+ }
+ }
+ }
+ /* instanceName can be samAccountName without $ or dnsHostName
+ * or "ntds_guid._msdcs.forest_domain for DC objects */
+ if (strncasecmp(instanceName, samAccountName, strlen(samAccountName - 1)) == 0) {
+ goto success;
+ } else if (strcasecmp(instanceName, dnsHostName) == 0) {
+ goto success;
+ } else if (is_dc) {
+ if (strcasecmp(instanceName, guid_str) == 0) {
+ goto success;
+ }
+ } else {
+ goto fail;
+ }
+fail:
+ krb5_free_principal(krb_ctx, principal);
+ krb5_free_context(krb_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+
+success:
+ krb5_free_principal(krb_ctx, principal);
+ krb5_free_context(krb_ctx);
+ return LDB_SUCCESS;
+}
+
+static int acl_check_spn(TALLOC_CTX *mem_ctx,
+ struct ldb_module *module,
+ struct ldb_request *req,
+ struct security_descriptor *sd,
+ struct dom_sid *sid,
+ const struct GUID *oc_guid,
+ const struct dsdb_attribute *attr)
+{
+ int ret;
+ unsigned int i;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct ldb_result *acl_res;
+ struct ldb_result *netbios_res;
+ struct ldb_message_element *el;
+ struct ldb_dn *partitions_dn = samdb_partitions_dn(ldb, tmp_ctx);
+ int userAccountControl;
+ const char *samAccountName;
+ const char *dnsHostName;
+ const char *netbios_name;
+ const struct GUID *ntds = samdb_ntds_objectGUID(ldb);
+ const char *ntds_guid = GUID_string(tmp_ctx, ntds);
+
+ static const char *acl_attrs[] = {
+ "samAccountName",
+ "dnsHostName",
+ "userAccountControl",
+ NULL
+ };
+ static const char *netbios_attrs[] = {
+ "nETBIOSName",
+ NULL
+ };
+ /* if we have wp, we can do whatever we like */
+ if (acl_check_access_on_attribute(module,
+ tmp_ctx,
+ sd,
+ sid,
+ SEC_ADS_WRITE_PROP,
+ attr) == LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+ }
+
+ ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
+ GUID_DRS_VALIDATE_SPN,
+ SEC_ADS_SELF_WRITE,
+ sid);
+
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ dsdb_acl_debug(sd, acl_user_token(module),
+ req->op.mod.message->dn,
+ true,
+ 10);
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = dsdb_module_search_dn(module, tmp_ctx,
+ &acl_res, req->op.mod.message->dn,
+ acl_attrs,
+ DSDB_FLAG_NEXT_MODULE |
+ DSDB_SEARCH_SHOW_DELETED);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ userAccountControl = ldb_msg_find_attr_as_int(acl_res->msgs[0], "userAccountControl", 0);
+ dnsHostName = ldb_msg_find_attr_as_string(acl_res->msgs[0], "dnsHostName", NULL);
+ samAccountName = ldb_msg_find_attr_as_string(acl_res->msgs[0], "samAccountName", NULL);
+
+ ret = dsdb_module_search(module, tmp_ctx,
+ &netbios_res, partitions_dn,
+ LDB_SCOPE_ONELEVEL,
+ netbios_attrs,
+ DSDB_FLAG_NEXT_MODULE,
+ "(ncName=%s)",
+ ldb_dn_get_linearized(ldb_get_default_basedn(ldb)));
+
+ netbios_name = ldb_msg_find_attr_as_string(netbios_res->msgs[0], "nETBIOSName", NULL);
+
+ el = ldb_msg_find_element(req->op.mod.message, "servicePrincipalName");
+ if (!el) {
+ return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
+ "Error finding element for servicePrincipalName.");
+ }
+
+ for (i=0; i < el->num_values; i++) {
+ ret = acl_validate_spn_value(tmp_ctx,
+ ldb,
+ (char *)el->values[i].data,
+ userAccountControl,
+ samAccountName,
+ dnsHostName,
+ netbios_name,
+ ntds_guid);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+}
+
static int acl_add(struct ldb_module *module, struct ldb_request *req)
{
int ret;
@@ -758,6 +962,17 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req)
if (ret != LDB_SUCCESS) {
goto fail;
}
+ } else if (ldb_attr_cmp("servicePrincipalName", req->op.mod.message->elements[i].name) == 0) {
+ ret = acl_check_spn(tmp_ctx,
+ module,
+ req,
+ sd,
+ sid,
+ guid,
+ attr);
+ if (ret != LDB_SUCCESS) {
+ goto fail;
+ }
} else {
/* This basic attribute existence check with the right errorcode