/* check that the SPN update should be allowed as an override via sam_ctx_system This is only called if the client is not a domain controller or administrator */ static bool writespn_check_spn(struct drsuapi_bind_state *b_state, struct dcesrv_call_state *dce_call, struct ldb_dn *dn, const char *spn) { /* * we only allow SPN updates if: * * 1) they are on the clients own account object * 2) they are of the form SERVICE/dnshostname */ struct dom_sid *user_sid, *sid; TALLOC_CTX *tmp_ctx = talloc_new(dce_call); struct ldb_result *res; const char *attrs[] = { "objectSID", "dNSHostName", NULL }; int ret; krb5_context krb_ctx; krb5_error_code kerr; krb5_principal principal; const krb5_data *component; const char *dns_name, *dnsHostName; /* The service principal name shouldn't be NULL */ if (spn == NULL) { talloc_free(tmp_ctx); return false; } /* get the objectSid of the DN that is being modified, and check it matches the user_sid in their token */ ret = dsdb_search_dn(b_state->sam_ctx, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_ONE_ONLY); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return false; } user_sid = &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX]; sid = samdb_result_dom_sid(tmp_ctx, res->msgs[0], "objectSid"); if (sid == NULL) { talloc_free(tmp_ctx); return false; } dnsHostName = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL); if (dnsHostName == NULL) { talloc_free(tmp_ctx); return false; } if (!dom_sid_equal(sid, user_sid)) { talloc_free(tmp_ctx); return false; } kerr = smb_krb5_init_context_basic(tmp_ctx, dce_call->conn->dce_ctx->lp_ctx, &krb_ctx); if (kerr != 0) { talloc_free(tmp_ctx); return false; } ret = krb5_parse_name_flags(krb_ctx, spn, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal); if (kerr != 0) { krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return false; } if (krb5_princ_size(krb_ctx, principal) != 2) { krb5_free_principal(krb_ctx, principal); krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return false; } component = krb5_princ_component(krb_ctx, principal, 1); dns_name = (const char *)component->data; if (strcasecmp(dns_name, dnsHostName) != 0) { krb5_free_principal(krb_ctx, principal); krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return false; } /* its a simple update on their own account - allow it with * permissions override */ krb5_free_principal(krb_ctx, principal); krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return true; }
/* Add a user, SAMR style, including the correct transaction * semantics. Used by the SAMR server and by pdb_samba4 */ NTSTATUS dsdb_add_user(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *account_name, uint32_t acct_flags, const struct dom_sid *forced_sid, struct dom_sid **sid, struct ldb_dn **dn) { const char *name; struct ldb_message *msg; int ret; const char *container, *obj_class=NULL; char *cn_name; size_t cn_name_len; const char *attrs[] = { "objectSid", "userAccountControl", NULL }; uint32_t user_account_control; struct ldb_dn *account_dn; struct dom_sid *account_sid; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); /* * Start a transaction, so we can query and do a subsequent atomic * modify */ ret = ldb_transaction_start(ldb); if (ret != LDB_SUCCESS) { DEBUG(0,("Failed to start a transaction for user creation: %s\n", ldb_errstring(ldb))); talloc_free(tmp_ctx); return NT_STATUS_LOCK_NOT_GRANTED; } /* check if the user already exists */ name = samdb_search_string(ldb, tmp_ctx, NULL, "sAMAccountName", "(&(sAMAccountName=%s)(objectclass=user))", ldb_binary_encode_string(tmp_ctx, account_name)); if (name != NULL) { ldb_transaction_cancel(ldb); talloc_free(tmp_ctx); return NT_STATUS_USER_EXISTS; } cn_name = talloc_strdup(tmp_ctx, account_name); if (!cn_name) { ldb_transaction_cancel(ldb); talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } cn_name_len = strlen(cn_name); if (cn_name_len < 1) { ldb_transaction_cancel(ldb); talloc_free(tmp_ctx); return NT_STATUS_INVALID_PARAMETER; } msg = ldb_msg_new(tmp_ctx); if (msg == NULL) { ldb_transaction_cancel(ldb); talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } /* This must be one of these values *only* */ if (acct_flags == ACB_NORMAL) { container = "CN=Users"; obj_class = "user"; } else if (acct_flags == ACB_WSTRUST) { if (cn_name[cn_name_len - 1] != '$') { ldb_transaction_cancel(ldb); return NT_STATUS_FOOBAR; } cn_name[cn_name_len - 1] = '\0'; container = "CN=Computers"; obj_class = "computer"; } else if (acct_flags == ACB_SVRTRUST) { if (cn_name[cn_name_len - 1] != '$') { ldb_transaction_cancel(ldb); return NT_STATUS_FOOBAR; } cn_name[cn_name_len - 1] = '\0'; container = "OU=Domain Controllers"; obj_class = "computer"; } else if (acct_flags == ACB_DOMTRUST) { DEBUG(3, ("Invalid account flags specified: cannot create domain trusts via this interface (must use LSA CreateTrustedDomain calls\n")); ldb_transaction_cancel(ldb); talloc_free(tmp_ctx); return NT_STATUS_INVALID_PARAMETER; } else { DEBUG(3, ("Invalid account flags specified 0x%08X, must be exactly one of \n" "ACB_NORMAL (0x%08X) ACB_WSTRUST (0x%08X) or ACB_SVRTRUST (0x%08X)\n", acct_flags, ACB_NORMAL, ACB_WSTRUST, ACB_SVRTRUST)); ldb_transaction_cancel(ldb); talloc_free(tmp_ctx); return NT_STATUS_INVALID_PARAMETER; } /* add core elements to the ldb_message for the user */ msg->dn = ldb_dn_copy(msg, ldb_get_default_basedn(ldb)); if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s,%s", cn_name, container)) { ldb_transaction_cancel(ldb); talloc_free(tmp_ctx); return NT_STATUS_FOOBAR; } ldb_msg_add_string(msg, "sAMAccountName", account_name); ldb_msg_add_string(msg, "objectClass", obj_class); /* This is only here for migrations using pdb_samba4, the * caller and the samldb are responsible for ensuring it makes * sense */ if (forced_sid) { ret = samdb_msg_add_dom_sid(ldb, msg, msg, "objectSID", forced_sid); if (ret != LDB_SUCCESS) { ldb_transaction_cancel(ldb); talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_ERROR; } } /* create the user */ ret = ldb_add(ldb, msg); switch (ret) { case LDB_SUCCESS: break; case LDB_ERR_ENTRY_ALREADY_EXISTS: ldb_transaction_cancel(ldb); DEBUG(0,("Failed to create user record %s: %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(ldb))); talloc_free(tmp_ctx); return NT_STATUS_USER_EXISTS; case LDB_ERR_UNWILLING_TO_PERFORM: case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS: ldb_transaction_cancel(ldb); DEBUG(0,("Failed to create user record %s: %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(ldb))); talloc_free(tmp_ctx); return NT_STATUS_ACCESS_DENIED; default: ldb_transaction_cancel(ldb); DEBUG(0,("Failed to create user record %s: %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(ldb))); talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } account_dn = msg->dn; /* retrieve the sid and account control bits for the user just created */ ret = dsdb_search_one(ldb, tmp_ctx, &msg, account_dn, LDB_SCOPE_BASE, attrs, 0, NULL); if (ret != LDB_SUCCESS) { ldb_transaction_cancel(ldb); DEBUG(0,("Can't locate the account we just created %s: %s\n", ldb_dn_get_linearized(account_dn), ldb_errstring(ldb))); talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid"); if (account_sid == NULL) { ldb_transaction_cancel(ldb); DEBUG(0,("Apparently we failed to get the objectSid of the just created account record %s\n", ldb_dn_get_linearized(msg->dn))); talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } /* Change the account control to be the correct account type. * The default is for a workstation account */ user_account_control = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0); user_account_control = (user_account_control & ~(UF_NORMAL_ACCOUNT | UF_INTERDOMAIN_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT | UF_SERVER_TRUST_ACCOUNT)); user_account_control |= ds_acb2uf(acct_flags); talloc_free(msg); msg = ldb_msg_new(tmp_ctx); if (msg == NULL) { ldb_transaction_cancel(ldb); talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } msg->dn = account_dn; if (samdb_msg_add_uint(ldb, tmp_ctx, msg, "userAccountControl", user_account_control) != LDB_SUCCESS) { ldb_transaction_cancel(ldb); talloc_free(tmp_ctx); return NT_STATUS_NO_MEMORY; } /* modify the samdb record */ ret = dsdb_replace(ldb, msg, 0); if (ret != LDB_SUCCESS) { DEBUG(0,("Failed to modify account record %s to set userAccountControl: %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(ldb))); ldb_transaction_cancel(ldb); talloc_free(tmp_ctx); /* we really need samdb.c to return NTSTATUS */ return NT_STATUS_UNSUCCESSFUL; } ret = ldb_transaction_commit(ldb); if (ret != LDB_SUCCESS) { DEBUG(0,("Failed to commit transaction to add and modify account record %s: %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(ldb))); talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } *dn = talloc_steal(mem_ctx, account_dn); if (sid) { *sid = talloc_steal(mem_ctx, account_sid); } talloc_free(tmp_ctx); return NT_STATUS_OK; }
/* construct the token groups for SAM objects from a message */ static int construct_token_groups(struct ldb_module *module, struct ldb_message *msg, enum ldb_scope scope, struct ldb_request *parent) { struct ldb_context *ldb = ldb_module_get_ctx(module);; TALLOC_CTX *tmp_ctx = talloc_new(msg); unsigned int i; int ret; const char *filter; NTSTATUS status; struct dom_sid *primary_group_sid; const char *primary_group_string; const char *primary_group_dn; DATA_BLOB primary_group_blob; struct dom_sid *account_sid; const char *account_sid_string; const char *account_sid_dn; DATA_BLOB account_sid_blob; struct dom_sid *groupSIDs = NULL; unsigned int num_groupSIDs = 0; struct dom_sid *domain_sid; if (scope != LDB_SCOPE_BASE) { ldb_set_errstring(ldb, "Cannot provide tokenGroups attribute, this is not a BASE search"); return LDB_ERR_OPERATIONS_ERROR; } /* If it's not a user, it won't have a primaryGroupID */ if (ldb_msg_find_element(msg, "primaryGroupID") == NULL) { talloc_free(tmp_ctx); return LDB_SUCCESS; } /* Ensure it has an objectSID too */ account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid"); if (account_sid == NULL) { talloc_free(tmp_ctx); return LDB_SUCCESS; } status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL); if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { talloc_free(tmp_ctx); return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; } else if (!NT_STATUS_IS_OK(status)) { talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } primary_group_sid = dom_sid_add_rid(tmp_ctx, domain_sid, ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0)); if (!primary_group_sid) { talloc_free(tmp_ctx); return ldb_oom(ldb); } /* only return security groups */ filter = talloc_asprintf(tmp_ctx, "(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=%u))", GROUP_TYPE_SECURITY_ENABLED); if (!filter) { talloc_free(tmp_ctx); return ldb_oom(ldb); } primary_group_string = dom_sid_string(tmp_ctx, primary_group_sid); if (!primary_group_string) { talloc_free(tmp_ctx); return ldb_oom(ldb); } primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string); if (!primary_group_dn) { talloc_free(tmp_ctx); return ldb_oom(ldb); } primary_group_blob = data_blob_string_const(primary_group_dn); account_sid_string = dom_sid_string(tmp_ctx, account_sid); if (!account_sid_string) { talloc_free(tmp_ctx); return ldb_oom(ldb); } account_sid_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", account_sid_string); if (!account_sid_dn) { talloc_free(tmp_ctx); return ldb_oom(ldb); } account_sid_blob = data_blob_string_const(account_sid_dn); status = dsdb_expand_nested_groups(ldb, &account_sid_blob, true, /* We don't want to add the object's SID itself, it's not returend in this attribute */ filter, tmp_ctx, &groupSIDs, &num_groupSIDs); if (!NT_STATUS_IS_OK(status)) { ldb_asprintf_errstring(ldb, "Failed to construct tokenGroups: expanding groups of SID %s failed: %s", account_sid_string, nt_errstr(status)); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } /* Expands the primary group - this function takes in * memberOf-like values, so we fake one up with the * <SID=S-...> format of DN and then let it expand * them, as long as they meet the filter - so only * domain groups, not builtin groups */ status = dsdb_expand_nested_groups(ldb, &primary_group_blob, false, filter, tmp_ctx, &groupSIDs, &num_groupSIDs); if (!NT_STATUS_IS_OK(status)) { ldb_asprintf_errstring(ldb, "Failed to construct tokenGroups: expanding groups of SID %s failed: %s", account_sid_string, nt_errstr(status)); talloc_free(tmp_ctx); return LDB_ERR_OPERATIONS_ERROR; } for (i=0; i < num_groupSIDs; i++) { ret = samdb_msg_add_dom_sid(ldb, msg, msg, "tokenGroups", &groupSIDs[i]); if (ret) { talloc_free(tmp_ctx); return ret; } } return LDB_SUCCESS; }