summaryrefslogtreecommitdiffstats
path: root/source4/rpc_server
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /source4/rpc_server
parentInitial commit. (diff)
downloadsamba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz
samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--source4/rpc_server/backupkey/dcesrv_backupkey.c1854
-rw-r--r--source4/rpc_server/browser/dcesrv_browser.c169
-rw-r--r--source4/rpc_server/common/common.h44
-rw-r--r--source4/rpc_server/common/forward.c134
-rw-r--r--source4/rpc_server/common/loadparm.c45
-rw-r--r--source4/rpc_server/common/server_info.c319
-rw-r--r--source4/rpc_server/common/share_info.c123
-rw-r--r--source4/rpc_server/dcerpc_server.c715
-rw-r--r--source4/rpc_server/dcerpc_server.h41
-rw-r--r--source4/rpc_server/dcerpc_server.pc.in12
-rw-r--r--source4/rpc_server/dnsserver/dcerpc_dnsserver.c2431
-rw-r--r--source4/rpc_server/dnsserver/dnsdata.c1121
-rw-r--r--source4/rpc_server/dnsserver/dnsdb.c1272
-rw-r--r--source4/rpc_server/dnsserver/dnsserver.h264
-rw-r--r--source4/rpc_server/dnsserver/dnsutils.c414
-rw-r--r--source4/rpc_server/drsuapi/addentry.c240
-rw-r--r--source4/rpc_server/drsuapi/dcesrv_drsuapi.c1070
-rw-r--r--source4/rpc_server/drsuapi/dcesrv_drsuapi.h84
-rw-r--r--source4/rpc_server/drsuapi/drsutil.c237
-rw-r--r--source4/rpc_server/drsuapi/getncchanges.c3861
-rw-r--r--source4/rpc_server/drsuapi/updaterefs.c411
-rw-r--r--source4/rpc_server/drsuapi/writespn.c257
-rw-r--r--source4/rpc_server/echo/rpc_echo.c211
-rw-r--r--source4/rpc_server/epmapper/rpc_epmapper.c294
-rw-r--r--source4/rpc_server/eventlog/dcesrv_eventlog6.c331
-rw-r--r--source4/rpc_server/lsa/dcesrv_lsa.c4847
-rw-r--r--source4/rpc_server/lsa/lsa.h70
-rw-r--r--source4/rpc_server/lsa/lsa_init.c293
-rw-r--r--source4/rpc_server/lsa/lsa_lookup.c2281
-rw-r--r--source4/rpc_server/netlogon/dcerpc_netlogon.c4631
-rw-r--r--source4/rpc_server/remote/README38
-rw-r--r--source4/rpc_server/remote/dcesrv_remote.c528
-rw-r--r--source4/rpc_server/samr/dcesrv_samr.c5365
-rw-r--r--source4/rpc_server/samr/dcesrv_samr.h88
-rw-r--r--source4/rpc_server/samr/samr_password.c833
-rw-r--r--source4/rpc_server/service_rpc.c220
-rw-r--r--source4/rpc_server/srvsvc/dcesrv_srvsvc.c2300
-rw-r--r--source4/rpc_server/srvsvc/srvsvc_ntvfs.c139
-rw-r--r--source4/rpc_server/tests/rpc_dns_server_dnsutils_test.c304
-rw-r--r--source4/rpc_server/unixinfo/dcesrv_unixinfo.c191
-rw-r--r--source4/rpc_server/winreg/README3
-rw-r--r--source4/rpc_server/winreg/rpc_winreg.c742
-rw-r--r--source4/rpc_server/wkssvc/dcesrv_wkssvc.c403
-rw-r--r--source4/rpc_server/wscript_build221
44 files changed, 39451 insertions, 0 deletions
diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c
new file mode 100644
index 0000000..7c4b9de
--- /dev/null
+++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c
@@ -0,0 +1,1854 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the backupkey interface
+
+ Copyright (C) Matthieu Patou <mat@samba.org> 2010
+ Copyright (C) Andreas Schneider <asn@samba.org> 2015
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "librpc/gen_ndr/ndr_backupkey.h"
+#include "dsdb/common/util.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "../lib/util/util_ldb.h"
+#include "param/param.h"
+#include "auth/session.h"
+#include "system/network.h"
+
+#include "../lib/tsocket/tsocket.h"
+#include "../libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libds/common/roles.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/crypto.h>
+#include <gnutls/abstract.h>
+
+#include "lib/crypto/gnutls_helpers.h"
+
+#undef strncasecmp
+
+#define DCESRV_INTERFACE_BACKUPKEY_BIND(context, iface) \
+ dcesrv_interface_backupkey_bind(context, iface)
+static NTSTATUS dcesrv_interface_backupkey_bind(struct dcesrv_connection_context *context,
+ const struct dcesrv_interface *iface)
+{
+ return dcesrv_interface_bind_require_privacy(context, iface);
+}
+
+static NTSTATUS set_lsa_secret(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *name,
+ const DATA_BLOB *lsa_secret)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct ldb_message *msg;
+ struct ldb_result *res;
+ struct ldb_dn *system_dn = NULL;
+ struct ldb_val val;
+ int ret;
+ char *name2;
+ struct timeval now = timeval_current();
+ NTTIME nt_now = timeval_to_nttime(&now);
+ const char *attrs[] = {
+ NULL
+ };
+
+ msg = ldb_msg_new(frame);
+ if (msg == NULL) {
+ talloc_free(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * This function is a lot like dcesrv_lsa_CreateSecret
+ * in the rpc_server/lsa directory
+ * The reason why we duplicate the effort here is that:
+ * * we want to keep the former function static
+ * * we want to avoid the burden of doing LSA calls
+ * when we can just manipulate the secrets directly
+ * * taillor the function to the particular needs of backup protocol
+ */
+
+ system_dn = samdb_system_container_dn(ldb, frame);
+ if (system_dn == NULL) {
+ talloc_free(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ name2 = talloc_asprintf(msg, "%s Secret", name);
+ if (name2 == NULL) {
+ talloc_free(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_search(ldb, mem_ctx, &res, system_dn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(cn=%s)(objectclass=secret))",
+ ldb_binary_encode_string(mem_ctx, name2));
+
+ if (ret != LDB_SUCCESS || res->count != 0 ) {
+ DEBUG(2, ("Secret %s already exists !\n", name2));
+ talloc_free(frame);
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ /*
+ * We don't care about previous value as we are
+ * here only if the key didn't exists before
+ */
+
+ msg->dn = ldb_dn_copy(frame, system_dn);
+ if (msg->dn == NULL) {
+ talloc_free(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!ldb_dn_add_child_fmt(msg->dn, "cn=%s", name2)) {
+ talloc_free(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_msg_add_string(msg, "cn", name2);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = ldb_msg_add_string(msg, "objectClass", "secret");
+ if (ret != LDB_SUCCESS) {
+ talloc_free(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = samdb_msg_add_uint64(ldb, frame, msg, "priorSetTime", nt_now);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ val.data = lsa_secret->data;
+ val.length = lsa_secret->length;
+ ret = ldb_msg_add_value(msg, "currentValue", &val, NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = samdb_msg_add_uint64(ldb, frame, msg, "lastSetTime", nt_now);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * create the secret with DSDB_MODIFY_RELAX
+ * otherwise dsdb/samdb/ldb_modules/objectclass.c forbid
+ * the create of LSA secret object
+ */
+ ret = dsdb_add(ldb, msg, DSDB_MODIFY_RELAX);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(2,("Failed to create secret record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(ldb)));
+ talloc_free(frame);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ talloc_free(frame);
+ return NT_STATUS_OK;
+}
+
+/* This function is pretty much like dcesrv_lsa_QuerySecret */
+static NTSTATUS get_lsa_secret(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *name,
+ DATA_BLOB *lsa_secret)
+{
+ TALLOC_CTX *tmp_mem;
+ struct ldb_result *res;
+ struct ldb_dn *system_dn = NULL;
+ const struct ldb_val *val;
+ uint8_t *data;
+ const char *attrs[] = {
+ "currentValue",
+ NULL
+ };
+ int ret;
+
+ lsa_secret->data = NULL;
+ lsa_secret->length = 0;
+
+ tmp_mem = talloc_new(mem_ctx);
+ if (tmp_mem == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ system_dn = samdb_system_container_dn(ldb, tmp_mem);
+ if (system_dn == NULL) {
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_search(ldb, mem_ctx, &res, system_dn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(cn=%s Secret)(objectclass=secret))",
+ ldb_binary_encode_string(tmp_mem, name));
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_mem);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (res->count == 0) {
+ talloc_free(tmp_mem);
+ return NT_STATUS_RESOURCE_NAME_NOT_FOUND;
+ }
+ if (res->count > 1) {
+ DEBUG(2, ("Secret %s collision\n", name));
+ talloc_free(tmp_mem);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ val = ldb_msg_find_ldb_val(res->msgs[0], "currentValue");
+ if (val == NULL) {
+ /*
+ * The secret object is here but we don't have the secret value
+ * The most common case is a RODC
+ */
+ *lsa_secret = data_blob_null;
+ talloc_free(tmp_mem);
+ return NT_STATUS_OK;
+ }
+
+ data = val->data;
+ lsa_secret->data = talloc_move(mem_ctx, &data);
+ lsa_secret->length = val->length;
+
+ talloc_free(tmp_mem);
+ return NT_STATUS_OK;
+}
+
+static int reverse_and_get_bignum(TALLOC_CTX *mem_ctx,
+ DATA_BLOB blob,
+ gnutls_datum_t *datum)
+{
+ uint32_t i;
+
+ datum->data = talloc_array(mem_ctx, uint8_t, blob.length);
+ if (datum->data == NULL) {
+ return -1;
+ }
+
+ for(i = 0; i < blob.length; i++) {
+ datum->data[i] = blob.data[blob.length - i - 1];
+ }
+ datum->size = blob.length;
+
+ return 0;
+}
+
+static NTSTATUS get_pk_from_raw_keypair_params(TALLOC_CTX *ctx,
+ struct bkrp_exported_RSA_key_pair *keypair,
+ gnutls_privkey_t *pk)
+{
+ gnutls_x509_privkey_t x509_privkey = NULL;
+ gnutls_privkey_t privkey = NULL;
+ gnutls_datum_t m, e, d, p, q, u, e1, e2;
+ int rc;
+
+ rc = reverse_and_get_bignum(ctx, keypair->modulus, &m);
+ if (rc != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ rc = reverse_and_get_bignum(ctx, keypair->public_exponent, &e);
+ if (rc != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ rc = reverse_and_get_bignum(ctx, keypair->private_exponent, &d);
+ if (rc != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ rc = reverse_and_get_bignum(ctx, keypair->prime1, &p);
+ if (rc != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ rc = reverse_and_get_bignum(ctx, keypair->prime2, &q);
+ if (rc != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ rc = reverse_and_get_bignum(ctx, keypair->coefficient, &u);
+ if (rc != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ rc = reverse_and_get_bignum(ctx, keypair->exponent1, &e1);
+ if (rc != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ rc = reverse_and_get_bignum(ctx, keypair->exponent2, &e2);
+ if (rc != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ rc = gnutls_x509_privkey_init(&x509_privkey);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_privkey_init failed - %s\n",
+ gnutls_strerror(rc));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ rc = gnutls_x509_privkey_import_rsa_raw2(x509_privkey,
+ &m,
+ &e,
+ &d,
+ &p,
+ &q,
+ &u,
+ &e1,
+ &e2);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_privkey_import_rsa_raw2 failed - %s\n",
+ gnutls_strerror(rc));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ rc = gnutls_privkey_init(&privkey);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_privkey_init failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_privkey_deinit(x509_privkey);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ rc = gnutls_privkey_import_x509(privkey,
+ x509_privkey,
+ GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_privkey_import_x509 failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_privkey_deinit(x509_privkey);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ *pk = privkey;
+
+ return NT_STATUS_OK;
+}
+
+static WERROR get_and_verify_access_check(TALLOC_CTX *sub_ctx,
+ uint32_t version,
+ uint8_t *key_and_iv,
+ uint8_t *access_check,
+ uint32_t access_check_len,
+ struct auth_session_info *session_info)
+{
+ struct bkrp_access_check_v2 uncrypted_accesscheckv2;
+ struct bkrp_access_check_v3 uncrypted_accesscheckv3;
+ gnutls_cipher_hd_t cipher_handle = { 0 };
+ gnutls_cipher_algorithm_t cipher_algo;
+ DATA_BLOB blob_us;
+ enum ndr_err_code ndr_err;
+ gnutls_datum_t key;
+ gnutls_datum_t iv;
+
+ struct dom_sid *access_sid = NULL;
+ struct dom_sid *caller_sid = NULL;
+ int rc;
+
+ switch (version) {
+ case 2:
+ cipher_algo = GNUTLS_CIPHER_3DES_CBC;
+ break;
+ case 3:
+ cipher_algo = GNUTLS_CIPHER_AES_256_CBC;
+ break;
+ default:
+ return WERR_INVALID_DATA;
+ }
+
+ key.data = key_and_iv;
+ key.size = gnutls_cipher_get_key_size(cipher_algo);
+
+ iv.data = key_and_iv + key.size;
+ iv.size = gnutls_cipher_get_iv_size(cipher_algo);
+
+ /* Allocate data structure for the plaintext */
+ blob_us = data_blob_talloc_zero(sub_ctx, access_check_len);
+ if (blob_us.data == NULL) {
+ return WERR_INVALID_DATA;
+ }
+
+ rc = gnutls_cipher_init(&cipher_handle,
+ cipher_algo,
+ &key,
+ &iv);
+ if (rc < 0) {
+ DBG_ERR("gnutls_cipher_init failed: %s\n",
+ gnutls_strerror(rc));
+ return WERR_INVALID_DATA;
+ }
+
+ rc = gnutls_cipher_decrypt2(cipher_handle,
+ access_check,
+ access_check_len,
+ blob_us.data,
+ blob_us.length);
+ gnutls_cipher_deinit(cipher_handle);
+ if (rc < 0) {
+ DBG_ERR("gnutls_cipher_decrypt2 failed: %s\n",
+ gnutls_strerror(rc));
+ return WERR_INVALID_DATA;
+ }
+
+ switch (version) {
+ case 2:
+ {
+ uint32_t hash_size = 20;
+ uint8_t hash[hash_size];
+ gnutls_hash_hd_t dig_ctx;
+
+ ndr_err = ndr_pull_struct_blob(&blob_us, sub_ctx, &uncrypted_accesscheckv2,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_access_check_v2);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ /* Unable to unmarshall */
+ return WERR_INVALID_DATA;
+ }
+ if (uncrypted_accesscheckv2.magic != 0x1) {
+ /* wrong magic */
+ return WERR_INVALID_DATA;
+ }
+
+ gnutls_hash_init(&dig_ctx, GNUTLS_DIG_SHA1);
+ gnutls_hash(dig_ctx,
+ blob_us.data,
+ blob_us.length - hash_size);
+ gnutls_hash_deinit(dig_ctx, hash);
+ /*
+ * We free it after the sha1 calculation because blob.data
+ * point to the same area
+ */
+
+ if (!mem_equal_const_time(hash, uncrypted_accesscheckv2.hash, hash_size)) {
+ DEBUG(2, ("Wrong hash value in the access check in backup key remote protocol\n"));
+ return WERR_INVALID_DATA;
+ }
+ access_sid = &(uncrypted_accesscheckv2.sid);
+ break;
+ }
+ case 3:
+ {
+ uint32_t hash_size = 64;
+ uint8_t hash[hash_size];
+ gnutls_hash_hd_t dig_ctx;
+
+ ndr_err = ndr_pull_struct_blob(&blob_us, sub_ctx, &uncrypted_accesscheckv3,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_access_check_v3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ /* Unable to unmarshall */
+ return WERR_INVALID_DATA;
+ }
+ if (uncrypted_accesscheckv3.magic != 0x1) {
+ /* wrong magic */
+ return WERR_INVALID_DATA;
+ }
+
+ gnutls_hash_init(&dig_ctx, GNUTLS_DIG_SHA512);
+ gnutls_hash(dig_ctx,
+ blob_us.data,
+ blob_us.length - hash_size);
+ gnutls_hash_deinit(dig_ctx, hash);
+
+ /*
+ * We free it after the sha1 calculation because blob.data
+ * point to the same area
+ */
+
+ if (!mem_equal_const_time(hash, uncrypted_accesscheckv3.hash, hash_size)) {
+ DEBUG(2, ("Wrong hash value in the access check in backup key remote protocol\n"));
+ return WERR_INVALID_DATA;
+ }
+ access_sid = &(uncrypted_accesscheckv3.sid);
+ break;
+ }
+ default:
+ /* Never reached normally as we filtered at the switch / case level */
+ return WERR_INVALID_DATA;
+ }
+
+ caller_sid = &session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
+
+ if (!dom_sid_equal(caller_sid, access_sid)) {
+ return WERR_INVALID_ACCESS;
+ }
+ return WERR_OK;
+}
+
+/*
+ * We have some data, such as saved website or IMAP passwords that the
+ * client has in profile on-disk. This needs to be decrypted. This
+ * version gives the server the data over the network (protected by
+ * the X.509 certificate and public key encryption, and asks that it
+ * be decrypted returned for short-term use, protected only by the
+ * negotiated transport encryption.
+ *
+ * The data is NOT stored in the LSA, but a X.509 certificate, public
+ * and private keys used to encrypt the data will be stored. There is
+ * only one active encryption key pair and certificate per domain, it
+ * is pointed at with G$BCKUPKEY_PREFERRED in the LSA secrets store.
+ *
+ * The potentially multiple valid decrypting key pairs are in turn
+ * stored in the LSA secrets store as G$BCKUPKEY_keyGuidString.
+ *
+ */
+static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct bkrp_BackupKey *r,
+ struct ldb_context *ldb_ctx)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct bkrp_client_side_wrapped uncrypt_request;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ char *guid_string;
+ char *cert_secret_name;
+ DATA_BLOB lsa_secret;
+ DATA_BLOB *uncrypted_data = NULL;
+ NTSTATUS status;
+ uint32_t requested_version;
+
+ blob.data = r->in.data_in;
+ blob.length = r->in.data_in_len;
+
+ if (r->in.data_in_len < 4 || r->in.data_in == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * We check for the version here, so we can actually print the
+ * message as we are unlikely to parse it with NDR.
+ */
+ requested_version = IVAL(r->in.data_in, 0);
+ if ((requested_version != BACKUPKEY_CLIENT_WRAP_VERSION2)
+ && (requested_version != BACKUPKEY_CLIENT_WRAP_VERSION3)) {
+ DEBUG(1, ("Request for unknown BackupKey sub-protocol %d\n", requested_version));
+ return WERR_INVALID_PARAMETER;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &uncrypt_request,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_wrapped);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if ((uncrypt_request.version != BACKUPKEY_CLIENT_WRAP_VERSION2)
+ && (uncrypt_request.version != BACKUPKEY_CLIENT_WRAP_VERSION3)) {
+ DEBUG(1, ("Request for unknown BackupKey sub-protocol %d\n", uncrypt_request.version));
+ return WERR_INVALID_PARAMETER;
+ }
+
+ guid_string = GUID_string(mem_ctx, &uncrypt_request.guid);
+ if (guid_string == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ cert_secret_name = talloc_asprintf(mem_ctx,
+ "BCKUPKEY_%s",
+ guid_string);
+ if (cert_secret_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = get_lsa_secret(mem_ctx,
+ ldb_ctx,
+ cert_secret_name,
+ &lsa_secret);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Error while fetching secret %s\n", cert_secret_name));
+ return WERR_INVALID_DATA;
+ } else if (lsa_secret.length == 0) {
+ /* we do not have the real secret attribute, like if we are an RODC */
+ return WERR_INVALID_PARAMETER;
+ } else {
+ struct bkrp_exported_RSA_key_pair keypair;
+ gnutls_privkey_t privkey = NULL;
+ gnutls_datum_t reversed_secret;
+ gnutls_datum_t uncrypted_secret;
+ uint32_t i;
+ DATA_BLOB blob_us;
+ WERROR werr;
+ int rc;
+
+ ndr_err = ndr_pull_struct_blob(&lsa_secret, mem_ctx, &keypair, (ndr_pull_flags_fn_t)ndr_pull_bkrp_exported_RSA_key_pair);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(2, ("Unable to parse the ndr encoded cert in key %s\n", cert_secret_name));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ status = get_pk_from_raw_keypair_params(mem_ctx,
+ &keypair,
+ &privkey);
+ if (!NT_STATUS_IS_OK(status)) {
+ return WERR_INTERNAL_ERROR;
+ }
+
+ reversed_secret.data = talloc_array(mem_ctx, uint8_t,
+ uncrypt_request.encrypted_secret_len);
+ if (reversed_secret.data == NULL) {
+ gnutls_privkey_deinit(privkey);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* The secret has to be reversed ... */
+ for(i=0; i< uncrypt_request.encrypted_secret_len; i++) {
+ uint8_t *reversed = (uint8_t *)reversed_secret.data;
+ uint8_t *uncrypt = uncrypt_request.encrypted_secret;
+ reversed[i] = uncrypt[uncrypt_request.encrypted_secret_len - 1 - i];
+ }
+ reversed_secret.size = uncrypt_request.encrypted_secret_len;
+
+ /*
+ * Let's try to decrypt the secret now that
+ * we have the private key ...
+ */
+ rc = gnutls_privkey_decrypt_data(privkey,
+ 0,
+ &reversed_secret,
+ &uncrypted_secret);
+ gnutls_privkey_deinit(privkey);
+ if (rc != GNUTLS_E_SUCCESS) {
+ /* We are not able to decrypt the secret, looks like something is wrong */
+ return WERR_INVALID_PARAMETER;
+ }
+ blob_us.data = uncrypted_secret.data;
+ blob_us.length = uncrypted_secret.size;
+
+ if (uncrypt_request.version == 2) {
+ struct bkrp_encrypted_secret_v2 uncrypted_secretv2;
+
+ ndr_err = ndr_pull_struct_blob(&blob_us, mem_ctx, &uncrypted_secretv2,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_encrypted_secret_v2);
+ gnutls_free(uncrypted_secret.data);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ /* Unable to unmarshall */
+ return WERR_INVALID_DATA;
+ }
+ if (uncrypted_secretv2.magic != 0x20) {
+ /* wrong magic */
+ return WERR_INVALID_DATA;
+ }
+
+ werr = get_and_verify_access_check(mem_ctx, 2,
+ uncrypted_secretv2.payload_key,
+ uncrypt_request.access_check,
+ uncrypt_request.access_check_len,
+ session_info);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ uncrypted_data = talloc(mem_ctx, DATA_BLOB);
+ if (uncrypted_data == NULL) {
+ return WERR_INVALID_DATA;
+ }
+
+ uncrypted_data->data = uncrypted_secretv2.secret;
+ uncrypted_data->length = uncrypted_secretv2.secret_len;
+ }
+ if (uncrypt_request.version == 3) {
+ struct bkrp_encrypted_secret_v3 uncrypted_secretv3;
+
+ ndr_err = ndr_pull_struct_blob(&blob_us, mem_ctx, &uncrypted_secretv3,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_encrypted_secret_v3);
+ gnutls_free(uncrypted_secret.data);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ /* Unable to unmarshall */
+ return WERR_INVALID_DATA;
+ }
+
+ if (uncrypted_secretv3.magic1 != 0x30 ||
+ uncrypted_secretv3.magic2 != 0x6610 ||
+ uncrypted_secretv3.magic3 != 0x800e) {
+ /* wrong magic */
+ return WERR_INVALID_DATA;
+ }
+
+ /*
+ * Confirm that the caller is permitted to
+ * read this particular data. Because one key
+ * pair is used per domain, the caller could
+ * have stolen the profile data on-disk and
+ * would otherwise be able to read the
+ * passwords.
+ */
+
+ werr = get_and_verify_access_check(mem_ctx, 3,
+ uncrypted_secretv3.payload_key,
+ uncrypt_request.access_check,
+ uncrypt_request.access_check_len,
+ session_info);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ uncrypted_data = talloc(mem_ctx, DATA_BLOB);
+ if (uncrypted_data == NULL) {
+ return WERR_INVALID_DATA;
+ }
+
+ uncrypted_data->data = uncrypted_secretv3.secret;
+ uncrypted_data->length = uncrypted_secretv3.secret_len;
+ }
+
+ /*
+ * Yeah if we are here all looks pretty good:
+ * - hash is ok
+ * - user sid is the same as the one in access check
+ * - we were able to decrypt the whole stuff
+ */
+ }
+
+ if (uncrypted_data->data == NULL) {
+ return WERR_INVALID_DATA;
+ }
+
+ /* There is a magic value a the beginning of the data
+ * we can use an adhoc structure but as the
+ * parent structure is just an array of bytes it a lot of work
+ * work just prepending 4 bytes
+ */
+ *(r->out.data_out) = talloc_zero_array(mem_ctx, uint8_t, uncrypted_data->length + 4);
+ W_ERROR_HAVE_NO_MEMORY(*(r->out.data_out));
+ memcpy(4+*(r->out.data_out), uncrypted_data->data, uncrypted_data->length);
+ *(r->out.data_out_len) = uncrypted_data->length + 4;
+
+ return WERR_OK;
+}
+
+static DATA_BLOB *reverse_and_get_blob(TALLOC_CTX *mem_ctx,
+ gnutls_datum_t *datum)
+{
+ DATA_BLOB *blob;
+ size_t i;
+
+ blob = talloc(mem_ctx, DATA_BLOB);
+ if (blob == NULL) {
+ return NULL;
+ }
+
+ blob->length = datum->size;
+ if (datum->data[0] == '\0') {
+ /* The datum has a leading byte zero, skip it */
+ blob->length = datum->size - 1;
+ }
+ blob->data = talloc_zero_array(mem_ctx, uint8_t, blob->length);
+ if (blob->data == NULL) {
+ talloc_free(blob);
+ return NULL;
+ }
+
+ for (i = 0; i < blob->length; i++) {
+ blob->data[i] = datum->data[datum->size - i - 1];
+ }
+
+ return blob;
+}
+
+static WERROR create_privkey_rsa(gnutls_privkey_t *pk)
+{
+ int bits = 2048;
+ gnutls_x509_privkey_t x509_privkey = NULL;
+ gnutls_privkey_t privkey = NULL;
+ int rc;
+
+ rc = gnutls_x509_privkey_init(&x509_privkey);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_privkey_init failed - %s\n",
+ gnutls_strerror(rc));
+ return WERR_INTERNAL_ERROR;
+ }
+
+ rc = gnutls_x509_privkey_generate(x509_privkey,
+ GNUTLS_PK_RSA,
+ bits,
+ 0);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_privkey_generate failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_privkey_deinit(x509_privkey);
+ return WERR_INTERNAL_ERROR;
+ }
+
+ rc = gnutls_privkey_init(&privkey);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_privkey_init failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_privkey_deinit(x509_privkey);
+ return WERR_INTERNAL_ERROR;
+ }
+
+ rc = gnutls_privkey_import_x509(privkey,
+ x509_privkey,
+ GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_privkey_import_x509 failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_privkey_deinit(x509_privkey);
+ return WERR_INTERNAL_ERROR;
+ }
+
+ *pk = privkey;
+
+ return WERR_OK;
+}
+
+static WERROR self_sign_cert(TALLOC_CTX *mem_ctx,
+ time_t lifetime,
+ const char *dn,
+ gnutls_privkey_t issuer_privkey,
+ gnutls_x509_crt_t *certificate,
+ DATA_BLOB *guidblob)
+{
+ gnutls_datum_t unique_id;
+ gnutls_datum_t serial_number;
+ gnutls_x509_crt_t issuer_cert;
+ gnutls_x509_privkey_t x509_issuer_privkey;
+ time_t activation = time(NULL);
+ time_t expiry = activation + lifetime;
+ const char *error_string;
+ uint8_t *reversed;
+ size_t i;
+ int rc;
+
+ unique_id.size = guidblob->length;
+ unique_id.data = talloc_memdup(mem_ctx,
+ guidblob->data,
+ guidblob->length);
+ if (unique_id.data == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ reversed = talloc_array(mem_ctx, uint8_t, guidblob->length);
+ if (reversed == NULL) {
+ talloc_free(unique_id.data);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* Native AD generates certificates with serialnumber in reversed notation */
+ for (i = 0; i < guidblob->length; i++) {
+ uint8_t *uncrypt = guidblob->data;
+ reversed[i] = uncrypt[guidblob->length - i - 1];
+ }
+ serial_number.size = guidblob->length;
+ serial_number.data = reversed;
+
+ /* Create certificate to sign */
+ rc = gnutls_x509_crt_init(&issuer_cert);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_crt_init failed - %s\n",
+ gnutls_strerror(rc));
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ rc = gnutls_x509_crt_set_dn(issuer_cert, dn, &error_string);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_crt_set_dn failed - %s (%s)\n",
+ gnutls_strerror(rc),
+ error_string);
+ gnutls_x509_crt_deinit(issuer_cert);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ rc = gnutls_x509_crt_set_issuer_dn(issuer_cert, dn, &error_string);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_crt_set_issuer_dn failed - %s (%s)\n",
+ gnutls_strerror(rc),
+ error_string);
+ gnutls_x509_crt_deinit(issuer_cert);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* Get x509 privkey for subjectPublicKeyInfo */
+ rc = gnutls_x509_privkey_init(&x509_issuer_privkey);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_privkey_init failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_crt_deinit(issuer_cert);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ rc = gnutls_privkey_export_x509(issuer_privkey,
+ &x509_issuer_privkey);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_privkey_init failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_privkey_deinit(x509_issuer_privkey);
+ gnutls_x509_crt_deinit(issuer_cert);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* Set subjectPublicKeyInfo */
+ rc = gnutls_x509_crt_set_key(issuer_cert, x509_issuer_privkey);
+ gnutls_x509_privkey_deinit(x509_issuer_privkey);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_crt_set_pubkey failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_crt_deinit(issuer_cert);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ rc = gnutls_x509_crt_set_activation_time(issuer_cert, activation);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_crt_set_activation_time failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_crt_deinit(issuer_cert);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ rc = gnutls_x509_crt_set_expiration_time(issuer_cert, expiry);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_crt_set_expiration_time failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_crt_deinit(issuer_cert);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ rc = gnutls_x509_crt_set_version(issuer_cert, 3);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_crt_set_version failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_crt_deinit(issuer_cert);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ rc = gnutls_x509_crt_set_subject_unique_id(issuer_cert,
+ unique_id.data,
+ unique_id.size);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_crt_set_subject_key_id failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_crt_deinit(issuer_cert);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ rc = gnutls_x509_crt_set_issuer_unique_id(issuer_cert,
+ unique_id.data,
+ unique_id.size);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_crt_set_issuer_unique_id failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_crt_deinit(issuer_cert);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ rc = gnutls_x509_crt_set_serial(issuer_cert,
+ serial_number.data,
+ serial_number.size);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_crt_set_serial failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_x509_crt_deinit(issuer_cert);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ rc = gnutls_x509_crt_privkey_sign(issuer_cert,
+ issuer_cert,
+ issuer_privkey,
+ GNUTLS_DIG_SHA1,
+ 0);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_crt_privkey_sign failed - %s\n",
+ gnutls_strerror(rc));
+ return WERR_INVALID_PARAMETER;
+ }
+
+ *certificate = issuer_cert;
+
+ return WERR_OK;
+}
+
+/* Return an error when we fail to generate a certificate */
+static WERROR generate_bkrp_cert(TALLOC_CTX *mem_ctx,
+ struct dcesrv_call_state *dce_call,
+ struct ldb_context *ldb_ctx,
+ const char *dn)
+{
+ WERROR werr;
+ gnutls_privkey_t issuer_privkey = NULL;
+ gnutls_x509_crt_t cert = NULL;
+ gnutls_datum_t cert_blob;
+ gnutls_datum_t m, e, d, p, q, u, e1, e2;
+ DATA_BLOB blob;
+ DATA_BLOB blobkeypair;
+ DATA_BLOB *tmp;
+ bool ok = true;
+ struct GUID guid = GUID_random();
+ NTSTATUS status;
+ char *secret_name;
+ struct bkrp_exported_RSA_key_pair keypair;
+ enum ndr_err_code ndr_err;
+ time_t nb_seconds_validity = 3600 * 24 * 365;
+ int rc;
+
+ DEBUG(6, ("Trying to generate a certificate\n"));
+ werr = create_privkey_rsa(&issuer_privkey);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ status = GUID_to_ndr_blob(&guid, mem_ctx, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ gnutls_privkey_deinit(issuer_privkey);
+ return WERR_INVALID_DATA;
+ }
+
+ werr = self_sign_cert(mem_ctx,
+ nb_seconds_validity,
+ dn,
+ issuer_privkey,
+ &cert,
+ &blob);
+ if (!W_ERROR_IS_OK(werr)) {
+ gnutls_privkey_deinit(issuer_privkey);
+ return WERR_INVALID_DATA;
+ }
+
+ rc = gnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_DER, &cert_blob);
+ if (rc != GNUTLS_E_SUCCESS) {
+ DBG_ERR("gnutls_x509_crt_export2 failed - %s\n",
+ gnutls_strerror(rc));
+ gnutls_privkey_deinit(issuer_privkey);
+ gnutls_x509_crt_deinit(cert);
+ return WERR_INVALID_DATA;
+ }
+
+ keypair.cert.length = cert_blob.size;
+ keypair.cert.data = talloc_memdup(mem_ctx, cert_blob.data, cert_blob.size);
+ gnutls_x509_crt_deinit(cert);
+ gnutls_free(cert_blob.data);
+ if (keypair.cert.data == NULL) {
+ gnutls_privkey_deinit(issuer_privkey);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ rc = gnutls_privkey_export_rsa_raw(issuer_privkey,
+ &m,
+ &e,
+ &d,
+ &p,
+ &q,
+ &u,
+ &e1,
+ &e2);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_privkey_deinit(issuer_privkey);
+ return WERR_INVALID_DATA;
+ }
+
+ /*
+ * Heimdal's bignum are big endian and the
+ * structure expect it to be in little endian
+ * so we reverse the buffer to make it work
+ */
+ tmp = reverse_and_get_blob(mem_ctx, &e);
+ if (tmp == NULL) {
+ ok = false;
+ } else {
+ SMB_ASSERT(tmp->length <= 4);
+ keypair.public_exponent = *tmp;
+ }
+
+ tmp = reverse_and_get_blob(mem_ctx, &d);
+ if (tmp == NULL) {
+ ok = false;
+ } else {
+ keypair.private_exponent = *tmp;
+ }
+
+ tmp = reverse_and_get_blob(mem_ctx, &m);
+ if (tmp == NULL) {
+ ok = false;
+ } else {
+ keypair.modulus = *tmp;
+ }
+
+ tmp = reverse_and_get_blob(mem_ctx, &p);
+ if (tmp == NULL) {
+ ok = false;
+ } else {
+ keypair.prime1 = *tmp;
+ }
+
+ tmp = reverse_and_get_blob(mem_ctx, &q);
+ if (tmp == NULL) {
+ ok = false;
+ } else {
+ keypair.prime2 = *tmp;
+ }
+
+ tmp = reverse_and_get_blob(mem_ctx, &e1);
+ if (tmp == NULL) {
+ ok = false;
+ } else {
+ keypair.exponent1 = *tmp;
+ }
+
+ tmp = reverse_and_get_blob(mem_ctx, &e2);
+ if (tmp == NULL) {
+ ok = false;
+ } else {
+ keypair.exponent2 = *tmp;
+ }
+
+ tmp = reverse_and_get_blob(mem_ctx, &u);
+ if (tmp == NULL) {
+ ok = false;
+ } else {
+ keypair.coefficient = *tmp;
+ }
+
+ /* One of the keypair allocation was wrong */
+ if (ok == false) {
+ gnutls_privkey_deinit(issuer_privkey);
+ return WERR_INVALID_DATA;
+ }
+
+ keypair.certificate_len = keypair.cert.length;
+ ndr_err = ndr_push_struct_blob(&blobkeypair,
+ mem_ctx,
+ &keypair,
+ (ndr_push_flags_fn_t)ndr_push_bkrp_exported_RSA_key_pair);
+ gnutls_privkey_deinit(issuer_privkey);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INVALID_DATA;
+ }
+
+ secret_name = talloc_asprintf(mem_ctx, "BCKUPKEY_%s", GUID_string(mem_ctx, &guid));
+ if (secret_name == NULL) {
+ return WERR_OUTOFMEMORY;
+ }
+
+ status = set_lsa_secret(mem_ctx, ldb_ctx, secret_name, &blobkeypair);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("Failed to save the secret %s\n", secret_name));
+ }
+ talloc_free(secret_name);
+
+ GUID_to_ndr_blob(&guid, mem_ctx, &blob);
+ status = set_lsa_secret(mem_ctx, ldb_ctx, "BCKUPKEY_PREFERRED", &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("Failed to save the secret BCKUPKEY_PREFERRED\n"));
+ }
+
+ return WERR_OK;
+}
+
+static WERROR bkrp_retrieve_client_wrap_key(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct bkrp_BackupKey *r, struct ldb_context *ldb_ctx)
+{
+ struct GUID guid;
+ char *guid_string;
+ DATA_BLOB lsa_secret;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+
+ /*
+ * here we basicaly need to return our certificate
+ * search for lsa secret BCKUPKEY_PREFERRED first
+ */
+
+ status = get_lsa_secret(mem_ctx,
+ ldb_ctx,
+ "BCKUPKEY_PREFERRED",
+ &lsa_secret);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RESOURCE_NAME_NOT_FOUND)) {
+ /* Ok we can be in this case if there was no certs */
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ char *dn = talloc_asprintf(mem_ctx, "CN=%s",
+ lpcfg_realm(lp_ctx));
+
+ WERROR werr = generate_bkrp_cert(mem_ctx, dce_call, ldb_ctx, dn);
+ if (!W_ERROR_IS_OK(werr)) {
+ return WERR_INVALID_PARAMETER;
+ }
+ status = get_lsa_secret(mem_ctx,
+ ldb_ctx,
+ "BCKUPKEY_PREFERRED",
+ &lsa_secret);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /* Ok we really don't manage to get this certs ...*/
+ DEBUG(2, ("Unable to locate BCKUPKEY_PREFERRED after cert generation\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ return WERR_INTERNAL_ERROR;
+ }
+
+ if (lsa_secret.length == 0) {
+ DEBUG(1, ("No secret in BCKUPKEY_PREFERRED, are we an undetected RODC?\n"));
+ return WERR_INTERNAL_ERROR;
+ } else {
+ char *cert_secret_name;
+
+ status = GUID_from_ndr_blob(&lsa_secret, &guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ guid_string = GUID_string(mem_ctx, &guid);
+ if (guid_string == NULL) {
+ /* We return file not found because the client
+ * expect this error
+ */
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ cert_secret_name = talloc_asprintf(mem_ctx,
+ "BCKUPKEY_%s",
+ guid_string);
+ status = get_lsa_secret(mem_ctx,
+ ldb_ctx,
+ cert_secret_name,
+ &lsa_secret);
+ if (!NT_STATUS_IS_OK(status)) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ if (lsa_secret.length != 0) {
+ struct bkrp_exported_RSA_key_pair keypair;
+ ndr_err = ndr_pull_struct_blob(&lsa_secret, mem_ctx, &keypair,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_exported_RSA_key_pair);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_FILE_NOT_FOUND;
+ }
+ *(r->out.data_out_len) = keypair.cert.length;
+ *(r->out.data_out) = talloc_memdup(mem_ctx, keypair.cert.data, keypair.cert.length);
+ W_ERROR_HAVE_NO_MEMORY(*(r->out.data_out));
+ return WERR_OK;
+ } else {
+ DEBUG(1, ("No or broken secret called %s\n", cert_secret_name));
+ return WERR_INTERNAL_ERROR;
+ }
+ }
+
+ return WERR_NOT_SUPPORTED;
+}
+
+static WERROR generate_bkrp_server_wrap_key(TALLOC_CTX *ctx, struct ldb_context *ldb_ctx)
+{
+ struct GUID guid = GUID_random();
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob_wrap_key, guid_blob;
+ struct bkrp_dc_serverwrap_key wrap_key;
+ NTSTATUS status;
+ char *secret_name;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ generate_random_buffer(wrap_key.key, sizeof(wrap_key.key));
+
+ ndr_err = ndr_push_struct_blob(&blob_wrap_key, ctx, &wrap_key, (ndr_push_flags_fn_t)ndr_push_bkrp_dc_serverwrap_key);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(frame);
+ return WERR_INVALID_DATA;
+ }
+
+ secret_name = talloc_asprintf(frame, "BCKUPKEY_%s", GUID_string(ctx, &guid));
+ if (secret_name == NULL) {
+ TALLOC_FREE(frame);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = set_lsa_secret(frame, ldb_ctx, secret_name, &blob_wrap_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("Failed to save the secret %s\n", secret_name));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+
+ status = GUID_to_ndr_blob(&guid, frame, &guid_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("Failed to save the secret %s\n", secret_name));
+ TALLOC_FREE(frame);
+ }
+
+ status = set_lsa_secret(frame, ldb_ctx, "BCKUPKEY_P", &guid_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("Failed to save the secret %s\n", secret_name));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+
+ TALLOC_FREE(frame);
+
+ return WERR_OK;
+}
+
+/*
+ * Find the specified decryption keys from the LSA secrets store as
+ * G$BCKUPKEY_keyGuidString.
+ */
+
+static WERROR bkrp_do_retrieve_server_wrap_key(TALLOC_CTX *mem_ctx, struct ldb_context *ldb_ctx,
+ struct bkrp_dc_serverwrap_key *server_key,
+ struct GUID *guid)
+{
+ NTSTATUS status;
+ DATA_BLOB lsa_secret;
+ char *secret_name;
+ char *guid_string;
+ enum ndr_err_code ndr_err;
+
+ guid_string = GUID_string(mem_ctx, guid);
+ if (guid_string == NULL) {
+ /* We return file not found because the client
+ * expect this error
+ */
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ secret_name = talloc_asprintf(mem_ctx, "BCKUPKEY_%s", guid_string);
+ if (secret_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = get_lsa_secret(mem_ctx, ldb_ctx, secret_name, &lsa_secret);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Error while fetching secret %s\n", secret_name));
+ return WERR_INVALID_DATA;
+ }
+ if (lsa_secret.length == 0) {
+ /* RODC case, we do not have secrets locally */
+ DEBUG(1, ("Unable to fetch value for secret %s, are we an undetected RODC?\n",
+ secret_name));
+ return WERR_INTERNAL_ERROR;
+ }
+ ndr_err = ndr_pull_struct_blob(&lsa_secret, mem_ctx, server_key,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_dc_serverwrap_key);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(2, ("Unable to parse the ndr encoded server wrap key %s\n", secret_name));
+ return WERR_INVALID_DATA;
+ }
+
+ return WERR_OK;
+}
+
+/*
+ * Find the current, preferred ServerWrap Key by looking at
+ * G$BCKUPKEY_P in the LSA secrets store.
+ *
+ * Then find the current decryption keys from the LSA secrets store as
+ * G$BCKUPKEY_keyGuidString.
+ */
+
+static WERROR bkrp_do_retrieve_default_server_wrap_key(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb_ctx,
+ struct bkrp_dc_serverwrap_key *server_key,
+ struct GUID *returned_guid)
+{
+ NTSTATUS status;
+ DATA_BLOB guid_binary;
+
+ status = get_lsa_secret(mem_ctx, ldb_ctx, "BCKUPKEY_P", &guid_binary);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Error while fetching secret BCKUPKEY_P to find current GUID\n"));
+ return WERR_FILE_NOT_FOUND;
+ } else if (guid_binary.length == 0) {
+ /* RODC case, we do not have secrets locally */
+ DEBUG(1, ("Unable to fetch value for secret BCKUPKEY_P, are we an undetected RODC?\n"));
+ return WERR_INTERNAL_ERROR;
+ }
+
+ status = GUID_from_ndr_blob(&guid_binary, returned_guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ return bkrp_do_retrieve_server_wrap_key(mem_ctx, ldb_ctx,
+ server_key, returned_guid);
+}
+
+static WERROR bkrp_server_wrap_decrypt_data(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct bkrp_BackupKey *r ,struct ldb_context *ldb_ctx)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ WERROR werr;
+ struct bkrp_server_side_wrapped decrypt_request;
+ DATA_BLOB sid_blob, encrypted_blob;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ struct bkrp_dc_serverwrap_key server_key;
+ struct bkrp_rc4encryptedpayload rc4payload;
+ struct dom_sid *caller_sid;
+ uint8_t symkey[20]; /* SHA-1 hash len */
+ uint8_t mackey[20]; /* SHA-1 hash len */
+ uint8_t mac[20]; /* SHA-1 hash len */
+ gnutls_hmac_hd_t hmac_hnd;
+ gnutls_cipher_hd_t cipher_hnd;
+ gnutls_datum_t cipher_key;
+ int rc;
+
+ blob.data = r->in.data_in;
+ blob.length = r->in.data_in_len;
+
+ if (r->in.data_in_len == 0 || r->in.data_in == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ ndr_err = ndr_pull_struct_blob_all(&blob, mem_ctx, &decrypt_request,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_server_side_wrapped);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (decrypt_request.magic != BACKUPKEY_SERVER_WRAP_VERSION) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ werr = bkrp_do_retrieve_server_wrap_key(mem_ctx, ldb_ctx, &server_key,
+ &decrypt_request.guid);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ dump_data_pw("server_key: \n", server_key.key, sizeof(server_key.key));
+
+ dump_data_pw("r2: \n", decrypt_request.r2, sizeof(decrypt_request.r2));
+
+ /*
+ * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1
+ * BACKUPKEY_BACKUP_GUID, it really is the whole key
+ */
+
+ rc = gnutls_hmac_init(&hmac_hnd,
+ GNUTLS_MAC_SHA1,
+ server_key.key,
+ sizeof(server_key.key));
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+
+ rc = gnutls_hmac(hmac_hnd,
+ decrypt_request.r2,
+ sizeof(decrypt_request.r2));
+
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+
+ gnutls_hmac_output(hmac_hnd, symkey);
+ dump_data_pw("symkey: \n", symkey, sizeof(symkey));
+
+ /* rc4 decrypt sid and secret using sym key */
+ cipher_key.data = symkey;
+ cipher_key.size = sizeof(symkey);
+
+ encrypted_blob = data_blob_const(decrypt_request.rc4encryptedpayload,
+ decrypt_request.ciphertext_length);
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &cipher_key,
+ NULL);
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+ rc = gnutls_cipher_encrypt2(cipher_hnd,
+ encrypted_blob.data,
+ encrypted_blob.length,
+ encrypted_blob.data,
+ encrypted_blob.length);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+
+ ndr_err = ndr_pull_struct_blob_all(&encrypted_blob, mem_ctx, &rc4payload,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_rc4encryptedpayload);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (decrypt_request.payload_length != rc4payload.secret_data.length) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ dump_data_pw("r3: \n", rc4payload.r3, sizeof(rc4payload.r3));
+
+ /*
+ * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1
+ * BACKUPKEY_BACKUP_GUID, it really is the whole key
+ */
+ rc = gnutls_hmac(hmac_hnd,
+ rc4payload.r3,
+ sizeof(rc4payload.r3));
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+
+ gnutls_hmac_deinit(hmac_hnd, mackey);
+
+ dump_data_pw("mackey: \n", mackey, sizeof(mackey));
+
+ ndr_err = ndr_push_struct_blob(&sid_blob, mem_ctx, &rc4payload.sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INTERNAL_ERROR;
+ }
+
+ rc = gnutls_hmac_init(&hmac_hnd,
+ GNUTLS_MAC_SHA1,
+ mackey,
+ sizeof(mackey));
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+
+ /* SID field */
+ rc = gnutls_hmac(hmac_hnd,
+ sid_blob.data,
+ sid_blob.length);
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+
+ /* Secret field */
+ rc = gnutls_hmac(hmac_hnd,
+ rc4payload.secret_data.data,
+ rc4payload.secret_data.length);
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+
+ gnutls_hmac_deinit(hmac_hnd, mac);
+ dump_data_pw("mac: \n", mac, sizeof(mac));
+ dump_data_pw("rc4payload.mac: \n", rc4payload.mac, sizeof(rc4payload.mac));
+
+ if (!mem_equal_const_time(mac, rc4payload.mac, sizeof(mac))) {
+ return WERR_INVALID_ACCESS;
+ }
+
+ caller_sid = &session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
+
+ if (!dom_sid_equal(&rc4payload.sid, caller_sid)) {
+ return WERR_INVALID_ACCESS;
+ }
+
+ *(r->out.data_out) = rc4payload.secret_data.data;
+ *(r->out.data_out_len) = rc4payload.secret_data.length;
+
+ return WERR_OK;
+}
+
+/*
+ * For BACKUPKEY_RESTORE_GUID we need to check the first 4 bytes to
+ * determine what type of restore is wanted.
+ *
+ * See MS-BKRP 3.1.4.1.4 BACKUPKEY_RESTORE_GUID point 1.
+ */
+
+static WERROR bkrp_generic_decrypt_data(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct bkrp_BackupKey *r, struct ldb_context *ldb_ctx)
+{
+ if (r->in.data_in_len < 4 || r->in.data_in == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (IVAL(r->in.data_in, 0) == BACKUPKEY_SERVER_WRAP_VERSION) {
+ return bkrp_server_wrap_decrypt_data(dce_call, mem_ctx, r, ldb_ctx);
+ }
+
+ return bkrp_client_wrap_decrypt_data(dce_call, mem_ctx, r, ldb_ctx);
+}
+
+/*
+ * We have some data, such as saved website or IMAP passwords that the
+ * client would like to put into the profile on-disk. This needs to
+ * be encrypted. This version gives the server the data over the
+ * network (protected only by the negotiated transport encryption),
+ * and asks that it be encrypted and returned for long-term storage.
+ *
+ * The data is NOT stored in the LSA, but a key to encrypt the data
+ * will be stored. There is only one active encryption key per domain,
+ * it is pointed at with G$BCKUPKEY_P in the LSA secrets store.
+ *
+ * The potentially multiple valid decryptiong keys (and the encryption
+ * key) are in turn stored in the LSA secrets store as
+ * G$BCKUPKEY_keyGuidString.
+ *
+ */
+
+static WERROR bkrp_server_wrap_encrypt_data(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct bkrp_BackupKey *r ,struct ldb_context *ldb_ctx)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ DATA_BLOB sid_blob, encrypted_blob, server_wrapped_blob;
+ WERROR werr;
+ struct dom_sid *caller_sid;
+ uint8_t symkey[20]; /* SHA-1 hash len */
+ uint8_t mackey[20]; /* SHA-1 hash len */
+ struct bkrp_rc4encryptedpayload rc4payload;
+ gnutls_hmac_hd_t hmac_hnd;
+ struct bkrp_dc_serverwrap_key server_key;
+ enum ndr_err_code ndr_err;
+ struct bkrp_server_side_wrapped server_side_wrapped;
+ struct GUID guid;
+ gnutls_cipher_hd_t cipher_hnd;
+ gnutls_datum_t cipher_key;
+ int rc;
+
+ if (r->in.data_in_len == 0 || r->in.data_in == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ werr = bkrp_do_retrieve_default_server_wrap_key(mem_ctx,
+ ldb_ctx, &server_key,
+ &guid);
+
+ if (!W_ERROR_IS_OK(werr)) {
+ if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) {
+ /* Generate the server wrap key since one wasn't found */
+ werr = generate_bkrp_server_wrap_key(mem_ctx,
+ ldb_ctx);
+ if (!W_ERROR_IS_OK(werr)) {
+ return WERR_INVALID_PARAMETER;
+ }
+ werr = bkrp_do_retrieve_default_server_wrap_key(mem_ctx,
+ ldb_ctx,
+ &server_key,
+ &guid);
+
+ if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) {
+ /* Ok we really don't manage to get this secret ...*/
+ return WERR_FILE_NOT_FOUND;
+ }
+ } else {
+ /* In theory we should NEVER reach this point as it
+ should only appear in a rodc server */
+ /* we do not have the real secret attribute */
+ return WERR_INVALID_PARAMETER;
+ }
+ }
+
+ caller_sid = &session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
+
+ dump_data_pw("server_key: \n", server_key.key, sizeof(server_key.key));
+
+ /*
+ * This is the key derivation step, so that the HMAC and RC4
+ * operations over the user-supplied data are not able to
+ * disclose the master key. By using random data, the symkey
+ * and mackey values are unique for this operation, and
+ * discovering these (by reversing the RC4 over the
+ * attacker-controlled data) does not return something able to
+ * be used to decyrpt the encrypted data of other users
+ */
+ generate_random_buffer(server_side_wrapped.r2, sizeof(server_side_wrapped.r2));
+
+ dump_data_pw("r2: \n", server_side_wrapped.r2, sizeof(server_side_wrapped.r2));
+
+ generate_random_buffer(rc4payload.r3, sizeof(rc4payload.r3));
+
+ dump_data_pw("r3: \n", rc4payload.r3, sizeof(rc4payload.r3));
+
+
+ /*
+ * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1
+ * BACKUPKEY_BACKUP_GUID, it really is the whole key
+ */
+ rc = gnutls_hmac_init(&hmac_hnd,
+ GNUTLS_MAC_SHA1,
+ server_key.key,
+ sizeof(server_key.key));
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+
+ rc = gnutls_hmac(hmac_hnd,
+ server_side_wrapped.r2,
+ sizeof(server_side_wrapped.r2));
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+ gnutls_hmac_output(hmac_hnd, symkey);
+ dump_data_pw("symkey: \n", symkey, sizeof(symkey));
+
+ /*
+ * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1
+ * BACKUPKEY_BACKUP_GUID, it really is the whole key
+ */
+ rc = gnutls_hmac(hmac_hnd,
+ rc4payload.r3,
+ sizeof(rc4payload.r3));
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+ gnutls_hmac_deinit(hmac_hnd, mackey);
+ dump_data_pw("mackey: \n", mackey, sizeof(mackey));
+
+ ndr_err = ndr_push_struct_blob(&sid_blob, mem_ctx, caller_sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INTERNAL_ERROR;
+ }
+
+ rc4payload.secret_data.data = r->in.data_in;
+ rc4payload.secret_data.length = r->in.data_in_len;
+
+ rc = gnutls_hmac_init(&hmac_hnd,
+ GNUTLS_MAC_SHA1,
+ mackey,
+ sizeof(mackey));
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+
+ /* SID field */
+ rc = gnutls_hmac(hmac_hnd,
+ sid_blob.data,
+ sid_blob.length);
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+
+ /* Secret field */
+ rc = gnutls_hmac(hmac_hnd,
+ rc4payload.secret_data.data,
+ rc4payload.secret_data.length);
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+
+ gnutls_hmac_deinit(hmac_hnd, rc4payload.mac);
+ dump_data_pw("rc4payload.mac: \n", rc4payload.mac, sizeof(rc4payload.mac));
+
+ rc4payload.sid = *caller_sid;
+
+ ndr_err = ndr_push_struct_blob(&encrypted_blob, mem_ctx, &rc4payload,
+ (ndr_push_flags_fn_t)ndr_push_bkrp_rc4encryptedpayload);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INTERNAL_ERROR;
+ }
+
+ /* rc4 encrypt sid and secret using sym key */
+ cipher_key.data = symkey;
+ cipher_key.size = sizeof(symkey);
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &cipher_key,
+ NULL);
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+ rc = gnutls_cipher_encrypt2(cipher_hnd,
+ encrypted_blob.data,
+ encrypted_blob.length,
+ encrypted_blob.data,
+ encrypted_blob.length);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ }
+
+ /* create server wrap structure */
+
+ server_side_wrapped.payload_length = rc4payload.secret_data.length;
+ server_side_wrapped.ciphertext_length = encrypted_blob.length;
+ server_side_wrapped.guid = guid;
+ server_side_wrapped.rc4encryptedpayload = encrypted_blob.data;
+
+ ndr_err = ndr_push_struct_blob(&server_wrapped_blob, mem_ctx, &server_side_wrapped,
+ (ndr_push_flags_fn_t)ndr_push_bkrp_server_side_wrapped);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INTERNAL_ERROR;
+ }
+
+ *(r->out.data_out) = server_wrapped_blob.data;
+ *(r->out.data_out_len) = server_wrapped_blob.length;
+
+ return WERR_OK;
+}
+
+static WERROR dcesrv_bkrp_BackupKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx, struct bkrp_BackupKey *r)
+{
+ WERROR error = WERR_INVALID_PARAMETER;
+ struct ldb_context *ldb_ctx;
+ bool is_rodc;
+ const char *addr = "unknown";
+ /* At which level we start to add more debug of what is done in the protocol */
+ const int debuglevel = 4;
+
+ if (DEBUGLVL(debuglevel)) {
+ const struct tsocket_address *remote_address;
+ remote_address = dcesrv_connection_get_remote_address(dce_call->conn);
+ if (tsocket_address_is_inet(remote_address, "ip")) {
+ addr = tsocket_address_inet_addr_string(remote_address, mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(addr);
+ }
+ }
+
+ if (lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx) != ROLE_ACTIVE_DIRECTORY_DC) {
+ return WERR_NOT_SUPPORTED;
+ }
+
+ /*
+ * Save the current remote session details so they can used by the
+ * audit logging module. This allows the audit logging to report the
+ * remote users details, rather than the system users details.
+ */
+ ldb_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
+
+ if (samdb_rodc(ldb_ctx, &is_rodc) != LDB_SUCCESS) {
+ talloc_unlink(mem_ctx, ldb_ctx);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!is_rodc) {
+ if(strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent),
+ BACKUPKEY_RESTORE_GUID, strlen(BACKUPKEY_RESTORE_GUID)) == 0) {
+ DEBUG(debuglevel, ("Client %s requested to decrypt a wrapped secret\n", addr));
+ error = bkrp_generic_decrypt_data(dce_call, mem_ctx, r, ldb_ctx);
+ }
+
+ if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent),
+ BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID, strlen(BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID)) == 0) {
+ DEBUG(debuglevel, ("Client %s requested certificate for client wrapped secret\n", addr));
+ error = bkrp_retrieve_client_wrap_key(dce_call, mem_ctx, r, ldb_ctx);
+ }
+
+ if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent),
+ BACKUPKEY_RESTORE_GUID_WIN2K, strlen(BACKUPKEY_RESTORE_GUID_WIN2K)) == 0) {
+ DEBUG(debuglevel, ("Client %s requested to decrypt a server side wrapped secret\n", addr));
+ error = bkrp_server_wrap_decrypt_data(dce_call, mem_ctx, r, ldb_ctx);
+ }
+
+ if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent),
+ BACKUPKEY_BACKUP_GUID, strlen(BACKUPKEY_BACKUP_GUID)) == 0) {
+ DEBUG(debuglevel, ("Client %s requested a server wrapped secret\n", addr));
+ error = bkrp_server_wrap_encrypt_data(dce_call, mem_ctx, r, ldb_ctx);
+ }
+ }
+ /*else: I am a RODC so I don't handle backup key protocol */
+
+ talloc_unlink(mem_ctx, ldb_ctx);
+ return error;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_backupkey_s.c"
diff --git a/source4/rpc_server/browser/dcesrv_browser.c b/source4/rpc_server/browser/dcesrv_browser.c
new file mode 100644
index 0000000..797eb86
--- /dev/null
+++ b/source4/rpc_server/browser/dcesrv_browser.c
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the browser pipe
+
+ Copyright (C) Stefan Metzmacher 2008
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_browser.h"
+
+
+/*
+ BrowserrServerEnum
+*/
+static void dcesrv_BrowserrServerEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrServerEnum *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrDebugCall
+*/
+static void dcesrv_BrowserrDebugCall(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrDebugCall *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrQueryOtherDomains
+*/
+static WERROR dcesrv_BrowserrQueryOtherDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrQueryOtherDomains *r)
+{
+ struct BrowserrSrvInfo100Ctr *ctr100;
+
+ switch (r->in.info->level) {
+ case 100:
+ if (!r->in.info->info.info100) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ ctr100 = talloc(mem_ctx, struct BrowserrSrvInfo100Ctr);
+ W_ERROR_HAVE_NO_MEMORY(ctr100);
+
+ ctr100->entries_read = 0;
+ ctr100->entries = talloc_zero_array(ctr100, struct srvsvc_NetSrvInfo100,
+ ctr100->entries_read);
+ W_ERROR_HAVE_NO_MEMORY(ctr100->entries);
+
+ r->out.info->info.info100 = ctr100;
+ *r->out.total_entries = ctr100->entries_read;
+ return WERR_OK;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ BrowserrResetNetlogonState
+*/
+static void dcesrv_BrowserrResetNetlogonState(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrResetNetlogonState *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrDebugTrace
+*/
+static void dcesrv_BrowserrDebugTrace(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrDebugTrace *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrQueryStatistics
+*/
+static void dcesrv_BrowserrQueryStatistics(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrQueryStatistics *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserResetStatistics
+*/
+static void dcesrv_BrowserResetStatistics(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserResetStatistics *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ NetrBrowserStatisticsClear
+*/
+static void dcesrv_NetrBrowserStatisticsClear(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct NetrBrowserStatisticsClear *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ NetrBrowserStatisticsGet
+*/
+static void dcesrv_NetrBrowserStatisticsGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct NetrBrowserStatisticsGet *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrSetNetlogonState
+*/
+static void dcesrv_BrowserrSetNetlogonState(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrSetNetlogonState *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrQueryEmulatedDomains
+*/
+static void dcesrv_BrowserrQueryEmulatedDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrQueryEmulatedDomains *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrServerEnumEx
+*/
+static void dcesrv_BrowserrServerEnumEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrServerEnumEx *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_browser_s.c"
diff --git a/source4/rpc_server/common/common.h b/source4/rpc_server/common/common.h
new file mode 100644
index 0000000..b57ddf2
--- /dev/null
+++ b/source4/rpc_server/common/common.h
@@ -0,0 +1,44 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ common macros for the dcerpc server interfaces
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Andrew Tridgell 2004
+
+ 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 _DCERPC_SERVER_COMMON_H_
+#define _DCERPC_SERVER_COMMON_H_
+
+struct share_config;
+struct dcesrv_connection;
+struct dcesrv_context;
+struct dcesrv_context;
+struct dcesrv_call_state;
+struct ndr_interface_table;
+struct ncacn_packet;
+struct auth_session_info;
+
+struct dcerpc_server_info {
+ const char *domain_name;
+ uint32_t version_major;
+ uint32_t version_minor;
+ uint32_t version_build;
+};
+
+#include "rpc_server/common/proto.h"
+
+#endif /* _DCERPC_SERVER_COMMON_H_ */
diff --git a/source4/rpc_server/common/forward.c b/source4/rpc_server/common/forward.c
new file mode 100644
index 0000000..4ae8c1b
--- /dev/null
+++ b/source4/rpc_server/common/forward.c
@@ -0,0 +1,134 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ forwarding of RPC calls to other tasks
+
+ Copyright (C) Andrew Tridgell 2009
+
+ 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 "includes.h"
+#include <tevent.h>
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/dcerpc.h"
+#include "rpc_server/common/common.h"
+#include "messaging/irpc.h"
+#include "auth/auth.h"
+
+
+struct dcesrv_forward_state {
+ const char *opname;
+ struct dcesrv_call_state *dce_call;
+};
+
+/*
+ called when the forwarded rpc request is finished
+ */
+static void dcesrv_irpc_forward_callback(struct tevent_req *subreq)
+{
+ struct dcesrv_forward_state *st =
+ tevent_req_callback_data(subreq,
+ struct dcesrv_forward_state);
+ const char *opname = st->opname;
+ NTSTATUS status;
+
+ status = dcerpc_binding_handle_call_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("IRPC callback failed for %s - %s\n",
+ opname, nt_errstr(status)));
+ st->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ }
+ status = dcesrv_reply(st->dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s_handler: dcesrv_reply() failed - %s\n",
+ opname, nt_errstr(status)));
+ }
+}
+
+
+
+/**
+ * Forward a RPC call using IRPC to another task
+ */
+void dcesrv_irpc_forward_rpc_call(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ void *r, uint32_t callid,
+ const struct ndr_interface_table *ndr_table,
+ const char *dest_task, const char *opname,
+ uint32_t timeout)
+{
+ struct dcesrv_forward_state *st;
+ struct dcerpc_binding_handle *binding_handle;
+ struct tevent_req *subreq;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+
+ st = talloc(mem_ctx, struct dcesrv_forward_state);
+ if (st == NULL) {
+ dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ return;
+ }
+
+ st->dce_call = dce_call;
+ st->opname = opname;
+
+ /* if the caller has said they can't support async calls
+ then fail the call */
+ if (!(dce_call->state_flags & DCESRV_CALL_STATE_FLAG_MAY_ASYNC)) {
+ /* we're not allowed to reply async */
+ DEBUG(0,("%s: Not available synchronously\n", dest_task));
+ dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ return;
+ }
+
+ binding_handle = irpc_binding_handle_by_name(st,
+ imsg_ctx,
+ dest_task,
+ ndr_table);
+ if (binding_handle == NULL) {
+ DEBUG(0,("%s: Failed to forward request to %s task\n",
+ opname, dest_task));
+ dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ return;
+ }
+
+ /* reset timeout for the handle */
+ dcerpc_binding_handle_set_timeout(binding_handle, timeout);
+
+ /* add security token to the handle*/
+ irpc_binding_handle_add_security_token(binding_handle,
+ session_info->security_token);
+
+ /* forward the call */
+ subreq = dcerpc_binding_handle_call_send(st, dce_call->event_ctx,
+ binding_handle,
+ NULL, ndr_table,
+ callid,
+ dce_call, r);
+ if (subreq == NULL) {
+ DEBUG(0,("%s: Failed to forward request to %s task\n",
+ opname, dest_task));
+ dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ return;
+ }
+
+ /* mark the request as replied async */
+ dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+
+ /* setup the callback */
+ tevent_req_set_callback(subreq, dcesrv_irpc_forward_callback, st);
+}
diff --git a/source4/rpc_server/common/loadparm.c b/source4/rpc_server/common/loadparm.c
new file mode 100644
index 0000000..174063e
--- /dev/null
+++ b/source4/rpc_server/common/loadparm.c
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+ DCERPC server info param function
+ Moved into rpc_server/common to break dependencies to rpc_server from param
+ Copyright (C) Karl Auer 1993-1998
+
+ Largely re-written by Andrew Tridgell, September 1994
+
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Alexander Bokovoy 2002
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003.
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+
+ 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 "includes.h"
+#include "lib/param/param.h"
+#include "rpc_server/common/common.h"
+
+_PUBLIC_ struct dcerpc_server_info *lpcfg_dcerpc_server_info(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ struct dcerpc_server_info *ret = talloc_zero(mem_ctx, struct dcerpc_server_info);
+
+ ret->domain_name = talloc_reference(mem_ctx, lpcfg_workgroup(lp_ctx));
+ ret->version_major = lpcfg_parm_int(lp_ctx, NULL, "server_info", "version_major", 5);
+ ret->version_minor = lpcfg_parm_int(lp_ctx, NULL, "server_info", "version_minor", 2);
+ ret->version_build = lpcfg_parm_int(lp_ctx, NULL, "server_info", "version_build", 3790);
+
+ return ret;
+}
+
diff --git a/source4/rpc_server/common/server_info.c b/source4/rpc_server/common/server_info.c
new file mode 100644
index 0000000..34228c3
--- /dev/null
+++ b/source4/rpc_server/common/server_info.c
@@ -0,0 +1,319 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ common server info functions
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ 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 "includes.h"
+#include "librpc/gen_ndr/srvsvc.h"
+#include "rpc_server/dcerpc_server.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "auth/auth.h"
+#include "param/param.h"
+#include "rpc_server/common/common.h"
+#include "libds/common/roles.h"
+#include "auth/auth_util.h"
+#include "lib/tsocket/tsocket.h"
+
+/*
+ Here are common server info functions used by some dcerpc server interfaces
+*/
+
+/* This hardcoded value should go into a ldb database! */
+enum srvsvc_PlatformId dcesrv_common_get_platform_id(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ enum srvsvc_PlatformId id;
+
+ id = lpcfg_parm_int(dce_ctx->lp_ctx, NULL, "server_info", "platform_id", PLATFORM_ID_NT);
+
+ return id;
+}
+
+const char *dcesrv_common_get_server_name(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, const char *server_unc)
+{
+ const char *p = server_unc;
+
+ /* if there's no string return our NETBIOS name */
+ if (!p) {
+ return talloc_strdup(mem_ctx, lpcfg_netbios_name(dce_ctx->lp_ctx));
+ }
+
+ /* if there're '\\\\' in front remove them otherwise just pass the string */
+ if (p[0] == '\\' && p[1] == '\\') {
+ p += 2;
+ }
+
+ return talloc_strdup(mem_ctx, p);
+}
+
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_server_type(TALLOC_CTX *mem_ctx, struct tevent_context *event_ctx, struct dcesrv_context *dce_ctx)
+{
+ int default_server_announce = 0;
+ default_server_announce |= SV_TYPE_WORKSTATION;
+ default_server_announce |= SV_TYPE_SERVER;
+ default_server_announce |= SV_TYPE_SERVER_UNIX;
+
+ default_server_announce |= SV_TYPE_SERVER_NT;
+ default_server_announce |= SV_TYPE_NT;
+
+ switch (lpcfg_server_role(dce_ctx->lp_ctx)) {
+ case ROLE_DOMAIN_MEMBER:
+ default_server_announce |= SV_TYPE_DOMAIN_MEMBER;
+ break;
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ {
+ struct ldb_context *samctx;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ break;
+ }
+ /* open main ldb */
+ samctx = samdb_connect(
+ tmp_ctx,
+ event_ctx,
+ dce_ctx->lp_ctx,
+ anonymous_session(tmp_ctx, dce_ctx->lp_ctx),
+ NULL,
+ 0);
+ if (samctx == NULL) {
+ DEBUG(2,("Unable to open samdb in determining server announce flags\n"));
+ } else {
+ /* Determine if we are the pdc */
+ bool is_pdc = samdb_is_pdc(samctx);
+ if (is_pdc) {
+ default_server_announce |= SV_TYPE_DOMAIN_CTRL;
+ } else {
+ default_server_announce |= SV_TYPE_DOMAIN_BAKCTRL;
+ }
+ }
+ /* Close it */
+ talloc_free(tmp_ctx);
+ break;
+ }
+ case ROLE_STANDALONE:
+ default:
+ break;
+ }
+ if (lpcfg_time_server(dce_ctx->lp_ctx))
+ default_server_announce |= SV_TYPE_TIME_SOURCE;
+
+ if (lpcfg_host_msdfs(dce_ctx->lp_ctx))
+ default_server_announce |= SV_TYPE_DFS_SERVER;
+
+
+#if 0
+ {
+ /* TODO: announce us as print server when we are a print server */
+ bool is_print_server = false;
+ if (is_print_server) {
+ default_server_announce |= SV_TYPE_PRINTQ_SERVER;
+ }
+ }
+#endif
+ return default_server_announce;
+}
+
+/* This hardcoded value should go into a ldb database! */
+const char *dcesrv_common_get_lan_root(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return talloc_strdup(mem_ctx, "");
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_users(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return -1;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_disc(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 15;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_hidden(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_announce(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 240;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_anndelta(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 3000;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_licenses(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+const char *dcesrv_common_get_userpath(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return talloc_strdup(mem_ctx, "c:\\");
+}
+
+#define INVALID_SHARE_NAME_CHARS " \"*+,./:;<=>?[\\]|"
+
+bool dcesrv_common_validate_share_name(TALLOC_CTX *mem_ctx, const char *share_name)
+{
+ if (strpbrk(share_name, INVALID_SHARE_NAME_CHARS)) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * call_session_info is session info for samdb. call_audit_session_info is for
+ * auditing and may be NULL.
+ */
+struct ldb_context *dcesrv_samdb_connect_session_info(
+ TALLOC_CTX *mem_ctx,
+ struct dcesrv_call_state *dce_call,
+ const struct auth_session_info *call_session_info,
+ const struct auth_session_info *call_audit_session_info)
+{
+ struct ldb_context *samdb = NULL;
+ struct auth_session_info *user_session_info = NULL;
+ struct auth_session_info *audit_session_info = NULL;
+ struct tsocket_address *remote_address = NULL;
+
+ user_session_info = copy_session_info(mem_ctx, call_session_info);
+ if (user_session_info == NULL) {
+ return NULL;
+ }
+
+ if (call_audit_session_info != NULL) {
+ audit_session_info = copy_session_info(mem_ctx, call_audit_session_info);
+ if (audit_session_info == NULL) {
+ talloc_free(user_session_info);
+ return NULL;
+ }
+ }
+
+ if (dce_call->conn->remote_address != NULL) {
+ remote_address = tsocket_address_copy(dce_call->conn->remote_address,
+ user_session_info);
+ if (remote_address == NULL) {
+ TALLOC_FREE(audit_session_info);
+ talloc_free(user_session_info);
+ return NULL;
+ }
+ }
+
+ /*
+ * We need to make sure every argument
+ * stays arround for the lifetime of 'samdb',
+ * typically it is allocated on the scope of
+ * an assoc group, so we can't reference dce_call->conn,
+ * as the assoc group may stay when the current connection
+ * gets disconnected.
+ *
+ * The following are global per process:
+ * - dce_call->conn->dce_ctx->lp_ctx
+ * - dce_call->event_ctx
+ * - system_session
+ *
+ * We make a copy of:
+ * - dce_call->conn->remote_address
+ * - dce_call->auth_state->session_info
+ */
+ samdb = samdb_connect(
+ mem_ctx,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ user_session_info,
+ remote_address,
+ 0);
+ if (samdb == NULL) {
+ TALLOC_FREE(audit_session_info);
+ talloc_free(user_session_info);
+ return NULL;
+ }
+ talloc_move(samdb, &user_session_info);
+
+ if (audit_session_info != NULL) {
+ int ret;
+
+ talloc_steal(samdb, audit_session_info);
+
+ ret = ldb_set_opaque(samdb,
+ DSDB_NETWORK_SESSION_INFO,
+ audit_session_info);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(samdb);
+ return NULL;
+ }
+ }
+
+ return samdb;
+}
+
+/*
+ * Open an ldb connection under the system session and save the remote users
+ * session details in a ldb_opaque. This will allow the audit logging to
+ * log the original session for operations performed in the system session.
+ *
+ * Access checks are required by the caller!
+ */
+struct ldb_context *dcesrv_samdb_connect_as_system(
+ TALLOC_CTX *mem_ctx,
+ struct dcesrv_call_state *dce_call)
+{
+ const struct auth_session_info *system_session_info = NULL;
+ const struct auth_session_info *call_session_info = NULL;
+
+ system_session_info = system_session(dce_call->conn->dce_ctx->lp_ctx);
+ if (system_session_info == NULL) {
+ return NULL;
+ }
+
+ call_session_info = dcesrv_call_session_info(dce_call);
+
+ return dcesrv_samdb_connect_session_info(mem_ctx, dce_call,
+ system_session_info, call_session_info);
+}
+
+/*
+ * Open an ldb connection under the remote users session details.
+ *
+ * Access checks are done at the ldb level.
+ */
+struct ldb_context *dcesrv_samdb_connect_as_user(
+ TALLOC_CTX *mem_ctx,
+ struct dcesrv_call_state *dce_call)
+{
+ const struct auth_session_info *call_session_info = NULL;
+
+ call_session_info = dcesrv_call_session_info(dce_call);
+
+ return dcesrv_samdb_connect_session_info(mem_ctx, dce_call,
+ call_session_info, NULL);
+}
diff --git a/source4/rpc_server/common/share_info.c b/source4/rpc_server/common/share_info.c
new file mode 100644
index 0000000..d7ed5ee
--- /dev/null
+++ b/source4/rpc_server/common/share_info.c
@@ -0,0 +1,123 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ common share info functions
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ 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 "includes.h"
+#include "param/share.h"
+#include "librpc/gen_ndr/srvsvc.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/share.h"
+
+#undef strcasecmp
+
+/*
+ Here are common server info functions used by some dcerpc server interfaces
+*/
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_share_permissions(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_share_current_users(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return 1;
+}
+
+/* This hardcoded value should go into a ldb database! */
+enum srvsvc_ShareType dcesrv_common_get_share_type(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ /* for disk share 0x00000000
+ * for print share 0x00000001
+ * for IPC$ share 0x00000003
+ *
+ * administrative shares:
+ * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000
+ * this ones are hidden in NetShareEnum, but shown in NetShareEnumAll
+ */
+ enum srvsvc_ShareType share_type = 0;
+ char *sharetype;
+
+ if (!share_bool_option(scfg, SHARE_BROWSEABLE, SHARE_BROWSEABLE_DEFAULT)) {
+ share_type |= STYPE_HIDDEN;
+ }
+
+ sharetype = share_string_option(mem_ctx, scfg, SHARE_TYPE, SHARE_TYPE_DEFAULT);
+ if (sharetype && strcasecmp(sharetype, "IPC") == 0) {
+ share_type |= STYPE_IPC;
+ TALLOC_FREE(sharetype);
+ return share_type;
+ }
+
+ if (sharetype && strcasecmp(sharetype, "PRINTER") == 0) {
+ share_type |= STYPE_PRINTQ;
+ TALLOC_FREE(sharetype);
+ return share_type;
+ }
+
+ TALLOC_FREE(sharetype);
+ share_type |= STYPE_DISKTREE;
+
+ return share_type;
+}
+
+/* This hardcoded value should go into a ldb database! */
+const char *dcesrv_common_get_share_path(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ char *sharetype;
+ char *p;
+ char *path;
+
+ sharetype = share_string_option(mem_ctx, scfg, SHARE_TYPE, SHARE_TYPE_DEFAULT);
+
+ if (sharetype && strcasecmp(sharetype, "IPC") == 0) {
+ TALLOC_FREE(sharetype);
+ return talloc_strdup(mem_ctx, "");
+ }
+
+ TALLOC_FREE(sharetype);
+
+ p = share_string_option(mem_ctx, scfg, SHARE_PATH, "");
+ if (!p) {
+ return NULL;
+ }
+ if (p[0] == '\0') {
+ return p;
+ }
+ all_string_sub(p, "/", "\\", 0);
+
+ path = talloc_asprintf(mem_ctx, "C:%s", p);
+ TALLOC_FREE(p);
+ return path;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_share_dfs_flags(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+struct security_descriptor *dcesrv_common_get_security_descriptor(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return NULL;
+}
diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c
new file mode 100644
index 0000000..c896b35
--- /dev/null
+++ b/source4/rpc_server/dcerpc_server.c
@@ -0,0 +1,715 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ server side dcerpc core code
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 2004-2005
+
+ 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 "includes.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/dcerpc_server_proto.h"
+#include "param/param.h"
+#include "samba/service_stream.h"
+#include "lib/tsocket/tsocket.h"
+#include "lib/socket/socket.h"
+#include "samba/process_model.h"
+#include "lib/util/samba_modules.h"
+#include "lib/util/tevent_ntstatus.h"
+
+/*
+ take a reference to an existing association group
+ */
+static struct dcesrv_assoc_group *dcesrv_assoc_group_reference(struct dcesrv_connection *conn,
+ uint32_t id)
+{
+ const struct dcesrv_endpoint *endpoint = conn->endpoint;
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(endpoint->ep_description);
+ struct dcesrv_assoc_group *assoc_group;
+ void *id_ptr = NULL;
+
+ /* find an association group given a assoc_group_id */
+ id_ptr = idr_find(conn->dce_ctx->assoc_groups_idr, id);
+ if (id_ptr == NULL) {
+ DBG_NOTICE("Failed to find assoc_group 0x%08x\n", id);
+ return NULL;
+ }
+ assoc_group = talloc_get_type_abort(id_ptr, struct dcesrv_assoc_group);
+
+ if (assoc_group->transport != transport) {
+ const char *at =
+ derpc_transport_string_by_transport(
+ assoc_group->transport);
+ const char *ct =
+ derpc_transport_string_by_transport(
+ transport);
+
+ DBG_NOTICE("assoc_group 0x%08x (transport %s) "
+ "is not available on transport %s",
+ id, at, ct);
+ return NULL;
+ }
+
+ return talloc_reference(conn, assoc_group);
+}
+
+static int dcesrv_assoc_group_destructor(struct dcesrv_assoc_group *assoc_group)
+{
+ int ret;
+ ret = idr_remove(assoc_group->dce_ctx->assoc_groups_idr, assoc_group->id);
+ if (ret != 0) {
+ DEBUG(0,(__location__ ": Failed to remove assoc_group 0x%08x\n",
+ assoc_group->id));
+ }
+ return 0;
+}
+
+/*
+ allocate a new association group
+ */
+static struct dcesrv_assoc_group *dcesrv_assoc_group_new(struct dcesrv_connection *conn)
+{
+ struct dcesrv_context *dce_ctx = conn->dce_ctx;
+ const struct dcesrv_endpoint *endpoint = conn->endpoint;
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(endpoint->ep_description);
+ struct dcesrv_assoc_group *assoc_group;
+ int id;
+
+ assoc_group = talloc_zero(conn, struct dcesrv_assoc_group);
+ if (assoc_group == NULL) {
+ return NULL;
+ }
+
+ id = idr_get_new_random(dce_ctx->assoc_groups_idr, assoc_group, UINT16_MAX);
+ if (id == -1) {
+ talloc_free(assoc_group);
+ DEBUG(0,(__location__ ": Out of association groups!\n"));
+ return NULL;
+ }
+
+ assoc_group->transport = transport;
+ assoc_group->id = id;
+ assoc_group->dce_ctx = dce_ctx;
+
+ talloc_set_destructor(assoc_group, dcesrv_assoc_group_destructor);
+
+ return assoc_group;
+}
+
+NTSTATUS dcesrv_assoc_group_find_s4(
+ struct dcesrv_call_state *call,
+ void *private_data)
+{
+ /*
+ if provided, check the assoc_group is valid
+ */
+ if (call->pkt.u.bind.assoc_group_id != 0) {
+ call->conn->assoc_group =
+ dcesrv_assoc_group_reference(call->conn,
+ call->pkt.u.bind.assoc_group_id);
+ } else {
+ call->conn->assoc_group = dcesrv_assoc_group_new(call->conn);
+ }
+
+ /*
+ * The NETLOGON server does not use handles and so
+ * there is no need to support association groups, but
+ * we need to give back a number regardless.
+ *
+ * We have to do this when it is not run as a single process,
+ * because then it can't see the other valid association
+ * groups. We handle this genericly for all endpoints not
+ * running in single process mode.
+ *
+ * We know which endpoint we are on even before checking the
+ * iface UUID, so for simplicity we enforce the same policy
+ * for all interfaces on the endpoint.
+ *
+ * This means that where NETLOGON
+ * shares an endpoint (such as ncalrpc or if 'lsa over
+ * netlogon' is set) we will still check association groups.
+ *
+ */
+
+ if (call->conn->assoc_group == NULL &&
+ !call->conn->endpoint->use_single_process) {
+ call->conn->assoc_group
+ = dcesrv_assoc_group_new(call->conn);
+ }
+
+ if (call->conn->assoc_group == NULL) {
+ /* TODO Return correct status */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+void dcerpc_server_init(struct loadparm_context *lp_ctx)
+{
+ static bool initialized;
+#define _MODULE_PROTO(init) extern NTSTATUS init(TALLOC_CTX *);
+ STATIC_dcerpc_server_MODULES_PROTO;
+ init_module_fn static_init[] = { STATIC_dcerpc_server_MODULES };
+ init_module_fn *shared_init;
+
+ if (initialized) {
+ return;
+ }
+ initialized = true;
+
+ shared_init = load_samba_modules(NULL, "dcerpc_server");
+
+ run_init_functions(NULL, static_init);
+ run_init_functions(NULL, shared_init);
+
+ talloc_free(shared_init);
+}
+
+struct dcesrv_socket_context {
+ const struct dcesrv_endpoint *endpoint;
+ struct dcesrv_context *dcesrv_ctx;
+};
+
+static void dcesrv_sock_accept(struct stream_connection *srv_conn)
+{
+ NTSTATUS status;
+ struct dcesrv_socket_context *dcesrv_sock =
+ talloc_get_type(srv_conn->private_data, struct dcesrv_socket_context);
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dcesrv_sock->endpoint->ep_description);
+ struct dcesrv_connection *dcesrv_conn = NULL;
+ int ret;
+ struct loadparm_context *lp_ctx = dcesrv_sock->dcesrv_ctx->lp_ctx;
+
+ dcesrv_cleanup_broken_connections(dcesrv_sock->dcesrv_ctx);
+
+ if (!srv_conn->session_info) {
+ status = auth_anonymous_session_info(srv_conn,
+ lp_ctx,
+ &srv_conn->session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_sock_accept: auth_anonymous_session_info failed: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+ }
+
+ /*
+ * This fills in dcesrv_conn->endpoint with the endpoint
+ * associated with the socket. From this point on we know
+ * which (group of) services we are handling, but not the
+ * specific interface.
+ */
+
+ status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx,
+ srv_conn,
+ dcesrv_sock->endpoint,
+ srv_conn->session_info,
+ srv_conn->event.ctx,
+ DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
+ &dcesrv_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_sock_accept: dcesrv_endpoint_connect failed: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+
+ dcesrv_conn->transport.private_data = srv_conn;
+ dcesrv_conn->transport.report_output_data = dcesrv_sock_report_output_data;
+ dcesrv_conn->transport.terminate_connection = dcesrv_transport_terminate_connection_s4;
+
+ TALLOC_FREE(srv_conn->event.fde);
+
+ dcesrv_conn->send_queue = tevent_queue_create(dcesrv_conn, "dcesrv send queue");
+ if (!dcesrv_conn->send_queue) {
+ status = NT_STATUS_NO_MEMORY;
+ DEBUG(0,("dcesrv_sock_accept: tevent_queue_create(%s)\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+
+ if (transport == NCACN_NP) {
+ dcesrv_conn->stream = talloc_move(dcesrv_conn,
+ &srv_conn->tstream);
+ } else {
+ ret = tstream_bsd_existing_socket(dcesrv_conn,
+ socket_get_fd(srv_conn->socket),
+ &dcesrv_conn->stream);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(errno);
+ DEBUG(0, ("dcesrv_sock_accept: "
+ "failed to setup tstream: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+ socket_set_flags(srv_conn->socket, SOCKET_FLAG_NOCLOSE);
+ }
+
+ dcesrv_conn->local_address = srv_conn->local_address;
+ dcesrv_conn->remote_address = srv_conn->remote_address;
+
+ if (transport == NCALRPC) {
+ uid_t uid;
+ gid_t gid;
+ int sock_fd;
+
+ sock_fd = socket_get_fd(srv_conn->socket);
+ if (sock_fd == -1) {
+ stream_terminate_connection(
+ srv_conn, "socket_get_fd failed\n");
+ return;
+ }
+
+ ret = getpeereid(sock_fd, &uid, &gid);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(errno);
+ DEBUG(0, ("dcesrv_sock_accept: "
+ "getpeereid() failed for NCALRPC: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+ if (uid == dcesrv_conn->dce_ctx->initial_euid) {
+ struct tsocket_address *r = NULL;
+
+ ret = tsocket_address_unix_from_path(dcesrv_conn,
+ AS_SYSTEM_MAGIC_PATH_TOKEN,
+ &r);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(errno);
+ DEBUG(0, ("dcesrv_sock_accept: "
+ "tsocket_address_unix_from_path() failed for NCALRPC: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+ dcesrv_conn->remote_address = r;
+ }
+ }
+
+ srv_conn->private_data = dcesrv_conn;
+
+ status = dcesrv_connection_loop_start(dcesrv_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_sock_accept: dcerpc_read_fragment_buffer_send(%s)\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+
+ return;
+}
+
+static void dcesrv_sock_recv(struct stream_connection *conn, uint16_t flags)
+{
+ struct dcesrv_connection *dce_conn = talloc_get_type(conn->private_data,
+ struct dcesrv_connection);
+ dcesrv_terminate_connection(dce_conn, "dcesrv_sock_recv triggered");
+}
+
+static void dcesrv_sock_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct dcesrv_connection *dce_conn = talloc_get_type(conn->private_data,
+ struct dcesrv_connection);
+ dcesrv_terminate_connection(dce_conn, "dcesrv_sock_send triggered");
+}
+
+
+static const struct stream_server_ops dcesrv_stream_ops = {
+ .name = "rpc",
+ .accept_connection = dcesrv_sock_accept,
+ .recv_handler = dcesrv_sock_recv,
+ .send_handler = dcesrv_sock_send,
+};
+
+static NTSTATUS dcesrv_add_ep_unix(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx,
+ const struct model_ops *model_ops,
+ void *process_context)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ uint16_t port = 1;
+ NTSTATUS status;
+ const char *endpoint;
+
+ dcesrv_sock = talloc_zero(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ endpoint = dcerpc_binding_get_string_option(e->ep_description, "endpoint");
+
+ status = stream_setup_socket(dcesrv_sock, event_ctx, lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ "unix", endpoint, &port,
+ lpcfg_socket_options(lp_ctx),
+ dcesrv_sock, process_context);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("service_setup_stream_socket(path=%s) failed - %s\n",
+ endpoint, nt_errstr(status)));
+ }
+
+ return status;
+}
+
+static NTSTATUS dcesrv_add_ep_ncalrpc(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx,
+ const struct model_ops *model_ops,
+ void *process_context)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ uint16_t port = 1;
+ char *full_path;
+ NTSTATUS status;
+ const char *endpoint;
+
+ endpoint = dcerpc_binding_get_string_option(e->ep_description, "endpoint");
+
+ if (endpoint == NULL) {
+ /*
+ * No identifier specified: use DEFAULT.
+ *
+ * TODO: DO NOT hardcode this value anywhere else. Rather, specify
+ * no endpoint and let the epmapper worry about it.
+ */
+ endpoint = "DEFAULT";
+ status = dcerpc_binding_set_string_option(e->ep_description,
+ "endpoint",
+ endpoint);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcerpc_binding_set_string_option() failed - %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ full_path = talloc_asprintf(dce_ctx, "%s/%s", lpcfg_ncalrpc_dir(lp_ctx),
+ endpoint);
+
+ dcesrv_sock = talloc_zero(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = stream_setup_socket(dcesrv_sock, event_ctx, lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ "unix", full_path, &port,
+ lpcfg_socket_options(lp_ctx),
+ dcesrv_sock, process_context);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("service_setup_stream_socket(identifier=%s,path=%s) failed - %s\n",
+ endpoint, full_path, nt_errstr(status)));
+ }
+ return status;
+}
+
+static NTSTATUS dcesrv_add_ep_np(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx,
+ const struct model_ops *model_ops,
+ void *process_context)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ NTSTATUS status;
+ const char *endpoint;
+
+ endpoint = dcerpc_binding_get_string_option(e->ep_description, "endpoint");
+ if (endpoint == NULL) {
+ DEBUG(0, ("Endpoint mandatory for named pipes\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ dcesrv_sock = talloc_zero(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = tstream_setup_named_pipe(dce_ctx, event_ctx, lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ endpoint,
+ dcesrv_sock, process_context);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("stream_setup_named_pipe(pipe=%s) failed - %s\n",
+ endpoint, nt_errstr(status)));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ add a socket address to the list of events, one event per dcerpc endpoint
+*/
+static NTSTATUS add_socket_rpc_tcp_iface(struct dcesrv_context *dce_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx,
+ const struct model_ops *model_ops,
+ const char *address,
+ void *process_context)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ uint16_t port = 0;
+ NTSTATUS status;
+ const char *endpoint;
+ char port_str[6];
+
+ endpoint = dcerpc_binding_get_string_option(e->ep_description, "endpoint");
+ if (endpoint != NULL) {
+ port = atoi(endpoint);
+ }
+
+ dcesrv_sock = talloc_zero(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = stream_setup_socket(dcesrv_sock, event_ctx, dce_ctx->lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ "ip", address, &port,
+ lpcfg_socket_options(dce_ctx->lp_ctx),
+ dcesrv_sock, process_context);
+ if (!NT_STATUS_IS_OK(status)) {
+ struct dcesrv_if_list *iface;
+ DEBUG(0,("service_setup_stream_socket(address=%s,port=%u) for ",
+ address, port));
+ for (iface = e->interface_list; iface; iface = iface->next) {
+ DEBUGADD(0, ("%s ", iface->iface->name));
+ }
+ DEBUGADD(0, ("failed - %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ snprintf(port_str, sizeof(port_str), "%u", port);
+
+ status = dcerpc_binding_set_string_option(e->ep_description,
+ "endpoint", port_str);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcerpc_binding_set_string_option(endpoint, %s) failed - %s\n",
+ port_str, nt_errstr(status)));
+ return status;
+ } else {
+ struct dcesrv_if_list *iface;
+ DEBUG(4,("Successfully listening on ncacn_ip_tcp endpoint [%s]:[%s] for ",
+ address, port_str));
+ for (iface = e->interface_list; iface; iface = iface->next) {
+ DEBUGADD(4, ("%s ", iface->iface->name));
+ }
+ DEBUGADD(4, ("\n"));
+ }
+
+ return NT_STATUS_OK;
+}
+
+#include "lib/socket/netif.h" /* Included here to work around the fact that socket_wrapper redefines bind() */
+
+static NTSTATUS dcesrv_add_ep_tcp(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx,
+ const struct model_ops *model_ops,
+ void *process_context)
+{
+ NTSTATUS status;
+
+ /* Add TCP/IP sockets */
+ if (lpcfg_interfaces(lp_ctx) && lpcfg_bind_interfaces_only(lp_ctx)) {
+ int num_interfaces;
+ int i;
+ struct interface *ifaces;
+
+ load_interface_list(dce_ctx, lp_ctx, &ifaces);
+
+ num_interfaces = iface_list_count(ifaces);
+ for(i = 0; i < num_interfaces; i++) {
+ const char *address = iface_list_n_ip(ifaces, i);
+ status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx,
+ model_ops, address,
+ process_context);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+ } else {
+ char **wcard;
+ size_t i;
+ size_t num_binds = 0;
+ wcard = iface_list_wildcard(dce_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(wcard);
+ for (i=0; wcard[i]; i++) {
+ status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx,
+ model_ops, wcard[i],
+ process_context);
+ if (NT_STATUS_IS_OK(status)) {
+ num_binds++;
+ }
+ }
+ talloc_free(wcard);
+ if (num_binds == 0) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS dcesrv_add_ep(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx,
+ const struct model_ops *model_ops,
+ void *process_context)
+{
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(e->ep_description);
+
+ switch (transport) {
+ case NCACN_UNIX_STREAM:
+ return dcesrv_add_ep_unix(dce_ctx, lp_ctx, e, event_ctx,
+ model_ops, process_context);
+
+ case NCALRPC:
+ return dcesrv_add_ep_ncalrpc(dce_ctx, lp_ctx, e, event_ctx,
+ model_ops, process_context);
+
+ case NCACN_IP_TCP:
+ return dcesrv_add_ep_tcp(dce_ctx, lp_ctx, e, event_ctx,
+ model_ops, process_context);
+
+ case NCACN_NP:
+ return dcesrv_add_ep_np(dce_ctx, lp_ctx, e, event_ctx,
+ model_ops, process_context);
+
+ default:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+}
+
+_PUBLIC_ struct imessaging_context *dcesrv_imessaging_context(
+ struct dcesrv_connection *conn)
+{
+ struct stream_connection *srv_conn =
+ talloc_get_type_abort(conn->transport.private_data,
+ struct stream_connection);
+ return srv_conn->msg_ctx;
+}
+
+_PUBLIC_ struct server_id dcesrv_server_id(struct dcesrv_connection *conn)
+{
+ struct stream_connection *srv_conn =
+ talloc_get_type_abort(conn->transport.private_data,
+ struct stream_connection);
+ return srv_conn->server_id;
+}
+
+void log_successful_dcesrv_authz_event(
+ struct dcesrv_call_state *call,
+ void *private_data)
+{
+ struct dcesrv_auth *auth = call->auth_state;
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(call->conn->endpoint->ep_description);
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(call->conn);
+ const char *auth_type = derpc_transport_string_by_transport(transport);
+ const char *transport_protection = AUTHZ_TRANSPORT_PROTECTION_NONE;
+
+ if (transport == NCACN_NP) {
+ transport_protection = AUTHZ_TRANSPORT_PROTECTION_SMB;
+ }
+
+ /*
+ * Log the authorization to this RPC interface. This
+ * covered ncacn_np pass-through auth, and anonymous
+ * DCE/RPC (eg epmapper, netlogon etc)
+ */
+ log_successful_authz_event(imsg_ctx,
+ call->conn->dce_ctx->lp_ctx,
+ call->conn->remote_address,
+ call->conn->local_address,
+ "DCE/RPC",
+ auth_type,
+ transport_protection,
+ auth->session_info);
+
+ auth->auth_audited = true;
+}
+
+NTSTATUS dcesrv_gensec_prepare(
+ TALLOC_CTX *mem_ctx,
+ struct dcesrv_call_state *call,
+ struct gensec_security **out,
+ void *private_data)
+{
+ struct cli_credentials *server_creds = NULL;
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(call->conn);
+ bool ok;
+
+ server_creds = cli_credentials_init_server(call->auth_state,
+ call->conn->dce_ctx->lp_ctx);
+ if (server_creds == NULL) {
+ DEBUG(1, ("Failed to init server credentials\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ /* This is required for ncalrpc_as_system. */
+ ok = cli_credentials_set_kerberos_state(server_creds,
+ CRED_USE_KERBEROS_DESIRED,
+ CRED_SPECIFIED);
+ if (!ok) {
+ DBG_WARNING("Failed to set kerberos state\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return samba_server_gensec_start(mem_ctx,
+ call->event_ctx,
+ imsg_ctx,
+ call->conn->dce_ctx->lp_ctx,
+ server_creds,
+ NULL,
+ out);
+}
+
+void dcesrv_transport_terminate_connection_s4(struct dcesrv_connection *dce_conn,
+ const char *reason)
+{
+ struct stream_connection *srv_conn =
+ talloc_get_type_abort(dce_conn->transport.private_data,
+ struct stream_connection);
+ stream_terminate_connection(srv_conn, reason);
+}
diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h
new file mode 100644
index 0000000..529c604
--- /dev/null
+++ b/source4/rpc_server/dcerpc_server.h
@@ -0,0 +1,41 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ server side dcerpc defines
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 2004-2005
+
+ 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 SAMBA_DCERPC_SERVER_H
+#define SAMBA_DCERPC_SERVER_H
+
+#include "librpc/rpc/dcesrv_core.h"
+
+struct model_ops;
+
+NTSTATUS dcesrv_add_ep(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx,
+ const struct model_ops *model_ops,
+ void *process_context);
+
+_PUBLIC_ struct imessaging_context *dcesrv_imessaging_context(
+ struct dcesrv_connection *conn);
+_PUBLIC_ struct server_id dcesrv_server_id(struct dcesrv_connection *conn);
+
+#endif /* SAMBA_DCERPC_SERVER_H */
diff --git a/source4/rpc_server/dcerpc_server.pc.in b/source4/rpc_server/dcerpc_server.pc.in
new file mode 100644
index 0000000..0ab833a
--- /dev/null
+++ b/source4/rpc_server/dcerpc_server.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+modulesdir=@modulesdir@/dcerpc_server
+
+Name: dcerpc_server
+Description: DCE/RPC server library
+Requires: dcerpc
+Version: @PACKAGE_VERSION@
+Libs: @LIB_RPATH@ -L${libdir} -ldcerpc-server
+Cflags: -I${includedir} -DHAVE_IMMEDIATE_STRUCTURES=1
diff --git a/source4/rpc_server/dnsserver/dcerpc_dnsserver.c b/source4/rpc_server/dnsserver/dcerpc_dnsserver.c
new file mode 100644
index 0000000..ea4d86d
--- /dev/null
+++ b/source4/rpc_server/dnsserver/dcerpc_dnsserver.c
@@ -0,0 +1,2431 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DNS Server
+
+ Copyright (C) Amitay Isaacs 2011
+
+ 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 "includes.h"
+#include "talloc.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_dnsserver.h"
+#include "dns_server/dnsserver_common.h"
+#include "dnsserver.h"
+
+#undef strcasecmp
+
+#define DCESRV_INTERFACE_DNSSERVER_BIND(context, iface) \
+ dcesrv_interface_dnsserver_bind(context, iface)
+static NTSTATUS dcesrv_interface_dnsserver_bind(struct dcesrv_connection_context *context,
+ const struct dcesrv_interface *iface)
+{
+ return dcesrv_interface_bind_require_integrity(context, iface);
+}
+
+#define DNSSERVER_STATE_MAGIC 0xc9657ab4
+struct dnsserver_state {
+ struct loadparm_context *lp_ctx;
+ struct ldb_context *samdb;
+ struct dnsserver_partition *partitions;
+ struct dnsserver_zone *zones;
+ int zones_count;
+ struct dnsserver_serverinfo *serverinfo;
+};
+
+
+/* Utility functions */
+
+static void dnsserver_reload_zones(struct dnsserver_state *dsstate)
+{
+ struct dnsserver_partition *p;
+ struct dnsserver_zone *zones, *z, *znext, *zmatch;
+ struct dnsserver_zone *old_list, *new_list;
+
+ old_list = dsstate->zones;
+ new_list = NULL;
+
+ for (p = dsstate->partitions; p; p = p->next) {
+ zones = dnsserver_db_enumerate_zones(dsstate, dsstate->samdb, p);
+ if (zones == NULL) {
+ continue;
+ }
+ for (z = zones; z; ) {
+ znext = z->next;
+ zmatch = dnsserver_find_zone(old_list, z->name);
+ if (zmatch == NULL) {
+ /* Missing zone */
+ z->zoneinfo = dnsserver_init_zoneinfo(z, dsstate->serverinfo);
+ if (z->zoneinfo == NULL) {
+ continue;
+ }
+ DLIST_ADD_END(new_list, z);
+ p->zones_count++;
+ dsstate->zones_count++;
+ } else {
+ /* Existing zone */
+ talloc_free(z);
+ DLIST_REMOVE(old_list, zmatch);
+ DLIST_ADD_END(new_list, zmatch);
+ }
+ z = znext;
+ }
+ }
+
+ if (new_list == NULL) {
+ return;
+ }
+
+ /* Deleted zones */
+ for (z = old_list; z; ) {
+ znext = z->next;
+ z->partition->zones_count--;
+ dsstate->zones_count--;
+ talloc_free(z);
+ z = znext;
+ }
+
+ dsstate->zones = new_list;
+}
+
+
+static struct dnsserver_state *dnsserver_connect(struct dcesrv_call_state *dce_call)
+{
+ struct dnsserver_state *dsstate;
+ struct dnsserver_zone *zones, *z, *znext;
+ struct dnsserver_partition *partitions, *p;
+ NTSTATUS status;
+
+ dsstate = dcesrv_iface_state_find_conn(dce_call,
+ DNSSERVER_STATE_MAGIC,
+ struct dnsserver_state);
+ if (dsstate != NULL) {
+ return dsstate;
+ }
+
+ dsstate = talloc_zero(dce_call, struct dnsserver_state);
+ if (dsstate == NULL) {
+ return NULL;
+ }
+
+ dsstate->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+
+ dsstate->samdb = dcesrv_samdb_connect_as_user(dsstate, dce_call);
+ if (dsstate->samdb == NULL) {
+ DEBUG(0,("dnsserver: Failed to open samdb"));
+ goto failed;
+ }
+
+ /* Initialize server info */
+ dsstate->serverinfo = dnsserver_init_serverinfo(dsstate,
+ dsstate->lp_ctx,
+ dsstate->samdb);
+ if (dsstate->serverinfo == NULL) {
+ goto failed;
+ }
+
+ /* Search for DNS partitions */
+ partitions = dnsserver_db_enumerate_partitions(dsstate, dsstate->serverinfo, dsstate->samdb);
+ if (partitions == NULL) {
+ goto failed;
+ }
+ dsstate->partitions = partitions;
+
+ /* Search for DNS zones */
+ for (p = partitions; p; p = p->next) {
+ zones = dnsserver_db_enumerate_zones(dsstate, dsstate->samdb, p);
+ if (zones == NULL) {
+ goto failed;
+ }
+ for (z = zones; z; ) {
+ znext = z->next;
+ if (dnsserver_find_zone(dsstate->zones, z->name) == NULL) {
+ z->zoneinfo = dnsserver_init_zoneinfo(z, dsstate->serverinfo);
+ if (z->zoneinfo == NULL) {
+ goto failed;
+ }
+ DLIST_ADD_END(dsstate->zones, z);
+ p->zones_count++;
+ dsstate->zones_count++;
+ } else {
+ /* Ignore duplicate zone */
+ DEBUG(3,("dnsserver: Ignoring duplicate zone '%s' from '%s'",
+ z->name, ldb_dn_get_linearized(z->zone_dn)));
+ }
+ z = znext;
+ }
+ }
+
+ status = dcesrv_iface_state_store_conn(dce_call,
+ DNSSERVER_STATE_MAGIC,
+ dsstate);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ return dsstate;
+
+failed:
+ talloc_free(dsstate);
+ dsstate = NULL;
+ return NULL;
+}
+
+
+/* dnsserver query functions */
+
+/* [MS-DNSP].pdf Section 3.1.1.1 DNS Server Configuration Information */
+static WERROR dnsserver_query_server(struct dnsserver_state *dsstate,
+ TALLOC_CTX *mem_ctx,
+ const char *operation,
+ const unsigned int client_version,
+ enum DNS_RPC_TYPEID *typeid,
+ union DNSSRV_RPC_UNION *r)
+{
+ uint8_t is_integer, is_addresses, is_string, is_wstring, is_stringlist;
+ uint32_t answer_integer;
+ struct IP4_ARRAY *answer_iparray;
+ struct DNS_ADDR_ARRAY *answer_addrarray;
+ char *answer_string;
+ struct DNS_RPC_UTF8_STRING_LIST *answer_stringlist;
+ struct dnsserver_serverinfo *serverinfo;
+
+ serverinfo = dsstate->serverinfo;
+
+ if (strcasecmp(operation, "ServerInfo") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_W2K) {
+ *typeid = DNSSRV_TYPEID_SERVER_INFO_W2K;
+ r->ServerInfoW2K = talloc_zero(mem_ctx, struct DNS_RPC_SERVER_INFO_W2K);
+
+ r->ServerInfoW2K->dwVersion = serverinfo->dwVersion;
+ r->ServerInfoW2K->fBootMethod = serverinfo->fBootMethod;
+ r->ServerInfoW2K->fAdminConfigured = serverinfo->fAdminConfigured;
+ r->ServerInfoW2K->fAllowUpdate = serverinfo->fAllowUpdate;
+ r->ServerInfoW2K->fDsAvailable = serverinfo->fDsAvailable;
+ r->ServerInfoW2K->pszServerName = talloc_strdup(mem_ctx, serverinfo->pszServerName);
+ r->ServerInfoW2K->pszDsContainer = talloc_strdup(mem_ctx, serverinfo->pszDsContainer);
+ r->ServerInfoW2K->aipServerAddrs = dns_addr_array_to_ip4_array(mem_ctx,
+ serverinfo->aipServerAddrs);
+ r->ServerInfoW2K->aipListenAddrs = dns_addr_array_to_ip4_array(mem_ctx,
+ serverinfo->aipListenAddrs);
+ r->ServerInfoW2K->aipForwarders = ip4_array_copy(mem_ctx, serverinfo->aipForwarders);
+ r->ServerInfoW2K->dwLogLevel = serverinfo->dwLogLevel;
+ r->ServerInfoW2K->dwDebugLevel = serverinfo->dwDebugLevel;
+ r->ServerInfoW2K->dwForwardTimeout = serverinfo->dwForwardTimeout;
+ r->ServerInfoW2K->dwRpcProtocol = serverinfo->dwRpcProtocol;
+ r->ServerInfoW2K->dwNameCheckFlag = serverinfo->dwNameCheckFlag;
+ r->ServerInfoW2K->cAddressAnswerLimit = serverinfo->cAddressAnswerLimit;
+ r->ServerInfoW2K->dwRecursionRetry = serverinfo->dwRecursionRetry;
+ r->ServerInfoW2K->dwRecursionTimeout = serverinfo->dwRecursionTimeout;
+ r->ServerInfoW2K->dwMaxCacheTtl = serverinfo->dwMaxCacheTtl;
+ r->ServerInfoW2K->dwDsPollingInterval = serverinfo->dwDsPollingInterval;
+ r->ServerInfoW2K->dwScavengingInterval = serverinfo->dwScavengingInterval;
+ r->ServerInfoW2K->dwDefaultRefreshInterval = serverinfo->dwDefaultRefreshInterval;
+ r->ServerInfoW2K->dwDefaultNoRefreshInterval = serverinfo->dwDefaultNoRefreshInterval;
+ r->ServerInfoW2K->fAutoReverseZones = serverinfo->fAutoReverseZones;
+ r->ServerInfoW2K->fAutoCacheUpdate = serverinfo->fAutoCacheUpdate;
+ r->ServerInfoW2K->fRecurseAfterForwarding = serverinfo->fRecurseAfterForwarding;
+ r->ServerInfoW2K->fForwardDelegations = serverinfo->fForwardDelegations;
+ r->ServerInfoW2K->fNoRecursion = serverinfo->fNoRecursion;
+ r->ServerInfoW2K->fSecureResponses = serverinfo->fSecureResponses;
+ r->ServerInfoW2K->fRoundRobin = serverinfo->fRoundRobin;
+ r->ServerInfoW2K->fLocalNetPriority = serverinfo->fLocalNetPriority;
+ r->ServerInfoW2K->fBindSecondaries = serverinfo->fBindSecondaries;
+ r->ServerInfoW2K->fWriteAuthorityNs = serverinfo->fWriteAuthorityNs;
+ r->ServerInfoW2K->fStrictFileParsing = serverinfo->fStrictFileParsing;
+ r->ServerInfoW2K->fLooseWildcarding = serverinfo->fLooseWildcarding;
+ r->ServerInfoW2K->fDefaultAgingState = serverinfo->fDefaultAgingState;
+
+ } else if (client_version == DNS_CLIENT_VERSION_DOTNET) {
+ *typeid = DNSSRV_TYPEID_SERVER_INFO_DOTNET;
+ r->ServerInfoDotNet = talloc_zero(mem_ctx, struct DNS_RPC_SERVER_INFO_DOTNET);
+
+ r->ServerInfoDotNet->dwRpcStructureVersion = 0x01;
+ r->ServerInfoDotNet->dwVersion = serverinfo->dwVersion;
+ r->ServerInfoDotNet->fBootMethod = serverinfo->fBootMethod;
+ r->ServerInfoDotNet->fAdminConfigured = serverinfo->fAdminConfigured;
+ r->ServerInfoDotNet->fAllowUpdate = serverinfo->fAllowUpdate;
+ r->ServerInfoDotNet->fDsAvailable = serverinfo->fDsAvailable;
+ r->ServerInfoDotNet->pszServerName = talloc_strdup(mem_ctx, serverinfo->pszServerName);
+ r->ServerInfoDotNet->pszDsContainer = talloc_strdup(mem_ctx, serverinfo->pszDsContainer);
+ r->ServerInfoDotNet->aipServerAddrs = dns_addr_array_to_ip4_array(mem_ctx,
+ serverinfo->aipServerAddrs);
+ r->ServerInfoDotNet->aipListenAddrs = dns_addr_array_to_ip4_array(mem_ctx,
+ serverinfo->aipListenAddrs);
+ r->ServerInfoDotNet->aipForwarders = ip4_array_copy(mem_ctx, serverinfo->aipForwarders);
+ r->ServerInfoDotNet->aipLogFilter = ip4_array_copy(mem_ctx, serverinfo->aipLogFilter);
+ r->ServerInfoDotNet->pwszLogFilePath = talloc_strdup(mem_ctx, serverinfo->pwszLogFilePath);
+ r->ServerInfoDotNet->pszDomainName = talloc_strdup(mem_ctx, serverinfo->pszDomainName);
+ r->ServerInfoDotNet->pszForestName = talloc_strdup(mem_ctx, serverinfo->pszForestName);
+ r->ServerInfoDotNet->pszDomainDirectoryPartition = talloc_strdup(mem_ctx, serverinfo->pszDomainDirectoryPartition);
+ r->ServerInfoDotNet->pszForestDirectoryPartition = talloc_strdup(mem_ctx, serverinfo->pszForestDirectoryPartition);
+ r->ServerInfoDotNet->dwLogLevel = serverinfo->dwLogLevel;
+ r->ServerInfoDotNet->dwDebugLevel = serverinfo->dwDebugLevel;
+ r->ServerInfoDotNet->dwForwardTimeout = serverinfo->dwForwardTimeout;
+ r->ServerInfoDotNet->dwRpcProtocol = serverinfo->dwRpcProtocol;
+ r->ServerInfoDotNet->dwNameCheckFlag = serverinfo->dwNameCheckFlag;
+ r->ServerInfoDotNet->cAddressAnswerLimit = serverinfo->cAddressAnswerLimit;
+ r->ServerInfoDotNet->dwRecursionRetry = serverinfo->dwRecursionRetry;
+ r->ServerInfoDotNet->dwRecursionTimeout = serverinfo->dwRecursionTimeout;
+ r->ServerInfoDotNet->dwMaxCacheTtl = serverinfo->dwMaxCacheTtl;
+ r->ServerInfoDotNet->dwDsPollingInterval = serverinfo->dwDsPollingInterval;
+ r->ServerInfoDotNet->dwLocalNetPriorityNetMask = serverinfo->dwLocalNetPriorityNetMask;
+ r->ServerInfoDotNet->dwScavengingInterval = serverinfo->dwScavengingInterval;
+ r->ServerInfoDotNet->dwDefaultRefreshInterval = serverinfo->dwDefaultRefreshInterval;
+ r->ServerInfoDotNet->dwDefaultNoRefreshInterval = serverinfo->dwDefaultNoRefreshInterval;
+ r->ServerInfoDotNet->dwLastScavengeTime = serverinfo->dwLastScavengeTime;
+ r->ServerInfoDotNet->dwEventLogLevel = serverinfo->dwEventLogLevel;
+ r->ServerInfoDotNet->dwLogFileMaxSize = serverinfo->dwLogFileMaxSize;
+ r->ServerInfoDotNet->dwDsForestVersion = serverinfo->dwDsForestVersion;
+ r->ServerInfoDotNet->dwDsDomainVersion = serverinfo->dwDsDomainVersion;
+ r->ServerInfoDotNet->dwDsDsaVersion = serverinfo->dwDsDsaVersion;
+ r->ServerInfoDotNet->fAutoReverseZones = serverinfo->fAutoReverseZones;
+ r->ServerInfoDotNet->fAutoCacheUpdate = serverinfo->fAutoCacheUpdate;
+ r->ServerInfoDotNet->fRecurseAfterForwarding = serverinfo->fRecurseAfterForwarding;
+ r->ServerInfoDotNet->fForwardDelegations = serverinfo->fForwardDelegations;
+ r->ServerInfoDotNet->fNoRecursion = serverinfo->fNoRecursion;
+ r->ServerInfoDotNet->fSecureResponses = serverinfo->fSecureResponses;
+ r->ServerInfoDotNet->fRoundRobin = serverinfo->fRoundRobin;
+ r->ServerInfoDotNet->fLocalNetPriority = serverinfo->fLocalNetPriority;
+ r->ServerInfoDotNet->fBindSecondaries = serverinfo->fBindSecondaries;
+ r->ServerInfoDotNet->fWriteAuthorityNs = serverinfo->fWriteAuthorityNs;
+ r->ServerInfoDotNet->fStrictFileParsing = serverinfo->fStrictFileParsing;
+ r->ServerInfoDotNet->fLooseWildcarding = serverinfo->fLooseWildcarding;
+ r->ServerInfoDotNet->fDefaultAgingState = serverinfo->fDefaultAgingState;
+
+ } else if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ *typeid = DNSSRV_TYPEID_SERVER_INFO;
+ r->ServerInfo = talloc_zero(mem_ctx, struct DNS_RPC_SERVER_INFO_LONGHORN);
+
+ r->ServerInfo->dwRpcStructureVersion = 0x02;
+ r->ServerInfo->dwVersion = serverinfo->dwVersion;
+ r->ServerInfo->fBootMethod = serverinfo->fBootMethod;
+ r->ServerInfo->fAdminConfigured = serverinfo->fAdminConfigured;
+ r->ServerInfo->fAllowUpdate = serverinfo->fAllowUpdate;
+ r->ServerInfo->fDsAvailable = serverinfo->fDsAvailable;
+ r->ServerInfo->pszServerName = talloc_strdup(mem_ctx, serverinfo->pszServerName);
+ r->ServerInfo->pszDsContainer = talloc_strdup(mem_ctx, serverinfo->pszDsContainer);
+ r->ServerInfo->aipServerAddrs = serverinfo->aipServerAddrs;
+ r->ServerInfo->aipListenAddrs = serverinfo->aipListenAddrs;
+ r->ServerInfo->aipForwarders = ip4_array_to_dns_addr_array(mem_ctx, serverinfo->aipForwarders);
+ r->ServerInfo->aipLogFilter = ip4_array_to_dns_addr_array(mem_ctx, serverinfo->aipLogFilter);
+ r->ServerInfo->pwszLogFilePath = talloc_strdup(mem_ctx, serverinfo->pwszLogFilePath);
+ r->ServerInfo->pszDomainName = talloc_strdup(mem_ctx, serverinfo->pszDomainName);
+ r->ServerInfo->pszForestName = talloc_strdup(mem_ctx, serverinfo->pszForestName);
+ r->ServerInfo->pszDomainDirectoryPartition = talloc_strdup(mem_ctx, serverinfo->pszDomainDirectoryPartition);
+ r->ServerInfo->pszForestDirectoryPartition = talloc_strdup(mem_ctx, serverinfo->pszForestDirectoryPartition);
+ r->ServerInfo->dwLogLevel = serverinfo->dwLogLevel;
+ r->ServerInfo->dwDebugLevel = serverinfo->dwDebugLevel;
+ r->ServerInfo->dwForwardTimeout = serverinfo->dwForwardTimeout;
+ r->ServerInfo->dwRpcProtocol = serverinfo->dwRpcProtocol;
+ r->ServerInfo->dwNameCheckFlag = serverinfo->dwNameCheckFlag;
+ r->ServerInfo->cAddressAnswerLimit = serverinfo->cAddressAnswerLimit;
+ r->ServerInfo->dwRecursionRetry = serverinfo->dwRecursionRetry;
+ r->ServerInfo->dwRecursionTimeout = serverinfo->dwRecursionTimeout;
+ r->ServerInfo->dwMaxCacheTtl = serverinfo->dwMaxCacheTtl;
+ r->ServerInfo->dwDsPollingInterval = serverinfo->dwDsPollingInterval;
+ r->ServerInfo->dwLocalNetPriorityNetMask = serverinfo->dwLocalNetPriorityNetMask;
+ r->ServerInfo->dwScavengingInterval = serverinfo->dwScavengingInterval;
+ r->ServerInfo->dwDefaultRefreshInterval = serverinfo->dwDefaultRefreshInterval;
+ r->ServerInfo->dwDefaultNoRefreshInterval = serverinfo->dwDefaultNoRefreshInterval;
+ r->ServerInfo->dwLastScavengeTime = serverinfo->dwLastScavengeTime;
+ r->ServerInfo->dwEventLogLevel = serverinfo->dwEventLogLevel;
+ r->ServerInfo->dwLogFileMaxSize = serverinfo->dwLogFileMaxSize;
+ r->ServerInfo->dwDsForestVersion = serverinfo->dwDsForestVersion;
+ r->ServerInfo->dwDsDomainVersion = serverinfo->dwDsDomainVersion;
+ r->ServerInfo->dwDsDsaVersion = serverinfo->dwDsDsaVersion;
+ r->ServerInfo->fReadOnlyDC = serverinfo->fReadOnlyDC;
+ r->ServerInfo->fAutoReverseZones = serverinfo->fAutoReverseZones;
+ r->ServerInfo->fAutoCacheUpdate = serverinfo->fAutoCacheUpdate;
+ r->ServerInfo->fRecurseAfterForwarding = serverinfo->fRecurseAfterForwarding;
+ r->ServerInfo->fForwardDelegations = serverinfo->fForwardDelegations;
+ r->ServerInfo->fNoRecursion = serverinfo->fNoRecursion;
+ r->ServerInfo->fSecureResponses = serverinfo->fSecureResponses;
+ r->ServerInfo->fRoundRobin = serverinfo->fRoundRobin;
+ r->ServerInfo->fLocalNetPriority = serverinfo->fLocalNetPriority;
+ r->ServerInfo->fBindSecondaries = serverinfo->fBindSecondaries;
+ r->ServerInfo->fWriteAuthorityNs = serverinfo->fWriteAuthorityNs;
+ r->ServerInfo->fStrictFileParsing = serverinfo->fStrictFileParsing;
+ r->ServerInfo->fLooseWildcarding = serverinfo->fLooseWildcarding;
+ r->ServerInfo->fDefaultAgingState = serverinfo->fDefaultAgingState;
+ }
+ return WERR_OK;
+ }
+
+ is_integer = 0;
+ answer_integer = 0;
+
+ if (strcasecmp(operation, "AddressAnswerLimit") == 0) {
+ answer_integer = serverinfo->cAddressAnswerLimit;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "AdminConfigured") == 0) {
+ answer_integer = serverinfo->fAdminConfigured;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "AllowCNAMEAtNS") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "AllowUpdate") == 0) {
+ answer_integer = serverinfo->fAllowUpdate;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "AutoCacheUpdate") == 0) {
+ answer_integer = serverinfo->fAutoCacheUpdate;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "AutoConfigFileZones") == 0) {
+ answer_integer = 1;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "BindSecondaries") == 0) {
+ answer_integer = serverinfo->fBindSecondaries;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "BootMethod") == 0) {
+ answer_integer = serverinfo->fBootMethod;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DebugLevel") == 0) {
+ answer_integer = serverinfo->dwDebugLevel;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DefaultAgingState") == 0) {
+ answer_integer = serverinfo->fDefaultAgingState;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DefaultNoRefreshInterval") == 0) {
+ answer_integer = serverinfo->dwDefaultNoRefreshInterval;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DefaultRefreshInterval") == 0) {
+ answer_integer = serverinfo->dwDefaultRefreshInterval;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DeleteOutsideGlue") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DisjointNets") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DsLazyUpdateInterval") == 0) {
+ answer_integer = 3; /* seconds */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DsPollingInterval") == 0) {
+ answer_integer = serverinfo->dwDsPollingInterval;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DsTombstoneInterval") == 0) {
+ answer_integer = 0x00127500; /* 14 days */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableRegistryBoot") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EventLogLevel") == 0) {
+ answer_integer = serverinfo->dwEventLogLevel;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ForceSoaSerial") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ForceSaoRetry") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ForceSoaRefresh") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ForceSoaMinimumTtl") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ForwardDelegations") == 0) {
+ answer_integer = 1;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ForwardingTimeout") == 0) {
+ answer_integer = serverinfo->dwForwardTimeout;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "IsSlave") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "LocalNetPriority") == 0) {
+ answer_integer = serverinfo->fLocalNetPriority;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "LogFileMaxSize") == 0) {
+ answer_integer = serverinfo->dwLogFileMaxSize;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "LogLevel") == 0) {
+ answer_integer = serverinfo->dwLogLevel;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "LooseWildcarding") == 0) {
+ answer_integer = serverinfo->fLooseWildcarding;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "MaxCacheTtl") == 0) {
+ answer_integer = serverinfo->dwMaxCacheTtl;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "MaxNegativeCacheTtl") == 0) {
+ answer_integer = 0x00000384; /* 15 minutes */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "NameCheckFlag") == 0) {
+ answer_integer = serverinfo->dwNameCheckFlag;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "NoRecursion") == 0) {
+ answer_integer = serverinfo->fNoRecursion;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "NoUpdateDelegations") == 0) {
+ answer_integer = 1;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "PublishAutonet") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "QuietRecvFaultInterval") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "QuietRecvLogInterval") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "RecursionRetry") == 0) {
+ answer_integer = serverinfo->dwRecursionRetry;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "RecursionTimeout") == 0) {
+ answer_integer = serverinfo->dwRecursionTimeout;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ReloadException") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "RoundRobin") == 0) {
+ answer_integer = serverinfo->fRoundRobin;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "RpcProtocol") == 0) {
+ answer_integer = serverinfo->dwRpcProtocol;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "SecureResponses") == 0) {
+ answer_integer = serverinfo->fSecureResponses;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "SendPort") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ScavengingInterval") == 0) {
+ answer_integer = serverinfo->dwScavengingInterval;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "SocketPoolSize") == 0) {
+ answer_integer = 0x000009C4;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "StrictFileParsing") == 0) {
+ answer_integer = serverinfo->fStrictFileParsing;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "SyncDnsZoneSerial") == 0) {
+ answer_integer = 2; /* ZONE_SERIAL_SYNC_XFER */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "UpdateOptions") == 0) {
+ answer_integer = 0x0000030F; /* DNS_DEFAULT_UPDATE_OPTIONS */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "UseSystemEvengLog") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "Version") == 0) {
+ answer_integer = serverinfo->dwVersion;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "XfrConnectTimeout") == 0) {
+ answer_integer = 0x0000001E;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "WriteAuthorityNs") == 0) {
+ answer_integer = serverinfo->fWriteAuthorityNs;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "AdditionalRecursionTimeout") == 0) {
+ answer_integer = 0x00000004;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "AppendMsZoneTransferFlag") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "AutoCreateDelegations") == 0) {
+ answer_integer = 0; /* DNS_ACD_DONT_CREATE */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "BreakOnAscFailure") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "CacheEmptyAuthResponses") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DirectoryPartitionAutoEnlistInterval") == 0) {
+ answer_integer = 0x00015180; /* 1 day */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DisableAutoReverseZones") == 0) {
+ answer_integer = ~serverinfo->fAutoReverseZones;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EDnsCacheTimeout") == 0) {
+ answer_integer = 0x00000384; /* 15 minutes */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableDirectoryPartitions") == 0) {
+ answer_integer = serverinfo->fDsAvailable;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableDnsSec") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableEDnsProbes") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableEDnsReception") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableIPv6") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableIQueryResponseGeneration") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableSendErrorSuppression") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableUpdateForwarding") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableWinsR") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ForceDsaBehaviorVersion") == 0) {
+ answer_integer = serverinfo->dwDsDsaVersion;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ForceDomainBehaviorVersion") == 0) {
+ answer_integer = serverinfo->dwDsDsaVersion;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ForceForestBehaviorVersion") == 0) {
+ answer_integer = serverinfo->dwDsDsaVersion;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "HeapDebug") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "LameDelegationTtl") == 0) {
+ answer_integer = 0; /* seconds */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "LocalNetPriorityNetMask") == 0) {
+ answer_integer = serverinfo->dwLocalNetPriorityNetMask;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "MaxCacheSize") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "MaxResourceRecordsInNonSecureUpdate") == 0) {
+ answer_integer = 0x0000001E;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "OperationsLogLevel") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "OperationsLogLevel2") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "MaximumUdpPacketSize") == 0) {
+ answer_integer = 0x00004000; /* maximum possible */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "RecurseToInternetRootMask") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "SelfTest") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "SilentlyIgnoreCNameUpdateConflicts") == 0) {
+ answer_integer = 1;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "TcpReceivePacketSize") == 0) {
+ answer_integer = 0x00010000;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "XfrThrottleMultiplier") == 0) {
+ answer_integer = 0x0000000A;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "AllowMsdcsLookupRetry") == 0) {
+ answer_integer = 1;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "AllowReadOnlyZoneTransfer") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DsBackGroundLoadPaused") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DsMinimumBackgroundLoadThreads") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DsRemoteReplicationDelay") == 0) {
+ answer_integer = 0x0000001E; /* 30 seconds */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableDuplicateQuerySuppresion") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableGlobalNamesSupport") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableVersionQuery") == 0) {
+ answer_integer = 1; /* DNS_VERSION_QUERY_FULL */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableRsoForRodc") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ForceRODCMode") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "GlobalNamesAlwaysQuerySrv") == 0) {
+ answer_integer = 1;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "GlobalNamesBlockUpdates") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "GlobalNamesEnableEDnsProbes") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "GlobalNamesPreferAAAA") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "GlobalNamesQueryOrder") == 0) {
+ answer_integer = 1;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "GlobalNamesSendTimeout") == 0) {
+ answer_integer = 3; /* seconds */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "GlobalNamesServerQueryInterval") == 0) {
+ answer_integer = 0x00005460; /* 6 hours */
+ is_integer = 1;
+ } else if (strcasecmp(operation, "RemoteIPv4RankBoost") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "RemoteIPv6RankBoost") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "MaximumRodcRsoAttemptsPerCycle") == 0) {
+ answer_integer = 0x00000064;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "MaximumRodcRsoQueueLength") == 0) {
+ answer_integer = 0x0000012C;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "EnableGlobalQueryBlockList") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "OpenACLOnProxyUpdates") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "CacheLockingPercent") == 0) {
+ answer_integer = 0x00000064;
+ is_integer = 1;
+ }
+
+ if (is_integer == 1) {
+ *typeid = DNSSRV_TYPEID_DWORD;
+ r->Dword = answer_integer;
+ return WERR_OK;
+ }
+
+ is_addresses = 0;
+
+ if (strcasecmp(operation, "Forwarders") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, serverinfo->aipForwarders);
+ } else {
+ answer_iparray = ip4_array_copy(mem_ctx, serverinfo->aipForwarders);
+ }
+ is_addresses = 1;
+ } else if (strcasecmp(operation, "ListenAddresses") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ answer_addrarray = serverinfo->aipListenAddrs;
+ } else {
+ answer_iparray = dns_addr_array_to_ip4_array(mem_ctx, serverinfo->aipListenAddrs);
+ }
+ is_addresses = 1;
+ } else if (strcasecmp(operation, "BreakOnReceiveFrom") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ answer_addrarray = NULL;
+ } else {
+ answer_iparray = NULL;
+ }
+ is_addresses = 1;
+ } else if (strcasecmp(operation, "BreakOnUpdateFrom") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ answer_addrarray = NULL;
+ } else {
+ answer_iparray = NULL;
+ }
+ is_addresses = 1;
+ } else if (strcasecmp(operation, "LogIPFilterList") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, serverinfo->aipLogFilter);
+ } else {
+ answer_iparray = ip4_array_copy(mem_ctx, serverinfo->aipLogFilter);
+ }
+ is_addresses = 1;
+ }
+
+ if (is_addresses == 1) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ *typeid = DNSSRV_TYPEID_ADDRARRAY;
+ r->AddrArray = answer_addrarray;
+ } else {
+ *typeid = DNSSRV_TYPEID_IPARRAY;
+ r->IpArray = answer_iparray;
+ }
+ return WERR_OK;
+ }
+
+ is_string = is_wstring = 0;
+
+ if (strcasecmp(operation, "DomainDirectoryPartitionBaseName") == 0) {
+ answer_string = talloc_strdup(mem_ctx, "DomainDnsZones");
+ if (! answer_string) {
+ return WERR_OUTOFMEMORY;
+ }
+ is_string = 1;
+ } else if (strcasecmp(operation, "ForestDirectoryPartitionBaseName") == 0) {
+ answer_string = talloc_strdup(mem_ctx, "ForestDnsZones");
+ if (! answer_string) {
+ return WERR_OUTOFMEMORY;
+ }
+ is_string = 1;
+ } else if (strcasecmp(operation, "LogFilePath") == 0) {
+ answer_string = talloc_strdup(mem_ctx, serverinfo->pwszLogFilePath);
+ is_wstring = 1;
+ } else if (strcasecmp(operation, "ServerLevelPluginDll") == 0) {
+ answer_string = NULL;
+ is_wstring = 1;
+ } else if (strcasecmp(operation, "DsBackgroundPauseName") == 0) {
+ answer_string = NULL;
+ is_string = 1;
+ } else if (strcasecmp(operation, "DsNotRoundRobinTypes") == 0) {
+ answer_string = NULL;
+ is_string = 1;
+ }
+
+ if (is_string == 1) {
+ *typeid = DNSSRV_TYPEID_LPSTR;
+ r->String = answer_string;
+ return WERR_OK;
+ } else if (is_wstring == 1) {
+ *typeid = DNSSRV_TYPEID_LPWSTR;
+ r->WideString = answer_string;
+ return WERR_OK;
+ }
+
+ is_stringlist = 0;
+
+ if (strcasecmp(operation, "GlobalQueryBlockList") == 0) {
+ answer_stringlist = NULL;
+ is_stringlist = 1;
+ } else if (strcasecmp(operation, "SocketPoolExcludedPortRanges") == 0) {
+ answer_stringlist = NULL;
+ is_stringlist = 1;
+ }
+
+ if (is_stringlist == 1) {
+ *typeid = DNSSRV_TYPEID_UTF8_STRING_LIST;
+ r->Utf8StringList = answer_stringlist;
+ return WERR_OK;
+ }
+
+ DEBUG(0,("dnsserver: Invalid server operation %s", operation));
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+}
+
+/* [MS-DNSP].pdf Section 3.1.1.2 Zone Configuration Information */
+static WERROR dnsserver_query_zone(struct dnsserver_state *dsstate,
+ TALLOC_CTX *mem_ctx,
+ struct dnsserver_zone *z,
+ const char *operation,
+ const unsigned int client_version,
+ enum DNS_RPC_TYPEID *typeid,
+ union DNSSRV_RPC_UNION *r)
+{
+ uint8_t is_integer, is_addresses, is_string;
+ uint32_t answer_integer = 0;
+ struct IP4_ARRAY *answer_iparray;
+ struct DNS_ADDR_ARRAY *answer_addrarray;
+ char *answer_string;
+ struct dnsserver_zoneinfo *zoneinfo;
+
+ zoneinfo = z->zoneinfo;
+
+ if (strcasecmp(operation, "Zone") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_W2K) {
+ *typeid = DNSSRV_TYPEID_ZONE_W2K;
+ r->ZoneW2K = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_W2K);
+
+ r->ZoneW2K->pszZoneName = talloc_strdup(mem_ctx, z->name);
+ r->ZoneW2K->Flags = zoneinfo->Flags;
+ r->ZoneW2K->ZoneType = zoneinfo->dwZoneType;
+ r->ZoneW2K->Version = zoneinfo->Version;
+ } else {
+ *typeid = DNSSRV_TYPEID_ZONE;
+ r->Zone = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_DOTNET);
+
+ r->Zone->dwRpcStructureVersion = 0x01;
+ r->Zone->pszZoneName = talloc_strdup(mem_ctx, z->name);
+ r->Zone->Flags = zoneinfo->Flags;
+ r->Zone->ZoneType = zoneinfo->dwZoneType;
+ r->Zone->Version = zoneinfo->Version;
+ r->Zone->dwDpFlags = z->partition->dwDpFlags;
+ r->Zone->pszDpFqdn = talloc_strdup(mem_ctx, z->partition->pszDpFqdn);
+ }
+ return WERR_OK;
+ }
+
+ if (strcasecmp(operation, "ZoneInfo") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_W2K) {
+ *typeid = DNSSRV_TYPEID_ZONE_INFO_W2K;
+ r->ZoneInfoW2K = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_INFO_W2K);
+
+ r->ZoneInfoW2K->pszZoneName = talloc_strdup(mem_ctx, z->name);
+ r->ZoneInfoW2K->dwZoneType = zoneinfo->dwZoneType;
+ r->ZoneInfoW2K->fReverse = zoneinfo->fReverse;
+ r->ZoneInfoW2K->fAllowUpdate = zoneinfo->fAllowUpdate;
+ r->ZoneInfoW2K->fPaused = zoneinfo->fPaused;
+ r->ZoneInfoW2K->fShutdown = zoneinfo->fShutdown;
+ r->ZoneInfoW2K->fAutoCreated = zoneinfo->fAutoCreated;
+ r->ZoneInfoW2K->fUseDatabase = zoneinfo->fUseDatabase;
+ r->ZoneInfoW2K->pszDataFile = talloc_strdup(mem_ctx, zoneinfo->pszDataFile);
+ r->ZoneInfoW2K->aipMasters = ip4_array_copy(mem_ctx, zoneinfo->aipMasters);
+ r->ZoneInfoW2K->fSecureSecondaries = zoneinfo->fSecureSecondaries;
+ r->ZoneInfoW2K->fNotifyLevel = zoneinfo->fNotifyLevel;
+ r->ZoneInfoW2K->aipSecondaries = ip4_array_copy(mem_ctx, zoneinfo->aipSecondaries);
+ r->ZoneInfoW2K->aipNotify = ip4_array_copy(mem_ctx, zoneinfo->aipNotify);
+ r->ZoneInfoW2K->fUseWins = zoneinfo->fUseWins;
+ r->ZoneInfoW2K->fUseNbstat = zoneinfo->fUseNbstat;
+ r->ZoneInfoW2K->fAging = zoneinfo->fAging;
+ r->ZoneInfoW2K->dwNoRefreshInterval = zoneinfo->dwNoRefreshInterval;
+ r->ZoneInfoW2K->dwRefreshInterval = zoneinfo->dwRefreshInterval;
+ r->ZoneInfoW2K->dwAvailForScavengeTime = zoneinfo->dwAvailForScavengeTime;
+ r->ZoneInfoW2K->aipScavengeServers = ip4_array_copy(mem_ctx, zoneinfo->aipScavengeServers);
+
+ } else if (client_version == DNS_CLIENT_VERSION_DOTNET) {
+ *typeid = DNSSRV_TYPEID_ZONE_INFO_DOTNET;
+ r->ZoneInfoDotNet = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_INFO_DOTNET);
+
+ r->ZoneInfoDotNet->dwRpcStructureVersion = 0x01;
+ r->ZoneInfoDotNet->pszZoneName = talloc_strdup(mem_ctx, z->name);
+ r->ZoneInfoDotNet->dwZoneType = zoneinfo->dwZoneType;
+ r->ZoneInfoDotNet->fReverse = zoneinfo->fReverse;
+ r->ZoneInfoDotNet->fAllowUpdate = zoneinfo->fAllowUpdate;
+ r->ZoneInfoDotNet->fPaused = zoneinfo->fPaused;
+ r->ZoneInfoDotNet->fShutdown = zoneinfo->fShutdown;
+ r->ZoneInfoDotNet->fAutoCreated = zoneinfo->fAutoCreated;
+ r->ZoneInfoDotNet->fUseDatabase = zoneinfo->fUseDatabase;
+ r->ZoneInfoDotNet->pszDataFile = talloc_strdup(mem_ctx, zoneinfo->pszDataFile);
+ r->ZoneInfoDotNet->aipMasters = ip4_array_copy(mem_ctx, zoneinfo->aipMasters);
+ r->ZoneInfoDotNet->fSecureSecondaries = zoneinfo->fSecureSecondaries;
+ r->ZoneInfoDotNet->fNotifyLevel = zoneinfo->fNotifyLevel;
+ r->ZoneInfoDotNet->aipSecondaries = ip4_array_copy(mem_ctx, zoneinfo->aipSecondaries);
+ r->ZoneInfoDotNet->aipNotify = ip4_array_copy(mem_ctx, zoneinfo->aipNotify);
+ r->ZoneInfoDotNet->fUseWins = zoneinfo->fUseWins;
+ r->ZoneInfoDotNet->fUseNbstat = zoneinfo->fUseNbstat;
+ r->ZoneInfoDotNet->fAging = zoneinfo->fAging;
+ r->ZoneInfoDotNet->dwNoRefreshInterval = zoneinfo->dwNoRefreshInterval;
+ r->ZoneInfoDotNet->dwRefreshInterval = zoneinfo->dwRefreshInterval;
+ r->ZoneInfoDotNet->dwAvailForScavengeTime = zoneinfo->dwAvailForScavengeTime;
+ r->ZoneInfoDotNet->aipScavengeServers = ip4_array_copy(mem_ctx, zoneinfo->aipScavengeServers);
+ r->ZoneInfoDotNet->dwForwarderTimeout = zoneinfo->dwForwarderTimeout;
+ r->ZoneInfoDotNet->fForwarderSlave = zoneinfo->fForwarderSlave;
+ r->ZoneInfoDotNet->aipLocalMasters = ip4_array_copy(mem_ctx, zoneinfo->aipLocalMasters);
+ r->ZoneInfoDotNet->dwDpFlags = z->partition->dwDpFlags;
+ r->ZoneInfoDotNet->pszDpFqdn = talloc_strdup(mem_ctx, z->partition->pszDpFqdn);
+ r->ZoneInfoDotNet->pwszZoneDn = talloc_strdup(mem_ctx, zoneinfo->pwszZoneDn);
+ r->ZoneInfoDotNet->dwLastSuccessfulSoaCheck = zoneinfo->dwLastSuccessfulSoaCheck;
+ r->ZoneInfoDotNet->dwLastSuccessfulXfr = zoneinfo->dwLastSuccessfulXfr;
+
+ } else {
+ *typeid = DNSSRV_TYPEID_ZONE_INFO;
+ r->ZoneInfo = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_INFO_LONGHORN);
+
+ r->ZoneInfo->dwRpcStructureVersion = 0x02;
+ r->ZoneInfo->pszZoneName = talloc_strdup(mem_ctx, z->name);
+ r->ZoneInfo->dwZoneType = zoneinfo->dwZoneType;
+ r->ZoneInfo->fReverse = zoneinfo->fReverse;
+ r->ZoneInfo->fAllowUpdate = zoneinfo->fAllowUpdate;
+ r->ZoneInfo->fPaused = zoneinfo->fPaused;
+ r->ZoneInfo->fShutdown = zoneinfo->fShutdown;
+ r->ZoneInfo->fAutoCreated = zoneinfo->fAutoCreated;
+ r->ZoneInfo->fUseDatabase = zoneinfo->fUseDatabase;
+ r->ZoneInfo->pszDataFile = talloc_strdup(mem_ctx, zoneinfo->pszDataFile);
+ r->ZoneInfo->aipMasters = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipMasters);
+ r->ZoneInfo->fSecureSecondaries = zoneinfo->fSecureSecondaries;
+ r->ZoneInfo->fNotifyLevel = zoneinfo->fNotifyLevel;
+ r->ZoneInfo->aipSecondaries = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipSecondaries);
+ r->ZoneInfo->aipNotify = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipNotify);
+ r->ZoneInfo->fUseWins = zoneinfo->fUseWins;
+ r->ZoneInfo->fUseNbstat = zoneinfo->fUseNbstat;
+ r->ZoneInfo->fAging = zoneinfo->fAging;
+ r->ZoneInfo->dwNoRefreshInterval = zoneinfo->dwNoRefreshInterval;
+ r->ZoneInfo->dwRefreshInterval = zoneinfo->dwRefreshInterval;
+ r->ZoneInfo->dwAvailForScavengeTime = zoneinfo->dwAvailForScavengeTime;
+ r->ZoneInfo->aipScavengeServers = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipScavengeServers);
+ r->ZoneInfo->dwForwarderTimeout = zoneinfo->dwForwarderTimeout;
+ r->ZoneInfo->fForwarderSlave = zoneinfo->fForwarderSlave;
+ r->ZoneInfo->aipLocalMasters = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipLocalMasters);
+ r->ZoneInfo->dwDpFlags = z->partition->dwDpFlags;
+ r->ZoneInfo->pszDpFqdn = talloc_strdup(mem_ctx, z->partition->pszDpFqdn);
+ r->ZoneInfo->pwszZoneDn = talloc_strdup(mem_ctx, zoneinfo->pwszZoneDn);
+ r->ZoneInfo->dwLastSuccessfulSoaCheck = zoneinfo->dwLastSuccessfulSoaCheck;
+ r->ZoneInfo->dwLastSuccessfulXfr = zoneinfo->dwLastSuccessfulXfr;
+
+ r->ZoneInfo->fQueuedForBackgroundLoad = zoneinfo->fQueuedForBackgroundLoad;
+ r->ZoneInfo->fBackgroundLoadInProgress = zoneinfo->fBackgroundLoadInProgress;
+ r->ZoneInfo->fReadOnlyZone = zoneinfo->fReadOnlyZone;
+ r->ZoneInfo->dwLastXfrAttempt = zoneinfo->dwLastXfrAttempt;
+ r->ZoneInfo->dwLastXfrResult = zoneinfo->dwLastXfrResult;
+ }
+
+ return WERR_OK;
+ }
+
+ is_integer = 0;
+
+ if (strcasecmp(operation, "AllowUpdate") == 0) {
+ answer_integer = zoneinfo->fAllowUpdate;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "Secured") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "DsIntegrated") == 0) {
+ answer_integer = zoneinfo->fUseDatabase;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "LogUpdates") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "NoRefreshInterval") == 0) {
+ answer_integer = zoneinfo->dwNoRefreshInterval;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "NotifyLevel") == 0) {
+ answer_integer = zoneinfo->fNotifyLevel;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "RefreshInterval") == 0) {
+ answer_integer = zoneinfo->dwRefreshInterval;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "SecureSecondaries") == 0) {
+ answer_integer = zoneinfo->fSecureSecondaries;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "Type") == 0) {
+ answer_integer = zoneinfo->dwZoneType;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "Aging") == 0) {
+ answer_integer = zoneinfo->fAging;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ForwarderSlave") == 0) {
+ answer_integer = zoneinfo->fForwarderSlave;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "ForwarderTimeout") == 0) {
+ answer_integer = zoneinfo->dwForwarderTimeout;
+ is_integer = 1;
+ } else if (strcasecmp(operation, "Unicode") == 0) {
+ answer_integer = 0;
+ is_integer = 1;
+ }
+
+ if (is_integer == 1) {
+ *typeid = DNSSRV_TYPEID_DWORD;
+ r->Dword = answer_integer;
+ return WERR_OK;
+ }
+
+ is_addresses = 0;
+
+ if (strcasecmp(operation, "AllowNSRecordsAutoCreation") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ answer_addrarray = NULL;
+ } else {
+ answer_iparray = NULL;
+ }
+ is_addresses = 1;
+ } else if (strcasecmp(operation, "ScavengeServers") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipScavengeServers);
+ } else {
+ answer_iparray = ip4_array_copy(mem_ctx, zoneinfo->aipScavengeServers);
+ }
+ is_addresses = 1;
+ } else if (strcasecmp(operation, "MasterServers") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipMasters);
+ } else {
+ answer_iparray = ip4_array_copy(mem_ctx, zoneinfo->aipMasters);
+ }
+ is_addresses = 1;
+ } else if (strcasecmp(operation, "LocalMasterServers") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipLocalMasters);
+ } else {
+ answer_iparray = ip4_array_copy(mem_ctx, zoneinfo->aipLocalMasters);
+ }
+ is_addresses = 1;
+ } else if (strcasecmp(operation, "NotifyServers") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipNotify);
+ } else {
+ answer_iparray = ip4_array_copy(mem_ctx, zoneinfo->aipNotify);
+ }
+ is_addresses = 1;
+ } else if (strcasecmp(operation, "SecondaryServers") == 0) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipSecondaries);
+ } else {
+ answer_iparray = ip4_array_copy(mem_ctx, zoneinfo->aipSecondaries);
+ }
+ is_addresses = 1;
+ }
+
+ if (is_addresses == 1) {
+ if (client_version == DNS_CLIENT_VERSION_LONGHORN) {
+ *typeid = DNSSRV_TYPEID_ADDRARRAY;
+ r->AddrArray = answer_addrarray;
+ } else {
+ *typeid = DNSSRV_TYPEID_IPARRAY;
+ r->IpArray = answer_iparray;
+ }
+ return WERR_OK;
+ }
+
+ is_string = 0;
+
+ if (strcasecmp(operation, "DatabaseFile") == 0) {
+ answer_string = talloc_strdup(mem_ctx, zoneinfo->pszDataFile);
+ is_string = 1;
+ } else if (strcasecmp(operation, "ApplicationDirectoryPartition") == 0) {
+ answer_string = talloc_strdup(mem_ctx, z->partition->pszDpFqdn);
+ is_string = 1;
+ } else if (strcasecmp(operation, "BreakOnNameUpdate") == 0) {
+ answer_string = NULL;
+ is_string = 1;
+ }
+
+ if (is_string == 1) {
+ *typeid = DNSSRV_TYPEID_LPSTR;
+ r->String = answer_string;
+ return WERR_OK;
+ }
+
+ DEBUG(0,("dnsserver: Invalid zone operation %s", operation));
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+
+}
+
+/* dnsserver operation functions */
+
+/* [MS-DNSP].pdf Section 3.1.1.1 DNS Server Configuration Information */
+static WERROR dnsserver_operate_server(struct dnsserver_state *dsstate,
+ TALLOC_CTX *mem_ctx,
+ const char *operation,
+ const unsigned int client_version,
+ enum DNS_RPC_TYPEID typeid,
+ union DNSSRV_RPC_UNION *r)
+{
+ bool valid_operation = false;
+
+ if (strcasecmp(operation, "ResetDwordProperty") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "Restart") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ClearDebugLog") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ClearCache") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "WriteDirtyZones") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ZoneCreate") == 0) {
+ struct dnsserver_zone *z, *z2;
+ WERROR status;
+ size_t len;
+ const char *name;
+ z = talloc_zero(mem_ctx, struct dnsserver_zone);
+ W_ERROR_HAVE_NO_MEMORY(z);
+ z->partition = talloc_zero(z, struct dnsserver_partition);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(z->partition, z);
+ z->zoneinfo = talloc_zero(z, struct dnsserver_zoneinfo);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(z->zoneinfo, z);
+
+ if (typeid == DNSSRV_TYPEID_ZONE_CREATE_W2K) {
+ name = r->ZoneCreateW2K->pszZoneName;
+ z->zoneinfo->dwZoneType = r->ZoneCreateW2K->dwZoneType;
+ z->zoneinfo->fAllowUpdate = r->ZoneCreateW2K->fAllowUpdate;
+ z->zoneinfo->fAging = r->ZoneCreateW2K->fAging;
+ z->zoneinfo->Flags = r->ZoneCreateW2K->dwFlags;
+ } else if (typeid == DNSSRV_TYPEID_ZONE_CREATE_DOTNET) {
+ name = r->ZoneCreateDotNet->pszZoneName;
+ z->zoneinfo->dwZoneType = r->ZoneCreateDotNet->dwZoneType;
+ z->zoneinfo->fAllowUpdate = r->ZoneCreateDotNet->fAllowUpdate;
+ z->zoneinfo->fAging = r->ZoneCreateDotNet->fAging;
+ z->zoneinfo->Flags = r->ZoneCreateDotNet->dwFlags;
+ z->partition->dwDpFlags = r->ZoneCreateDotNet->dwDpFlags;
+ } else if (typeid == DNSSRV_TYPEID_ZONE_CREATE) {
+ name = r->ZoneCreate->pszZoneName;
+ z->zoneinfo->dwZoneType = r->ZoneCreate->dwZoneType;
+ z->zoneinfo->fAllowUpdate = r->ZoneCreate->fAllowUpdate;
+ z->zoneinfo->fAging = r->ZoneCreate->fAging;
+ z->zoneinfo->Flags = r->ZoneCreate->dwFlags;
+ z->partition->dwDpFlags = r->ZoneCreate->dwDpFlags;
+ } else {
+ talloc_free(z);
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+ }
+
+ len = strlen(name);
+ if (name[len-1] == '.') {
+ len -= 1;
+ }
+ z->name = talloc_strndup(z, name, len);
+ if (z->name == NULL) {
+ talloc_free(z);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ z2 = dnsserver_find_zone(dsstate->zones, z->name);
+ if (z2 != NULL) {
+ talloc_free(z);
+ return WERR_DNS_ERROR_ZONE_ALREADY_EXISTS;
+ }
+
+ status = dnsserver_db_create_zone(dsstate->samdb, dsstate->partitions, z,
+ dsstate->lp_ctx);
+ talloc_free(z);
+
+ if (W_ERROR_IS_OK(status)) {
+ dnsserver_reload_zones(dsstate);
+ }
+ return status;
+ } else if (strcasecmp(operation, "ClearStatistics") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "EnlistDirectoryPartition") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "StartScavenging") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "AbortScavenging") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "AutoConfigure") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ExportSettings") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "PrepareForDemotion") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "PrepareForUninstall") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "DeleteNode") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "DeleteRecord") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "WriteBackFile") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ListenAddresses") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "Forwarders") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "LogFilePath") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "LogIpFilterList") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ForestDirectoryPartitionBaseName") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "DomainDirectoryPartitionBaseName") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "GlobalQueryBlockList") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "BreakOnReceiveFrom") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "BreakOnUpdateFrom") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ServerLevelPluginDll") == 0) {
+ valid_operation = true;
+ }
+
+ if (valid_operation) {
+ DEBUG(0, ("dnsserver: server operation '%s' not implemented", operation));
+ return WERR_CALL_NOT_IMPLEMENTED;
+ }
+
+ DEBUG(0, ("dnsserver: invalid server operation '%s'", operation));
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+}
+
+static WERROR dnsserver_complex_operate_server(struct dnsserver_state *dsstate,
+ TALLOC_CTX *mem_ctx,
+ const char *operation,
+ const unsigned int client_version,
+ enum DNS_RPC_TYPEID typeid_in,
+ union DNSSRV_RPC_UNION *rin,
+ enum DNS_RPC_TYPEID *typeid_out,
+ union DNSSRV_RPC_UNION *rout)
+{
+ int valid_operation = 0;
+ struct dnsserver_zone *z, **zlist;
+ size_t zcount;
+ bool found1, found2, found3, found4;
+ size_t i;
+
+ if (strcasecmp(operation, "QueryDwordProperty") == 0) {
+ if (typeid_in == DNSSRV_TYPEID_LPSTR) {
+ return dnsserver_query_server(dsstate, mem_ctx,
+ rin->String,
+ client_version,
+ typeid_out,
+ rout);
+ }
+ } else if (strcasecmp(operation, "EnumZones") == 0) {
+ if (typeid_in != DNSSRV_TYPEID_DWORD) {
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+ }
+
+ zcount = 0;
+ zlist = talloc_zero_array(mem_ctx, struct dnsserver_zone *, 0);
+ for (z = dsstate->zones; z; z = z->next) {
+
+ /* Match the flags in groups
+ *
+ * Group1 : PRIMARY, SECONDARY, CACHE, AUTO
+ * Group2 : FORWARD, REVERSE, FORWARDER, STUB
+ * Group3 : DS, NON_DS, DOMAIN_DP, FOREST_DP
+ * Group4 : CUSTOM_DP, LEGACY_DP
+ */
+
+ /* Group 1 */
+ found1 = false;
+ if (rin->Dword & 0x0000000f) {
+ if (rin->Dword & DNS_ZONE_REQUEST_PRIMARY) {
+ if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_PRIMARY) {
+ found1 = true;
+ }
+ }
+ if (rin->Dword & DNS_ZONE_REQUEST_SECONDARY) {
+ if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_SECONDARY) {
+ found1 = true;
+ }
+ }
+ if (rin->Dword & DNS_ZONE_REQUEST_CACHE) {
+ if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_CACHE) {
+ found1 = true;
+ }
+ }
+ if (rin->Dword & DNS_ZONE_REQUEST_AUTO) {
+ if (z->zoneinfo->fAutoCreated
+ || z->partition->dwDpFlags & DNS_DP_AUTOCREATED) {
+ found1 = true;
+ }
+ }
+ } else {
+ found1 = true;
+ }
+
+ /* Group 2 */
+ found2 = false;
+ if (rin->Dword & 0x000000f0) {
+ if (rin->Dword & DNS_ZONE_REQUEST_FORWARD) {
+ if (!(z->zoneinfo->fReverse)) {
+ found2 = true;
+ }
+ }
+ if (rin->Dword & DNS_ZONE_REQUEST_REVERSE) {
+ if (z->zoneinfo->fReverse) {
+ found2 = true;
+ }
+ }
+ if (rin->Dword & DNS_ZONE_REQUEST_FORWARDER) {
+ if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_FORWARDER) {
+ found2 = true;
+ }
+ }
+ if (rin->Dword & DNS_ZONE_REQUEST_STUB) {
+ if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_STUB) {
+ found2 = true;
+ }
+ }
+ } else {
+ found2 = true;
+ }
+
+ /* Group 3 */
+ found3 = false;
+ if (rin->Dword & 0x00000f00) {
+ if (rin->Dword & DNS_ZONE_REQUEST_DS) {
+ if (z->zoneinfo->Flags & DNS_RPC_ZONE_DSINTEGRATED) {
+ found3 = true;
+ }
+ }
+ if (rin->Dword & DNS_ZONE_REQUEST_NON_DS) {
+ if (!(z->zoneinfo->Flags & DNS_RPC_ZONE_DSINTEGRATED)) {
+ found3 = true;
+ }
+ }
+ if (rin->Dword & DNS_ZONE_REQUEST_DOMAIN_DP) {
+ if (!(z->partition->dwDpFlags & DNS_DP_DOMAIN_DEFAULT)) {
+ found3 = true;
+ }
+ }
+ if (rin->Dword & DNS_ZONE_REQUEST_FOREST_DP) {
+ if (!(z->partition->dwDpFlags & DNS_DP_FOREST_DEFAULT)) {
+ found3 = true;
+ }
+ }
+ } else {
+ found3 = true;
+ }
+
+ /* Group 4 */
+ if (rin->Dword & 0x0000f000) {
+ found4 = false;
+ } else {
+ found4 = true;
+ }
+
+ if (found1 && found2 && found3 && found4) {
+ zlist = talloc_realloc(mem_ctx, zlist, struct dnsserver_zone *, zcount+1);
+ zlist[zcount] = z;
+ zcount++;
+ }
+ }
+
+ if (client_version == DNS_CLIENT_VERSION_W2K) {
+ *typeid_out = DNSSRV_TYPEID_ZONE_LIST_W2K;
+ rout->ZoneListW2K = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_LIST_W2K);
+
+ if (zcount == 0) {
+ rout->ZoneListW2K->dwZoneCount = 0;
+ rout->ZoneListW2K->ZoneArray = NULL;
+
+ return WERR_OK;
+ }
+
+ rout->ZoneListW2K->ZoneArray = talloc_zero_array(mem_ctx, struct DNS_RPC_ZONE_W2K *, zcount);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(rout->ZoneListW2K->ZoneArray, zlist);
+
+ for (i=0; i<zcount; i++) {
+ rout->ZoneListW2K->ZoneArray[i] = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_W2K);
+
+ rout->ZoneListW2K->ZoneArray[i]->pszZoneName = talloc_strdup(mem_ctx, zlist[i]->name);
+ rout->ZoneListW2K->ZoneArray[i]->Flags = zlist[i]->zoneinfo->Flags;
+ rout->ZoneListW2K->ZoneArray[i]->ZoneType = zlist[i]->zoneinfo->dwZoneType;
+ rout->ZoneListW2K->ZoneArray[i]->Version = zlist[i]->zoneinfo->Version;
+ }
+ rout->ZoneListW2K->dwZoneCount = zcount;
+
+ } else {
+ *typeid_out = DNSSRV_TYPEID_ZONE_LIST;
+ rout->ZoneList = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_LIST_DOTNET);
+
+ if (zcount == 0) {
+ rout->ZoneList->dwRpcStructureVersion = 1;
+ rout->ZoneList->dwZoneCount = 0;
+ rout->ZoneList->ZoneArray = NULL;
+
+ return WERR_OK;
+ }
+
+ rout->ZoneList->ZoneArray = talloc_zero_array(mem_ctx, struct DNS_RPC_ZONE_DOTNET *, zcount);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(rout->ZoneList->ZoneArray, zlist);
+
+ for (i=0; i<zcount; i++) {
+ rout->ZoneList->ZoneArray[i] = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_DOTNET);
+
+ rout->ZoneList->ZoneArray[i]->dwRpcStructureVersion = 1;
+ rout->ZoneList->ZoneArray[i]->pszZoneName = talloc_strdup(mem_ctx, zlist[i]->name);
+ rout->ZoneList->ZoneArray[i]->Flags = zlist[i]->zoneinfo->Flags;
+ rout->ZoneList->ZoneArray[i]->ZoneType = zlist[i]->zoneinfo->dwZoneType;
+ rout->ZoneList->ZoneArray[i]->Version = zlist[i]->zoneinfo->Version;
+ rout->ZoneList->ZoneArray[i]->dwDpFlags = zlist[i]->partition->dwDpFlags;
+ rout->ZoneList->ZoneArray[i]->pszDpFqdn = talloc_strdup(mem_ctx, zlist[i]->partition->pszDpFqdn);
+ }
+ rout->ZoneList->dwRpcStructureVersion = 1;
+ rout->ZoneList->dwZoneCount = zcount;
+ }
+ talloc_free(zlist);
+ return WERR_OK;
+ } else if (strcasecmp(operation, "EnumZones2") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "EnumDirectoryPartitions") == 0) {
+ if (typeid_in != DNSSRV_TYPEID_DWORD) {
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+ }
+
+ *typeid_out = DNSSRV_TYPEID_DP_LIST;
+ rout->DirectoryPartitionList = talloc_zero(mem_ctx, struct DNS_RPC_DP_LIST);
+
+ if (rin->Dword != 0) {
+ rout->DirectoryPartitionList->dwDpCount = 0;
+ rout->DirectoryPartitionList->DpArray = NULL;
+ } else {
+ struct DNS_RPC_DP_ENUM **dplist;
+ struct dnsserver_partition *p;
+ int pcount = 2;
+
+ dplist = talloc_zero_array(mem_ctx, struct DNS_RPC_DP_ENUM *, pcount);
+ W_ERROR_HAVE_NO_MEMORY(dplist);
+
+ p = dsstate->partitions;
+ for (i=0; i<pcount; i++) {
+ dplist[i] = talloc_zero(dplist, struct DNS_RPC_DP_ENUM);
+
+ dplist[i]->pszDpFqdn = talloc_strdup(mem_ctx, p->pszDpFqdn);
+ dplist[i]->dwFlags = p->dwDpFlags;
+ dplist[i]->dwZoneCount = p->zones_count;
+ p = p->next;
+ }
+
+ rout->DirectoryPartitionList->dwDpCount = pcount;
+ rout->DirectoryPartitionList->DpArray = dplist;
+ }
+ return WERR_OK;
+ } else if (strcasecmp(operation, "DirectoryPartitionInfo") == 0) {
+ struct dnsserver_partition *p;
+ struct dnsserver_partition_info *partinfo;
+ struct DNS_RPC_DP_INFO *dpinfo = NULL;
+
+ if (typeid_in != DNSSRV_TYPEID_LPSTR) {
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+ }
+
+ *typeid_out = DNSSRV_TYPEID_DP_INFO;
+
+ for (p = dsstate->partitions; p; p = p->next) {
+ if (strcasecmp(p->pszDpFqdn, rin->String) == 0) {
+ dpinfo = talloc_zero(mem_ctx, struct DNS_RPC_DP_INFO);
+ W_ERROR_HAVE_NO_MEMORY(dpinfo);
+
+ partinfo = dnsserver_db_partition_info(mem_ctx, dsstate->samdb, p);
+ W_ERROR_HAVE_NO_MEMORY(partinfo);
+
+ dpinfo->pszDpFqdn = talloc_strdup(dpinfo, p->pszDpFqdn);
+ dpinfo->pszDpDn = talloc_strdup(dpinfo, ldb_dn_get_linearized(p->partition_dn));
+ dpinfo->pszCrDn = talloc_steal(dpinfo, partinfo->pszCrDn);
+ dpinfo->dwFlags = p->dwDpFlags;
+ dpinfo->dwZoneCount = p->zones_count;
+ dpinfo->dwState = partinfo->dwState;
+ dpinfo->dwReplicaCount = partinfo->dwReplicaCount;
+ if (partinfo->dwReplicaCount > 0) {
+ dpinfo->ReplicaArray = talloc_steal(dpinfo,
+ partinfo->ReplicaArray);
+ } else {
+ dpinfo->ReplicaArray = NULL;
+ }
+ break;
+ }
+ }
+
+ if (dpinfo == NULL) {
+ return WERR_DNS_ERROR_DP_DOES_NOT_EXIST;
+ }
+
+ rout->DirectoryPartition = dpinfo;
+ return WERR_OK;
+ } else if (strcasecmp(operation, "Statistics") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "IpValidate") == 0) {
+ valid_operation = true;
+ }
+
+ if (valid_operation) {
+ DEBUG(0, ("dnsserver: server complex operation '%s' not implemented", operation));
+ return WERR_CALL_NOT_IMPLEMENTED;
+ }
+
+ DEBUG(0, ("dnsserver: invalid server complex operation '%s'", operation));
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+}
+
+/* [MS-DNSP].pdf Section 3.1.1.2 Zone Configuration Information */
+static WERROR dnsserver_operate_zone(struct dnsserver_state *dsstate,
+ TALLOC_CTX *mem_ctx,
+ struct dnsserver_zone *z,
+ unsigned int request_filter,
+ const char *operation,
+ const unsigned int client_version,
+ enum DNS_RPC_TYPEID typeid,
+ union DNSSRV_RPC_UNION *r)
+{
+ bool valid_operation = false;
+
+ if (strcasecmp(operation, "ResetDwordProperty") == 0) {
+
+ if (typeid != DNSSRV_TYPEID_NAME_AND_PARAM) {
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+ }
+
+ return dnsserver_db_do_reset_dword(dsstate->samdb, z,
+ r->NameAndParam);
+
+ } else if (strcasecmp(operation, "ZoneTypeReset") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "PauseZone") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ResumeZone") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "DeleteZone") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ReloadZone") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "RefreshZone") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ExpireZone") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "IncrementVersion") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "WriteBackFile") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "DeleteZoneFromDs") == 0) {
+ WERROR status;
+ if (z == NULL) {
+ return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST;
+ }
+ status = dnsserver_db_delete_zone(dsstate->samdb, z);
+ if (W_ERROR_IS_OK(status)) {
+ dnsserver_reload_zones(dsstate);
+ }
+ return status;
+ } else if (strcasecmp(operation, "UpdateZoneFromDs") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ZoneExport") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ZoneChangeDirectoryPartition") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "DeleteNode") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "DeleteRecordSet") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ForceAgingOnNode") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "DatabaseFile") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "MasterServers") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "LocalMasterServers") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "NotifyServers") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "SecondaryServers") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ScavengingServers") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "AllowNSRecordsAutoCreation") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "BreakOnNameUpdate") == 0) {
+ valid_operation = true;
+ } else if (strcasecmp(operation, "ApplicationDirectoryPartition") == 0) {
+ valid_operation = true;
+ }
+
+ if (valid_operation) {
+ DEBUG(0, ("dnsserver: zone operation '%s' not implemented", operation));
+ return WERR_CALL_NOT_IMPLEMENTED;
+ }
+
+ DEBUG(0, ("dnsserver: invalid zone operation '%s'", operation));
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+}
+
+static WERROR dnsserver_complex_operate_zone(struct dnsserver_state *dsstate,
+ TALLOC_CTX *mem_ctx,
+ struct dnsserver_zone *z,
+ const char *operation,
+ const unsigned int client_version,
+ enum DNS_RPC_TYPEID typeid_in,
+ union DNSSRV_RPC_UNION *rin,
+ enum DNS_RPC_TYPEID *typeid_out,
+ union DNSSRV_RPC_UNION *rout)
+{
+ if (strcasecmp(operation, "QueryDwordProperty") == 0) {
+ if (typeid_in == DNSSRV_TYPEID_LPSTR) {
+ return dnsserver_query_zone(dsstate, mem_ctx, z,
+ rin->String,
+ client_version,
+ typeid_out,
+ rout);
+
+ }
+ }
+
+ DEBUG(0,("dnsserver: Invalid zone operation %s", operation));
+ return WERR_DNS_ERROR_INVALID_PROPERTY;
+}
+
+/* dnsserver enumerate function */
+
+static WERROR dnsserver_enumerate_root_records(struct dnsserver_state *dsstate,
+ TALLOC_CTX *mem_ctx,
+ unsigned int client_version,
+ const char *node_name,
+ enum dns_record_type record_type,
+ unsigned int select_flag,
+ unsigned int *buffer_length,
+ struct DNS_RPC_RECORDS_ARRAY **buffer)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct dnsserver_zone *z;
+ const char * const attrs[] = { "name", "dnsRecord", NULL };
+ struct ldb_result *res;
+ struct DNS_RPC_RECORDS_ARRAY *recs;
+ char **add_names;
+ char *rname;
+ int add_count;
+ int i, ret, len;
+ WERROR status;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ z = dnsserver_find_zone(dsstate->zones, ".");
+ if (z == NULL) {
+ return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+ }
+
+ ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z->zone_dn,
+ LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(name=@)(!(dNSTombstoned=TRUE)))");
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return WERR_INTERNAL_DB_ERROR;
+ }
+ if (res->count == 0) {
+ talloc_free(tmp_ctx);
+ return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+ }
+
+ recs = talloc_zero(mem_ctx, struct DNS_RPC_RECORDS_ARRAY);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(recs, tmp_ctx);
+
+ add_names = NULL;
+ add_count = 0;
+
+ for (i=0; i<res->count; i++) {
+ status = dns_fill_records_array(tmp_ctx, NULL, record_type,
+ select_flag, NULL,
+ res->msgs[i], 0, recs,
+ &add_names, &add_count);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ }
+ talloc_free(res);
+
+ /* Add any additional records */
+ if (select_flag & DNS_RPC_VIEW_ADDITIONAL_DATA) {
+ for (i=0; i<add_count; i++) {
+ char *encoded_name
+ = ldb_binary_encode_string(tmp_ctx,
+ add_names[i]);
+ ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z->zone_dn,
+ LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(name=%s)(!(dNSTombstoned=TRUE)))",
+ encoded_name);
+ if (ret != LDB_SUCCESS || res->count == 0) {
+ talloc_free(res);
+ continue;
+ }
+
+ len = strlen(add_names[i]);
+ if (add_names[i][len-1] == '.') {
+ rname = talloc_strdup(tmp_ctx, add_names[i]);
+ } else {
+ rname = talloc_asprintf(tmp_ctx, "%s.", add_names[i]);
+ }
+ status = dns_fill_records_array(tmp_ctx, NULL, DNS_TYPE_A,
+ select_flag, rname,
+ res->msgs[0], 0, recs,
+ NULL, NULL);
+ talloc_free(rname);
+ talloc_free(res);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ *buffer_length = ndr_size_DNS_RPC_RECORDS_ARRAY(recs, 0);
+ *buffer = recs;
+
+ return WERR_OK;
+}
+
+
+static WERROR dnsserver_enumerate_records(struct dnsserver_state *dsstate,
+ TALLOC_CTX *mem_ctx,
+ struct dnsserver_zone *z,
+ unsigned int client_version,
+ const char *node_name,
+ const char *start_child,
+ enum dns_record_type record_type,
+ unsigned int select_flag,
+ const char *filter_start,
+ const char *filter_stop,
+ unsigned int *buffer_length,
+ struct DNS_RPC_RECORDS_ARRAY **buffer)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *name;
+ const char * const attrs[] = { "name", "dnsRecord", NULL };
+ struct ldb_result *res = NULL;
+ struct DNS_RPC_RECORDS_ARRAY *recs = NULL;
+ char **add_names = NULL;
+ char *rname = NULL;
+ const char *preference_name = NULL;
+ int add_count = 0;
+ int i, ret, len;
+ WERROR status;
+ struct dns_tree *tree = NULL;
+ struct dns_tree *base = NULL;
+ struct dns_tree *node = NULL;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ name = dns_split_node_name(tmp_ctx, node_name, z->name);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(name, tmp_ctx);
+
+ /* search all records under parent tree */
+ if (strcasecmp(name, z->name) == 0) {
+ ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z->zone_dn,
+ LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))");
+ preference_name = "@";
+ } else {
+ char *encoded_name
+ = ldb_binary_encode_string(tmp_ctx, name);
+ ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z->zone_dn,
+ LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(|(name=%s)(name=*.%s))(!(dNSTombstoned=TRUE)))",
+ encoded_name, encoded_name);
+ preference_name = name;
+ }
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return WERR_INTERNAL_DB_ERROR;
+ }
+ if (res->count == 0) {
+ talloc_free(tmp_ctx);
+ return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+ }
+
+ recs = talloc_zero(mem_ctx, struct DNS_RPC_RECORDS_ARRAY);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(recs, tmp_ctx);
+
+ /*
+ * Sort the names, so that the records are in order by the child
+ * component below "name".
+ *
+ * A full tree sort is not required, so we pass in "name" so
+ * we know which level to sort, as only direct children are
+ * eventually returned
+ */
+ LDB_TYPESAFE_QSORT(res->msgs, res->count, name, dns_name_compare);
+
+ /* Build a tree of name components from dns name */
+ tree = dns_build_tree(tmp_ctx, preference_name, res);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(tree, tmp_ctx);
+
+ /* Find the parent record in the tree */
+ base = tree;
+ while (base->level != -1) {
+ base = base->children[0];
+ }
+
+ /* Add the parent record with blank name */
+ if (!(select_flag & DNS_RPC_VIEW_ONLY_CHILDREN)) {
+ status = dns_fill_records_array(tmp_ctx, z, record_type,
+ select_flag, NULL,
+ base->data, 0,
+ recs, &add_names, &add_count);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ }
+
+ /* Add all the children records */
+ if (!(select_flag & DNS_RPC_VIEW_NO_CHILDREN)) {
+ for (i=0; i<base->num_children; i++) {
+ node = base->children[i];
+
+ status = dns_fill_records_array(tmp_ctx, z, record_type,
+ select_flag, node->name,
+ node->data, node->num_children,
+ recs, &add_names, &add_count);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ }
+ }
+
+ TALLOC_FREE(res);
+ TALLOC_FREE(tree);
+ TALLOC_FREE(name);
+
+ /* Add any additional records */
+ if (select_flag & DNS_RPC_VIEW_ADDITIONAL_DATA) {
+ for (i=0; i<add_count; i++) {
+ struct dnsserver_zone *z2 = NULL;
+ struct ldb_message *msg = NULL;
+ /* Search all the available zones for additional name */
+ for (z2 = dsstate->zones; z2; z2 = z2->next) {
+ char *encoded_name;
+ name = dns_split_node_name(tmp_ctx, add_names[i], z2->name);
+ encoded_name
+ = ldb_binary_encode_string(tmp_ctx,
+ name);
+ ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z2->zone_dn,
+ LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(name=%s)(!(dNSTombstoned=TRUE)))",
+ encoded_name);
+ TALLOC_FREE(name);
+ if (ret != LDB_SUCCESS) {
+ continue;
+ }
+ if (res->count == 1) {
+ msg = res->msgs[0];
+ break;
+ } else {
+ TALLOC_FREE(res);
+ continue;
+ }
+ }
+
+ len = strlen(add_names[i]);
+ if (add_names[i][len-1] == '.') {
+ rname = talloc_strdup(tmp_ctx, add_names[i]);
+ } else {
+ rname = talloc_asprintf(tmp_ctx, "%s.", add_names[i]);
+ }
+ status = dns_fill_records_array(tmp_ctx, NULL, DNS_TYPE_A,
+ select_flag, rname,
+ msg, 0, recs,
+ NULL, NULL);
+ TALLOC_FREE(rname);
+ TALLOC_FREE(res);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ }
+ }
+
+ *buffer_length = ndr_size_DNS_RPC_RECORDS_ARRAY(recs, 0);
+ *buffer = recs;
+
+ return WERR_OK;
+}
+
+/*
+ * Check str1 + '.' + str2 = name, for example:
+ * ("dc0", "example.com", "dc0.example.com") = true
+ */
+static bool cname_self_reference(const char* node_name,
+ const char* zone_name,
+ struct DNS_RPC_NAME name) {
+ size_t node_len, zone_len;
+
+ if (node_name == NULL || zone_name == NULL) {
+ return false;
+ }
+
+ node_len = strlen(node_name);
+ zone_len = strlen(zone_name);
+
+ if (node_len == 0 ||
+ zone_len == 0 ||
+ (name.len != node_len + zone_len + 1)) {
+ return false;
+ }
+
+ if (strncmp(node_name, name.str, node_len) == 0 &&
+ name.str[node_len] == '.' &&
+ strncmp(zone_name, name.str + node_len + 1, zone_len) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+/* dnsserver update function */
+
+static WERROR dnsserver_update_record(struct dnsserver_state *dsstate,
+ TALLOC_CTX *mem_ctx,
+ struct dnsserver_zone *z,
+ unsigned int client_version,
+ const char *node_name,
+ struct DNS_RPC_RECORD_BUF *add_buf,
+ struct DNS_RPC_RECORD_BUF *del_buf)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *name;
+ WERROR status;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ /* If node_name is @ or zone name, dns record is @ */
+ if (strcmp(node_name, "@") == 0 ||
+ strcmp(node_name, ".") == 0 ||
+ strcasecmp(node_name, z->name) == 0) {
+ name = talloc_strdup(tmp_ctx, "@");
+ } else {
+ name = dns_split_node_name(tmp_ctx, node_name, z->name);
+ }
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(name, tmp_ctx);
+
+ /* CNAMEs can't point to themselves */
+ if (add_buf != NULL && add_buf->rec.wType == DNS_TYPE_CNAME) {
+ if (cname_self_reference(node_name, z->name, add_buf->rec.data.name)) {
+ return WERR_DNS_ERROR_CNAME_LOOP;
+ }
+ }
+
+ if (add_buf != NULL) {
+ if (del_buf == NULL) {
+ /* Add record */
+ status = dnsserver_db_add_record(tmp_ctx, dsstate->samdb,
+ z, name,
+ &add_buf->rec);
+ } else {
+ /* Update record */
+ status = dnsserver_db_update_record(tmp_ctx, dsstate->samdb,
+ z, name,
+ &add_buf->rec,
+ &del_buf->rec);
+ }
+ } else {
+ if (del_buf == NULL) {
+ /* Add empty node */
+ status = dnsserver_db_add_empty_node(tmp_ctx, dsstate->samdb,
+ z, name);
+ } else {
+ /* Delete record */
+ status = dnsserver_db_delete_record(tmp_ctx, dsstate->samdb,
+ z, name,
+ &del_buf->rec);
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+
+/* dnsserver interface functions */
+
+static WERROR dcesrv_DnssrvOperation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvOperation *r)
+{
+ struct dnsserver_state *dsstate;
+ struct dnsserver_zone *z = NULL;
+ uint32_t request_filter = 0;
+ WERROR ret;
+
+ if ((dsstate = dnsserver_connect(dce_call)) == NULL) {
+ return WERR_DNS_ERROR_DS_UNAVAILABLE;
+ }
+
+ if (r->in.dwContext == 0) {
+ if (r->in.pszZone != NULL) {
+ request_filter = dnsserver_zone_to_request_filter(r->in.pszZone);
+ }
+ } else {
+ request_filter = r->in.dwContext;
+ }
+
+ if (r->in.pszZone == NULL) {
+ ret = dnsserver_operate_server(dsstate, mem_ctx,
+ r->in.pszOperation,
+ DNS_CLIENT_VERSION_W2K,
+ r->in.dwTypeId,
+ &r->in.pData);
+ } else {
+ z = dnsserver_find_zone(dsstate->zones, r->in.pszZone);
+ /*
+ * In the case that request_filter is not 0 and z is NULL,
+ * the request is for a multizone operation, which we do not
+ * yet support, so just error on NULL zone name.
+ */
+ if (z == NULL) {
+ return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST;
+ }
+
+ ret = dnsserver_operate_zone(dsstate, mem_ctx, z,
+ request_filter,
+ r->in.pszOperation,
+ DNS_CLIENT_VERSION_W2K,
+ r->in.dwTypeId,
+ &r->in.pData);
+ }
+
+ if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) {
+ NDR_PRINT_FUNCTION_DEBUG(DnssrvOperation, NDR_IN, r);
+ }
+ return ret;
+}
+
+static WERROR dcesrv_DnssrvQuery(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvQuery *r)
+{
+ struct dnsserver_state *dsstate;
+ struct dnsserver_zone *z;
+ WERROR ret;
+
+ ZERO_STRUCTP(r->out.pdwTypeId);
+ ZERO_STRUCTP(r->out.ppData);
+
+ if ((dsstate = dnsserver_connect(dce_call)) == NULL) {
+ return WERR_DNS_ERROR_DS_UNAVAILABLE;
+ }
+
+ if (r->in.pszZone == NULL) {
+ /* FIXME: DNS Server Configuration Access Control List */
+ ret = dnsserver_query_server(dsstate, mem_ctx,
+ r->in.pszOperation,
+ DNS_CLIENT_VERSION_W2K,
+ r->out.pdwTypeId,
+ r->out.ppData);
+ } else {
+ z = dnsserver_find_zone(dsstate->zones, r->in.pszZone);
+ if (z == NULL) {
+ return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST;
+ }
+
+ ret = dnsserver_query_zone(dsstate, mem_ctx, z,
+ r->in.pszOperation,
+ DNS_CLIENT_VERSION_W2K,
+ r->out.pdwTypeId,
+ r->out.ppData);
+ }
+
+ if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) {
+ NDR_PRINT_FUNCTION_DEBUG(DnssrvQuery, NDR_IN, r);
+ }
+ return ret;
+}
+
+static WERROR dcesrv_DnssrvComplexOperation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvComplexOperation *r)
+{
+ struct dnsserver_state *dsstate;
+ struct dnsserver_zone *z;
+ WERROR ret;
+
+ ZERO_STRUCTP(r->out.pdwTypeOut);
+ ZERO_STRUCTP(r->out.ppDataOut);
+
+ if ((dsstate = dnsserver_connect(dce_call)) == NULL) {
+ return WERR_DNS_ERROR_DS_UNAVAILABLE;
+ }
+
+ if (r->in.pszZone == NULL) {
+ /* Server operation */
+ ret = dnsserver_complex_operate_server(dsstate, mem_ctx,
+ r->in.pszOperation,
+ DNS_CLIENT_VERSION_W2K,
+ r->in.dwTypeIn,
+ &r->in.pDataIn,
+ r->out.pdwTypeOut,
+ r->out.ppDataOut);
+ } else {
+ z = dnsserver_find_zone(dsstate->zones, r->in.pszZone);
+ if (z == NULL) {
+ return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST;
+ }
+
+ ret = dnsserver_complex_operate_zone(dsstate, mem_ctx, z,
+ r->in.pszOperation,
+ DNS_CLIENT_VERSION_W2K,
+ r->in.dwTypeIn,
+ &r->in.pDataIn,
+ r->out.pdwTypeOut,
+ r->out.ppDataOut);
+ }
+
+ if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) {
+ NDR_PRINT_FUNCTION_DEBUG(DnssrvComplexOperation, NDR_IN, r);
+ }
+ return ret;
+}
+
+static WERROR dcesrv_DnssrvEnumRecords(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvEnumRecords *r)
+{
+ struct dnsserver_state *dsstate;
+ struct dnsserver_zone *z;
+ WERROR ret;
+
+ ZERO_STRUCTP(r->out.pdwBufferLength);
+ ZERO_STRUCTP(r->out.pBuffer);
+
+ if ((dsstate = dnsserver_connect(dce_call)) == NULL) {
+ return WERR_DNS_ERROR_DS_UNAVAILABLE;
+ }
+
+ if (r->in.pszZone == NULL) {
+ return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+ }
+
+ if (strcasecmp(r->in.pszZone, "..RootHints") == 0) {
+ ret = dnsserver_enumerate_root_records(dsstate, mem_ctx,
+ DNS_CLIENT_VERSION_W2K,
+ r->in.pszNodeName,
+ r->in.wRecordType,
+ r->in.fSelectFlag,
+ r->out.pdwBufferLength,
+ r->out.pBuffer);
+ } else {
+ z = dnsserver_find_zone(dsstate->zones, r->in.pszZone);
+ if (z == NULL) {
+ return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+ }
+
+ ret = dnsserver_enumerate_records(dsstate, mem_ctx, z,
+ DNS_CLIENT_VERSION_W2K,
+ r->in.pszNodeName,
+ r->in.pszStartChild,
+ r->in.wRecordType,
+ r->in.fSelectFlag,
+ r->in.pszFilterStart,
+ r->in.pszFilterStop,
+ r->out.pdwBufferLength,
+ r->out.pBuffer);
+ }
+
+ if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) {
+ NDR_PRINT_FUNCTION_DEBUG(DnssrvEnumRecords, NDR_IN, r);
+ }
+ return ret;
+}
+
+static WERROR dcesrv_DnssrvUpdateRecord(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvUpdateRecord *r)
+{
+ struct dnsserver_state *dsstate;
+ struct dnsserver_zone *z;
+ WERROR ret;
+
+ if ((dsstate = dnsserver_connect(dce_call)) == NULL) {
+ return WERR_DNS_ERROR_DS_UNAVAILABLE;
+ }
+
+ if (r->in.pszZone == NULL) {
+ return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+ }
+
+ z = dnsserver_find_zone(dsstate->zones, r->in.pszZone);
+ if (z == NULL) {
+ return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+ }
+
+ ret = dnsserver_update_record(dsstate, mem_ctx, z,
+ DNS_CLIENT_VERSION_W2K,
+ r->in.pszNodeName,
+ r->in.pAddRecord,
+ r->in.pDeleteRecord);
+
+ if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) {
+ NDR_PRINT_FUNCTION_DEBUG(DnssrvUpdateRecord, NDR_IN, r);
+ }
+ return ret;
+}
+
+static WERROR dcesrv_DnssrvOperation2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvOperation2 *r)
+{
+ struct dnsserver_state *dsstate;
+ struct dnsserver_zone *z = NULL;
+ uint32_t request_filter = 0;
+ WERROR ret;
+
+ if ((dsstate = dnsserver_connect(dce_call)) == NULL) {
+ return WERR_DNS_ERROR_DS_UNAVAILABLE;
+ }
+
+ if (r->in.dwContext == 0) {
+ if (r->in.pszZone != NULL) {
+ request_filter = dnsserver_zone_to_request_filter(r->in.pszZone);
+ }
+ } else {
+ request_filter = r->in.dwContext;
+ }
+
+ if (r->in.pszZone == NULL) {
+ ret = dnsserver_operate_server(dsstate, mem_ctx,
+ r->in.pszOperation,
+ r->in.dwClientVersion,
+ r->in.dwTypeId,
+ &r->in.pData);
+ } else {
+ z = dnsserver_find_zone(dsstate->zones, r->in.pszZone);
+ /*
+ * In the case that request_filter is not 0 and z is NULL,
+ * the request is for a multizone operation, which we do not
+ * yet support, so just error on NULL zone name.
+ */
+ if (z == NULL) {
+ return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST;
+ }
+
+ ret = dnsserver_operate_zone(dsstate, mem_ctx, z,
+ request_filter,
+ r->in.pszOperation,
+ r->in.dwClientVersion,
+ r->in.dwTypeId,
+ &r->in.pData);
+ }
+
+ if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) {
+ NDR_PRINT_FUNCTION_DEBUG(DnssrvOperation2, NDR_IN, r);
+ }
+ return ret;
+}
+
+static WERROR dcesrv_DnssrvQuery2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvQuery2 *r)
+{
+ struct dnsserver_state *dsstate;
+ struct dnsserver_zone *z;
+ WERROR ret;
+
+ ZERO_STRUCTP(r->out.pdwTypeId);
+ ZERO_STRUCTP(r->out.ppData);
+
+ if ((dsstate = dnsserver_connect(dce_call)) == NULL) {
+ return WERR_DNS_ERROR_DS_UNAVAILABLE;
+ }
+
+ if (r->in.pszZone == NULL) {
+ /* FIXME: DNS Server Configuration Access Control List */
+ ret = dnsserver_query_server(dsstate, mem_ctx,
+ r->in.pszOperation,
+ r->in.dwClientVersion,
+ r->out.pdwTypeId,
+ r->out.ppData);
+ } else {
+ z = dnsserver_find_zone(dsstate->zones, r->in.pszZone);
+ if (z == NULL) {
+ return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST;
+ }
+
+ ret = dnsserver_query_zone(dsstate, mem_ctx, z,
+ r->in.pszOperation,
+ r->in.dwClientVersion,
+ r->out.pdwTypeId,
+ r->out.ppData);
+ }
+
+ if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) {
+ NDR_PRINT_FUNCTION_DEBUG(DnssrvQuery2, NDR_IN, r);
+ }
+ return ret;
+}
+
+static WERROR dcesrv_DnssrvComplexOperation2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvComplexOperation2 *r)
+{
+ struct dnsserver_state *dsstate;
+ struct dnsserver_zone *z;
+ WERROR ret;
+
+ ZERO_STRUCTP(r->out.pdwTypeOut);
+ ZERO_STRUCTP(r->out.ppDataOut);
+
+ if ((dsstate = dnsserver_connect(dce_call)) == NULL) {
+ return WERR_DNS_ERROR_DS_UNAVAILABLE;
+ }
+
+ if (r->in.pszZone == NULL) {
+ /* Server operation */
+ ret = dnsserver_complex_operate_server(dsstate, mem_ctx,
+ r->in.pszOperation,
+ r->in.dwClientVersion,
+ r->in.dwTypeIn,
+ &r->in.pDataIn,
+ r->out.pdwTypeOut,
+ r->out.ppDataOut);
+ } else {
+
+ z = dnsserver_find_zone(dsstate->zones, r->in.pszZone);
+ if (z == NULL) {
+ return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST;
+ }
+
+ ret = dnsserver_complex_operate_zone(dsstate, mem_ctx, z,
+ r->in.pszOperation,
+ r->in.dwClientVersion,
+ r->in.dwTypeIn,
+ &r->in.pDataIn,
+ r->out.pdwTypeOut,
+ r->out.ppDataOut);
+ }
+
+ if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) {
+ NDR_PRINT_FUNCTION_DEBUG(DnssrvComplexOperation2, NDR_IN, r);
+ }
+ return ret;
+}
+
+static WERROR dcesrv_DnssrvEnumRecords2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvEnumRecords2 *r)
+{
+ struct dnsserver_state *dsstate;
+ struct dnsserver_zone *z;
+ WERROR ret;
+
+ ZERO_STRUCTP(r->out.pdwBufferLength);
+ ZERO_STRUCTP(r->out.pBuffer);
+
+ if ((dsstate = dnsserver_connect(dce_call)) == NULL) {
+ return WERR_DNS_ERROR_DS_UNAVAILABLE;
+ }
+
+ if (r->in.pszZone == NULL) {
+ return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+ }
+
+ if (strcasecmp(r->in.pszZone, "..RootHints") == 0) {
+ ret = dnsserver_enumerate_root_records(dsstate, mem_ctx,
+ r->in.dwClientVersion,
+ r->in.pszNodeName,
+ r->in.wRecordType,
+ r->in.fSelectFlag,
+ r->out.pdwBufferLength,
+ r->out.pBuffer);
+ } else {
+ z = dnsserver_find_zone(dsstate->zones, r->in.pszZone);
+ if (z == NULL) {
+ return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+ }
+
+ ret = dnsserver_enumerate_records(dsstate, mem_ctx, z,
+ r->in.dwClientVersion,
+ r->in.pszNodeName,
+ r->in.pszStartChild,
+ r->in.wRecordType,
+ r->in.fSelectFlag,
+ r->in.pszFilterStart,
+ r->in.pszFilterStop,
+ r->out.pdwBufferLength,
+ r->out.pBuffer);
+
+ }
+
+ if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) {
+ NDR_PRINT_FUNCTION_DEBUG(DnssrvEnumRecords2, NDR_IN, r);
+ }
+ return ret;
+}
+
+static WERROR dcesrv_DnssrvUpdateRecord2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvUpdateRecord2 *r)
+{
+ struct dnsserver_state *dsstate;
+ struct dnsserver_zone *z;
+ WERROR ret;
+
+ if ((dsstate = dnsserver_connect(dce_call)) == NULL) {
+ return WERR_DNS_ERROR_DS_UNAVAILABLE;
+ }
+
+ if (r->in.pszZone == NULL) {
+ return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+ }
+
+ z = dnsserver_find_zone(dsstate->zones, r->in.pszZone);
+ if (z == NULL) {
+ return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+ }
+
+ ret = dnsserver_update_record(dsstate, mem_ctx, z,
+ r->in.dwClientVersion,
+ r->in.pszNodeName,
+ r->in.pAddRecord,
+ r->in.pDeleteRecord);
+
+ if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) {
+ NDR_PRINT_FUNCTION_DEBUG(DnssrvUpdateRecord2, NDR_IN, r);
+ }
+ return ret;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_dnsserver_s.c"
diff --git a/source4/rpc_server/dnsserver/dnsdata.c b/source4/rpc_server/dnsserver/dnsdata.c
new file mode 100644
index 0000000..002d9e6
--- /dev/null
+++ b/source4/rpc_server/dnsserver/dnsdata.c
@@ -0,0 +1,1121 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DNS Server
+
+ Copyright (C) Amitay Isaacs 2011
+
+ 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 "includes.h"
+#include "dnsserver.h"
+#include "dns_server/dnsserver_common.h"
+#include "lib/replace/system/network.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "librpc/gen_ndr/ndr_dnsserver.h"
+
+#undef strcasecmp
+
+struct IP4_ARRAY *ip4_array_copy(TALLOC_CTX *mem_ctx, struct IP4_ARRAY *ip4)
+{
+ struct IP4_ARRAY *ret;
+
+ if (!ip4) {
+ return NULL;
+ }
+
+ ret = talloc_zero(mem_ctx, struct IP4_ARRAY);
+ if (!ret) {
+ return ret;
+ }
+
+ ret->AddrCount = ip4->AddrCount;
+ if (ip4->AddrCount > 0) {
+ ret->AddrArray = talloc_zero_array(mem_ctx, unsigned int, ip4->AddrCount);
+ if (ret->AddrArray) {
+ memcpy(ret->AddrArray, ip4->AddrArray,
+ sizeof(unsigned int) * ip4->AddrCount);
+ } else {
+ talloc_free(ret);
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+
+struct DNS_ADDR_ARRAY *ip4_array_to_dns_addr_array(TALLOC_CTX *mem_ctx,
+ struct IP4_ARRAY *ip4)
+{
+ struct DNS_ADDR_ARRAY *ret;
+ int i;
+
+ if (!ip4) {
+ return NULL;
+ }
+
+ ret = talloc_zero(mem_ctx, struct DNS_ADDR_ARRAY);
+ if (!ret) {
+ return ret;
+ }
+
+ ret->MaxCount = ip4->AddrCount;
+ ret->AddrCount = ip4->AddrCount;
+ ret->Family = AF_INET;
+ if (ip4->AddrCount > 0) {
+ ret->AddrArray = talloc_zero_array(mem_ctx, struct DNS_ADDR, ip4->AddrCount);
+ if (ret->AddrArray) {
+ for (i=0; i<ip4->AddrCount; i++) {
+ ret->AddrArray[i].MaxSa[0] = 0x02;
+ ret->AddrArray[i].MaxSa[3] = 53;
+ memcpy(&ret->AddrArray[i].MaxSa[4], ip4->AddrArray,
+ sizeof(unsigned int));
+ ret->AddrArray[i].DnsAddrUserDword[0] = 6;
+ }
+
+ } else {
+ talloc_free(ret);
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+struct IP4_ARRAY *dns_addr_array_to_ip4_array(TALLOC_CTX *mem_ctx,
+ struct DNS_ADDR_ARRAY *ip)
+{
+ struct IP4_ARRAY *ret;
+ size_t i, count, curr;
+
+ if (ip == NULL) {
+ return NULL;
+ }
+ /* We must only return IPv4 addresses.
+ The passed DNS_ADDR_ARRAY may contain:
+ - only ipv4 addresses
+ - only ipv6 addresses
+ - a mixture of both
+ - an empty array
+ */
+ ret = talloc_zero(mem_ctx, struct IP4_ARRAY);
+ if (!ret) {
+ return ret;
+ }
+ if (ip->AddrCount == 0 || ip->Family == AF_INET6) {
+ ret->AddrCount = 0;
+ return ret;
+ }
+ /* Now only ipv4 addresses or a mixture are left */
+ count = 0;
+ for (i = 0; i < ip->AddrCount; i++) {
+ if (ip->AddrArray[i].MaxSa[0] == 0x02) {
+ /* Is ipv4 */
+ count++;
+ }
+ }
+ if (count == 0) {
+ /* should not happen */
+ ret->AddrCount = 0;
+ return ret;
+ }
+ ret->AddrArray = talloc_zero_array(mem_ctx, uint32_t, count);
+ if (ret->AddrArray) {
+ curr = 0;
+ for (i = 0; i < ip->AddrCount; i++) {
+ if (ip->AddrArray[i].MaxSa[0] == 0x02) {
+ /* Is ipv4 */
+ memcpy(&ret->AddrArray[curr],
+ &ip->AddrArray[i].MaxSa[4],
+ sizeof(uint32_t));
+ curr++;
+ }
+ }
+ } else {
+ talloc_free(ret);
+ return NULL;
+ }
+ ret->AddrCount = curr;
+ return ret;
+}
+
+struct DNS_ADDR_ARRAY *dns_addr_array_copy(TALLOC_CTX *mem_ctx,
+ struct DNS_ADDR_ARRAY *addr)
+{
+ struct DNS_ADDR_ARRAY *ret;
+
+ if (!addr) {
+ return NULL;
+ }
+
+ ret = talloc_zero(mem_ctx, struct DNS_ADDR_ARRAY);
+ if (!ret) {
+ return ret;
+ }
+
+ ret->MaxCount = addr->MaxCount;
+ ret->AddrCount = addr->AddrCount;
+ ret->Family = addr->Family;
+ if (addr->AddrCount > 0) {
+ ret->AddrArray = talloc_zero_array(mem_ctx, struct DNS_ADDR, addr->AddrCount);
+ if (ret->AddrArray) {
+ memcpy(ret->AddrArray, addr->AddrArray,
+ sizeof(struct DNS_ADDR) * addr->AddrCount);
+ } else {
+ talloc_free(ret);
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+
+int dns_split_name_components(TALLOC_CTX *tmp_ctx, const char *name, char ***components)
+{
+ char *str = NULL, *ptr, **list;
+ int count = 0;
+
+ if (name == NULL) {
+ return 0;
+ }
+
+ str = talloc_strdup(tmp_ctx, name);
+ if (!str) {
+ goto failed;
+ }
+
+ list = talloc_zero_array(tmp_ctx, char *, 0);
+ if (!list) {
+ goto failed;
+ }
+
+ ptr = strtok(str, ".");
+ while (ptr != NULL) {
+ count++;
+ list = talloc_realloc(tmp_ctx, list, char *, count);
+ if (!list) {
+ goto failed;
+ }
+ list[count-1] = talloc_strdup(tmp_ctx, ptr);
+ if (list[count-1] == NULL) {
+ goto failed;
+ }
+ ptr = strtok(NULL, ".");
+ }
+
+ talloc_free(str);
+
+ *components = list;
+ return count;
+
+failed:
+ TALLOC_FREE(str);
+ return -1;
+}
+
+
+char *dns_split_node_name(TALLOC_CTX *tmp_ctx, const char *node_name, const char *zone_name)
+{
+ char **nlist, **zlist;
+ char *prefix;
+ int ncount, zcount, i, match;
+
+ /*
+ * If node_name is "@", return the zone_name
+ * If node_name is ".", return NULL
+ * If there is no '.' in node_name, return the node_name as is.
+ *
+ * If node_name does not have zone_name in it, return the node_name as is.
+ *
+ * If node_name has additional components as compared to zone_name
+ * return only the additional components as a prefix.
+ *
+ */
+ if (strcmp(node_name, "@") == 0) {
+ prefix = talloc_strdup(tmp_ctx, zone_name);
+ } else if (strcmp(node_name, ".") == 0) {
+ prefix = NULL;
+ } else if (strchr(node_name, '.') == NULL) {
+ prefix = talloc_strdup(tmp_ctx, node_name);
+ } else {
+ zcount = dns_split_name_components(tmp_ctx, zone_name, &zlist);
+ ncount = dns_split_name_components(tmp_ctx, node_name, &nlist);
+ if (zcount < 0 || ncount < 0) {
+ return NULL;
+ }
+
+ if (ncount < zcount) {
+ prefix = talloc_strdup(tmp_ctx, node_name);
+ } else {
+ match = 0;
+ for (i=1; i<=zcount; i++) {
+ if (strcasecmp(nlist[ncount-i], zlist[zcount-i]) != 0) {
+ break;
+ }
+ match++;
+ }
+
+ if (match == ncount) {
+ prefix = talloc_strdup(tmp_ctx, zone_name);
+ } else {
+ prefix = talloc_strdup(tmp_ctx, nlist[0]);
+ if (prefix != NULL) {
+ for (i=1; i<ncount-match; i++) {
+ prefix = talloc_asprintf_append(prefix, ".%s", nlist[i]);
+ if (prefix == NULL) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ talloc_free(zlist);
+ talloc_free(nlist);
+ }
+
+ return prefix;
+}
+
+
+void dnsp_to_dns_copy(TALLOC_CTX *mem_ctx, struct dnsp_DnssrvRpcRecord *dnsp,
+ struct DNS_RPC_RECORD *dns)
+{
+ int i, len;
+
+ ZERO_STRUCTP(dns);
+
+ dns->wDataLength = dnsp->wDataLength;
+ dns->wType = dnsp->wType;
+ dns->dwFlags = dnsp->rank;
+ dns->dwSerial = dnsp->dwSerial;
+ dns->dwTtlSeconds = dnsp->dwTtlSeconds;
+ dns->dwTimeStamp = dnsp->dwTimeStamp;
+
+ switch (dnsp->wType) {
+
+ case DNS_TYPE_TOMBSTONE:
+ dns->data.EntombedTime = dnsp->data.EntombedTime;
+ break;
+
+ case DNS_TYPE_A:
+ dns->data.ipv4 = talloc_strdup(mem_ctx, dnsp->data.ipv4);
+ break;
+
+ case DNS_TYPE_NS:
+ len = strlen(dnsp->data.ns);
+ if (dnsp->data.ns[len-1] == '.') {
+ dns->data.name.len = len;
+ dns->data.name.str = talloc_strdup(mem_ctx, dnsp->data.ns);
+ } else {
+ dns->data.name.len = len+1;
+ dns->data.name.str = talloc_asprintf(mem_ctx, "%s.", dnsp->data.ns);
+ }
+ break;
+
+ case DNS_TYPE_CNAME:
+ len = strlen(dnsp->data.cname);
+ if (dnsp->data.cname[len-1] == '.') {
+ dns->data.name.len = len;
+ dns->data.name.str = talloc_strdup(mem_ctx, dnsp->data.cname);
+ } else {
+ dns->data.name.len = len+1;
+ dns->data.name.str = talloc_asprintf(mem_ctx, "%s.", dnsp->data.cname);
+ }
+ break;
+
+ case DNS_TYPE_SOA:
+ dns->data.soa.dwSerialNo = dnsp->data.soa.serial;
+ dns->data.soa.dwRefresh = dnsp->data.soa.refresh;
+ dns->data.soa.dwRetry = dnsp->data.soa.retry;
+ dns->data.soa.dwExpire = dnsp->data.soa.expire;
+ dns->data.soa.dwMinimumTtl = dnsp->data.soa.minimum;
+
+ len = strlen(dnsp->data.soa.mname);
+ if (dnsp->data.soa.mname[len-1] == '.') {
+ dns->data.soa.NamePrimaryServer.len = len;
+ dns->data.soa.NamePrimaryServer.str = talloc_strdup(mem_ctx, dnsp->data.soa.mname);
+ } else {
+ dns->data.soa.NamePrimaryServer.len = len+1;
+ dns->data.soa.NamePrimaryServer.str = talloc_asprintf(mem_ctx, "%s.", dnsp->data.soa.mname);
+ }
+
+ len = strlen(dnsp->data.soa.rname);
+ if (dnsp->data.soa.rname[len-1] == '.') {
+ dns->data.soa.ZoneAdministratorEmail.len = len;
+ dns->data.soa.ZoneAdministratorEmail.str = talloc_strdup(mem_ctx, dnsp->data.soa.rname);
+ } else {
+ dns->data.soa.ZoneAdministratorEmail.len = len+1;
+ dns->data.soa.ZoneAdministratorEmail.str = talloc_asprintf(mem_ctx, "%s.", dnsp->data.soa.rname);
+ }
+ break;
+
+ case DNS_TYPE_PTR:
+ dns->data.ptr.len = strlen(dnsp->data.ptr);
+ dns->data.ptr.str = talloc_strdup(mem_ctx, dnsp->data.ptr);
+ break;
+
+ case DNS_TYPE_MX:
+ dns->data.mx.wPreference = dnsp->data.mx.wPriority;
+ len = strlen(dnsp->data.mx.nameTarget);
+ if (dnsp->data.mx.nameTarget[len-1] == '.') {
+ dns->data.mx.nameExchange.len = len;
+ dns->data.mx.nameExchange.str = talloc_strdup(mem_ctx, dnsp->data.mx.nameTarget);
+ } else {
+ dns->data.mx.nameExchange.len = len+1;
+ dns->data.mx.nameExchange.str = talloc_asprintf(mem_ctx, "%s.", dnsp->data.mx.nameTarget);
+ }
+ break;
+
+ case DNS_TYPE_TXT:
+ dns->data.txt.count = dnsp->data.txt.count;
+ dns->data.txt.str = talloc_array(mem_ctx, struct DNS_RPC_NAME, dnsp->data.txt.count);
+ for (i=0; i<dnsp->data.txt.count; i++) {
+ dns->data.txt.str[i].str = talloc_strdup(mem_ctx, dnsp->data.txt.str[i]);
+ dns->data.txt.str[i].len = strlen(dnsp->data.txt.str[i]);
+ }
+ break;
+
+ case DNS_TYPE_AAAA:
+ dns->data.ipv6 = talloc_strdup(mem_ctx, dnsp->data.ipv6);
+ break;
+
+ case DNS_TYPE_SRV:
+ dns->data.srv.wPriority = dnsp->data.srv.wPriority;
+ dns->data.srv.wWeight = dnsp->data.srv.wWeight;
+ dns->data.srv.wPort = dnsp->data.srv.wPort;
+ len = strlen(dnsp->data.srv.nameTarget);
+ if (dnsp->data.srv.nameTarget[len-1] == '.') {
+ dns->data.srv.nameTarget.len = len;
+ dns->data.srv.nameTarget.str = talloc_strdup(mem_ctx, dnsp->data.srv.nameTarget);
+ } else {
+ dns->data.srv.nameTarget.len = len+1;
+ dns->data.srv.nameTarget.str = talloc_asprintf(mem_ctx, "%s.", dnsp->data.srv.nameTarget);
+ }
+ break;
+
+ default:
+ memcpy(&dns->data, &dnsp->data, sizeof(union DNS_RPC_RECORD_DATA));
+ DEBUG(0, ("dnsserver: Found Unhandled DNS record type=%d", dnsp->wType));
+ }
+
+}
+
+WERROR dns_to_dnsp_convert(TALLOC_CTX *mem_ctx, struct DNS_RPC_RECORD *dns,
+ struct dnsp_DnssrvRpcRecord **out_dnsp, bool check_name)
+{
+ WERROR res;
+ int i, len;
+ const char *name;
+ char *talloc_res = NULL;
+ struct dnsp_DnssrvRpcRecord *dnsp = NULL;
+
+ dnsp = talloc_zero(mem_ctx, struct dnsp_DnssrvRpcRecord);
+ if (dnsp == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ dnsp->wDataLength = dns->wDataLength;
+ dnsp->wType = dns->wType;
+ dnsp->version = 5;
+ dnsp->rank = dns->dwFlags & 0x000000FF;
+ dnsp->dwSerial = dns->dwSerial;
+ dnsp->dwTtlSeconds = dns->dwTtlSeconds;
+ dnsp->dwTimeStamp = dns->dwTimeStamp;
+
+ switch (dns->wType) {
+
+ case DNS_TYPE_TOMBSTONE:
+ dnsp->data.EntombedTime = dns->data.EntombedTime;
+ break;
+
+ case DNS_TYPE_A:
+ talloc_res = talloc_strdup(mem_ctx, dns->data.ipv4);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.ipv4 = talloc_res;
+ break;
+
+ case DNS_TYPE_NS:
+ name = dns->data.name.str;
+ len = dns->data.name.len;
+
+ if (check_name) {
+ res = dns_name_check(mem_ctx, len, name);
+ if (!W_ERROR_IS_OK(res)) {
+ return res;
+ }
+ }
+
+ if (len > 0 && name[len-1] == '.') {
+ talloc_res = talloc_strndup(mem_ctx, name, len-1);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.ns = talloc_res;
+ } else {
+ talloc_res = talloc_strdup(mem_ctx, name);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.ns = talloc_res;
+ }
+
+ break;
+
+ case DNS_TYPE_CNAME:
+ name = dns->data.name.str;
+ len = dns->data.name.len;
+
+ if (check_name) {
+ res = dns_name_check(mem_ctx, len, name);
+ if (!W_ERROR_IS_OK(res)) {
+ return res;
+ }
+ }
+
+ if (len > 0 && name[len-1] == '.') {
+ talloc_res = talloc_strndup(mem_ctx, name, len-1);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.cname = talloc_res;
+ } else {
+ talloc_res = talloc_strdup(mem_ctx, name);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.cname = talloc_res;
+ }
+
+ break;
+
+ case DNS_TYPE_SOA:
+ dnsp->data.soa.serial = dns->data.soa.dwSerialNo;
+ dnsp->data.soa.refresh = dns->data.soa.dwRefresh;
+ dnsp->data.soa.retry = dns->data.soa.dwRetry;
+ dnsp->data.soa.expire = dns->data.soa.dwExpire;
+ dnsp->data.soa.minimum = dns->data.soa.dwMinimumTtl;
+
+ name = dns->data.soa.NamePrimaryServer.str;
+ len = dns->data.soa.NamePrimaryServer.len;
+
+ if (check_name) {
+ res = dns_name_check(mem_ctx, len, name);
+ if (!W_ERROR_IS_OK(res)) {
+ return res;
+ }
+ }
+
+ if (len > 0 && name[len-1] == '.') {
+ talloc_res = talloc_strndup(mem_ctx, name, len-1);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.soa.mname = talloc_res;
+ } else {
+ talloc_res = talloc_strdup(mem_ctx, name);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.soa.mname = talloc_res;
+ }
+
+ name = dns->data.soa.ZoneAdministratorEmail.str;
+ len = dns->data.soa.ZoneAdministratorEmail.len;
+
+ res = dns_name_check(mem_ctx, len, name);
+ if (!W_ERROR_IS_OK(res)) {
+ return res;
+ }
+
+ if (len > 0 && name[len-1] == '.') {
+ talloc_res = talloc_strndup(mem_ctx, name, len-1);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.soa.rname = talloc_res;
+ } else {
+ talloc_res = talloc_strdup(mem_ctx, name);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.soa.rname = talloc_res;
+ }
+
+ break;
+
+ case DNS_TYPE_PTR:
+ name = dns->data.ptr.str;
+ len = dns->data.ptr.len;
+
+ if (check_name) {
+ res = dns_name_check(mem_ctx, len, name);
+ if (!W_ERROR_IS_OK(res)) {
+ return res;
+ }
+ }
+
+ talloc_res = talloc_strdup(mem_ctx, name);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.ptr = talloc_res;
+
+ break;
+
+ case DNS_TYPE_MX:
+ dnsp->data.mx.wPriority = dns->data.mx.wPreference;
+
+ name = dns->data.mx.nameExchange.str;
+ len = dns->data.mx.nameExchange.len;
+
+ if (check_name) {
+ res = dns_name_check(mem_ctx, len, name);
+ if (!W_ERROR_IS_OK(res)) {
+ return res;
+ }
+ }
+
+ if (len > 0 && name[len-1] == '.') {
+ talloc_res = talloc_strndup(mem_ctx, name, len-1);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.mx.nameTarget = talloc_res;
+ } else {
+ talloc_res = talloc_strdup(mem_ctx, name);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.mx.nameTarget = talloc_res;
+ }
+
+ break;
+
+ case DNS_TYPE_TXT:
+ dnsp->data.txt.count = dns->data.txt.count;
+ dnsp->data.txt.str = talloc_array(mem_ctx, const char *, dns->data.txt.count);
+ for (i=0; i<dns->data.txt.count; i++) {
+ talloc_res = talloc_strdup(mem_ctx, dns->data.txt.str[i].str);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.txt.str[i] = talloc_res;
+ }
+ break;
+
+ case DNS_TYPE_AAAA:
+ dnsp->data.ipv6 = talloc_strdup(mem_ctx, dns->data.ipv6);
+ break;
+
+ case DNS_TYPE_SRV:
+ dnsp->data.srv.wPriority = dns->data.srv.wPriority;
+ dnsp->data.srv.wWeight = dns->data.srv.wWeight;
+ dnsp->data.srv.wPort = dns->data.srv.wPort;
+
+ name = dns->data.srv.nameTarget.str;
+ len = dns->data.srv.nameTarget.len;
+
+ if (check_name) {
+ res = dns_name_check(mem_ctx, len, name);
+ if (!W_ERROR_IS_OK(res)) {
+ return res;
+ }
+ }
+
+ if (len > 0 && name[len-1] == '.') {
+ talloc_res = talloc_strndup(mem_ctx, name, len-1);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.srv.nameTarget = talloc_res;
+ } else {
+ talloc_res = talloc_strdup(mem_ctx, name);
+ if (talloc_res == NULL) {
+ goto fail_nomemory;
+ }
+ dnsp->data.srv.nameTarget = talloc_res;
+ }
+
+ break;
+
+ default:
+ memcpy(&dnsp->data, &dns->data, sizeof(union dnsRecordData));
+ DEBUG(0, ("dnsserver: Found Unhandled DNS record type=%d", dns->wType));
+ }
+
+ *out_dnsp = dnsp;
+ return WERR_OK;
+
+fail_nomemory:
+ return WERR_NOT_ENOUGH_MEMORY;
+}
+
+/* Intialize tree with given name as the root */
+static struct dns_tree *dns_tree_init(TALLOC_CTX *mem_ctx, const char *name, void *data)
+{
+ struct dns_tree *tree;
+
+ tree = talloc_zero(mem_ctx, struct dns_tree);
+ if (tree == NULL) {
+ return NULL;
+ }
+
+ tree->name = talloc_strdup(tree, name);
+ if (tree->name == NULL) {
+ talloc_free(tree);
+ return NULL;
+ }
+
+ tree->data = data;
+
+ return tree;
+}
+
+
+/* Add a child one level below */
+static struct dns_tree *dns_tree_add(struct dns_tree *tree, const char *name, void *data)
+{
+ struct dns_tree *node;
+
+ node = talloc_zero(tree, struct dns_tree);
+ if (node == NULL) {
+ return NULL;
+ }
+
+ node->name = talloc_strdup(tree, name);
+ if (node->name == NULL) {
+ talloc_free(node);
+ return NULL;
+ }
+ node->level = tree->level + 1;
+ node->num_children = 0;
+ node->children = NULL;
+ node->data = data;
+
+ if (tree->num_children == 0) {
+ tree->children = talloc_zero(tree, struct dns_tree *);
+ } else {
+ tree->children = talloc_realloc(tree, tree->children, struct dns_tree *,
+ tree->num_children+1);
+ }
+ if (tree->children == NULL) {
+ talloc_free(node);
+ return NULL;
+ }
+ tree->children[tree->num_children] = node;
+ tree->num_children++;
+
+ return node;
+}
+
+/* Find a node that matches the name components */
+static struct dns_tree *dns_tree_find(struct dns_tree *tree, int ncount, char **nlist, int *match_count)
+{
+ struct dns_tree *node, *next;
+ int i, j, start;
+
+ *match_count = -1;
+
+ if (strcmp(tree->name, "@") == 0) {
+ start = 0;
+ } else {
+ if (strcasecmp(tree->name, nlist[ncount-1]) != 0) {
+ return NULL;
+ }
+ start = 1;
+ *match_count = 0;
+ }
+
+ node = tree;
+ for (i=start; i<ncount; i++) {
+ if (node->num_children == 0) {
+ break;
+ }
+ next = NULL;
+ for (j=0; j<node->num_children; j++) {
+ if (strcasecmp(nlist[(ncount-1)-i], node->children[j]->name) == 0) {
+ next = node->children[j];
+ *match_count = i;
+ break;
+ }
+ }
+ if (next == NULL) {
+ break;
+ } else {
+ node = next;
+ }
+ }
+
+ return node;
+}
+
+/* Build a 2-level tree for resulting dns names */
+struct dns_tree *dns_build_tree(TALLOC_CTX *mem_ctx, const char *name, struct ldb_result *res)
+{
+ struct dns_tree *root, *base, *tree, *node;
+ const char *ptr;
+ int rootcount, ncount;
+ char **nlist;
+ int i, level, match_count;
+
+ rootcount = dns_split_name_components(mem_ctx, name, &nlist);
+ if (rootcount <= 0) {
+ return NULL;
+ }
+
+ root = dns_tree_init(mem_ctx, nlist[rootcount-1], NULL);
+ if (root == NULL) {
+ talloc_free(nlist);
+ return NULL;
+ }
+
+ tree = root;
+ for (i=rootcount-2; i>=0; i--) {
+ tree = dns_tree_add(tree, nlist[i], NULL);
+ if (tree == NULL) {
+ goto failed;
+ }
+ }
+
+ base = tree;
+
+ /* Add all names in the result in a tree */
+ for (i=0; i<res->count; i++) {
+ ptr = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL);
+ if (ptr == NULL) {
+ DBG_ERR("dnsserver: dns record has no name (%s)",
+ ldb_dn_get_linearized(res->msgs[i]->dn));
+ goto failed;
+ }
+
+ /*
+ * This might be the sub-domain in the zone being
+ * requested, or @ for the root of the zone
+ */
+ if (strcasecmp(ptr, name) == 0) {
+ base->data = res->msgs[i];
+ continue;
+ }
+
+ ncount = dns_split_name_components(root, ptr, &nlist);
+ if (ncount < 0) {
+ goto failed;
+ }
+
+ /* Find matching node */
+ tree = dns_tree_find(root, ncount, nlist, &match_count);
+ if (tree == NULL) {
+ goto failed;
+ }
+
+ /* If the node is on leaf, then add record data */
+ if (match_count+1 == ncount) {
+ tree->data = res->msgs[i];
+ }
+
+ /* Add missing name components */
+ for (level=match_count+1; level<ncount; level++) {
+ if (tree->level == rootcount+1) {
+ break;
+ }
+ if (level == ncount-1) {
+ node = dns_tree_add(tree, nlist[(ncount-1)-level], res->msgs[i]);
+ } else {
+ node = dns_tree_add(tree, nlist[(ncount-1)-level], NULL);
+ }
+ if (node == NULL) {
+ goto failed;
+ }
+ tree = node;
+ }
+
+ talloc_free(nlist);
+ }
+
+ /* Mark the base record, so it can be found easily */
+ base->level = -1;
+
+ return root;
+
+failed:
+ talloc_free(nlist);
+ talloc_free(root);
+ return NULL;
+}
+
+
+static void _dns_add_name(TALLOC_CTX *mem_ctx, const char *name, char ***add_names, int *add_count)
+{
+ int i;
+ char **ptr = *add_names;
+ int count = *add_count;
+
+ for (i=0; i<count; i++) {
+ if (strcasecmp(ptr[i], name) == 0) {
+ return;
+ }
+ }
+
+ ptr = talloc_realloc(mem_ctx, ptr, char *, count+1);
+ if (ptr == NULL) {
+ return;
+ }
+
+ ptr[count] = talloc_strdup(mem_ctx, name);
+ if (ptr[count] == NULL) {
+ talloc_free(ptr);
+ return;
+ }
+
+ *add_names = ptr;
+ *add_count = count+1;
+}
+
+
+static void dns_find_additional_names(TALLOC_CTX *mem_ctx, struct dnsp_DnssrvRpcRecord *rec, char ***add_names, int *add_count)
+{
+ if (add_names == NULL) {
+ return;
+ }
+
+ switch (rec->wType) {
+
+ case DNS_TYPE_NS:
+ _dns_add_name(mem_ctx, rec->data.ns, add_names, add_count);
+ break;
+
+ case DNS_TYPE_CNAME:
+ _dns_add_name(mem_ctx, rec->data.cname, add_names, add_count);
+ break;
+
+ case DNS_TYPE_SOA:
+ _dns_add_name(mem_ctx, rec->data.soa.mname, add_names, add_count);
+ break;
+
+ case DNS_TYPE_MX:
+ _dns_add_name(mem_ctx, rec->data.mx.nameTarget, add_names, add_count);
+ break;
+
+ case DNS_TYPE_SRV:
+ _dns_add_name(mem_ctx, rec->data.srv.nameTarget, add_names, add_count);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+WERROR dns_fill_records_array(TALLOC_CTX *mem_ctx,
+ struct dnsserver_zone *z,
+ enum dns_record_type record_type,
+ unsigned int select_flag,
+ const char *branch_name,
+ struct ldb_message *msg,
+ int num_children,
+ struct DNS_RPC_RECORDS_ARRAY *recs,
+ char ***add_names,
+ int *add_count)
+{
+ struct ldb_message_element *el;
+ const char *ptr;
+ int i, j;
+ bool found;
+
+ if (recs->count == 0) {
+ recs->rec = talloc_zero(recs, struct DNS_RPC_RECORDS);
+ } else {
+ recs->rec = talloc_realloc(recs, recs->rec, struct DNS_RPC_RECORDS, recs->count+1);
+ }
+ if (recs->rec == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ i = recs->count;
+ recs->rec[i].wLength = 0;
+ recs->rec[i].wRecordCount = 0;
+ recs->rec[i].dwChildCount = num_children;
+ recs->rec[i].dwFlags = 0;
+
+ /* The base records returned with empty name */
+ /* Children records returned with names */
+ if (branch_name == NULL) {
+ recs->rec[i].dnsNodeName.str = talloc_strdup(recs, "");
+ recs->rec[i].dnsNodeName.len = 0;
+ } else {
+ recs->rec[i].dnsNodeName.str = talloc_strdup(recs, branch_name);
+ recs->rec[i].dnsNodeName.len = strlen(branch_name);
+ }
+ recs->rec[i].records = talloc_zero_array(recs, struct DNS_RPC_RECORD, 0);
+ recs->count++;
+
+ /* Allow empty records */
+ if (msg == NULL) {
+ return WERR_OK;
+ }
+
+ /* Do not return RR records, if the node has children */
+ if (branch_name != NULL && num_children > 0) {
+ return WERR_OK;
+ }
+
+ ptr = ldb_msg_find_attr_as_string(msg, "name", NULL);
+ if (ptr == NULL) {
+ DBG_ERR("dnsserver: dns record has no name (%s)",
+ ldb_dn_get_linearized(msg->dn));
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ el = ldb_msg_find_element(msg, "dnsRecord");
+ if (el == NULL || el->values == 0) {
+ return WERR_OK;
+ }
+
+ /* Add RR records */
+ for (j=0; j<el->num_values; j++) {
+ struct dnsp_DnssrvRpcRecord dnsp_rec;
+ struct DNS_RPC_RECORD *dns_rec;
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_pull_struct_blob(&el->values[j], mem_ctx, &dnsp_rec,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0, ("dnsserver: Unable to parse dns record (%s)", ldb_dn_get_linearized(msg->dn)));
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ /* Match the records based on search criteria */
+ if (record_type == DNS_TYPE_ALL || dnsp_rec.wType == record_type) {
+ found = false;
+
+ if (select_flag & DNS_RPC_VIEW_AUTHORITY_DATA) {
+ if (dnsp_rec.rank == DNS_RANK_ZONE) {
+ found = true;
+ } else if (dnsp_rec.rank == DNS_RANK_NS_GLUE) {
+ /*
+ * If branch_name is NULL, we're
+ * explicitly asked to also return
+ * DNS_RANK_NS_GLUE records
+ */
+ if (branch_name == NULL) {
+ found = true;
+ }
+ }
+ }
+ if (select_flag & DNS_RPC_VIEW_CACHE_DATA) {
+ if (dnsp_rec.rank == DNS_RANK_ZONE) {
+ found = true;
+ }
+ }
+ if (select_flag & DNS_RPC_VIEW_GLUE_DATA) {
+ if (dnsp_rec.rank == DNS_RANK_GLUE) {
+ found = true;
+ }
+ }
+ if (select_flag & DNS_RPC_VIEW_ROOT_HINT_DATA) {
+ if (dnsp_rec.rank == DNS_RANK_ROOT_HINT) {
+ found = true;
+ }
+ }
+
+ if (found) {
+ recs->rec[i].records = talloc_realloc(recs,
+ recs->rec[i].records,
+ struct DNS_RPC_RECORD,
+ recs->rec[i].wRecordCount+1);
+ if (recs->rec[i].records == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ dns_rec = &recs->rec[i].records[recs->rec[i].wRecordCount];
+ dnsp_to_dns_copy(recs, &dnsp_rec, dns_rec);
+
+ /* Fix record flags */
+ if (strcmp(ptr, "@") == 0) {
+ dns_rec->dwFlags |= DNS_RPC_FLAG_ZONE_ROOT;
+
+ if (dnsp_rec.rank == DNS_RANK_ZONE) {
+ dns_rec->dwFlags |= DNS_RPC_FLAG_AUTH_ZONE_ROOT;
+ }
+ }
+
+ if (dns_rec->dwFlags == DNS_RANK_NS_GLUE) {
+ dns_rec->dwFlags |= DNS_RPC_FLAG_ZONE_ROOT;
+ }
+
+ recs->rec[i].wRecordCount++;
+
+ dns_find_additional_names(mem_ctx, &dnsp_rec, add_names, add_count);
+ }
+ }
+ }
+
+ return WERR_OK;
+}
+
+
+int dns_name_compare(struct ldb_message * const *m1, struct ldb_message * const *m2,
+ const char *search_name)
+{
+ const char *name1, *name2;
+ const char *ptr1, *ptr2;
+
+ name1 = ldb_msg_find_attr_as_string(*m1, "name", NULL);
+ name2 = ldb_msg_find_attr_as_string(*m2, "name", NULL);
+ if (name1 == NULL || name2 == NULL) {
+ return 0;
+ }
+
+ /* Compare the last components of names.
+ * If search_name is not NULL, compare the second last components of names */
+ ptr1 = strrchr(name1, '.');
+ if (ptr1 == NULL) {
+ ptr1 = name1;
+ } else {
+ if (search_name && strcasecmp(ptr1+1, search_name) == 0) {
+ ptr1--;
+ while (ptr1 != name1) {
+ ptr1--;
+ if (*ptr1 == '.') {
+ break;
+ }
+ }
+ }
+ if (*ptr1 == '.') {
+ ptr1 = &ptr1[1];
+ }
+ }
+
+ ptr2 = strrchr(name2, '.');
+ if (ptr2 == NULL) {
+ ptr2 = name2;
+ } else {
+ if (search_name && strcasecmp(ptr2+1, search_name) == 0) {
+ ptr2--;
+ while (ptr2 != name2) {
+ ptr2--;
+ if (*ptr2 == '.') {
+ break;
+ }
+ }
+ }
+ if (*ptr2 == '.') {
+ ptr2 = &ptr2[1];
+ }
+ }
+
+ return strcasecmp(ptr1, ptr2);
+}
diff --git a/source4/rpc_server/dnsserver/dnsdb.c b/source4/rpc_server/dnsserver/dnsdb.c
new file mode 100644
index 0000000..bde54a0
--- /dev/null
+++ b/source4/rpc_server/dnsserver/dnsdb.c
@@ -0,0 +1,1272 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DNS Server
+
+ Copyright (C) Amitay Isaacs 2011
+
+ 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 "includes.h"
+#include "dnsserver.h"
+#include "lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/security.h"
+#include "dsdb/common/util.h"
+
+#undef strcasecmp
+
+/* There are only 2 fixed partitions for DNS */
+struct dnsserver_partition *dnsserver_db_enumerate_partitions(TALLOC_CTX *mem_ctx,
+ struct dnsserver_serverinfo *serverinfo,
+ struct ldb_context *samdb)
+{
+ struct dnsserver_partition *partitions, *p;
+
+ partitions = NULL;
+
+ /* Domain partition */
+ p = talloc_zero(mem_ctx, struct dnsserver_partition);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ p->partition_dn = ldb_dn_new(p, samdb, serverinfo->pszDomainDirectoryPartition);
+ if (p->partition_dn == NULL) {
+ goto failed;
+ }
+
+ p->pszDpFqdn = samdb_dn_to_dns_domain(p, p->partition_dn);
+ p->dwDpFlags = DNS_DP_AUTOCREATED | DNS_DP_DOMAIN_DEFAULT | DNS_DP_ENLISTED;
+ p->is_forest = false;
+
+ DLIST_ADD_END(partitions, p);
+
+ /* Forest Partition */
+ p = talloc_zero(mem_ctx, struct dnsserver_partition);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ p->partition_dn = ldb_dn_new(p, samdb, serverinfo->pszForestDirectoryPartition);
+ if (p->partition_dn == NULL) {
+ goto failed;
+ }
+
+ p->pszDpFqdn = samdb_dn_to_dns_domain(p, p->partition_dn);
+ p->dwDpFlags = DNS_DP_AUTOCREATED | DNS_DP_FOREST_DEFAULT | DNS_DP_ENLISTED;
+ p->is_forest = true;
+
+ DLIST_ADD_END(partitions, p);
+
+ return partitions;
+
+failed:
+ return NULL;
+
+}
+
+
+/* Search for all dnsZone records */
+struct dnsserver_zone *dnsserver_db_enumerate_zones(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_partition *p)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char * const attrs[] = {"name", "dNSProperty", NULL};
+ struct ldb_dn *dn;
+ struct ldb_result *res;
+ struct dnsserver_zone *zones, *z;
+ int i, j, ret;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ dn = ldb_dn_copy(tmp_ctx, p->partition_dn);
+ if (dn == NULL) {
+ goto failed;
+ }
+ if (!ldb_dn_add_child_fmt(dn, "CN=MicrosoftDNS")) {
+ goto failed;
+ }
+
+ ret = ldb_search(samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
+ attrs, "(objectClass=dnsZone)");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("dnsserver: Failed to find DNS Zones in %s\n",
+ ldb_dn_get_linearized(dn)));
+ goto failed;
+ }
+
+ zones = NULL;
+ for(i=0; i<res->count; i++) {
+ char *name;
+ struct ldb_message_element *element = NULL;
+ struct dnsp_DnsProperty *props = NULL;
+ enum ndr_err_code err;
+ z = talloc_zero(mem_ctx, struct dnsserver_zone);
+ if (z == NULL) {
+ goto failed;
+ }
+
+ z->partition = p;
+ name = talloc_strdup(z,
+ ldb_msg_find_attr_as_string(res->msgs[i],
+ "name", NULL));
+ if (strcmp(name, "..TrustAnchors") == 0) {
+ talloc_free(z);
+ continue;
+ }
+ if (strcmp(name, "RootDNSServers") == 0) {
+ talloc_free(name);
+ z->name = talloc_strdup(z, ".");
+ } else {
+ z->name = name;
+ }
+ z->zone_dn = talloc_steal(z, res->msgs[i]->dn);
+
+ DLIST_ADD_END(zones, z);
+ DEBUG(2, ("dnsserver: Found DNS zone %s\n", z->name));
+
+ element = ldb_msg_find_element(res->msgs[i], "dNSProperty");
+ if(element != NULL){
+ props = talloc_zero_array(tmp_ctx,
+ struct dnsp_DnsProperty,
+ element->num_values);
+ for (j = 0; j < element->num_values; j++ ) {
+ err = ndr_pull_struct_blob(
+ &(element->values[j]),
+ mem_ctx,
+ &props[j],
+ (ndr_pull_flags_fn_t)
+ ndr_pull_dnsp_DnsProperty);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)){
+ /*
+ * Per Microsoft we must
+ * ignore invalid data here
+ * and continue as a Windows
+ * server can put in a
+ * structure with an invalid
+ * length.
+ *
+ * We can safely fill in an
+ * extra empty property here
+ * because
+ * dns_zoneinfo_load_zone_property()
+ * just ignores
+ * DSPROPERTY_ZONE_EMPTY
+ */
+ ZERO_STRUCT(props[j]);
+ props[j].id = DSPROPERTY_ZONE_EMPTY;
+ continue;
+ }
+ }
+ z->tmp_props = props;
+ z->num_props = element->num_values;
+ }
+ }
+ return zones;
+
+failed:
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+
+/* Find DNS partition information */
+struct dnsserver_partition_info *dnsserver_db_partition_info(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_partition *p)
+{
+ const char * const attrs[] = { "instanceType", "msDs-masteredBy", NULL };
+ const char * const attrs_none[] = { NULL };
+ struct ldb_result *res;
+ struct ldb_message_element *el;
+ struct ldb_dn *dn;
+ struct dnsserver_partition_info *partinfo;
+ int i, ret, instance_type;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ partinfo = talloc_zero(mem_ctx, struct dnsserver_partition_info);
+ if (partinfo == NULL) {
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ /* Search for the active replica and state */
+ ret = ldb_search(samdb, tmp_ctx, &res, p->partition_dn, LDB_SCOPE_BASE,
+ attrs, NULL);
+ if (ret != LDB_SUCCESS || res->count != 1) {
+ goto failed;
+ }
+
+ /* Set the state of the partition */
+ instance_type = ldb_msg_find_attr_as_int(res->msgs[0], "instanceType", -1);
+ if (instance_type == -1) {
+ partinfo->dwState = DNS_DP_STATE_UNKNOWN;
+ } else if (instance_type & INSTANCE_TYPE_NC_COMING) {
+ partinfo->dwState = DNS_DP_STATE_REPL_INCOMING;
+ } else if (instance_type & INSTANCE_TYPE_NC_GOING) {
+ partinfo->dwState = DNS_DP_STATE_REPL_OUTGOING;
+ } else {
+ partinfo->dwState = DNS_DP_OKAY;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "msDs-masteredBy");
+ if (el == NULL) {
+ partinfo->dwReplicaCount = 0;
+ partinfo->ReplicaArray = NULL;
+ } else {
+ partinfo->dwReplicaCount = el->num_values;
+ partinfo->ReplicaArray = talloc_zero_array(partinfo,
+ struct DNS_RPC_DP_REPLICA *,
+ el->num_values);
+ if (partinfo->ReplicaArray == NULL) {
+ goto failed;
+ }
+ for (i=0; i<el->num_values; i++) {
+ partinfo->ReplicaArray[i] = talloc_zero(partinfo,
+ struct DNS_RPC_DP_REPLICA);
+ if (partinfo->ReplicaArray[i] == NULL) {
+ goto failed;
+ }
+ partinfo->ReplicaArray[i]->pszReplicaDn = talloc_strdup(
+ partinfo,
+ (const char *)el->values[i].data);
+ if (partinfo->ReplicaArray[i]->pszReplicaDn == NULL) {
+ goto failed;
+ }
+ }
+ }
+ talloc_free(res);
+
+ /* Search for cross-reference object */
+ dn = ldb_dn_copy(tmp_ctx, ldb_get_config_basedn(samdb));
+ if (dn == NULL) {
+ goto failed;
+ }
+
+ ret = ldb_search(samdb, tmp_ctx, &res, dn, LDB_SCOPE_DEFAULT, attrs_none,
+ "(nCName=%s)", ldb_dn_get_linearized(p->partition_dn));
+ if (ret != LDB_SUCCESS || res->count != 1) {
+ goto failed;
+ }
+ partinfo->pszCrDn = talloc_strdup(partinfo, ldb_dn_get_linearized(res->msgs[0]->dn));
+ if (partinfo->pszCrDn == NULL) {
+ goto failed;
+ }
+ talloc_free(res);
+
+ talloc_free(tmp_ctx);
+ return partinfo;
+
+failed:
+ talloc_free(tmp_ctx);
+ talloc_free(partinfo);
+ return NULL;
+}
+
+
+/* Increment serial number and update timestamp */
+static unsigned int dnsserver_update_soa(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ WERROR *werr)
+{
+ const char * const attrs[] = { "dnsRecord", NULL };
+ struct ldb_result *res;
+ struct dnsp_DnssrvRpcRecord rec;
+ struct ldb_message_element *el;
+ enum ndr_err_code ndr_err;
+ int ret, i, serial = -1;
+
+ *werr = WERR_INTERNAL_DB_ERROR;
+
+ ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(name=@))");
+ if (ret != LDB_SUCCESS || res->count == 0) {
+ return -1;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
+ if (el == NULL) {
+ return -1;
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ continue;
+ }
+
+ if (rec.wType == DNS_TYPE_SOA) {
+ serial = rec.data.soa.serial + 1;
+ rec.dwSerial = serial;
+ rec.dwTimeStamp = 0;
+ rec.data.soa.serial = serial;
+
+ ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, &rec,
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ *werr = WERR_NOT_ENOUGH_MEMORY;
+ return -1;
+ }
+ break;
+ }
+ }
+
+ if (serial != -1) {
+ el->flags = LDB_FLAG_MOD_REPLACE;
+ ret = ldb_modify(samdb, res->msgs[0]);
+ if (ret != LDB_SUCCESS) {
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ *werr = WERR_ACCESS_DENIED;
+ }
+ return -1;
+ }
+ }
+
+ *werr = WERR_OK;
+
+ return serial;
+}
+
+
+/* Add DNS record to the database */
+static WERROR dnsserver_db_do_add_rec(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct ldb_dn *dn,
+ int num_rec,
+ struct dnsp_DnssrvRpcRecord *rec)
+{
+ struct ldb_message *msg;
+ struct ldb_val v;
+ int ret;
+ enum ndr_err_code ndr_err;
+ int i;
+
+ msg = ldb_msg_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+
+ msg->dn = dn;
+ ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (num_rec > 0 && rec) {
+ for (i=0; i<num_rec; i++) {
+ ndr_err = ndr_push_struct_blob(&v, mem_ctx, &rec[i],
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+ }
+
+ ret = ldb_add(samdb, msg);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+
+/* Add dnsNode record to the database with DNS record */
+WERROR dnsserver_db_add_empty_node(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ const char *name)
+{
+ const char * const attrs[] = { "name", NULL };
+ struct ldb_result *res;
+ struct ldb_dn *dn;
+ char *encoded_name = ldb_binary_encode_string(mem_ctx, name);
+ struct ldb_val name_val = data_blob_string_const(name);
+ int ret;
+
+ ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_BASE, attrs,
+ "(&(objectClass=dnsNode)(name=%s))",
+ encoded_name);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ if (res->count > 0) {
+ talloc_free(res);
+ return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
+ }
+
+ dn = ldb_dn_copy(mem_ctx, z->zone_dn);
+ W_ERROR_HAVE_NO_MEMORY(dn);
+
+ if (!ldb_dn_add_child_val(dn, "DC", name_val)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, 0, NULL);
+}
+
+static void set_record_rank(struct dnsserver_zone *z,
+ const char *name,
+ struct dnsp_DnssrvRpcRecord *rec)
+{
+ if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_PRIMARY) {
+ if (strcmp(name, "@") != 0 && rec->wType == DNS_TYPE_NS) {
+ rec->rank = DNS_RANK_NS_GLUE;
+ } else {
+ rec->rank = DNS_RANK_ZONE;
+ }
+ } else if (strcmp(z->name, ".") == 0) {
+ rec->rank = DNS_RANK_ROOT_HINT;
+ }
+}
+
+
+/* Add a DNS record */
+WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ const char *name,
+ struct DNS_RPC_RECORD *add_record)
+{
+ const char * const attrs[] = { "dnsRecord", "dNSTombstoned", NULL };
+ struct ldb_result *res;
+ struct dnsp_DnssrvRpcRecord *rec = NULL;
+ struct ldb_message_element *el;
+ struct ldb_dn *dn;
+ enum ndr_err_code ndr_err;
+ int ret, i;
+ int serial;
+ WERROR werr;
+ bool was_tombstoned = false;
+ char *encoded_name = ldb_binary_encode_string(mem_ctx, name);
+
+ werr = dns_to_dnsp_convert(mem_ctx, add_record, &rec, true);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ /* Set the correct rank for the record. */
+ set_record_rank(z, name, rec);
+
+ serial = dnsserver_update_soa(mem_ctx, samdb, z, &werr);
+ if (serial < 0) {
+ return werr;
+ }
+
+ rec->dwSerial = serial;
+ rec->dwTimeStamp = 0;
+
+ ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(name=%s))",
+ encoded_name);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ if (res->count == 0) {
+ dn = dnsserver_name_to_dn(mem_ctx, z, name);
+ W_ERROR_HAVE_NO_MEMORY(dn);
+
+ return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, 1, rec);
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
+ if (el == NULL) {
+ ret = ldb_msg_add_empty(res->msgs[0], "dnsRecord", 0, &el);
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ was_tombstoned = ldb_msg_find_attr_as_bool(res->msgs[0],
+ "dNSTombstoned", false);
+ if (was_tombstoned) {
+ el->num_values = 0;
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ struct dnsp_DnssrvRpcRecord rec2;
+
+ ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ if (dns_record_match(rec, &rec2)) {
+ break;
+ }
+ }
+ if (i < el->num_values) {
+ return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
+ }
+ if (i == el->num_values) {
+ /* adding a new value */
+ el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
+ W_ERROR_HAVE_NO_MEMORY(el->values);
+ el->num_values++;
+ }
+
+ ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, rec,
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ el = ldb_msg_find_element(res->msgs[0], "dNSTombstoned");
+ if (el != NULL) {
+ el->flags = LDB_FLAG_MOD_DELETE;
+ }
+
+ ret = ldb_modify(samdb, res->msgs[0]);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+
+/* Update a DNS record */
+WERROR dnsserver_db_update_record(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ const char *name,
+ struct DNS_RPC_RECORD *add_record,
+ struct DNS_RPC_RECORD *del_record)
+{
+ const char * const attrs[] = { "dnsRecord", NULL };
+ struct ldb_result *res;
+ struct dnsp_DnssrvRpcRecord rec2;
+ struct dnsp_DnssrvRpcRecord *arec = NULL, *drec = NULL;
+ struct ldb_message_element *el;
+ enum ndr_err_code ndr_err;
+ int ret, i;
+ int serial;
+ WERROR werr;
+ bool updating_ttl = false;
+ char *encoded_name = ldb_binary_encode_string(mem_ctx, name);
+
+ werr = dns_to_dnsp_convert(mem_ctx, add_record, &arec, true);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ werr = dns_to_dnsp_convert(mem_ctx, del_record, &drec, true);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(name=%s)(!(dNSTombstoned=TRUE)))",
+ encoded_name);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ if (res->count == 0) {
+ return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
+ if (el == NULL || el->num_values == 0) {
+ return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ if (dns_record_match(arec, &rec2)) {
+ break;
+ }
+ }
+ if (i < el->num_values) {
+ /*
+ * The record already exists, which is an error UNLESS we are
+ * doing an in-place update.
+ *
+ * Therefore we need to see if drec also matches, in which
+ * case it's OK, though we can only update dwTtlSeconds and
+ * reset the timestamp to zero.
+ */
+ updating_ttl = dns_record_match(drec, &rec2);
+ if (! updating_ttl) {
+ return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
+ }
+ /* In this case the next loop is redundant */
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ if (dns_record_match(drec, &rec2)) {
+ /*
+ * we are replacing this one with arec, which is done
+ * by pushing arec into el->values[i] below, after the
+ * various manipulations.
+ */
+ break;
+ }
+ }
+ if (i == el->num_values) {
+ return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
+ }
+
+ /*
+ * If we're updating a SOA record, use the specified serial.
+ *
+ * Otherwise, if we are updating ttl in place (i.e., not changing
+ * .wType and .data on a record), we should increment the existing
+ * serial, and save to the SOA.
+ *
+ * Outside of those two cases, we look for the zone's SOA record and
+ * use its serial.
+ */
+ if (arec->wType != DNS_TYPE_SOA) {
+ if (updating_ttl) {
+ /*
+ * In this case, we keep some of the old values.
+ */
+ arec->dwSerial = rec2.dwSerial;
+ arec->dwReserved = rec2.dwReserved;
+ /*
+ * TODO: if the old TTL and the new TTL are
+ * different, the serial number is incremented.
+ */
+ } else {
+ arec->dwReserved = 0;
+ serial = dnsserver_update_soa(mem_ctx, samdb, z, &werr);
+ if (serial < 0) {
+ return werr;
+ }
+ arec->dwSerial = serial;
+ }
+ }
+
+ /* Set the correct rank for the record. */
+ set_record_rank(z, name, arec);
+ /*
+ * Successful RPC updates *always* zero timestamp and flags and set
+ * version.
+ */
+ arec->dwTimeStamp = 0;
+ arec->version = 5;
+ arec->flags = 0;
+
+ ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, arec,
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ el->flags = LDB_FLAG_MOD_REPLACE;
+ ret = ldb_modify(samdb, res->msgs[0]);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+
+/* Delete a DNS record */
+WERROR dnsserver_db_delete_record(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ const char *name,
+ struct DNS_RPC_RECORD *del_record)
+{
+ const char * const attrs[] = { "dnsRecord", NULL };
+ struct ldb_result *res;
+ struct dnsp_DnssrvRpcRecord *rec = NULL;
+ struct ldb_message_element *el;
+ enum ndr_err_code ndr_err;
+ int ret, i;
+ int serial;
+ WERROR werr;
+
+ serial = dnsserver_update_soa(mem_ctx, samdb, z, &werr);
+ if (serial < 0) {
+ return werr;
+ }
+
+ werr = dns_to_dnsp_convert(mem_ctx, del_record, &rec, false);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(name=%s))",
+ ldb_binary_encode_string(mem_ctx, name));
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ if (res->count == 0) {
+ return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
+ }
+ if (res->count > 1) {
+ return WERR_DNS_ERROR_RCODE_SERVER_FAILURE;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
+ if (el == NULL || el->num_values == 0) {
+ return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ struct dnsp_DnssrvRpcRecord rec2;
+
+ ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ if (dns_record_match(rec, &rec2)) {
+ break;
+ }
+ }
+ if (i == el->num_values) {
+ return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
+ }
+ if (i < el->num_values-1) {
+ memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
+ }
+ el->num_values--;
+
+ if (el->num_values == 0) {
+ ret = ldb_delete(samdb, res->msgs[0]->dn);
+ } else {
+ el->flags = LDB_FLAG_MOD_REPLACE;
+ ret = ldb_modify(samdb, res->msgs[0]);
+ }
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+
+static bool dnsserver_db_msg_add_dnsproperty(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct dnsp_DnsProperty *prop)
+{
+ DATA_BLOB *prop_blob;
+ enum ndr_err_code ndr_err;
+ int ret;
+
+ prop_blob = talloc_zero(mem_ctx, DATA_BLOB);
+ if (prop_blob == NULL) return false;
+
+ ndr_err = ndr_push_struct_blob(prop_blob, mem_ctx, prop,
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnsProperty);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+ ret = ldb_msg_add_steal_value(msg, "dNSProperty", prop_blob);
+ if (ret != LDB_SUCCESS) {
+ return false;
+ }
+ return true;
+}
+
+WERROR dnsserver_db_do_reset_dword(struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ struct DNS_RPC_NAME_AND_PARAM *n_p)
+{
+ struct ldb_message_element *element = NULL;
+ struct dnsp_DnsProperty *prop = NULL;
+ enum ndr_err_code err;
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char * const attrs[] = {"dNSProperty", NULL};
+ struct ldb_result *res = NULL;
+ int i, ret, prop_id;
+
+ if (strcasecmp(n_p->pszNodeName, "Aging") == 0) {
+ z->zoneinfo->fAging = n_p->dwParam;
+ prop_id = DSPROPERTY_ZONE_AGING_STATE;
+ } else if (strcasecmp(n_p->pszNodeName, "RefreshInterval") == 0) {
+ z->zoneinfo->dwRefreshInterval = n_p->dwParam;
+ prop_id = DSPROPERTY_ZONE_REFRESH_INTERVAL;
+ } else if (strcasecmp(n_p->pszNodeName, "NoRefreshInterval") == 0) {
+ z->zoneinfo->dwNoRefreshInterval = n_p->dwParam;
+ prop_id = DSPROPERTY_ZONE_NOREFRESH_INTERVAL;
+ } else if (strcasecmp(n_p->pszNodeName, "AllowUpdate") == 0) {
+ z->zoneinfo->fAllowUpdate = n_p->dwParam;
+ prop_id = DSPROPERTY_ZONE_ALLOW_UPDATE;
+ } else {
+ return WERR_UNKNOWN_PROPERTY;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ret = ldb_search(samdb, tmp_ctx, &res, z->zone_dn, LDB_SCOPE_BASE,
+ attrs, "(objectClass=dnsZone)");
+ if (ret != LDB_SUCCESS) {
+ DBG_ERR("dnsserver: no zone: %s\n",
+ ldb_dn_get_linearized(z->zone_dn));
+ TALLOC_FREE(tmp_ctx);
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ if (res->count != 1) {
+ DBG_ERR("dnsserver: duplicate zone: %s\n",
+ ldb_dn_get_linearized(z->zone_dn));
+ TALLOC_FREE(tmp_ctx);
+ return WERR_GEN_FAILURE;
+ }
+
+ element = ldb_msg_find_element(res->msgs[0], "dNSProperty");
+ if (element == NULL) {
+ DBG_ERR("dnsserver: zone %s has no properties.\n",
+ ldb_dn_get_linearized(z->zone_dn));
+ TALLOC_FREE(tmp_ctx);
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ for (i = 0; i < element->num_values; i++) {
+ prop = talloc_zero(element, struct dnsp_DnsProperty);
+ if (prop == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ err = ndr_pull_struct_blob(
+ &(element->values[i]),
+ tmp_ctx,
+ prop,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnsProperty);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)){
+ /*
+ * If we can't pull it then try again parsing
+ * it again. A Windows server in the domain
+ * will permit the addition of an invalidly
+ * formed property with a 0 length and cause a
+ * failure here
+ */
+ struct dnsp_DnsProperty_short
+ *short_property
+ = talloc_zero(element,
+ struct dnsp_DnsProperty_short);
+ if (short_property == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ err = ndr_pull_struct_blob_all(
+ &(element->values[i]),
+ tmp_ctx,
+ short_property,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnsProperty_short);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ /*
+ * Unknown invalid data should be
+ * ignored and logged to match Windows
+ * behaviour
+ */
+ DBG_NOTICE("dnsserver: couldn't PULL "
+ "dnsProperty value#%d in "
+ "zone %s while trying to "
+ "reset id %d\n",
+ i,
+ ldb_dn_get_linearized(z->zone_dn),
+ prop_id);
+ continue;
+ }
+
+ /*
+ * Initialise the parts of the property not
+ * overwritten by value() in the IDL for
+ * re-push
+ */
+ *prop = (struct dnsp_DnsProperty){
+ .namelength = short_property->namelength,
+ .id = short_property->id,
+ .name = short_property->name
+ /* .data will be filled in below */
+ };
+ }
+
+ if (prop->id == prop_id) {
+ switch (prop_id) {
+ case DSPROPERTY_ZONE_AGING_STATE:
+ prop->data.aging_enabled = n_p->dwParam;
+ break;
+ case DSPROPERTY_ZONE_NOREFRESH_INTERVAL:
+ prop->data.norefresh_hours = n_p->dwParam;
+ break;
+ case DSPROPERTY_ZONE_REFRESH_INTERVAL:
+ prop->data.refresh_hours = n_p->dwParam;
+ break;
+ case DSPROPERTY_ZONE_ALLOW_UPDATE:
+ prop->data.allow_update_flag = n_p->dwParam;
+ break;
+ }
+
+ err = ndr_push_struct_blob(
+ &(element->values[i]),
+ tmp_ctx,
+ prop,
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnsProperty);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)){
+ DBG_ERR("dnsserver: couldn't PUSH dns prop id "
+ "%d in zone %s\n",
+ prop->id,
+ ldb_dn_get_linearized(z->zone_dn));
+ TALLOC_FREE(tmp_ctx);
+ return WERR_INTERNAL_DB_ERROR;
+ }
+ }
+ }
+
+ element->flags = LDB_FLAG_MOD_REPLACE;
+ ret = ldb_modify(samdb, res->msgs[0]);
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(tmp_ctx);
+ DBG_ERR("dnsserver: Failed to modify zone %s prop %s: %s\n",
+ z->name,
+ n_p->pszNodeName,
+ ldb_errstring(samdb));
+ return WERR_INTERNAL_DB_ERROR;
+ }
+ TALLOC_FREE(tmp_ctx);
+
+ return WERR_OK;
+}
+
+/* Create dnsZone record to database and set security descriptor */
+static WERROR dnsserver_db_do_create_zone(TALLOC_CTX *tmp_ctx,
+ struct ldb_context *samdb,
+ struct ldb_dn *zone_dn,
+ struct dnsserver_zone *z)
+{
+ const char * const attrs[] = { "objectSID", NULL };
+ struct ldb_message *msg;
+ struct ldb_result *res;
+ struct ldb_message_element *el;
+ const char sddl_template[] = "D:AI(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;CC;;;AU)(A;;RPLCLORC;;;WD)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;%s)(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)(OA;CIID;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)(A;CIID;LC;;;RU)(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;BA)S:AI";
+ char *sddl;
+ struct dom_sid dnsadmins_sid;
+ const struct dom_sid *domain_sid;
+ struct security_descriptor *secdesc;
+ struct dnsp_DnsProperty *prop;
+ DATA_BLOB *sd_encoded;
+ enum ndr_err_code ndr_err;
+ int ret;
+
+ /* Get DnsAdmins SID */
+ ret = ldb_search(samdb, tmp_ctx, &res, ldb_get_default_basedn(samdb),
+ LDB_SCOPE_DEFAULT, attrs, "(sAMAccountName=DnsAdmins)");
+ if (ret != LDB_SUCCESS || res->count != 1) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "objectSID");
+ if (el == NULL || el->num_values != 1) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&el->values[0], tmp_ctx, &dnsadmins_sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ /* create security descriptor with DnsAdmins GUID in sddl template */
+ sddl = talloc_asprintf(tmp_ctx, sddl_template,
+ dom_sid_string(tmp_ctx, &dnsadmins_sid));
+ if (sddl == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ talloc_free(res);
+
+ domain_sid = samdb_domain_sid(samdb);
+ if (domain_sid == NULL) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ secdesc = sddl_decode(tmp_ctx, sddl, domain_sid);
+ if (secdesc == NULL) {
+ return WERR_GEN_FAILURE;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+
+ msg->dn = zone_dn;
+ ret = ldb_msg_add_string(msg, "objectClass", "dnsZone");
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ sd_encoded = talloc_zero(tmp_ctx, DATA_BLOB);
+ W_ERROR_HAVE_NO_MEMORY(sd_encoded);
+
+ ndr_err = ndr_push_struct_blob(sd_encoded, tmp_ctx, secdesc,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ ret = ldb_msg_add_steal_value(msg, "nTSecurityDescriptor", sd_encoded);
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* dns zone Properties */
+ prop = talloc_zero(tmp_ctx, struct dnsp_DnsProperty);
+ W_ERROR_HAVE_NO_MEMORY(prop);
+
+ prop->version = 1;
+
+ /* zone type */
+ prop->id = DSPROPERTY_ZONE_TYPE;
+ prop->data.zone_type = z->zoneinfo->dwZoneType;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* allow update */
+ prop->id = DSPROPERTY_ZONE_ALLOW_UPDATE;
+ prop->data.allow_update_flag = z->zoneinfo->fAllowUpdate;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* secure time */
+ prop->id = DSPROPERTY_ZONE_SECURE_TIME;
+ prop->data.zone_secure_time = 0;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* norefresh interval */
+ prop->id = DSPROPERTY_ZONE_NOREFRESH_INTERVAL;
+ prop->data.norefresh_hours = 168;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* refresh interval */
+ prop->id = DSPROPERTY_ZONE_REFRESH_INTERVAL;
+ prop->data.refresh_hours = 168;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* aging state */
+ prop->id = DSPROPERTY_ZONE_AGING_STATE;
+ prop->data.aging_enabled = z->zoneinfo->fAging;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* aging enabled time */
+ prop->id = DSPROPERTY_ZONE_AGING_ENABLED_TIME;
+ prop->data.next_scavenging_cycle_hours = 0;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ talloc_free(prop);
+
+ ret = ldb_add(samdb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("dnsserver: Failed to create zone (%s): %s\n",
+ z->name, ldb_errstring(samdb)));
+
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+
+/* Create new dnsZone record and @ record (SOA + NS) */
+WERROR dnsserver_db_create_zone(struct ldb_context *samdb,
+ struct dnsserver_partition *partitions,
+ struct dnsserver_zone *zone,
+ struct loadparm_context *lp_ctx)
+{
+ struct dnsserver_partition *p;
+ bool in_forest = false;
+ WERROR status;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx;
+ struct dnsp_DnssrvRpcRecord *dns_rec;
+ struct dnsp_soa soa;
+ char *tmpstr, *server_fqdn, *soa_email;
+ struct ldb_val name_val = data_blob_string_const(zone->name);
+
+ /* We only support primary zones for now */
+ if (zone->zoneinfo->dwZoneType != DNS_ZONE_TYPE_PRIMARY) {
+ return WERR_CALL_NOT_IMPLEMENTED;
+ }
+
+ /* Get the correct partition */
+ if (zone->partition->dwDpFlags & DNS_DP_FOREST_DEFAULT) {
+ in_forest = true;
+ }
+ for (p = partitions; p; p = p->next) {
+ if (in_forest == p->is_forest) {
+ break;
+ }
+ }
+ if (p == NULL) {
+ return WERR_DNS_ERROR_DP_DOES_NOT_EXIST;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ dn = ldb_dn_copy(tmp_ctx, p->partition_dn);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(dn, tmp_ctx);
+
+ if (!ldb_dn_add_child_fmt(dn, "CN=MicrosoftDNS")) {
+ talloc_free(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (!ldb_dn_add_child_val(dn, "DC", name_val)) {
+ talloc_free(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* Add dnsZone record */
+ status = dnsserver_db_do_create_zone(tmp_ctx, samdb, dn, zone);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (!ldb_dn_add_child_fmt(dn, "DC=@")) {
+ talloc_free(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ dns_rec = talloc_zero_array(tmp_ctx, struct dnsp_DnssrvRpcRecord, 2);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(dns_rec, tmp_ctx);
+
+ tmpstr = talloc_asprintf(tmp_ctx, "%s.%s",
+ lpcfg_netbios_name(lp_ctx),
+ lpcfg_realm(lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(tmpstr, tmp_ctx);
+ server_fqdn = strlower_talloc(tmp_ctx, tmpstr);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(server_fqdn, tmp_ctx);
+ talloc_free(tmpstr);
+
+ tmpstr = talloc_asprintf(tmp_ctx, "hostmaster.%s",
+ lpcfg_realm(lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(tmpstr, tmp_ctx);
+ soa_email = strlower_talloc(tmp_ctx, tmpstr);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(soa_email, tmp_ctx);
+ talloc_free(tmpstr);
+
+ /* SOA Record - values same as defined in provision/sambadns.py */
+ soa.serial = 1;
+ soa.refresh = 900;
+ soa.retry = 600;
+ soa.expire = 86400;
+ soa.minimum = 3600;
+ soa.mname = server_fqdn;
+ soa.rname = soa_email;
+
+ dns_rec[0].wType = DNS_TYPE_SOA;
+ dns_rec[0].rank = DNS_RANK_ZONE;
+ dns_rec[0].dwSerial = soa.serial;
+ dns_rec[0].dwTtlSeconds = 3600;
+ dns_rec[0].dwTimeStamp = 0;
+ dns_rec[0].data.soa = soa;
+
+ /* NS Record */
+ dns_rec[1].wType = DNS_TYPE_NS;
+ dns_rec[1].rank = DNS_RANK_ZONE;
+ dns_rec[1].dwSerial = soa.serial;
+ dns_rec[1].dwTtlSeconds = 3600;
+ dns_rec[1].dwTimeStamp = 0;
+ dns_rec[1].data.ns = server_fqdn;
+
+ /* Add @ Record */
+ status = dnsserver_db_do_add_rec(tmp_ctx, samdb, dn, 2, dns_rec);
+
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+
+/* Delete dnsZone record and all DNS records in the zone */
+WERROR dnsserver_db_delete_zone(struct ldb_context *samdb,
+ struct dnsserver_zone *zone)
+{
+ int ret;
+
+ ret = ldb_transaction_start(samdb);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ ret = dsdb_delete(samdb, zone->zone_dn, DSDB_TREE_DELETE);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(samdb);
+
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ return WERR_ACCESS_DENIED;
+ }
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ ret = ldb_transaction_commit(samdb);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ return WERR_OK;
+}
diff --git a/source4/rpc_server/dnsserver/dnsserver.h b/source4/rpc_server/dnsserver/dnsserver.h
new file mode 100644
index 0000000..2e46e7c
--- /dev/null
+++ b/source4/rpc_server/dnsserver/dnsserver.h
@@ -0,0 +1,264 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DNS Server
+
+ Copyright (C) Amitay Isaacs 2011
+
+ 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 __DNSSERVER_H__
+#define __DNSSERVER_H__
+
+#include "librpc/gen_ndr/dnsp.h"
+#include "librpc/gen_ndr/dnsserver.h"
+#include "param/param.h"
+#include "ldb.h"
+
+struct dnsserver_serverinfo {
+ uint32_t dwVersion;
+ uint8_t fBootMethod;
+ uint8_t fAdminConfigured;
+ uint8_t fAllowUpdate;
+ uint8_t fDsAvailable;
+
+ char * pszServerName;
+ char * pszDsContainer;
+
+ uint32_t dwDsForestVersion;
+ uint32_t dwDsDomainVersion;
+ uint32_t dwDsDsaVersion;
+ uint32_t fReadOnlyDC;
+ char * pszDomainName;
+ char * pszForestName;
+ char * pszDomainDirectoryPartition;
+ char * pszForestDirectoryPartition;
+
+ struct DNS_ADDR_ARRAY * aipServerAddrs;
+ struct DNS_ADDR_ARRAY * aipListenAddrs;
+ struct IP4_ARRAY * aipForwarders;
+
+ struct IP4_ARRAY * aipLogFilter;
+ char * pwszLogFilePath;
+
+ uint32_t dwLogLevel;
+ uint32_t dwDebugLevel;
+ uint32_t dwEventLogLevel;
+ uint32_t dwLogFileMaxSize;
+
+ uint32_t dwForwardTimeout;
+ uint32_t dwRpcProtocol;
+ uint32_t dwNameCheckFlag;
+ uint32_t cAddressAnswerLimit;
+ uint32_t dwRecursionRetry;
+ uint32_t dwRecursionTimeout;
+ uint32_t dwMaxCacheTtl;
+ uint32_t dwDsPollingInterval;
+ uint32_t dwLocalNetPriorityNetMask;
+
+ uint32_t dwScavengingInterval;
+ uint32_t dwDefaultRefreshInterval;
+ uint32_t dwDefaultNoRefreshInterval;
+ uint32_t dwLastScavengeTime;
+
+ uint8_t fAutoReverseZones;
+ uint8_t fAutoCacheUpdate;
+
+ uint8_t fRecurseAfterForwarding;
+ uint8_t fForwardDelegations;
+ uint8_t fNoRecursion;
+ uint8_t fSecureResponses;
+
+ uint8_t fRoundRobin;
+ uint8_t fLocalNetPriority;
+
+ uint8_t fBindSecondaries;
+ uint8_t fWriteAuthorityNs;
+
+ uint8_t fStrictFileParsing;
+ uint8_t fLooseWildcarding;
+ uint8_t fDefaultAgingState;
+};
+
+struct dnsserver_zoneinfo {
+ uint8_t Version;
+ uint32_t Flags;
+ uint8_t dwZoneType;
+ uint8_t fReverse;
+ uint8_t fAllowUpdate;
+ uint8_t fPaused;
+ uint8_t fShutdown;
+ uint8_t fAutoCreated;
+
+ uint8_t fUseDatabase;
+ char * pszDataFile;
+
+ struct IP4_ARRAY * aipMasters;
+
+ uint32_t fSecureSecondaries;
+ uint32_t fNotifyLevel;
+ struct IP4_ARRAY * aipSecondaries;
+ struct IP4_ARRAY * aipNotify;
+
+ uint32_t fUseWins;
+ uint32_t fUseNbstat;
+
+ uint32_t fAging;
+ uint32_t dwNoRefreshInterval;
+ uint32_t dwRefreshInterval;
+ uint32_t dwAvailForScavengeTime;
+ struct IP4_ARRAY * aipScavengeServers;
+
+ uint32_t dwForwarderTimeout;
+ uint32_t fForwarderSlave;
+
+ struct IP4_ARRAY * aipLocalMasters;
+
+ char * pwszZoneDn;
+
+ uint32_t dwLastSuccessfulSoaCheck;
+ uint32_t dwLastSuccessfulXfr;
+
+ uint32_t fQueuedForBackgroundLoad;
+ uint32_t fBackgroundLoadInProgress;
+ uint8_t fReadOnlyZone;
+
+ uint32_t dwLastXfrAttempt;
+ uint32_t dwLastXfrResult;
+};
+
+
+struct dnsserver_partition {
+ struct dnsserver_partition *prev, *next;
+ struct ldb_dn *partition_dn;
+ const char *pszDpFqdn;
+ uint32_t dwDpFlags;
+ bool is_forest;
+ int zones_count;
+};
+
+
+struct dnsserver_partition_info {
+ const char *pszCrDn;
+ uint32_t dwState;
+ uint32_t dwReplicaCount;
+ struct DNS_RPC_DP_REPLICA **ReplicaArray;
+};
+
+
+struct dnsserver_zone {
+ struct dnsserver_zone *prev, *next;
+ struct dnsserver_partition *partition;
+ const char *name;
+ struct ldb_dn *zone_dn;
+ struct dnsserver_zoneinfo *zoneinfo;
+ struct dnsp_DnsProperty *tmp_props;
+ int32_t num_props;
+};
+
+
+struct dns_tree {
+ const char *name;
+ int level;
+ unsigned int num_children;
+ struct dns_tree **children;
+ void *data;
+};
+
+/* Data structure manipulation functions from dnsdata.c */
+
+struct IP4_ARRAY *ip4_array_copy(TALLOC_CTX *mem_ctx, struct IP4_ARRAY *ip4);
+struct DNS_ADDR_ARRAY *ip4_array_to_dns_addr_array(TALLOC_CTX *mem_ctx, struct IP4_ARRAY *ip4);
+struct IP4_ARRAY *dns_addr_array_to_ip4_array(TALLOC_CTX *mem_ctx,
+ struct DNS_ADDR_ARRAY *ip);
+struct DNS_ADDR_ARRAY *dns_addr_array_copy(TALLOC_CTX *mem_ctx, struct DNS_ADDR_ARRAY *addr);
+
+int dns_split_name_components(TALLOC_CTX *mem_ctx, const char *name, char ***components);
+char *dns_split_node_name(TALLOC_CTX *mem_ctx, const char *node_name, const char *zone_name);
+
+int dns_name_compare(struct ldb_message * const *m1, struct ldb_message * const *m2,
+ const char *search_name);
+bool dns_record_match(struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2);
+
+void dnsp_to_dns_copy(TALLOC_CTX *mem_ctx, struct dnsp_DnssrvRpcRecord *dnsp,
+ struct DNS_RPC_RECORD *dns);
+WERROR dns_to_dnsp_convert(TALLOC_CTX *mem_ctx, struct DNS_RPC_RECORD *dns,
+ struct dnsp_DnssrvRpcRecord **out_dnsp,
+ bool check_name);
+
+struct dns_tree *dns_build_tree(TALLOC_CTX *mem_ctx, const char *name, struct ldb_result *res);
+WERROR dns_fill_records_array(TALLOC_CTX *mem_ctx, struct dnsserver_zone *z,
+ enum dns_record_type record_type,
+ unsigned int select_flag, const char *zone_name,
+ struct ldb_message *msg, int num_children,
+ struct DNS_RPC_RECORDS_ARRAY *recs,
+ char ***add_names, int *add_count);
+
+
+/* Utility functions from dnsutils.c */
+
+struct dnsserver_serverinfo *dnsserver_init_serverinfo(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct ldb_context *samdb);
+struct dnsserver_zoneinfo *dnsserver_init_zoneinfo(struct dnsserver_zone *zone,
+ struct dnsserver_serverinfo *serverinfo);
+struct dnsserver_zone *dnsserver_find_zone(struct dnsserver_zone *zones,
+ const char *zone_name);
+struct ldb_dn *dnsserver_name_to_dn(TALLOC_CTX *mem_ctx, struct dnsserver_zone *z,
+ const char *name);
+uint32_t dnsserver_zone_to_request_filter(const char *zone);
+
+/* Database functions from dnsdb.c */
+
+struct dnsserver_partition *dnsserver_db_enumerate_partitions(TALLOC_CTX *mem_ctx,
+ struct dnsserver_serverinfo *serverinfo,
+ struct ldb_context *samdb);
+struct dnsserver_zone *dnsserver_db_enumerate_zones(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_partition *p);
+struct dnsserver_partition_info *dnsserver_db_partition_info(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_partition *p);
+WERROR dnsserver_db_add_empty_node(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ const char *node_name);
+WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ const char *node_name,
+ struct DNS_RPC_RECORD *add_record);
+WERROR dnsserver_db_update_record(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ const char *node_name,
+ struct DNS_RPC_RECORD *add_record,
+ struct DNS_RPC_RECORD *del_record);
+WERROR dnsserver_db_do_reset_dword(struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ struct DNS_RPC_NAME_AND_PARAM *n_p);
+WERROR dnsserver_db_delete_record(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ const char *node_name,
+ struct DNS_RPC_RECORD *del_record);
+WERROR dnsserver_db_create_zone(struct ldb_context *samdb,
+ struct dnsserver_partition *partitions,
+ struct dnsserver_zone *z,
+ struct loadparm_context *lp_ctx);
+WERROR dnsserver_db_delete_zone(struct ldb_context *samdb,
+ struct dnsserver_zone *z);
+
+#endif /* __DNSSERVER_H__ */
diff --git a/source4/rpc_server/dnsserver/dnsutils.c b/source4/rpc_server/dnsserver/dnsutils.c
new file mode 100644
index 0000000..2c56946
--- /dev/null
+++ b/source4/rpc_server/dnsserver/dnsutils.c
@@ -0,0 +1,414 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DNS Server
+
+ Copyright (C) Amitay Isaacs 2011
+
+ 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 "includes.h"
+#include "dnsserver.h"
+#include "rpc_server/common/common.h"
+#include "dns_server/dnsserver_common.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/socket/netif.h"
+#include "lib/util/util_net.h"
+#include "dnsserver_common.h"
+
+#undef strcasecmp
+
+static struct DNS_ADDR_ARRAY *fill_dns_addr_array(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ bool listen_only)
+{
+ struct interface *ifaces;
+ int num_interfaces, i;
+ struct DNS_ADDR_ARRAY *dns_addr_array;
+ const char *ipstr;
+ bool have_ipv4, have_ipv6;
+ uint16_t family;
+
+ have_ipv4 = have_ipv6 = false;
+
+ if (!listen_only) {
+ /*
+ Return all interfaces from kernel
+ Not implemented!
+ */
+ return NULL;
+ }
+
+ /* Only the used interfaces */
+ load_interface_list(mem_ctx, lp_ctx, &ifaces);
+ num_interfaces = iface_list_count(ifaces);
+
+ dns_addr_array = talloc_zero(mem_ctx, struct DNS_ADDR_ARRAY);
+ if (dns_addr_array == NULL) {
+ goto nomem;
+ }
+ dns_addr_array->MaxCount = num_interfaces;
+ dns_addr_array->AddrCount = num_interfaces;
+ if (num_interfaces == 0) {
+ goto nomem;
+ }
+
+ dns_addr_array->AddrArray = talloc_zero_array(mem_ctx, struct DNS_ADDR,
+ num_interfaces);
+ if (!dns_addr_array->AddrArray) {
+ TALLOC_FREE(dns_addr_array);
+ goto nomem;
+ }
+
+ for (i = 0; i < num_interfaces; i++) {
+ int ret;
+ ipstr = iface_list_n_ip(ifaces, i);
+ if (is_ipaddress_v4(ipstr)) {
+ have_ipv4 = true;
+ dns_addr_array->AddrArray[i].MaxSa[0] = 0x02;
+ ret = inet_pton(AF_INET, ipstr,
+ &dns_addr_array->AddrArray[i].MaxSa[4]);
+ } else {
+ have_ipv6 = true;
+ dns_addr_array->AddrArray[i].MaxSa[0] = 0x17;
+ ret = inet_pton(AF_INET6, ipstr,
+ &dns_addr_array->AddrArray[i].MaxSa[8]);
+ }
+ if (ret != 1) { /*yep, 1 means success for inet_pton */
+ DBG_ERR("Interface %d address (%s) is invalid\n",
+ i, ipstr);
+ goto nomem;
+ }
+ }
+
+ if (have_ipv4 && have_ipv6) {
+ family = 0; /* mixed: MS-DNSP */
+ } else if (have_ipv4 && !have_ipv6) {
+ family = AF_INET;
+ } else {
+ family = AF_INET6;
+ }
+ dns_addr_array->Family = family;
+
+nomem:
+ talloc_free(ifaces);
+ return dns_addr_array;
+}
+
+struct dnsserver_serverinfo *dnsserver_init_serverinfo(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct ldb_context *samdb)
+{
+ struct dnsserver_serverinfo *serverinfo;
+ struct dcerpc_server_info *dinfo;
+ struct ldb_dn *domain_dn, *forest_dn;
+
+ serverinfo = talloc_zero(mem_ctx, struct dnsserver_serverinfo);
+ if (serverinfo == NULL) {
+ return NULL;
+ }
+
+ dinfo = lpcfg_dcerpc_server_info(mem_ctx, lp_ctx);
+ if (dinfo) {
+ serverinfo->dwVersion = (dinfo->version_build & 0x0000FFFF) << 16 |
+ (dinfo->version_minor & 0x000000FF) << 8 |
+ (dinfo->version_major & 0x000000FF);
+ talloc_free(dinfo);
+ } else {
+ serverinfo->dwVersion = 0x0ECE0205; /* build, os_minor, os_major */;
+ }
+
+ serverinfo->fBootMethod = DNS_BOOT_METHOD_DIRECTORY;
+ serverinfo->fAdminConfigured = 0;
+ serverinfo->fAllowUpdate = 1;
+ serverinfo->fDsAvailable = 1;
+
+ serverinfo->pszServerName = talloc_asprintf(mem_ctx, "%s.%s",
+ lpcfg_netbios_name(lp_ctx),
+ lpcfg_dnsdomain(lp_ctx));
+
+ domain_dn = ldb_get_default_basedn(samdb);
+ forest_dn = ldb_get_root_basedn(samdb);
+
+ serverinfo->pszDsContainer = talloc_asprintf(mem_ctx,
+ "CN=MicrosoftDNS,DC=DomainDnsZones,%s",
+ ldb_dn_get_linearized(domain_dn));
+
+ serverinfo->dwDsForestVersion = dsdb_forest_functional_level(samdb);
+ serverinfo->dwDsDomainVersion = dsdb_functional_level(samdb);
+ serverinfo->dwDsDsaVersion = dsdb_dc_functional_level(samdb);
+
+ serverinfo->pszDomainName = samdb_dn_to_dns_domain(mem_ctx, domain_dn);
+ serverinfo->pszForestName = samdb_dn_to_dns_domain(mem_ctx, forest_dn);
+
+ serverinfo->pszDomainDirectoryPartition = talloc_asprintf(mem_ctx,
+ "DC=DomainDnsZones,%s",
+ ldb_dn_get_linearized(domain_dn));
+ serverinfo->pszForestDirectoryPartition = talloc_asprintf(mem_ctx,
+ "DC=ForestDnsZones,%s",
+ ldb_dn_get_linearized(forest_dn));
+ /* IP addresses on which the DNS server listens for DNS requests */
+ serverinfo->aipListenAddrs = fill_dns_addr_array(mem_ctx, lp_ctx, true);
+
+ /* All IP addresses available on the server
+ * Not implemented!
+ * Use same as listen addresses
+ */
+ serverinfo->aipServerAddrs = serverinfo->aipListenAddrs;
+
+ serverinfo->aipForwarders = NULL;
+
+ serverinfo->aipLogFilter = NULL;
+ serverinfo->pwszLogFilePath = NULL;
+
+ serverinfo->dwLogLevel = 0;
+ serverinfo->dwDebugLevel = 0;
+ serverinfo->dwEventLogLevel = DNS_EVENT_LOG_INFORMATION_TYPE;
+ serverinfo->dwLogFileMaxSize = 0;
+
+ serverinfo->dwForwardTimeout = 3; /* seconds (default) */
+ serverinfo->dwRpcProtocol = 5;
+ serverinfo->dwNameCheckFlag = DNS_ALLOW_MULTIBYTE_NAMES;
+ serverinfo->cAddressAnswerLimit = 0;
+ serverinfo->dwRecursionRetry = 3; /* seconds (default) */
+ serverinfo->dwRecursionTimeout = 8; /* seconds (default) */
+ serverinfo->dwMaxCacheTtl = 0x00015180; /* 1 day (default) */
+ serverinfo->dwDsPollingInterval = 0xB4; /* 3 minutes (default) */
+ serverinfo->dwLocalNetPriorityNetMask = 0x000000FF;
+
+ serverinfo->dwScavengingInterval = lpcfg_parm_int(
+ lp_ctx, NULL, "dnsserver", "ScavengingInterval", 24 * 7);
+ serverinfo->dwDefaultRefreshInterval = lpcfg_parm_int(
+ lp_ctx, NULL, "dnsserver", "DefaultRefreshInterval", 24 * 3);
+ serverinfo->dwDefaultNoRefreshInterval = lpcfg_parm_int(
+ lp_ctx, NULL, "dnsserver", "DefaultNoRefreshInterval", 24 * 3);
+
+ serverinfo->dwLastScavengeTime = 0;
+
+ serverinfo->fAutoReverseZones = 0;
+ serverinfo->fAutoCacheUpdate = 0;
+
+ serverinfo->fRecurseAfterForwarding = 0;
+ serverinfo->fForwardDelegations = 1;
+ serverinfo->fNoRecursion = 0;
+ serverinfo->fSecureResponses = 0;
+
+ serverinfo->fRoundRobin = 1;
+ serverinfo->fLocalNetPriority = 0;
+
+ serverinfo->fBindSecondaries = 0;
+ serverinfo->fWriteAuthorityNs = 0;
+
+ serverinfo->fStrictFileParsing = 0;
+ serverinfo->fLooseWildcarding = 0 ;
+ serverinfo->fDefaultAgingState = 0;
+
+ return serverinfo;
+}
+
+struct dnsserver_zoneinfo *dnsserver_init_zoneinfo(struct dnsserver_zone *zone,
+ struct dnsserver_serverinfo *serverinfo)
+{
+ struct dnsserver_zoneinfo *zoneinfo;
+ uint32_t fReverse;
+ const char *revzone = "in-addr.arpa";
+ const char *revzone6 = "ip6.arpa";
+ int len1, len2;
+ unsigned int i = 0;
+
+ zoneinfo = talloc_zero(zone, struct dnsserver_zoneinfo);
+ if (zoneinfo == NULL) {
+ return NULL;
+ }
+
+ /* If the zone name ends with in-addr.arpa, it's reverse zone */
+ /* If the zone name ends with ip6.arpa, it's reverse zone (IPv6) */
+ fReverse = 0;
+ len1 = strlen(zone->name);
+ len2 = strlen(revzone);
+ if (len1 > len2 && strcasecmp(&zone->name[len1-len2], revzone) == 0) {
+ fReverse = 1;
+ } else {
+ len2 = strlen(revzone6);
+ if (len1 > len2 && strcasecmp(&zone->name[len1-len2], revzone6) == 0) {
+ fReverse = 1;
+ }
+ }
+
+ zoneinfo->Version = 0x32;
+ zoneinfo->Flags = DNS_RPC_ZONE_DSINTEGRATED;
+
+ if (strcmp(zone->name, ".") == 0) {
+ zoneinfo->dwZoneType = DNS_ZONE_TYPE_CACHE;
+ zoneinfo->fAllowUpdate = DNS_ZONE_UPDATE_OFF;
+ zoneinfo->fSecureSecondaries = DNS_ZONE_SECSECURE_NO_SECURITY;
+ zoneinfo->fNotifyLevel = DNS_ZONE_NOTIFY_OFF;
+ zoneinfo->dwNoRefreshInterval = 0;
+ zoneinfo->dwRefreshInterval = 0;
+ } else {
+ zoneinfo->Flags |= DNS_RPC_ZONE_UPDATE_SECURE;
+ zoneinfo->dwZoneType = DNS_ZONE_TYPE_PRIMARY;
+ zoneinfo->fAllowUpdate = DNS_ZONE_UPDATE_SECURE;
+ zoneinfo->fSecureSecondaries = DNS_ZONE_SECSECURE_NO_XFER;
+ zoneinfo->fNotifyLevel = DNS_ZONE_NOTIFY_LIST_ONLY;
+ zoneinfo->dwNoRefreshInterval = serverinfo->dwDefaultNoRefreshInterval;
+ zoneinfo->dwRefreshInterval = serverinfo->dwDefaultRefreshInterval;
+ }
+
+ zoneinfo->fReverse = fReverse;
+ zoneinfo->fPaused = 0;
+ zoneinfo->fShutdown = 0;
+ zoneinfo->fAutoCreated = 0;
+ zoneinfo->fUseDatabase = 1;
+ zoneinfo->pszDataFile = NULL;
+ zoneinfo->aipMasters = NULL;
+ zoneinfo->aipSecondaries = NULL;
+ zoneinfo->aipNotify = NULL;
+ zoneinfo->fUseWins = 0;
+ zoneinfo->fUseNbstat = 0;
+ zoneinfo->fAging = 0;
+ zoneinfo->dwAvailForScavengeTime = 0;
+ zoneinfo->aipScavengeServers = NULL;
+ zoneinfo->dwForwarderTimeout = 0;
+ zoneinfo->fForwarderSlave = 0;
+ zoneinfo->aipLocalMasters = NULL;
+ zoneinfo->pwszZoneDn = discard_const_p(char, ldb_dn_get_linearized(zone->zone_dn));
+ zoneinfo->dwLastSuccessfulSoaCheck = 0;
+ zoneinfo->dwLastSuccessfulXfr = 0;
+ zoneinfo->fQueuedForBackgroundLoad = 0;
+ zoneinfo->fBackgroundLoadInProgress = 0;
+ zoneinfo->fReadOnlyZone = 0;
+ zoneinfo->dwLastXfrAttempt = 0;
+ zoneinfo->dwLastXfrResult = 0;
+
+ for(i=0; i<zone->num_props; i++){
+ bool valid_property;
+ valid_property = dns_zoneinfo_load_zone_property(
+ zoneinfo, &zone->tmp_props[i]);
+ if (!valid_property) {
+ TALLOC_FREE(zoneinfo);
+ return NULL;
+ }
+ }
+
+ return zoneinfo;
+}
+
+struct dnsserver_zone *dnsserver_find_zone(struct dnsserver_zone *zones, const char *zone_name)
+{
+ struct dnsserver_zone *z = NULL;
+
+ for (z = zones; z; z = z->next) {
+ if (samba_dns_name_equal(zone_name, z->name)) {
+ break;
+ }
+ }
+
+ return z;
+}
+
+struct ldb_dn *dnsserver_name_to_dn(TALLOC_CTX *mem_ctx, struct dnsserver_zone *z, const char *name)
+{
+ struct ldb_dn *dn;
+ bool ret;
+ struct ldb_val name_val =
+ data_blob_string_const(name);
+
+ dn = ldb_dn_copy(mem_ctx, z->zone_dn);
+ if (dn == NULL) {
+ return NULL;
+ }
+ if (strcasecmp(name, z->name) == 0) {
+ ret = ldb_dn_add_child_fmt(dn, "DC=@");
+ if (!ret) {
+ talloc_free(dn);
+ return NULL;
+ }
+ return dn;
+ }
+
+ ret = ldb_dn_add_child_val(dn,
+ "DC",
+ name_val);
+
+ if (!ret) {
+ talloc_free(dn);
+ return NULL;
+ }
+
+ return dn;
+}
+
+uint32_t dnsserver_zone_to_request_filter(const char *zone_name)
+{
+ uint32_t request_filter = 0;
+
+ if (strcmp(zone_name, "..AllZones") == 0) {
+ request_filter = DNS_ZONE_REQUEST_PRIMARY
+ | DNS_ZONE_REQUEST_SECONDARY
+ | DNS_ZONE_REQUEST_AUTO
+ | DNS_ZONE_REQUEST_FORWARD
+ | DNS_ZONE_REQUEST_REVERSE
+ | DNS_ZONE_REQUEST_FORWARDER
+ | DNS_ZONE_REQUEST_STUB
+ | DNS_ZONE_REQUEST_DS
+ | DNS_ZONE_REQUEST_NON_DS
+ | DNS_ZONE_REQUEST_DOMAIN_DP
+ | DNS_ZONE_REQUEST_FOREST_DP
+ | DNS_ZONE_REQUEST_CUSTOM_DP
+ | DNS_ZONE_REQUEST_LEGACY_DP;
+ } else if (strcmp(zone_name, "..AllZonesAndCache") == 0) {
+ request_filter = DNS_ZONE_REQUEST_PRIMARY
+ | DNS_ZONE_REQUEST_SECONDARY
+ | DNS_ZONE_REQUEST_CACHE
+ | DNS_ZONE_REQUEST_AUTO
+ | DNS_ZONE_REQUEST_FORWARD
+ | DNS_ZONE_REQUEST_REVERSE
+ | DNS_ZONE_REQUEST_FORWARDER
+ | DNS_ZONE_REQUEST_STUB
+ | DNS_ZONE_REQUEST_DS
+ | DNS_ZONE_REQUEST_NON_DS
+ | DNS_ZONE_REQUEST_DOMAIN_DP
+ | DNS_ZONE_REQUEST_FOREST_DP
+ | DNS_ZONE_REQUEST_CUSTOM_DP
+ | DNS_ZONE_REQUEST_LEGACY_DP;
+ } else if (strcmp(zone_name, "..AllPrimaryZones") == 0) {
+ request_filter = DNS_ZONE_REQUEST_PRIMARY;
+ } else if (strcmp(zone_name, "..AllSecondaryZones") == 0) {
+ request_filter = DNS_ZONE_REQUEST_SECONDARY;
+ } else if (strcmp(zone_name, "..AllForwardZones") == 0) {
+ request_filter = DNS_ZONE_REQUEST_FORWARD;
+ } else if (strcmp(zone_name, "..AllReverseZones") == 0) {
+ request_filter = DNS_ZONE_REQUEST_REVERSE;
+ } else if (strcmp(zone_name, "..AllDsZones") == 0) {
+ request_filter = DNS_ZONE_REQUEST_DS;
+ } else if (strcmp(zone_name, "..AllNonDsZones") == 0) {
+ request_filter = DNS_ZONE_REQUEST_NON_DS;
+ } else if (strcmp(zone_name, "..AllPrimaryReverseZones") == 0) {
+ request_filter = DNS_ZONE_REQUEST_PRIMARY
+ | DNS_ZONE_REQUEST_REVERSE;
+ } else if (strcmp(zone_name, "..AllPrimaryForwardZones") == 0) {
+ request_filter = DNS_ZONE_REQUEST_PRIMARY
+ | DNS_ZONE_REQUEST_FORWARD;
+ } else if (strcmp(zone_name, "..AllSecondaryReverseZones") == 0) {
+ request_filter = DNS_ZONE_REQUEST_SECONDARY
+ | DNS_ZONE_REQUEST_REVERSE;
+ } else if (strcmp(zone_name, "..AllSecondaryForwardZones") == 0) {
+ request_filter = DNS_ZONE_REQUEST_SECONDARY
+ | DNS_ZONE_REQUEST_REVERSE;
+ }
+
+ return request_filter;
+}
diff --git a/source4/rpc_server/drsuapi/addentry.c b/source4/rpc_server/drsuapi/addentry.c
new file mode 100644
index 0000000..ff23c52
--- /dev/null
+++ b/source4/rpc_server/drsuapi/addentry.c
@@ -0,0 +1,240 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ implement the DsAddEntry call
+
+ Copyright (C) Stefan Metzmacher 2009
+ Copyright (C) Andrew Tridgell 2009
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "param/param.h"
+#include "libcli/security/security.h"
+#include "libcli/security/session.h"
+#include "rpc_server/drsuapi/dcesrv_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DRS_REPL
+
+/*
+ add special SPNs needed for DRS replication to machine accounts when
+ an AddEntry is done to create a nTDSDSA object
+ */
+static WERROR drsuapi_add_SPNs(struct drsuapi_bind_state *b_state,
+ struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ const struct drsuapi_DsReplicaObjectListItem *first_object)
+{
+ int ret;
+ const struct drsuapi_DsReplicaObjectListItem *obj;
+ const char *attrs[] = { "serverReference", "objectGUID", NULL };
+
+ for (obj = first_object; obj; obj=obj->next_object) {
+ const char *dn_string = obj->object.identifier->dn;
+ struct ldb_dn *dn = ldb_dn_new(mem_ctx, b_state->sam_ctx, dn_string);
+ struct ldb_result *res, *res2;
+ struct ldb_dn *ref_dn;
+ struct GUID ntds_guid;
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+ const char *ntds_guid_str;
+ const char *dom_string;
+ const char *attrs2[] = { "dNSHostName", "cn", NULL };
+ const char *dNSHostName, *cn;
+
+ DEBUG(6,(__location__ ": Adding SPNs for %s\n",
+ ldb_dn_get_linearized(dn)));
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res,
+ dn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=ntDSDSA)");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to find dn '%s'\n", dn_string));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if (res->count < 1) {
+ /* we only add SPNs for nTDSDSA objects */
+ continue;
+ }
+
+ ref_dn = samdb_result_dn(b_state->sam_ctx, mem_ctx, res->msgs[0], "serverReference", NULL);
+ if (ref_dn == NULL) {
+ /* we only add SPNs for objects with a
+ serverReference */
+ continue;
+ }
+
+ DEBUG(6,(__location__ ": serverReference %s\n",
+ ldb_dn_get_linearized(ref_dn)));
+
+ ntds_guid = samdb_result_guid(res->msgs[0], "objectGUID");
+
+ ntds_guid_str = GUID_string(res, &ntds_guid);
+
+ dom_string = lpcfg_dnsdomain(dce_call->conn->dce_ctx->lp_ctx);
+
+ /* get the dNSHostName and cn */
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res2,
+ ref_dn, LDB_SCOPE_BASE, attrs2, NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to find ref_dn '%s'\n",
+ ldb_dn_get_linearized(ref_dn)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ dNSHostName = ldb_msg_find_attr_as_string(res2->msgs[0], "dNSHostName", NULL);
+ cn = ldb_msg_find_attr_as_string(res2->msgs[0], "cn", NULL);
+
+ /*
+ * construct a modify request to add the new SPNs to
+ * the machine account
+ */
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ msg->dn = ref_dn;
+ ret = ldb_msg_add_empty(msg, "servicePrincipalName",
+ LDB_FLAG_MOD_ADD, &el);
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+
+ ldb_msg_add_steal_string(msg, "servicePrincipalName",
+ talloc_asprintf(el->values,
+ "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s",
+ ntds_guid_str, dom_string));
+ ldb_msg_add_steal_string(msg, "servicePrincipalName",
+ talloc_asprintf(el->values, "ldap/%s._msdcs.%s",
+ ntds_guid_str, dom_string));
+ if (cn) {
+ ldb_msg_add_steal_string(msg, "servicePrincipalName",
+ talloc_asprintf(el->values, "ldap/%s", cn));
+ }
+ if (dNSHostName) {
+ ldb_msg_add_steal_string(msg, "servicePrincipalName",
+ talloc_asprintf(el->values, "ldap/%s", dNSHostName));
+ }
+ if (el->num_values < 2) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ret = dsdb_modify(b_state->sam_ctx, msg, DSDB_MODIFY_PERMISSIVE);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to add SPNs - %s\n",
+ ldb_errstring(b_state->sam_ctx)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ }
+
+ return WERR_OK;
+}
+
+
+
+
+/*
+ drsuapi_DsAddEntry
+*/
+WERROR dcesrv_drsuapi_DsAddEntry(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsAddEntry *r)
+{
+ WERROR status;
+ struct drsuapi_bind_state *b_state;
+ struct dcesrv_handle *h;
+ uint32_t num = 0;
+ struct drsuapi_DsReplicaObjectIdentifier2 *ids = NULL;
+ int ret;
+ const struct drsuapi_DsReplicaObjectListItem *first_object;
+
+ if (DEBUGLVL(4)) {
+ NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsAddEntry, NDR_IN, r);
+ }
+
+ /* TODO: check which out level the client supports */
+
+ ZERO_STRUCTP(r->out.ctr);
+ *r->out.level_out = 3;
+ r->out.ctr->ctr3.err_ver = 1;
+ r->out.ctr->ctr3.err_data = talloc_zero(mem_ctx, union drsuapi_DsAddEntry_ErrData);
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ status = drs_security_level_check(dce_call, "DsAddEntry", SECURITY_DOMAIN_CONTROLLER, NULL);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ switch (r->in.level) {
+ case 2:
+ ret = ldb_transaction_start(b_state->sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ DBG_ERR("DsAddEntry start transaction failed: %s\n",
+ ldb_errstring(b_state->sam_ctx));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+
+ first_object = &r->in.req->req2.first_object;
+
+ status = dsdb_origin_objects_commit(b_state->sam_ctx,
+ mem_ctx,
+ first_object,
+ &num,
+ DSDB_REPL_FLAG_ADD_NCNAME,
+ &ids);
+ if (!W_ERROR_IS_OK(status)) {
+ r->out.ctr->ctr3.err_data->v1.status = status;
+ ldb_transaction_cancel(b_state->sam_ctx);
+ DEBUG(0,(__location__ ": DsAddEntry failed - %s\n", win_errstr(status)));
+ return status;
+ }
+
+ r->out.ctr->ctr3.count = num;
+ r->out.ctr->ctr3.objects = ids;
+
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ /* if any of the added entries are nTDSDSA objects then we
+ * need to add the SPNs to the machine account
+ */
+ status = drsuapi_add_SPNs(b_state, dce_call, mem_ctx, first_object);
+ if (!W_ERROR_IS_OK(status)) {
+ r->out.ctr->ctr3.err_data->v1.status = status;
+ ldb_transaction_cancel(b_state->sam_ctx);
+ DEBUG(0,(__location__ ": DsAddEntry add SPNs failed - %s\n", win_errstr(status)));
+ return status;
+ }
+
+ ret = ldb_transaction_commit(b_state->sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": DsAddEntry commit failed: %s\n",
+ ldb_errstring(b_state->sam_ctx)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ return WERR_OK;
+}
diff --git a/source4/rpc_server/drsuapi/dcesrv_drsuapi.c b/source4/rpc_server/drsuapi/dcesrv_drsuapi.c
new file mode 100644
index 0000000..8bb4fb2
--- /dev/null
+++ b/source4/rpc_server/drsuapi/dcesrv_drsuapi.c
@@ -0,0 +1,1070 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the drsuapi pipe
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
+
+ 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 "includes.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "libcli/security/security.h"
+#include "libcli/security/session.h"
+#include "rpc_server/drsuapi/dcesrv_drsuapi.h"
+#include "auth/auth.h"
+#include "param/param.h"
+#include "lib/messaging/irpc.h"
+
+#undef strcasecmp
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DRS_REPL
+
+#define DRSUAPI_UNSUPPORTED(fname) do { \
+ DEBUG(1,(__location__ ": Unsupported DRS call %s\n", #fname)); \
+ if (DEBUGLVL(2)) NDR_PRINT_IN_DEBUG(fname, r); \
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR); \
+} while (0)
+
+#define DCESRV_INTERFACE_DRSUAPI_BIND(context, iface) \
+ dcesrv_interface_drsuapi_bind(context, iface)
+static NTSTATUS dcesrv_interface_drsuapi_bind(struct dcesrv_connection_context *context,
+ const struct dcesrv_interface *iface)
+{
+ return dcesrv_interface_bind_require_privacy(context, iface);
+}
+
+/*
+ drsuapi_DsBind
+*/
+static WERROR dcesrv_drsuapi_DsBind(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsBind *r)
+{
+ struct drsuapi_bind_state *b_state;
+ struct dcesrv_handle *handle;
+ struct drsuapi_DsBindInfoCtr *bind_info;
+ struct drsuapi_DsBindInfoCtr *local_info;
+ struct GUID site_guid, config_guid;
+ struct ldb_result *site_res, *config_res;
+ struct ldb_dn *server_site_dn, *config_dn;
+ static const char *site_attrs[] = { "objectGUID", NULL };
+ static const char *config_attrs[] = { "objectGUID", NULL };
+ struct ldb_result *ntds_res;
+ struct ldb_dn *ntds_dn;
+ static const char *ntds_attrs[] = { "ms-DS-ReplicationEpoch", NULL };
+ uint32_t pid;
+ uint32_t repl_epoch;
+ uint32_t supported_extensions;
+ uint32_t req_length;
+ int ret;
+ WERROR werr;
+
+ r->out.bind_info = NULL;
+ ZERO_STRUCTP(r->out.bind_handle);
+
+ b_state = talloc_zero(mem_ctx, struct drsuapi_bind_state);
+ W_ERROR_HAVE_NO_MEMORY(b_state);
+
+ /* if this is a DC connecting, give them system level access */
+ werr = drs_security_level_check(dce_call, NULL, SECURITY_DOMAIN_CONTROLLER, NULL);
+ if (W_ERROR_IS_OK(werr)) {
+ DBG_NOTICE("doing DsBind with system_session\n");
+ b_state->sam_ctx_system = dcesrv_samdb_connect_as_system(b_state, dce_call);
+ if (b_state->sam_ctx_system == NULL) {
+ return WERR_DS_UNAVAILABLE;
+ }
+ b_state->sam_ctx = b_state->sam_ctx_system;
+ } else {
+ b_state->sam_ctx = dcesrv_samdb_connect_as_user(b_state, dce_call);
+ if (b_state->sam_ctx == NULL) {
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ /*
+ * an RODC also needs system samdb access for secret
+ * attribute replication
+ */
+ werr = drs_security_level_check(dce_call, NULL, SECURITY_RO_DOMAIN_CONTROLLER,
+ samdb_domain_sid(b_state->sam_ctx));
+ if (W_ERROR_IS_OK(werr)) {
+ DBG_NOTICE("doing DsBind as RODC\n");
+ b_state->sam_ctx_system =
+ dcesrv_samdb_connect_as_system(b_state, dce_call);
+ if (b_state->sam_ctx_system == NULL) {
+ return WERR_DS_UNAVAILABLE;
+ }
+ }
+ }
+
+ /*
+ * find out the guid of our own site
+ */
+ server_site_dn = samdb_server_site_dn(b_state->sam_ctx, mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(server_site_dn);
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &site_res,
+ server_site_dn, LDB_SCOPE_BASE, site_attrs,
+ "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ if (site_res->count != 1) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ site_guid = samdb_result_guid(site_res->msgs[0], "objectGUID");
+
+ /*
+ * lookup the local servers Replication Epoch
+ */
+ ntds_dn = samdb_ntds_settings_dn(b_state->sam_ctx, mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(ntds_dn);
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &ntds_res,
+ ntds_dn, LDB_SCOPE_BASE, ntds_attrs,
+ "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ if (ntds_res->count != 1) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ repl_epoch = ldb_msg_find_attr_as_uint(ntds_res->msgs[0],
+ "ms-DS-ReplicationEpoch", 0);
+
+ /*
+ * The "process identifier" of the client.
+ * According to the WSPP docs, sectin 5.35, this is
+ * for informational and debugging purposes only.
+ * The assignment is implementation specific.
+ */
+ pid = 0;
+
+ /*
+ * store the clients bind_guid
+ */
+ if (r->in.bind_guid) {
+ b_state->remote_bind_guid = *r->in.bind_guid;
+ }
+
+ /*
+ * store the clients bind_info
+ */
+ if (r->in.bind_info) {
+ b_state->remote_info = r->in.bind_info;
+ }
+
+ /*
+ * fill in our local bind info
+ */
+ local_info = talloc_zero(mem_ctx, struct drsuapi_DsBindInfoCtr);
+ W_ERROR_HAVE_NO_MEMORY(local_info);
+
+ /*
+ * Fill in supported extensions
+ */
+ supported_extensions = 0;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
+#if 0 /* we don't support MSZIP compression (only decompression) */
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
+#endif
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V5;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
+#if 0 /* we don't support XPRESS compression yet */
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS;
+#endif
+ supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V10;
+
+ /*
+ * There is a chance for r->in.bind_info == NULL
+ * Currently we don't care, since it seems to be used nowhere else.
+ * But we need a request length. So use 28 as default.
+ */
+ req_length = 28;
+ if (r->in.bind_info) {
+ req_length = r->in.bind_info->length;
+ }
+
+ /*
+ * fill 28 or 48 info, depends on request
+ */
+ if (req_length < 48) {
+ local_info->length = 28;
+ local_info->info.info28.supported_extensions = supported_extensions;
+ local_info->info.info28.site_guid = site_guid;
+ local_info->info.info28.pid = pid;
+ local_info->info.info28.repl_epoch = repl_epoch;
+ } else {
+ local_info->length = 48;
+ local_info->info.info48.supported_extensions = supported_extensions;
+ local_info->info.info48.site_guid = site_guid;
+ local_info->info.info48.pid = pid;
+ local_info->info.info48.repl_epoch = repl_epoch;
+
+ local_info->info.info48.supported_extensions_ext = 0;
+ local_info->info.info48.supported_extensions_ext |= DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2;
+
+ /*
+ * find out the guid of our own site
+ */
+ config_dn = ldb_get_config_basedn(b_state->sam_ctx);
+ W_ERROR_HAVE_NO_MEMORY(config_dn);
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &config_res,
+ config_dn, LDB_SCOPE_BASE, config_attrs,
+ "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ if (config_res->count != 1) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ config_guid = samdb_result_guid(config_res->msgs[0], "objectGUID");
+ local_info->info.info48.config_dn_guid = config_guid;
+ }
+
+ /*
+ * set local_info
+ */
+ b_state->local_info = local_info;
+
+ /*
+ * set bind_info
+ */
+ bind_info = local_info;
+
+ /*
+ * allocate a bind handle
+ */
+ handle = dcesrv_handle_create(dce_call, DRSUAPI_BIND_HANDLE);
+ W_ERROR_HAVE_NO_MEMORY(handle);
+ handle->data = talloc_steal(handle, b_state);
+
+ /*
+ * prepare reply
+ */
+ r->out.bind_info = bind_info;
+ *r->out.bind_handle = handle->wire_handle;
+
+ return WERR_OK;
+}
+
+
+/*
+ drsuapi_DsUnbind
+*/
+static WERROR dcesrv_drsuapi_DsUnbind(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsUnbind *r)
+{
+ struct dcesrv_handle *h;
+
+ *r->out.bind_handle = *r->in.bind_handle;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.bind_handle);
+
+ return WERR_OK;
+}
+
+
+/*
+ drsuapi_DsReplicaSync
+*/
+static WERROR dcesrv_drsuapi_DsReplicaSync(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaSync *r)
+{
+ WERROR status;
+ uint32_t timeout;
+
+ status = drs_security_level_check(dce_call, "DsReplicaSync", SECURITY_DOMAIN_CONTROLLER, NULL);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ if (r->in.level != 1) {
+ DEBUG(0,("DsReplicaSync called with unsupported level %d\n", r->in.level));
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ if (r->in.req->req1.options & DRSUAPI_DRS_ASYNC_OP) {
+ timeout = IRPC_CALL_TIMEOUT;
+ } else {
+ /*
+ * use Infinite time for timeout in case
+ * the caller made a sync call
+ */
+ timeout = IRPC_CALL_TIMEOUT_INF;
+ }
+
+ dcesrv_irpc_forward_rpc_call(dce_call, mem_ctx,
+ r, NDR_DRSUAPI_DSREPLICASYNC,
+ &ndr_table_drsuapi,
+ "dreplsrv", "DsReplicaSync",
+ timeout);
+
+ return WERR_OK;
+}
+
+
+/*
+ drsuapi_DsReplicaAdd
+*/
+static WERROR dcesrv_drsuapi_DsReplicaAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAdd *r)
+{
+ WERROR status;
+
+ status = drs_security_level_check(dce_call, "DsReplicaAdd", SECURITY_DOMAIN_CONTROLLER, NULL);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ dcesrv_irpc_forward_rpc_call(dce_call, mem_ctx,
+ r, NDR_DRSUAPI_DSREPLICAADD,
+ &ndr_table_drsuapi,
+ "dreplsrv", "DsReplicaAdd",
+ IRPC_CALL_TIMEOUT);
+
+ return WERR_OK;
+}
+
+
+/*
+ drsuapi_DsReplicaDel
+*/
+static WERROR dcesrv_drsuapi_DsReplicaDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaDel *r)
+{
+ WERROR status;
+
+ status = drs_security_level_check(dce_call, "DsReplicaDel", SECURITY_DOMAIN_CONTROLLER, NULL);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ dcesrv_irpc_forward_rpc_call(dce_call, mem_ctx,
+ r, NDR_DRSUAPI_DSREPLICADEL,
+ &ndr_table_drsuapi,
+ "dreplsrv", "DsReplicaDel",
+ IRPC_CALL_TIMEOUT);
+
+ return WERR_OK;
+}
+
+
+/*
+ drsuapi_DsReplicaModify
+*/
+static WERROR dcesrv_drsuapi_DsReplicaMod(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaMod *r)
+{
+ WERROR status;
+
+ status = drs_security_level_check(dce_call, "DsReplicaMod", SECURITY_DOMAIN_CONTROLLER, NULL);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ dcesrv_irpc_forward_rpc_call(dce_call, mem_ctx,
+ r, NDR_DRSUAPI_DSREPLICAMOD,
+ &ndr_table_drsuapi,
+ "dreplsrv", "DsReplicaMod",
+ IRPC_CALL_TIMEOUT);
+
+ return WERR_OK;
+}
+
+
+/*
+ DRSUAPI_VERIFY_NAMES
+*/
+static WERROR dcesrv_DRSUAPI_VERIFY_NAMES(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_VERIFY_NAMES *r)
+{
+ DRSUAPI_UNSUPPORTED(DRSUAPI_VERIFY_NAMES);
+}
+
+
+/*
+ drsuapi_DsGetMemberships
+*/
+static WERROR dcesrv_drsuapi_DsGetMemberships(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetMemberships *r)
+{
+ DRSUAPI_UNSUPPORTED(drsuapi_DsGetMemberships);
+}
+
+
+/*
+ DRSUAPI_INTER_DOMAIN_MOVE
+*/
+static WERROR dcesrv_DRSUAPI_INTER_DOMAIN_MOVE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_INTER_DOMAIN_MOVE *r)
+{
+ DRSUAPI_UNSUPPORTED(DRSUAPI_INTER_DOMAIN_MOVE);
+}
+
+
+/*
+ drsuapi_DsGetNT4ChangeLog
+*/
+static WERROR dcesrv_drsuapi_DsGetNT4ChangeLog(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetNT4ChangeLog *r)
+{
+ DRSUAPI_UNSUPPORTED(drsuapi_DsGetNT4ChangeLog);
+}
+
+/*
+ drsuapi_DsCrackNames
+*/
+static WERROR dcesrv_drsuapi_DsCrackNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsCrackNames *r)
+{
+ struct drsuapi_bind_state *b_state;
+ struct dcesrv_handle *h;
+
+ *r->out.level_out = r->in.level;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ r->out.ctr = talloc_zero(mem_ctx, union drsuapi_DsNameCtr);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr);
+
+ switch (r->in.level) {
+ case 1: {
+ switch(r->in.req->req1.format_offered){
+ case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT_NAME_SANS_DOMAIN_EX:
+ case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT_NAME_SANS_DOMAIN:
+ case DRSUAPI_DS_NAME_FORMAT_STRING_SID_NAME:
+ case DRSUAPI_DS_NAME_FORMAT_ALT_SECURITY_IDENTITIES_NAME:
+ case DRSUAPI_DS_NAME_FORMAT_MAP_SCHEMA_GUID:
+ case DRSUAPI_DS_NAME_FORMAT_LIST_NCS:
+ case DRSUAPI_DS_NAME_FORMAT_LIST_DOMAINS:
+ case DRSUAPI_DS_NAME_FORMAT_LIST_GLOBAL_CATALOG_SERVERS:
+ case DRSUAPI_DS_NAME_FORMAT_LIST_SERVERS_WITH_DCS_IN_SITE:
+ case DRSUAPI_DS_NAME_FORMAT_LIST_SERVERS_FOR_DOMAIN_IN_SITE:
+ case DRSUAPI_DS_NAME_FORMAT_LIST_DOMAINS_IN_SITE:
+ case DRSUAPI_DS_NAME_FORMAT_LIST_SERVERS_IN_SITE:
+ case DRSUAPI_DS_NAME_FORMAT_LIST_SITES:
+ case DRSUAPI_DS_NAME_FORMAT_UPN_AND_ALTSECID:
+ case DRSUAPI_DS_NAME_FORMAT_UPN_FOR_LOGON:
+ DEBUG(0, ("DsCrackNames: Unsupported operation requested: %X",
+ r->in.req->req1.format_offered));
+ return WERR_OK;
+ case DRSUAPI_DS_NAME_FORMAT_LIST_INFO_FOR_SERVER:
+ return dcesrv_drsuapi_ListInfoServer(b_state->sam_ctx, mem_ctx, &r->in.req->req1, &r->out.ctr->ctr1);
+ case DRSUAPI_DS_NAME_FORMAT_LIST_ROLES:
+ return dcesrv_drsuapi_ListRoles(b_state->sam_ctx, mem_ctx,
+ &r->in.req->req1, &r->out.ctr->ctr1);
+ default:/* format_offered is in the enum drsuapi_DsNameFormat*/
+ return dcesrv_drsuapi_CrackNamesByNameFormat(b_state->sam_ctx, mem_ctx,
+ &r->in.req->req1, &r->out.ctr->ctr1);
+ }
+ }
+ }
+ return WERR_INVALID_LEVEL;
+}
+
+
+/*
+ drsuapi_DsRemoveDSServer
+*/
+static WERROR dcesrv_drsuapi_DsRemoveDSServer(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsRemoveDSServer *r)
+{
+ struct drsuapi_bind_state *b_state;
+ struct dcesrv_handle *h;
+ struct ldb_dn *ntds_dn;
+ int ret;
+ bool ok;
+ WERROR status;
+
+ *r->out.level_out = 1;
+
+ status = drs_security_level_check(dce_call, "DsRemoveDSServer", SECURITY_DOMAIN_CONTROLLER, NULL);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ switch (r->in.level) {
+ case 1:
+ ntds_dn = ldb_dn_new(mem_ctx, b_state->sam_ctx, r->in.req->req1.server_dn);
+ W_ERROR_HAVE_NO_MEMORY(ntds_dn);
+
+ ok = ldb_dn_validate(ntds_dn);
+ if (!ok) {
+ return WERR_FOOBAR;
+ }
+
+ /* TODO: it's likely that we need more checks here */
+
+ ok = ldb_dn_add_child_fmt(ntds_dn, "CN=NTDS Settings");
+ if (!ok) {
+ return WERR_FOOBAR;
+ }
+
+ if (r->in.req->req1.commit) {
+ ret = dsdb_delete(b_state->sam_ctx, ntds_dn, DSDB_TREE_DELETE);
+ if (ret != LDB_SUCCESS) {
+ return WERR_FOOBAR;
+ }
+ }
+
+ return WERR_OK;
+ default:
+ break;
+ }
+
+ return WERR_FOOBAR;
+}
+
+
+/*
+ DRSUAPI_REMOVE_DS_DOMAIN
+*/
+static WERROR dcesrv_DRSUAPI_REMOVE_DS_DOMAIN(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_REMOVE_DS_DOMAIN *r)
+{
+ DRSUAPI_UNSUPPORTED(DRSUAPI_REMOVE_DS_DOMAIN);
+}
+
+/* Obtain the site name from a server DN */
+static const char *result_site_name(struct ldb_dn *server_dn)
+{
+ /* Format is cn=<NETBIOS name>,cn=Servers,cn=<site>,cn=sites.... */
+ const struct ldb_val *val = ldb_dn_get_component_val(server_dn, 2);
+ const char *name = ldb_dn_get_component_name(server_dn, 2);
+
+ if (!name || (ldb_attr_cmp(name, "cn") != 0)) {
+ /* Ensure this matches the format. This gives us a
+ * bit more confidence that a 'cn' value will be a
+ * ascii string */
+ return NULL;
+ }
+ if (val) {
+ return (char *)val->data;
+ }
+ return NULL;
+}
+
+/*
+ drsuapi_DsGetDomainControllerInfo
+*/
+static WERROR dcesrv_drsuapi_DsGetDomainControllerInfo_1(struct drsuapi_bind_state *b_state,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetDomainControllerInfo *r)
+{
+ struct ldb_dn *sites_dn;
+ struct ldb_result *res;
+
+ const char *attrs_account_1[] = { "cn", "dnsHostName", NULL };
+ const char *attrs_account_2[] = { "cn", "dnsHostName", "objectGUID", NULL };
+
+ const char *attrs_none[] = { NULL };
+
+ const char *attrs_site[] = { "objectGUID", NULL };
+
+ const char *attrs_ntds[] = { "options", "objectGUID", NULL };
+
+ const char *attrs_1[] = { "serverReference", "cn", "dnsHostName", NULL };
+ const char *attrs_2[] = { "serverReference", "cn", "dnsHostName", "objectGUID", NULL };
+ const char **attrs;
+
+ struct drsuapi_DsGetDCInfoCtr1 *ctr1;
+ struct drsuapi_DsGetDCInfoCtr2 *ctr2;
+ struct drsuapi_DsGetDCInfoCtr3 *ctr3;
+
+ int ret;
+ unsigned int i;
+
+ *r->out.level_out = r->in.req->req1.level;
+ r->out.ctr = talloc_zero(mem_ctx, union drsuapi_DsGetDCInfoCtr);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr);
+
+ switch (*r->out.level_out) {
+ case -1:
+ /* this level is not like the others */
+ return WERR_INVALID_LEVEL;
+ case 1:
+ attrs = attrs_1;
+ break;
+ case 2:
+ case 3:
+ attrs = attrs_2;
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ sites_dn = samdb_sites_dn(b_state->sam_ctx, mem_ctx);
+ if (!sites_dn) {
+ return WERR_DS_OBJ_NOT_FOUND;
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res, sites_dn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(objectClass=server)(serverReference=*))");
+
+ if (ret) {
+ DEBUG(1, ("searching for servers in sites DN %s failed: %s\n",
+ ldb_dn_get_linearized(sites_dn), ldb_errstring(b_state->sam_ctx)));
+ return WERR_GEN_FAILURE;
+ }
+
+ switch (*r->out.level_out) {
+ case 1:
+ ctr1 = &r->out.ctr->ctr1;
+ ctr1->count = res->count;
+ ctr1->array = talloc_zero_array(mem_ctx,
+ struct drsuapi_DsGetDCInfo1,
+ res->count);
+ for (i=0; i < res->count; i++) {
+ struct ldb_dn *domain_dn;
+ struct ldb_result *res_domain;
+ struct ldb_result *res_account;
+ struct ldb_dn *ntds_dn = ldb_dn_copy(mem_ctx, res->msgs[i]->dn);
+
+ struct ldb_dn *ref_dn
+ = ldb_msg_find_attr_as_dn(b_state->sam_ctx,
+ mem_ctx, res->msgs[i],
+ "serverReference");
+
+ if (!ntds_dn || !ldb_dn_add_child_fmt(ntds_dn, "CN=NTDS Settings")) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_account, ref_dn,
+ LDB_SCOPE_BASE, attrs_account_1,
+ "(&(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=%u))",
+ UF_SERVER_TRUST_ACCOUNT);
+ if (ret == LDB_SUCCESS && res_account->count == 1) {
+ const char *errstr;
+ ctr1->array[i].dns_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "dNSHostName", NULL);
+ ctr1->array[i].netbios_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "cn", NULL);
+ ctr1->array[i].computer_dn
+ = ldb_dn_get_linearized(res_account->msgs[0]->dn);
+
+ /* Determine if this is the PDC */
+ ret = samdb_search_for_parent_domain(b_state->sam_ctx,
+ mem_ctx, res_account->msgs[0]->dn,
+ &domain_dn, &errstr);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_domain, domain_dn,
+ LDB_SCOPE_BASE, attrs_none, "fSMORoleOwner=%s",
+ ldb_dn_get_linearized(ntds_dn));
+ if (ret) {
+ return WERR_GEN_FAILURE;
+ }
+ if (res_domain->count == 1) {
+ ctr1->array[i].is_pdc = true;
+ }
+ }
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for computer DN %s failed: %s\n",
+ ldb_dn_get_linearized(ref_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ /* Look at server DN and extract site component */
+ ctr1->array[i].site_name = result_site_name(res->msgs[i]->dn);
+ ctr1->array[i].server_dn = ldb_dn_get_linearized(res->msgs[i]->dn);
+
+
+ ctr1->array[i].is_enabled = true;
+
+ }
+ break;
+ case 2:
+ ctr2 = &r->out.ctr->ctr2;
+ ctr2->count = res->count;
+ ctr2->array = talloc_zero_array(mem_ctx,
+ struct drsuapi_DsGetDCInfo2,
+ res->count);
+ for (i=0; i < res->count; i++) {
+ struct ldb_dn *domain_dn;
+ struct ldb_result *res_domain;
+ struct ldb_result *res_account;
+ struct ldb_dn *ntds_dn = ldb_dn_copy(mem_ctx, res->msgs[i]->dn);
+ struct ldb_result *res_ntds;
+ struct ldb_dn *site_dn = ldb_dn_copy(mem_ctx, res->msgs[i]->dn);
+ struct ldb_result *res_site;
+ struct ldb_dn *ref_dn
+ = ldb_msg_find_attr_as_dn(b_state->sam_ctx,
+ mem_ctx, res->msgs[i],
+ "serverReference");
+
+ if (!ntds_dn || !ldb_dn_add_child_fmt(ntds_dn, "CN=NTDS Settings")) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* Format is cn=<NETBIOS name>,cn=Servers,cn=<site>,cn=sites.... */
+ if (!site_dn || !ldb_dn_remove_child_components(site_dn, 2)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_ntds, ntds_dn,
+ LDB_SCOPE_BASE, attrs_ntds, "objectClass=nTDSDSA");
+ if (ret == LDB_SUCCESS && res_ntds->count == 1) {
+ ctr2->array[i].is_gc
+ = (ldb_msg_find_attr_as_uint(res_ntds->msgs[0], "options", 0) & DS_NTDSDSA_OPT_IS_GC);
+ ctr2->array[i].ntds_guid
+ = samdb_result_guid(res_ntds->msgs[0], "objectGUID");
+ ctr2->array[i].ntds_dn = ldb_dn_get_linearized(res_ntds->msgs[0]->dn);
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for NTDS DN %s failed: %s\n",
+ ldb_dn_get_linearized(ntds_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_site, site_dn,
+ LDB_SCOPE_BASE, attrs_site, "objectClass=site");
+ if (ret == LDB_SUCCESS && res_site->count == 1) {
+ ctr2->array[i].site_guid
+ = samdb_result_guid(res_site->msgs[0], "objectGUID");
+ ctr2->array[i].site_dn = ldb_dn_get_linearized(res_site->msgs[0]->dn);
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for site DN %s failed: %s\n",
+ ldb_dn_get_linearized(site_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_account, ref_dn,
+ LDB_SCOPE_BASE, attrs_account_2, "objectClass=computer");
+ if (ret == LDB_SUCCESS && res_account->count == 1) {
+ const char *errstr;
+ ctr2->array[i].dns_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "dNSHostName", NULL);
+ ctr2->array[i].netbios_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "cn", NULL);
+ ctr2->array[i].computer_dn = ldb_dn_get_linearized(res_account->msgs[0]->dn);
+ ctr2->array[i].computer_guid
+ = samdb_result_guid(res_account->msgs[0], "objectGUID");
+
+ /* Determine if this is the PDC */
+ ret = samdb_search_for_parent_domain(b_state->sam_ctx,
+ mem_ctx, res_account->msgs[0]->dn,
+ &domain_dn, &errstr);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_domain, domain_dn,
+ LDB_SCOPE_BASE, attrs_none, "fSMORoleOwner=%s",
+ ldb_dn_get_linearized(ntds_dn));
+ if (ret == LDB_SUCCESS && res_domain->count == 1) {
+ ctr2->array[i].is_pdc = true;
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for domain DN %s failed: %s\n",
+ ldb_dn_get_linearized(domain_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+ }
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for computer account DN %s failed: %s\n",
+ ldb_dn_get_linearized(ref_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ /* Look at server DN and extract site component */
+ ctr2->array[i].site_name = result_site_name(res->msgs[i]->dn);
+ ctr2->array[i].server_dn = ldb_dn_get_linearized(res->msgs[i]->dn);
+ ctr2->array[i].server_guid
+ = samdb_result_guid(res->msgs[i], "objectGUID");
+
+ ctr2->array[i].is_enabled = true;
+
+ }
+ break;
+ case 3:
+ ctr3 = &r->out.ctr->ctr3;
+ ctr3->count = res->count;
+ ctr3->array = talloc_zero_array(mem_ctx,
+ struct drsuapi_DsGetDCInfo3,
+ res->count);
+ for (i=0; i<res->count; i++) {
+ struct ldb_dn *domain_dn;
+ struct ldb_result *res_domain;
+ struct ldb_result *res_account;
+ struct ldb_dn *ntds_dn = ldb_dn_copy(mem_ctx, res->msgs[i]->dn);
+ struct ldb_result *res_ntds;
+ struct ldb_dn *site_dn = ldb_dn_copy(mem_ctx, res->msgs[i]->dn);
+ struct ldb_result *res_site;
+ bool is_rodc;
+ struct ldb_dn *ref_dn
+ = ldb_msg_find_attr_as_dn(b_state->sam_ctx,
+ mem_ctx, res->msgs[i],
+ "serverReference");
+
+ if (!ntds_dn || !ldb_dn_add_child_fmt(ntds_dn, "CN=NTDS Settings")) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* Format is cn=<NETBIOS name>,cn=Servers,cn=<site>,cn=sites.... */
+ if (!site_dn || !ldb_dn_remove_child_components(site_dn, 2)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_ntds, ntds_dn,
+ LDB_SCOPE_BASE, attrs_ntds, "objectClass=nTDSDSA");
+ if (ret == LDB_SUCCESS && res_ntds->count == 1) {
+ ctr3->array[i].is_gc
+ = (ldb_msg_find_attr_as_uint(res_ntds->msgs[0], "options", 0) & DS_NTDSDSA_OPT_IS_GC);
+ ctr3->array[i].ntds_guid
+ = samdb_result_guid(res_ntds->msgs[0], "objectGUID");
+ ctr3->array[i].ntds_dn = ldb_dn_get_linearized(res_ntds->msgs[0]->dn);
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for NTDS DN %s failed: %s\n",
+ ldb_dn_get_linearized(ntds_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_site, site_dn,
+ LDB_SCOPE_BASE, attrs_site, "objectClass=site");
+ if (ret == LDB_SUCCESS && res_site->count == 1) {
+ ctr3->array[i].site_guid
+ = samdb_result_guid(res_site->msgs[0], "objectGUID");
+ ctr3->array[i].site_dn = ldb_dn_get_linearized(res_site->msgs[0]->dn);
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for site DN %s failed: %s\n",
+ ldb_dn_get_linearized(site_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_account, ref_dn,
+ LDB_SCOPE_BASE, attrs_account_2, "objectClass=computer");
+ if (ret == LDB_SUCCESS && res_account->count == 1) {
+ const char *errstr;
+ ctr3->array[i].dns_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "dNSHostName", NULL);
+ ctr3->array[i].netbios_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "cn", NULL);
+ ctr3->array[i].computer_dn = ldb_dn_get_linearized(res_account->msgs[0]->dn);
+ ctr3->array[i].computer_guid
+ = samdb_result_guid(res_account->msgs[0], "objectGUID");
+
+ /* Determine if this is the PDC */
+ ret = samdb_search_for_parent_domain(b_state->sam_ctx,
+ mem_ctx, res_account->msgs[0]->dn,
+ &domain_dn, &errstr);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_domain, domain_dn,
+ LDB_SCOPE_BASE, attrs_none, "fSMORoleOwner=%s",
+ ldb_dn_get_linearized(ntds_dn));
+ if (ret == LDB_SUCCESS && res_domain->count == 1) {
+ ctr3->array[i].is_pdc = true;
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for domain DN %s failed: %s\n",
+ ldb_dn_get_linearized(domain_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+ }
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for computer account DN %s failed: %s\n",
+ ldb_dn_get_linearized(ref_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ /* Look at server DN and extract site component */
+ ctr3->array[i].site_name = result_site_name(res->msgs[i]->dn);
+ ctr3->array[i].server_dn = ldb_dn_get_linearized(res->msgs[i]->dn);
+ ctr3->array[i].server_guid
+ = samdb_result_guid(res->msgs[i], "objectGUID");
+
+ ctr3->array[i].is_enabled = true;
+
+ /* rodc? */
+ ret = samdb_is_rodc(b_state->sam_ctx, &ctr3->array[i].server_guid, &is_rodc);
+ if (ret == LDB_SUCCESS && is_rodc) {
+ ctr3->array[i].is_rodc = true;
+ }
+ }
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+ return WERR_OK;
+}
+
+/*
+ drsuapi_DsGetDomainControllerInfo
+*/
+static WERROR dcesrv_drsuapi_DsGetDomainControllerInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetDomainControllerInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct drsuapi_bind_state *b_state;
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ switch (r->in.level) {
+ case 1:
+ return dcesrv_drsuapi_DsGetDomainControllerInfo_1(b_state, mem_ctx, r);
+ }
+
+ return WERR_INVALID_LEVEL;
+}
+
+
+
+/*
+ drsuapi_DsExecuteKCC
+*/
+static WERROR dcesrv_drsuapi_DsExecuteKCC(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsExecuteKCC *r)
+{
+ WERROR status;
+ uint32_t timeout;
+ status = drs_security_level_check(dce_call, "DsExecuteKCC", SECURITY_DOMAIN_CONTROLLER, NULL);
+
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ if (r->in.req->ctr1.taskID != 0) {
+ return WERR_INVALID_PARAMETER;
+ }
+ if (r->in.req->ctr1.flags & DRSUAPI_DS_EXECUTE_KCC_ASYNCHRONOUS_OPERATION) {
+ timeout = IRPC_CALL_TIMEOUT;
+ } else {
+ /*
+ * use Infinite time for timeout in case
+ * the caller made a sync call
+ */
+ timeout = IRPC_CALL_TIMEOUT_INF;
+ }
+
+ dcesrv_irpc_forward_rpc_call(dce_call, mem_ctx, r, NDR_DRSUAPI_DSEXECUTEKCC,
+ &ndr_table_drsuapi, "kccsrv", "DsExecuteKCC",
+ timeout);
+ DEBUG(10, ("Forwarded the call to execute the KCC\n"));
+ return WERR_OK;
+}
+
+
+/*
+ drsuapi_DsReplicaGetInfo
+*/
+static WERROR dcesrv_drsuapi_DsReplicaGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaGetInfo *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ enum security_user_level level;
+
+ if (!lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx, NULL,
+ "drs", "disable_sec_check", false)) {
+ level = security_session_user_level(session_info, NULL);
+ if (level < SECURITY_DOMAIN_CONTROLLER) {
+ DEBUG(1,(__location__ ": Administrator access required for DsReplicaGetInfo\n"));
+ security_token_debug(DBGC_DRS_REPL, 2,
+ session_info->security_token);
+ return WERR_DS_DRA_ACCESS_DENIED;
+ }
+ }
+
+ dcesrv_irpc_forward_rpc_call(dce_call, mem_ctx, r, NDR_DRSUAPI_DSREPLICAGETINFO,
+ &ndr_table_drsuapi, "kccsrv", "DsReplicaGetInfo",
+ IRPC_CALL_TIMEOUT);
+
+ return WERR_OK;
+}
+
+
+/*
+ DRSUAPI_ADD_SID_HISTORY
+*/
+static WERROR dcesrv_DRSUAPI_ADD_SID_HISTORY(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_ADD_SID_HISTORY *r)
+{
+ DRSUAPI_UNSUPPORTED(DRSUAPI_ADD_SID_HISTORY);
+}
+
+/*
+ drsuapi_DsGetMemberships2
+*/
+static WERROR dcesrv_drsuapi_DsGetMemberships2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetMemberships2 *r)
+{
+ DRSUAPI_UNSUPPORTED(drsuapi_DsGetMemberships2);
+}
+
+/*
+ DRSUAPI_REPLICA_VERIFY_OBJECTS
+*/
+static WERROR dcesrv_DRSUAPI_REPLICA_VERIFY_OBJECTS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_REPLICA_VERIFY_OBJECTS *r)
+{
+ DRSUAPI_UNSUPPORTED(DRSUAPI_REPLICA_VERIFY_OBJECTS);
+}
+
+
+/*
+ DRSUAPI_GET_OBJECT_EXISTENCE
+*/
+static WERROR dcesrv_DRSUAPI_GET_OBJECT_EXISTENCE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_GET_OBJECT_EXISTENCE *r)
+{
+ DRSUAPI_UNSUPPORTED(DRSUAPI_GET_OBJECT_EXISTENCE);
+}
+
+
+/*
+ drsuapi_QuerySitesByCost
+*/
+static WERROR dcesrv_drsuapi_QuerySitesByCost(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_QuerySitesByCost *r)
+{
+ DRSUAPI_UNSUPPORTED(drsuapi_QuerySitesByCost);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_drsuapi_s.c"
diff --git a/source4/rpc_server/drsuapi/dcesrv_drsuapi.h b/source4/rpc_server/drsuapi/dcesrv_drsuapi.h
new file mode 100644
index 0000000..18ac997
--- /dev/null
+++ b/source4/rpc_server/drsuapi/dcesrv_drsuapi.h
@@ -0,0 +1,84 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the drsuapi pipe
+
+ Copyright (C) Stefan Metzmacher 2004
+
+ 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/>.
+*/
+
+/*
+ this type allows us to distinguish handle types
+*/
+enum drsuapi_handle {
+ DRSUAPI_BIND_HANDLE,
+};
+
+/*
+ state asscoiated with a drsuapi_DsBind*() operation
+*/
+struct drsuapi_bind_state {
+ struct ldb_context *sam_ctx;
+ struct ldb_context *sam_ctx_system;
+ struct GUID remote_bind_guid;
+ struct drsuapi_DsBindInfoCtr *remote_info;
+ struct drsuapi_DsBindInfoCtr *local_info;
+ struct drsuapi_getncchanges_state *getncchanges_full_repl_state;
+};
+
+
+/* prototypes of internal functions */
+WERROR drsuapi_UpdateRefs(struct imessaging_context *msg_ctx,
+ struct tevent_context *event_ctx,
+ struct drsuapi_bind_state *b_state, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaUpdateRefsRequest1 *req);
+WERROR dcesrv_drsuapi_DsReplicaUpdateRefs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaUpdateRefs *r);
+WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetNCChanges *r);
+WERROR dcesrv_drsuapi_DsAddEntry(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsAddEntry *r);
+WERROR dcesrv_drsuapi_DsWriteAccountSpn(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsWriteAccountSpn *r);
+
+char *drs_ObjectIdentifier_to_string(TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaObjectIdentifier *nc);
+
+int drsuapi_search_with_extended_dn(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_result **_res,
+ struct ldb_dn *basedn,
+ enum ldb_scope scope,
+ const char * const *attrs,
+ const char *filter);
+
+WERROR drs_security_level_check(struct dcesrv_call_state *dce_call,
+ const char* call, enum security_user_level minimum_level,
+ const struct dom_sid *domain_sid);
+
+void drsuapi_process_secret_attribute(struct drsuapi_DsReplicaAttribute *attr,
+ struct drsuapi_DsReplicaMetaData *meta_data);
+
+WERROR drs_security_access_check(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct security_token *token,
+ struct drsuapi_DsReplicaObjectIdentifier *nc,
+ const char *ext_right);
+
+WERROR drs_security_access_check_nc_root(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct security_token *token,
+ struct drsuapi_DsReplicaObjectIdentifier *nc,
+ const char *ext_right);
diff --git a/source4/rpc_server/drsuapi/drsutil.c b/source4/rpc_server/drsuapi/drsutil.c
new file mode 100644
index 0000000..48423bb
--- /dev/null
+++ b/source4/rpc_server/drsuapi/drsutil.c
@@ -0,0 +1,237 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ useful utilities for the DRS server
+
+ Copyright (C) Andrew Tridgell 2009
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/security.h"
+#include "libcli/security/session.h"
+#include "param/param.h"
+#include "auth/session.h"
+#include "rpc_server/drsuapi/dcesrv_drsuapi.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DRS_REPL
+
+int drsuapi_search_with_extended_dn(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_result **_res,
+ struct ldb_dn *basedn,
+ enum ldb_scope scope,
+ const char * const *attrs,
+ const char *filter)
+{
+ int ret;
+ struct ldb_request *req;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+
+ tmp_ctx = talloc_new(mem_ctx);
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (!res) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req(&req, ldb, tmp_ctx,
+ basedn,
+ scope,
+ filter,
+ attrs,
+ NULL,
+ res,
+ ldb_search_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = ldb_request_add_control(req, LDB_CONTROL_EXTENDED_DN_OID, true, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_RECYCLED_OID, true, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_request_add_control(req, LDB_CONTROL_REVEAL_INTERNALS, false, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_request(ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ talloc_free(req);
+ *_res = talloc_steal(mem_ctx, res);
+ return ret;
+}
+
+WERROR drs_security_level_check(struct dcesrv_call_state *dce_call,
+ const char* call,
+ enum security_user_level minimum_level,
+ const struct dom_sid *domain_sid)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ enum security_user_level level;
+
+ if (lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx, NULL,
+ "drs", "disable_sec_check", false)) {
+ return WERR_OK;
+ }
+
+ level = security_session_user_level(session_info, domain_sid);
+ if (level < minimum_level) {
+ if (call) {
+ DEBUG(0,("%s refused for security token (level=%u)\n",
+ call, (unsigned)level));
+ security_token_debug(DBGC_DRS_REPL, 2, session_info->security_token);
+ }
+ return WERR_DS_DRA_ACCESS_DENIED;
+ }
+
+ return WERR_OK;
+}
+
+void drsuapi_process_secret_attribute(struct drsuapi_DsReplicaAttribute *attr,
+ struct drsuapi_DsReplicaMetaData *meta_data)
+{
+ if (attr->value_ctr.num_values == 0) {
+ return;
+ }
+
+ switch (attr->attid) {
+ case DRSUAPI_ATTID_dBCSPwd:
+ case DRSUAPI_ATTID_unicodePwd:
+ case DRSUAPI_ATTID_ntPwdHistory:
+ case DRSUAPI_ATTID_lmPwdHistory:
+ case DRSUAPI_ATTID_supplementalCredentials:
+ case DRSUAPI_ATTID_priorValue:
+ case DRSUAPI_ATTID_currentValue:
+ case DRSUAPI_ATTID_trustAuthOutgoing:
+ case DRSUAPI_ATTID_trustAuthIncoming:
+ case DRSUAPI_ATTID_initialAuthOutgoing:
+ case DRSUAPI_ATTID_initialAuthIncoming:
+ /*set value to null*/
+ attr->value_ctr.num_values = 0;
+ talloc_free(attr->value_ctr.values);
+ attr->value_ctr.values = NULL;
+ meta_data->originating_change_time = 0;
+ return;
+ default:
+ return;
+ }
+}
+
+
+/*
+ check security on a DN, with logging of errors
+ */
+static WERROR drs_security_access_check_log(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct security_token *token,
+ struct ldb_dn *dn,
+ const char *ext_right)
+{
+ int ret;
+ if (!dn) {
+ DEBUG(3,("drs_security_access_check: Null dn provided, access is denied for %s\n",
+ ext_right));
+ return WERR_DS_DRA_ACCESS_DENIED;
+ }
+ ret = dsdb_check_access_on_dn(sam_ctx,
+ mem_ctx,
+ dn,
+ token,
+ SEC_ADS_CONTROL_ACCESS,
+ ext_right);
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ DEBUG(3,("%s refused for security token on %s\n",
+ ext_right, ldb_dn_get_linearized(dn)));
+ security_token_debug(DBGC_DRS_REPL, 3, token);
+ return WERR_DS_DRA_ACCESS_DENIED;
+ } else if (ret != LDB_SUCCESS) {
+ DEBUG(1,("Failed to perform access check on %s: %s\n", ldb_dn_get_linearized(dn), ldb_strerror(ret)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ return WERR_OK;
+}
+
+
+/*
+ check security on a object identifier
+ */
+WERROR drs_security_access_check(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct security_token *token,
+ struct drsuapi_DsReplicaObjectIdentifier *nc,
+ const char *ext_right)
+{
+ struct ldb_dn *dn;
+ WERROR werr;
+ int ret;
+
+ ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx,
+ sam_ctx,
+ nc,
+ &dn,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_DRA_BAD_DN;
+ }
+
+ werr = drs_security_access_check_log(sam_ctx, mem_ctx, token, dn, ext_right);
+ talloc_free(dn);
+ return werr;
+}
+
+/*
+ check security on the NC root of a object identifier
+ */
+WERROR drs_security_access_check_nc_root(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct security_token *token,
+ struct drsuapi_DsReplicaObjectIdentifier *nc,
+ const char *ext_right)
+{
+ struct ldb_dn *nc_root;
+ WERROR werr;
+ int ret;
+
+ ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx,
+ sam_ctx,
+ nc,
+ NULL,
+ &nc_root);
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_DRA_BAD_NC;
+ }
+
+ werr = drs_security_access_check_log(sam_ctx, mem_ctx, token, nc_root, ext_right);
+ talloc_free(nc_root);
+ return werr;
+}
diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
new file mode 100644
index 0000000..8864e79
--- /dev/null
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -0,0 +1,3861 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ implement the DSGetNCChanges call
+
+ Copyright (C) Anatoliy Atanasov 2009
+ Copyright (C) Andrew Tridgell 2009-2010
+ Copyright (C) Andrew Bartlett 2010-2016
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "dsdb/samdb/samdb.h"
+#include "param/param.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "libcli/security/session.h"
+#include "rpc_server/drsuapi/dcesrv_drsuapi.h"
+#include "../libcli/drsuapi/drsuapi.h"
+#include "lib/util/binsearch.h"
+#include "lib/util/tsort.h"
+#include "auth/session.h"
+#include "dsdb/common/util.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DRS_REPL
+
+#define DRS_GUID_SIZE 16
+#define DEFAULT_MAX_OBJECTS 1000
+#define DEFAULT_MAX_LINKS 1500
+
+/*
+ * state of a partially-completed replication cycle. This state persists
+ * over multiple calls to dcesrv_drsuapi_DsGetNCChanges()
+ */
+struct drsuapi_getncchanges_state {
+ struct db_context *obj_cache;
+ struct GUID *guids;
+ uint32_t num_records;
+ uint32_t num_processed;
+ struct ldb_dn *ncRoot_dn;
+ struct GUID ncRoot_guid;
+ bool is_schema_nc;
+ bool is_get_anc;
+ bool broken_samba_4_5_get_anc_emulation;
+ bool is_get_tgt;
+ bool send_nc_root_first;
+ uint64_t min_usn;
+ uint64_t max_usn;
+ struct drsuapi_DsReplicaHighWaterMark last_hwm;
+ struct ldb_dn *last_dn;
+ struct drsuapi_DsReplicaHighWaterMark final_hwm;
+ struct drsuapi_DsReplicaCursor2CtrEx *final_udv;
+ struct drsuapi_DsReplicaLinkedAttribute *la_list;
+ uint32_t la_count;
+ uint32_t la_idx;
+
+ /* these are just used for debugging the replication's progress */
+ uint32_t links_given;
+ uint32_t total_links;
+};
+
+/* We must keep the GUIDs in NDR form for sorting */
+struct la_for_sorting {
+ const struct drsuapi_DsReplicaLinkedAttribute *link;
+ uint8_t target_guid[DRS_GUID_SIZE];
+ uint8_t source_guid[DRS_GUID_SIZE];
+};
+
+/*
+ * stores the state for a chunk of replication data. This state information
+ * only exists for a single call to dcesrv_drsuapi_DsGetNCChanges()
+ */
+struct getncchanges_repl_chunk {
+ uint32_t max_objects;
+ uint32_t max_links;
+ uint32_t tgt_la_count;
+ bool immediate_link_sync;
+ time_t max_wait;
+ time_t start;
+
+ /* stores the objects to be sent in this chunk */
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *object_list;
+
+ /* the last object added to this replication chunk */
+ struct drsuapi_DsReplicaObjectListItemEx *last_object;
+};
+
+static int drsuapi_DsReplicaHighWaterMark_cmp(const struct drsuapi_DsReplicaHighWaterMark *h1,
+ const struct drsuapi_DsReplicaHighWaterMark *h2)
+{
+ if (h1->highest_usn < h2->highest_usn) {
+ return -1;
+ } else if (h1->highest_usn > h2->highest_usn) {
+ return 1;
+ } else if (h1->tmp_highest_usn < h2->tmp_highest_usn) {
+ return -1;
+ } else if (h1->tmp_highest_usn > h2->tmp_highest_usn) {
+ return 1;
+ } else if (h1->reserved_usn < h2->reserved_usn) {
+ return -1;
+ } else if (h1->reserved_usn > h2->reserved_usn) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ build a DsReplicaObjectIdentifier from a ldb msg
+ */
+static struct drsuapi_DsReplicaObjectIdentifier *get_object_identifier(TALLOC_CTX *mem_ctx,
+ const struct ldb_message *msg)
+{
+ struct drsuapi_DsReplicaObjectIdentifier *identifier;
+ struct dom_sid *sid;
+
+ identifier = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
+ if (identifier == NULL) {
+ return NULL;
+ }
+
+ identifier->dn = ldb_dn_alloc_linearized(identifier, msg->dn);
+ identifier->guid = samdb_result_guid(msg, "objectGUID");
+
+ sid = samdb_result_dom_sid(identifier, msg, "objectSid");
+ if (sid) {
+ identifier->sid = *sid;
+ } else {
+ ZERO_STRUCT(identifier->sid);
+ }
+ return identifier;
+}
+
+static int udv_compare(const struct GUID *guid1, struct GUID guid2)
+{
+ return GUID_compare(guid1, &guid2);
+}
+
+/*
+ see if we can filter an attribute using the uptodateness_vector
+ */
+static bool udv_filter(const struct drsuapi_DsReplicaCursorCtrEx *udv,
+ const struct GUID *originating_invocation_id,
+ uint64_t originating_usn)
+{
+ const struct drsuapi_DsReplicaCursor *c;
+ if (udv == NULL) return false;
+ BINARY_ARRAY_SEARCH(udv->cursors, udv->count, source_dsa_invocation_id,
+ originating_invocation_id, udv_compare, c);
+ if (c && originating_usn <= c->highest_usn) {
+ return true;
+ }
+ return false;
+}
+
+static int uint32_t_cmp(uint32_t a1, uint32_t a2)
+{
+ if (a1 == a2) return 0;
+ return a1 > a2 ? 1 : -1;
+}
+
+static int uint32_t_ptr_cmp(uint32_t *a1, uint32_t *a2)
+{
+ if (*a1 == *a2) return 0;
+ return *a1 > *a2 ? 1 : -1;
+}
+
+static WERROR getncchanges_attid_remote_to_local(const struct dsdb_schema *schema,
+ const struct dsdb_syntax_ctx *ctx,
+ enum drsuapi_DsAttributeId remote_attid_as_enum,
+ enum drsuapi_DsAttributeId *local_attid_as_enum,
+ const struct dsdb_attribute **_sa)
+{
+ WERROR werr;
+ const struct dsdb_attribute *sa = NULL;
+
+ if (ctx->pfm_remote == NULL) {
+ DEBUG(7, ("No prefixMap supplied, falling back to local prefixMap.\n"));
+ goto fail;
+ }
+
+ werr = dsdb_attribute_drsuapi_remote_to_local(ctx,
+ remote_attid_as_enum,
+ local_attid_as_enum,
+ _sa);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(3, ("WARNING: Unable to resolve remote attid, falling back to local prefixMap.\n"));
+ goto fail;
+ }
+
+ return werr;
+fail:
+
+ sa = dsdb_attribute_by_attributeID_id(schema, remote_attid_as_enum);
+ if (sa == NULL) {
+ return WERR_DS_DRA_SCHEMA_MISMATCH;
+ } else {
+ if (local_attid_as_enum != NULL) {
+ *local_attid_as_enum = sa->attributeID_id;
+ }
+ if (_sa != NULL) {
+ *_sa = sa;
+ }
+ return WERR_OK;
+ }
+}
+
+static WERROR getncchanges_update_revealed_list(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **msg,
+ struct ldb_dn *object_dn,
+ const struct GUID *object_guid,
+ const struct dsdb_attribute *sa,
+ struct replPropertyMetaData1 *meta_data,
+ struct ldb_message *revealed_users)
+{
+ enum ndr_err_code ndr_err;
+ int ldb_err;
+ char *attr_str = NULL;
+ char *attr_hex = NULL;
+ DATA_BLOB attr_blob;
+ struct ldb_message_element *existing = NULL, *el_add = NULL, *el_del = NULL;
+ const char * const * secret_attributes = ldb_get_opaque(sam_ctx, "LDB_SECRET_ATTRIBUTE_LIST");
+
+ if (!ldb_attr_in_list(secret_attributes,
+ sa->lDAPDisplayName)) {
+ return WERR_OK;
+ }
+
+
+ ndr_err = ndr_push_struct_blob(&attr_blob, mem_ctx, meta_data, (ndr_push_flags_fn_t)ndr_push_replPropertyMetaData1);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ attr_hex = hex_encode_talloc(mem_ctx, attr_blob.data, attr_blob.length);
+ if (attr_hex == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ attr_str = talloc_asprintf(mem_ctx, "B:%zd:%s:%s", attr_blob.length*2, attr_hex, ldb_dn_get_linearized(object_dn));
+ if (attr_str == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ existing = ldb_msg_find_element(revealed_users, "msDS-RevealedUsers");
+ if (existing != NULL) {
+ /* Replace the old value (if one exists) with the current one */
+ struct parsed_dn *link_dns;
+ struct parsed_dn *exact = NULL, *unused = NULL;
+ uint8_t attid[4];
+ DATA_BLOB partial_meta;
+
+ ldb_err = get_parsed_dns_trusted(mem_ctx, existing, &link_dns);
+ if (ldb_err != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ /* Construct a partial metadata blob to match on in the DB */
+ SIVAL(attid, 0, sa->attributeID_id);
+ partial_meta.length = 4;
+ partial_meta.data = attid;
+
+ /* Binary search using GUID and attribute id for uniqueness */
+ ldb_err = parsed_dn_find(sam_ctx, link_dns, existing->num_values,
+ object_guid, object_dn,
+ partial_meta, 4,
+ &exact, &unused,
+ DSDB_SYNTAX_BINARY_DN, true);
+
+ if (ldb_err != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed parsed DN find - %s\n",
+ ldb_errstring(sam_ctx)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if (exact != NULL) {
+ /* Perform some verification of the blob */
+ struct replPropertyMetaData1 existing_meta_data;
+ ndr_err = ndr_pull_struct_blob_all_noalloc(&exact->dsdb_dn->extra_part,
+ &existing_meta_data,
+ (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaData1);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if (existing_meta_data.attid == sa->attributeID_id) {
+ ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_DELETE, &el_del);
+ if (ldb_err != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ el_del->values = talloc_array((*msg)->elements, struct ldb_val, 1);
+ if (el_del->values == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ el_del->values[0] = *exact->v;
+ el_del->num_values = 1;
+ } else {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ }
+ }
+
+ ldb_err = ldb_msg_add_empty(*msg, "msDS-RevealedUsers", LDB_FLAG_MOD_ADD, &el_add);
+ if (ldb_err != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ el_add->values = talloc_array((*msg)->elements, struct ldb_val, 1);
+ if (el_add->values == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+
+ }
+
+ el_add->values[0] = data_blob_string_const(attr_str);
+ el_add->num_values = 1;
+
+ return WERR_OK;
+}
+
+/*
+ * This function filter attributes for build_object based on the
+ * uptodatenessvector and partial attribute set.
+ *
+ * Any secret attributes are forced here for REPL_SECRET, and audited at this
+ * point with msDS-RevealedUsers.
+ */
+static WERROR get_nc_changes_filter_attrs(struct drsuapi_DsReplicaObjectListItemEx *obj,
+ struct replPropertyMetaDataBlob md,
+ struct ldb_context *sam_ctx,
+ const struct ldb_message *msg,
+ const struct GUID *guid,
+ uint32_t *count,
+ uint64_t highest_usn,
+ const struct dsdb_attribute *rdn_sa,
+ struct dsdb_schema *schema,
+ struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector,
+ struct drsuapi_DsPartialAttributeSet *partial_attribute_set,
+ uint32_t *local_pas,
+ uint32_t *attids,
+ bool exop_secret,
+ struct ldb_message **revealed_list_msg,
+ struct ldb_message *existing_revealed_list_msg)
+{
+ uint32_t i, n;
+ WERROR werr;
+ for (n=i=0; i<md.ctr.ctr1.count; i++) {
+ const struct dsdb_attribute *sa;
+ bool force_attribute = false;
+
+ /* if the attribute has not changed, and it is not the
+ instanceType then don't include it */
+ if (md.ctr.ctr1.array[i].local_usn < highest_usn &&
+ !exop_secret &&
+ md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType) continue;
+
+ /* don't include the rDN */
+ if (md.ctr.ctr1.array[i].attid == rdn_sa->attributeID_id) continue;
+
+ sa = dsdb_attribute_by_attributeID_id(schema, md.ctr.ctr1.array[i].attid);
+ if (!sa) {
+ DEBUG(0,(__location__ ": Failed to find attribute in schema for attrid %u mentioned in replPropertyMetaData of %s\n",
+ (unsigned int)md.ctr.ctr1.array[i].attid,
+ ldb_dn_get_linearized(msg->dn)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if (sa->linkID) {
+ struct ldb_message_element *el;
+ el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
+ if (el && el->num_values && dsdb_dn_is_upgraded_link_val(&el->values[0])) {
+ /* don't send upgraded links inline */
+ continue;
+ }
+ }
+
+ if (exop_secret &&
+ !dsdb_attr_in_rodc_fas(sa)) {
+ force_attribute = true;
+ DEBUG(4,("Forcing attribute %s in %s\n",
+ sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
+ werr = getncchanges_update_revealed_list(sam_ctx, obj,
+ revealed_list_msg,
+ msg->dn, guid, sa,
+ &md.ctr.ctr1.array[i],
+ existing_revealed_list_msg);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ }
+
+ /* filter by uptodateness_vector */
+ if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTID_instanceType &&
+ !force_attribute &&
+ udv_filter(uptodateness_vector,
+ &md.ctr.ctr1.array[i].originating_invocation_id,
+ md.ctr.ctr1.array[i].originating_usn)) {
+ continue;
+ }
+
+ /* filter by partial_attribute_set */
+ if (partial_attribute_set && !force_attribute) {
+ uint32_t *result = NULL;
+ BINARY_ARRAY_SEARCH_V(local_pas, partial_attribute_set->num_attids, sa->attributeID_id,
+ uint32_t_cmp, result);
+ if (result == NULL) {
+ continue;
+ }
+ }
+
+ obj->meta_data_ctr->meta_data[n].originating_change_time = md.ctr.ctr1.array[i].originating_change_time;
+ obj->meta_data_ctr->meta_data[n].version = md.ctr.ctr1.array[i].version;
+ obj->meta_data_ctr->meta_data[n].originating_invocation_id = md.ctr.ctr1.array[i].originating_invocation_id;
+ obj->meta_data_ctr->meta_data[n].originating_usn = md.ctr.ctr1.array[i].originating_usn;
+ attids[n] = md.ctr.ctr1.array[i].attid;
+
+ n++;
+ }
+
+ *count = n;
+
+ return WERR_OK;
+}
+
+/*
+ drsuapi_DsGetNCChanges for one object
+*/
+static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItemEx *obj,
+ const struct ldb_message *msg,
+ struct ldb_context *sam_ctx,
+ struct drsuapi_getncchanges_state *getnc_state,
+ struct dsdb_schema *schema,
+ DATA_BLOB *session_key,
+ struct drsuapi_DsGetNCChangesRequest10 *req10,
+ bool force_object_return,
+ uint32_t *local_pas,
+ struct ldb_dn *machine_dn,
+ const struct GUID *guid)
+{
+ const struct ldb_val *md_value;
+ uint32_t i, n;
+ struct replPropertyMetaDataBlob md;
+ uint32_t rid = 0;
+ int ldb_err;
+ enum ndr_err_code ndr_err;
+ uint32_t *attids;
+ const char *rdn;
+ const struct dsdb_attribute *rdn_sa;
+ uint64_t uSNChanged;
+ unsigned int instanceType;
+ struct dsdb_syntax_ctx syntax_ctx;
+ struct ldb_result *res = NULL;
+ WERROR werr;
+ int ret;
+ uint32_t replica_flags = req10->replica_flags;
+ struct drsuapi_DsPartialAttributeSet *partial_attribute_set =
+ req10->partial_attribute_set;
+ struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector =
+ req10->uptodateness_vector;
+ enum drsuapi_DsExtendedOperation extended_op = req10->extended_op;
+ bool is_schema_nc = getnc_state->is_schema_nc;
+ uint64_t highest_usn = getnc_state->min_usn;
+
+ /* make dsdb sytanx context for conversions */
+ dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
+ syntax_ctx.is_schema_nc = is_schema_nc;
+
+ uSNChanged = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
+ instanceType = ldb_msg_find_attr_as_uint(msg, "instanceType", 0);
+ if (instanceType & INSTANCE_TYPE_IS_NC_HEAD) {
+ obj->is_nc_prefix = true;
+ obj->parent_object_guid = NULL;
+ } else {
+ obj->is_nc_prefix = false;
+ obj->parent_object_guid = talloc(obj, struct GUID);
+ if (obj->parent_object_guid == NULL) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ *obj->parent_object_guid = samdb_result_guid(msg, "parentGUID");
+ if (GUID_all_zero(obj->parent_object_guid)) {
+ DEBUG(0,(__location__ ": missing parentGUID for %s\n",
+ ldb_dn_get_linearized(msg->dn)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ }
+ obj->next_object = NULL;
+
+ md_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
+ if (!md_value) {
+ /* nothing to send */
+ return WERR_OK;
+ }
+
+ if (instanceType & INSTANCE_TYPE_UNINSTANT) {
+ /* don't send uninstantiated objects */
+ return WERR_OK;
+ }
+
+ if (uSNChanged <= highest_usn) {
+ /* nothing to send */
+ return WERR_OK;
+ }
+
+ ndr_err = ndr_pull_struct_blob(md_value, obj, &md,
+ (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if (md.version != 1) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ rdn = ldb_dn_get_rdn_name(msg->dn);
+ if (rdn == NULL) {
+ DEBUG(0,(__location__ ": No rDN for %s\n", ldb_dn_get_linearized(msg->dn)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn);
+ if (rdn_sa == NULL) {
+ DEBUG(0,(__location__ ": Can't find dsds_attribute for rDN %s in %s\n",
+ rdn, ldb_dn_get_linearized(msg->dn)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ obj->meta_data_ctr = talloc(obj, struct drsuapi_DsReplicaMetaDataCtr);
+ attids = talloc_array(obj, uint32_t, md.ctr.ctr1.count);
+
+ obj->object.identifier = get_object_identifier(obj, msg);
+ if (obj->object.identifier == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ dom_sid_split_rid(NULL, &obj->object.identifier->sid, NULL, &rid);
+
+ obj->meta_data_ctr->meta_data = talloc_array(obj, struct drsuapi_DsReplicaMetaData, md.ctr.ctr1.count);
+
+ if (extended_op == DRSUAPI_EXOP_REPL_SECRET) {
+ /* Get the existing revealed users for the destination */
+ struct ldb_message *revealed_list_msg = NULL;
+ struct ldb_message *existing_revealed_list_msg = NULL;
+ const char *machine_attrs[] = {
+ "msDS-RevealedUsers",
+ NULL
+ };
+
+ revealed_list_msg = ldb_msg_new(sam_ctx);
+ if (revealed_list_msg == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ revealed_list_msg->dn = machine_dn;
+
+ ret = ldb_transaction_start(sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed transaction start - %s\n",
+ ldb_errstring(sam_ctx)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ ldb_err = dsdb_search_dn(sam_ctx, obj, &res, machine_dn, machine_attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
+ if (ldb_err != LDB_SUCCESS || res->count != 1) {
+ ldb_transaction_cancel(sam_ctx);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ existing_revealed_list_msg = res->msgs[0];
+
+ werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg,
+ guid, &n, highest_usn,
+ rdn_sa, schema,
+ uptodateness_vector,
+ partial_attribute_set, local_pas,
+ attids,
+ true,
+ &revealed_list_msg,
+ existing_revealed_list_msg);
+ if (!W_ERROR_IS_OK(werr)) {
+ ldb_transaction_cancel(sam_ctx);
+ return werr;
+ }
+
+ if (revealed_list_msg != NULL) {
+ ret = ldb_modify(sam_ctx, revealed_list_msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to alter revealed links - %s\n",
+ ldb_errstring(sam_ctx)));
+ ldb_transaction_cancel(sam_ctx);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ }
+
+ ret = ldb_transaction_commit(sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
+ ldb_errstring(sam_ctx)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ } else {
+ werr = get_nc_changes_filter_attrs(obj, md, sam_ctx, msg, guid,
+ &n, highest_usn, rdn_sa,
+ schema, uptodateness_vector,
+ partial_attribute_set, local_pas,
+ attids,
+ false,
+ NULL,
+ NULL);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ }
+
+ /* ignore it if its an empty change. Note that renames always
+ * change the 'name' attribute, so they won't be ignored by
+ * this
+
+ * the force_object_return check is used to force an empty
+ * object return when we timeout in the getncchanges loop.
+ * This allows us to return an empty object, which keeps the
+ * client happy while preventing timeouts
+ */
+ if (n == 0 ||
+ (n == 1 &&
+ attids[0] == DRSUAPI_ATTID_instanceType &&
+ !force_object_return)) {
+ talloc_free(obj->meta_data_ctr);
+ obj->meta_data_ctr = NULL;
+ return WERR_OK;
+ }
+
+ obj->meta_data_ctr->count = n;
+
+ obj->object.flags = DRSUAPI_DS_REPLICA_OBJECT_FROM_MASTER;
+ obj->object.attribute_ctr.num_attributes = obj->meta_data_ctr->count;
+ obj->object.attribute_ctr.attributes = talloc_array(obj, struct drsuapi_DsReplicaAttribute,
+ obj->object.attribute_ctr.num_attributes);
+ if (obj->object.attribute_ctr.attributes == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /*
+ * Note that the meta_data array and the attributes array must
+ * be the same size and in the same order
+ */
+ for (i=0; i<obj->object.attribute_ctr.num_attributes; i++) {
+ struct ldb_message_element *el;
+ const struct dsdb_attribute *sa;
+
+ sa = dsdb_attribute_by_attributeID_id(schema, attids[i]);
+ if (!sa) {
+ DEBUG(0,("Unable to find attributeID %u in schema\n", attids[i]));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
+ if (el == NULL) {
+ /* this happens for attributes that have been removed */
+ DEBUG(5,("No element '%s' for attributeID %u in message\n",
+ sa->lDAPDisplayName, attids[i]));
+ ZERO_STRUCT(obj->object.attribute_ctr.attributes[i]);
+ obj->object.attribute_ctr.attributes[i].attid =
+ dsdb_attribute_get_attid(sa, syntax_ctx.is_schema_nc);
+ } else {
+ werr = sa->syntax->ldb_to_drsuapi(&syntax_ctx, sa, el, obj,
+ &obj->object.attribute_ctr.attributes[i]);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,("Unable to convert %s on %s to DRS object - %s\n",
+ sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn),
+ win_errstr(werr)));
+ return werr;
+ }
+ /* if DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING is set
+ * check if attribute is secret and send a null value
+ */
+ if (replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
+ drsuapi_process_secret_attribute(&obj->object.attribute_ctr.attributes[i],
+ &obj->meta_data_ctr->meta_data[i]);
+ }
+ /* some attributes needs to be encrypted
+ before being sent */
+ werr = drsuapi_encrypt_attribute(obj, session_key, rid,
+ &obj->object.attribute_ctr.attributes[i]);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,("Unable to encrypt %s on %s in DRS object - %s\n",
+ sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn),
+ win_errstr(werr)));
+ return werr;
+ }
+ }
+ if (attids[i] != obj->object.attribute_ctr.attributes[i].attid) {
+ DEBUG(0, ("Unable to replicate attribute %s on %s via DRS, incorrect attributeID: "
+ "0x%08x vs 0x%08x "
+ "Run dbcheck!\n",
+ sa->lDAPDisplayName,
+ ldb_dn_get_linearized(msg->dn),
+ attids[i],
+ obj->object.attribute_ctr.attributes[i].attid));
+ return WERR_DS_DATABASE_ERROR;
+ }
+ }
+
+ return WERR_OK;
+}
+
+/*
+ add one linked attribute from an object to the list of linked
+ attributes in a getncchanges request
+ */
+static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx,
+ struct ldb_context *sam_ctx,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *sa,
+ const struct ldb_message *msg,
+ struct dsdb_dn *dsdb_dn,
+ struct drsuapi_DsReplicaLinkedAttribute **la_list,
+ uint32_t *la_count,
+ bool is_schema_nc)
+{
+ struct drsuapi_DsReplicaLinkedAttribute *la;
+ bool active;
+ NTSTATUS status;
+ WERROR werr;
+
+ (*la_list) = talloc_realloc(mem_ctx, *la_list, struct drsuapi_DsReplicaLinkedAttribute, (*la_count)+1);
+ W_ERROR_HAVE_NO_MEMORY(*la_list);
+
+ la = &(*la_list)[*la_count];
+
+ la->identifier = get_object_identifier(*la_list, msg);
+ W_ERROR_HAVE_NO_MEMORY(la->identifier);
+
+ active = (dsdb_dn_rmd_flags(dsdb_dn->dn) & DSDB_RMD_FLAG_DELETED) == 0;
+
+ if (!active) {
+ /* We have to check that the inactive link still point to an existing object */
+ struct GUID guid;
+ struct ldb_dn *tdn;
+ int ret;
+ const char *v;
+
+ v = ldb_msg_find_attr_as_string(msg, "isDeleted", "FALSE");
+ if (strncmp(v, "TRUE", 4) == 0) {
+ /*
+ * Note: we skip the transmition of the deleted link even if the other part used to
+ * know about it because when we transmit the deletion of the object, the link will
+ * be deleted too due to deletion of object where link points and Windows do so.
+ */
+ if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008_R2) {
+ v = ldb_msg_find_attr_as_string(msg, "isRecycled", "FALSE");
+ /*
+ * On Windows 2008R2 isRecycled is always present even if FL or DL are < FL 2K8R2
+ * if it join an existing domain with deleted objets, it firsts impose to have a
+ * schema with the is-Recycled object and for all deleted objects it adds the isRecycled
+ * either during initial replication or after the getNCChanges.
+ * Behavior of samba has been changed to always have this attribute if it's present in the schema.
+ *
+ * So if FL <2K8R2 isRecycled might be here or not but we don't care, it's meaning less.
+ * If FL >=2K8R2 we are sure that this attribute will be here.
+ * For this kind of forest level we do not return the link if the object is recycled
+ * (isRecycled = true).
+ */
+ if (strncmp(v, "TRUE", 4) == 0) {
+ DEBUG(2, (" object %s is recycled, not returning linked attribute !\n",
+ ldb_dn_get_linearized(msg->dn)));
+ return WERR_OK;
+ }
+ } else {
+ return WERR_OK;
+ }
+ }
+ status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ " Unable to extract GUID in linked attribute '%s' in '%s'\n",
+ sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
+ return ntstatus_to_werror(status);
+ }
+ ret = dsdb_find_dn_by_guid(sam_ctx, mem_ctx, &guid, 0, &tdn);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(2, (" Search of guid %s returned 0 objects, skipping it !\n",
+ GUID_string(mem_ctx, &guid)));
+ return WERR_OK;
+ } else if (ret != LDB_SUCCESS) {
+ DEBUG(0, (__location__ " Search of guid %s failed with error code %d\n",
+ GUID_string(mem_ctx, &guid),
+ ret));
+ return WERR_OK;
+ }
+ }
+ la->attid = dsdb_attribute_get_attid(sa, is_schema_nc);
+ la->flags = active?DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE:0;
+
+ status = dsdb_get_extended_dn_uint32(dsdb_dn->dn, &la->meta_data.version, "RMD_VERSION");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ " No RMD_VERSION in linked attribute '%s' in '%s'\n",
+ sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
+ return ntstatus_to_werror(status);
+ }
+ status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->meta_data.originating_change_time, "RMD_CHANGETIME");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ " No RMD_CHANGETIME in linked attribute '%s' in '%s'\n",
+ sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
+ return ntstatus_to_werror(status);
+ }
+ status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &la->meta_data.originating_invocation_id, "RMD_INVOCID");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ " No RMD_INVOCID in linked attribute '%s' in '%s'\n",
+ sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
+ return ntstatus_to_werror(status);
+ }
+ status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &la->meta_data.originating_usn, "RMD_ORIGINATING_USN");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ " No RMD_ORIGINATING_USN in linked attribute '%s' in '%s'\n",
+ sa->lDAPDisplayName, ldb_dn_get_linearized(msg->dn)));
+ return ntstatus_to_werror(status);
+ }
+
+ status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->originating_add_time, "RMD_ADDTIME");
+ if (!NT_STATUS_IS_OK(status)) {
+ /* this is possible for upgraded links */
+ la->originating_add_time = la->meta_data.originating_change_time;
+ }
+
+ werr = dsdb_dn_la_to_blob(sam_ctx, sa, schema, *la_list, dsdb_dn, &la->value.blob);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ (*la_count)++;
+ return WERR_OK;
+}
+
+
+/*
+ add linked attributes from an object to the list of linked
+ attributes in a getncchanges request
+ */
+static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ bool is_schema_nc,
+ struct dsdb_schema *schema,
+ uint64_t highest_usn,
+ uint32_t replica_flags,
+ const struct ldb_message *msg,
+ struct drsuapi_DsReplicaLinkedAttribute **la_list,
+ uint32_t *la_count,
+ struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
+{
+ unsigned int i;
+ TALLOC_CTX *tmp_ctx = NULL;
+ uint64_t uSNChanged = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
+ bool is_critical = ldb_msg_find_attr_as_bool(msg, "isCriticalSystemObject", false);
+
+ if (replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
+ if (!is_critical) {
+ return WERR_OK;
+ }
+ }
+
+ if (uSNChanged <= highest_usn) {
+ return WERR_OK;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ for (i=0; i<msg->num_elements; i++) {
+ struct ldb_message_element *el = &msg->elements[i];
+ const struct dsdb_attribute *sa;
+ unsigned int j;
+
+ sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
+
+ if (!sa || sa->linkID == 0 || (sa->linkID & 1)) {
+ /* we only want forward links */
+ continue;
+ }
+
+ if (el->num_values && !dsdb_dn_is_upgraded_link_val(&el->values[0])) {
+ /* its an old style link, it will have been
+ * sent in the main replication data */
+ continue;
+ }
+
+ for (j=0; j<el->num_values; j++) {
+ struct dsdb_dn *dsdb_dn;
+ uint64_t local_usn;
+ uint64_t originating_usn;
+ NTSTATUS status, status2;
+ WERROR werr;
+ struct GUID originating_invocation_id;
+
+ dsdb_dn = dsdb_dn_parse(tmp_ctx, sam_ctx, &el->values[j], sa->syntax->ldap_oid);
+ if (dsdb_dn == NULL) {
+ DEBUG(1,(__location__ ": Failed to parse DN for %s in %s\n",
+ el->name, ldb_dn_get_linearized(msg->dn)));
+ talloc_free(tmp_ctx);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ status = dsdb_get_extended_dn_uint64(dsdb_dn->dn, &local_usn, "RMD_LOCAL_USN");
+ if (!NT_STATUS_IS_OK(status)) {
+ /* this can happen for attributes
+ given to us with old style meta
+ data */
+ continue;
+ }
+
+ if (local_usn > uSNChanged) {
+ DEBUG(1,(__location__ ": uSNChanged less than RMD_LOCAL_USN for %s on %s\n",
+ el->name, ldb_dn_get_linearized(msg->dn)));
+ talloc_free(tmp_ctx);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if (local_usn <= highest_usn) {
+ continue;
+ }
+
+ status = dsdb_get_extended_dn_guid(dsdb_dn->dn,
+ &originating_invocation_id,
+ "RMD_INVOCID");
+ status2 = dsdb_get_extended_dn_uint64(dsdb_dn->dn,
+ &originating_usn,
+ "RMD_ORIGINATING_USN");
+
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(status2)) {
+ if (udv_filter(uptodateness_vector,
+ &originating_invocation_id,
+ originating_usn)) {
+ continue;
+ }
+ }
+
+ werr = get_nc_changes_add_la(mem_ctx, sam_ctx, schema,
+ sa, msg, dsdb_dn, la_list,
+ la_count, is_schema_nc);
+ if (!W_ERROR_IS_OK(werr)) {
+ talloc_free(tmp_ctx);
+ return werr;
+ }
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return WERR_OK;
+}
+
+/*
+ fill in the cursors return based on the replUpToDateVector for the ncRoot_dn
+ */
+static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
+ struct ldb_dn *ncRoot_dn,
+ struct drsuapi_DsReplicaCursor2CtrEx *udv)
+{
+ int ret;
+
+ udv->version = 2;
+ udv->reserved1 = 0;
+ udv->reserved2 = 0;
+
+ ret = dsdb_load_udv_v2(sam_ctx, ncRoot_dn, udv, &udv->cursors, &udv->count);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to load UDV for %s - %s\n",
+ ldb_dn_get_linearized(ncRoot_dn), ldb_errstring(sam_ctx)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+
+/* comparison function for linked attributes - see CompareLinks() in
+ * MS-DRSR section 4.1.10.5.17 */
+static int linked_attribute_compare(const struct la_for_sorting *la1,
+ const struct la_for_sorting *la2)
+{
+ int c;
+ c = memcmp(la1->source_guid,
+ la2->source_guid, sizeof(la2->source_guid));
+ if (c != 0) {
+ return c;
+ }
+
+ if (la1->link->attid != la2->link->attid) {
+ return la1->link->attid < la2->link->attid? -1:1;
+ }
+
+ if ((la1->link->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) !=
+ (la2->link->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)) {
+ return (la1->link->flags &
+ DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)? 1:-1;
+ }
+
+ return memcmp(la1->target_guid,
+ la2->target_guid, sizeof(la2->target_guid));
+}
+
+struct drsuapi_changed_objects {
+ struct ldb_dn *dn;
+ struct GUID guid;
+ uint64_t usn;
+};
+
+
+/*
+ sort the objects we send by tree order (Samba 4.5 emulation)
+ */
+static int site_res_cmp_anc_order(struct drsuapi_changed_objects *m1,
+ struct drsuapi_changed_objects *m2,
+ struct drsuapi_getncchanges_state *getnc_state)
+{
+ return ldb_dn_compare(m2->dn, m1->dn);
+}
+
+/*
+ sort the objects we send first by uSNChanged
+ */
+static int site_res_cmp_usn_order(struct drsuapi_changed_objects *m1,
+ struct drsuapi_changed_objects *m2,
+ struct drsuapi_getncchanges_state *getnc_state)
+{
+ if (m1->usn == m2->usn) {
+ return ldb_dn_compare(m2->dn, m1->dn);
+ }
+
+ if (m1->usn < m2->usn) {
+ return -1;
+ }
+
+ return 1;
+}
+
+
+/*
+ handle a DRSUAPI_EXOP_FSMO_RID_ALLOC call
+ */
+static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetNCChangesRequest10 *req10,
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6,
+ struct ldb_dn **rid_manager_dn)
+{
+ struct ldb_dn *req_dn, *ntds_dn = NULL;
+ int ret;
+ struct ldb_context *ldb = b_state->sam_ctx;
+ struct ldb_result *ext_res;
+ struct dsdb_fsmo_extended_op *exop;
+ bool is_us;
+
+ /*
+ steps:
+ - verify that the DN being asked for is the RID Manager DN
+ - verify that we are the RID Manager
+ */
+
+ /* work out who is the RID Manager, also return to caller */
+ ret = samdb_rid_manager_dn(ldb, mem_ctx, rid_manager_dn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, (__location__ ": Failed to find RID Manager object - %s\n", ldb_errstring(ldb)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx,
+ ldb,
+ req10->naming_context,
+ &req_dn,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ DBG_ERR("RID Alloc request for invalid DN %s: %s\n",
+ drs_ObjectIdentifier_to_debug_string(mem_ctx, req10->naming_context),
+ ldb_strerror(ret));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
+ return WERR_OK;
+ }
+
+ if (ldb_dn_compare(req_dn, *rid_manager_dn) != 0) {
+ /* that isn't the RID Manager DN */
+ DBG_ERR("RID Alloc request for wrong DN %s\n",
+ drs_ObjectIdentifier_to_debug_string(mem_ctx,
+ req10->naming_context));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
+ return WERR_OK;
+ }
+
+ /* TODO: make sure ntds_dn is a valid nTDSDSA object */
+ ret = dsdb_find_dn_by_guid(ldb, mem_ctx, &req10->destination_dsa_guid, 0, &ntds_dn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, (__location__ ": Unable to find NTDS object for guid %s - %s\n",
+ GUID_string(mem_ctx, &req10->destination_dsa_guid), ldb_errstring(ldb)));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER;
+ return WERR_OK;
+ }
+
+ /* find the DN of the RID Manager */
+ ret = samdb_reference_dn_is_our_ntdsa(ldb, *rid_manager_dn, "fSMORoleOwner", &is_us);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to find fSMORoleOwner in RID Manager object\n"));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if (!is_us) {
+ /* we're not the RID Manager - go away */
+ DEBUG(0,(__location__ ": RID Alloc request when not RID Manager\n"));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
+ return WERR_OK;
+ }
+
+ exop = talloc(mem_ctx, struct dsdb_fsmo_extended_op);
+ W_ERROR_HAVE_NO_MEMORY(exop);
+
+ exop->fsmo_info = req10->fsmo_info;
+ exop->destination_dsa_guid = req10->destination_dsa_guid;
+
+ ret = ldb_transaction_start(ldb);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed transaction start - %s\n",
+ ldb_errstring(ldb)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ ret = ldb_extended(ldb, DSDB_EXTENDED_ALLOCATE_RID_POOL, exop, &ext_res);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed extended allocation RID pool operation - %s\n",
+ ldb_errstring(ldb)));
+ ldb_transaction_cancel(ldb);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ ret = ldb_transaction_commit(ldb);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
+ ldb_errstring(ldb)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ talloc_free(ext_res);
+
+ DEBUG(2,("Allocated RID pool for server %s\n",
+ GUID_string(mem_ctx, &req10->destination_dsa_guid)));
+
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
+
+ return WERR_OK;
+}
+
+/*
+ handle a DRSUAPI_EXOP_REPL_SECRET call
+ */
+static WERROR getncchanges_repl_secret(struct drsuapi_bind_state *b_state,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetNCChangesRequest10 *req10,
+ struct dom_sid *user_sid,
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6,
+ bool has_get_all_changes,
+ struct ldb_dn **machine_dn)
+{
+ struct drsuapi_DsReplicaObjectIdentifier *ncRoot = req10->naming_context;
+ struct ldb_dn *obj_dn = NULL;
+ struct ldb_message *ntds_msg = NULL;
+ struct ldb_dn *ntds_dn = NULL, *server_dn = NULL;
+ struct ldb_dn *rodc_dn, *krbtgt_link_dn;
+ int ret;
+ const char *ntds_attrs[] = { NULL };
+ const char *rodc_attrs[] = { "msDS-KrbTgtLink",
+ "msDS-NeverRevealGroup",
+ "msDS-RevealOnDemandGroup",
+ "userAccountControl",
+ NULL };
+ const char *obj_attrs[] = { "tokenGroups", "objectSid", "UserAccountControl", "msDS-KrbTgtLinkBL", NULL };
+ struct ldb_result *rodc_res = NULL, *obj_res = NULL;
+ WERROR werr;
+ struct GUID_txt_buf guid_buf;
+
+ DEBUG(3,(__location__ ": DRSUAPI_EXOP_REPL_SECRET extended op on %s\n",
+ drs_ObjectIdentifier_to_debug_string(mem_ctx, ncRoot)));
+
+ /*
+ * we need to work out if we will allow this DC to
+ * replicate the secrets for this object
+ *
+ * see 4.1.10.5.14 GetRevealSecretsPolicyForUser for details
+ * of this function
+ */
+
+ if (b_state->sam_ctx_system == NULL) {
+ /* this operation needs system level access */
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_ACCESS_DENIED;
+ return WERR_DS_DRA_ACCESS_DENIED;
+ }
+
+ /*
+ * Before we accept or deny, fetch the machine DN for the destination
+ * DSA GUID.
+ *
+ * If we are the RODC, we will check that this matches the SID.
+ */
+ ret = samdb_get_ntds_obj_by_guid(mem_ctx,
+ b_state->sam_ctx_system,
+ &req10->destination_dsa_guid,
+ ntds_attrs,
+ &ntds_msg);
+ if (ret != LDB_SUCCESS) {
+ goto dest_dsa_error;
+ }
+
+ ntds_dn = ntds_msg->dn;
+
+ server_dn = ldb_dn_get_parent(mem_ctx, ntds_dn);
+ if (server_dn == NULL) {
+ goto failed;
+ }
+
+ ret = samdb_reference_dn(b_state->sam_ctx_system, mem_ctx, server_dn,
+ "serverReference", machine_dn);
+
+ if (ret != LDB_SUCCESS) {
+ goto dest_dsa_error;
+ }
+
+ /*
+ * In MS-DRSR.pdf 5.99 IsGetNCChangesPermissionGranted
+ *
+ * The pseudo code indicate
+ * revealsecrets = true
+ * if IsRevealSecretRequest(msgIn) then
+ * if AccessCheckCAR(ncRoot, Ds-Replication-Get-Changes-All) = false
+ * then
+ * if (msgIn.ulExtendedOp = EXOP_REPL_SECRETS) then
+ * <... check if this account is ok to be replicated on this DC ...>
+ * <... and if not reveal secrets = no ...>
+ * else
+ * reveal secrets = false
+ * endif
+ * endif
+ * endif
+ *
+ * Which basically means that if you have GET_ALL_CHANGES rights (~== RWDC)
+ * then you can do EXOP_REPL_SECRETS
+ */
+ ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx,
+ b_state->sam_ctx_system,
+ ncRoot,
+ &obj_dn,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ DBG_ERR("RevealSecretRequest for for invalid DN %s\n",
+ drs_ObjectIdentifier_to_debug_string(mem_ctx, ncRoot));
+ goto failed;
+ }
+
+ if (!ldb_dn_validate(obj_dn)) goto failed;
+
+ if (has_get_all_changes) {
+ goto allowed;
+ }
+
+ rodc_dn = ldb_dn_new_fmt(mem_ctx, b_state->sam_ctx_system, "<SID=%s>",
+ dom_sid_string(mem_ctx, user_sid));
+ if (!ldb_dn_validate(rodc_dn)) goto failed;
+
+ /*
+ * do the two searches we need
+ * We need DSDB_SEARCH_SHOW_EXTENDED_DN as we get a SID lists
+ * out of the extended DNs
+ */
+ ret = dsdb_search_dn(b_state->sam_ctx_system, mem_ctx, &rodc_res, rodc_dn, rodc_attrs,
+ DSDB_SEARCH_SHOW_EXTENDED_DN);
+ if (ret != LDB_SUCCESS || rodc_res->count != 1) goto failed;
+
+ ret = dsdb_search_dn(b_state->sam_ctx_system, mem_ctx, &obj_res, obj_dn, obj_attrs, 0);
+ if (ret != LDB_SUCCESS || obj_res->count != 1) goto failed;
+
+ /*
+ * Must be an RODC account at this point, verify machine DN matches the
+ * SID account
+ */
+ if (ldb_dn_compare(rodc_res->msgs[0]->dn, *machine_dn) != 0) {
+ goto denied;
+ }
+
+ /* an RODC is allowed to get its own krbtgt account secrets */
+ krbtgt_link_dn = samdb_result_dn(b_state->sam_ctx_system, mem_ctx,
+ rodc_res->msgs[0], "msDS-KrbTgtLink", NULL);
+ if (krbtgt_link_dn != NULL &&
+ ldb_dn_compare(obj_dn, krbtgt_link_dn) == 0) {
+ goto allowed;
+ }
+
+ werr = samdb_confirm_rodc_allowed_to_repl_to(b_state->sam_ctx_system,
+ user_sid,
+ rodc_res->msgs[0],
+ obj_res->msgs[0]);
+
+ if (W_ERROR_IS_OK(werr)) {
+ goto allowed;
+ }
+
+ /* default deny */
+denied:
+ DEBUG(2,(__location__ ": Denied single object with secret replication for %s by RODC %s\n",
+ ldb_dn_get_linearized(obj_dn), ldb_dn_get_linearized(rodc_res->msgs[0]->dn)));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
+ return WERR_DS_DRA_SECRETS_DENIED;
+
+allowed:
+ DEBUG(2,(__location__ ": Allowed single object with secret replication for %s by %s %s\n",
+ ldb_dn_get_linearized(obj_dn), has_get_all_changes?"RWDC":"RODC",
+ ldb_dn_get_linearized(*machine_dn)));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
+ req10->highwatermark.highest_usn = 0;
+ return WERR_OK;
+
+failed:
+ DEBUG(2,(__location__ ": Failed single secret replication for %s by RODC %s\n",
+ ldb_dn_get_linearized(obj_dn), dom_sid_string(mem_ctx, user_sid)));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
+ return WERR_DS_DRA_BAD_DN;
+
+dest_dsa_error:
+ DBG_WARNING("Failed secret replication for %s by RODC %s as dest_dsa_guid %s is invalid\n",
+ ldb_dn_get_linearized(obj_dn),
+ dom_sid_string(mem_ctx, user_sid),
+ GUID_buf_string(&req10->destination_dsa_guid,
+ &guid_buf));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_NONE;
+ return WERR_DS_DRA_DB_ERROR;
+}
+
+/*
+ handle a DRSUAPI_EXOP_REPL_OBJ call
+ */
+static WERROR getncchanges_repl_obj(struct drsuapi_bind_state *b_state,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetNCChangesRequest10 *req10,
+ struct dom_sid *user_sid,
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6)
+{
+ struct drsuapi_DsReplicaObjectIdentifier *ncRoot = req10->naming_context;
+
+ DEBUG(3,(__location__ ": DRSUAPI_EXOP_REPL_OBJ extended op on %s\n",
+ drs_ObjectIdentifier_to_debug_string(mem_ctx, ncRoot)));
+
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
+ return WERR_OK;
+}
+
+
+/*
+ handle DRSUAPI_EXOP_FSMO_REQ_ROLE,
+ DRSUAPI_EXOP_FSMO_RID_REQ_ROLE,
+ and DRSUAPI_EXOP_FSMO_REQ_PDC calls
+ */
+static WERROR getncchanges_change_master(struct drsuapi_bind_state *b_state,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetNCChangesRequest10 *req10,
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6)
+{
+ struct ldb_dn *req_dn, *ntds_dn;
+ int ret;
+ unsigned int i;
+ struct ldb_context *ldb = b_state->sam_ctx;
+ struct ldb_message *msg;
+ bool is_us;
+
+ /*
+ steps:
+ - verify that the client dn exists
+ - verify that we are the current master
+ */
+
+ ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx, ldb, req10->naming_context,
+ &req_dn, NULL);
+ if (ret != LDB_SUCCESS) {
+ /* that is not a valid dn */
+ DBG_ERR("FSMO role transfer request for invalid DN %s: %s\n",
+ drs_ObjectIdentifier_to_debug_string(mem_ctx, req10->naming_context),
+ ldb_strerror(ret));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
+ return WERR_OK;
+ }
+
+ /* find the DN of the current role owner */
+ ret = samdb_reference_dn_is_our_ntdsa(ldb, req_dn, "fSMORoleOwner", &is_us);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to find fSMORoleOwner in RID Manager object\n"));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if (!is_us) {
+ /* we're not the RID Manager or role owner - go away */
+ DEBUG(0,(__location__ ": FSMO role or RID manager transfer owner request when not role owner\n"));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
+ return WERR_OK;
+ }
+
+ /* change the current master */
+ msg = ldb_msg_new(ldb);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+ ret = drs_ObjectIdentifier_to_dn_and_nc_root(msg, ldb, req10->naming_context,
+ &msg->dn, NULL);
+ if (ret != LDB_SUCCESS) {
+ /* that is not a valid dn */
+ DBG_ERR("FSMO role transfer request for invalid DN %s: %s\n",
+ drs_ObjectIdentifier_to_debug_string(mem_ctx, req10->naming_context),
+ ldb_strerror(ret));
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
+ return WERR_OK;
+ }
+
+ /* TODO: make sure ntds_dn is a valid nTDSDSA object */
+ ret = dsdb_find_dn_by_guid(ldb, msg, &req10->destination_dsa_guid, 0, &ntds_dn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, (__location__ ": Unable to find NTDS object for guid %s - %s\n",
+ GUID_string(mem_ctx, &req10->destination_dsa_guid), ldb_errstring(ldb)));
+ talloc_free(msg);
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER;
+ return WERR_OK;
+ }
+
+ ret = ldb_msg_add_string(msg, "fSMORoleOwner", ldb_dn_get_linearized(ntds_dn));
+ if (ret != 0) {
+ talloc_free(msg);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = ldb_transaction_start(ldb);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed transaction start - %s\n",
+ ldb_errstring(ldb)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ ret = ldb_modify(ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to change current owner - %s\n",
+ ldb_errstring(ldb)));
+ ldb_transaction_cancel(ldb);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ ret = ldb_transaction_commit(ldb);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
+ ldb_errstring(ldb)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
+
+ return WERR_OK;
+}
+
+/*
+ see if this getncchanges request includes a request to reveal secret information
+ */
+static WERROR dcesrv_drsuapi_is_reveal_secrets_request(struct drsuapi_bind_state *b_state,
+ struct drsuapi_DsGetNCChangesRequest10 *req10,
+ struct dsdb_schema_prefixmap *pfm_remote,
+ bool *is_secret_request)
+{
+ enum drsuapi_DsExtendedOperation exop;
+ uint32_t i;
+ struct dsdb_schema *schema;
+ struct dsdb_syntax_ctx syntax_ctx;
+
+ *is_secret_request = true;
+
+ exop = req10->extended_op;
+
+ switch (exop) {
+ case DRSUAPI_EXOP_FSMO_REQ_ROLE:
+ case DRSUAPI_EXOP_FSMO_RID_ALLOC:
+ case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
+ case DRSUAPI_EXOP_FSMO_REQ_PDC:
+ case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
+ /* FSMO exops can reveal secrets */
+ *is_secret_request = true;
+ return WERR_OK;
+ case DRSUAPI_EXOP_REPL_SECRET:
+ case DRSUAPI_EXOP_REPL_OBJ:
+ case DRSUAPI_EXOP_NONE:
+ break;
+ }
+
+ if (req10->replica_flags & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
+ *is_secret_request = false;
+ return WERR_OK;
+ }
+
+ if (exop == DRSUAPI_EXOP_REPL_SECRET ||
+ req10->partial_attribute_set == NULL) {
+ /* they want secrets */
+ *is_secret_request = true;
+ return WERR_OK;
+ }
+
+ schema = dsdb_get_schema(b_state->sam_ctx, NULL);
+ dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema);
+ syntax_ctx.pfm_remote = pfm_remote;
+
+ /* check the attributes they asked for */
+ for (i=0; i<req10->partial_attribute_set->num_attids; i++) {
+ const struct dsdb_attribute *sa;
+ WERROR werr = getncchanges_attid_remote_to_local(schema,
+ &syntax_ctx,
+ req10->partial_attribute_set->attids[i],
+ NULL,
+ &sa);
+
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
+ req10->partial_attribute_set->attids[i], win_errstr(werr)));
+ return werr;
+ }
+
+ if (!dsdb_attr_in_rodc_fas(sa)) {
+ *is_secret_request = true;
+ return WERR_OK;
+ }
+ }
+
+ if (req10->partial_attribute_set_ex) {
+ /* check the extended attributes they asked for */
+ for (i=0; i<req10->partial_attribute_set_ex->num_attids; i++) {
+ const struct dsdb_attribute *sa;
+ WERROR werr = getncchanges_attid_remote_to_local(schema,
+ &syntax_ctx,
+ req10->partial_attribute_set_ex->attids[i],
+ NULL,
+ &sa);
+
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
+ req10->partial_attribute_set_ex->attids[i], win_errstr(werr)));
+ return werr;
+ }
+
+ if (!dsdb_attr_in_rodc_fas(sa)) {
+ *is_secret_request = true;
+ return WERR_OK;
+ }
+ }
+ }
+
+ *is_secret_request = false;
+ return WERR_OK;
+}
+
+/*
+ see if this getncchanges request is only for attributes in the GC
+ partial attribute set
+ */
+static WERROR dcesrv_drsuapi_is_gc_pas_request(struct drsuapi_bind_state *b_state,
+ struct drsuapi_DsGetNCChangesRequest10 *req10,
+ struct dsdb_schema_prefixmap *pfm_remote,
+ bool *is_gc_pas_request)
+{
+ enum drsuapi_DsExtendedOperation exop;
+ uint32_t i;
+ struct dsdb_schema *schema;
+ struct dsdb_syntax_ctx syntax_ctx;
+
+ exop = req10->extended_op;
+
+ switch (exop) {
+ case DRSUAPI_EXOP_FSMO_REQ_ROLE:
+ case DRSUAPI_EXOP_FSMO_RID_ALLOC:
+ case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
+ case DRSUAPI_EXOP_FSMO_REQ_PDC:
+ case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
+ case DRSUAPI_EXOP_REPL_SECRET:
+ *is_gc_pas_request = false;
+ return WERR_OK;
+ case DRSUAPI_EXOP_REPL_OBJ:
+ case DRSUAPI_EXOP_NONE:
+ break;
+ }
+
+ if (req10->partial_attribute_set == NULL) {
+ /* they want it all */
+ *is_gc_pas_request = false;
+ return WERR_OK;
+ }
+
+ schema = dsdb_get_schema(b_state->sam_ctx, NULL);
+ dsdb_syntax_ctx_init(&syntax_ctx, b_state->sam_ctx, schema);
+ syntax_ctx.pfm_remote = pfm_remote;
+
+ /* check the attributes they asked for */
+ for (i=0; i<req10->partial_attribute_set->num_attids; i++) {
+ const struct dsdb_attribute *sa;
+ WERROR werr = getncchanges_attid_remote_to_local(schema,
+ &syntax_ctx,
+ req10->partial_attribute_set->attids[i],
+ NULL,
+ &sa);
+
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
+ req10->partial_attribute_set->attids[i], win_errstr(werr)));
+ return werr;
+ }
+
+ if (!sa->isMemberOfPartialAttributeSet) {
+ *is_gc_pas_request = false;
+ return WERR_OK;
+ }
+ }
+
+ if (req10->partial_attribute_set_ex) {
+ /* check the extended attributes they asked for */
+ for (i=0; i<req10->partial_attribute_set_ex->num_attids; i++) {
+ const struct dsdb_attribute *sa;
+ WERROR werr = getncchanges_attid_remote_to_local(schema,
+ &syntax_ctx,
+ req10->partial_attribute_set_ex->attids[i],
+ NULL,
+ &sa);
+
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,(__location__": attid 0x%08X not found: %s\n",
+ req10->partial_attribute_set_ex->attids[i], win_errstr(werr)));
+ return werr;
+ }
+
+ if (!sa->isMemberOfPartialAttributeSet) {
+ *is_gc_pas_request = false;
+ return WERR_OK;
+ }
+ }
+ }
+
+ *is_gc_pas_request = true;
+ return WERR_OK;
+}
+
+
+/*
+ map from req8 to req10
+ */
+static struct drsuapi_DsGetNCChangesRequest10 *
+getncchanges_map_req8(TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetNCChangesRequest8 *req8)
+{
+ struct drsuapi_DsGetNCChangesRequest10 *req10 = talloc_zero(mem_ctx,
+ struct drsuapi_DsGetNCChangesRequest10);
+ if (req10 == NULL) {
+ return NULL;
+ }
+
+ req10->destination_dsa_guid = req8->destination_dsa_guid;
+ req10->source_dsa_invocation_id = req8->source_dsa_invocation_id;
+ req10->naming_context = req8->naming_context;
+ req10->highwatermark = req8->highwatermark;
+ req10->uptodateness_vector = req8->uptodateness_vector;
+ req10->replica_flags = req8->replica_flags;
+ req10->max_object_count = req8->max_object_count;
+ req10->max_ndr_size = req8->max_ndr_size;
+ req10->extended_op = req8->extended_op;
+ req10->fsmo_info = req8->fsmo_info;
+ req10->partial_attribute_set = req8->partial_attribute_set;
+ req10->partial_attribute_set_ex = req8->partial_attribute_set_ex;
+ req10->mapping_ctr = req8->mapping_ctr;
+
+ return req10;
+}
+
+static const char *collect_objects_attrs[] = { "uSNChanged",
+ "objectGUID" ,
+ NULL };
+
+/**
+ * Collects object for normal replication cycle.
+ */
+static WERROR getncchanges_collect_objects(struct drsuapi_bind_state *b_state,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_getncchanges_state *getnc_state,
+ struct drsuapi_DsGetNCChangesRequest10 *req10,
+ struct ldb_dn *search_dn,
+ const char *extra_filter,
+ struct ldb_result **search_res)
+{
+ int ret;
+ char* search_filter;
+ enum ldb_scope scope = LDB_SCOPE_SUBTREE;
+ bool critical_only = false;
+
+ if (req10->replica_flags & DRSUAPI_DRS_CRITICAL_ONLY) {
+ critical_only = true;
+ }
+
+ if (req10->extended_op == DRSUAPI_EXOP_REPL_OBJ ||
+ req10->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
+ scope = LDB_SCOPE_BASE;
+ critical_only = false;
+ }
+
+ /* Construct response. */
+ search_filter = talloc_asprintf(mem_ctx,
+ "(uSNChanged>=%llu)",
+ (unsigned long long)(getnc_state->min_usn+1));
+
+ if (extra_filter) {
+ search_filter = talloc_asprintf(mem_ctx, "(&%s(%s))", search_filter, extra_filter);
+ }
+
+ if (critical_only) {
+ search_filter = talloc_asprintf(mem_ctx,
+ "(&%s(isCriticalSystemObject=TRUE))",
+ search_filter);
+ }
+
+ if (req10->replica_flags & DRSUAPI_DRS_ASYNC_REP) {
+ scope = LDB_SCOPE_BASE;
+ }
+
+ if (!search_dn) {
+ search_dn = getnc_state->ncRoot_dn;
+ }
+
+ DEBUG(2,(__location__ ": getncchanges on %s using filter %s\n",
+ ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter));
+ ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, getnc_state, search_res,
+ search_dn, scope,
+ collect_objects_attrs,
+ search_filter);
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+/**
+ * Collects object for normal replication cycle.
+ */
+static WERROR getncchanges_collect_objects_exop(struct drsuapi_bind_state *b_state,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_getncchanges_state *getnc_state,
+ struct drsuapi_DsGetNCChangesRequest10 *req10,
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6,
+ struct ldb_dn *search_dn,
+ const char *extra_filter,
+ struct ldb_result **search_res)
+{
+ /* we have nothing to do in case of ex-op failure */
+ if (ctr6->extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
+ return WERR_OK;
+ }
+
+ switch (req10->extended_op) {
+ case DRSUAPI_EXOP_FSMO_RID_ALLOC:
+ {
+ int ret;
+ struct ldb_dn *ntds_dn = NULL;
+ struct ldb_dn *server_dn = NULL;
+ struct ldb_dn *machine_dn = NULL;
+ struct ldb_dn *rid_set_dn = NULL;
+ struct ldb_result *search_res2 = NULL;
+ struct ldb_result *search_res3 = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ /* get RID manager, RID set and server DN (in that order) */
+
+ /* This first search will get the RID Manager */
+ ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
+ search_res,
+ search_dn, LDB_SCOPE_BASE,
+ collect_objects_attrs,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Manager object %s - %s",
+ ldb_dn_get_linearized(search_dn),
+ ldb_errstring(b_state->sam_ctx)));
+ TALLOC_FREE(frame);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if ((*search_res)->count != 1) {
+ DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Manager object %s - %u objects returned",
+ ldb_dn_get_linearized(search_dn),
+ (*search_res)->count));
+ TALLOC_FREE(frame);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ /* Now extend it to the RID set */
+
+ /* Find the computer account DN for the destination
+ * dsa GUID specified */
+
+ ret = dsdb_find_dn_by_guid(b_state->sam_ctx, frame,
+ &req10->destination_dsa_guid, 0,
+ &ntds_dn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Unable to find NTDS object for guid %s - %s\n",
+ GUID_string(frame,
+ &req10->destination_dsa_guid),
+ ldb_errstring(b_state->sam_ctx)));
+ TALLOC_FREE(frame);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ server_dn = ldb_dn_get_parent(frame, ntds_dn);
+ if (!server_dn) {
+ TALLOC_FREE(frame);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ ret = samdb_reference_dn(b_state->sam_ctx, frame, server_dn,
+ "serverReference", &machine_dn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to find serverReference in %s - %s",
+ ldb_dn_get_linearized(server_dn),
+ ldb_errstring(b_state->sam_ctx)));
+ TALLOC_FREE(frame);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ ret = samdb_reference_dn(b_state->sam_ctx, frame, machine_dn,
+ "rIDSetReferences", &rid_set_dn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to find rIDSetReferences in %s - %s",
+ ldb_dn_get_linearized(server_dn),
+ ldb_errstring(b_state->sam_ctx)));
+ TALLOC_FREE(frame);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+
+ /* This first search will get the RID Manager, now get the RID set */
+ ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
+ &search_res2,
+ rid_set_dn, LDB_SCOPE_BASE,
+ collect_objects_attrs,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Set object %s - %s",
+ ldb_dn_get_linearized(rid_set_dn),
+ ldb_errstring(b_state->sam_ctx)));
+ TALLOC_FREE(frame);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if (search_res2->count != 1) {
+ DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get RID Set object %s - %u objects returned",
+ ldb_dn_get_linearized(rid_set_dn),
+ search_res2->count));
+ TALLOC_FREE(frame);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ /* Finally get the server DN */
+ ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, frame,
+ &search_res3,
+ machine_dn, LDB_SCOPE_BASE,
+ collect_objects_attrs,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get server object %s - %s",
+ ldb_dn_get_linearized(server_dn),
+ ldb_errstring(b_state->sam_ctx)));
+ TALLOC_FREE(frame);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if (search_res3->count != 1) {
+ DEBUG(1, ("DRSUAPI_EXOP_FSMO_RID_ALLOC: Failed to get server object %s - %u objects returned",
+ ldb_dn_get_linearized(server_dn),
+ search_res3->count));
+ TALLOC_FREE(frame);
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ /* Now extend the original search_res with these answers */
+ (*search_res)->count = 3;
+
+ (*search_res)->msgs = talloc_realloc(frame, (*search_res)->msgs,
+ struct ldb_message *,
+ (*search_res)->count);
+ if ((*search_res)->msgs == NULL) {
+ TALLOC_FREE(frame);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+
+ talloc_steal(mem_ctx, *search_res);
+ (*search_res)->msgs[1] =
+ talloc_steal((*search_res)->msgs, search_res2->msgs[0]);
+ (*search_res)->msgs[2] =
+ talloc_steal((*search_res)->msgs, search_res3->msgs[0]);
+
+ TALLOC_FREE(frame);
+ return WERR_OK;
+ }
+ default:
+ /* TODO: implement extended op specific collection
+ * of objects. Right now we just normal procedure
+ * for collecting objects */
+ return getncchanges_collect_objects(b_state,
+ mem_ctx,
+ getnc_state,
+ req10,
+ search_dn,
+ extra_filter,
+ search_res);
+ }
+}
+
+static void dcesrv_drsuapi_update_highwatermark(const struct ldb_message *msg,
+ uint64_t max_usn,
+ struct drsuapi_DsReplicaHighWaterMark *hwm)
+{
+ uint64_t uSN = ldb_msg_find_attr_as_uint64(msg, "uSNChanged", 0);
+
+ if (uSN > max_usn) {
+ /*
+ * Only report the max_usn we had at the start
+ * of the replication cycle.
+ *
+ * If this object has changed lately we better
+ * let the destination dsa refetch the change.
+ * This is better than the risk of loosing some
+ * objects or linked attributes.
+ */
+ return;
+ }
+
+ if (uSN <= hwm->tmp_highest_usn) {
+ return;
+ }
+
+ hwm->tmp_highest_usn = uSN;
+ hwm->reserved_usn = 0;
+}
+
+/**
+ * Adds an object's GUID to the cache of objects already sent.
+ * This avoids us sending the same object multiple times when
+ * the GetNCChanges request uses a flag like GET_ANC.
+ */
+static WERROR dcesrv_drsuapi_obj_cache_add(struct db_context *obj_cache,
+ const struct GUID *guid)
+{
+ enum ndr_err_code ndr_err;
+ uint8_t guid_buf[DRS_GUID_SIZE] = { 0, };
+ DATA_BLOB b = {
+ .data = guid_buf,
+ .length = sizeof(guid_buf),
+ };
+ TDB_DATA key = {
+ .dptr = b.data,
+ .dsize = b.length,
+ };
+ TDB_DATA val = {
+ .dptr = NULL,
+ .dsize = 0,
+ };
+ NTSTATUS status;
+
+ ndr_err = ndr_push_struct_into_fixed_blob(&b, guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ status = dbwrap_store(obj_cache, key, val, TDB_REPLACE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+/**
+ * Checks if the object with the GUID specified already exists in the
+ * object cache, i.e. it's already been sent in a GetNCChanges response.
+ */
+static WERROR dcesrv_drsuapi_obj_cache_exists(struct db_context *obj_cache,
+ const struct GUID *guid)
+{
+ enum ndr_err_code ndr_err;
+ uint8_t guid_buf[DRS_GUID_SIZE] = { 0, };
+ DATA_BLOB b = {
+ .data = guid_buf,
+ .length = sizeof(guid_buf),
+ };
+ TDB_DATA key = {
+ .dptr = b.data,
+ .dsize = b.length,
+ };
+ bool exists;
+
+ ndr_err = ndr_push_struct_into_fixed_blob(&b, guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ exists = dbwrap_exists(obj_cache, key);
+ if (!exists) {
+ return WERR_OBJECT_NOT_FOUND;
+ }
+
+ return WERR_OBJECT_NAME_EXISTS;
+}
+
+/**
+ * Copies the la_list specified into a sorted array, ready to be sent in a
+ * GetNCChanges response.
+ */
+static WERROR getncchanges_get_sorted_array(const struct drsuapi_DsReplicaLinkedAttribute *la_list,
+ const uint32_t link_count,
+ struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct dsdb_schema *schema,
+ struct la_for_sorting **ret_array)
+{
+ int j;
+ struct la_for_sorting *guid_array;
+ WERROR werr = WERR_OK;
+
+ *ret_array = NULL;
+ guid_array = talloc_array(mem_ctx, struct la_for_sorting, link_count);
+ if (guid_array == NULL) {
+ DEBUG(0, ("Out of memory allocating %u linked attributes for sorting", link_count));
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ for (j = 0; j < link_count; j++) {
+
+ /* we need to get the target GUIDs to compare */
+ struct dsdb_dn *dn;
+ const struct drsuapi_DsReplicaLinkedAttribute *la = &la_list[j];
+ const struct dsdb_attribute *schema_attrib;
+ const struct ldb_val *target_guid;
+ DATA_BLOB source_guid;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ schema_attrib = dsdb_attribute_by_attributeID_id(schema, la->attid);
+
+ werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, frame, la->value.blob, &dn);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,(__location__ ": Bad la blob in sort\n"));
+ TALLOC_FREE(frame);
+ return werr;
+ }
+
+ /* Extract the target GUID in NDR form */
+ target_guid = ldb_dn_get_extended_component(dn->dn, "GUID");
+ if (target_guid == NULL
+ || target_guid->length != sizeof(guid_array[0].target_guid)) {
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ } else {
+ /* Repack the source GUID as NDR for sorting */
+ status = GUID_to_ndr_blob(&la->identifier->guid,
+ frame,
+ &source_guid);
+ }
+
+ if (!NT_STATUS_IS_OK(status)
+ || source_guid.length != sizeof(guid_array[0].source_guid)) {
+ DEBUG(0,(__location__ ": Bad la guid in sort\n"));
+ TALLOC_FREE(frame);
+ return ntstatus_to_werror(status);
+ }
+
+ guid_array[j].link = &la_list[j];
+ memcpy(guid_array[j].target_guid, target_guid->data,
+ sizeof(guid_array[j].target_guid));
+ memcpy(guid_array[j].source_guid, source_guid.data,
+ sizeof(guid_array[j].source_guid));
+ TALLOC_FREE(frame);
+ }
+
+ TYPESAFE_QSORT(guid_array, link_count, linked_attribute_compare);
+
+ *ret_array = guid_array;
+
+ return werr;
+}
+
+
+/**
+ * Adds any ancestor/parent objects of the child_obj specified.
+ * This is needed when the GET_ANC flag is specified in the request.
+ * @param new_objs if parents are added, this gets updated to point to a chain
+ * of parent objects (with the parents first and the child last)
+ */
+static WERROR getncchanges_add_ancestors(const struct GUID *parent_object_guid,
+ struct ldb_dn *child_dn,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_context *sam_ctx,
+ struct drsuapi_getncchanges_state *getnc_state,
+ struct dsdb_schema *schema,
+ DATA_BLOB *session_key,
+ struct drsuapi_DsGetNCChangesRequest10 *req10,
+ uint32_t *local_pas,
+ struct ldb_dn *machine_dn,
+ struct drsuapi_DsReplicaObjectListItemEx **new_objs)
+{
+ int ret;
+ const struct GUID *next_anc_guid = NULL;
+ WERROR werr = WERR_OK;
+ static const char * const msg_attrs[] = {
+ "*",
+ "nTSecurityDescriptor",
+ "parentGUID",
+ "replPropertyMetaData",
+ DSDB_SECRET_ATTRIBUTES,
+ NULL };
+
+ next_anc_guid = parent_object_guid;
+
+ while (next_anc_guid != NULL) {
+ struct drsuapi_DsReplicaObjectListItemEx *anc_obj = NULL;
+ struct ldb_message *anc_msg = NULL;
+ struct ldb_result *anc_res = NULL;
+ struct ldb_dn *anc_dn = NULL;
+
+ /*
+ * For the GET_ANC case (but not the 'send NC root
+ * first' case), don't send an object twice.
+ *
+ * (If we've sent the object, then we've also sent all
+ * its parents as well)
+ */
+ if (getnc_state->obj_cache) {
+ werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
+ next_anc_guid);
+ if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
+ return WERR_OK;
+ }
+ if (W_ERROR_IS_OK(werr)) {
+ return WERR_INTERNAL_ERROR;
+ }
+ if (!W_ERROR_EQUAL(werr, WERR_OBJECT_NOT_FOUND)) {
+ return werr;
+ }
+ }
+
+ anc_obj = talloc_zero(mem_ctx,
+ struct drsuapi_DsReplicaObjectListItemEx);
+ if (anc_obj == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ anc_dn = ldb_dn_new_fmt(anc_obj, sam_ctx, "<GUID=%s>",
+ GUID_string(anc_obj, next_anc_guid));
+ if (anc_dn == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ret = drsuapi_search_with_extended_dn(sam_ctx, anc_obj,
+ &anc_res, anc_dn,
+ LDB_SCOPE_BASE,
+ msg_attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ const char *anc_str = NULL;
+ const char *obj_str = NULL;
+
+ anc_str = ldb_dn_get_extended_linearized(anc_obj,
+ anc_dn,
+ 1);
+ obj_str = ldb_dn_get_extended_linearized(anc_obj,
+ child_dn,
+ 1);
+
+ DBG_ERR("getncchanges: failed to fetch ANC "
+ "DN %s for DN %s - %s\n",
+ anc_str, obj_str, ldb_errstring(sam_ctx));
+ return WERR_DS_DRA_INCONSISTENT_DIT;
+ }
+
+ anc_msg = anc_res->msgs[0];
+
+ werr = get_nc_changes_build_object(anc_obj, anc_msg,
+ sam_ctx,
+ getnc_state,
+ schema, session_key,
+ req10,
+ false, /* force_object_return */
+ local_pas,
+ machine_dn,
+ next_anc_guid);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ /*
+ * Regardless of whether we actually use it or not,
+ * we add it to the cache so we don't look at it again
+ *
+ * The only time we are here without
+ * getnc_state->obj_cache is for the forced addition
+ * of the NC root to the start of the reply, this we
+ * want to add each and every call..
+ */
+ if (getnc_state->obj_cache) {
+ werr = dcesrv_drsuapi_obj_cache_add(getnc_state->obj_cache,
+ next_anc_guid);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ }
+
+ /*
+ * Any ancestors which are below the highwatermark
+ * or uptodateness_vector shouldn't be added,
+ * but we still look further up the
+ * tree for ones which have been changed recently.
+ */
+ if (anc_obj->meta_data_ctr != NULL) {
+
+ /*
+ * prepend the parent to the list so that the client-side
+ * adds the parent object before it adds the children
+ */
+ anc_obj->next_object = *new_objs;
+ *new_objs = anc_obj;
+ }
+
+ anc_msg = NULL;
+ TALLOC_FREE(anc_res);
+ TALLOC_FREE(anc_dn);
+
+ /*
+ * We may need to resolve more parents...
+ */
+ next_anc_guid = anc_obj->parent_object_guid;
+ }
+ return werr;
+}
+
+/**
+ * Adds a list of new objects into the current chunk of replication data to send
+ */
+static void getncchanges_chunk_add_objects(struct getncchanges_repl_chunk *repl_chunk,
+ struct drsuapi_DsReplicaObjectListItemEx *obj_list)
+{
+ struct drsuapi_DsReplicaObjectListItemEx *obj;
+
+ /*
+ * We track the last object added to the replication chunk, so just add
+ * the new object-list onto the end
+ */
+ if (repl_chunk->object_list == NULL) {
+ repl_chunk->object_list = obj_list;
+ } else {
+ repl_chunk->last_object->next_object = obj_list;
+ }
+
+ for (obj = obj_list; obj != NULL; obj = obj->next_object) {
+ repl_chunk->object_count += 1;
+
+ /*
+ * Remember the last object in the response - we'll use this to
+ * link the next object(s) processed onto the existing list
+ */
+ if (obj->next_object == NULL) {
+ repl_chunk->last_object = obj;
+ }
+ }
+}
+
+/**
+ * Gets the object to send, packed into an RPC struct ready to send. This also
+ * adds the object to the object cache, and adds any ancestors (if needed).
+ * @param msg - DB search result for the object to add
+ * @param guid - GUID of the object to add
+ * @param ret_obj_list - returns the object ready to be sent (in a list, along
+ * with any ancestors that might be needed). NULL if nothing to send.
+ */
+static WERROR getncchanges_get_obj_to_send(const struct ldb_message *msg,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_context *sam_ctx,
+ struct drsuapi_getncchanges_state *getnc_state,
+ struct dsdb_schema *schema,
+ DATA_BLOB *session_key,
+ struct drsuapi_DsGetNCChangesRequest10 *req10,
+ bool force_object_return,
+ uint32_t *local_pas,
+ struct ldb_dn *machine_dn,
+ const struct GUID *guid,
+ struct drsuapi_DsReplicaObjectListItemEx **ret_obj_list)
+{
+ struct drsuapi_DsReplicaObjectListItemEx *obj;
+ WERROR werr;
+
+ *ret_obj_list = NULL;
+
+ obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
+ W_ERROR_HAVE_NO_MEMORY(obj);
+
+ werr = get_nc_changes_build_object(obj, msg, sam_ctx, getnc_state,
+ schema, session_key, req10,
+ force_object_return,
+ local_pas, machine_dn, guid);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ /*
+ * The object may get filtered out by the UTDV's USN and not actually
+ * sent, in which case there's nothing more to do here
+ */
+ if (obj->meta_data_ctr == NULL) {
+ TALLOC_FREE(obj);
+ return WERR_OK;
+ }
+
+ if (getnc_state->obj_cache != NULL) {
+ werr = dcesrv_drsuapi_obj_cache_add(getnc_state->obj_cache,
+ guid);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ }
+
+ *ret_obj_list = obj;
+
+ /*
+ * If required, also add any ancestors that the client may need to know
+ * about before it can resolve this object. These get prepended to the
+ * ret_obj_list so the client adds them first.
+ *
+ * We allow this to be disabled to permit testing of a
+ * client-side fallback for the broken behaviour in Samba 4.5
+ * and earlier.
+ */
+ if (getnc_state->is_get_anc
+ && !getnc_state->broken_samba_4_5_get_anc_emulation) {
+ werr = getncchanges_add_ancestors(obj->parent_object_guid,
+ msg->dn, mem_ctx,
+ sam_ctx, getnc_state,
+ schema, session_key,
+ req10, local_pas,
+ machine_dn, ret_obj_list);
+ }
+
+ return werr;
+}
+
+/**
+ * Returns the number of links that are waiting to be sent
+ */
+static uint32_t getncchanges_chunk_links_pending(struct getncchanges_repl_chunk *repl_chunk,
+ struct drsuapi_getncchanges_state *getnc_state)
+{
+ uint32_t links_to_send = 0;
+
+ if (getnc_state->is_get_tgt) {
+
+ /*
+ * when the GET_TGT flag is set, only include the linked
+ * attributes whose target object has already been checked
+ * (i.e. they're ready to send).
+ */
+ if (repl_chunk->tgt_la_count > getnc_state->la_idx) {
+ links_to_send = (repl_chunk->tgt_la_count -
+ getnc_state->la_idx);
+ }
+ } else {
+ links_to_send = getnc_state->la_count - getnc_state->la_idx;
+ }
+
+ return links_to_send;
+}
+
+/**
+ * Returns the max number of links that will fit in the current replication chunk
+ */
+static uint32_t getncchanges_chunk_max_links(struct getncchanges_repl_chunk *repl_chunk)
+{
+ uint32_t max_links = 0;
+
+ if (repl_chunk->max_links != DEFAULT_MAX_LINKS ||
+ repl_chunk->max_objects != DEFAULT_MAX_OBJECTS) {
+
+ /*
+ * We're using non-default settings, so don't try to adjust
+ * them, just trust the user has configured decent values
+ */
+ max_links = repl_chunk->max_links;
+
+ } else if (repl_chunk->max_links > repl_chunk->object_count) {
+
+ /*
+ * This is just an approximate guess to avoid overfilling the
+ * replication chunk. It's the logic we've used historically.
+ * E.g. if we've already sent 1000 objects, then send 1000 fewer
+ * links. For comparison, the max that Windows seems to send is
+ * ~2700 links and ~250 objects (although this may vary based
+ * on timeouts)
+ */
+ max_links = repl_chunk->max_links - repl_chunk->object_count;
+ }
+
+ return max_links;
+}
+
+/**
+ * Returns true if the current GetNCChanges() call has taken longer than its
+ * allotted time. This prevents the client from timing out.
+ */
+static bool getncchanges_chunk_timed_out(struct getncchanges_repl_chunk *repl_chunk)
+{
+ return (time(NULL) - repl_chunk->start > repl_chunk->max_wait);
+}
+
+/**
+ * Returns true if the current chunk of replication data has reached the
+ * max_objects and/or max_links thresholds.
+ */
+static bool getncchanges_chunk_is_full(struct getncchanges_repl_chunk *repl_chunk,
+ struct drsuapi_getncchanges_state *getnc_state)
+{
+ bool chunk_full = false;
+ uint32_t links_to_send;
+ uint32_t chunk_limit;
+
+ /* check if the current chunk is already full with objects */
+ if (repl_chunk->object_count >= repl_chunk->max_objects) {
+ chunk_full = true;
+
+ } else if (repl_chunk->object_count > 0 &&
+ getncchanges_chunk_timed_out(repl_chunk)) {
+
+ /*
+ * We've exceeded our allotted time building this chunk,
+ * and we have at least one object to send back to the client
+ */
+ chunk_full = true;
+
+ } else if (repl_chunk->immediate_link_sync) {
+
+ /* check if the chunk is already full with links */
+ links_to_send = getncchanges_chunk_links_pending(repl_chunk,
+ getnc_state);
+
+ chunk_limit = getncchanges_chunk_max_links(repl_chunk);
+
+ /*
+ * The chunk is full if we've got more links to send than will
+ * fit in one chunk
+ */
+ if (links_to_send > 0 && chunk_limit <= links_to_send) {
+ chunk_full = true;
+ }
+ }
+
+ return chunk_full;
+}
+
+/**
+ * Goes through any new linked attributes and checks that the target object
+ * will be known to the client, i.e. we've already sent it in an replication
+ * chunk. If not, then it adds the target object to the current replication
+ * chunk. This is only done when the client specifies DRS_GET_TGT.
+ */
+static WERROR getncchanges_chunk_add_la_targets(struct getncchanges_repl_chunk *repl_chunk,
+ struct drsuapi_getncchanges_state *getnc_state,
+ uint32_t start_la_index,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_context *sam_ctx,
+ struct dsdb_schema *schema,
+ DATA_BLOB *session_key,
+ struct drsuapi_DsGetNCChangesRequest10 *req10,
+ uint32_t *local_pas,
+ struct ldb_dn *machine_dn)
+{
+ int ret;
+ uint32_t i;
+ uint32_t max_la_index;
+ uint32_t max_links;
+ uint32_t target_count = 0;
+ WERROR werr = WERR_OK;
+ static const char * const msg_attrs[] = {
+ "*",
+ "nTSecurityDescriptor",
+ "parentGUID",
+ "replPropertyMetaData",
+ DSDB_SECRET_ATTRIBUTES,
+ NULL };
+
+ /*
+ * A object can potentially link to thousands of targets. Only bother
+ * checking as many targets as will fit into the current response
+ */
+ max_links = getncchanges_chunk_max_links(repl_chunk);
+ max_la_index = MIN(getnc_state->la_count,
+ start_la_index + max_links);
+
+ /* loop through any linked attributes to check */
+ for (i = start_la_index;
+ (i < max_la_index &&
+ !getncchanges_chunk_is_full(repl_chunk, getnc_state));
+ i++) {
+
+ struct GUID target_guid;
+ struct drsuapi_DsReplicaObjectListItemEx *new_objs = NULL;
+ const struct drsuapi_DsReplicaLinkedAttribute *la;
+ struct ldb_result *msg_res;
+ struct ldb_dn *search_dn;
+ TALLOC_CTX *tmp_ctx;
+ struct dsdb_dn *dn;
+ const struct dsdb_attribute *schema_attrib;
+ NTSTATUS status;
+ bool same_nc;
+
+ la = &getnc_state->la_list[i];
+ tmp_ctx = talloc_new(mem_ctx);
+
+ /*
+ * Track what linked attribute targets we've checked. We might
+ * not have time to check them all, so we should only send back
+ * the ones we've actually checked.
+ */
+ repl_chunk->tgt_la_count = i + 1;
+
+ /* get the GUID of the linked attribute's target object */
+ schema_attrib = dsdb_attribute_by_attributeID_id(schema,
+ la->attid);
+
+ werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema,
+ tmp_ctx, la->value.blob, &dn);
+
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,(__location__ ": Bad la blob\n"));
+ return werr;
+ }
+
+ status = dsdb_get_extended_dn_guid(dn->dn, &target_guid, "GUID");
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ /*
+ * if the target isn't in the cache, then the client
+ * might not know about it, so send the target now
+ */
+ werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
+ &target_guid);
+
+ if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
+
+ /* target already sent, nothing to do */
+ TALLOC_FREE(tmp_ctx);
+ continue;
+ }
+
+ same_nc = dsdb_objects_have_same_nc(sam_ctx, tmp_ctx, dn->dn,
+ getnc_state->ncRoot_dn);
+
+ /* don't try to fetch target objects from another partition */
+ if (!same_nc) {
+ TALLOC_FREE(tmp_ctx);
+ continue;
+ }
+
+ search_dn = ldb_dn_new_fmt(tmp_ctx, sam_ctx, "<GUID=%s>",
+ GUID_string(tmp_ctx, &target_guid));
+ W_ERROR_HAVE_NO_MEMORY(search_dn);
+
+ ret = drsuapi_search_with_extended_dn(sam_ctx, tmp_ctx,
+ &msg_res, search_dn,
+ LDB_SCOPE_BASE,
+ msg_attrs, NULL);
+
+ /*
+ * Don't fail the replication if we can't find the target.
+ * This could happen for a one-way linked attribute, if the
+ * target is deleted and then later expunged (thus, the source
+ * object can be left with a hanging link). Continue to send
+ * the the link (the client-side has already tried once with
+ * GET_TGT, so it should just end up ignoring it).
+ */
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ DBG_WARNING("Encountered unknown link target DN %s\n",
+ ldb_dn_get_extended_linearized(tmp_ctx, dn->dn, 1));
+ TALLOC_FREE(tmp_ctx);
+ continue;
+
+ } else if (ret != LDB_SUCCESS) {
+ DBG_ERR("Failed to fetch link target DN %s - %s\n",
+ ldb_dn_get_extended_linearized(tmp_ctx, dn->dn, 1),
+ ldb_errstring(sam_ctx));
+ return WERR_DS_DRA_INCONSISTENT_DIT;
+ }
+
+ /*
+ * Construct an object, ready to send (this will include
+ * the object's ancestors as well, if GET_ANC is set)
+ */
+ werr = getncchanges_get_obj_to_send(msg_res->msgs[0], mem_ctx,
+ sam_ctx, getnc_state,
+ schema, session_key, req10,
+ false, local_pas,
+ machine_dn, &target_guid,
+ &new_objs);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ if (new_objs != NULL) {
+ target_count++;
+ getncchanges_chunk_add_objects(repl_chunk, new_objs);
+ }
+ TALLOC_FREE(tmp_ctx);
+ }
+
+ if (target_count > 0) {
+ DEBUG(3, ("GET_TGT: checked %u link-attrs, added %u target objs\n",
+ i - start_la_index, target_count));
+ }
+
+ return WERR_OK;
+}
+
+/**
+ * Creates a helper struct used for building a chunk of replication data,
+ * i.e. used over a single call to dcesrv_drsuapi_DsGetNCChanges().
+ */
+static struct getncchanges_repl_chunk * getncchanges_chunk_new(TALLOC_CTX *mem_ctx,
+ struct dcesrv_call_state *dce_call,
+ struct drsuapi_DsGetNCChangesRequest10 *req10)
+{
+ struct getncchanges_repl_chunk *repl_chunk;
+
+ repl_chunk = talloc_zero(mem_ctx, struct getncchanges_repl_chunk);
+
+ repl_chunk->start = time(NULL);
+
+ repl_chunk->max_objects = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL,
+ "drs", "max object sync",
+ DEFAULT_MAX_OBJECTS);
+
+ /*
+ * The client control here only applies in normal replication, not extended
+ * operations, which return a fixed set, even if the caller
+ * sets max_object_count == 0
+ */
+ if (req10->extended_op == DRSUAPI_EXOP_NONE) {
+
+ /*
+ * use this to force single objects at a time, which is useful
+ * for working out what object is giving problems
+ */
+ if (req10->max_object_count < repl_chunk->max_objects) {
+ repl_chunk->max_objects = req10->max_object_count;
+ }
+ }
+
+ repl_chunk->max_links =
+ lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL,
+ "drs", "max link sync",
+ DEFAULT_MAX_LINKS);
+
+ repl_chunk->immediate_link_sync =
+ lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx, NULL,
+ "drs", "immediate link sync", false);
+
+ /*
+ * Maximum time that we can spend in a getncchanges
+ * in order to avoid timeout of the other part.
+ * 10 seconds by default.
+ */
+ repl_chunk->max_wait = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx,
+ NULL, "drs", "max work time", 10);
+
+ return repl_chunk;
+}
+
+/*
+ drsuapi_DsGetNCChanges
+
+ see MS-DRSR 4.1.10.5.2 for basic logic of this function
+*/
+WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetNCChanges *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+ struct drsuapi_DsReplicaObjectIdentifier *untrusted_ncRoot;
+ int ret;
+ uint32_t i, k;
+ struct dsdb_schema *schema;
+ struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
+ struct getncchanges_repl_chunk *repl_chunk;
+ NTSTATUS status;
+ DATA_BLOB session_key;
+ WERROR werr;
+ struct dcesrv_handle *h;
+ struct drsuapi_bind_state *b_state;
+ struct drsuapi_getncchanges_state *getnc_state = NULL;
+ struct drsuapi_DsGetNCChangesRequest10 *req10;
+ uint32_t options;
+ uint32_t link_count = 0;
+ struct ldb_dn *search_dn = NULL;
+ bool am_rodc;
+ enum security_user_level security_level;
+ struct ldb_context *sam_ctx;
+ struct dom_sid *user_sid;
+ bool is_secret_request;
+ bool is_gc_pas_request;
+ struct drsuapi_changed_objects *changes;
+ bool has_get_all_changes = false;
+ struct GUID invocation_id;
+ static const struct drsuapi_DsReplicaLinkedAttribute no_linked_attr;
+ struct dsdb_schema_prefixmap *pfm_remote = NULL;
+ bool full = true;
+ uint32_t *local_pas = NULL;
+ struct ldb_dn *machine_dn = NULL; /* Only used for REPL SECRET EXOP */
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ /* sam_ctx_system is not present for non-administrator users */
+ sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx;
+
+ invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
+
+ *r->out.level_out = 6;
+
+ r->out.ctr->ctr6.linked_attributes_count = 0;
+ r->out.ctr->ctr6.linked_attributes = discard_const_p(struct drsuapi_DsReplicaLinkedAttribute, &no_linked_attr);
+
+ r->out.ctr->ctr6.object_count = 0;
+ r->out.ctr->ctr6.nc_object_count = 0;
+ r->out.ctr->ctr6.more_data = false;
+ r->out.ctr->ctr6.uptodateness_vector = NULL;
+ r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(sam_ctx));
+ r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
+ r->out.ctr->ctr6.first_object = NULL;
+
+ /* Check request revision.
+ */
+ switch (r->in.level) {
+ case 8:
+ req10 = getncchanges_map_req8(mem_ctx, &r->in.req->req8);
+ if (req10 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ break;
+ case 10:
+ req10 = &r->in.req->req10;
+ break;
+ default:
+ DEBUG(0,(__location__ ": Request for DsGetNCChanges with unsupported level %u\n",
+ r->in.level));
+ return WERR_REVISION_MISMATCH;
+ }
+
+ repl_chunk = getncchanges_chunk_new(mem_ctx, dce_call, req10);
+
+ if (repl_chunk == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* a RODC doesn't allow for any replication */
+ ret = samdb_rodc(sam_ctx, &am_rodc);
+ if (ret == LDB_SUCCESS && am_rodc) {
+ DEBUG(0,(__location__ ": DsGetNCChanges attempt on RODC\n"));
+ return WERR_DS_DRA_SOURCE_DISABLED;
+ }
+
+ /*
+ * Help our tests pass by pre-checking the
+ * destination_dsa_guid before the NC permissions. Info on
+ * valid DSA GUIDs is not sensitive so this isn't a leak
+ */
+ switch (req10->extended_op) {
+ case DRSUAPI_EXOP_FSMO_REQ_ROLE:
+ case DRSUAPI_EXOP_FSMO_RID_ALLOC:
+ case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
+ case DRSUAPI_EXOP_FSMO_REQ_PDC:
+ case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
+ {
+ const char *attrs[] = { NULL };
+
+ ret = samdb_get_ntds_obj_by_guid(mem_ctx,
+ sam_ctx,
+ &req10->destination_dsa_guid,
+ attrs,
+ NULL);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ /*
+ * Error out with an EXOP error but success at
+ * the top level return value
+ */
+ r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_UNKNOWN_CALLER;
+ return WERR_OK;
+ } else if (ret != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ break;
+ }
+ case DRSUAPI_EXOP_REPL_SECRET:
+ case DRSUAPI_EXOP_REPL_OBJ:
+ case DRSUAPI_EXOP_NONE:
+ break;
+ }
+
+ /* Perform access checks. */
+ untrusted_ncRoot = req10->naming_context;
+ if (untrusted_ncRoot == NULL) {
+ DEBUG(0,(__location__ ": Request for DsGetNCChanges with no NC\n"));
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ if (samdb_ntds_options(sam_ctx, &options) != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if ((options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) &&
+ !(req10->replica_flags & DRSUAPI_DRS_SYNC_FORCED)) {
+ return WERR_DS_DRA_SOURCE_DISABLED;
+ }
+
+ user_sid = &session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
+
+ /*
+ * all clients must have GUID_DRS_GET_CHANGES. This finds the
+ * actual NC root of the given value and checks that, allowing
+ * REPL_OBJ to work safely
+ */
+ werr = drs_security_access_check_nc_root(sam_ctx,
+ mem_ctx,
+ session_info->security_token,
+ req10->naming_context,
+ GUID_DRS_GET_CHANGES);
+
+ if (W_ERROR_EQUAL(werr, WERR_DS_DRA_BAD_NC)) {
+ /*
+ * These extended operations need a different error if
+ * the supplied DN can't be found
+ */
+ switch (req10->extended_op) {
+ case DRSUAPI_EXOP_REPL_OBJ:
+ case DRSUAPI_EXOP_REPL_SECRET:
+ return WERR_DS_DRA_BAD_DN;
+ default:
+ return werr;
+ }
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ if (dsdb_functional_level(sam_ctx) >= DS_DOMAIN_FUNCTION_2008) {
+ full = req10->partial_attribute_set == NULL &&
+ req10->partial_attribute_set_ex == NULL;
+ } else {
+ full = (options & DRSUAPI_DRS_WRIT_REP) != 0;
+ }
+
+ werr = dsdb_schema_pfm_from_drsuapi_pfm(&req10->mapping_ctr, true,
+ mem_ctx, &pfm_remote, NULL);
+
+ /* We were supplied a partial attribute set, without the prefix map! */
+ if (!full && !W_ERROR_IS_OK(werr)) {
+ if (req10->mapping_ctr.num_mappings == 0) {
+ /*
+ * Despite the fact MS-DRSR specifies that this shouldn't
+ * happen, Windows RODCs will in fact not provide a prefixMap.
+ */
+ DEBUG(5,(__location__ ": Failed to provide a remote prefixMap,"
+ " falling back to local prefixMap\n"));
+ } else {
+ DEBUG(0,(__location__ ": Failed to decode remote prefixMap: %s\n",
+ win_errstr(werr)));
+ return werr;
+ }
+ }
+
+ /* allowed if the GC PAS and client has
+ GUID_DRS_GET_FILTERED_ATTRIBUTES */
+ werr = dcesrv_drsuapi_is_gc_pas_request(b_state, req10, pfm_remote, &is_gc_pas_request);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ if (is_gc_pas_request) {
+ werr = drs_security_access_check_nc_root(sam_ctx,
+ mem_ctx,
+ session_info->security_token,
+ req10->naming_context,
+ GUID_DRS_GET_FILTERED_ATTRIBUTES);
+ if (W_ERROR_IS_OK(werr)) {
+ goto allowed;
+ }
+ }
+
+ werr = dcesrv_drsuapi_is_reveal_secrets_request(b_state, req10,
+ pfm_remote,
+ &is_secret_request);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ if (is_secret_request) {
+ werr = drs_security_access_check_nc_root(sam_ctx,
+ mem_ctx,
+ session_info->security_token,
+ req10->naming_context,
+ GUID_DRS_GET_ALL_CHANGES);
+ if (!W_ERROR_IS_OK(werr)) {
+ /* Only bail if this is not a EXOP_REPL_SECRET */
+ if (req10->extended_op != DRSUAPI_EXOP_REPL_SECRET) {
+ return werr;
+ }
+ } else {
+ has_get_all_changes = true;
+ }
+ }
+
+allowed:
+ /* for non-administrator replications, check that they have
+ given the correct source_dsa_invocation_id */
+ security_level = security_session_user_level(session_info,
+ samdb_domain_sid(sam_ctx));
+ if (security_level == SECURITY_RO_DOMAIN_CONTROLLER) {
+ if (req10->replica_flags & DRSUAPI_DRS_WRIT_REP) {
+ /* we rely on this flag being unset for RODC requests */
+ req10->replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
+ }
+ }
+
+ if (req10->replica_flags & DRSUAPI_DRS_FULL_SYNC_PACKET) {
+ /* Ignore the _in_ uptpdateness vector*/
+ req10->uptodateness_vector = NULL;
+ }
+
+ if (GUID_all_zero(&req10->source_dsa_invocation_id)) {
+ req10->source_dsa_invocation_id = invocation_id;
+ }
+
+ if (!GUID_equal(&req10->source_dsa_invocation_id, &invocation_id)) {
+ /*
+ * The given highwatermark is only valid relative to the
+ * specified source_dsa_invocation_id.
+ */
+ ZERO_STRUCT(req10->highwatermark);
+ }
+
+ /*
+ * An extended operation is "special single-response cycle"
+ * per MS-DRSR 4.1.10.1.1 "Start and Finish" so we don't need
+ * to guess if this is a continuation of any long-term
+ * state.
+ *
+ * Otherwise, maintain (including marking as stale, which is
+ * what the below is for) the replication state.
+ *
+ * Note that point 5 "The server implementation MAY declare
+ * the supplied values ... as too stale to use." would allow
+ * resetting the state at almost any point, Microsoft Azure AD
+ * Connect will switch back and forth between a REPL_OBJ and a
+ * full replication, so we must not reset the statue during
+ * extended operations.
+ */
+ if (req10->extended_op == DRSUAPI_EXOP_NONE &&
+ b_state->getncchanges_full_repl_state != NULL) {
+ /*
+ * Knowing that this is not an extended operation, we
+ * can access (and validate) the full replication
+ * state
+ */
+ getnc_state = b_state->getncchanges_full_repl_state;
+ }
+
+ /* see if a previous replication has been abandoned */
+ if (getnc_state != NULL) {
+ struct ldb_dn *new_dn;
+ ret = drs_ObjectIdentifier_to_dn_and_nc_root(getnc_state,
+ sam_ctx,
+ untrusted_ncRoot,
+ &new_dn,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ /*
+ * This can't fail as we have done this above
+ * implicitly but not got the DN out, but
+ * print a good error message regardless just
+ * in case.
+ */
+ DBG_ERR("Bad DN '%s' as Naming Context for GetNCChanges: %s\n",
+ drs_ObjectIdentifier_to_debug_string(mem_ctx, untrusted_ncRoot),
+ ldb_strerror(ret));
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+ if (ldb_dn_compare(new_dn, getnc_state->ncRoot_dn) != 0) {
+ DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication on different DN %s %s (last_dn %s)\n",
+ ldb_dn_get_linearized(new_dn),
+ ldb_dn_get_linearized(getnc_state->ncRoot_dn),
+ ldb_dn_get_linearized(getnc_state->last_dn)));
+ TALLOC_FREE(getnc_state);
+ b_state->getncchanges_full_repl_state = NULL;
+ }
+ }
+
+ if (getnc_state != NULL) {
+ ret = drsuapi_DsReplicaHighWaterMark_cmp(&getnc_state->last_hwm,
+ &req10->highwatermark);
+ if (ret != 0) {
+ DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication "
+ "on DN %s %s highwatermark (last_dn %s)\n",
+ ldb_dn_get_linearized(getnc_state->ncRoot_dn),
+ (ret > 0) ? "older" : "newer",
+ ldb_dn_get_linearized(getnc_state->last_dn)));
+ TALLOC_FREE(getnc_state);
+ b_state->getncchanges_full_repl_state = NULL;
+ }
+ }
+
+ /*
+ * This is either a new replication cycle, or an extended
+ * operation. A new cycle is triggered above by the
+ * TALLOC_FREE() which sets getnc_state to NULL.
+ */
+ if (getnc_state == NULL) {
+ struct ldb_result *res = NULL;
+ const char *attrs[] = {
+ "instanceType",
+ "objectGuID",
+ NULL
+ };
+ uint32_t nc_instanceType;
+ struct ldb_dn *ncRoot_dn;
+
+ ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx,
+ sam_ctx,
+ untrusted_ncRoot,
+ &ncRoot_dn,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ DBG_ERR("Bad DN '%s' as Naming Context or EXOP DN for GetNCChanges: %s\n",
+ drs_ObjectIdentifier_to_debug_string(mem_ctx, untrusted_ncRoot),
+ ldb_strerror(ret));
+ return WERR_DS_DRA_BAD_DN;
+ }
+
+ ret = dsdb_search_dn(sam_ctx, mem_ctx, &res,
+ ncRoot_dn, attrs,
+ DSDB_SEARCH_SHOW_DELETED |
+ DSDB_SEARCH_SHOW_RECYCLED);
+ if (ret != LDB_SUCCESS) {
+ DBG_WARNING("Failed to find ncRoot_dn %s\n",
+ ldb_dn_get_linearized(ncRoot_dn));
+ return WERR_DS_DRA_BAD_DN;
+ }
+ nc_instanceType = ldb_msg_find_attr_as_int(res->msgs[0],
+ "instanceType",
+ 0);
+
+ if (req10->extended_op != DRSUAPI_EXOP_NONE) {
+ r->out.ctr->ctr6.extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
+ }
+
+ /*
+ * This is the first replication cycle and it is
+ * a good place to handle extended operations
+ *
+ * FIXME: we don't fully support extended operations yet
+ */
+ switch (req10->extended_op) {
+ case DRSUAPI_EXOP_NONE:
+ if ((nc_instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0) {
+ const char *dn_str
+ = ldb_dn_get_linearized(ncRoot_dn);
+
+ DBG_NOTICE("Rejecting full replication on "
+ "not NC %s", dn_str);
+
+ return WERR_DS_CANT_FIND_EXPECTED_NC;
+ }
+
+ break;
+ case DRSUAPI_EXOP_FSMO_RID_ALLOC:
+ werr = getncchanges_rid_alloc(b_state, mem_ctx, req10, &r->out.ctr->ctr6, &search_dn);
+ W_ERROR_NOT_OK_RETURN(werr);
+ if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
+ return WERR_OK;
+ }
+ break;
+ case DRSUAPI_EXOP_REPL_SECRET:
+ werr = getncchanges_repl_secret(b_state, mem_ctx, req10,
+ user_sid,
+ &r->out.ctr->ctr6,
+ has_get_all_changes,
+ &machine_dn);
+ r->out.result = werr;
+ W_ERROR_NOT_OK_RETURN(werr);
+ break;
+ case DRSUAPI_EXOP_FSMO_REQ_ROLE:
+ werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
+ W_ERROR_NOT_OK_RETURN(werr);
+ if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
+ return WERR_OK;
+ }
+ break;
+ case DRSUAPI_EXOP_FSMO_RID_REQ_ROLE:
+ werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
+ W_ERROR_NOT_OK_RETURN(werr);
+ if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
+ return WERR_OK;
+ }
+ break;
+ case DRSUAPI_EXOP_FSMO_REQ_PDC:
+ werr = getncchanges_change_master(b_state, mem_ctx, req10, &r->out.ctr->ctr6);
+ W_ERROR_NOT_OK_RETURN(werr);
+ if (r->out.ctr->ctr6.extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
+ return WERR_OK;
+ }
+ break;
+ case DRSUAPI_EXOP_REPL_OBJ:
+ werr = getncchanges_repl_obj(b_state, mem_ctx, req10, user_sid, &r->out.ctr->ctr6);
+ r->out.result = werr;
+ W_ERROR_NOT_OK_RETURN(werr);
+ break;
+
+ case DRSUAPI_EXOP_FSMO_ABANDON_ROLE:
+
+ DEBUG(0,(__location__ ": Request for DsGetNCChanges unsupported extended op 0x%x\n",
+ (unsigned)req10->extended_op));
+ return WERR_DS_DRA_NOT_SUPPORTED;
+ }
+
+ /*
+ * Initialize the state, initially for the remainder
+ * of this call (EXOPs)
+ *
+ * An extended operation is a "special single-response
+ * cycle" per MS-DRSR 4.1.10.1.1 "Start and Finish"
+ *
+ */
+ getnc_state = talloc_zero(mem_ctx, struct drsuapi_getncchanges_state);
+ if (getnc_state == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (req10->extended_op == DRSUAPI_EXOP_NONE) {
+ /*
+ * Promote the memory to being a store of
+ * long-term state that we will use over the
+ * replication cycle for full replication
+ * requests
+ *
+ * Store the state in a clearly named location
+ * for pulling back only during full
+ * replications
+ */
+ b_state->getncchanges_full_repl_state
+ = talloc_steal(b_state, getnc_state);
+ }
+
+ getnc_state->ncRoot_dn = ncRoot_dn;
+ talloc_steal(getnc_state, ncRoot_dn);
+
+ getnc_state->ncRoot_guid = samdb_result_guid(res->msgs[0],
+ "objectGUID");
+
+ /* find out if we are to replicate Schema NC */
+ ret = ldb_dn_compare_base(ldb_get_schema_basedn(sam_ctx),
+ ncRoot_dn);
+ getnc_state->is_schema_nc = (0 == ret);
+
+ TALLOC_FREE(res);
+ }
+
+ /* we need the session key for encrypting password attributes */
+ status = dcesrv_auth_session_key(dce_call, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": Failed to get session key\n"));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ /*
+ TODO: MS-DRSR section 4.1.10.1.1
+ Work out if this is the start of a new cycle */
+
+ if (getnc_state->guids == NULL) {
+ const char *extra_filter;
+ struct ldb_result *search_res = NULL;
+ static const struct drsuapi_DsReplicaCursorCtrEx empty_udv;
+ const struct drsuapi_DsReplicaCursorCtrEx *udv = NULL;
+
+ extra_filter = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
+
+ if (req10->extended_op == DRSUAPI_EXOP_NONE) {
+ if (req10->uptodateness_vector != NULL) {
+ udv = req10->uptodateness_vector;
+ } else {
+ udv = &empty_udv;
+ }
+
+ getnc_state->min_usn = req10->highwatermark.highest_usn;
+ for (i = 0; i < udv->count; i++) {
+ bool match;
+ const struct drsuapi_DsReplicaCursor *cur =
+ &udv->cursors[i];
+
+ match = GUID_equal(&invocation_id,
+ &cur->source_dsa_invocation_id);
+ if (!match) {
+ continue;
+ }
+ if (cur->highest_usn > getnc_state->min_usn) {
+ getnc_state->min_usn = cur->highest_usn;
+ }
+ break;
+ }
+ } else {
+ /* We do not want REPL_SECRETS or REPL_SINGLE to return empty-handed */
+ udv = &empty_udv;
+ getnc_state->min_usn = 0;
+ }
+
+ getnc_state->max_usn = getnc_state->min_usn;
+
+ getnc_state->final_udv = talloc_zero(getnc_state,
+ struct drsuapi_DsReplicaCursor2CtrEx);
+ if (getnc_state->final_udv == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ werr = get_nc_changes_udv(sam_ctx, getnc_state->ncRoot_dn,
+ getnc_state->final_udv);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ if (req10->extended_op == DRSUAPI_EXOP_NONE) {
+ werr = getncchanges_collect_objects(b_state, mem_ctx,
+ getnc_state, req10,
+ search_dn, extra_filter,
+ &search_res);
+ } else {
+ werr = getncchanges_collect_objects_exop(b_state, mem_ctx,
+ getnc_state, req10,
+ &r->out.ctr->ctr6,
+ search_dn, extra_filter,
+ &search_res);
+ }
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ /* extract out the GUIDs list */
+ getnc_state->num_records = search_res ? search_res->count : 0;
+ getnc_state->guids = talloc_array(getnc_state, struct GUID, getnc_state->num_records);
+ W_ERROR_HAVE_NO_MEMORY(getnc_state->guids);
+
+ changes = talloc_array(getnc_state,
+ struct drsuapi_changed_objects,
+ getnc_state->num_records);
+ W_ERROR_HAVE_NO_MEMORY(changes);
+
+ for (i=0; i<getnc_state->num_records; i++) {
+ changes[i].dn = search_res->msgs[i]->dn;
+ changes[i].guid = samdb_result_guid(search_res->msgs[i], "objectGUID");
+ changes[i].usn = ldb_msg_find_attr_as_uint64(search_res->msgs[i], "uSNChanged", 0);
+
+ if (changes[i].usn > getnc_state->max_usn) {
+ getnc_state->max_usn = changes[i].usn;
+ }
+
+ if (req10->extended_op == DRSUAPI_EXOP_NONE &&
+ GUID_equal(&changes[i].guid, &getnc_state->ncRoot_guid))
+ {
+ getnc_state->send_nc_root_first = true;
+ }
+ }
+
+ if (req10->extended_op == DRSUAPI_EXOP_NONE) {
+ getnc_state->is_get_anc =
+ ((req10->replica_flags & DRSUAPI_DRS_GET_ANC) != 0);
+ if (getnc_state->is_get_anc
+ && lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx,
+ NULL,
+ "drs",
+ "broken_samba_4.5_get_anc_emulation",
+ false)) {
+ getnc_state->broken_samba_4_5_get_anc_emulation = true;
+ }
+ if (lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx,
+ NULL,
+ "drs",
+ "get_tgt_support",
+ true)) {
+ getnc_state->is_get_tgt =
+ ((req10->more_flags & DRSUAPI_DRS_GET_TGT) != 0);
+ }
+ }
+
+ /* RID_ALLOC returns 3 objects in a fixed order */
+ if (req10->extended_op == DRSUAPI_EXOP_FSMO_RID_ALLOC) {
+ /* Do nothing */
+ } else if (getnc_state->broken_samba_4_5_get_anc_emulation) {
+ LDB_TYPESAFE_QSORT(changes,
+ getnc_state->num_records,
+ getnc_state,
+ site_res_cmp_anc_order);
+ } else {
+ LDB_TYPESAFE_QSORT(changes,
+ getnc_state->num_records,
+ getnc_state,
+ site_res_cmp_usn_order);
+ }
+
+ for (i=0; i < getnc_state->num_records; i++) {
+ getnc_state->guids[i] = changes[i].guid;
+ if (GUID_all_zero(&getnc_state->guids[i])) {
+ DEBUG(2,("getncchanges: bad objectGUID from %s\n",
+ ldb_dn_get_linearized(search_res->msgs[i]->dn)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ }
+
+ getnc_state->final_hwm.tmp_highest_usn = getnc_state->max_usn;
+ getnc_state->final_hwm.reserved_usn = 0;
+ getnc_state->final_hwm.highest_usn = getnc_state->max_usn;
+
+ talloc_free(search_res);
+ talloc_free(changes);
+
+ /*
+ * when using GET_ANC or GET_TGT, cache the objects that have
+ * been already sent, to avoid sending them multiple times
+ */
+ if (getnc_state->is_get_anc || getnc_state->is_get_tgt) {
+ DEBUG(3,("Using object cache, GET_ANC %u, GET_TGT %u\n",
+ getnc_state->is_get_anc,
+ getnc_state->is_get_tgt));
+
+ getnc_state->obj_cache = db_open_rbt(getnc_state);
+ if (getnc_state->obj_cache == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+ }
+
+ if (req10->uptodateness_vector) {
+ /* make sure its sorted */
+ TYPESAFE_QSORT(req10->uptodateness_vector->cursors,
+ req10->uptodateness_vector->count,
+ drsuapi_DsReplicaCursor_compare);
+ }
+
+ /* Prefix mapping */
+ schema = dsdb_get_schema(sam_ctx, mem_ctx);
+ if (!schema) {
+ DEBUG(0,("No schema in sam_ctx\n"));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ r->out.ctr->ctr6.naming_context = talloc(mem_ctx, struct drsuapi_DsReplicaObjectIdentifier);
+ if (r->out.ctr->ctr6.naming_context == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /*
+ * Match Windows and echo back the original values from the request, even if
+ * they say DummyDN for the string NC
+ */
+ *r->out.ctr->ctr6.naming_context = *untrusted_ncRoot;
+
+ /* find the SID if there is one */
+ dsdb_find_sid_by_dn(sam_ctx, getnc_state->ncRoot_dn, &r->out.ctr->ctr6.naming_context->sid);
+
+ /* Set GUID */
+ r->out.ctr->ctr6.naming_context->guid = getnc_state->ncRoot_guid;
+
+ dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, &ctr);
+ r->out.ctr->ctr6.mapping_ctr = *ctr;
+
+ r->out.ctr->ctr6.source_dsa_guid = *(samdb_ntds_objectGUID(sam_ctx));
+ r->out.ctr->ctr6.source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
+
+ r->out.ctr->ctr6.old_highwatermark = req10->highwatermark;
+ r->out.ctr->ctr6.new_highwatermark = req10->highwatermark;
+
+ /*
+ * If the client has already set GET_TGT then we know they can handle
+ * receiving the linked attributes interleaved with the source objects
+ */
+ if (getnc_state->is_get_tgt) {
+ repl_chunk->immediate_link_sync = true;
+ }
+
+ if (req10->partial_attribute_set != NULL) {
+ struct dsdb_syntax_ctx syntax_ctx;
+ uint32_t j = 0;
+
+ dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
+ syntax_ctx.pfm_remote = pfm_remote;
+
+ local_pas = talloc_array(b_state, uint32_t, req10->partial_attribute_set->num_attids);
+
+ for (j = 0; j < req10->partial_attribute_set->num_attids; j++) {
+ getncchanges_attid_remote_to_local(schema,
+ &syntax_ctx,
+ req10->partial_attribute_set->attids[j],
+ (enum drsuapi_DsAttributeId *)&local_pas[j],
+ NULL);
+ }
+
+ TYPESAFE_QSORT(local_pas,
+ req10->partial_attribute_set->num_attids,
+ uint32_t_ptr_cmp);
+ }
+
+ /*
+ * If we have the NC root in this replication, send it
+ * first regardless. However, don't bump the USN now,
+ * treat it as if it was sent early due to GET_ANC
+ *
+ * This is triggered for each call, so every page of responses
+ * gets the NC root as the first object, up to the point where
+ * it naturally occurs in the replication.
+ */
+
+ if (getnc_state->send_nc_root_first) {
+ struct drsuapi_DsReplicaObjectListItemEx *new_objs = NULL;
+
+ werr = getncchanges_add_ancestors(&getnc_state->ncRoot_guid,
+ NULL, mem_ctx,
+ sam_ctx, getnc_state,
+ schema, &session_key,
+ req10, local_pas,
+ machine_dn, &new_objs);
+
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ getncchanges_chunk_add_objects(repl_chunk, new_objs);
+
+ DEBUG(8,(__location__ ": replicating NC root %s\n",
+ ldb_dn_get_linearized(getnc_state->ncRoot_dn)));
+ }
+
+ /*
+ * Check in case we're still processing the links from an object in the
+ * previous chunk. We want to send the links (and any targets needed)
+ * before moving on to the next object.
+ */
+ if (getnc_state->is_get_tgt) {
+ werr = getncchanges_chunk_add_la_targets(repl_chunk,
+ getnc_state,
+ getnc_state->la_idx,
+ mem_ctx, sam_ctx,
+ schema, &session_key,
+ req10, local_pas,
+ machine_dn);
+
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ }
+
+ for (i=getnc_state->num_processed;
+ i<getnc_state->num_records &&
+ !getncchanges_chunk_is_full(repl_chunk, getnc_state);
+ i++) {
+ struct drsuapi_DsReplicaObjectListItemEx *new_objs = NULL;
+ struct ldb_message *msg;
+ static const char * const msg_attrs[] = {
+ "*",
+ "nTSecurityDescriptor",
+ "parentGUID",
+ "replPropertyMetaData",
+ DSDB_SECRET_ATTRIBUTES,
+ NULL };
+ struct ldb_result *msg_res;
+ struct ldb_dn *msg_dn;
+ bool obj_already_sent = false;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ uint32_t old_la_index;
+
+ /*
+ * Once we get to the 'natural' place to send the NC
+ * root, stop sending it at the front of each reply
+ * and make sure to suppress sending it now
+ *
+ * We don't just 'continue' here as we must send links
+ * and unlike Windows we want to update the
+ * tmp_highest_usn
+ */
+
+ if (getnc_state->send_nc_root_first &&
+ GUID_equal(&getnc_state->guids[i], &getnc_state->ncRoot_guid))
+ {
+ getnc_state->send_nc_root_first = false;
+ obj_already_sent = true;
+ }
+
+ msg_dn = ldb_dn_new_fmt(tmp_ctx, sam_ctx, "<GUID=%s>",
+ GUID_string(tmp_ctx, &getnc_state->guids[i]));
+ W_ERROR_HAVE_NO_MEMORY(msg_dn);
+
+ /*
+ * by re-searching here we avoid having a lot of full
+ * records in memory between calls to getncchanges.
+ *
+ * We expect that we may get some objects that vanish
+ * (tombstone expunge) between the first and second
+ * check.
+ */
+ ret = drsuapi_search_with_extended_dn(sam_ctx, tmp_ctx, &msg_res,
+ msg_dn,
+ LDB_SCOPE_BASE, msg_attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ if (ret != LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(1,("getncchanges: failed to fetch DN %s - %s\n",
+ ldb_dn_get_extended_linearized(tmp_ctx, msg_dn, 1),
+ ldb_errstring(sam_ctx)));
+ }
+ TALLOC_FREE(tmp_ctx);
+ continue;
+ }
+
+ if (msg_res->count == 0) {
+ DEBUG(1,("getncchanges: got LDB_SUCCESS but failed"
+ "to get any results in fetch of DN "
+ "%s (race with tombstone expunge?)\n",
+ ldb_dn_get_extended_linearized(tmp_ctx,
+ msg_dn, 1)));
+ TALLOC_FREE(tmp_ctx);
+ continue;
+ }
+
+ msg = msg_res->msgs[0];
+
+ /*
+ * Check if we've already sent the object as an ancestor of
+ * another object. If so, we don't need to send it again
+ */
+ if (getnc_state->obj_cache != NULL) {
+ werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
+ &getnc_state->guids[i]);
+ if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
+ obj_already_sent = true;
+ }
+ }
+
+ if (!obj_already_sent) {
+ bool max_wait_reached;
+
+ max_wait_reached = getncchanges_chunk_timed_out(repl_chunk);
+
+ /*
+ * Construct an object, ready to send (this will include
+ * the object's ancestors as well, if needed)
+ */
+ werr = getncchanges_get_obj_to_send(msg, mem_ctx, sam_ctx,
+ getnc_state, schema,
+ &session_key, req10,
+ max_wait_reached,
+ local_pas, machine_dn,
+ &getnc_state->guids[i],
+ &new_objs);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ }
+
+ old_la_index = getnc_state->la_count;
+
+ /*
+ * We've reached the USN where this object naturally occurs.
+ * Regardless of whether we've already sent the object (as an
+ * ancestor), we add its links and update the HWM at this point
+ */
+ werr = get_nc_changes_add_links(sam_ctx, getnc_state,
+ getnc_state->is_schema_nc,
+ schema, getnc_state->min_usn,
+ req10->replica_flags,
+ msg,
+ &getnc_state->la_list,
+ &getnc_state->la_count,
+ req10->uptodateness_vector);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ dcesrv_drsuapi_update_highwatermark(msg,
+ getnc_state->max_usn,
+ &r->out.ctr->ctr6.new_highwatermark);
+
+ if (new_objs != NULL) {
+
+ /*
+ * Add the object (and, if GET_ANC, any parents it may
+ * have) into the current chunk of replication data
+ */
+ getncchanges_chunk_add_objects(repl_chunk, new_objs);
+
+ talloc_free(getnc_state->last_dn);
+ /*
+ * talloc_steal() as we still need msg->dn to
+ * be a valid pointer for the log on the next
+ * line.
+ *
+ * msg only remains in scope for the next 25
+ * lines or so anyway.
+ */
+ getnc_state->last_dn = talloc_steal(getnc_state, msg->dn);
+ }
+
+ DEBUG(8,(__location__ ": %s object %s new tmp_highest_usn=%" PRIu64 "\n",
+ new_objs ? "replicating" : "skipping send of",
+ ldb_dn_get_linearized(msg->dn),
+ r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn));
+
+ getnc_state->total_links += (getnc_state->la_count - old_la_index);
+
+ /*
+ * If the GET_TGT flag was set, check any new links added to
+ * make sure the client knows about the link target object
+ */
+ if (getnc_state->is_get_tgt) {
+ werr = getncchanges_chunk_add_la_targets(repl_chunk,
+ getnc_state,
+ old_la_index,
+ mem_ctx, sam_ctx,
+ schema, &session_key,
+ req10, local_pas,
+ machine_dn);
+
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ }
+
+ /* copy the constructed object list into the response message */
+ r->out.ctr->ctr6.object_count = repl_chunk->object_count;
+ r->out.ctr->ctr6.first_object = repl_chunk->object_list;
+
+ getnc_state->num_processed = i;
+
+ if (i < getnc_state->num_records) {
+ r->out.ctr->ctr6.more_data = true;
+ }
+
+ /* the client can us to call UpdateRefs on its behalf to
+ re-establish monitoring of the NC */
+ if ((req10->replica_flags & (DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_REF_GCSPN)) &&
+ !GUID_all_zero(&req10->destination_dsa_guid)) {
+ struct drsuapi_DsReplicaUpdateRefsRequest1 ureq;
+ DEBUG(3,("UpdateRefs on getncchanges for %s\n",
+ GUID_string(mem_ctx, &req10->destination_dsa_guid)));
+
+ /*
+ * We pass the pre-validation NC root here as
+ * drsuapi_UpdateRefs() has to check its own input
+ * values due to being called from
+ * dcesrv_drsuapi_DsReplicaUpdateRefs()
+ */
+
+ ureq.naming_context = untrusted_ncRoot;
+ ureq.dest_dsa_dns_name = samdb_ntds_msdcs_dns_name(sam_ctx, mem_ctx,
+ &req10->destination_dsa_guid);
+ if (!ureq.dest_dsa_dns_name) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ ureq.dest_dsa_guid = req10->destination_dsa_guid;
+ ureq.options = DRSUAPI_DRS_ADD_REF |
+ DRSUAPI_DRS_ASYNC_OP |
+ DRSUAPI_DRS_GETCHG_CHECK;
+
+ /* we also need to pass through the
+ DRSUAPI_DRS_REF_GCSPN bit so that repsTo gets flagged
+ to send notifies using the GC SPN */
+ ureq.options |= (req10->replica_flags & DRSUAPI_DRS_REF_GCSPN);
+
+ werr = drsuapi_UpdateRefs(imsg_ctx,
+ dce_call->event_ctx,
+ b_state,
+ mem_ctx,
+ &ureq);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,(__location__ ": Failed UpdateRefs on %s for %s in DsGetNCChanges - %s\n",
+ drs_ObjectIdentifier_to_debug_string(mem_ctx, untrusted_ncRoot),
+ ureq.dest_dsa_dns_name,
+ win_errstr(werr)));
+ }
+ }
+
+ /*
+ * Work out how many links we can send in this chunk. The default is to
+ * send all the links last, but there is a config option to send them
+ * immediately, in the same chunk as their source object
+ */
+ if (!r->out.ctr->ctr6.more_data || repl_chunk->immediate_link_sync) {
+ link_count = getncchanges_chunk_links_pending(repl_chunk,
+ getnc_state);
+ link_count = MIN(link_count,
+ getncchanges_chunk_max_links(repl_chunk));
+ }
+
+ /* If we've got linked attributes to send, add them now */
+ if (link_count > 0) {
+ struct la_for_sorting *la_sorted;
+
+ /*
+ * Grab a chunk of linked attributes off the list and put them
+ * in sorted array, ready to send
+ */
+ werr = getncchanges_get_sorted_array(&getnc_state->la_list[getnc_state->la_idx],
+ link_count,
+ sam_ctx, getnc_state,
+ schema,
+ &la_sorted);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ r->out.ctr->ctr6.linked_attributes_count = link_count;
+ r->out.ctr->ctr6.linked_attributes = talloc_array(r->out.ctr, struct drsuapi_DsReplicaLinkedAttribute, link_count);
+ if (r->out.ctr->ctr6.linked_attributes == NULL) {
+ DEBUG(0, ("Out of memory allocating %u linked attributes for output", link_count));
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ for (k = 0; k < link_count; k++) {
+ r->out.ctr->ctr6.linked_attributes[k] = *la_sorted[k].link;
+ }
+
+ getnc_state->la_idx += link_count;
+ getnc_state->links_given += link_count;
+
+ if (getnc_state->la_idx < getnc_state->la_count) {
+ r->out.ctr->ctr6.more_data = true;
+ } else {
+
+ /*
+ * We've now sent all the links seen so far, so we can
+ * reset la_list back to an empty list again. Note that
+ * the steal means the linked attribute memory gets
+ * freed after this RPC message is sent on the wire.
+ */
+ talloc_steal(mem_ctx, getnc_state->la_list);
+ getnc_state->la_list = NULL;
+ getnc_state->la_idx = 0;
+ getnc_state->la_count = 0;
+ }
+
+ TALLOC_FREE(la_sorted);
+ }
+
+ if (req10->replica_flags & DRSUAPI_DRS_GET_NC_SIZE) {
+ /*
+ * TODO: This implementation is wrong
+ * we should find out the total number of
+ * objects and links in the whole naming context
+ * at the start of the cycle and return these
+ * values in each message.
+ *
+ * For now we keep our current strategy and return
+ * the number of objects for this cycle and the number
+ * of links we found so far during the cycle.
+ */
+ r->out.ctr->ctr6.nc_object_count = getnc_state->num_records;
+ r->out.ctr->ctr6.nc_linked_attributes_count = getnc_state->total_links;
+ }
+
+ if (req10->extended_op != DRSUAPI_EXOP_NONE) {
+ r->out.ctr->ctr6.uptodateness_vector = NULL;
+ r->out.ctr->ctr6.nc_object_count = 0;
+ ZERO_STRUCT(r->out.ctr->ctr6.new_highwatermark);
+ } else if (!r->out.ctr->ctr6.more_data) {
+
+ /* this is the last response in the replication cycle */
+ r->out.ctr->ctr6.new_highwatermark = getnc_state->final_hwm;
+ r->out.ctr->ctr6.uptodateness_vector = talloc_move(mem_ctx,
+ &getnc_state->final_udv);
+
+ /*
+ * Free the state info stored for the replication cycle. Note
+ * that the RPC message we're sending contains links stored in
+ * getnc_state. mem_ctx is local to this RPC call, so the memory
+ * will get freed after the RPC message is sent on the wire.
+ *
+ * We must not do this for an EXOP, as that should not
+ * end the replication state, which is why that is
+ * checked first above.
+ */
+ talloc_steal(mem_ctx, getnc_state);
+ b_state->getncchanges_full_repl_state = NULL;
+ } else {
+ ret = drsuapi_DsReplicaHighWaterMark_cmp(&r->out.ctr->ctr6.old_highwatermark,
+ &r->out.ctr->ctr6.new_highwatermark);
+ if (ret == 0) {
+ /*
+ * We need to make sure that we never return the
+ * same highwatermark within the same replication
+ * cycle more than once. Otherwise we cannot detect
+ * when the client uses an unexptected highwatermark.
+ *
+ * This is a HACK which is needed because our
+ * object ordering is wrong and set tmp_highest_usn
+ * to a value that is higher than what we already
+ * sent to the client (destination dsa).
+ */
+ r->out.ctr->ctr6.new_highwatermark.reserved_usn += 1;
+ }
+
+ getnc_state->last_hwm = r->out.ctr->ctr6.new_highwatermark;
+ }
+
+ TALLOC_FREE(repl_chunk);
+
+ DEBUG(r->out.ctr->ctr6.more_data?4:2,
+ ("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %u/%u) %u links (done %u/%u (as %s))\n",
+ (unsigned long long)(req10->highwatermark.highest_usn+1),
+ req10->replica_flags,
+ drs_ObjectIdentifier_to_debug_string(mem_ctx, untrusted_ncRoot),
+ r->out.ctr->ctr6.object_count,
+ i, r->out.ctr->ctr6.more_data?getnc_state->num_records:i,
+ r->out.ctr->ctr6.linked_attributes_count,
+ getnc_state->links_given, getnc_state->total_links,
+ dom_sid_string(mem_ctx, user_sid)));
+
+#if 0
+ if (!r->out.ctr->ctr6.more_data && req10->extended_op != DRSUAPI_EXOP_NONE) {
+ NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsGetNCChanges, NDR_BOTH, r);
+ }
+#endif
+
+ return WERR_OK;
+}
+
diff --git a/source4/rpc_server/drsuapi/updaterefs.c b/source4/rpc_server/drsuapi/updaterefs.c
new file mode 100644
index 0000000..0be675f
--- /dev/null
+++ b/source4/rpc_server/drsuapi/updaterefs.c
@@ -0,0 +1,411 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ implement the DRSUpdateRefs call
+
+ Copyright (C) Andrew Tridgell 2009
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/security.h"
+#include "libcli/security/session.h"
+#include "rpc_server/drsuapi/dcesrv_drsuapi.h"
+#include "auth/session.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_irpc_c.h"
+#include "lib/messaging/irpc.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DRS_REPL
+
+struct repsTo {
+ uint32_t count;
+ struct repsFromToBlob *r;
+};
+
+static WERROR uref_check_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+ struct ldb_dn *dn, struct GUID *dest_guid,
+ uint32_t options)
+{
+ struct repsTo reps;
+ WERROR werr;
+ unsigned int i;
+ bool found = false;
+
+ werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ for (i=0; i<reps.count; i++) {
+ if (GUID_equal(dest_guid,
+ &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (options & DRSUAPI_DRS_ADD_REF) {
+ if (found && !(options & DRSUAPI_DRS_DEL_REF)) {
+ return WERR_DS_DRA_REF_ALREADY_EXISTS;
+ }
+ }
+
+ if (options & DRSUAPI_DRS_DEL_REF) {
+ if (!found && !(options & DRSUAPI_DRS_ADD_REF)) {
+ return WERR_DS_DRA_REF_NOT_FOUND;
+ }
+ }
+
+ return WERR_OK;
+}
+
+/*
+ add a replication destination for a given partition GUID
+ */
+static WERROR uref_add_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+ struct ldb_dn *dn, struct repsFromTo1 *dest,
+ uint32_t options)
+{
+ struct repsTo reps;
+ WERROR werr;
+ unsigned int i;
+
+ werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ for (i=0; i<reps.count; i++) {
+ if (GUID_equal(&dest->source_dsa_obj_guid,
+ &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
+ if (options & DRSUAPI_DRS_GETCHG_CHECK) {
+ return WERR_OK;
+ } else {
+ return WERR_DS_DRA_REF_ALREADY_EXISTS;
+ }
+ }
+ }
+
+ reps.r = talloc_realloc(mem_ctx, reps.r, struct repsFromToBlob, reps.count+1);
+ if (reps.r == NULL) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ ZERO_STRUCT(reps.r[reps.count]);
+ reps.r[reps.count].version = 1;
+ reps.r[reps.count].ctr.ctr1 = *dest;
+ /* add the GCSPN flag if the client asked for it */
+ reps.r[reps.count].ctr.ctr1.replica_flags |= (options & DRSUAPI_DRS_REF_GCSPN);
+ reps.count++;
+
+ werr = dsdb_savereps(sam_ctx, mem_ctx, dn, "repsTo", reps.r, reps.count);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ return WERR_OK;
+}
+
+/*
+ delete a replication destination for a given partition GUID
+ */
+static WERROR uref_del_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+ struct ldb_dn *dn, struct GUID *dest_guid,
+ uint32_t options)
+{
+ struct repsTo reps;
+ WERROR werr;
+ unsigned int i;
+ bool found = false;
+
+ werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ for (i=0; i<reps.count; i++) {
+ if (GUID_equal(dest_guid,
+ &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
+ if (i+1 < reps.count) {
+ memmove(&reps.r[i], &reps.r[i+1], sizeof(reps.r[i])*(reps.count-(i+1)));
+ }
+ reps.count--;
+ found = true;
+ }
+ }
+
+ werr = dsdb_savereps(sam_ctx, mem_ctx, dn, "repsTo", reps.r, reps.count);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ if (!found &&
+ !(options & DRSUAPI_DRS_GETCHG_CHECK) &&
+ !(options & DRSUAPI_DRS_ADD_REF)) {
+ return WERR_DS_DRA_REF_NOT_FOUND;
+ }
+
+ return WERR_OK;
+}
+
+struct drepl_refresh_state {
+ struct dreplsrv_refresh r;
+};
+
+/**
+ * @brief Update the references for the given NC and the destination DSA object
+ *
+ * This function is callable from non RPC functions (ie. getncchanges), it
+ * will validate the request to update reference and then will add/del a repsTo
+ * to the specified server referenced by its DSA GUID in the request.
+ *
+ * @param[in] msg_ctx Messaging context for sending partition
+ * refresh in dreplsrv
+ *
+ * @param[in] b_state A bind_state object
+ *
+ * @param[in] mem_ctx A talloc context for memory allocation
+ *
+ * @param[in] req A drsuapi_DsReplicaUpdateRefsRequest1
+ * object which NC, which server and which
+ * action (add/delete) should be performed
+ *
+ * @return WERR_OK is success, different error
+ * otherwise.
+ */
+WERROR drsuapi_UpdateRefs(struct imessaging_context *msg_ctx,
+ struct tevent_context *event_ctx,
+ struct drsuapi_bind_state *b_state, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaUpdateRefsRequest1 *req)
+{
+ WERROR werr;
+ int ret;
+ struct ldb_dn *dn_normalised;
+ struct ldb_dn *nc_root;
+ struct ldb_context *sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx;
+ struct dcerpc_binding_handle *irpc_handle;
+ struct tevent_req *subreq;
+ struct drepl_refresh_state *state;
+
+
+ DEBUG(4,("DsReplicaUpdateRefs for host '%s' with GUID %s options 0x%08x nc=%s\n",
+ req->dest_dsa_dns_name, GUID_string(mem_ctx, &req->dest_dsa_guid),
+ req->options,
+ drs_ObjectIdentifier_to_debug_string(mem_ctx, req->naming_context)));
+
+ /*
+ * 4.1.26.2 Server Behavior of the IDL_DRSUpdateRefs Method
+ * Implements the input validation checks
+ */
+ if (GUID_all_zero(&req->dest_dsa_guid)) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ /* FIXME it seems that we should check the length of the stuff too*/
+ if (req->dest_dsa_dns_name == NULL) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ if (!(req->options & (DRSUAPI_DRS_DEL_REF|DRSUAPI_DRS_ADD_REF))) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx, sam_ctx, req->naming_context,
+ &dn_normalised, &nc_root);
+ if (ret != LDB_SUCCESS) {
+ DBG_WARNING("Didn't find a nc for %s: %s\n",
+ drs_ObjectIdentifier_to_debug_string(mem_ctx,
+ req->naming_context),
+ ldb_errstring(sam_ctx));
+ return WERR_DS_DRA_BAD_NC;
+ }
+ if (ldb_dn_compare(dn_normalised, nc_root) != 0) {
+ DBG_NOTICE("dn %s is not equal to %s (from %s)\n",
+ ldb_dn_get_linearized(dn_normalised),
+ ldb_dn_get_linearized(nc_root),
+ drs_ObjectIdentifier_to_debug_string(mem_ctx,
+ req->naming_context));
+ return WERR_DS_DRA_BAD_NC;
+ }
+
+ /*
+ * First check without a transaction open.
+ *
+ * This means that in the usual case, it will never open it and never
+ * bother to refresh the dreplsrv.
+ */
+ werr = uref_check_dest(sam_ctx,
+ mem_ctx,
+ dn_normalised,
+ &req->dest_dsa_guid,
+ req->options);
+ if (W_ERROR_EQUAL(werr, WERR_DS_DRA_REF_ALREADY_EXISTS) ||
+ W_ERROR_EQUAL(werr, WERR_DS_DRA_REF_NOT_FOUND)) {
+ if (req->options & DRSUAPI_DRS_GETCHG_CHECK) {
+ return WERR_OK;
+ }
+ return werr;
+ }
+
+ if (ldb_transaction_start(sam_ctx) != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to start transaction on samdb: %s\n",
+ ldb_errstring(sam_ctx)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ if (req->options & DRSUAPI_DRS_DEL_REF) {
+ werr = uref_del_dest(sam_ctx,
+ mem_ctx,
+ dn_normalised,
+ &req->dest_dsa_guid,
+ req->options);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,("Failed to delete repsTo for %s: %s\n",
+ GUID_string(mem_ctx, &req->dest_dsa_guid),
+ win_errstr(werr)));
+ goto failed;
+ }
+ }
+
+ if (req->options & DRSUAPI_DRS_ADD_REF) {
+ struct repsFromTo1 dest;
+ struct repsFromTo1OtherInfo oi;
+
+ ZERO_STRUCT(dest);
+ ZERO_STRUCT(oi);
+
+ oi.dns_name = req->dest_dsa_dns_name;
+ dest.other_info = &oi;
+ dest.source_dsa_obj_guid = req->dest_dsa_guid;
+ dest.replica_flags = req->options;
+
+ werr = uref_add_dest(sam_ctx,
+ mem_ctx,
+ dn_normalised,
+ &dest,
+ req->options);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,("Failed to add repsTo for %s: %s\n",
+ GUID_string(mem_ctx, &dest.source_dsa_obj_guid),
+ win_errstr(werr)));
+ goto failed;
+ }
+ }
+
+ if (ldb_transaction_commit(sam_ctx) != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to commit transaction on samdb: %s\n",
+ ldb_errstring(sam_ctx)));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ state = talloc_zero(mem_ctx, struct drepl_refresh_state);
+ if (state == NULL) {
+ return WERR_OK;
+ }
+
+ irpc_handle = irpc_binding_handle_by_name(mem_ctx, msg_ctx,
+ "dreplsrv", &ndr_table_irpc);
+ if (irpc_handle == NULL) {
+ /* dreplsrv is not running yet */
+ TALLOC_FREE(state);
+ return WERR_OK;
+ }
+
+ /*
+ * [Taken from auth_sam_trigger_repl_secret in auth_sam.c]
+ *
+ * This seem to rely on the current IRPC implementation,
+ * which delivers the message in the _send function.
+ *
+ * TODO: we need a ONE_WAY IRPC handle and register
+ * a callback and wait for it to be triggered!
+ */
+ subreq = dcerpc_dreplsrv_refresh_r_send(state, event_ctx,
+ irpc_handle, &state->r);
+ TALLOC_FREE(subreq);
+ TALLOC_FREE(state);
+
+ return WERR_OK;
+
+failed:
+ ldb_transaction_cancel(sam_ctx);
+ return werr;
+}
+
+/*
+ drsuapi_DsReplicaUpdateRefs
+*/
+WERROR dcesrv_drsuapi_DsReplicaUpdateRefs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaUpdateRefs *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+ struct dcesrv_handle *h;
+ struct drsuapi_bind_state *b_state;
+ struct drsuapi_DsReplicaUpdateRefsRequest1 *req;
+ WERROR werr;
+ int ret;
+ enum security_user_level security_level;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ if (r->in.level != 1) {
+ DEBUG(0,("DrReplicUpdateRefs - unsupported level %u\n", r->in.level));
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+ req = &r->in.req.req1;
+ werr = drs_security_access_check(b_state->sam_ctx,
+ mem_ctx,
+ session_info->security_token,
+ req->naming_context,
+ GUID_DRS_MANAGE_TOPOLOGY);
+
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ security_level = security_session_user_level(session_info, NULL);
+ if (security_level < SECURITY_ADMINISTRATOR) {
+ /* check that they are using an DSA objectGUID that they own */
+ ret = dsdb_validate_dsa_guid(b_state->sam_ctx,
+ &req->dest_dsa_guid,
+ &session_info->security_token->sids[PRIMARY_USER_SID_INDEX]);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Refusing DsReplicaUpdateRefs for sid %s with GUID %s\n",
+ dom_sid_string(mem_ctx,
+ &session_info->security_token->sids[PRIMARY_USER_SID_INDEX]),
+ GUID_string(mem_ctx, &req->dest_dsa_guid)));
+ return WERR_DS_DRA_ACCESS_DENIED;
+ }
+ }
+
+ werr = drsuapi_UpdateRefs(imsg_ctx,
+ dce_call->event_ctx,
+ b_state,
+ mem_ctx,
+ req);
+
+#if 0
+ NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsReplicaUpdateRefs, NDR_BOTH, r);
+#endif
+
+ return werr;
+}
diff --git a/source4/rpc_server/drsuapi/writespn.c b/source4/rpc_server/drsuapi/writespn.c
new file mode 100644
index 0000000..9e4b533
--- /dev/null
+++ b/source4/rpc_server/drsuapi/writespn.c
@@ -0,0 +1,257 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ implement the DsWriteAccountSpn call
+
+ Copyright (C) Stefan Metzmacher 2009
+ Copyright (C) Andrew Tridgell 2010
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "libcli/security/security.h"
+#include "libcli/security/session.h"
+#include "rpc_server/drsuapi/dcesrv_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "auth/session.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DRS_REPL
+
+#undef strcasecmp
+
+/*
+ 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 auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ 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 = &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;
+ }
+
+ kerr = 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;
+}
+
+/*
+ drsuapi_DsWriteAccountSpn
+*/
+WERROR dcesrv_drsuapi_DsWriteAccountSpn(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsWriteAccountSpn *r)
+{
+ struct drsuapi_bind_state *b_state;
+ struct dcesrv_handle *h;
+
+ *r->out.level_out = r->in.level;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ r->out.res = talloc(mem_ctx, union drsuapi_DsWriteAccountSpnResult);
+ W_ERROR_HAVE_NO_MEMORY(r->out.res);
+
+ switch (r->in.level) {
+ case 1: {
+ struct drsuapi_DsWriteAccountSpnRequest1 *req;
+ struct ldb_message *msg;
+ uint32_t count;
+ unsigned int i;
+ int ret;
+ unsigned spn_count=0;
+ bool passed_checks = true;
+ struct ldb_context *sam_ctx;
+
+ req = &r->in.req->req1;
+ count = req->count;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ msg->dn = ldb_dn_new(msg, b_state->sam_ctx,
+ req->object_dn);
+ if ( ! ldb_dn_validate(msg->dn)) {
+ r->out.res->res1.status = WERR_OK;
+ return WERR_OK;
+ }
+
+ /* construct mods */
+ for (i = 0; i < count; i++) {
+ if (!writespn_check_spn(b_state,
+ dce_call,
+ msg->dn,
+ req->spn_names[i].str)) {
+ passed_checks = false;
+ }
+ ret = ldb_msg_add_string(msg,
+ "servicePrincipalName",
+ req->spn_names[i].str);
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ spn_count++;
+ }
+
+ if (msg->num_elements == 0) {
+ DEBUG(2,("No SPNs need changing on %s\n",
+ ldb_dn_get_linearized(msg->dn)));
+ r->out.res->res1.status = WERR_OK;
+ return WERR_OK;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ switch (req->operation) {
+ case DRSUAPI_DS_SPN_OPERATION_ADD:
+ msg->elements[i].flags = LDB_FLAG_MOD_ADD;
+ break;
+ case DRSUAPI_DS_SPN_OPERATION_REPLACE:
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ break;
+ case DRSUAPI_DS_SPN_OPERATION_DELETE:
+ msg->elements[i].flags = LDB_FLAG_MOD_DELETE;
+ break;
+ }
+ }
+
+ if (passed_checks && b_state->sam_ctx_system) {
+ sam_ctx = b_state->sam_ctx_system;
+ } else {
+ sam_ctx = b_state->sam_ctx;
+ }
+
+ /* Apply to database */
+ ret = dsdb_modify(sam_ctx, msg, DSDB_MODIFY_PERMISSIVE);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to modify SPNs on %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(b_state->sam_ctx)));
+ NDR_PRINT_IN_DEBUG(
+ drsuapi_DsWriteAccountSpn, r);
+ r->out.res->res1.status = WERR_ACCESS_DENIED;
+ } else {
+ DEBUG(2,("Modified %u SPNs on %s\n", spn_count,
+ ldb_dn_get_linearized(msg->dn)));
+ r->out.res->res1.status = WERR_OK;
+ }
+
+ return WERR_OK;
+ }
+ }
+
+ return WERR_INVALID_LEVEL;
+}
diff --git a/source4/rpc_server/echo/rpc_echo.c b/source4/rpc_server/echo/rpc_echo.c
new file mode 100644
index 0000000..b0baf82
--- /dev/null
+++ b/source4/rpc_server/echo/rpc_echo.c
@@ -0,0 +1,211 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the echo pipe
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2005
+
+ 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 "includes.h"
+#include "system/filesys.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_echo.h"
+#include "lib/events/events.h"
+
+#define DCESRV_INTERFACE_RPCECHO_BIND(context, iface) \
+ dcesrv_interface_rpcecho_bind(context, iface)
+static NTSTATUS dcesrv_interface_rpcecho_bind(struct dcesrv_connection_context *context,
+ const struct dcesrv_interface *iface)
+{
+ return dcesrv_interface_bind_allow_connect(context, iface);
+}
+
+static NTSTATUS dcesrv_echo_AddOne(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_AddOne *r)
+{
+ *r->out.out_data = r->in.in_data + 1;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_EchoData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_EchoData *r)
+{
+ if (!r->in.len) {
+ return NT_STATUS_OK;
+ }
+
+ r->out.out_data = (uint8_t *)talloc_memdup(mem_ctx, r->in.in_data, r->in.len);
+ if (!r->out.out_data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_SinkData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_SinkData *r)
+{
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_SourceData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_SourceData *r)
+{
+ unsigned int i;
+
+ r->out.data = talloc_array(mem_ctx, uint8_t, r->in.len);
+ if (!r->out.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<r->in.len;i++) {
+ r->out.data[i] = i;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_TestCall(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestCall *r)
+{
+ *r->out.s2 = talloc_strdup(mem_ctx, r->in.s1);
+ if (r->in.s1 && !*r->out.s2) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_TestCall2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestCall2 *r)
+{
+ r->out.info = talloc(mem_ctx, union echo_Info);
+ if (!r->out.info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ r->out.info->info1.v = 10;
+ break;
+ case 2:
+ r->out.info->info2.v = 20;
+ break;
+ case 3:
+ r->out.info->info3.v = 30;
+ break;
+ case 4:
+ r->out.info->info4.v = 40;
+ break;
+ case 5:
+ r->out.info->info5.v1 = 50;
+ r->out.info->info5.v2 = 60;
+ break;
+ case 6:
+ r->out.info->info6.v1 = 70;
+ r->out.info->info6.info1.v= 80;
+ break;
+ case 7:
+ r->out.info->info7.v1 = 80;
+ r->out.info->info7.info4.v = 90;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_TestEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestEnum *r)
+{
+ r->out.foo2->e1 = ECHO_ENUM2;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_TestSurrounding(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestSurrounding *r)
+{
+ if (!r->in.data) {
+ r->out.data = NULL;
+ return NT_STATUS_OK;
+ }
+
+ r->out.data = talloc(mem_ctx, struct echo_Surrounding);
+ if (!r->out.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.data->x = 2 * r->in.data->x;
+ r->out.data->surrounding = talloc_zero_array(mem_ctx, uint16_t, r->out.data->x);
+ if (!r->out.data->surrounding) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static uint16_t dcesrv_echo_TestDoublePointer(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestDoublePointer *r)
+{
+ if (!*r->in.data)
+ return 0;
+ if (!**r->in.data)
+ return 0;
+ return ***r->in.data;
+}
+
+struct echo_TestSleep_private {
+ struct dcesrv_call_state *dce_call;
+ struct echo_TestSleep *r;
+};
+
+static void echo_TestSleep_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct echo_TestSleep_private *p = talloc_get_type(private_data,
+ struct echo_TestSleep_private);
+ struct echo_TestSleep *r = p->r;
+ NTSTATUS status;
+
+ r->out.result = r->in.seconds;
+
+ status = dcesrv_reply(p->dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("echo_TestSleep_handler: dcesrv_reply() failed - %s\n",
+ nt_errstr(status)));
+ }
+}
+
+static long dcesrv_echo_TestSleep(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestSleep *r)
+{
+ struct echo_TestSleep_private *p;
+
+ if (!(dce_call->state_flags & DCESRV_CALL_STATE_FLAG_MAY_ASYNC)) {
+ /* we're not allowed to reply async */
+ sleep(r->in.seconds);
+ return r->in.seconds;
+ }
+
+ /* we're allowed to reply async */
+ p = talloc(mem_ctx, struct echo_TestSleep_private);
+ if (!p) {
+ return 0;
+ }
+
+ p->dce_call = dce_call;
+ p->r = r;
+
+ tevent_add_timer(dce_call->event_ctx, p,
+ timeval_add(&dce_call->time, r->in.seconds, 0),
+ echo_TestSleep_handler, p);
+
+ dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ return 0;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_echo_s.c"
diff --git a/source4/rpc_server/epmapper/rpc_epmapper.c b/source4/rpc_server/epmapper/rpc_epmapper.c
new file mode 100644
index 0000000..d91fc8c
--- /dev/null
+++ b/source4/rpc_server/epmapper/rpc_epmapper.c
@@ -0,0 +1,294 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the epmapper pipe
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Jelmer Vernooij 2004
+
+ 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 "includes.h"
+#include "librpc/gen_ndr/ndr_epmapper.h"
+#include "rpc_server/dcerpc_server.h"
+
+#define DCESRV_INTERFACE_EPMAPPER_BIND(context, iface) \
+ dcesrv_interface_epmapper_bind(context, iface)
+static NTSTATUS dcesrv_interface_epmapper_bind(struct dcesrv_connection_context *context,
+ const struct dcesrv_interface *iface)
+{
+ return dcesrv_interface_bind_allow_connect(context, iface);
+}
+
+typedef uint32_t error_status_t;
+
+/* handle types for this module */
+enum handle_types {HTYPE_LOOKUP};
+
+/* a endpoint combined with an interface description */
+struct dcesrv_ep_iface {
+ const char *name;
+ struct epm_tower ep;
+};
+
+/*
+ build a list of all interfaces handled by all endpoint servers
+*/
+static uint32_t build_ep_list(TALLOC_CTX *mem_ctx,
+ struct dcesrv_endpoint *endpoint_list,
+ struct dcesrv_ep_iface **eps)
+{
+ struct dcesrv_endpoint *d;
+ uint32_t total = 0;
+ NTSTATUS status;
+
+ *eps = NULL;
+
+ for (d=endpoint_list; d; d=d->next) {
+ struct dcesrv_if_list *iface;
+
+ for (iface=d->interface_list;iface;iface=iface->next) {
+ struct dcerpc_binding *description;
+
+ (*eps) = talloc_realloc(mem_ctx,
+ *eps,
+ struct dcesrv_ep_iface,
+ total + 1);
+ if (!*eps) {
+ return 0;
+ }
+ (*eps)[total].name = iface->iface->name;
+
+ description = dcerpc_binding_dup(*eps, d->ep_description);
+ if (description == NULL) {
+ return 0;
+ }
+
+ status = dcerpc_binding_set_abstract_syntax(description,
+ &iface->iface->syntax_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ return 0;
+ }
+
+ status = dcerpc_binding_build_tower(*eps, description, &(*eps)[total].ep);
+ TALLOC_FREE(description);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Unable to build tower for %s - %s\n",
+ iface->iface->name,
+ nt_errstr(status));
+ continue;
+ }
+ total++;
+ }
+ }
+
+ return total;
+}
+
+
+static error_status_t dcesrv_epm_Insert(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct epm_Insert *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static error_status_t dcesrv_epm_Delete(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_Delete *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ implement epm_Lookup. This call is used to enumerate the interfaces
+ available on a rpc server
+*/
+static error_status_t dcesrv_epm_Lookup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_Lookup *r)
+{
+ struct dcesrv_handle *h;
+ struct rpc_eps {
+ uint32_t count;
+ struct dcesrv_ep_iface *e;
+ } *eps;
+ uint32_t num_ents;
+ unsigned int i;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.entry_handle, HTYPE_LOOKUP);
+
+ eps = h->data;
+
+ if (!eps) {
+ /* this is the first call - fill the list. Subsequent calls
+ will feed from this list, stored in the handle */
+ eps = talloc(h, struct rpc_eps);
+ if (!eps) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+ h->data = eps;
+
+ eps->count = build_ep_list(h, dce_call->conn->dce_ctx->endpoint_list, &eps->e);
+ }
+
+ /* return the next N elements */
+ num_ents = r->in.max_ents;
+ if (num_ents > eps->count) {
+ num_ents = eps->count;
+ }
+
+ *r->out.entry_handle = h->wire_handle;
+ r->out.num_ents = talloc(mem_ctx, uint32_t);
+ *r->out.num_ents = num_ents;
+
+ if (num_ents == 0) {
+ r->out.entries = NULL;
+ ZERO_STRUCTP(r->out.entry_handle);
+ talloc_free(h);
+ return EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ }
+
+ r->out.entries = talloc_array(mem_ctx, struct epm_entry_t, num_ents);
+ if (!r->out.entries) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<num_ents;i++) {
+ ZERO_STRUCT(r->out.entries[i].object);
+ r->out.entries[i].annotation = eps->e[i].name;
+ r->out.entries[i].tower = talloc(mem_ctx, struct epm_twr_t);
+ if (!r->out.entries[i].tower) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+ r->out.entries[i].tower->tower = eps->e[i].ep;
+ }
+
+ eps->count -= num_ents;
+ eps->e += num_ents;
+
+ return EPMAPPER_STATUS_OK;
+}
+
+
+/*
+ implement epm_Map. This is used to find the specific endpoint to talk to given
+ a generic protocol tower
+*/
+static error_status_t dcesrv_epm_Map(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_Map *r)
+{
+ uint32_t count;
+ unsigned int i;
+ struct dcesrv_ep_iface *eps;
+ struct epm_floor *floors;
+ enum dcerpc_transport_t transport;
+ struct ndr_syntax_id ndr_syntax;
+
+ count = build_ep_list(mem_ctx, dce_call->conn->dce_ctx->endpoint_list, &eps);
+
+ ZERO_STRUCT(*r->out.entry_handle);
+ r->out.num_towers = talloc(mem_ctx, uint32_t);
+ if (!r->out.num_towers) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+ *r->out.num_towers = 1;
+ r->out.towers = talloc(mem_ctx, struct epm_twr_p_t);
+ if (!r->out.towers) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+ r->out.towers->twr = talloc(mem_ctx, struct epm_twr_t);
+ if (!r->out.towers->twr) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+
+ if (!r->in.map_tower || r->in.max_towers == 0 ||
+ r->in.map_tower->tower.num_floors < 3) {
+ goto failed;
+ }
+
+ floors = r->in.map_tower->tower.floors;
+
+ dcerpc_floor_get_lhs_data(&r->in.map_tower->tower.floors[1], &ndr_syntax);
+
+ if (floors[1].lhs.protocol != EPM_PROTOCOL_UUID ||
+ !ndr_syntax_id_equal(&ndr_syntax, &ndr_transfer_syntax_ndr)) {
+ goto failed;
+ }
+
+ transport = dcerpc_transport_by_tower(&r->in.map_tower->tower);
+
+ if (transport == -1) {
+ DEBUG(2, ("Client requested unknown transport with levels: "));
+ for (i = 2; i < r->in.map_tower->tower.num_floors; i++) {
+ DEBUG(2, ("%d, ", r->in.map_tower->tower.floors[i].lhs.protocol));
+ }
+ DEBUG(2, ("\n"));
+ goto failed;
+ }
+
+ for (i=0;i<count;i++) {
+ if (
+ data_blob_cmp(&r->in.map_tower->tower.floors[0].lhs.lhs_data,
+ &eps[i].ep.floors[0].lhs.lhs_data) != 0
+ || transport != dcerpc_transport_by_tower(&eps[i].ep)) {
+ continue;
+ }
+
+ r->out.towers->twr->tower = eps[i].ep;
+ r->out.towers->twr->tower_length = 0;
+ return EPMAPPER_STATUS_OK;
+ }
+
+
+failed:
+ *r->out.num_towers = 0;
+ r->out.towers->twr = NULL;
+
+ return EPMAPPER_STATUS_NO_MORE_ENTRIES;
+}
+
+static error_status_t dcesrv_epm_LookupHandleFree(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_LookupHandleFree *r)
+{
+ struct dcesrv_handle *h = NULL;
+
+ r->out.entry_handle = r->in.entry_handle;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.entry_handle, HTYPE_LOOKUP);
+ TALLOC_FREE(h);
+
+ ZERO_STRUCTP(r->out.entry_handle);
+
+ return EPMAPPER_STATUS_OK;
+}
+
+static error_status_t dcesrv_epm_InqObject(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_InqObject *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static error_status_t dcesrv_epm_MgmtDelete(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_MgmtDelete *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static error_status_t dcesrv_epm_MapAuth(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_MapAuth *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_epmapper_s.c"
diff --git a/source4/rpc_server/eventlog/dcesrv_eventlog6.c b/source4/rpc_server/eventlog/dcesrv_eventlog6.c
new file mode 100644
index 0000000..4962984
--- /dev/null
+++ b/source4/rpc_server/eventlog/dcesrv_eventlog6.c
@@ -0,0 +1,331 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the eventlog6 pipe
+
+ Copyright (C) Anatoliy Atanasov 2010
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_eventlog6.h"
+#include "rpc_server/common/common.h"
+
+
+/*
+ eventlog6_EvtRpcRegisterRemoteSubscription
+*/
+static WERROR dcesrv_eventlog6_EvtRpcRegisterRemoteSubscription(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcRegisterRemoteSubscription *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcRemoteSubscriptionNextAsync
+*/
+static WERROR dcesrv_eventlog6_EvtRpcRemoteSubscriptionNextAsync(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcRemoteSubscriptionNextAsync *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcRemoteSubscriptionNext
+*/
+static WERROR dcesrv_eventlog6_EvtRpcRemoteSubscriptionNext(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcRemoteSubscriptionNext *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcRemoteSubscriptionWaitAsync
+*/
+static WERROR dcesrv_eventlog6_EvtRpcRemoteSubscriptionWaitAsync(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcRemoteSubscriptionWaitAsync *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcRegisterControllableOperation
+*/
+static WERROR dcesrv_eventlog6_EvtRpcRegisterControllableOperation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcRegisterControllableOperation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcRegisterLogQuery
+*/
+static WERROR dcesrv_eventlog6_EvtRpcRegisterLogQuery(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcRegisterLogQuery *r)
+{
+ struct dcesrv_handle *handle;
+
+ handle = dcesrv_handle_create(dce_call, 0);
+ W_ERROR_HAVE_NO_MEMORY(handle);
+
+ r->out.handle = &handle->wire_handle;
+
+ handle = dcesrv_handle_create(dce_call, 0);
+ W_ERROR_HAVE_NO_MEMORY(handle);
+
+ r->out.opControl = &handle->wire_handle;
+
+ return WERR_OK;
+}
+
+
+/*
+ eventlog6_EvtRpcClearLog
+*/
+static WERROR dcesrv_eventlog6_EvtRpcClearLog(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcClearLog *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcExportLog
+*/
+static WERROR dcesrv_eventlog6_EvtRpcExportLog(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcExportLog *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcLocalizeExportLog
+*/
+static WERROR dcesrv_eventlog6_EvtRpcLocalizeExportLog(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcLocalizeExportLog *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcMessageRender
+*/
+static WERROR dcesrv_eventlog6_EvtRpcMessageRender(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcMessageRender *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcMessageRenderDefault
+*/
+static WERROR dcesrv_eventlog6_EvtRpcMessageRenderDefault(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcMessageRenderDefault *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcQueryNext
+*/
+static WERROR dcesrv_eventlog6_EvtRpcQueryNext(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcQueryNext *r)
+{
+ return WERR_OK;
+}
+
+
+/*
+ eventlog6_EvtRpcQuerySeek
+*/
+static WERROR dcesrv_eventlog6_EvtRpcQuerySeek(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcQuerySeek *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcClose
+*/
+static WERROR dcesrv_eventlog6_EvtRpcClose(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcClose *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcCancel
+*/
+static WERROR dcesrv_eventlog6_EvtRpcCancel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcCancel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcAssertConfig
+*/
+static WERROR dcesrv_eventlog6_EvtRpcAssertConfig(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcAssertConfig *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcRetractConfig
+*/
+static WERROR dcesrv_eventlog6_EvtRpcRetractConfig(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcRetractConfig *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcOpenLogHandle
+*/
+static WERROR dcesrv_eventlog6_EvtRpcOpenLogHandle(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcOpenLogHandle *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcGetLogFileInfo
+*/
+static WERROR dcesrv_eventlog6_EvtRpcGetLogFileInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcGetLogFileInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcGetChannelList
+*/
+static WERROR dcesrv_eventlog6_EvtRpcGetChannelList(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcGetChannelList *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcGetChannelConfig
+*/
+static WERROR dcesrv_eventlog6_EvtRpcGetChannelConfig(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcGetChannelConfig *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcPutChannelConfig
+*/
+static WERROR dcesrv_eventlog6_EvtRpcPutChannelConfig(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcPutChannelConfig *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcGetPublisherList
+*/
+static WERROR dcesrv_eventlog6_EvtRpcGetPublisherList(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcGetPublisherList *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcGetPublisherListForChannel
+*/
+static WERROR dcesrv_eventlog6_EvtRpcGetPublisherListForChannel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcGetPublisherListForChannel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcGetPublisherMetadata
+*/
+static WERROR dcesrv_eventlog6_EvtRpcGetPublisherMetadata(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcGetPublisherMetadata *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcGetPublisherResourceMetadata
+*/
+static WERROR dcesrv_eventlog6_EvtRpcGetPublisherResourceMetadata(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcGetPublisherResourceMetadata *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcGetEventMetadataEnum
+*/
+static WERROR dcesrv_eventlog6_EvtRpcGetEventMetadataEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcGetEventMetadataEnum *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcGetNextEventMetadata
+*/
+static WERROR dcesrv_eventlog6_EvtRpcGetNextEventMetadata(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcGetNextEventMetadata *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ eventlog6_EvtRpcGetClassicLogDisplayName
+*/
+static WERROR dcesrv_eventlog6_EvtRpcGetClassicLogDisplayName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct eventlog6_EvtRpcGetClassicLogDisplayName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_eventlog6_s.c"
diff --git a/source4/rpc_server/lsa/dcesrv_lsa.c b/source4/rpc_server/lsa/dcesrv_lsa.c
new file mode 100644
index 0000000..575bf84
--- /dev/null
+++ b/source4/rpc_server/lsa/dcesrv_lsa.c
@@ -0,0 +1,4847 @@
+/* need access mask/acl implementation */
+
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the lsarpc pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2008
+
+ 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 "rpc_server/lsa/lsa.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "lib/util/tsort.h"
+#include "dsdb/common/util.h"
+#include "libcli/security/session.h"
+#include "libcli/lsarpc/util_lsarpc.h"
+#include "lib/messaging/irpc.h"
+#include "libds/common/roles.h"
+#include "lib/util/smb_strtox.h"
+#include "lib/param/loadparm.h"
+#include "librpc/rpc/dcerpc_helper.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#undef strcasecmp
+
+#define DCESRV_INTERFACE_LSARPC_BIND(context, iface) \
+ dcesrv_interface_lsarpc_bind(context, iface)
+static NTSTATUS dcesrv_interface_lsarpc_bind(struct dcesrv_connection_context *context,
+ const struct dcesrv_interface *iface)
+{
+ return dcesrv_interface_bind_reject_connect(context, iface);
+}
+
+static NTSTATUS lsarpc__op_init_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+static const struct dcesrv_interface dcesrv_lsarpc_interface;
+
+#define NCACN_NP_PIPE_NETLOGON "ncacn_np:[\\pipe\\netlogon]"
+#define NCACN_NP_PIPE_LSASS "ncacn_np:[\\pipe\\lsass]"
+#define DCESRV_INTERFACE_LSARPC_NCACN_NP_SECONDARY_ENDPOINT NCACN_NP_PIPE_LSASS
+
+#define DCESRV_INTERFACE_LSARPC_INIT_SERVER \
+ dcesrv_interface_lsarpc_init_server
+static NTSTATUS dcesrv_interface_lsarpc_init_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ if (lpcfg_lsa_over_netlogon(dce_ctx->lp_ctx)) {
+ NTSTATUS ret = dcesrv_interface_register(dce_ctx,
+ NCACN_NP_PIPE_NETLOGON,
+ NCACN_NP_PIPE_LSASS,
+ &dcesrv_lsarpc_interface, NULL);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1,("lsarpc_op_init_server: failed to register endpoint '\\pipe\\netlogon'\n"));
+ return ret;
+ }
+ }
+ return lsarpc__op_init_server(dce_ctx, ep_server);
+}
+
+/*
+ this type allows us to distinguish handle types
+*/
+
+/*
+ state associated with a lsa_OpenAccount() operation
+*/
+struct lsa_account_state {
+ struct lsa_policy_state *policy;
+ uint32_t access_mask;
+ struct dom_sid *account_sid;
+};
+
+
+/*
+ state associated with a lsa_OpenSecret() operation
+*/
+struct lsa_secret_state {
+ struct lsa_policy_state *policy;
+ uint32_t access_mask;
+ struct ldb_dn *secret_dn;
+ struct ldb_context *sam_ldb;
+ bool global;
+};
+
+/*
+ state associated with a lsa_OpenTrustedDomain() operation
+*/
+struct lsa_trusted_domain_state {
+ struct lsa_policy_state *policy;
+ uint32_t access_mask;
+ struct ldb_dn *trusted_domain_dn;
+ struct ldb_dn *trusted_domain_user_dn;
+};
+
+static bool dcesrc_lsa_valid_AccountRight(const char *right)
+{
+ enum sec_privilege priv_id;
+ uint32_t right_bit;
+
+ priv_id = sec_privilege_id(right);
+ if (priv_id != SEC_PRIV_INVALID) {
+ return true;
+ }
+
+ right_bit = sec_right_bit(right);
+ if (right_bit != 0) {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ this is based on the samba3 function make_lsa_object_sd()
+ It uses the same logic, but with samba4 helper functions
+ */
+static NTSTATUS dcesrv_build_lsa_sd(TALLOC_CTX *mem_ctx,
+ struct security_descriptor **sd,
+ struct dom_sid *sid,
+ uint32_t sid_access)
+{
+ NTSTATUS status;
+ uint32_t rid;
+ struct dom_sid *domain_sid, *domain_admins_sid;
+ const char *domain_admins_sid_str, *sidstr;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ status = dom_sid_split_rid(tmp_ctx, sid, &domain_sid, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(tmp_ctx);
+ return status;
+ }
+
+ domain_admins_sid = dom_sid_add_rid(tmp_ctx, domain_sid, DOMAIN_RID_ADMINS);
+ if (domain_admins_sid == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ domain_admins_sid_str = dom_sid_string(tmp_ctx, domain_admins_sid);
+ if (domain_admins_sid_str == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sidstr = dom_sid_string(tmp_ctx, sid);
+ if (sidstr == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *sd = security_descriptor_dacl_create(mem_ctx,
+ 0, sidstr, NULL,
+
+ SID_WORLD,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_EXECUTE | SEC_GENERIC_READ, 0,
+
+ SID_BUILTIN_ADMINISTRATORS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL, 0,
+
+ SID_BUILTIN_ACCOUNT_OPERATORS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL, 0,
+
+ domain_admins_sid_str,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL, 0,
+
+ sidstr,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sid_access, 0,
+
+ NULL);
+ talloc_free(tmp_ctx);
+
+ NT_STATUS_HAVE_NO_MEMORY(*sd);
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS dcesrv_lsa_EnumAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_EnumAccountRights *r);
+
+static NTSTATUS dcesrv_lsa_AddRemoveAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_policy_state *state,
+ int ldb_flag,
+ struct dom_sid *sid,
+ const struct lsa_RightSet *rights);
+
+/*
+ lsa_Close
+*/
+static NTSTATUS dcesrv_lsa_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_Close *r)
+{
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
+ struct dcesrv_handle *h;
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ *r->out.handle = *r->in.handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_Delete
+*/
+static NTSTATUS dcesrv_lsa_Delete(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_Delete *r)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+
+/*
+ lsa_DeleteObject
+*/
+static NTSTATUS dcesrv_lsa_DeleteObject(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_DeleteObject *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *h;
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ if (h->wire_handle.handle_type == LSA_HANDLE_SECRET) {
+ struct lsa_secret_state *secret_state = h->data;
+
+ /* Ensure user is permitted to delete this... */
+ switch (security_session_user_level(session_info, NULL))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ break;
+ default:
+ /* Users and anonymous are not allowed to delete things */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ ret = ldb_delete(secret_state->sam_ldb,
+ secret_state->secret_dn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+
+ } else if (h->wire_handle.handle_type == LSA_HANDLE_TRUSTED_DOMAIN) {
+ struct lsa_trusted_domain_state *trusted_domain_state =
+ talloc_get_type(h->data, struct lsa_trusted_domain_state);
+ ret = ldb_transaction_start(trusted_domain_state->policy->sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ ret = ldb_delete(trusted_domain_state->policy->sam_ldb,
+ trusted_domain_state->trusted_domain_dn);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (trusted_domain_state->trusted_domain_user_dn) {
+ ret = ldb_delete(trusted_domain_state->policy->sam_ldb,
+ trusted_domain_state->trusted_domain_user_dn);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ }
+
+ ret = ldb_transaction_commit(trusted_domain_state->policy->sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+
+ } else if (h->wire_handle.handle_type == LSA_HANDLE_ACCOUNT) {
+ struct lsa_RightSet *rights;
+ struct lsa_account_state *astate;
+ struct lsa_EnumAccountRights r2;
+ NTSTATUS status;
+
+ rights = talloc(mem_ctx, struct lsa_RightSet);
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ r2.in.handle = &astate->policy->handle->wire_handle;
+ r2.in.sid = astate->account_sid;
+ r2.out.rights = rights;
+
+ /* dcesrv_lsa_EnumAccountRights takes a LSA_HANDLE_POLICY,
+ but we have a LSA_HANDLE_ACCOUNT here, so this call
+ will always fail */
+ status = dcesrv_lsa_EnumAccountRights(dce_call, mem_ctx, &r2);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, astate->policy,
+ LDB_FLAG_MOD_DELETE, astate->account_sid,
+ r2.out.rights);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_INVALID_HANDLE;
+}
+
+
+/*
+ lsa_EnumPrivs
+*/
+static NTSTATUS dcesrv_lsa_EnumPrivs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_EnumPrivs *r)
+{
+ struct dcesrv_handle *h;
+ uint32_t i;
+ enum sec_privilege priv;
+ const char *privname;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ i = *r->in.resume_handle;
+
+ while (((priv = sec_privilege_from_index(i)) != SEC_PRIV_INVALID) &&
+ r->out.privs->count < r->in.max_count) {
+ struct lsa_PrivEntry *e;
+ privname = sec_privilege_name(priv);
+ r->out.privs->privs = talloc_realloc(r->out.privs,
+ r->out.privs->privs,
+ struct lsa_PrivEntry,
+ r->out.privs->count+1);
+ if (r->out.privs->privs == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ e = &r->out.privs->privs[r->out.privs->count];
+ e->luid.low = priv;
+ e->luid.high = 0;
+ e->name.string = privname;
+ r->out.privs->count++;
+ i++;
+ }
+
+ *r->out.resume_handle = i;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_QuerySecObj
+*/
+static NTSTATUS dcesrv_lsa_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QuerySecurity *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *h;
+ const struct security_descriptor *sd = NULL;
+ uint32_t access_granted = 0;
+ struct sec_desc_buf *sdbuf = NULL;
+ NTSTATUS status;
+ struct dom_sid *sid;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ sid = &session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
+
+ if (h->wire_handle.handle_type == LSA_HANDLE_POLICY) {
+ struct lsa_policy_state *pstate = h->data;
+
+ sd = pstate->sd;
+ access_granted = pstate->access_mask;
+
+ } else if (h->wire_handle.handle_type == LSA_HANDLE_ACCOUNT) {
+ struct lsa_account_state *astate = h->data;
+ struct security_descriptor *_sd = NULL;
+
+ status = dcesrv_build_lsa_sd(mem_ctx, &_sd, sid,
+ LSA_ACCOUNT_ALL_ACCESS);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ sd = _sd;
+ access_granted = astate->access_mask;
+ } else {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ sdbuf = talloc_zero(mem_ctx, struct sec_desc_buf);
+ if (sdbuf == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = security_descriptor_for_client(sdbuf, sd, r->in.sec_info,
+ access_granted, &sdbuf->sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *r->out.sdbuf = sdbuf;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_SetSecObj
+*/
+static NTSTATUS dcesrv_lsa_SetSecObj(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetSecObj *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_ChangePassword
+*/
+static NTSTATUS dcesrv_lsa_ChangePassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_ChangePassword *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ dssetup_DsRoleGetPrimaryDomainInformation
+
+ This is not an LSA call, but is the only call left on the DSSETUP
+ pipe (after the pipe was truncated), and needs lsa_get_policy_state
+*/
+static WERROR dcesrv_dssetup_DsRoleGetPrimaryDomainInformation(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleGetPrimaryDomainInformation *r)
+{
+ union dssetup_DsRoleInfo *info;
+
+ info = talloc_zero(mem_ctx, union dssetup_DsRoleInfo);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ switch (r->in.level) {
+ case DS_ROLE_BASIC_INFORMATION:
+ {
+ enum dssetup_DsRole role = DS_ROLE_STANDALONE_SERVER;
+ uint32_t flags = 0;
+ const char *domain = NULL;
+ const char *dns_domain = NULL;
+ const char *forest = NULL;
+ struct GUID domain_guid;
+ struct lsa_policy_state *state;
+
+ NTSTATUS status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx,
+ 0, /* we skip access checks */
+ &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ ZERO_STRUCT(domain_guid);
+
+ switch (lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx)) {
+ case ROLE_STANDALONE:
+ role = DS_ROLE_STANDALONE_SERVER;
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ role = DS_ROLE_MEMBER_SERVER;
+ break;
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ if (samdb_is_pdc(state->sam_ldb)) {
+ role = DS_ROLE_PRIMARY_DC;
+ } else {
+ role = DS_ROLE_BACKUP_DC;
+ }
+ break;
+ }
+
+ switch (lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx)) {
+ case ROLE_STANDALONE:
+ domain = talloc_strdup(mem_ctx, lpcfg_workgroup(dce_call->conn->dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(domain);
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ domain = talloc_strdup(mem_ctx, lpcfg_workgroup(dce_call->conn->dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(domain);
+ /* TODO: what is with dns_domain and forest and guid? */
+ break;
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ flags = DS_ROLE_PRIMARY_DS_RUNNING;
+
+ if (state->mixed_domain == 1) {
+ flags |= DS_ROLE_PRIMARY_DS_MIXED_MODE;
+ }
+
+ domain = state->domain_name;
+ dns_domain = state->domain_dns;
+ forest = state->forest_dns;
+
+ domain_guid = state->domain_guid;
+ flags |= DS_ROLE_PRIMARY_DOMAIN_GUID_PRESENT;
+ break;
+ }
+
+ info->basic.role = role;
+ info->basic.flags = flags;
+ info->basic.domain = domain;
+ info->basic.dns_domain = dns_domain;
+ info->basic.forest = forest;
+ info->basic.domain_guid = domain_guid;
+
+ r->out.info = info;
+ return WERR_OK;
+ }
+ case DS_ROLE_UPGRADE_STATUS:
+ {
+ info->upgrade.upgrading = DS_ROLE_NOT_UPGRADING;
+ info->upgrade.previous_role = DS_ROLE_PREVIOUS_UNKNOWN;
+
+ r->out.info = info;
+ return WERR_OK;
+ }
+ case DS_ROLE_OP_STATUS:
+ {
+ info->opstatus.status = DS_ROLE_OP_IDLE;
+
+ r->out.info = info;
+ return WERR_OK;
+ }
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+}
+
+/*
+ fill in the AccountDomain info
+*/
+static NTSTATUS dcesrv_lsa_info_AccountDomain(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
+ struct lsa_DomainInfo *info)
+{
+ info->name.string = state->domain_name;
+ info->sid = state->domain_sid;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ fill in the DNS domain info
+*/
+static NTSTATUS dcesrv_lsa_info_DNS(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
+ struct lsa_DnsDomainInfo *info)
+{
+ info->name.string = state->domain_name;
+ info->sid = state->domain_sid;
+ info->dns_domain.string = state->domain_dns;
+ info->dns_forest.string = state->forest_dns;
+ info->domain_guid = state->domain_guid;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_QueryInfoPolicy2
+*/
+static NTSTATUS dcesrv_lsa_QueryInfoPolicy2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QueryInfoPolicy2 *r)
+{
+ struct lsa_policy_state *state;
+ struct dcesrv_handle *h;
+ union lsa_PolicyInformation *info;
+
+ *r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ info = talloc_zero(mem_ctx, union lsa_PolicyInformation);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.info = info;
+
+ switch (r->in.level) {
+ case LSA_POLICY_INFO_AUDIT_LOG:
+ /* we don't need to fill in any of this */
+ ZERO_STRUCT(info->audit_log);
+ return NT_STATUS_OK;
+ case LSA_POLICY_INFO_AUDIT_EVENTS:
+ /* we don't need to fill in any of this */
+ ZERO_STRUCT(info->audit_events);
+ return NT_STATUS_OK;
+ case LSA_POLICY_INFO_PD:
+ /* we don't need to fill in any of this */
+ ZERO_STRUCT(info->pd);
+ return NT_STATUS_OK;
+
+ case LSA_POLICY_INFO_DOMAIN:
+ return dcesrv_lsa_info_AccountDomain(state, mem_ctx, &info->domain);
+ case LSA_POLICY_INFO_ACCOUNT_DOMAIN:
+ return dcesrv_lsa_info_AccountDomain(state, mem_ctx, &info->account_domain);
+ case LSA_POLICY_INFO_L_ACCOUNT_DOMAIN:
+ return dcesrv_lsa_info_AccountDomain(state, mem_ctx, &info->l_account_domain);
+
+ case LSA_POLICY_INFO_ROLE:
+ info->role.role = LSA_ROLE_PRIMARY;
+ return NT_STATUS_OK;
+
+ case LSA_POLICY_INFO_DNS:
+ case LSA_POLICY_INFO_DNS_INT:
+ return dcesrv_lsa_info_DNS(state, mem_ctx, &info->dns);
+
+ case LSA_POLICY_INFO_REPLICA:
+ ZERO_STRUCT(info->replica);
+ return NT_STATUS_OK;
+
+ case LSA_POLICY_INFO_QUOTA:
+ ZERO_STRUCT(info->quota);
+ return NT_STATUS_OK;
+
+ case LSA_POLICY_INFO_MOD:
+ case LSA_POLICY_INFO_AUDIT_FULL_SET:
+ case LSA_POLICY_INFO_AUDIT_FULL_QUERY:
+ /* windows gives INVALID_PARAMETER */
+ *r->out.info = NULL;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *r->out.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+/*
+ lsa_QueryInfoPolicy
+*/
+static NTSTATUS dcesrv_lsa_QueryInfoPolicy(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QueryInfoPolicy *r)
+{
+ struct lsa_QueryInfoPolicy2 r2;
+ NTSTATUS status;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.handle = r->in.handle;
+ r2.in.level = r->in.level;
+ r2.out.info = r->out.info;
+
+ status = dcesrv_lsa_QueryInfoPolicy2(dce_call, mem_ctx, &r2);
+
+ return status;
+}
+
+/*
+ lsa_SetInfoPolicy
+*/
+static NTSTATUS dcesrv_lsa_SetInfoPolicy(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetInfoPolicy *r)
+{
+ /* need to support this */
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_ClearAuditLog
+*/
+static NTSTATUS dcesrv_lsa_ClearAuditLog(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_ClearAuditLog *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+static const struct generic_mapping dcesrv_lsa_account_mapping = {
+ LSA_ACCOUNT_READ,
+ LSA_ACCOUNT_WRITE,
+ LSA_ACCOUNT_EXECUTE,
+ LSA_ACCOUNT_ALL_ACCESS
+};
+
+/*
+ lsa_CreateAccount
+
+ This call does not seem to have any long-term effects, hence no database operations
+
+ we need to talk to the MS product group to find out what this account database means!
+
+ answer is that the lsa database is totally separate from the SAM and
+ ldap databases. We are going to need a separate ldb to store these
+ accounts. The SIDs on this account bear no relation to the SIDs in
+ AD
+*/
+static NTSTATUS dcesrv_lsa_CreateAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CreateAccount *r)
+{
+ struct lsa_account_state *astate;
+
+ struct lsa_policy_state *state;
+ struct dcesrv_handle *h, *ah;
+
+ ZERO_STRUCTP(r->out.acct_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ astate = talloc(dce_call->conn, struct lsa_account_state);
+ if (astate == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ astate->account_sid = dom_sid_dup(astate, r->in.sid);
+ if (astate->account_sid == NULL) {
+ talloc_free(astate);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ astate->policy = talloc_reference(astate, state);
+ astate->access_mask = r->in.access_mask;
+
+ /*
+ * For now we grant all requested access.
+ *
+ * We will fail at the ldb layer later.
+ */
+ if (astate->access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
+ astate->access_mask &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+ astate->access_mask |= LSA_ACCOUNT_ALL_ACCESS;
+ }
+ se_map_generic(&astate->access_mask, &dcesrv_lsa_account_mapping);
+
+ DEBUG(10,("%s: %s access desired[0x%08X] granted[0x%08X].\n",
+ __func__, dom_sid_string(mem_ctx, astate->account_sid),
+ (unsigned)r->in.access_mask,
+ (unsigned)astate->access_mask));
+
+ ah = dcesrv_handle_create(dce_call, LSA_HANDLE_ACCOUNT);
+ if (!ah) {
+ talloc_free(astate);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ah->data = talloc_steal(ah, astate);
+
+ *r->out.acct_handle = ah->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_EnumAccounts
+*/
+static NTSTATUS dcesrv_lsa_EnumAccounts(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_EnumAccounts *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int ret;
+ struct ldb_message **res;
+ const char * const attrs[] = { "objectSid", NULL};
+ uint32_t count, i;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ /* NOTE: This call must only return accounts that have at least
+ one privilege set
+ */
+ ret = gendb_search(state->pdb, mem_ctx, NULL, &res, attrs,
+ "(&(objectSid=*)(privilege=*))");
+ if (ret < 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (*r->in.resume_handle >= ret) {
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ count = ret - *r->in.resume_handle;
+ if (count > r->in.num_entries) {
+ count = r->in.num_entries;
+ }
+
+ if (count == 0) {
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ r->out.sids->sids = talloc_array(r->out.sids, struct lsa_SidPtr, count);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<count;i++) {
+ r->out.sids->sids[i].sid =
+ samdb_result_dom_sid(r->out.sids->sids,
+ res[i + *r->in.resume_handle],
+ "objectSid");
+ NT_STATUS_HAVE_NO_MEMORY(r->out.sids->sids[i].sid);
+ }
+
+ r->out.sids->num_sids = count;
+ *r->out.resume_handle = count + *r->in.resume_handle;
+
+ return NT_STATUS_OK;
+}
+
+/* This decrypts and returns Trusted Domain Auth Information Internal data */
+static NTSTATUS get_trustdom_auth_blob(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx, DATA_BLOB *auth_blob,
+ struct trustDomainPasswords *auth_struct)
+{
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ enum ndr_err_code ndr_err;
+ NTSTATUS nt_status;
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t _session_key;
+ int rc;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ bool encrypted;
+
+ encrypted =
+ dcerpc_is_transport_encrypted(session_info);
+ if (lpcfg_weak_crypto(lp_ctx) == SAMBA_WEAK_CRYPTO_DISALLOWED &&
+ !encrypted) {
+ DBG_ERR("Transport isn't encrypted and weak crypto disallowed!\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+
+ nt_status = dcesrv_transport_session_key(dce_call, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ _session_key = (gnutls_datum_t) {
+ .data = session_key.data,
+ .size = session_key.length,
+ };
+
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &_session_key,
+ NULL);
+ if (rc < 0) {
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ nt_status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ rc = gnutls_cipher_decrypt(cipher_hnd,
+ auth_blob->data,
+ auth_blob->length);
+ gnutls_cipher_deinit(cipher_hnd);
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ if (rc < 0) {
+ nt_status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ ndr_err = ndr_pull_struct_blob(auth_blob, mem_ctx,
+ auth_struct,
+ (ndr_pull_flags_fn_t)ndr_pull_trustDomainPasswords);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt_status = NT_STATUS_OK;
+out:
+ return nt_status;
+}
+
+static NTSTATUS get_trustauth_inout_blob(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct trustAuthInOutBlob *iopw,
+ DATA_BLOB *trustauth_blob)
+{
+ enum ndr_err_code ndr_err;
+
+ if (iopw->current.count != iopw->count) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (iopw->previous.count > iopw->current.count) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (iopw->previous.count == 0) {
+ /*
+ * If the previous credentials are not present
+ * we need to make a copy.
+ */
+ iopw->previous = iopw->current;
+ }
+
+ if (iopw->previous.count < iopw->current.count) {
+ struct AuthenticationInformationArray *c = &iopw->current;
+ struct AuthenticationInformationArray *p = &iopw->previous;
+
+ /*
+ * The previous array needs to have the same size
+ * as the current one.
+ *
+ * We may have to fill with TRUST_AUTH_TYPE_NONE
+ * elements.
+ */
+ p->array = talloc_realloc(mem_ctx, p->array,
+ struct AuthenticationInformation,
+ c->count);
+ if (p->array == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while (p->count < c->count) {
+ struct AuthenticationInformation *a =
+ &p->array[p->count++];
+
+ *a = (struct AuthenticationInformation) {
+ .LastUpdateTime = p->array[0].LastUpdateTime,
+ .AuthType = TRUST_AUTH_TYPE_NONE,
+ };
+ }
+ }
+
+ ndr_err = ndr_push_struct_blob(trustauth_blob, mem_ctx,
+ iopw,
+ (ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS add_trust_user(TALLOC_CTX *mem_ctx,
+ struct ldb_context *sam_ldb,
+ struct ldb_dn *base_dn,
+ const char *netbios_name,
+ struct trustAuthInOutBlob *in,
+ struct ldb_dn **user_dn)
+{
+ struct ldb_request *req;
+ struct ldb_message *msg;
+ struct ldb_dn *dn;
+ uint32_t i;
+ int ret;
+
+ dn = ldb_dn_copy(mem_ctx, base_dn);
+ if (!dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!ldb_dn_add_child_fmt(dn, "cn=%s$,cn=users", netbios_name)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (!msg) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ msg->dn = dn;
+
+ ret = ldb_msg_add_string(msg, "objectClass", "user");
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_msg_add_fmt(msg, "samAccountName", "%s$", netbios_name);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = samdb_msg_add_uint(sam_ldb, msg, msg, "userAccountControl",
+ UF_INTERDOMAIN_TRUST_ACCOUNT);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < in->count; i++) {
+ const char *attribute;
+ struct ldb_val v;
+ switch (in->current.array[i].AuthType) {
+ case TRUST_AUTH_TYPE_NT4OWF:
+ attribute = "unicodePwd";
+ v.data = (uint8_t *)&in->current.array[i].AuthInfo.nt4owf.password;
+ v.length = 16;
+ break;
+ case TRUST_AUTH_TYPE_CLEAR:
+ attribute = "clearTextPassword";
+ v.data = in->current.array[i].AuthInfo.clear.password;
+ v.length = in->current.array[i].AuthInfo.clear.size;
+ break;
+ default:
+ continue;
+ }
+
+ ret = ldb_msg_add_value(msg, attribute, &v, NULL);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* create the trusted_domain user account */
+ ret = ldb_build_add_req(&req, sam_ldb, mem_ctx, msg, NULL, NULL,
+ ldb_op_default_callback, NULL);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_request_add_control(req, DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID,
+ false, NULL);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dsdb_autotransaction_request(sam_ldb, req);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(sam_ldb)));
+
+ switch (ret) {
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ return NT_STATUS_DOMAIN_EXISTS;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ if (user_dn) {
+ *user_dn = dn;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_CreateTrustedDomainEx2
+*/
+static NTSTATUS dcesrv_lsa_CreateTrustedDomain_base(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_CreateTrustedDomainEx2 *r,
+ int op,
+ struct lsa_TrustDomainInfoAuthInfo *unencrypted_auth_info)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_policy_state *policy_state;
+ struct lsa_trusted_domain_state *trusted_domain_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs, *msg;
+ const char *attrs[] = {
+ NULL
+ };
+ const char *netbios_name;
+ const char *dns_name;
+ DATA_BLOB trustAuthIncoming, trustAuthOutgoing, auth_blob;
+ struct trustDomainPasswords auth_struct;
+ int ret;
+ NTSTATUS nt_status;
+ struct ldb_context *sam_ldb;
+ struct server_id *server_ids = NULL;
+ uint32_t num_server_ids = 0;
+ NTSTATUS status;
+ bool ok;
+ char *dns_encoded = NULL;
+ char *netbios_encoded = NULL;
+ char *sid_encoded = NULL;
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.policy_handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.trustdom_handle);
+
+ policy_state = policy_handle->data;
+ sam_ldb = policy_state->sam_ldb;
+
+ netbios_name = r->in.info->netbios_name.string;
+ if (!netbios_name) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ dns_name = r->in.info->domain_name.string;
+ if (dns_name == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.info->sid == NULL) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ /*
+ * We expect S-1-5-21-A-B-C, but we don't
+ * allow S-1-5-21-0-0-0 as this is used
+ * for claims and compound identities.
+ */
+ ok = dom_sid_is_valid_account_domain(r->in.info->sid);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ dns_encoded = ldb_binary_encode_string(mem_ctx, dns_name);
+ if (dns_encoded == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ netbios_encoded = ldb_binary_encode_string(mem_ctx, netbios_name);
+ if (netbios_encoded == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ sid_encoded = ldap_encode_ndr_dom_sid(mem_ctx, r->in.info->sid);
+ if (sid_encoded == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ trusted_domain_state = talloc_zero(mem_ctx, struct lsa_trusted_domain_state);
+ if (!trusted_domain_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ trusted_domain_state->policy = policy_state;
+
+ if (strcasecmp(netbios_name, "BUILTIN") == 0
+ || (strcasecmp(dns_name, "BUILTIN") == 0)
+ || (dom_sid_in_domain(policy_state->builtin_sid, r->in.info->sid))) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strcasecmp(netbios_name, policy_state->domain_name) == 0
+ || strcasecmp(netbios_name, policy_state->domain_dns) == 0
+ || strcasecmp(dns_name, policy_state->domain_dns) == 0
+ || strcasecmp(dns_name, policy_state->domain_name) == 0
+ || (dom_sid_equal(policy_state->domain_sid, r->in.info->sid))) {
+ return NT_STATUS_CURRENT_DOMAIN_NOT_ALLOWED;
+ }
+
+ /* While this is a REF pointer, some of the functions that wrap this don't provide this */
+ if (op == NDR_LSA_CREATETRUSTEDDOMAIN) {
+ /* No secrets are created at this time, for this function */
+ auth_struct.outgoing.count = 0;
+ auth_struct.incoming.count = 0;
+ } else if (op == NDR_LSA_CREATETRUSTEDDOMAINEX2) {
+ auth_blob = data_blob_const(r->in.auth_info_internal->auth_blob.data,
+ r->in.auth_info_internal->auth_blob.size);
+ nt_status = get_trustdom_auth_blob(dce_call, mem_ctx,
+ &auth_blob, &auth_struct);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ } else if (op == NDR_LSA_CREATETRUSTEDDOMAINEX) {
+
+ if (unencrypted_auth_info->incoming_count > 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* more investigation required here, do not create secrets for
+ * now */
+ auth_struct.outgoing.count = 0;
+ auth_struct.incoming.count = 0;
+ } else {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (auth_struct.incoming.count) {
+ nt_status = get_trustauth_inout_blob(dce_call, mem_ctx,
+ &auth_struct.incoming,
+ &trustAuthIncoming);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ } else {
+ trustAuthIncoming = data_blob(NULL, 0);
+ }
+
+ if (auth_struct.outgoing.count) {
+ nt_status = get_trustauth_inout_blob(dce_call, mem_ctx,
+ &auth_struct.outgoing,
+ &trustAuthOutgoing);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ } else {
+ trustAuthOutgoing = data_blob(NULL, 0);
+ }
+
+ ret = ldb_transaction_start(sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* search for the trusted_domain record */
+ ret = gendb_search(sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(objectClass=trustedDomain)(|"
+ "(flatname=%s)(trustPartner=%s)"
+ "(flatname=%s)(trustPartner=%s)"
+ "(securityIdentifier=%s)))",
+ dns_encoded, dns_encoded,
+ netbios_encoded, netbios_encoded,
+ sid_encoded);
+ if (ret > 0) {
+ ldb_transaction_cancel(sam_ldb);
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ if (ret < 0) {
+ ldb_transaction_cancel(sam_ldb);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, policy_state->system_dn);
+ if ( ! ldb_dn_add_child_fmt(msg->dn, "cn=%s", dns_name)) {
+ ldb_transaction_cancel(sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_msg_add_string(msg, "objectClass", "trustedDomain");
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ldb);
+ return NT_STATUS_NO_MEMORY;;
+ }
+
+ ret = ldb_msg_add_string(msg, "flatname", netbios_name);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_msg_add_string(msg, "trustPartner", dns_name);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ldb);
+ return NT_STATUS_NO_MEMORY;;
+ }
+
+ ret = samdb_msg_add_dom_sid(sam_ldb, mem_ctx, msg, "securityIdentifier",
+ r->in.info->sid);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ldb);
+ return NT_STATUS_NO_MEMORY;;
+ }
+
+ ret = samdb_msg_add_int(sam_ldb, mem_ctx, msg, "trustType", r->in.info->trust_type);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ldb);
+ return NT_STATUS_NO_MEMORY;;
+ }
+
+ ret = samdb_msg_add_int(sam_ldb, mem_ctx, msg, "trustAttributes", r->in.info->trust_attributes);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ldb);
+ return NT_STATUS_NO_MEMORY;;
+ }
+
+ ret = samdb_msg_add_int(sam_ldb, mem_ctx, msg, "trustDirection", r->in.info->trust_direction);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ldb);
+ return NT_STATUS_NO_MEMORY;;
+ }
+
+ if (trustAuthIncoming.data) {
+ ret = ldb_msg_add_value(msg, "trustAuthIncoming", &trustAuthIncoming, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ if (trustAuthOutgoing.data) {
+ ret = ldb_msg_add_value(msg, "trustAuthOutgoing", &trustAuthOutgoing, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ trusted_domain_state->trusted_domain_dn = talloc_reference(trusted_domain_state, msg->dn);
+
+ /* create the trusted_domain */
+ ret = ldb_add(sam_ldb, msg);
+ switch (ret) {
+ case LDB_SUCCESS:
+ break;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ ldb_transaction_cancel(sam_ldb);
+ DEBUG(0,("Failed to create trusted domain record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(sam_ldb)));
+ return NT_STATUS_DOMAIN_EXISTS;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ ldb_transaction_cancel(sam_ldb);
+ DEBUG(0,("Failed to create trusted domain record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(sam_ldb)));
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ ldb_transaction_cancel(sam_ldb);
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (r->in.info->trust_direction & LSA_TRUST_DIRECTION_INBOUND) {
+ struct ldb_dn *user_dn;
+ /* Inbound trusts must also create a cn=users object to match */
+ nt_status = add_trust_user(mem_ctx, sam_ldb,
+ policy_state->domain_dn,
+ netbios_name,
+ &auth_struct.incoming,
+ &user_dn);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ ldb_transaction_cancel(sam_ldb);
+ return nt_status;
+ }
+
+ /* save the trust user dn */
+ trusted_domain_state->trusted_domain_user_dn
+ = talloc_steal(trusted_domain_state, user_dn);
+ }
+
+ ret = ldb_transaction_commit(sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /*
+ * Notify winbindd that we have a new trust
+ */
+ status = irpc_servers_byname(imsg_ctx,
+ mem_ctx,
+ "winbind_server",
+ &num_server_ids,
+ &server_ids);
+ if (NT_STATUS_IS_OK(status) && num_server_ids >= 1) {
+ imessaging_send(imsg_ctx,
+ server_ids[0],
+ MSG_WINBIND_RELOAD_TRUSTED_DOMAINS,
+ NULL);
+ }
+ TALLOC_FREE(server_ids);
+
+ handle = dcesrv_handle_create(dce_call, LSA_HANDLE_TRUSTED_DOMAIN);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, trusted_domain_state);
+
+ trusted_domain_state->access_mask = r->in.access_mask;
+ trusted_domain_state->policy = talloc_reference(trusted_domain_state, policy_state);
+
+ *r->out.trustdom_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_CreateTrustedDomainEx2
+*/
+static NTSTATUS dcesrv_lsa_CreateTrustedDomainEx2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_CreateTrustedDomainEx2 *r)
+{
+ return dcesrv_lsa_CreateTrustedDomain_base(dce_call, mem_ctx, r, NDR_LSA_CREATETRUSTEDDOMAINEX2, NULL);
+}
+/*
+ lsa_CreateTrustedDomainEx
+*/
+static NTSTATUS dcesrv_lsa_CreateTrustedDomainEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_CreateTrustedDomainEx *r)
+{
+ struct lsa_CreateTrustedDomainEx2 r2;
+
+ r2.in.policy_handle = r->in.policy_handle;
+ r2.in.info = r->in.info;
+ r2.out.trustdom_handle = r->out.trustdom_handle;
+ return dcesrv_lsa_CreateTrustedDomain_base(dce_call, mem_ctx, &r2, NDR_LSA_CREATETRUSTEDDOMAINEX, r->in.auth_info);
+}
+
+/*
+ lsa_CreateTrustedDomain
+*/
+static NTSTATUS dcesrv_lsa_CreateTrustedDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CreateTrustedDomain *r)
+{
+ struct lsa_CreateTrustedDomainEx2 r2;
+
+ r2.in.policy_handle = r->in.policy_handle;
+ r2.in.info = talloc(mem_ctx, struct lsa_TrustDomainInfoInfoEx);
+ if (!r2.in.info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r2.in.info->domain_name = r->in.info->name;
+ r2.in.info->netbios_name = r->in.info->name;
+ r2.in.info->sid = r->in.info->sid;
+ r2.in.info->trust_direction = LSA_TRUST_DIRECTION_OUTBOUND;
+ r2.in.info->trust_type = LSA_TRUST_TYPE_DOWNLEVEL;
+ r2.in.info->trust_attributes = 0;
+
+ r2.in.access_mask = r->in.access_mask;
+ r2.out.trustdom_handle = r->out.trustdom_handle;
+
+ return dcesrv_lsa_CreateTrustedDomain_base(dce_call, mem_ctx, &r2, NDR_LSA_CREATETRUSTEDDOMAIN, NULL);
+}
+
+static NTSTATUS dcesrv_lsa_OpenTrustedDomain_common(
+ struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *tmp_mem,
+ struct lsa_policy_state *policy_state,
+ const char *filter,
+ uint32_t access_mask,
+ struct dcesrv_handle **_handle)
+{
+ struct lsa_trusted_domain_state *trusted_domain_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs;
+ const char *attrs[] = {
+ "trustDirection",
+ "flatname",
+ NULL
+ };
+ uint32_t direction;
+ int ret;
+
+ /* TODO: perform access checks */
+
+ /* search for the trusted_domain record */
+ ret = gendb_search(policy_state->sam_ldb, tmp_mem,
+ policy_state->system_dn,
+ &msgs, attrs, "%s", filter);
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching %s under %s\n", ret,
+ filter,
+ ldb_dn_get_linearized(policy_state->system_dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ trusted_domain_state = talloc_zero(tmp_mem,
+ struct lsa_trusted_domain_state);
+ if (!trusted_domain_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ trusted_domain_state->policy = policy_state;
+
+ trusted_domain_state->trusted_domain_dn =
+ talloc_steal(trusted_domain_state, msgs[0]->dn);
+
+ direction = ldb_msg_find_attr_as_int(msgs[0], "trustDirection", 0);
+ if (direction & LSA_TRUST_DIRECTION_INBOUND) {
+ const char *flatname = ldb_msg_find_attr_as_string(msgs[0],
+ "flatname", NULL);
+
+ /* search for the trusted_domain account */
+ ret = gendb_search(policy_state->sam_ldb, tmp_mem,
+ policy_state->domain_dn,
+ &msgs, attrs,
+ "(&(samaccountname=%s$)(objectclass=user)"
+ "(userAccountControl:%s:=%u))",
+ flatname,
+ LDB_OID_COMPARATOR_AND,
+ UF_INTERDOMAIN_TRUST_ACCOUNT);
+ if (ret == 1) {
+ trusted_domain_state->trusted_domain_user_dn =
+ talloc_steal(trusted_domain_state, msgs[0]->dn);
+ }
+ }
+
+ handle = dcesrv_handle_create(dce_call, LSA_HANDLE_TRUSTED_DOMAIN);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, trusted_domain_state);
+
+ trusted_domain_state->access_mask = access_mask;
+ trusted_domain_state->policy = talloc_reference(trusted_domain_state,
+ policy_state);
+
+ *_handle = handle;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_OpenTrustedDomain
+*/
+static NTSTATUS dcesrv_lsa_OpenTrustedDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenTrustedDomain *r)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_policy_state *policy_state;
+ struct dcesrv_handle *handle;
+ const char *sid_string;
+ char *filter;
+ NTSTATUS status;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.trustdom_handle);
+ policy_state = policy_handle->data;
+
+ sid_string = dom_sid_string(mem_ctx, r->in.sid);
+ if (!sid_string) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(securityIdentifier=%s)"
+ "(objectclass=trustedDomain))",
+ sid_string);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcesrv_lsa_OpenTrustedDomain_common(dce_call, mem_ctx,
+ policy_state,
+ filter,
+ r->in.access_mask,
+ &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *r->out.trustdom_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_OpenTrustedDomainByName
+*/
+static NTSTATUS dcesrv_lsa_OpenTrustedDomainByName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_OpenTrustedDomainByName *r)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_policy_state *policy_state;
+ struct dcesrv_handle *handle;
+ char *td_name;
+ char *filter;
+ NTSTATUS status;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.trustdom_handle);
+ policy_state = policy_handle->data;
+
+ if (!r->in.name.string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* search for the trusted_domain record */
+ td_name = ldb_binary_encode_string(mem_ctx, r->in.name.string);
+ if (td_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(|(flatname=%s)(cn=%s)(trustPartner=%s))"
+ "(objectclass=trustedDomain))",
+ td_name, td_name, td_name);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcesrv_lsa_OpenTrustedDomain_common(dce_call, mem_ctx,
+ policy_state,
+ filter,
+ r->in.access_mask,
+ &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *r->out.trustdom_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ lsa_SetTrustedDomainInfo
+*/
+static NTSTATUS dcesrv_lsa_SetTrustedDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetTrustedDomainInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+/* parameters 4 to 6 are optional if the dn is a dn of a TDO object,
+ * otherwise at least one must be provided */
+static NTSTATUS get_tdo(struct ldb_context *sam, TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn, const char *dns_domain,
+ const char *netbios, struct dom_sid2 *sid,
+ struct ldb_message ***msgs)
+{
+ const char *attrs[] = { "flatname", "trustPartner",
+ "securityIdentifier", "trustDirection",
+ "trustType", "trustAttributes",
+ "trustPosixOffset",
+ "msDs-supportedEncryptionTypes",
+ "msDS-TrustForestTrustInfo",
+ NULL
+ };
+ char *dns = NULL;
+ char *nbn = NULL;
+ char *filter;
+ int ret;
+
+
+ if (dns_domain || netbios || sid) {
+ filter = talloc_strdup(mem_ctx,
+ "(&(objectclass=trustedDomain)(|");
+ } else {
+ filter = talloc_strdup(mem_ctx,
+ "(objectclass=trustedDomain)");
+ }
+
+ if (dns_domain) {
+ dns = ldb_binary_encode_string(mem_ctx, dns_domain);
+ if (!dns) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_asprintf_addbuf(&filter, "(trustPartner=%s)", dns);
+ }
+ if (netbios) {
+ nbn = ldb_binary_encode_string(mem_ctx, netbios);
+ if (!nbn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_asprintf_addbuf(&filter, "(flatname=%s)", nbn);
+ }
+ if (sid) {
+ struct dom_sid_buf buf;
+ char *sidstr = dom_sid_str_buf(sid, &buf);
+ talloc_asprintf_addbuf(
+ &filter, "(securityIdentifier=%s)", sidstr);
+ }
+ if (dns_domain || netbios || sid) {
+ talloc_asprintf_addbuf(&filter, "))");
+ }
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = gendb_search(sam, mem_ctx, basedn, msgs, attrs, "%s", filter);
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS update_uint32_t_value(TALLOC_CTX *mem_ctx,
+ struct ldb_context *sam_ldb,
+ struct ldb_message *orig,
+ struct ldb_message *dest,
+ const char *attribute,
+ uint32_t value,
+ uint32_t *orig_value)
+{
+ const struct ldb_val *orig_val;
+ uint32_t orig_uint = 0;
+ unsigned int flags = 0;
+ int ret;
+ int error = 0;
+
+ orig_val = ldb_msg_find_ldb_val(orig, attribute);
+ if (!orig_val || !orig_val->data) {
+ /* add new attribute */
+ flags = LDB_FLAG_MOD_ADD;
+
+ } else {
+ orig_uint = smb_strtoul((const char *)orig_val->data,
+ NULL,
+ 0,
+ &error,
+ SMB_STR_STANDARD);
+ if (error != 0 || orig_uint != value) {
+ /* replace also if can't get value */
+ flags = LDB_FLAG_MOD_REPLACE;
+ }
+ }
+
+ if (flags == 0) {
+ /* stored value is identical, nothing to change */
+ goto done;
+ }
+
+ ret = samdb_msg_append_uint(sam_ldb, dest, dest, attribute, value, flags);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+done:
+ if (orig_value) {
+ *orig_value = orig_uint;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS update_trust_user(TALLOC_CTX *mem_ctx,
+ struct ldb_context *sam_ldb,
+ struct ldb_dn *base_dn,
+ bool delete_user,
+ const char *netbios_name,
+ struct trustAuthInOutBlob *in)
+{
+ const char *attrs[] = { "userAccountControl", NULL };
+ struct ldb_message **msgs;
+ struct ldb_message *msg;
+ uint32_t uac;
+ uint32_t i;
+ int ret;
+
+ ret = gendb_search(sam_ldb, mem_ctx,
+ base_dn, &msgs, attrs,
+ "samAccountName=%s$", netbios_name);
+ if (ret > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (ret == 0) {
+ if (delete_user) {
+ return NT_STATUS_OK;
+ }
+
+ /* ok no existing user, add it from scratch */
+ return add_trust_user(mem_ctx, sam_ldb, base_dn,
+ netbios_name, in, NULL);
+ }
+
+ /* check user is what we are looking for */
+ uac = ldb_msg_find_attr_as_uint(msgs[0],
+ "userAccountControl", 0);
+ if (!(uac & UF_INTERDOMAIN_TRUST_ACCOUNT)) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if (delete_user) {
+ ret = ldb_delete(sam_ldb, msgs[0]->dn);
+ switch (ret) {
+ case LDB_SUCCESS:
+ return NT_STATUS_OK;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ /* entry exists, just modify secret if any */
+ if (in == NULL || in->count == 0) {
+ return NT_STATUS_OK;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (!msg) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ msg->dn = msgs[0]->dn;
+
+ for (i = 0; i < in->count; i++) {
+ const char *attribute;
+ struct ldb_val v;
+ switch (in->current.array[i].AuthType) {
+ case TRUST_AUTH_TYPE_NT4OWF:
+ attribute = "unicodePwd";
+ v.data = (uint8_t *)&in->current.array[i].AuthInfo.nt4owf.password;
+ v.length = 16;
+ break;
+ case TRUST_AUTH_TYPE_CLEAR:
+ attribute = "clearTextPassword";
+ v.data = in->current.array[i].AuthInfo.clear.password;
+ v.length = in->current.array[i].AuthInfo.clear.size;
+ break;
+ default:
+ continue;
+ }
+
+ ret = ldb_msg_append_value(msg, attribute, &v, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* create the trusted_domain user account */
+ ret = ldb_modify(sam_ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(sam_ldb)));
+
+ switch (ret) {
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ return NT_STATUS_DOMAIN_EXISTS;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS setInfoTrustedDomain_base(struct dcesrv_call_state *dce_call,
+ struct lsa_policy_state *p_state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message *dom_msg,
+ enum lsa_TrustDomInfoEnum level,
+ union lsa_TrustedDomainInfo *info)
+{
+ uint32_t *posix_offset = NULL;
+ struct lsa_TrustDomainInfoInfoEx *info_ex = NULL;
+ struct lsa_TrustDomainInfoAuthInfo *auth_info = NULL;
+ struct lsa_TrustDomainInfoAuthInfoInternal *auth_info_int = NULL;
+ uint32_t *enc_types = NULL;
+ DATA_BLOB trustAuthIncoming, trustAuthOutgoing, auth_blob;
+ struct trustDomainPasswords auth_struct;
+ struct trustAuthInOutBlob *current_passwords = NULL;
+ NTSTATUS nt_status;
+ struct ldb_message **msgs;
+ struct ldb_message *msg;
+ bool add_outgoing = false;
+ bool add_incoming = false;
+ bool del_outgoing = false;
+ bool del_incoming = false;
+ bool del_forest_info = false;
+ bool in_transaction = false;
+ int ret;
+ bool am_rodc;
+
+ switch (level) {
+ case LSA_TRUSTED_DOMAIN_INFO_POSIX_OFFSET:
+ posix_offset = &info->posix_offset.posix_offset;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_INFO_EX:
+ info_ex = &info->info_ex;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO:
+ auth_info = &info->auth_info;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO:
+ posix_offset = &info->full_info.posix_offset.posix_offset;
+ info_ex = &info->full_info.info_ex;
+ auth_info = &info->full_info.auth_info;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO_INTERNAL:
+ auth_info_int = &info->auth_info_internal;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_INTERNAL:
+ posix_offset = &info->full_info_internal.posix_offset.posix_offset;
+ info_ex = &info->full_info_internal.info_ex;
+ auth_info_int = &info->full_info_internal.auth_info;
+ break;
+ case LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES:
+ enc_types = &info->enc_types.enc_types;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (auth_info) {
+ nt_status = auth_info_2_auth_blob(mem_ctx, auth_info,
+ &trustAuthIncoming,
+ &trustAuthOutgoing);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ if (trustAuthIncoming.data) {
+ /* This does the decode of some of this twice, but it is easier that way */
+ nt_status = auth_info_2_trustauth_inout(mem_ctx,
+ auth_info->incoming_count,
+ auth_info->incoming_current_auth_info,
+ NULL,
+ &current_passwords);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ }
+ }
+
+ /* decode auth_info_int if set */
+ if (auth_info_int) {
+
+ /* now decrypt blob */
+ auth_blob = data_blob_const(auth_info_int->auth_blob.data,
+ auth_info_int->auth_blob.size);
+
+ nt_status = get_trustdom_auth_blob(dce_call, mem_ctx,
+ &auth_blob, &auth_struct);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ }
+
+ if (info_ex) {
+ /* verify data matches */
+ if (info_ex->trust_attributes &
+ LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ /* TODO: check what behavior level we have */
+ if (strcasecmp_m(p_state->domain_dns,
+ p_state->forest_dns) != 0) {
+ return NT_STATUS_INVALID_DOMAIN_STATE;
+ }
+ }
+
+ ret = samdb_rodc(p_state->sam_ldb, &am_rodc);
+ if (ret == LDB_SUCCESS && am_rodc) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ /* verify only one object matches the dns/netbios/sid
+ * triplet and that this is the one we already have */
+ nt_status = get_tdo(p_state->sam_ldb, mem_ctx,
+ p_state->system_dn,
+ info_ex->domain_name.string,
+ info_ex->netbios_name.string,
+ info_ex->sid, &msgs);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ if (ldb_dn_compare(dom_msg->dn, msgs[0]->dn) != 0) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ talloc_free(msgs);
+ }
+
+ /* TODO: should we fetch previous values from the existing entry
+ * and append them ? */
+ if (auth_info_int && auth_struct.incoming.count) {
+ nt_status = get_trustauth_inout_blob(dce_call, mem_ctx,
+ &auth_struct.incoming,
+ &trustAuthIncoming);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ current_passwords = &auth_struct.incoming;
+
+ } else {
+ trustAuthIncoming = data_blob(NULL, 0);
+ }
+
+ if (auth_info_int && auth_struct.outgoing.count) {
+ nt_status = get_trustauth_inout_blob(dce_call, mem_ctx,
+ &auth_struct.outgoing,
+ &trustAuthOutgoing);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ } else {
+ trustAuthOutgoing = data_blob(NULL, 0);
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ msg->dn = dom_msg->dn;
+
+ if (posix_offset) {
+ nt_status = update_uint32_t_value(mem_ctx, p_state->sam_ldb,
+ dom_msg, msg,
+ "trustPosixOffset",
+ *posix_offset, NULL);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ }
+
+ if (info_ex) {
+ uint32_t origattrs;
+ uint32_t changed_attrs;
+ uint32_t origdir;
+ int origtype;
+
+ nt_status = update_uint32_t_value(mem_ctx, p_state->sam_ldb,
+ dom_msg, msg,
+ "trustDirection",
+ info_ex->trust_direction,
+ &origdir);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ if (info_ex->trust_direction & LSA_TRUST_DIRECTION_INBOUND) {
+ if (auth_info != NULL && trustAuthIncoming.length > 0) {
+ add_incoming = true;
+ }
+ }
+ if (info_ex->trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) {
+ if (auth_info != NULL && trustAuthOutgoing.length > 0) {
+ add_outgoing = true;
+ }
+ }
+
+ if ((origdir & LSA_TRUST_DIRECTION_INBOUND) &&
+ !(info_ex->trust_direction & LSA_TRUST_DIRECTION_INBOUND)) {
+ del_incoming = true;
+ }
+ if ((origdir & LSA_TRUST_DIRECTION_OUTBOUND) &&
+ !(info_ex->trust_direction & LSA_TRUST_DIRECTION_OUTBOUND)) {
+ del_outgoing = true;
+ }
+
+ origtype = ldb_msg_find_attr_as_int(dom_msg, "trustType", -1);
+ if (origtype == -1 || origtype != info_ex->trust_type) {
+ DEBUG(1, ("Attempted to change trust type! "
+ "Operation not handled\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt_status = update_uint32_t_value(mem_ctx, p_state->sam_ldb,
+ dom_msg, msg,
+ "trustAttributes",
+ info_ex->trust_attributes,
+ &origattrs);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ /* TODO: check forestFunctionality from ldb opaque */
+ /* TODO: check what is set makes sense */
+
+ changed_attrs = origattrs ^ info_ex->trust_attributes;
+ if (changed_attrs & ~LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ /*
+ * For now we only allow
+ * LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE to be changed.
+ *
+ * TODO: we may need to support more attribute changes
+ */
+ DEBUG(1, ("Attempted to change trust attributes "
+ "(0x%08x != 0x%08x)! "
+ "Operation not handled yet...\n",
+ (unsigned)origattrs,
+ (unsigned)info_ex->trust_attributes));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!(info_ex->trust_attributes &
+ LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE))
+ {
+ struct ldb_message_element *orig_forest_el = NULL;
+
+ orig_forest_el = ldb_msg_find_element(dom_msg,
+ "msDS-TrustForestTrustInfo");
+ if (orig_forest_el != NULL) {
+ del_forest_info = true;
+ }
+ }
+ }
+
+ if (enc_types) {
+ nt_status = update_uint32_t_value(mem_ctx, p_state->sam_ldb,
+ dom_msg, msg,
+ "msDS-SupportedEncryptionTypes",
+ *enc_types, NULL);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ }
+
+ if (add_incoming || del_incoming) {
+ if (add_incoming) {
+ ret = ldb_msg_append_value(msg, "trustAuthIncoming",
+ &trustAuthIncoming, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ ret = ldb_msg_add_empty(msg, "trustAuthIncoming",
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+ if (add_outgoing || del_outgoing) {
+ if (add_outgoing) {
+ ret = ldb_msg_append_value(msg, "trustAuthOutgoing",
+ &trustAuthOutgoing, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ ret = ldb_msg_add_empty(msg, "trustAuthOutgoing",
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+ if (del_forest_info) {
+ ret = ldb_msg_add_empty(msg, "msDS-TrustForestTrustInfo",
+ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* start transaction */
+ ret = ldb_transaction_start(p_state->sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ in_transaction = true;
+
+ if (msg->num_elements) {
+ ret = ldb_modify(p_state->sam_ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1,("Failed to modify trusted domain record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(p_state->sam_ldb)));
+ nt_status = dsdb_ldb_err_to_ntstatus(ret);
+ goto done;
+ }
+ }
+
+ if (add_incoming || del_incoming) {
+ const char *netbios_name;
+
+ netbios_name = ldb_msg_find_attr_as_string(dom_msg,
+ "flatname", NULL);
+ if (!netbios_name) {
+ nt_status = NT_STATUS_INVALID_DOMAIN_STATE;
+ goto done;
+ }
+
+ /* We use trustAuthIncoming.data to incidate that auth_struct.incoming is valid */
+ nt_status = update_trust_user(mem_ctx,
+ p_state->sam_ldb,
+ p_state->domain_dn,
+ del_incoming,
+ netbios_name,
+ current_passwords);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ goto done;
+ }
+ }
+
+ /* ok, all fine, commit transaction and return */
+ ret = ldb_transaction_commit(p_state->sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ in_transaction = false;
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (in_transaction) {
+ ldb_transaction_cancel(p_state->sam_ldb);
+ }
+ return nt_status;
+}
+
+/*
+ lsa_SetInformationTrustedDomain
+*/
+static NTSTATUS dcesrv_lsa_SetInformationTrustedDomain(
+ struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SetInformationTrustedDomain *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_trusted_domain_state *td_state;
+ struct ldb_message **msgs;
+ NTSTATUS nt_status;
+
+ DCESRV_PULL_HANDLE(h, r->in.trustdom_handle,
+ LSA_HANDLE_TRUSTED_DOMAIN);
+
+ td_state = talloc_get_type(h->data, struct lsa_trusted_domain_state);
+
+ /* get the trusted domain object */
+ nt_status = get_tdo(td_state->policy->sam_ldb, mem_ctx,
+ td_state->trusted_domain_dn,
+ NULL, NULL, NULL, &msgs);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ if (NT_STATUS_EQUAL(nt_status,
+ NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return nt_status;
+ }
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return setInfoTrustedDomain_base(dce_call, td_state->policy, mem_ctx,
+ msgs[0], r->in.level, r->in.info);
+}
+
+
+/*
+ lsa_DeleteTrustedDomain
+*/
+static NTSTATUS dcesrv_lsa_DeleteTrustedDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_DeleteTrustedDomain *r)
+{
+ NTSTATUS status;
+ struct lsa_OpenTrustedDomain opn = {{0},{0}};
+ struct lsa_DeleteObject del;
+ struct dcesrv_handle *h;
+
+ opn.in.handle = r->in.handle;
+ opn.in.sid = r->in.dom_sid;
+ opn.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ opn.out.trustdom_handle = talloc(mem_ctx, struct policy_handle);
+ if (!opn.out.trustdom_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_OpenTrustedDomain(dce_call, mem_ctx, &opn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DCESRV_PULL_HANDLE(h, opn.out.trustdom_handle, DCESRV_HANDLE_ANY);
+ talloc_steal(mem_ctx, h);
+
+ del.in.handle = opn.out.trustdom_handle;
+ del.out.handle = opn.out.trustdom_handle;
+ status = dcesrv_lsa_DeleteObject(dce_call, mem_ctx, &del);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fill_trust_domain_ex(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct lsa_TrustDomainInfoInfoEx *info_ex)
+{
+ info_ex->domain_name.string
+ = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL);
+ info_ex->netbios_name.string
+ = ldb_msg_find_attr_as_string(msg, "flatname", NULL);
+ info_ex->sid
+ = samdb_result_dom_sid(mem_ctx, msg, "securityIdentifier");
+ info_ex->trust_direction
+ = ldb_msg_find_attr_as_int(msg, "trustDirection", 0);
+ info_ex->trust_type
+ = ldb_msg_find_attr_as_int(msg, "trustType", 0);
+ info_ex->trust_attributes
+ = ldb_msg_find_attr_as_int(msg, "trustAttributes", 0);
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_QueryTrustedDomainInfo
+*/
+static NTSTATUS dcesrv_lsa_QueryTrustedDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QueryTrustedDomainInfo *r)
+{
+ union lsa_TrustedDomainInfo *info = NULL;
+ struct dcesrv_handle *h;
+ struct lsa_trusted_domain_state *trusted_domain_state;
+ struct ldb_message *msg;
+ int ret;
+ struct ldb_message **res;
+ const char *attrs[] = {
+ "flatname",
+ "trustPartner",
+ "securityIdentifier",
+ "trustDirection",
+ "trustType",
+ "trustAttributes",
+ "msDs-supportedEncryptionTypes",
+ NULL
+ };
+
+ DCESRV_PULL_HANDLE(h, r->in.trustdom_handle, LSA_HANDLE_TRUSTED_DOMAIN);
+
+ trusted_domain_state = talloc_get_type(h->data, struct lsa_trusted_domain_state);
+
+ /* pull all the user attributes */
+ ret = gendb_search_dn(trusted_domain_state->policy->sam_ldb, mem_ctx,
+ trusted_domain_state->trusted_domain_dn, &res, attrs);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ info = talloc_zero(mem_ctx, union lsa_TrustedDomainInfo);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.info = info;
+
+ switch (r->in.level) {
+ case LSA_TRUSTED_DOMAIN_INFO_NAME:
+ info->name.netbios_name.string
+ = ldb_msg_find_attr_as_string(msg, "flatname", NULL);
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_POSIX_OFFSET:
+ info->posix_offset.posix_offset
+ = ldb_msg_find_attr_as_uint(msg, "posixOffset", 0);
+ break;
+#if 0 /* Win2k3 doesn't implement this */
+ case LSA_TRUSTED_DOMAIN_INFO_BASIC:
+ r->out.info->info_basic.netbios_name.string
+ = ldb_msg_find_attr_as_string(msg, "flatname", NULL);
+ r->out.info->info_basic.sid
+ = samdb_result_dom_sid(mem_ctx, msg, "securityIdentifier");
+ break;
+#endif
+ case LSA_TRUSTED_DOMAIN_INFO_INFO_EX:
+ return fill_trust_domain_ex(mem_ctx, msg, &info->info_ex);
+
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO:
+ ZERO_STRUCT(info->full_info);
+ return fill_trust_domain_ex(mem_ctx, msg, &info->full_info.info_ex);
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_2_INTERNAL:
+ ZERO_STRUCT(info->full_info2_internal);
+ info->full_info2_internal.posix_offset.posix_offset
+ = ldb_msg_find_attr_as_uint(msg, "posixOffset", 0);
+ return fill_trust_domain_ex(mem_ctx, msg, &info->full_info2_internal.info.info_ex);
+
+ case LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES:
+ info->enc_types.enc_types
+ = ldb_msg_find_attr_as_uint(msg, "msDs-supportedEncryptionTypes", KERB_ENCTYPE_RC4_HMAC_MD5);
+ break;
+
+ case LSA_TRUSTED_DOMAIN_INFO_CONTROLLERS:
+ case LSA_TRUSTED_DOMAIN_INFO_INFO_EX2_INTERNAL:
+ /* oops, we don't want to return the info after all */
+ talloc_free(info);
+ *r->out.info = NULL;
+ return NT_STATUS_INVALID_PARAMETER;
+ default:
+ /* oops, we don't want to return the info after all */
+ talloc_free(info);
+ *r->out.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_QueryTrustedDomainInfoBySid
+*/
+static NTSTATUS dcesrv_lsa_QueryTrustedDomainInfoBySid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QueryTrustedDomainInfoBySid *r)
+{
+ NTSTATUS status;
+ struct lsa_OpenTrustedDomain opn = {{0},{0}};
+ struct lsa_QueryTrustedDomainInfo query;
+ struct dcesrv_handle *h;
+
+ opn.in.handle = r->in.handle;
+ opn.in.sid = r->in.dom_sid;
+ opn.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ opn.out.trustdom_handle = talloc(mem_ctx, struct policy_handle);
+ if (!opn.out.trustdom_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_OpenTrustedDomain(dce_call, mem_ctx, &opn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Ensure this handle goes away at the end of this call */
+ DCESRV_PULL_HANDLE(h, opn.out.trustdom_handle, DCESRV_HANDLE_ANY);
+ talloc_steal(mem_ctx, h);
+
+ query.in.trustdom_handle = opn.out.trustdom_handle;
+ query.in.level = r->in.level;
+ query.out.info = r->out.info;
+ status = dcesrv_lsa_QueryTrustedDomainInfo(dce_call, mem_ctx, &query);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_SetTrustedDomainInfoByName
+*/
+static NTSTATUS dcesrv_lsa_SetTrustedDomainInfoByName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SetTrustedDomainInfoByName *r)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_policy_state *policy_state;
+ struct ldb_message **msgs;
+ NTSTATUS nt_status;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ policy_state = policy_handle->data;
+
+ /* get the trusted domain object */
+ nt_status = get_tdo(policy_state->sam_ldb, mem_ctx,
+ policy_state->domain_dn,
+ r->in.trusted_domain->string,
+ r->in.trusted_domain->string,
+ NULL, &msgs);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ if (NT_STATUS_EQUAL(nt_status,
+ NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return nt_status;
+ }
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return setInfoTrustedDomain_base(dce_call, policy_state, mem_ctx,
+ msgs[0], r->in.level, r->in.info);
+}
+
+/*
+ lsa_QueryTrustedDomainInfoByName
+*/
+static NTSTATUS dcesrv_lsa_QueryTrustedDomainInfoByName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_QueryTrustedDomainInfoByName *r)
+{
+ NTSTATUS status;
+ struct lsa_OpenTrustedDomainByName opn = {{0},{0}};
+ struct lsa_QueryTrustedDomainInfo query;
+ struct dcesrv_handle *h;
+
+ opn.in.handle = r->in.handle;
+ opn.in.name = *r->in.trusted_domain;
+ opn.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ opn.out.trustdom_handle = talloc(mem_ctx, struct policy_handle);
+ if (!opn.out.trustdom_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_OpenTrustedDomainByName(dce_call, mem_ctx, &opn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Ensure this handle goes away at the end of this call */
+ DCESRV_PULL_HANDLE(h, opn.out.trustdom_handle, DCESRV_HANDLE_ANY);
+ talloc_steal(mem_ctx, h);
+
+ query.in.trustdom_handle = opn.out.trustdom_handle;
+ query.in.level = r->in.level;
+ query.out.info = r->out.info;
+ status = dcesrv_lsa_QueryTrustedDomainInfo(dce_call, mem_ctx, &query);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_CloseTrustedDomainEx
+*/
+static NTSTATUS dcesrv_lsa_CloseTrustedDomainEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_CloseTrustedDomainEx *r)
+{
+ /* The result of a bad hair day from an IDL programmer? Not
+ * implmented in Win2k3. You should always just lsa_Close
+ * anyway. */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ comparison function for sorting lsa_DomainInformation array
+*/
+static int compare_DomainInfo(struct lsa_DomainInfo *e1, struct lsa_DomainInfo *e2)
+{
+ return strcasecmp_m(e1->name.string, e2->name.string);
+}
+
+/*
+ lsa_EnumTrustDom
+*/
+static NTSTATUS dcesrv_lsa_EnumTrustDom(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_EnumTrustDom *r)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_DomainInfo *entries;
+ struct lsa_policy_state *policy_state;
+ struct ldb_message **domains;
+ const char *attrs[] = {
+ "flatname",
+ "securityIdentifier",
+ NULL
+ };
+
+
+ int count, i;
+
+ *r->out.resume_handle = 0;
+
+ r->out.domains->domains = NULL;
+ r->out.domains->count = 0;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ policy_state = policy_handle->data;
+
+ /* search for all users in this domain. This could possibly be cached and
+ resumed based on resume_key */
+ count = gendb_search(policy_state->sam_ldb, mem_ctx, policy_state->system_dn, &domains, attrs,
+ "objectclass=trustedDomain");
+ if (count < 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* convert to lsa_TrustInformation format */
+ entries = talloc_array(mem_ctx, struct lsa_DomainInfo, count);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<count;i++) {
+ entries[i].sid = samdb_result_dom_sid(mem_ctx, domains[i], "securityIdentifier");
+ entries[i].name.string = ldb_msg_find_attr_as_string(domains[i], "flatname", NULL);
+ }
+
+ /* sort the results by name */
+ TYPESAFE_QSORT(entries, count, compare_DomainInfo);
+
+ if (*r->in.resume_handle >= count) {
+ *r->out.resume_handle = -1;
+
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ /* return the rest, limit by max_size. Note that we
+ use the w2k3 element size value of 60 */
+ r->out.domains->count = count - *r->in.resume_handle;
+ r->out.domains->count = MIN(r->out.domains->count,
+ 1+(r->in.max_size/LSA_ENUM_TRUST_DOMAIN_MULTIPLIER));
+
+ r->out.domains->domains = entries + *r->in.resume_handle;
+
+ if (r->out.domains->count < count - *r->in.resume_handle) {
+ *r->out.resume_handle = *r->in.resume_handle + r->out.domains->count;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ /* according to MS-LSAD 3.1.4.7.8 output resume handle MUST
+ * always be larger than the previous input resume handle, in
+ * particular when hitting the last query it is vital to set the
+ * resume handle correctly to avoid infinite client loops, as
+ * seen e.g. with Windows XP SP3 when resume handle is 0 and
+ * status is NT_STATUS_OK - gd */
+
+ *r->out.resume_handle = (uint32_t)-1;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ comparison function for sorting lsa_DomainInformation array
+*/
+static int compare_TrustDomainInfoInfoEx(struct lsa_TrustDomainInfoInfoEx *e1, struct lsa_TrustDomainInfoInfoEx *e2)
+{
+ return strcasecmp_m(e1->netbios_name.string, e2->netbios_name.string);
+}
+
+/*
+ lsa_EnumTrustedDomainsEx
+*/
+static NTSTATUS dcesrv_lsa_EnumTrustedDomainsEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_EnumTrustedDomainsEx *r)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_TrustDomainInfoInfoEx *entries;
+ struct lsa_policy_state *policy_state;
+ struct ldb_message **domains;
+ const char *attrs[] = {
+ "flatname",
+ "trustPartner",
+ "securityIdentifier",
+ "trustDirection",
+ "trustType",
+ "trustAttributes",
+ NULL
+ };
+ NTSTATUS nt_status;
+
+ int count, i;
+
+ *r->out.resume_handle = 0;
+
+ r->out.domains->domains = NULL;
+ r->out.domains->count = 0;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ policy_state = policy_handle->data;
+
+ /* search for all users in this domain. This could possibly be cached and
+ resumed based on resume_key */
+ count = gendb_search(policy_state->sam_ldb, mem_ctx, policy_state->system_dn, &domains, attrs,
+ "objectclass=trustedDomain");
+ if (count < 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* convert to lsa_DomainInformation format */
+ entries = talloc_array(mem_ctx, struct lsa_TrustDomainInfoInfoEx, count);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<count;i++) {
+ nt_status = fill_trust_domain_ex(mem_ctx, domains[i], &entries[i]);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ }
+
+ /* sort the results by name */
+ TYPESAFE_QSORT(entries, count, compare_TrustDomainInfoInfoEx);
+
+ if (*r->in.resume_handle >= count) {
+ *r->out.resume_handle = -1;
+
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ /* return the rest, limit by max_size. Note that we
+ use the w2k3 element size value of 60 */
+ r->out.domains->count = count - *r->in.resume_handle;
+ r->out.domains->count = MIN(r->out.domains->count,
+ 1+(r->in.max_size/LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER));
+
+ r->out.domains->domains = entries + *r->in.resume_handle;
+
+ if (r->out.domains->count < count - *r->in.resume_handle) {
+ *r->out.resume_handle = *r->in.resume_handle + r->out.domains->count;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ *r->out.resume_handle = *r->in.resume_handle + r->out.domains->count;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_OpenAccount
+*/
+static NTSTATUS dcesrv_lsa_OpenAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenAccount *r)
+{
+ struct dcesrv_handle *h, *ah;
+ struct lsa_policy_state *state;
+ struct lsa_account_state *astate;
+
+ ZERO_STRUCTP(r->out.acct_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ astate = talloc(dce_call->conn, struct lsa_account_state);
+ if (astate == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ astate->account_sid = dom_sid_dup(astate, r->in.sid);
+ if (astate->account_sid == NULL) {
+ talloc_free(astate);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ astate->policy = talloc_reference(astate, state);
+ astate->access_mask = r->in.access_mask;
+
+ /*
+ * For now we grant all requested access.
+ *
+ * We will fail at the ldb layer later.
+ */
+ if (astate->access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
+ astate->access_mask &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+ astate->access_mask |= LSA_ACCOUNT_ALL_ACCESS;
+ }
+ se_map_generic(&astate->access_mask, &dcesrv_lsa_account_mapping);
+
+ DEBUG(10,("%s: %s access desired[0x%08X] granted[0x%08X] - success.\n",
+ __func__, dom_sid_string(mem_ctx, astate->account_sid),
+ (unsigned)r->in.access_mask,
+ (unsigned)astate->access_mask));
+
+ ah = dcesrv_handle_create(dce_call, LSA_HANDLE_ACCOUNT);
+ if (!ah) {
+ talloc_free(astate);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ah->data = talloc_steal(ah, astate);
+
+ *r->out.acct_handle = ah->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_EnumPrivsAccount
+*/
+static NTSTATUS dcesrv_lsa_EnumPrivsAccount(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_EnumPrivsAccount *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_account_state *astate;
+ int ret;
+ unsigned int i, j;
+ struct ldb_message **res;
+ const char * const attrs[] = { "privilege", NULL};
+ struct ldb_message_element *el;
+ const char *sidstr;
+ struct lsa_PrivilegeSet *privs;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ privs = talloc(mem_ctx, struct lsa_PrivilegeSet);
+ if (privs == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ privs->count = 0;
+ privs->unknown = 0;
+ privs->set = NULL;
+
+ *r->out.privs = privs;
+
+ sidstr = ldap_encode_ndr_dom_sid(mem_ctx, astate->account_sid);
+ if (sidstr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = gendb_search(astate->policy->pdb, mem_ctx, NULL, &res, attrs,
+ "objectSid=%s", sidstr);
+ if (ret < 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (ret != 1) {
+ return NT_STATUS_OK;
+ }
+
+ el = ldb_msg_find_element(res[0], "privilege");
+ if (el == NULL || el->num_values == 0) {
+ return NT_STATUS_OK;
+ }
+
+ privs->set = talloc_array(privs,
+ struct lsa_LUIDAttribute, el->num_values);
+ if (privs->set == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ j = 0;
+ for (i=0;i<el->num_values;i++) {
+ int id = sec_privilege_id((const char *)el->values[i].data);
+ if (id == SEC_PRIV_INVALID) {
+ /* Perhaps an account right, not a privilege */
+ continue;
+ }
+ privs->set[j].attribute = 0;
+ privs->set[j].luid.low = id;
+ privs->set[j].luid.high = 0;
+ j++;
+ }
+
+ privs->count = j;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_EnumAccountRights
+*/
+static NTSTATUS dcesrv_lsa_EnumAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_EnumAccountRights *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int ret;
+ unsigned int i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "privilege", NULL};
+ const char *sidstr;
+ struct ldb_message_element *el;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ sidstr = ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid);
+ if (sidstr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = gendb_search(state->pdb, mem_ctx, NULL, &res, attrs,
+ "(&(objectSid=%s)(privilege=*))", sidstr);
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ if (ret != 1) {
+ DEBUG(3, ("searching for account rights for SID: %s failed: %s",
+ dom_sid_string(mem_ctx, r->in.sid),
+ ldb_errstring(state->pdb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ el = ldb_msg_find_element(res[0], "privilege");
+ if (el == NULL || el->num_values == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ r->out.rights->count = el->num_values;
+ r->out.rights->names = talloc_array(r->out.rights,
+ struct lsa_StringLarge, r->out.rights->count);
+ if (r->out.rights->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<el->num_values;i++) {
+ r->out.rights->names[i].string = (const char *)el->values[i].data;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ helper for lsa_AddAccountRights and lsa_RemoveAccountRights
+*/
+static NTSTATUS dcesrv_lsa_AddRemoveAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_policy_state *state,
+ int ldb_flag,
+ struct dom_sid *sid,
+ const struct lsa_RightSet *rights)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const char *sidstr, *sidndrstr;
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+ int ret;
+ uint32_t i;
+ struct lsa_EnumAccountRights r2;
+ char *dnstr;
+
+ if (security_session_user_level(session_info, NULL) <
+ SECURITY_ADMINISTRATOR) {
+ DEBUG(0,("lsa_AddRemoveAccount refused for supplied security token\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sidndrstr = ldap_encode_ndr_dom_sid(msg, sid);
+ if (sidndrstr == NULL) {
+ TALLOC_FREE(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sidstr = dom_sid_string(msg, sid);
+ if (sidstr == NULL) {
+ TALLOC_FREE(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dnstr = talloc_asprintf(msg, "sid=%s", sidstr);
+ if (dnstr == NULL) {
+ TALLOC_FREE(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_new(msg, state->pdb, dnstr);
+ if (msg->dn == NULL) {
+ TALLOC_FREE(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (LDB_FLAG_MOD_TYPE(ldb_flag) == LDB_FLAG_MOD_ADD) {
+ NTSTATUS status;
+
+ r2.in.handle = &state->handle->wire_handle;
+ r2.in.sid = sid;
+ r2.out.rights = talloc(mem_ctx, struct lsa_RightSet);
+
+ status = dcesrv_lsa_EnumAccountRights(dce_call, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(r2.out.rights);
+ }
+ }
+
+ for (i=0;i<rights->count;i++) {
+ bool ok;
+
+ ok = dcesrc_lsa_valid_AccountRight(rights->names[i].string);
+ if (!ok) {
+ talloc_free(msg);
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ if (LDB_FLAG_MOD_TYPE(ldb_flag) == LDB_FLAG_MOD_ADD) {
+ uint32_t j;
+ for (j=0;j<r2.out.rights->count;j++) {
+ if (strcasecmp_m(r2.out.rights->names[j].string,
+ rights->names[i].string) == 0) {
+ break;
+ }
+ }
+ if (j != r2.out.rights->count) continue;
+ }
+
+ ret = ldb_msg_add_string(msg, "privilege", rights->names[i].string);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ el = ldb_msg_find_element(msg, "privilege");
+ if (!el) {
+ talloc_free(msg);
+ return NT_STATUS_OK;
+ }
+
+ el->flags = ldb_flag;
+
+ ret = ldb_modify(state->pdb, msg);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ if (samdb_msg_add_dom_sid(state->pdb, msg, msg, "objectSid", sid) != LDB_SUCCESS) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ldb_msg_add_string(msg, "comment", "added via LSA");
+ ret = ldb_add(state->pdb, msg);
+ }
+ if (ret != LDB_SUCCESS) {
+ if (LDB_FLAG_MOD_TYPE(ldb_flag) == LDB_FLAG_MOD_DELETE && ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ talloc_free(msg);
+ return NT_STATUS_OK;
+ }
+ DEBUG(3, ("Could not %s attributes from %s: %s",
+ LDB_FLAG_MOD_TYPE(ldb_flag) == LDB_FLAG_MOD_DELETE ? "delete" : "add",
+ ldb_dn_get_linearized(msg->dn), ldb_errstring(state->pdb)));
+ talloc_free(msg);
+ return NT_STATUS_UNEXPECTED_IO_ERROR;
+ }
+
+ talloc_free(msg);
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_AddPrivilegesToAccount
+*/
+static NTSTATUS dcesrv_lsa_AddPrivilegesToAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_AddPrivilegesToAccount *r)
+{
+ struct lsa_RightSet rights;
+ struct dcesrv_handle *h;
+ struct lsa_account_state *astate;
+ uint32_t i;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ rights.count = r->in.privs->count;
+ rights.names = talloc_array(mem_ctx, struct lsa_StringLarge, rights.count);
+ if (rights.names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<rights.count;i++) {
+ int id = r->in.privs->set[i].luid.low;
+ if (r->in.privs->set[i].luid.high) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ rights.names[i].string = sec_privilege_name(id);
+ if (rights.names[i].string == NULL) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ }
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, astate->policy,
+ LDB_FLAG_MOD_ADD, astate->account_sid,
+ &rights);
+}
+
+
+/*
+ lsa_RemovePrivilegesFromAccount
+*/
+static NTSTATUS dcesrv_lsa_RemovePrivilegesFromAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_RemovePrivilegesFromAccount *r)
+{
+ struct lsa_RightSet *rights;
+ struct dcesrv_handle *h;
+ struct lsa_account_state *astate;
+ uint32_t i;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ rights = talloc(mem_ctx, struct lsa_RightSet);
+
+ if (r->in.remove_all == 1 &&
+ r->in.privs == NULL) {
+ struct lsa_EnumAccountRights r2;
+ NTSTATUS status;
+
+ r2.in.handle = &astate->policy->handle->wire_handle;
+ r2.in.sid = astate->account_sid;
+ r2.out.rights = rights;
+
+ status = dcesrv_lsa_EnumAccountRights(dce_call, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, astate->policy,
+ LDB_FLAG_MOD_DELETE, astate->account_sid,
+ r2.out.rights);
+ }
+
+ if (r->in.remove_all != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ rights->count = r->in.privs->count;
+ rights->names = talloc_array(mem_ctx, struct lsa_StringLarge, rights->count);
+ if (rights->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<rights->count;i++) {
+ int id = r->in.privs->set[i].luid.low;
+ if (r->in.privs->set[i].luid.high) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ rights->names[i].string = sec_privilege_name(id);
+ if (rights->names[i].string == NULL) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ }
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, astate->policy,
+ LDB_FLAG_MOD_DELETE, astate->account_sid,
+ rights);
+}
+
+
+/*
+ lsa_GetQuotasForAccount
+*/
+static NTSTATUS dcesrv_lsa_GetQuotasForAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_GetQuotasForAccount *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_SetQuotasForAccount
+*/
+static NTSTATUS dcesrv_lsa_SetQuotasForAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetQuotasForAccount *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_GetSystemAccessAccount
+*/
+static NTSTATUS dcesrv_lsa_GetSystemAccessAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_GetSystemAccessAccount *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_account_state *astate;
+ int ret;
+ unsigned int i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "privilege", NULL};
+ struct ldb_message_element *el;
+ const char *sidstr;
+
+ *(r->out.access_mask) = 0x00000000;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ sidstr = ldap_encode_ndr_dom_sid(mem_ctx, astate->account_sid);
+ if (sidstr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = gendb_search(astate->policy->pdb, mem_ctx, NULL, &res, attrs,
+ "objectSid=%s", sidstr);
+ if (ret < 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (ret != 1) {
+ return NT_STATUS_OK;
+ }
+
+ el = ldb_msg_find_element(res[0], "privilege");
+ if (el == NULL || el->num_values == 0) {
+ return NT_STATUS_OK;
+ }
+
+ for (i=0;i<el->num_values;i++) {
+ uint32_t right_bit = sec_right_bit((const char *)el->values[i].data);
+ if (right_bit == 0) {
+ /* Perhaps an privilege, not a right */
+ continue;
+ }
+ *(r->out.access_mask) |= right_bit;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_SetSystemAccessAccount
+*/
+static NTSTATUS dcesrv_lsa_SetSystemAccessAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetSystemAccessAccount *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+/*
+ lsa_CreateSecret
+*/
+static NTSTATUS dcesrv_lsa_CreateSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CreateSecret *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *policy_handle;
+ struct lsa_policy_state *policy_state;
+ struct lsa_secret_state *secret_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs, *msg;
+ const char *attrs[] = {
+ NULL
+ };
+
+ const char *name;
+
+ int ret;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.sec_handle);
+
+ switch (security_session_user_level(session_info, NULL))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ break;
+ default:
+ /* Users and annonymous are not allowed create secrets */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ policy_state = policy_handle->data;
+
+ if (!r->in.name.string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ secret_state = talloc(mem_ctx, struct lsa_secret_state);
+ NT_STATUS_HAVE_NO_MEMORY(secret_state);
+ secret_state->policy = policy_state;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (strncmp("G$", r->in.name.string, 2) == 0) {
+ const char *name2;
+
+ secret_state->global = true;
+
+ name = &r->in.name.string[2];
+ if (strlen(name) == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ name2 = talloc_asprintf(mem_ctx, "%s Secret",
+ ldb_binary_encode_string(mem_ctx, name));
+ NT_STATUS_HAVE_NO_MEMORY(name2);
+
+ /*
+ * We need to connect to the database as system, as this is
+ * one of the rare RPC calls that must read the secrets
+ * (and this is denied otherwise)
+ *
+ * We also save the current remote session details so they can
+ * used by the audit logging module. This allows the audit
+ * logging to report the remote users details, rather than the
+ * system users details.
+ */
+ secret_state->sam_ldb =
+ dcesrv_samdb_connect_as_system(secret_state, dce_call);
+ NT_STATUS_HAVE_NO_MEMORY(secret_state->sam_ldb);
+
+ /* search for the secret record */
+ ret = gendb_search(secret_state->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(cn=%s)(objectclass=secret))",
+ name2);
+ if (ret > 0) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if (ret < 0) {
+ DEBUG(0,("Failure searching for CN=%s: %s\n",
+ name2, ldb_errstring(secret_state->sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, policy_state->system_dn);
+ NT_STATUS_HAVE_NO_MEMORY(msg->dn);
+ if (!ldb_dn_add_child_fmt(msg->dn, "cn=%s", name2)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_msg_add_string(msg, "cn", name2);
+ if (ret != LDB_SUCCESS) return NT_STATUS_NO_MEMORY;
+ } else {
+ secret_state->global = false;
+
+ name = r->in.name.string;
+ if (strlen(name) == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ secret_state->sam_ldb = secrets_db_connect(secret_state,
+ dce_call->conn->dce_ctx->lp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(secret_state->sam_ldb);
+
+ /* search for the secret record */
+ ret = gendb_search(secret_state->sam_ldb, mem_ctx,
+ ldb_dn_new(mem_ctx, secret_state->sam_ldb, "cn=LSA Secrets"),
+ &msgs, attrs,
+ "(&(cn=%s)(objectclass=secret))",
+ ldb_binary_encode_string(mem_ctx, name));
+ if (ret > 0) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if (ret < 0) {
+ DEBUG(0,("Failure searching for CN=%s: %s\n",
+ name, ldb_errstring(secret_state->sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ msg->dn = ldb_dn_new_fmt(mem_ctx, secret_state->sam_ldb,
+ "cn=%s,cn=LSA Secrets", name);
+ NT_STATUS_HAVE_NO_MEMORY(msg->dn);
+ ret = ldb_msg_add_string(msg, "cn", name);
+ if (ret != LDB_SUCCESS) return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_msg_add_string(msg, "objectClass", "secret");
+ if (ret != LDB_SUCCESS) return NT_STATUS_NO_MEMORY;
+
+ secret_state->secret_dn = talloc_reference(secret_state, msg->dn);
+ NT_STATUS_HAVE_NO_MEMORY(secret_state->secret_dn);
+
+ /* create the secret */
+ ret = ldb_add(secret_state->sam_ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to create secret record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(secret_state->sam_ldb)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ handle = dcesrv_handle_create(dce_call, LSA_HANDLE_SECRET);
+ NT_STATUS_HAVE_NO_MEMORY(handle);
+
+ handle->data = talloc_steal(handle, secret_state);
+
+ secret_state->access_mask = r->in.access_mask;
+ secret_state->policy = talloc_reference(secret_state, policy_state);
+ NT_STATUS_HAVE_NO_MEMORY(secret_state->policy);
+
+ *r->out.sec_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_OpenSecret
+*/
+static NTSTATUS dcesrv_lsa_OpenSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenSecret *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *policy_handle;
+ struct lsa_policy_state *policy_state;
+ struct lsa_secret_state *secret_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs;
+ const char *attrs[] = {
+ NULL
+ };
+ const char *name;
+ int ret;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.sec_handle);
+ policy_state = policy_handle->data;
+
+ if (!r->in.name.string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (security_session_user_level(session_info, NULL))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ break;
+ default:
+ /* Users and annonymous are not allowed to access secrets */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ secret_state = talloc(mem_ctx, struct lsa_secret_state);
+ if (!secret_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ secret_state->policy = policy_state;
+
+ if (strncmp("G$", r->in.name.string, 2) == 0) {
+ name = &r->in.name.string[2];
+ /*
+ * We need to connect to the database as system, as this is
+ * one of the rare RPC calls that must read the secrets
+ * (and this is denied otherwise)
+ *
+ * We also save the current remote session details so they can
+ * used by the audit logging module. This allows the audit
+ * logging to report the remote users details, rather than the
+ * system users details.
+ */
+ secret_state->sam_ldb =
+ dcesrv_samdb_connect_as_system(secret_state, dce_call);
+ NT_STATUS_HAVE_NO_MEMORY(secret_state->sam_ldb);
+ secret_state->global = true;
+
+ if (strlen(name) < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* search for the secret record */
+ ret = gendb_search(secret_state->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(cn=%s Secret)(objectclass=secret))",
+ ldb_binary_encode_string(mem_ctx, name));
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching DN %s\n", ret,
+ ldb_dn_get_linearized(policy_state->system_dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ } else {
+ secret_state->global = false;
+ secret_state->sam_ldb = secrets_db_connect(secret_state,
+ dce_call->conn->dce_ctx->lp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(secret_state->sam_ldb);
+
+ name = r->in.name.string;
+ if (strlen(name) < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* search for the secret record */
+ ret = gendb_search(secret_state->sam_ldb, mem_ctx,
+ ldb_dn_new(mem_ctx, secret_state->sam_ldb, "cn=LSA Secrets"),
+ &msgs, attrs,
+ "(&(cn=%s)(objectclass=secret))",
+ ldb_binary_encode_string(mem_ctx, name));
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching CN=%s\n",
+ ret, ldb_binary_encode_string(mem_ctx, name)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ secret_state->secret_dn = talloc_reference(secret_state, msgs[0]->dn);
+
+ handle = dcesrv_handle_create(dce_call, LSA_HANDLE_SECRET);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, secret_state);
+
+ secret_state->access_mask = r->in.access_mask;
+ secret_state->policy = talloc_reference(secret_state, policy_state);
+
+ *r->out.sec_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_SetSecret
+*/
+static NTSTATUS dcesrv_lsa_SetSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetSecret *r)
+{
+
+ struct dcesrv_handle *h;
+ struct lsa_secret_state *secret_state;
+ struct ldb_message *msg;
+ DATA_BLOB session_key;
+ DATA_BLOB crypt_secret, secret;
+ struct ldb_val val;
+ int ret;
+ NTSTATUS status = NT_STATUS_OK;
+
+ struct timeval now = timeval_current();
+ NTTIME nt_now = timeval_to_nttime(&now);
+
+ DCESRV_PULL_HANDLE(h, r->in.sec_handle, LSA_HANDLE_SECRET);
+
+ secret_state = h->data;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = talloc_reference(mem_ctx, secret_state->secret_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_transport_session_key(dce_call, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (r->in.old_val) {
+ /* Decrypt */
+ crypt_secret.data = r->in.old_val->data;
+ crypt_secret.length = r->in.old_val->size;
+
+ status = sess_decrypt_blob(mem_ctx, &crypt_secret, &session_key, &secret);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ val.data = secret.data;
+ val.length = secret.length;
+
+ /* set value */
+ if (ldb_msg_add_value(msg, "priorValue", &val, NULL) != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* set old value mtime */
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "priorSetTime", nt_now) != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ } else {
+ /* If the old value is not set, then migrate the
+ * current value to the old value */
+ const struct ldb_val *old_val;
+ NTTIME last_set_time;
+ struct ldb_message **res;
+ const char *attrs[] = {
+ "currentValue",
+ "lastSetTime",
+ NULL
+ };
+
+ /* search for the secret record */
+ ret = gendb_search_dn(secret_state->sam_ldb,mem_ctx,
+ secret_state->secret_dn, &res, attrs);
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching dn=%s\n", ret,
+ ldb_dn_get_linearized(secret_state->secret_dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ old_val = ldb_msg_find_ldb_val(res[0], "currentValue");
+ last_set_time = ldb_msg_find_attr_as_uint64(res[0], "lastSetTime", 0);
+
+ if (old_val) {
+ /* set old value */
+ if (ldb_msg_add_value(msg, "priorValue",
+ old_val, NULL) != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ if (samdb_msg_add_delete(secret_state->sam_ldb,
+ mem_ctx, msg, "priorValue") != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* set old value mtime */
+ if (ldb_msg_find_ldb_val(res[0], "lastSetTime")) {
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "priorSetTime", last_set_time) != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "priorSetTime", nt_now) != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+
+ if (r->in.new_val) {
+ /* Decrypt */
+ crypt_secret.data = r->in.new_val->data;
+ crypt_secret.length = r->in.new_val->size;
+
+ status = sess_decrypt_blob(mem_ctx, &crypt_secret, &session_key, &secret);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ val.data = secret.data;
+ val.length = secret.length;
+
+ /* set value */
+ if (ldb_msg_add_value(msg, "currentValue", &val, NULL) != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* set new value mtime */
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "lastSetTime", nt_now) != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ /* NULL out the NEW value */
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "lastSetTime", nt_now) != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (samdb_msg_add_delete(secret_state->sam_ldb,
+ mem_ctx, msg, "currentValue") != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* modify the samdb record */
+ ret = dsdb_replace(secret_state->sam_ldb, msg, 0);
+ if (ret != LDB_SUCCESS) {
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_QuerySecret
+*/
+static NTSTATUS dcesrv_lsa_QuerySecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QuerySecret *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *h;
+ struct lsa_secret_state *secret_state;
+ struct ldb_message *msg;
+ DATA_BLOB session_key;
+ DATA_BLOB crypt_secret, secret;
+ int ret;
+ struct ldb_message **res;
+ const char *attrs[] = {
+ "currentValue",
+ "priorValue",
+ "lastSetTime",
+ "priorSetTime",
+ NULL
+ };
+
+ NTSTATUS nt_status;
+
+ DCESRV_PULL_HANDLE(h, r->in.sec_handle, LSA_HANDLE_SECRET);
+
+ /* Ensure user is permitted to read this... */
+ switch (security_session_user_level(session_info, NULL))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ break;
+ default:
+ /* Users and annonymous are not allowed to read secrets */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ secret_state = h->data;
+
+ /* pull all the user attributes */
+ ret = gendb_search_dn(secret_state->sam_ldb, mem_ctx,
+ secret_state->secret_dn, &res, attrs);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ nt_status = dcesrv_transport_session_key(dce_call, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ if (r->in.old_val) {
+ const struct ldb_val *prior_val;
+ r->out.old_val = talloc_zero(mem_ctx, struct lsa_DATA_BUF_PTR);
+ if (!r->out.old_val) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ prior_val = ldb_msg_find_ldb_val(msg, "priorValue");
+
+ if (prior_val && prior_val->length) {
+ secret.data = prior_val->data;
+ secret.length = prior_val->length;
+
+ /* Encrypt */
+ crypt_secret = sess_encrypt_blob(mem_ctx, &secret, &session_key);
+ if (!crypt_secret.length) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.old_val->buf = talloc(mem_ctx, struct lsa_DATA_BUF);
+ if (!r->out.old_val->buf) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.old_val->buf->size = crypt_secret.length;
+ r->out.old_val->buf->length = crypt_secret.length;
+ r->out.old_val->buf->data = crypt_secret.data;
+ }
+ }
+
+ if (r->in.old_mtime) {
+ r->out.old_mtime = talloc(mem_ctx, NTTIME);
+ if (!r->out.old_mtime) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.old_mtime = ldb_msg_find_attr_as_uint64(msg, "priorSetTime", 0);
+ }
+
+ if (r->in.new_val) {
+ const struct ldb_val *new_val;
+ r->out.new_val = talloc_zero(mem_ctx, struct lsa_DATA_BUF_PTR);
+ if (!r->out.new_val) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ new_val = ldb_msg_find_ldb_val(msg, "currentValue");
+
+ if (new_val && new_val->length) {
+ secret.data = new_val->data;
+ secret.length = new_val->length;
+
+ /* Encrypt */
+ crypt_secret = sess_encrypt_blob(mem_ctx, &secret, &session_key);
+ if (!crypt_secret.length) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.new_val->buf = talloc(mem_ctx, struct lsa_DATA_BUF);
+ if (!r->out.new_val->buf) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.new_val->buf->length = crypt_secret.length;
+ r->out.new_val->buf->size = crypt_secret.length;
+ r->out.new_val->buf->data = crypt_secret.data;
+ }
+ }
+
+ if (r->in.new_mtime) {
+ r->out.new_mtime = talloc(mem_ctx, NTTIME);
+ if (!r->out.new_mtime) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.new_mtime = ldb_msg_find_attr_as_uint64(msg, "lastSetTime", 0);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_LookupPrivValue
+*/
+static NTSTATUS dcesrv_lsa_LookupPrivValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupPrivValue *r)
+{
+ struct dcesrv_handle *h;
+ int id;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ id = sec_privilege_id(r->in.name->string);
+ if (id == SEC_PRIV_INVALID) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ r->out.luid->low = id;
+ r->out.luid->high = 0;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_LookupPrivName
+*/
+static NTSTATUS dcesrv_lsa_LookupPrivName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupPrivName *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_StringLarge *name;
+ const char *privname;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ if (r->in.luid->high != 0) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ privname = sec_privilege_name(r->in.luid->low);
+ if (privname == NULL) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ name = talloc(mem_ctx, struct lsa_StringLarge);
+ if (name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ name->string = privname;
+
+ *r->out.name = name;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_LookupPrivDisplayName
+*/
+static NTSTATUS dcesrv_lsa_LookupPrivDisplayName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupPrivDisplayName *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_StringLarge *disp_name = NULL;
+ enum sec_privilege id;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ id = sec_privilege_id(r->in.name->string);
+ if (id == SEC_PRIV_INVALID) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ disp_name = talloc(mem_ctx, struct lsa_StringLarge);
+ if (disp_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ disp_name->string = sec_privilege_display_name(id, &r->in.language_id);
+ if (disp_name->string == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ *r->out.disp_name = disp_name;
+ *r->out.returned_language_id = 0;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_EnumAccountsWithUserRight
+*/
+static NTSTATUS dcesrv_lsa_EnumAccountsWithUserRight(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_EnumAccountsWithUserRight *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int ret, i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "objectSid", NULL};
+ const char *privname;
+ bool ok;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ if (r->in.name == NULL) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ privname = r->in.name->string;
+
+ ok = dcesrc_lsa_valid_AccountRight(privname);
+ if (!ok) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ ret = gendb_search(state->pdb, mem_ctx, NULL, &res, attrs,
+ "privilege=%s", privname);
+ if (ret < 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (ret == 0) {
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ r->out.sids->sids = talloc_array(r->out.sids, struct lsa_SidPtr, ret);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<ret;i++) {
+ r->out.sids->sids[i].sid = samdb_result_dom_sid(r->out.sids->sids,
+ res[i], "objectSid");
+ NT_STATUS_HAVE_NO_MEMORY(r->out.sids->sids[i].sid);
+ }
+ r->out.sids->num_sids = ret;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_AddAccountRights
+*/
+static NTSTATUS dcesrv_lsa_AddAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_AddAccountRights *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, state,
+ LDB_FLAG_MOD_ADD,
+ r->in.sid, r->in.rights);
+}
+
+
+/*
+ lsa_RemoveAccountRights
+*/
+static NTSTATUS dcesrv_lsa_RemoveAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RemoveAccountRights *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, state,
+ LDB_FLAG_MOD_DELETE,
+ r->in.sid, r->in.rights);
+}
+
+
+/*
+ lsa_StorePrivateData
+*/
+static NTSTATUS dcesrv_lsa_StorePrivateData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_StorePrivateData *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_RetrievePrivateData
+*/
+static NTSTATUS dcesrv_lsa_RetrievePrivateData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_RetrievePrivateData *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_GetUserName
+*/
+static NTSTATUS dcesrv_lsa_GetUserName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_GetUserName *r)
+{
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ NTSTATUS status = NT_STATUS_OK;
+ const char *account_name;
+ const char *authority_name;
+ struct lsa_String *_account_name;
+ struct lsa_String *_authority_name = NULL;
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ /* this is what w2k3 does */
+ r->out.account_name = r->in.account_name;
+ r->out.authority_name = r->in.authority_name;
+
+ if (r->in.account_name
+ && *r->in.account_name
+ /* && *(*r->in.account_name)->string */
+ ) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.authority_name
+ && *r->in.authority_name
+ /* && *(*r->in.authority_name)->string */
+ ) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ account_name = talloc_reference(mem_ctx, session_info->info->account_name);
+ authority_name = talloc_reference(mem_ctx, session_info->info->domain_name);
+
+ _account_name = talloc(mem_ctx, struct lsa_String);
+ NT_STATUS_HAVE_NO_MEMORY(_account_name);
+ _account_name->string = account_name;
+
+ if (r->in.authority_name) {
+ _authority_name = talloc(mem_ctx, struct lsa_String);
+ NT_STATUS_HAVE_NO_MEMORY(_authority_name);
+ _authority_name->string = authority_name;
+ }
+
+ *r->out.account_name = _account_name;
+ if (r->out.authority_name) {
+ *r->out.authority_name = _authority_name;
+ }
+
+ return status;
+}
+
+/*
+ lsa_SetInfoPolicy2
+*/
+static NTSTATUS dcesrv_lsa_SetInfoPolicy2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SetInfoPolicy2 *r)
+{
+ /* need to support these */
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static void kdc_get_policy(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smb_krb5_context *smb_krb5_context,
+ struct lsa_DomainInfoKerberos *k)
+{
+ time_t svc_tkt_lifetime;
+ time_t usr_tkt_lifetime;
+ time_t renewal_lifetime;
+
+ /* Our KDC always re-validates the client */
+ k->authentication_options = LSA_POLICY_KERBEROS_VALIDATE_CLIENT;
+
+ lpcfg_default_kdc_policy(mem_ctx, lp_ctx, &svc_tkt_lifetime,
+ &usr_tkt_lifetime, &renewal_lifetime);
+
+ unix_to_nt_time(&k->service_tkt_lifetime, svc_tkt_lifetime);
+ unix_to_nt_time(&k->user_tkt_lifetime, usr_tkt_lifetime);
+ unix_to_nt_time(&k->user_tkt_renewaltime, renewal_lifetime);
+#ifdef SAMBA4_USES_HEIMDAL /* MIT lacks krb5_get_max_time_skew.
+ However in the parent function we basically just did a full
+ krb5_context init with the only purpose of getting a global
+ config option (the max skew), it would probably make more sense
+ to have a lp_ or ldb global option as the samba default */
+ if (smb_krb5_context) {
+ unix_to_nt_time(&k->clock_skew,
+ krb5_get_max_time_skew(smb_krb5_context->krb5_context));
+ }
+#endif
+ k->reserved = 0;
+}
+/*
+ lsa_QueryDomainInformationPolicy
+*/
+static NTSTATUS dcesrv_lsa_QueryDomainInformationPolicy(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_QueryDomainInformationPolicy *r)
+{
+ union lsa_DomainInformationPolicy *info;
+
+ info = talloc_zero(r->out.info, union lsa_DomainInformationPolicy);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case LSA_DOMAIN_INFO_POLICY_EFS:
+ talloc_free(info);
+ *r->out.info = NULL;
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ case LSA_DOMAIN_INFO_POLICY_KERBEROS:
+ {
+ struct lsa_DomainInfoKerberos *k = &info->kerberos_info;
+ struct smb_krb5_context *smb_krb5_context;
+ int ret = smb_krb5_init_context(mem_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ &smb_krb5_context);
+ if (ret != 0) {
+ talloc_free(info);
+ *r->out.info = NULL;
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ kdc_get_policy(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ smb_krb5_context,
+ k);
+ talloc_free(smb_krb5_context);
+ *r->out.info = info;
+ return NT_STATUS_OK;
+ }
+ default:
+ talloc_free(info);
+ *r->out.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+}
+
+/*
+ lsa_SetDomInfoPolicy
+*/
+static NTSTATUS dcesrv_lsa_SetDomainInformationPolicy(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SetDomainInformationPolicy *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ lsa_TestCall
+*/
+static NTSTATUS dcesrv_lsa_TestCall(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_TestCall *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ lsa_CREDRWRITE
+*/
+static NTSTATUS dcesrv_lsa_CREDRWRITE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRWRITE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRREAD
+*/
+static NTSTATUS dcesrv_lsa_CREDRREAD(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRREAD *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRENUMERATE
+*/
+static NTSTATUS dcesrv_lsa_CREDRENUMERATE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRENUMERATE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRWRITEDOMAINCREDENTIALS
+*/
+static NTSTATUS dcesrv_lsa_CREDRWRITEDOMAINCREDENTIALS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRWRITEDOMAINCREDENTIALS *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRREADDOMAINCREDENTIALS
+*/
+static NTSTATUS dcesrv_lsa_CREDRREADDOMAINCREDENTIALS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRREADDOMAINCREDENTIALS *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRDELETE
+*/
+static NTSTATUS dcesrv_lsa_CREDRDELETE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRDELETE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRGETTARGETINFO
+*/
+static NTSTATUS dcesrv_lsa_CREDRGETTARGETINFO(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRGETTARGETINFO *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRPROFILELOADED
+*/
+static NTSTATUS dcesrv_lsa_CREDRPROFILELOADED(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRPROFILELOADED *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRGETSESSIONTYPES
+*/
+static NTSTATUS dcesrv_lsa_CREDRGETSESSIONTYPES(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRGETSESSIONTYPES *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARREGISTERAUDITEVENT
+*/
+static NTSTATUS dcesrv_lsa_LSARREGISTERAUDITEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARREGISTERAUDITEVENT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARGENAUDITEVENT
+*/
+static NTSTATUS dcesrv_lsa_LSARGENAUDITEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARGENAUDITEVENT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARUNREGISTERAUDITEVENT
+*/
+static NTSTATUS dcesrv_lsa_LSARUNREGISTERAUDITEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARUNREGISTERAUDITEVENT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_lsaRQueryForestTrustInformation
+*/
+static NTSTATUS dcesrv_lsa_lsaRQueryForestTrustInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_lsaRQueryForestTrustInformation *r)
+{
+ struct dcesrv_handle *h = NULL;
+ struct lsa_policy_state *p_state = NULL;
+ int forest_level = DS_DOMAIN_FUNCTION_2000;
+ const char * const trust_attrs[] = {
+ "securityIdentifier",
+ "flatName",
+ "trustPartner",
+ "trustAttributes",
+ "trustDirection",
+ "trustType",
+ "msDS-TrustForestTrustInfo",
+ NULL
+ };
+ struct ldb_message *trust_tdo_msg = NULL;
+ struct lsa_TrustDomainInfoInfoEx *trust_tdo = NULL;
+ struct ForestTrustInfo *trust_fti = NULL;
+ struct lsa_ForestTrustInformation *trust_lfti = NULL;
+ NTSTATUS status;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ p_state = h->data;
+
+ if (strcmp(p_state->domain_dns, p_state->forest_dns)) {
+ return NT_STATUS_INVALID_DOMAIN_STATE;
+ }
+
+ forest_level = dsdb_forest_functional_level(p_state->sam_ldb);
+ if (forest_level < DS_DOMAIN_FUNCTION_2003) {
+ return NT_STATUS_INVALID_DOMAIN_STATE;
+ }
+
+ if (r->in.trusted_domain_name->string == NULL) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ status = dsdb_trust_search_tdo(p_state->sam_ldb,
+ r->in.trusted_domain_name->string,
+ r->in.trusted_domain_name->string,
+ trust_attrs, mem_ctx, &trust_tdo_msg);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dsdb_trust_parse_tdo_info(mem_ctx, trust_tdo_msg, &trust_tdo);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!(trust_tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.highest_record_type >= LSA_FOREST_TRUST_RECORD_TYPE_LAST) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dsdb_trust_parse_forest_info(mem_ctx,
+ trust_tdo_msg,
+ &trust_fti);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dsdb_trust_forest_info_to_lsa(mem_ctx, trust_fti,
+ &trust_lfti);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *r->out.forest_trust_info = trust_lfti;
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_lsaRSetForestTrustInformation
+*/
+static NTSTATUS dcesrv_lsa_lsaRSetForestTrustInformation(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_lsaRSetForestTrustInformation *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *p_state;
+ const char * const trust_attrs[] = {
+ "securityIdentifier",
+ "flatName",
+ "trustPartner",
+ "trustAttributes",
+ "trustDirection",
+ "trustType",
+ "msDS-TrustForestTrustInfo",
+ NULL
+ };
+ struct ldb_message *trust_tdo_msg = NULL;
+ struct lsa_TrustDomainInfoInfoEx *trust_tdo = NULL;
+ struct lsa_ForestTrustInformation *step1_lfti = NULL;
+ struct lsa_ForestTrustInformation *step2_lfti = NULL;
+ struct ForestTrustInfo *trust_fti = NULL;
+ struct ldb_result *trusts_res = NULL;
+ unsigned int i;
+ struct lsa_TrustDomainInfoInfoEx *xref_tdo = NULL;
+ struct lsa_ForestTrustInformation *xref_lfti = NULL;
+ struct lsa_ForestTrustCollisionInfo *c_info = NULL;
+ DATA_BLOB ft_blob = {};
+ struct ldb_message *msg = NULL;
+ struct server_id *server_ids = NULL;
+ uint32_t num_server_ids = 0;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ int ret;
+ bool in_transaction = false;
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ p_state = h->data;
+
+ if (strcmp(p_state->domain_dns, p_state->forest_dns)) {
+ return NT_STATUS_INVALID_DOMAIN_STATE;
+ }
+
+ if (r->in.check_only == 0) {
+ ret = ldb_transaction_start(p_state->sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ in_transaction = true;
+ }
+
+ /*
+ * abort if we are not a PDC
+ *
+ * In future we should use a function like IsEffectiveRoleOwner()
+ */
+ if (!samdb_is_pdc(p_state->sam_ldb)) {
+ status = NT_STATUS_INVALID_DOMAIN_ROLE;
+ goto done;
+ }
+
+ if (r->in.trusted_domain_name->string == NULL) {
+ status = NT_STATUS_NO_SUCH_DOMAIN;
+ goto done;
+ }
+
+ status = dsdb_trust_search_tdo(p_state->sam_ldb,
+ r->in.trusted_domain_name->string,
+ r->in.trusted_domain_name->string,
+ trust_attrs, mem_ctx, &trust_tdo_msg);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ status = NT_STATUS_NO_SUCH_DOMAIN;
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = dsdb_trust_parse_tdo_info(mem_ctx, trust_tdo_msg, &trust_tdo);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (!(trust_tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (r->in.highest_record_type >= LSA_FOREST_TRUST_RECORD_TYPE_LAST) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /*
+ * verify and normalize the given forest trust info.
+ *
+ * Step1: doesn't reorder yet, so step1_lfti might contain
+ * NULL entries. This means dsdb_trust_verify_forest_info()
+ * can generate collision entries with the callers index.
+ */
+ status = dsdb_trust_normalize_forest_info_step1(mem_ctx,
+ r->in.forest_trust_info,
+ &step1_lfti);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ c_info = talloc_zero(r->out.collision_info,
+ struct lsa_ForestTrustCollisionInfo);
+ if (c_info == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /*
+ * First check our own forest, then other domains/forests
+ */
+
+ status = dsdb_trust_xref_tdo_info(mem_ctx, p_state->sam_ldb,
+ &xref_tdo);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ status = dsdb_trust_xref_forest_info(mem_ctx, p_state->sam_ldb,
+ &xref_lfti);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ /*
+ * The documentation proposed to generate
+ * LSA_FOREST_TRUST_COLLISION_XREF collisions.
+ * But Windows always uses LSA_FOREST_TRUST_COLLISION_TDO.
+ */
+ status = dsdb_trust_verify_forest_info(xref_tdo, xref_lfti,
+ LSA_FOREST_TRUST_COLLISION_TDO,
+ c_info, step1_lfti);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ /* fetch all other trusted domain objects */
+ status = dsdb_trust_search_tdos(p_state->sam_ldb,
+ trust_tdo->domain_name.string,
+ trust_attrs,
+ mem_ctx, &trusts_res);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ /*
+ * now check against the other domains.
+ * and generate LSA_FOREST_TRUST_COLLISION_TDO collisions.
+ */
+ for (i = 0; i < trusts_res->count; i++) {
+ struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+ struct ForestTrustInfo *fti = NULL;
+ struct lsa_ForestTrustInformation *lfti = NULL;
+
+ status = dsdb_trust_parse_tdo_info(mem_ctx,
+ trusts_res->msgs[i],
+ &tdo);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = dsdb_trust_parse_forest_info(tdo,
+ trusts_res->msgs[i],
+ &fti);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ continue;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = dsdb_trust_forest_info_to_lsa(tdo, fti, &lfti);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = dsdb_trust_verify_forest_info(tdo, lfti,
+ LSA_FOREST_TRUST_COLLISION_TDO,
+ c_info, step1_lfti);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ TALLOC_FREE(tdo);
+ }
+
+ if (r->in.check_only != 0) {
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ /*
+ * not just a check, write info back
+ */
+
+ /*
+ * normalize the given forest trust info.
+ *
+ * Step2: adds TOP_LEVEL_NAME[_EX] in reverse order,
+ * followed by DOMAIN_INFO in reverse order. It also removes
+ * possible NULL entries from Step1.
+ */
+ status = dsdb_trust_normalize_forest_info_step2(mem_ctx, step1_lfti,
+ &step2_lfti);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = dsdb_trust_forest_info_from_lsa(mem_ctx, step2_lfti,
+ &trust_fti);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ ndr_err = ndr_push_struct_blob(&ft_blob, mem_ctx, trust_fti,
+ (ndr_push_flags_fn_t)ndr_push_ForestTrustInfo);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, trust_tdo_msg->dn);
+ if (!msg->dn) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ ret = ldb_msg_append_value(msg, "msDS-TrustForestTrustInfo",
+ &ft_blob, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ ret = ldb_modify(p_state->sam_ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ status = dsdb_ldb_err_to_ntstatus(ret);
+
+ DEBUG(0, ("Failed to store Forest Trust Info: %s\n",
+ ldb_errstring(p_state->sam_ldb)));
+
+ goto done;
+ }
+
+ /* ok, all fine, commit transaction and return */
+ in_transaction = false;
+ ret = ldb_transaction_commit(p_state->sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto done;
+ }
+
+ /*
+ * Notify winbindd that we have a acquired forest trust info
+ */
+ status = irpc_servers_byname(imsg_ctx,
+ mem_ctx,
+ "winbind_server",
+ &num_server_ids,
+ &server_ids);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("irpc_servers_byname failed\n");
+ goto done;
+ }
+
+ imessaging_send(imsg_ctx,
+ server_ids[0],
+ MSG_WINBIND_RELOAD_TRUSTED_DOMAINS,
+ NULL);
+
+ status = NT_STATUS_OK;
+
+done:
+ if (NT_STATUS_IS_OK(status) && c_info->count != 0) {
+ *r->out.collision_info = c_info;
+ }
+
+ if (in_transaction) {
+ ldb_transaction_cancel(p_state->sam_ldb);
+ }
+
+ return status;
+}
+
+/*
+ lsa_CREDRRENAME
+*/
+static NTSTATUS dcesrv_lsa_CREDRRENAME(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRRENAME *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+/*
+ lsa_LSAROPENPOLICYSCE
+*/
+static NTSTATUS dcesrv_lsa_LSAROPENPOLICYSCE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSAROPENPOLICYSCE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARADTREGISTERSECURITYEVENTSOURCE
+*/
+static NTSTATUS dcesrv_lsa_LSARADTREGISTERSECURITYEVENTSOURCE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARADTREGISTERSECURITYEVENTSOURCE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE
+*/
+static NTSTATUS dcesrv_lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARADTREPORTSECURITYEVENT
+*/
+static NTSTATUS dcesrv_lsa_LSARADTREPORTSECURITYEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARADTREPORTSECURITYEVENT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_lsa_s.c"
+
+
+
+/*****************************************
+NOTE! The remaining calls below were
+removed in w2k3, so the DCESRV_FAULT()
+replies are the correct implementation. Do
+not try and fill these in with anything else
+******************************************/
+
+/*
+ dssetup_DsRoleDnsNameToFlatName
+*/
+static WERROR dcesrv_dssetup_DsRoleDnsNameToFlatName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleDnsNameToFlatName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleDcAsDc
+*/
+static WERROR dcesrv_dssetup_DsRoleDcAsDc(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleDcAsDc *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleDcAsReplica
+*/
+static WERROR dcesrv_dssetup_DsRoleDcAsReplica(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleDcAsReplica *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleDemoteDc
+*/
+static WERROR dcesrv_dssetup_DsRoleDemoteDc(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleDemoteDc *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleGetDcOperationProgress
+*/
+static WERROR dcesrv_dssetup_DsRoleGetDcOperationProgress(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleGetDcOperationProgress *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleGetDcOperationResults
+*/
+static WERROR dcesrv_dssetup_DsRoleGetDcOperationResults(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleGetDcOperationResults *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleCancel
+*/
+static WERROR dcesrv_dssetup_DsRoleCancel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleCancel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleServerSaveStateForUpgrade
+*/
+static WERROR dcesrv_dssetup_DsRoleServerSaveStateForUpgrade(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleServerSaveStateForUpgrade *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleUpgradeDownlevelServer
+*/
+static WERROR dcesrv_dssetup_DsRoleUpgradeDownlevelServer(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleUpgradeDownlevelServer *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleAbortDownlevelServerUpgrade
+*/
+static WERROR dcesrv_dssetup_DsRoleAbortDownlevelServerUpgrade(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleAbortDownlevelServerUpgrade *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_dssetup_s.c"
+
+NTSTATUS dcerpc_server_lsa_init(TALLOC_CTX *ctx)
+{
+ NTSTATUS ret;
+
+ ret = dcerpc_server_dssetup_init(ctx);
+ if (!NT_STATUS_IS_OK(ret)) {
+ return ret;
+ }
+ ret = dcerpc_server_lsarpc_init(ctx);
+ if (!NT_STATUS_IS_OK(ret)) {
+ return ret;
+ }
+ return ret;
+}
diff --git a/source4/rpc_server/lsa/lsa.h b/source4/rpc_server/lsa/lsa.h
new file mode 100644
index 0000000..5c00ba5
--- /dev/null
+++ b/source4/rpc_server/lsa/lsa.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the lsarpc pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "auth/auth.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include <ldb_errors.h>
+#include "libcli/security/security.h"
+#include "libcli/auth/libcli_auth.h"
+#include "param/secrets.h"
+#include "../lib/util/util_ldb.h"
+#include "librpc/gen_ndr/ndr_dssetup.h"
+#include "param/param.h"
+
+/*
+ state associated with a lsa_OpenPolicy() operation
+*/
+struct lsa_policy_state {
+ struct dcesrv_handle *handle;
+ struct ldb_context *sam_ldb;
+ struct ldb_context *pdb;
+ struct ldb_dn *domain_dn;
+ struct ldb_dn *forest_dn;
+ struct ldb_dn *builtin_dn;
+ struct ldb_dn *system_dn;
+ const char *domain_name;
+ const char *domain_dns;
+ const char *forest_dns;
+ struct dom_sid *domain_sid;
+ struct GUID domain_guid;
+ struct dom_sid *builtin_sid;
+ struct dom_sid *nt_authority_sid;
+ struct dom_sid *creator_owner_domain_sid;
+ struct dom_sid *world_domain_sid;
+ int mixed_domain;
+ struct security_descriptor *sd;
+ uint32_t access_mask;
+};
+
+enum lsa_handle {
+ LSA_HANDLE_POLICY,
+ LSA_HANDLE_ACCOUNT,
+ LSA_HANDLE_SECRET,
+ LSA_HANDLE_TRUSTED_DOMAIN
+};
+
+#include "rpc_server/lsa/proto.h"
+
diff --git a/source4/rpc_server/lsa/lsa_init.c b/source4/rpc_server/lsa/lsa_init.c
new file mode 100644
index 0000000..1065cc3
--- /dev/null
+++ b/source4/rpc_server/lsa/lsa_init.c
@@ -0,0 +1,293 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the lsarpc pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2007
+
+ 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 "rpc_server/lsa/lsa.h"
+
+/*
+ * This matches a Windows 2012R2 dc in
+ * a domain with function level 2012R2.
+ */
+#define DCESRV_LSA_POLICY_SD_SDDL \
+ "O:BAG:SY" \
+ "D:" \
+ "(D;;0x00000800;;;AN)" \
+ "(A;;0x000f1fff;;;BA)" \
+ "(A;;0x00020801;;;WD)" \
+ "(A;;0x00000801;;;AN)" \
+ "(A;;0x00001000;;;LS)" \
+ "(A;;0x00001000;;;NS)" \
+ "(A;;0x00001000;;;S-1-5-17)" \
+ "(A;;0x00000801;;;AC)" \
+ "(A;;0x00000801;;;S-1-15-2-2)"
+
+static const struct generic_mapping dcesrv_lsa_policy_mapping = {
+ LSA_POLICY_READ,
+ LSA_POLICY_WRITE,
+ LSA_POLICY_EXECUTE,
+ LSA_POLICY_ALL_ACCESS
+};
+
+NTSTATUS dcesrv_lsa_get_policy_state(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ uint32_t access_desired,
+ struct lsa_policy_state **_state)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ enum security_user_level security_level;
+ struct lsa_policy_state *state;
+ struct ldb_result *dom_res;
+ const char *dom_attrs[] = {
+ "objectSid",
+ "objectGUID",
+ "nTMixedDomain",
+ "fSMORoleOwner",
+ NULL
+ };
+ char *p;
+ int ret;
+
+ state = talloc_zero(mem_ctx, struct lsa_policy_state);
+ if (!state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* make sure the sam database is accessible */
+ state->sam_ldb = dcesrv_samdb_connect_as_user(state, dce_call);
+ if (state->sam_ldb == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /* and the privilege database */
+ state->pdb = privilege_connect(state, dce_call->conn->dce_ctx->lp_ctx);
+ if (state->pdb == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /* work out the domain_dn - useful for so many calls its worth
+ fetching here */
+ state->domain_dn = ldb_get_default_basedn(state->sam_ldb);
+ if (!state->domain_dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* work out the forest root_dn - useful for so many calls its worth
+ fetching here */
+ state->forest_dn = ldb_get_root_basedn(state->sam_ldb);
+ if (!state->forest_dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_search(state->sam_ldb, mem_ctx, &dom_res,
+ state->domain_dn, LDB_SCOPE_BASE, dom_attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+ if (dom_res->count != 1) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->domain_sid = samdb_result_dom_sid(state, dom_res->msgs[0], "objectSid");
+ if (!state->domain_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->domain_guid = samdb_result_guid(dom_res->msgs[0], "objectGUID");
+
+ state->mixed_domain = ldb_msg_find_attr_as_uint(dom_res->msgs[0], "nTMixedDomain", 0);
+
+ talloc_free(dom_res);
+
+ state->domain_name = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
+
+ state->domain_dns = ldb_dn_canonical_string(state, state->domain_dn);
+ if (!state->domain_dns) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ p = strchr(state->domain_dns, '/');
+ if (p) {
+ *p = '\0';
+ }
+
+ state->forest_dns = ldb_dn_canonical_string(state, state->forest_dn);
+ if (!state->forest_dns) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ p = strchr(state->forest_dns, '/');
+ if (p) {
+ *p = '\0';
+ }
+
+ /* work out the builtin_dn - useful for so many calls its worth
+ fetching here */
+ state->builtin_dn = samdb_search_dn(state->sam_ldb, state, state->domain_dn, "(objectClass=builtinDomain)");
+ if (!state->builtin_dn) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ /* work out the system_dn - useful for so many calls its worth
+ fetching here */
+ state->system_dn = samdb_system_container_dn(state->sam_ldb, state);
+ if (state->system_dn == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->builtin_sid = dom_sid_parse_talloc(state, SID_BUILTIN);
+ if (!state->builtin_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->nt_authority_sid = dom_sid_parse_talloc(state, SID_NT_AUTHORITY);
+ if (!state->nt_authority_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->creator_owner_domain_sid = dom_sid_parse_talloc(state, SID_CREATOR_OWNER_DOMAIN);
+ if (!state->creator_owner_domain_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->world_domain_sid = dom_sid_parse_talloc(state, SID_WORLD_DOMAIN);
+ if (!state->world_domain_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->sd = sddl_decode(state, DCESRV_LSA_POLICY_SD_SDDL,
+ state->domain_sid);
+ if (state->sd == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->sd->dacl->revision = SECURITY_ACL_REVISION_NT4;
+
+ se_map_generic(&access_desired, &dcesrv_lsa_policy_mapping);
+ security_acl_map_generic(state->sd->dacl, &dcesrv_lsa_policy_mapping);
+
+ security_level = security_session_user_level(session_info, NULL);
+ if (security_level >= SECURITY_SYSTEM) {
+ /*
+ * The security descriptor doesn't allow system,
+ * but we want to allow system via ncalrpc as root.
+ */
+ state->access_mask = access_desired;
+ if (state->access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
+ state->access_mask &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+ state->access_mask |= LSA_POLICY_ALL_ACCESS;
+ }
+ } else {
+ NTSTATUS status;
+
+ status = se_access_check(state->sd,
+ session_info->security_token,
+ access_desired,
+ &state->access_mask);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("%s: access desired[0x%08X] rejected[0x%08X] - %s\n",
+ __func__,
+ (unsigned)access_desired,
+ (unsigned)state->access_mask,
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ DEBUG(10,("%s: access desired[0x%08X] granted[0x%08X] - success.\n",
+ __func__,
+ (unsigned)access_desired,
+ (unsigned)state->access_mask));
+
+ *_state = state;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_OpenPolicy2
+*/
+NTSTATUS dcesrv_lsa_OpenPolicy2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenPolicy2 *r)
+{
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
+ NTSTATUS status;
+ struct lsa_policy_state *state;
+ struct dcesrv_handle *handle;
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ ZERO_STRUCTP(r->out.handle);
+
+ if (r->in.attr != NULL &&
+ r->in.attr->root_dir != NULL) {
+ /* MS-LSAD 3.1.4.4.1 */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx,
+ r->in.access_mask,
+ &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ handle = dcesrv_handle_create(dce_call, LSA_HANDLE_POLICY);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, state);
+
+ state->handle = handle;
+ *r->out.handle = handle->wire_handle;
+
+ /* note that we have completely ignored the attr element of
+ the OpenPolicy. As far as I can tell, this is what w2k3
+ does */
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_OpenPolicy
+ a wrapper around lsa_OpenPolicy2
+*/
+NTSTATUS dcesrv_lsa_OpenPolicy(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenPolicy *r)
+{
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
+ struct lsa_OpenPolicy2 r2;
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ r2.in.system_name = NULL;
+ r2.in.attr = r->in.attr;
+ r2.in.access_mask = r->in.access_mask;
+ r2.out.handle = r->out.handle;
+
+ return dcesrv_lsa_OpenPolicy2(dce_call, mem_ctx, &r2);
+}
+
+
diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
new file mode 100644
index 0000000..61cb8a1
--- /dev/null
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -0,0 +1,2281 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the lsarpc pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2007
+
+ 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 "rpc_server/lsa/lsa.h"
+#include "libds/common/roles.h"
+#include "libds/common/flag_mapping.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+
+struct dcesrv_lsa_TranslatedItem {
+ enum lsa_SidType type;
+ const struct dom_sid *sid;
+ const char *name;
+ const char *authority_name;
+ const struct dom_sid *authority_sid;
+ uint32_t flags;
+ uint32_t wb_idx;
+ bool done;
+ struct {
+ const char *domain; /* only $DOMAIN\ */
+ const char *namespace; /* $NAMESPACE\ or @$NAMESPACE */
+ const char *principal; /* \$PRINCIPAL or $PRIN@IPAL */
+ const char *sid; /* "S-1-5-21-9000-8000-7000-6000" */
+ const char *rid; /* "00001770" */
+ } hints;
+};
+
+struct dcesrv_lsa_LookupSids_base_state;
+struct dcesrv_lsa_LookupNames_base_state;
+
+struct dcesrv_lsa_Lookup_view {
+ const char *name;
+ NTSTATUS (*lookup_sid)(struct dcesrv_lsa_LookupSids_base_state *state,
+ struct dcesrv_lsa_TranslatedItem *item);
+ NTSTATUS (*lookup_name)(struct dcesrv_lsa_LookupNames_base_state *state,
+ struct dcesrv_lsa_TranslatedItem *item);
+};
+
+struct dcesrv_lsa_Lookup_view_table {
+ const char *name;
+ size_t count;
+ const struct dcesrv_lsa_Lookup_view **array;
+};
+
+static const struct dcesrv_lsa_Lookup_view_table *dcesrv_lsa_view_table(
+ enum lsa_LookupNamesLevel level);
+
+/*
+ lookup a SID for 1 name
+*/
+static NTSTATUS dcesrv_lsa_lookup_name(struct lsa_policy_state *state,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const struct dom_sid *domain_sid,
+ struct ldb_dn *domain_dn,
+ const char *principal,
+ const struct dom_sid **p_sid,
+ enum lsa_SidType *p_type)
+{
+ const char * const attrs[] = { "objectSid", "sAMAccountType", NULL};
+ struct ldb_message **res = NULL;
+ const char *nt4_account = NULL;
+ char *encoded_account = NULL;
+ const char *at = NULL;
+ NTSTATUS status;
+ const struct dom_sid *sid = NULL;
+ uint32_t atype;
+ enum lsa_SidType type;
+ bool match = false;
+ int ret;
+
+ if ((principal == NULL) || (principal[0] == '\0')) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ at = strchr(principal, '@');
+ if (at != NULL) {
+ const char *nt4_domain = NULL;
+
+ status = crack_name_to_nt4_name(mem_ctx,
+ state->sam_ldb,
+ DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+ principal,
+ &nt4_domain,
+ &nt4_account);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("Failed to crack name %s into an NT4 name: %s\n",
+ principal, nt_errstr(status)));
+ return status;
+ }
+
+ match = strequal(nt4_domain, domain_name);
+ if (!match) {
+ /*
+ * TODO: handle multiple domains in a forest.
+ */
+ return NT_STATUS_NONE_MAPPED;
+ }
+ } else {
+ nt4_account = principal;
+ }
+
+ encoded_account = ldb_binary_encode_string(mem_ctx, nt4_account);
+ if (encoded_account == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = gendb_search(state->sam_ldb, mem_ctx, domain_dn, &res, attrs,
+ "(&(sAMAccountName=%s)(objectSid=*))",
+ encoded_account);
+ TALLOC_FREE(encoded_account);
+ if (ret < 0) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+ if (ret == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (ret > 1) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ DBG_ERR("nt4_account[%s] found %d times (principal[%s]) - %s\n",
+ nt4_account, ret, principal, nt_errstr(status));
+ return status;
+ }
+
+ sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
+ if (sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Check that this is in the domain */
+ match = dom_sid_in_domain(domain_sid, sid);
+ if (!match) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
+ type = ds_atype_map(atype);
+ if (type == SID_NAME_UNKNOWN) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ *p_sid = sid;
+ *p_type = type;
+ return NT_STATUS_OK;
+}
+
+
+/*
+ add to the lsa_RefDomainList for LookupSids and LookupNames
+*/
+static NTSTATUS dcesrv_lsa_authority_list(const char *authority_name,
+ const struct dom_sid *authority_sid,
+ struct lsa_RefDomainList *domains,
+ uint32_t *sid_index)
+{
+ uint32_t i;
+
+ *sid_index = UINT32_MAX;
+
+ if (authority_name == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ /* see if we've already done this authority name */
+ for (i=0;i<domains->count;i++) {
+ if (strcasecmp_m(authority_name, domains->domains[i].name.string) == 0) {
+ *sid_index = i;
+ return NT_STATUS_OK;
+ }
+ }
+
+ domains->domains = talloc_realloc(domains,
+ domains->domains,
+ struct lsa_DomainInfo,
+ domains->count+1);
+ if (domains->domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ domains->domains[i].name.string = talloc_strdup(domains->domains,
+ authority_name);
+ if (domains->domains[i].name.string == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ domains->domains[i].sid = dom_sid_dup(domains->domains,
+ authority_sid);
+ if (domains->domains[i].sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ domains->count++;
+ domains->max_size = LSA_REF_DOMAIN_LIST_MULTIPLIER * domains->count;
+ *sid_index = i;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lookup a name for 1 SID
+*/
+static NTSTATUS dcesrv_lsa_lookup_sid(struct lsa_policy_state *state,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const struct dom_sid *domain_sid,
+ struct ldb_dn *domain_dn,
+ const struct dom_sid *sid,
+ const char **p_name,
+ enum lsa_SidType *p_type)
+{
+ const char * const attrs[] = { "sAMAccountName", "sAMAccountType", NULL};
+ struct ldb_message **res = NULL;
+ char *encoded_sid = NULL;
+ const char *name = NULL;
+ uint32_t atype;
+ enum lsa_SidType type;
+ int ret;
+
+ encoded_sid = ldap_encode_ndr_dom_sid(mem_ctx, sid);
+ if (encoded_sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = gendb_search(state->sam_ldb, mem_ctx, domain_dn, &res, attrs,
+ "(&(objectSid=%s)(sAMAccountName=*))", encoded_sid);
+ TALLOC_FREE(encoded_sid);
+ if (ret < 0) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+ if (ret == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (ret > 1) {
+ NTSTATUS status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ DBG_ERR("sid[%s] found %d times - %s\n",
+ dom_sid_string(mem_ctx, sid), ret, nt_errstr(status));
+ return status;
+ }
+
+ name = ldb_msg_find_attr_as_string(res[0], "sAMAccountName", NULL);
+ if (name == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
+ type = ds_atype_map(atype);
+ if (type == SID_NAME_UNKNOWN) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ *p_name = name;
+ *p_type = type;
+ return NT_STATUS_OK;
+}
+
+struct dcesrv_lsa_LookupSids_base_state {
+ struct dcesrv_call_state *dce_call;
+
+ TALLOC_CTX *mem_ctx;
+
+ struct lsa_policy_state *policy_state;
+
+ struct lsa_LookupSids3 r;
+
+ const struct dcesrv_lsa_Lookup_view_table *view_table;
+ struct dcesrv_lsa_TranslatedItem *items;
+
+ struct dsdb_trust_routing_table *routing_table;
+
+ struct {
+ struct dcerpc_binding_handle *irpc_handle;
+ struct lsa_SidArray sids;
+ struct lsa_RefDomainList *domains;
+ struct lsa_TransNameArray2 names;
+ uint32_t count;
+ NTSTATUS result;
+ } wb;
+
+ struct {
+ struct lsa_LookupSids *l;
+ struct lsa_LookupSids2 *l2;
+ struct lsa_LookupSids3 *l3;
+ } _r;
+};
+
+static NTSTATUS dcesrv_lsa_LookupSids_base_finish(
+ struct dcesrv_lsa_LookupSids_base_state *state);
+static void dcesrv_lsa_LookupSids_base_map(
+ struct dcesrv_lsa_LookupSids_base_state *state);
+static void dcesrv_lsa_LookupSids_base_done(struct tevent_req *subreq);
+
+static NTSTATUS dcesrv_lsa_LookupSids_base_call(struct dcesrv_lsa_LookupSids_base_state *state)
+{
+ struct lsa_LookupSids3 *r = &state->r;
+ struct tevent_req *subreq = NULL;
+ uint32_t v;
+ uint32_t i;
+
+ *r->out.domains = NULL;
+ r->out.names->count = 0;
+ r->out.names->names = NULL;
+ *r->out.count = 0;
+
+ state->view_table = dcesrv_lsa_view_table(r->in.level);
+ if (state->view_table == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *r->out.domains = talloc_zero(r->out.domains, struct lsa_RefDomainList);
+ if (*r->out.domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.names->names = talloc_zero_array(r->out.names,
+ struct lsa_TranslatedName2,
+ r->in.sids->num_sids);
+ if (r->out.names->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->items = talloc_zero_array(state,
+ struct dcesrv_lsa_TranslatedItem,
+ r->in.sids->num_sids);
+ if (state->items == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<r->in.sids->num_sids;i++) {
+ struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+ uint32_t rid = 0;
+
+ if (r->in.sids->sids[i].sid == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ item->type = SID_NAME_UNKNOWN;
+ item->sid = r->in.sids->sids[i].sid;
+
+ item->hints.sid = dom_sid_string(state->items, item->sid);
+ if (item->hints.sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dom_sid_split_rid(state->items, item->sid, NULL, &rid);
+ item->hints.rid = talloc_asprintf(state->items,
+ "%08X", (unsigned)rid);
+ if (item->hints.rid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ for (v=0; v < state->view_table->count; v++) {
+ const struct dcesrv_lsa_Lookup_view *view =
+ state->view_table->array[v];
+
+ for (i=0; i < r->in.sids->num_sids; i++) {
+ struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+ NTSTATUS status;
+
+ if (item->done) {
+ continue;
+ }
+
+ status = view->lookup_sid(state, item);
+ if (NT_STATUS_IS_OK(status)) {
+ item->done = true;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ status = NT_STATUS_OK;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_SOME_NOT_MAPPED)) {
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ }
+
+ if (state->wb.irpc_handle == NULL) {
+ return dcesrv_lsa_LookupSids_base_finish(state);
+ }
+
+ state->wb.sids.sids = talloc_zero_array(state, struct lsa_SidPtr,
+ r->in.sids->num_sids);
+ if (state->wb.sids.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i < r->in.sids->num_sids; i++) {
+ struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+
+ if (item->done) {
+ continue;
+ }
+
+ item->wb_idx = state->wb.sids.num_sids;
+ state->wb.sids.sids[item->wb_idx] = r->in.sids->sids[i];
+ state->wb.sids.num_sids++;
+ }
+
+ subreq = dcerpc_lsa_LookupSids3_send(state,
+ state->dce_call->event_ctx,
+ state->wb.irpc_handle,
+ &state->wb.sids,
+ &state->wb.domains,
+ &state->wb.names,
+ state->r.in.level,
+ &state->wb.count,
+ state->r.in.lookup_options,
+ state->r.in.client_revision);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;;
+ }
+ state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ tevent_req_set_callback(subreq,
+ dcesrv_lsa_LookupSids_base_done,
+ state);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_lsa_LookupSids_base_finish(
+ struct dcesrv_lsa_LookupSids_base_state *state)
+{
+ struct lsa_LookupSids3 *r = &state->r;
+ uint32_t i;
+
+ for (i=0;i<r->in.sids->num_sids;i++) {
+ struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+ NTSTATUS status;
+ uint32_t sid_index = UINT32_MAX;
+
+ status = dcesrv_lsa_authority_list(item->authority_name,
+ item->authority_sid,
+ *r->out.domains,
+ &sid_index);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (item->name == NULL && r->in.level == LSA_LOOKUP_NAMES_ALL) {
+ if (sid_index == UINT32_MAX) {
+ item->name = item->hints.sid;
+ } else {
+ item->name = item->hints.rid;
+ }
+ }
+
+ r->out.names->names[i].sid_type = item->type;
+ r->out.names->names[i].name.string = item->name;
+ r->out.names->names[i].sid_index = sid_index;
+ r->out.names->names[i].unknown = item->flags;
+
+ r->out.names->count++;
+ if (item->type != SID_NAME_UNKNOWN) {
+ (*r->out.count)++;
+ }
+ }
+
+ if (*r->out.count == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (*r->out.count != r->in.sids->num_sids) {
+ return STATUS_SOME_UNMAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void dcesrv_lsa_LookupSids_base_map(
+ struct dcesrv_lsa_LookupSids_base_state *state)
+{
+ if (state->_r.l3 != NULL) {
+ struct lsa_LookupSids3 *r = state->_r.l3;
+
+ r->out.result = state->r.out.result;
+ return;
+ }
+
+ if (state->_r.l2 != NULL) {
+ struct lsa_LookupSids2 *r = state->_r.l2;
+
+ r->out.result = state->r.out.result;
+ return;
+ }
+
+ if (state->_r.l != NULL) {
+ struct lsa_LookupSids *r = state->_r.l;
+ uint32_t i;
+
+ r->out.result = state->r.out.result;
+
+ SMB_ASSERT(state->r.out.names->count <= r->in.sids->num_sids);
+ for (i = 0; i < state->r.out.names->count; i++) {
+ struct lsa_TranslatedName2 *n2 =
+ &state->r.out.names->names[i];
+ struct lsa_TranslatedName *n =
+ &r->out.names->names[i];
+
+ n->sid_type = n2->sid_type;
+ n->name = n2->name;
+ n->sid_index = n2->sid_index;
+ }
+ r->out.names->count = state->r.out.names->count;
+ return;
+ }
+}
+
+static void dcesrv_lsa_LookupSids_base_done(struct tevent_req *subreq)
+{
+ struct dcesrv_lsa_LookupSids_base_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcesrv_lsa_LookupSids_base_state);
+ struct dcesrv_call_state *dce_call = state->dce_call;
+ NTSTATUS status;
+ uint32_t i;
+
+ status = dcerpc_lsa_LookupSids3_recv(subreq, state->mem_ctx,
+ &state->wb.result);
+ TALLOC_FREE(subreq);
+ TALLOC_FREE(state->wb.irpc_handle);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ DEBUG(0,(__location__ ": IRPC callback failed %s\n",
+ nt_errstr(status)));
+ goto finished;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ DEBUG(0,(__location__ ": IRPC callback failed %s\n",
+ nt_errstr(status)));
+ goto finished;
+ }
+
+ status = state->wb.result;
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ status = NT_STATUS_OK;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_SOME_NOT_MAPPED)) {
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ goto finished;
+ }
+
+ for (i=0; i < state->r.in.sids->num_sids; i++) {
+ struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+ struct lsa_TranslatedName2 *s2 = NULL;
+ struct lsa_DomainInfo *d = NULL;
+
+ if (item->done) {
+ continue;
+ }
+
+ if (item->wb_idx >= state->wb.names.count) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto finished;
+ }
+
+ s2 = &state->wb.names.names[item->wb_idx];
+
+ item->type = s2->sid_type;
+ item->name = s2->name.string;
+ item->flags = s2->unknown;
+
+ if (s2->sid_index == UINT32_MAX) {
+ continue;
+ }
+
+ if (state->wb.domains == NULL) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto finished;
+ }
+
+ if (s2->sid_index >= state->wb.domains->count) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto finished;
+ }
+
+ d = &state->wb.domains->domains[s2->sid_index];
+
+ item->authority_name = d->name.string;
+ item->authority_sid = d->sid;
+ }
+
+ status = dcesrv_lsa_LookupSids_base_finish(state);
+ finished:
+ state->r.out.result = status;
+ dcesrv_lsa_LookupSids_base_map(state);
+
+ status = dcesrv_reply(dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": dcesrv_reply() failed - %s\n", nt_errstr(status)));
+ }
+}
+
+/*
+ lsa_LookupSids2
+*/
+NTSTATUS dcesrv_lsa_LookupSids2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupSids2 *r)
+{
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
+ struct dcesrv_lsa_LookupSids_base_state *state = NULL;
+ struct dcesrv_handle *policy_handle = NULL;
+ NTSTATUS status;
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ *r->out.domains = NULL;
+ r->out.names->count = 0;
+ r->out.names->names = NULL;
+ *r->out.count = 0;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupSids_base_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->policy_state = policy_handle->data;
+
+ state->r.in.sids = r->in.sids;
+ state->r.in.level = r->in.level;
+ state->r.in.lookup_options = r->in.lookup_options;
+ state->r.in.client_revision = r->in.client_revision;
+ state->r.in.names = r->in.names;
+ state->r.in.count = r->in.count;
+ state->r.out.domains = r->out.domains;
+ state->r.out.names = r->out.names;
+ state->r.out.count = r->out.count;
+
+ state->_r.l2 = r;
+
+ status = dcesrv_lsa_LookupSids_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return status;
+ }
+
+ state->r.out.result = status;
+ dcesrv_lsa_LookupSids_base_map(state);
+ return status;
+}
+
+/* A random hexidecimal number (honest!) */
+#define LSA_SERVER_IMPLICIT_POLICY_STATE_MAGIC 0xc0c99e00
+
+/*
+ Ensure we're operating on an schannel connection,
+ and use a lsa_policy_state cache on the connection.
+*/
+static NTSTATUS schannel_call_setup(struct dcesrv_call_state *dce_call,
+ struct lsa_policy_state **_policy_state)
+{
+ struct lsa_policy_state *policy_state = NULL;
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
+ enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE;
+ if (transport != NCACN_IP_TCP) {
+ /* We can't call DCESRV_FAULT() in the sub-function */
+ dce_call->fault_code = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /*
+ * We don't have policy handles on this call. So this must be restricted
+ * to crypto connections only.
+ *
+ * NB. gensec requires schannel connections to
+ * have at least DCERPC_AUTH_LEVEL_INTEGRITY.
+ */
+ dcesrv_call_auth_info(dce_call, &auth_type, NULL);
+ if (auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
+ /* We can't call DCESRV_FAULT() in the sub-function */
+ dce_call->fault_code = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /*
+ * We don't have a policy handle on this call, so we want to
+ * make a policy state and cache it for the life of the
+ * connection, to avoid re-opening the DB each call.
+ */
+ policy_state
+ = dcesrv_iface_state_find_conn(dce_call,
+ LSA_SERVER_IMPLICIT_POLICY_STATE_MAGIC,
+ struct lsa_policy_state);
+
+ if (policy_state == NULL) {
+ NTSTATUS status
+ = dcesrv_lsa_get_policy_state(dce_call,
+ dce_call /* mem_ctx */,
+ 0, /* we skip access checks */
+ &policy_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * This will talloc_steal() policy_state onto the
+ * connection, which has longer lifetime than the
+ * immidiate caller requires
+ */
+ status = dcesrv_iface_state_store_conn(dce_call,
+ LSA_SERVER_IMPLICIT_POLICY_STATE_MAGIC,
+ policy_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ *_policy_state = policy_state;
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_LookupSids3
+
+ Identical to LookupSids2, but doesn't take a policy handle
+
+*/
+NTSTATUS dcesrv_lsa_LookupSids3(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupSids3 *r)
+{
+ struct dcesrv_lsa_LookupSids_base_state *state = NULL;
+ NTSTATUS status;
+
+ *r->out.domains = NULL;
+ r->out.names->count = 0;
+ r->out.names->names = NULL;
+ *r->out.count = 0;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupSids_base_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * We don't have a policy handle on this call, so we want to
+ * make a policy state and cache it for the life of the
+ * connection, to avoid re-opening the DB each call.
+ *
+ * This also enforces that this is only available over
+ * ncacn_ip_tcp and with SCHANNEL.
+ *
+ * schannel_call_setup may also set the fault state.
+ *
+ * state->policy_state is shared between all calls on this
+ * connection and is moved with talloc_steal() under the
+ * connection, not dce_call or state.
+ */
+ status = schannel_call_setup(dce_call, &state->policy_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+ state->r.in.sids = r->in.sids;
+ state->r.in.level = r->in.level;
+ state->r.in.lookup_options = r->in.lookup_options;
+ state->r.in.client_revision = r->in.client_revision;
+ state->r.in.names = r->in.names;
+ state->r.in.count = r->in.count;
+ state->r.out.domains = r->out.domains;
+ state->r.out.names = r->out.names;
+ state->r.out.count = r->out.count;
+
+ state->_r.l3 = r;
+
+ status = dcesrv_lsa_LookupSids_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return status;
+ }
+
+ state->r.out.result = status;
+ dcesrv_lsa_LookupSids_base_map(state);
+ return status;
+}
+
+
+/*
+ lsa_LookupSids
+*/
+NTSTATUS dcesrv_lsa_LookupSids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LookupSids *r)
+{
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
+ struct dcesrv_lsa_LookupSids_base_state *state = NULL;
+ struct dcesrv_handle *policy_handle = NULL;
+ NTSTATUS status;
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ *r->out.domains = NULL;
+ r->out.names->count = 0;
+ r->out.names->names = NULL;
+ *r->out.count = 0;
+
+ r->out.names->names = talloc_zero_array(r->out.names,
+ struct lsa_TranslatedName,
+ r->in.sids->num_sids);
+ if (r->out.names->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupSids_base_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->policy_state = policy_handle->data;
+
+ state->r.in.sids = r->in.sids;
+ state->r.in.level = r->in.level;
+ state->r.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
+ state->r.in.client_revision = LSA_CLIENT_REVISION_1;
+ state->r.in.names = talloc_zero(state, struct lsa_TransNameArray2);
+ if (state->r.in.names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->r.in.count = r->in.count;
+ state->r.out.domains = r->out.domains;
+ state->r.out.names = talloc_zero(state, struct lsa_TransNameArray2);
+ if (state->r.out.names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->r.out.count = r->out.count;
+
+ state->_r.l = r;
+
+ status = dcesrv_lsa_LookupSids_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return status;
+ }
+
+ state->r.out.result = status;
+ dcesrv_lsa_LookupSids_base_map(state);
+ return status;
+}
+
+struct dcesrv_lsa_LookupNames_base_state {
+ struct dcesrv_call_state *dce_call;
+
+ TALLOC_CTX *mem_ctx;
+
+ struct lsa_policy_state *policy_state;
+
+ struct lsa_LookupNames4 r;
+
+ const struct dcesrv_lsa_Lookup_view_table *view_table;
+ struct dcesrv_lsa_TranslatedItem *items;
+
+ struct dsdb_trust_routing_table *routing_table;
+
+ struct {
+ struct dcerpc_binding_handle *irpc_handle;
+ uint32_t num_names;
+ struct lsa_String *names;
+ struct lsa_RefDomainList *domains;
+ struct lsa_TransSidArray3 sids;
+ uint32_t count;
+ NTSTATUS result;
+ } wb;
+
+ struct {
+ struct lsa_LookupNames *l;
+ struct lsa_LookupNames2 *l2;
+ struct lsa_LookupNames3 *l3;
+ struct lsa_LookupNames4 *l4;
+ } _r;
+};
+
+static NTSTATUS dcesrv_lsa_LookupNames_base_finish(
+ struct dcesrv_lsa_LookupNames_base_state *state);
+static void dcesrv_lsa_LookupNames_base_map(
+ struct dcesrv_lsa_LookupNames_base_state *state);
+static void dcesrv_lsa_LookupNames_base_done(struct tevent_req *subreq);
+
+static NTSTATUS dcesrv_lsa_LookupNames_base_call(struct dcesrv_lsa_LookupNames_base_state *state)
+{
+ struct lsa_LookupNames4 *r = &state->r;
+ enum lsa_LookupOptions invalid_lookup_options = 0;
+ struct tevent_req *subreq = NULL;
+ uint32_t v;
+ uint32_t i;
+
+ *r->out.domains = NULL;
+ r->out.sids->count = 0;
+ r->out.sids->sids = NULL;
+ *r->out.count = 0;
+
+ if (r->in.level != LSA_LOOKUP_NAMES_ALL) {
+ invalid_lookup_options |=
+ LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES_LOCAL;
+ }
+ if (r->in.lookup_options & invalid_lookup_options) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ state->view_table = dcesrv_lsa_view_table(r->in.level);
+ if (state->view_table == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *r->out.domains = talloc_zero(r->out.domains, struct lsa_RefDomainList);
+ if (*r->out.domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.sids->sids = talloc_zero_array(r->out.sids,
+ struct lsa_TranslatedSid3,
+ r->in.num_names);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->items = talloc_zero_array(state,
+ struct dcesrv_lsa_TranslatedItem,
+ r->in.num_names);
+ if (state->items == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<r->in.num_names;i++) {
+ struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+ char *p = NULL;
+
+ item->type = SID_NAME_UNKNOWN;
+ item->name = r->in.names[i].string;
+ /*
+ * Note: that item->name can be NULL!
+ *
+ * See test_LookupNames_NULL() in
+ * source4/torture/rpc/lsa.c
+ *
+ * nt4 returns NT_STATUS_NONE_MAPPED with sid_type
+ * SID_NAME_UNKNOWN, rid 0, and sid_index -1;
+ *
+ * w2k3/w2k8 return NT_STATUS_OK with sid_type
+ * SID_NAME_DOMAIN, rid -1 and sid_index 0 and BUILTIN domain
+ */
+ if (item->name == NULL) {
+ continue;
+ }
+
+ item->hints.principal = item->name;
+ p = strchr(item->name, '\\');
+ if (p != NULL && p != item->name) {
+ item->hints.domain = talloc_strndup(state->items,
+ item->name,
+ p - item->name);
+ if (item->hints.domain == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ item->hints.namespace = item->hints.domain;
+ p++;
+ if (p[0] == '\0') {
+ /*
+ * This is just 'BUILTIN\'.
+ */
+ item->hints.principal = NULL;
+ } else {
+ item->hints.principal = p;
+ }
+ }
+ if (item->hints.domain == NULL) {
+ p = strchr(item->name, '@');
+ if (p != NULL && p != item->name && p[1] != '\0') {
+ item->hints.namespace = p + 1;
+ }
+ }
+ }
+
+ for (v=0; v < state->view_table->count; v++) {
+ const struct dcesrv_lsa_Lookup_view *view =
+ state->view_table->array[v];
+
+ for (i=0; i < r->in.num_names; i++) {
+ struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+ NTSTATUS status;
+
+ if (item->done) {
+ continue;
+ }
+
+ status = view->lookup_name(state, item);
+ if (NT_STATUS_IS_OK(status)) {
+ item->done = true;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ status = NT_STATUS_OK;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_SOME_NOT_MAPPED)) {
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ }
+
+ if (state->wb.irpc_handle == NULL) {
+ return dcesrv_lsa_LookupNames_base_finish(state);
+ }
+
+ state->wb.names = talloc_zero_array(state, struct lsa_String,
+ r->in.num_names);
+ if (state->wb.names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<r->in.num_names;i++) {
+ struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+
+ if (item->done) {
+ continue;
+ }
+
+ item->wb_idx = state->wb.num_names;
+ state->wb.names[item->wb_idx] = r->in.names[i];
+ state->wb.num_names++;
+ }
+
+ subreq = dcerpc_lsa_LookupNames4_send(state,
+ state->dce_call->event_ctx,
+ state->wb.irpc_handle,
+ state->wb.num_names,
+ state->wb.names,
+ &state->wb.domains,
+ &state->wb.sids,
+ state->r.in.level,
+ &state->wb.count,
+ state->r.in.lookup_options,
+ state->r.in.client_revision);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ tevent_req_set_callback(subreq,
+ dcesrv_lsa_LookupNames_base_done,
+ state);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_lsa_LookupNames_base_finish(
+ struct dcesrv_lsa_LookupNames_base_state *state)
+{
+ struct lsa_LookupNames4 *r = &state->r;
+ uint32_t i;
+
+ for (i=0;i<r->in.num_names;i++) {
+ struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+ NTSTATUS status;
+ uint32_t sid_index = UINT32_MAX;
+
+ status = dcesrv_lsa_authority_list(item->authority_name,
+ item->authority_sid,
+ *r->out.domains,
+ &sid_index);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.sids->sids[i].sid_type = item->type;
+ r->out.sids->sids[i].sid = discard_const_p(struct dom_sid,
+ item->sid);
+ r->out.sids->sids[i].sid_index = sid_index;
+ r->out.sids->sids[i].flags = item->flags;
+
+ r->out.sids->count++;
+ if (item->type != SID_NAME_UNKNOWN) {
+ (*r->out.count)++;
+ }
+ }
+
+ if (*r->out.count == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (*r->out.count != r->in.num_names) {
+ return STATUS_SOME_UNMAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void dcesrv_lsa_LookupNames_base_map(
+ struct dcesrv_lsa_LookupNames_base_state *state)
+{
+ if (state->_r.l4 != NULL) {
+ struct lsa_LookupNames4 *r = state->_r.l4;
+
+ r->out.result = state->r.out.result;
+ return;
+ }
+
+ if (state->_r.l3 != NULL) {
+ struct lsa_LookupNames3 *r = state->_r.l3;
+
+ r->out.result = state->r.out.result;
+ return;
+ }
+
+ if (state->_r.l2 != NULL) {
+ struct lsa_LookupNames2 *r = state->_r.l2;
+ uint32_t i;
+
+ r->out.result = state->r.out.result;
+
+ SMB_ASSERT(state->r.out.sids->count <= r->in.num_names);
+ for (i = 0; i < state->r.out.sids->count; i++) {
+ const struct lsa_TranslatedSid3 *s3 =
+ &state->r.out.sids->sids[i];
+ struct lsa_TranslatedSid2 *s2 =
+ &r->out.sids->sids[i];
+
+ s2->sid_type = s3->sid_type;
+ if (s3->sid_type == SID_NAME_DOMAIN) {
+ s2->rid = UINT32_MAX;
+ } else if (s3->flags & 0x00000004) {
+ s2->rid = UINT32_MAX;
+ } else if (s3->sid == NULL) {
+ /*
+ * MS-LSAT 3.1.4.7 - rid zero is considered
+ * equivalent to sid NULL - so we should return
+ * 0 rid for unmapped entries
+ */
+ s2->rid = 0;
+ } else {
+ s2->rid = 0;
+ dom_sid_split_rid(NULL, s3->sid,
+ NULL, &s2->rid);
+ }
+ s2->sid_index = s3->sid_index;
+ s2->unknown = s3->flags;
+ }
+ r->out.sids->count = state->r.out.sids->count;
+ return;
+ }
+
+ if (state->_r.l != NULL) {
+ struct lsa_LookupNames *r = state->_r.l;
+ uint32_t i;
+
+ r->out.result = state->r.out.result;
+
+ SMB_ASSERT(state->r.out.sids->count <= r->in.num_names);
+ for (i = 0; i < state->r.out.sids->count; i++) {
+ struct lsa_TranslatedSid3 *s3 =
+ &state->r.out.sids->sids[i];
+ struct lsa_TranslatedSid *s =
+ &r->out.sids->sids[i];
+
+ s->sid_type = s3->sid_type;
+ if (s3->sid_type == SID_NAME_DOMAIN) {
+ s->rid = UINT32_MAX;
+ } else if (s3->flags & 0x00000004) {
+ s->rid = UINT32_MAX;
+ } else if (s3->sid == NULL) {
+ /*
+ * MS-LSAT 3.1.4.7 - rid zero is considered
+ * equivalent to sid NULL - so we should return
+ * 0 rid for unmapped entries
+ */
+ s->rid = 0;
+ } else {
+ s->rid = 0;
+ dom_sid_split_rid(NULL, s3->sid,
+ NULL, &s->rid);
+ }
+ s->sid_index = s3->sid_index;
+ }
+ r->out.sids->count = state->r.out.sids->count;
+ return;
+ }
+}
+
+static void dcesrv_lsa_LookupNames_base_done(struct tevent_req *subreq)
+{
+ struct dcesrv_lsa_LookupNames_base_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcesrv_lsa_LookupNames_base_state);
+ struct dcesrv_call_state *dce_call = state->dce_call;
+ NTSTATUS status;
+ uint32_t i;
+
+ status = dcerpc_lsa_LookupNames4_recv(subreq, state->mem_ctx,
+ &state->wb.result);
+ TALLOC_FREE(subreq);
+ TALLOC_FREE(state->wb.irpc_handle);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ DEBUG(0,(__location__ ": IRPC callback failed %s\n",
+ nt_errstr(status)));
+ goto finished;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ DEBUG(0,(__location__ ": IRPC callback failed %s\n",
+ nt_errstr(status)));
+ goto finished;
+ }
+
+ status = state->wb.result;
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ status = NT_STATUS_OK;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_SOME_NOT_MAPPED)) {
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ goto finished;
+ }
+
+ for (i=0; i < state->r.in.num_names;i++) {
+ struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+ struct lsa_TranslatedSid3 *s3 = NULL;
+ struct lsa_DomainInfo *d = NULL;
+
+ if (item->done) {
+ continue;
+ }
+
+ if (item->wb_idx >= state->wb.sids.count) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto finished;
+ }
+
+ s3 = &state->wb.sids.sids[item->wb_idx];
+
+ item->type = s3->sid_type;
+ item->sid = s3->sid;
+ item->flags = s3->flags;
+
+ if (s3->sid_index == UINT32_MAX) {
+ continue;
+ }
+
+ if (state->wb.domains == NULL) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto finished;
+ }
+
+ if (s3->sid_index >= state->wb.domains->count) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto finished;
+ }
+
+ d = &state->wb.domains->domains[s3->sid_index];
+
+ item->authority_name = d->name.string;
+ item->authority_sid = d->sid;
+ }
+
+ status = dcesrv_lsa_LookupNames_base_finish(state);
+ finished:
+ state->r.out.result = status;
+ dcesrv_lsa_LookupNames_base_map(state);
+
+ status = dcesrv_reply(dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": dcesrv_reply() failed - %s\n", nt_errstr(status)));
+ }
+}
+
+/*
+ lsa_LookupNames3
+*/
+NTSTATUS dcesrv_lsa_LookupNames3(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupNames3 *r)
+{
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
+ struct dcesrv_lsa_LookupNames_base_state *state = NULL;
+ struct dcesrv_handle *policy_handle = NULL;
+ NTSTATUS status;
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ *r->out.domains = NULL;
+ r->out.sids->count = 0;
+ r->out.sids->sids = NULL;
+ *r->out.count = 0;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupNames_base_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->policy_state = policy_handle->data;
+
+ state->r.in.num_names = r->in.num_names;
+ state->r.in.names = r->in.names;
+ state->r.in.level = r->in.level;
+ state->r.in.lookup_options = r->in.lookup_options;
+ state->r.in.client_revision = r->in.client_revision;
+ state->r.in.sids = r->in.sids;
+ state->r.in.count = r->in.count;
+ state->r.out.domains = r->out.domains;
+ state->r.out.sids = r->out.sids;
+ state->r.out.count = r->out.count;
+
+ state->_r.l3 = r;
+
+ status = dcesrv_lsa_LookupNames_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return status;
+ }
+
+ state->r.out.result = status;
+ dcesrv_lsa_LookupNames_base_map(state);
+ return status;
+}
+
+/*
+ lsa_LookupNames4
+
+ Identical to LookupNames3, but doesn't take a policy handle
+
+*/
+NTSTATUS dcesrv_lsa_LookupNames4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LookupNames4 *r)
+{
+ struct dcesrv_lsa_LookupNames_base_state *state = NULL;
+ NTSTATUS status;
+
+ *r->out.domains = NULL;
+ r->out.sids->count = 0;
+ r->out.sids->sids = NULL;
+ *r->out.count = 0;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupNames_base_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ /*
+ * We don't have a policy handle on this call, so we want to
+ * make a policy state and cache it for the life of the
+ * connection, to avoid re-opening the DB each call.
+ *
+ * This also enforces that this is only available over
+ * ncacn_ip_tcp and with SCHANNEL.
+ *
+ * schannel_call_setup may also set the fault state.
+ *
+ * state->policy_state is shared between all calls on this
+ * connection and is moved with talloc_steal() under the
+ * connection, not dce_call or state.
+ */
+ status = schannel_call_setup(dce_call, &state->policy_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ state->r.in.num_names = r->in.num_names;
+ state->r.in.names = r->in.names;
+ state->r.in.level = r->in.level;
+ state->r.in.lookup_options = r->in.lookup_options;
+ state->r.in.client_revision = r->in.client_revision;
+ state->r.in.sids = r->in.sids;
+ state->r.in.count = r->in.count;
+ state->r.out.domains = r->out.domains;
+ state->r.out.sids = r->out.sids;
+ state->r.out.count = r->out.count;
+
+ state->_r.l4 = r;
+
+ status = dcesrv_lsa_LookupNames_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return status;
+ }
+
+ state->r.out.result = status;
+ dcesrv_lsa_LookupNames_base_map(state);
+ return status;
+}
+
+/*
+ lsa_LookupNames2
+*/
+NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupNames2 *r)
+{
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
+ struct dcesrv_lsa_LookupNames_base_state *state = NULL;
+ struct dcesrv_handle *policy_handle = NULL;
+ NTSTATUS status;
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ *r->out.domains = NULL;
+ r->out.sids->count = 0;
+ r->out.sids->sids = NULL;
+ *r->out.count = 0;
+
+ r->out.sids->sids = talloc_zero_array(r->out.sids,
+ struct lsa_TranslatedSid2,
+ r->in.num_names);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupNames_base_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->policy_state = policy_handle->data;
+
+ state->r.in.num_names = r->in.num_names;
+ state->r.in.names = r->in.names;
+ state->r.in.level = r->in.level;
+ /*
+ * MS-LSAT 3.1.4.7:
+ *
+ * The LookupOptions and ClientRevision parameters MUST be ignored.
+ * Message processing MUST happen as if LookupOptions is set to
+ * 0x00000000 and ClientRevision is set to 0x00000002.
+ */
+ state->r.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
+ state->r.in.client_revision = LSA_CLIENT_REVISION_2;
+ state->r.in.sids = talloc_zero(state, struct lsa_TransSidArray3);
+ if (state->r.in.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->r.in.count = r->in.count;
+ state->r.out.domains = r->out.domains;
+ state->r.out.sids = talloc_zero(state, struct lsa_TransSidArray3);
+ if (state->r.out.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->r.out.count = r->out.count;
+
+ state->_r.l2 = r;
+
+ status = dcesrv_lsa_LookupNames_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return status;
+ }
+
+ state->r.out.result = status;
+ dcesrv_lsa_LookupNames_base_map(state);
+ return status;
+}
+
+/*
+ lsa_LookupNames
+*/
+NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LookupNames *r)
+{
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
+ struct dcesrv_lsa_LookupNames_base_state *state = NULL;
+ struct dcesrv_handle *policy_handle = NULL;
+ NTSTATUS status;
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ *r->out.domains = NULL;
+ r->out.sids->count = 0;
+ r->out.sids->sids = NULL;
+ *r->out.count = 0;
+
+ r->out.sids->sids = talloc_zero_array(r->out.sids,
+ struct lsa_TranslatedSid,
+ r->in.num_names);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupNames_base_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->policy_state = policy_handle->data;
+
+ state->r.in.num_names = r->in.num_names;
+ state->r.in.names = r->in.names;
+ state->r.in.level = r->in.level;
+ state->r.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
+ state->r.in.client_revision = LSA_CLIENT_REVISION_1;
+ state->r.in.sids = talloc_zero(state, struct lsa_TransSidArray3);
+ if (state->r.in.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->r.in.count = r->in.count;
+ state->r.out.domains = r->out.domains;
+ state->r.out.sids = talloc_zero(state, struct lsa_TransSidArray3);
+ if (state->r.out.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->r.out.count = r->out.count;
+
+ state->_r.l = r;
+
+ status = dcesrv_lsa_LookupNames_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return status;
+ }
+
+ state->r.out.result = status;
+ dcesrv_lsa_LookupNames_base_map(state);
+ return status;
+}
+
+static NTSTATUS dcesrv_lsa_lookup_name_predefined(
+ struct dcesrv_lsa_LookupNames_base_state *state,
+ struct dcesrv_lsa_TranslatedItem *item)
+{
+ NTSTATUS status;
+
+ status = dom_sid_lookup_predefined_name(item->name,
+ &item->sid,
+ &item->type,
+ &item->authority_sid,
+ &item->authority_name);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_lsa_lookup_sid_predefined(
+ struct dcesrv_lsa_LookupSids_base_state *state,
+ struct dcesrv_lsa_TranslatedItem *item)
+{
+ NTSTATUS status;
+
+ status = dom_sid_lookup_predefined_sid(item->sid,
+ &item->name,
+ &item->type,
+ &item->authority_sid,
+ &item->authority_name);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static const struct dcesrv_lsa_Lookup_view view_predefined = {
+ .name = "Predefined",
+ .lookup_sid = dcesrv_lsa_lookup_sid_predefined,
+ .lookup_name = dcesrv_lsa_lookup_name_predefined,
+};
+
+static NTSTATUS dcesrv_lsa_lookup_name_builtin(
+ struct dcesrv_lsa_LookupNames_base_state *state,
+ struct dcesrv_lsa_TranslatedItem *item)
+{
+ struct lsa_policy_state *policy_state = state->policy_state;
+ NTSTATUS status;
+ bool is_builtin = false;
+
+ if (item->name == NULL) {
+ /*
+ * This should not be mapped.
+ */
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * The predefined view already handled the BUILTIN domain.
+ *
+ * Now we just need to find the principal.
+ *
+ * We only allow 'BUILTIN\something' and
+ * not 'something@BUILTIN.
+ *
+ * And we try out best for just 'something'.
+ */
+ is_builtin = strequal(item->hints.domain, NAME_BUILTIN);
+ if (!is_builtin && item->hints.domain != NULL) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ status = dcesrv_lsa_lookup_name(state->policy_state,
+ state->mem_ctx,
+ NAME_BUILTIN,
+ policy_state->builtin_sid,
+ policy_state->builtin_dn,
+ item->hints.principal,
+ &item->sid,
+ &item->type);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ if (!is_builtin) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ /*
+ * We know we're authoritative
+ */
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ item->authority_name = NAME_BUILTIN;
+ item->authority_sid = policy_state->builtin_sid;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_lsa_lookup_sid_builtin(
+ struct dcesrv_lsa_LookupSids_base_state *state,
+ struct dcesrv_lsa_TranslatedItem *item)
+{
+ struct lsa_policy_state *policy_state = state->policy_state;
+ NTSTATUS status;
+ bool is_builtin = false;
+
+ /*
+ * The predefined view already handled the BUILTIN domain.
+ *
+ * Now we just need to find the principal.
+ */
+ is_builtin = dom_sid_in_domain(policy_state->builtin_sid, item->sid);
+ if (!is_builtin) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ status = dcesrv_lsa_lookup_sid(state->policy_state,
+ state->mem_ctx,
+ NAME_BUILTIN,
+ policy_state->builtin_sid,
+ policy_state->builtin_dn,
+ item->sid,
+ &item->name,
+ &item->type);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ /*
+ * We know we're authoritative
+ */
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ item->authority_name = NAME_BUILTIN;
+ item->authority_sid = policy_state->builtin_sid;
+ return NT_STATUS_OK;
+}
+
+static const struct dcesrv_lsa_Lookup_view view_builtin = {
+ .name = "Builtin",
+ .lookup_sid = dcesrv_lsa_lookup_sid_builtin,
+ .lookup_name = dcesrv_lsa_lookup_name_builtin,
+};
+
+static NTSTATUS dcesrv_lsa_lookup_name_account(
+ struct dcesrv_lsa_LookupNames_base_state *state,
+ struct dcesrv_lsa_TranslatedItem *item)
+{
+ struct lsa_policy_state *policy_state = state->policy_state;
+ struct loadparm_context *lp_ctx = state->dce_call->conn->dce_ctx->lp_ctx;
+ struct lsa_LookupNames4 *r = &state->r;
+ NTSTATUS status;
+ int role;
+ bool (*is_local_match_fn)(struct loadparm_context *, const char *) = NULL;
+ bool is_domain = false;
+ bool try_lookup = false;
+ const char *check_domain_name = NULL;
+
+ role = lpcfg_server_role(lp_ctx);
+ if (role == ROLE_ACTIVE_DIRECTORY_DC) {
+ is_local_match_fn = lpcfg_is_my_domain_or_realm;
+ } else {
+ is_local_match_fn = lpcfg_is_myname;
+ }
+
+ if (item->name == NULL) {
+ /*
+ * This should not be mapped.
+ */
+ return NT_STATUS_OK;
+ }
+
+ if (item->hints.domain != NULL && item->hints.principal == NULL) {
+ /*
+ * This is 'DOMAIN\'.
+ */
+ check_domain_name = item->hints.domain;
+ } else {
+ /*
+ * This is just 'DOMAIN'.
+ */
+ check_domain_name = item->name;
+ }
+ is_domain = is_local_match_fn(lp_ctx, check_domain_name);
+ if (is_domain) {
+ item->type = SID_NAME_DOMAIN;
+ item->sid = policy_state->domain_sid;
+ item->authority_name = policy_state->domain_name;
+ item->authority_sid = policy_state->domain_sid;
+ return NT_STATUS_OK;
+ }
+
+ if (r->in.lookup_options & LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES_LOCAL) {
+ if (item->hints.domain != item->hints.namespace) {
+ /*
+ * This means the client asked for an UPN,
+ * and it should not be mapped.
+ */
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (item->hints.namespace != NULL) {
+ is_domain = is_local_match_fn(lp_ctx, item->hints.namespace);
+ try_lookup = is_domain;
+ } else {
+ try_lookup = true;
+ }
+
+ if (!try_lookup) {
+ struct dcesrv_lsa_TranslatedItem tmp;
+
+ tmp = *item;
+ status = dom_sid_lookup_predefined_name(item->hints.namespace,
+ &tmp.sid,
+ &tmp.type,
+ &tmp.authority_sid,
+ &tmp.authority_name);
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * It should not be handled by us.
+ */
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ return status;
+ }
+ }
+
+ if (!try_lookup) {
+ const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+ const struct lsa_ForestTrustDomainInfo *di = NULL;
+
+ if (state->routing_table == NULL) {
+ status = dsdb_trust_routing_table_load(policy_state->sam_ldb,
+ state,
+ &state->routing_table);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ tdo = dsdb_trust_domain_by_name(state->routing_table,
+ item->hints.namespace,
+ &di);
+ if (tdo == NULL) {
+ /*
+ * The name is not resolvable at all...
+ */
+ return NT_STATUS_OK;
+ }
+
+ if (!(tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST)) {
+ /*
+ * The name is not resolvable here
+ */
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ /*
+ * TODO: handle multiple domains in a forest together with
+ * LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY
+ */
+ is_domain = true;
+ try_lookup = true;
+ }
+
+ if (!try_lookup) {
+ /*
+ * It should not be handled by us.
+ */
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ /*
+ * TODO: handle multiple domains in our forest.
+ */
+
+ status = dcesrv_lsa_lookup_name(state->policy_state,
+ state->mem_ctx,
+ policy_state->domain_name,
+ policy_state->domain_sid,
+ policy_state->domain_dn,
+ item->hints.principal,
+ &item->sid,
+ &item->type);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ if (!is_domain) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ /*
+ * We know we're authoritative
+ */
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ item->authority_name = policy_state->domain_name;
+ item->authority_sid = policy_state->domain_sid;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_lsa_lookup_sid_account(
+ struct dcesrv_lsa_LookupSids_base_state *state,
+ struct dcesrv_lsa_TranslatedItem *item)
+{
+ struct lsa_policy_state *policy_state = state->policy_state;
+ NTSTATUS status;
+ bool is_domain;
+
+ is_domain = dom_sid_equal(policy_state->domain_sid, item->sid);
+ if (is_domain) {
+ item->type = SID_NAME_DOMAIN;
+ item->name = policy_state->domain_name;
+ item->authority_name = policy_state->domain_name;
+ item->authority_sid = policy_state->domain_sid;
+ return NT_STATUS_OK;
+ }
+ is_domain = dom_sid_in_domain(policy_state->domain_sid, item->sid);
+ if (!is_domain) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ status = dcesrv_lsa_lookup_sid(state->policy_state,
+ state->mem_ctx,
+ policy_state->domain_name,
+ policy_state->domain_sid,
+ policy_state->domain_dn,
+ item->sid,
+ &item->name,
+ &item->type);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ /*
+ * We know we're authoritative
+ */
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ item->authority_name = policy_state->domain_name;
+ item->authority_sid = policy_state->domain_sid;
+ return NT_STATUS_OK;
+}
+
+static const struct dcesrv_lsa_Lookup_view view_account = {
+ .name = "Account",
+ .lookup_sid = dcesrv_lsa_lookup_sid_account,
+ .lookup_name = dcesrv_lsa_lookup_name_account,
+};
+
+static NTSTATUS dcesrv_lsa_lookup_name_winbind(
+ struct dcesrv_lsa_LookupNames_base_state *state,
+ struct dcesrv_lsa_TranslatedItem *item)
+{
+ struct lsa_LookupNames4 *r = &state->r;
+ const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+ const struct lsa_ForestTrustDomainInfo *di = NULL;
+ NTSTATUS status;
+ const char *check_domain_name = NULL;
+ bool expect_domain = false;
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(state->dce_call->conn);
+
+ if (item->name == NULL) {
+ /*
+ * This should not be mapped.
+ */
+ return NT_STATUS_OK;
+ }
+
+ if (item->hints.domain != NULL && item->hints.principal == NULL) {
+ /*
+ * This is 'DOMAIN\'.
+ */
+ check_domain_name = item->hints.domain;
+ expect_domain = true;
+ } else if (item->hints.namespace != NULL) {
+ /*
+ * This is 'DOMAIN\someone'
+ * or 'someone@DOMAIN'
+ */
+ check_domain_name = item->hints.namespace;
+ } else {
+ /*
+ * This is just 'DOMAIN'.
+ */
+ check_domain_name = item->name;
+ expect_domain = true;
+ }
+
+ if (state->routing_table == NULL) {
+ struct lsa_policy_state *policy_state = state->policy_state;
+
+ status = dsdb_trust_routing_table_load(policy_state->sam_ldb,
+ state,
+ &state->routing_table);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ tdo = dsdb_trust_domain_by_name(state->routing_table,
+ check_domain_name,
+ &di);
+ if (tdo == NULL) {
+ /*
+ * The name is not resolvable at all...
+ *
+ * And for now we don't send unqualified names
+ * to winbindd, as we don't handle them
+ * there yet.
+ *
+ * TODO: how should that work within
+ * winbindd?
+ */
+ return NT_STATUS_OK;
+ }
+
+ if (tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+ /*
+ * The name should have been resolved in the account view.
+ *
+ * TODO: handle multiple domains in a forest...
+ */
+ return NT_STATUS_OK;
+ }
+
+ if (expect_domain) {
+ const char *name = NULL;
+ const struct dom_sid *sid = NULL;
+
+ name = talloc_strdup(state->mem_ctx,
+ di->netbios_domain_name.string);
+ if (name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ sid = dom_sid_dup(state->mem_ctx,
+ di->domain_sid);
+ if (sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ item->type = SID_NAME_DOMAIN;
+ item->sid = sid;
+ item->authority_name = name;
+ item->authority_sid = sid;
+ return NT_STATUS_OK;
+ }
+
+ if (r->in.lookup_options & LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES_LOCAL) {
+ if (item->hints.namespace == NULL) {
+ /*
+ * We should not try to resolve isolated names
+ * remotely.
+ */
+ return NT_STATUS_OK;
+ }
+ }
+
+ /*
+ * We know at least the domain part of the name exists.
+ *
+ * For now the rest handled within winbindd.
+ *
+ * In future we can optimize it based on
+ * r->in.level.
+ *
+ * We can also try to resolve SID_NAME_DOMAIN
+ * just based on the routing table.
+ */
+
+ if (state->wb.irpc_handle != NULL) {
+ /*
+ * already called...
+ */
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ state->wb.irpc_handle = irpc_binding_handle_by_name(state,
+ imsg_ctx,
+ "winbind_server",
+ &ndr_table_lsarpc);
+ if (state->wb.irpc_handle == NULL) {
+ DEBUG(0,("Failed to get binding_handle for winbind_server task\n"));
+ state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /*
+ * 60 seconds timeout should be enough
+ */
+ dcerpc_binding_handle_set_timeout(state->wb.irpc_handle, 60);
+
+ return NT_STATUS_NONE_MAPPED;
+}
+
+static NTSTATUS dcesrv_lsa_lookup_sid_winbind(
+ struct dcesrv_lsa_LookupSids_base_state *state,
+ struct dcesrv_lsa_TranslatedItem *item)
+{
+ const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+ const struct lsa_ForestTrustDomainInfo *di = NULL;
+ struct dcesrv_lsa_TranslatedItem tmp;
+ struct dom_sid domain_sid = {0,};
+ NTSTATUS status;
+ bool match;
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(state->dce_call->conn);
+
+ /*
+ * Verify the sid is not INVALID.
+ */
+ tmp = *item;
+ status = dom_sid_lookup_predefined_sid(tmp.sid,
+ &tmp.name,
+ &tmp.type,
+ &tmp.authority_sid,
+ &tmp.authority_name);
+ if (NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_NONE_MAPPED;
+ }
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ /*
+ * Typically INVALID_SID
+ */
+ return status;
+ }
+
+ if (state->routing_table == NULL) {
+ struct lsa_policy_state *policy_state = state->policy_state;
+
+ status = dsdb_trust_routing_table_load(policy_state->sam_ldb,
+ state,
+ &state->routing_table);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ domain_sid = *item->sid;
+ if (domain_sid.num_auths == 5) {
+ sid_split_rid(&domain_sid, NULL);
+ }
+
+ tdo = dsdb_trust_domain_by_sid(state->routing_table,
+ &domain_sid, &di);
+ if (tdo == NULL) {
+ /*
+ * The sid is not resolvable at all...
+ */
+ return NT_STATUS_OK;
+ }
+
+ if (tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+ /*
+ * The name should have been resolved in the account view.
+ *
+ * TODO: handle multiple domains in a forest...
+ */
+ return NT_STATUS_OK;
+ }
+
+ match = dom_sid_equal(di->domain_sid, item->sid);
+ if (match) {
+ const char *name = NULL;
+
+ name = talloc_strdup(state->mem_ctx,
+ di->netbios_domain_name.string);
+ if (name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ item->type = SID_NAME_DOMAIN;
+ item->name = name;
+ item->authority_name = name;
+ item->authority_sid = item->sid;
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * We know at least the domain part of the sid exists.
+ *
+ * For now the rest handled within winbindd.
+ *
+ * In future we can optimize it based on
+ * r->in.level.
+ *
+ * We can also try to resolve SID_NAME_DOMAIN
+ * just based on the routing table.
+ */
+ if (state->wb.irpc_handle != NULL) {
+ /*
+ * already called...
+ */
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ state->wb.irpc_handle = irpc_binding_handle_by_name(state,
+ imsg_ctx,
+ "winbind_server",
+ &ndr_table_lsarpc);
+ if (state->wb.irpc_handle == NULL) {
+ DEBUG(0,("Failed to get binding_handle for winbind_server task\n"));
+ state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /*
+ * 60 seconds timeout should be enough
+ */
+ dcerpc_binding_handle_set_timeout(state->wb.irpc_handle, 60);
+
+ return NT_STATUS_NONE_MAPPED;
+}
+
+static const struct dcesrv_lsa_Lookup_view view_winbind = {
+ .name = "Winbind",
+ .lookup_sid = dcesrv_lsa_lookup_sid_winbind,
+ .lookup_name = dcesrv_lsa_lookup_name_winbind,
+};
+
+static const struct dcesrv_lsa_Lookup_view *table_all_views[] = {
+ &view_predefined,
+ &view_builtin,
+ &view_account,
+ &view_winbind,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_all = {
+ .name = "LSA_LOOKUP_NAMES_ALL",
+ .count = ARRAY_SIZE(table_all_views),
+ .array = table_all_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view *table_domains_views[] = {
+ &view_account,
+ &view_winbind,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_domains = {
+ .name = "LSA_LOOKUP_NAMES_DOMAINS_ONLY",
+ .count = ARRAY_SIZE(table_domains_views),
+ .array = table_domains_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view *table_primary_views[] = {
+ &view_account,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_primary = {
+ .name = "LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY",
+ .count = ARRAY_SIZE(table_primary_views),
+ .array = table_primary_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view *table_remote_views[] = {
+ &view_winbind,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_gc = {
+ .name = "LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY",
+ .count = ARRAY_SIZE(table_domains_views),
+ .array = table_domains_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_xreferral = {
+ .name = "LSA_LOOKUP_NAMES_FOREST_TRUSTS_ONLY",
+ .count = ARRAY_SIZE(table_remote_views),
+ .array = table_remote_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_xresolve = {
+ .name = "LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2",
+ .count = ARRAY_SIZE(table_domains_views),
+ .array = table_domains_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_rodc = {
+ .name = "LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC",
+ .count = ARRAY_SIZE(table_remote_views),
+ .array = table_remote_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table *dcesrv_lsa_view_table(
+ enum lsa_LookupNamesLevel level)
+{
+ switch (level) {
+ case LSA_LOOKUP_NAMES_ALL:
+ return &table_all;
+ case LSA_LOOKUP_NAMES_DOMAINS_ONLY:
+ return &table_domains;
+ case LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY:
+ return &table_primary;
+ case LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY:
+ return &table_gc;
+ case LSA_LOOKUP_NAMES_FOREST_TRUSTS_ONLY:
+ return &table_xreferral;
+ case LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2:
+ return &table_xresolve;
+ case LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC:
+ return &table_rodc;
+ }
+
+ return NULL;
+}
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
new file mode 100644
index 0000000..3f312f1
--- /dev/null
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -0,0 +1,4631 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the netlogon pipe
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2008
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2005
+ Copyright (C) Matthias Dieter Wallnöfer 2009-2010
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "auth/auth.h"
+#include "auth/auth_sam_reply.h"
+#include "dsdb/samdb/samdb.h"
+#include "../lib/util/util_ldb.h"
+#include "../libcli/auth/schannel.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_irpc_c.h"
+#include "../libcli/ldap/ldap_ndr.h"
+#include "dsdb/samdb/ldb_modules/util.h"
+#include "lib/tsocket/tsocket.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "librpc/rpc/server/netlogon/schannel_util.h"
+#include "lib/socket/netif.h"
+#include "lib/util/util_str_escape.h"
+#include "lib/param/loadparm.h"
+
+#define DCESRV_INTERFACE_NETLOGON_BIND(context, iface) \
+ dcesrv_interface_netlogon_bind(context, iface)
+
+#undef strcasecmp
+
+/*
+ * This #define allows the netlogon interface to accept invalid
+ * association groups, because association groups are to coordinate
+ * handles, and handles are not used in NETLOGON. This in turn avoids
+ * the need to coordinate these across multiple possible NETLOGON
+ * processes
+ */
+#define DCESRV_INTERFACE_NETLOGON_FLAGS DCESRV_INTERFACE_FLAGS_HANDLES_NOT_USED
+
+static NTSTATUS dcesrv_interface_netlogon_bind(struct dcesrv_connection_context *context,
+ const struct dcesrv_interface *iface)
+{
+ struct loadparm_context *lp_ctx = context->conn->dce_ctx->lp_ctx;
+ bool global_allow_nt4_crypto = lpcfg_allow_nt4_crypto(lp_ctx);
+ bool global_reject_md5_client = lpcfg_reject_md5_clients(lp_ctx);
+ int schannel = lpcfg_server_schannel(lp_ctx);
+ bool schannel_global_required = (schannel == true);
+ bool global_require_seal = lpcfg_server_schannel_require_seal(lp_ctx);
+ static bool warned_global_nt4_once = false;
+ static bool warned_global_md5_once = false;
+ static bool warned_global_schannel_once = false;
+ static bool warned_global_seal_once = false;
+
+ if (global_allow_nt4_crypto && !warned_global_nt4_once) {
+ /*
+ * We want admins to notice their misconfiguration!
+ */
+ D_ERR("CVE-2022-38023 (and others): "
+ "Please configure 'allow nt4 crypto = no' (the default), "
+ "See https://bugzilla.samba.org/show_bug.cgi?id=15240\n");
+ warned_global_nt4_once = true;
+ }
+
+ if (!global_reject_md5_client && !warned_global_md5_once) {
+ /*
+ * We want admins to notice their misconfiguration!
+ */
+ D_ERR("CVE-2022-38023: "
+ "Please configure 'reject md5 clients = yes' (the default), "
+ "See https://bugzilla.samba.org/show_bug.cgi?id=15240\n");
+ warned_global_md5_once = true;
+ }
+
+ if (!schannel_global_required && !warned_global_schannel_once) {
+ /*
+ * We want admins to notice their misconfiguration!
+ */
+ D_ERR("CVE-2020-1472(ZeroLogon): "
+ "Please configure 'server schannel = yes' (the default), "
+ "See https://bugzilla.samba.org/show_bug.cgi?id=14497\n");
+ warned_global_schannel_once = true;
+ }
+
+ if (!global_require_seal && !warned_global_seal_once) {
+ /*
+ * We want admins to notice their misconfiguration!
+ */
+ D_ERR("CVE-2022-38023 (and others): "
+ "Please configure 'server schannel require seal = yes' (the default), "
+ "See https://bugzilla.samba.org/show_bug.cgi?id=15240\n");
+ warned_global_seal_once = true;
+ }
+
+ return dcesrv_interface_bind_reject_connect(context, iface);
+}
+
+static NTSTATUS dcesrv_netr_ServerReqChallenge(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerReqChallenge *r)
+{
+ struct netlogon_server_pipe_state *pipe_state = NULL;
+ NTSTATUS ntstatus;
+
+ ZERO_STRUCTP(r->out.return_credentials);
+
+ pipe_state = dcesrv_iface_state_find_conn(dce_call,
+ NETLOGON_SERVER_PIPE_STATE_MAGIC,
+ struct netlogon_server_pipe_state);
+ TALLOC_FREE(pipe_state);
+
+ pipe_state = talloc_zero(dce_call,
+ struct netlogon_server_pipe_state);
+ if (pipe_state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pipe_state->client_challenge = *r->in.credentials;
+
+ netlogon_creds_random_challenge(&pipe_state->server_challenge);
+
+ *r->out.return_credentials = pipe_state->server_challenge;
+
+ ntstatus = dcesrv_iface_state_store_conn(dce_call,
+ NETLOGON_SERVER_PIPE_STATE_MAGIC,
+ pipe_state);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ return ntstatus;
+ }
+
+ ntstatus = schannel_save_challenge(dce_call->conn->dce_ctx->lp_ctx,
+ &pipe_state->client_challenge,
+ &pipe_state->server_challenge,
+ r->in.computer_name);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ TALLOC_FREE(pipe_state);
+ return ntstatus;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ struct dcesrv_call_state *dce_call,
+ struct netr_ServerAuthenticate3 *r,
+ struct netlogon_server_pipe_state *pipe_state,
+ uint32_t negotiate_flags,
+ const char *trust_account_in_db,
+ NTSTATUS orig_status)
+{
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ bool global_allow_nt4_crypto = lpcfg_allow_nt4_crypto(lp_ctx);
+ bool account_allow_nt4_crypto = global_allow_nt4_crypto;
+ const char *explicit_nt4_opt = NULL;
+ bool global_reject_md5_client = lpcfg_reject_md5_clients(lp_ctx);
+ bool account_reject_md5_client = global_reject_md5_client;
+ const char *explicit_md5_opt = NULL;
+ bool reject_des_client;
+ bool allow_nt4_crypto;
+ bool reject_md5_client;
+ bool need_des = true;
+ bool need_md5 = true;
+ int CVE_2022_38023_warn_level = lpcfg_parm_int(lp_ctx, NULL,
+ "CVE_2022_38023", "warn_about_unused_debug_level", DBGLVL_ERR);
+ int CVE_2022_38023_error_level = lpcfg_parm_int(lp_ctx, NULL,
+ "CVE_2022_38023", "error_debug_level", DBGLVL_ERR);
+
+ /*
+ * We don't use lpcfg_parm_bool(), as we
+ * need the explicit_opt pointer in order to
+ * adjust the debug messages.
+ */
+
+ if (trust_account_in_db != NULL) {
+ explicit_nt4_opt = lpcfg_get_parametric(lp_ctx,
+ NULL,
+ "allow nt4 crypto",
+ trust_account_in_db);
+ }
+ if (explicit_nt4_opt != NULL) {
+ account_allow_nt4_crypto = lp_bool(explicit_nt4_opt);
+ }
+ allow_nt4_crypto = account_allow_nt4_crypto;
+ if (trust_account_in_db != NULL) {
+ explicit_md5_opt = lpcfg_get_parametric(lp_ctx,
+ NULL,
+ "server reject md5 schannel",
+ trust_account_in_db);
+ }
+ if (explicit_md5_opt != NULL) {
+ account_reject_md5_client = lp_bool(explicit_md5_opt);
+ }
+ reject_md5_client = account_reject_md5_client;
+
+ reject_des_client = !allow_nt4_crypto;
+
+ /*
+ * If weak cryto is disabled, do not announce that we support RC4.
+ */
+ if (lpcfg_weak_crypto(lp_ctx) == SAMBA_WEAK_CRYPTO_DISALLOWED) {
+ /* Without RC4 and DES we require AES */
+ reject_des_client = true;
+ reject_md5_client = true;
+ }
+
+ if (negotiate_flags & NETLOGON_NEG_STRONG_KEYS) {
+ need_des = false;
+ reject_des_client = false;
+ }
+
+ if (negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ need_des = false;
+ need_md5 = false;
+ reject_des_client = false;
+ reject_md5_client = false;
+ }
+
+ if (reject_des_client || reject_md5_client) {
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (lpcfg_weak_crypto(lp_ctx) == SAMBA_WEAK_CRYPTO_DISALLOWED) {
+ if (CVE_2022_38023_error_level < DBGLVL_NOTICE) {
+ CVE_2022_38023_error_level = DBGLVL_NOTICE;
+ }
+ DEBUG(CVE_2022_38023_error_level, (
+ "CVE-2022-38023: "
+ "client_account[%s] computer_name[%s] "
+ "schannel_type[%u] "
+ "client_negotiate_flags[0x%x] "
+ "%s%s%s "
+ "NT_STATUS_DOWNGRADE_DETECTED "
+ "WEAK_CRYPTO_DISALLOWED\n",
+ log_escape(frame, r->in.account_name),
+ log_escape(frame, r->in.computer_name),
+ r->in.secure_channel_type,
+ (unsigned)*r->in.negotiate_flags,
+ trust_account_in_db ? "real_account[" : "",
+ trust_account_in_db ? trust_account_in_db : "",
+ trust_account_in_db ? "]" : ""));
+ goto return_downgrade;
+ }
+
+ DEBUG(CVE_2022_38023_error_level, (
+ "CVE-2022-38023: "
+ "client_account[%s] computer_name[%s] "
+ "schannel_type[%u] "
+ "client_negotiate_flags[0x%x] "
+ "%s%s%s "
+ "NT_STATUS_DOWNGRADE_DETECTED "
+ "reject_des[%u] reject_md5[%u]\n",
+ log_escape(frame, r->in.account_name),
+ log_escape(frame, r->in.computer_name),
+ r->in.secure_channel_type,
+ (unsigned)*r->in.negotiate_flags,
+ trust_account_in_db ? "real_account[" : "",
+ trust_account_in_db ? trust_account_in_db : "",
+ trust_account_in_db ? "]" : "",
+ reject_des_client,
+ reject_md5_client));
+ if (trust_account_in_db == NULL) {
+ goto return_downgrade;
+ }
+
+ if (reject_md5_client && explicit_md5_opt == NULL) {
+ DEBUG(CVE_2022_38023_error_level, (
+ "CVE-2022-38023: Check if option "
+ "'server reject md5 schannel:%s = no' "
+ "might be needed for a legacy client.\n",
+ trust_account_in_db));
+ }
+ if (reject_des_client && explicit_nt4_opt == NULL) {
+ DEBUG(CVE_2022_38023_error_level, (
+ "CVE-2022-38023: Check if option "
+ "'allow nt4 crypto:%s = yes' "
+ "might be needed for a legacy client.\n",
+ trust_account_in_db));
+ }
+
+return_downgrade:
+ /*
+ * Here we match Windows 2012 and return no flags.
+ */
+ *r->out.negotiate_flags = 0;
+ TALLOC_FREE(frame);
+ return NT_STATUS_DOWNGRADE_DETECTED;
+ }
+
+ /*
+ * This talloc_free is important to prevent re-use of the
+ * challenge. We have to delay it this far due to NETApp
+ * servers per:
+ * https://bugzilla.samba.org/show_bug.cgi?id=11291
+ */
+ TALLOC_FREE(pipe_state);
+
+ /*
+ * At this point we must also cleanup the TDB cache
+ * entry, if we fail the client needs to call
+ * netr_ServerReqChallenge again.
+ *
+ * Note: this handles a non existing record just fine,
+ * the r->in.computer_name might not be the one used
+ * in netr_ServerReqChallenge(), but we are trying to
+ * just tidy up the normal case to prevent re-use.
+ */
+ schannel_delete_challenge(dce_call->conn->dce_ctx->lp_ctx,
+ r->in.computer_name);
+
+ /*
+ * According to Microsoft (see bugid #6099)
+ * Windows 7 looks at the negotiate_flags
+ * returned in this structure *even if the
+ * call fails with access denied!
+ */
+ *r->out.negotiate_flags = negotiate_flags;
+
+ if (!NT_STATUS_IS_OK(orig_status) || trust_account_in_db == NULL) {
+ return orig_status;
+ }
+
+ if (global_reject_md5_client && account_reject_md5_client && explicit_md5_opt) {
+ D_INFO("CVE-2022-38023: Check if option "
+ "'server reject md5 schannel:%s = yes' not needed!?\n",
+ trust_account_in_db);
+ } else if (need_md5 && !account_reject_md5_client && explicit_md5_opt) {
+ D_INFO("CVE-2022-38023: Check if option "
+ "'server reject md5 schannel:%s = no' "
+ "still needed for a legacy client.\n",
+ trust_account_in_db);
+ } else if (need_md5 && explicit_md5_opt == NULL) {
+ DEBUG(CVE_2022_38023_error_level, (
+ "CVE-2022-38023: Check if option "
+ "'server reject md5 schannel:%s = no' "
+ "might be needed for a legacy client.\n",
+ trust_account_in_db));
+ } else if (!account_reject_md5_client && explicit_md5_opt) {
+ DEBUG(CVE_2022_38023_warn_level, (
+ "CVE-2022-38023: Check if option "
+ "'server reject md5 schannel:%s = no' not needed!?\n",
+ trust_account_in_db));
+ }
+
+ if (!global_allow_nt4_crypto && !account_allow_nt4_crypto && explicit_nt4_opt) {
+ D_INFO("CVE-2022-38023: Check if option "
+ "'allow nt4 crypto:%s = no' not needed!?\n",
+ trust_account_in_db);
+ } else if (need_des && account_allow_nt4_crypto && explicit_nt4_opt) {
+ D_INFO("CVE-2022-38023: Check if option "
+ "'allow nt4 crypto:%s = yes' "
+ "still needed for a legacy client.\n",
+ trust_account_in_db);
+ } else if (need_des && explicit_nt4_opt == NULL) {
+ DEBUG(CVE_2022_38023_error_level, (
+ "CVE-2022-38023: Check if option "
+ "'allow nt4 crypto:%s = yes' "
+ "might be needed for a legacy client.\n",
+ trust_account_in_db));
+ } else if (account_allow_nt4_crypto && explicit_nt4_opt) {
+ DEBUG(CVE_2022_38023_warn_level, (
+ "CVE-2022-38023: Check if option "
+ "'allow nt4 crypto:%s = yes' not needed!?\n",
+ trust_account_in_db));
+ }
+
+ return orig_status;
+}
+
+/*
+ * Do the actual processing of a netr_ServerAuthenticate3 message.
+ * called from dcesrv_netr_ServerAuthenticate3, which handles the logging.
+ */
+static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper(
+ struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct netr_ServerAuthenticate3 *r,
+ const char **trust_account_for_search,
+ const char **trust_account_in_db,
+ struct dom_sid **sid)
+{
+ struct netlogon_server_pipe_state *pipe_state = NULL;
+ bool challenge_valid = false;
+ struct netlogon_server_pipe_state challenge;
+ struct netlogon_creds_CredentialState *creds;
+ struct ldb_context *sam_ctx;
+ struct samr_Password *curNtHash = NULL;
+ struct samr_Password *prevNtHash = NULL;
+ uint32_t user_account_control;
+ int num_records;
+ struct ldb_message **msgs;
+ NTSTATUS nt_status;
+ const char *attrs[] = {"unicodePwd", "userAccountControl",
+ "objectSid", "samAccountName", NULL};
+ uint32_t server_flags = 0;
+ uint32_t negotiate_flags = 0;
+
+ ZERO_STRUCTP(r->out.return_credentials);
+ *r->out.negotiate_flags = 0;
+ *r->out.rid = 0;
+
+ pipe_state = dcesrv_iface_state_find_conn(dce_call,
+ NETLOGON_SERVER_PIPE_STATE_MAGIC,
+ struct netlogon_server_pipe_state);
+ if (pipe_state != NULL) {
+ /*
+ * If we had a challenge remembered on the connection
+ * consider this for usage. This can't be cleanup
+ * by other clients.
+ *
+ * This is the default code path for typical clients
+ * which call netr_ServerReqChallenge() and
+ * netr_ServerAuthenticate3() on the same dcerpc connection.
+ */
+ challenge = *pipe_state;
+
+ challenge_valid = true;
+
+ } else {
+ NTSTATUS ntstatus;
+
+ /*
+ * Fallback and try to get the challenge from
+ * the global cache.
+ *
+ * If too many clients are using this code path,
+ * they may destroy their cache entries as the
+ * TDB has a fixed size limited via a lossy hash
+ *
+ * The TDB used is the schannel store, which is
+ * initialised at startup.
+ *
+ * NOTE: The challenge is deleted from the DB as soon as it is
+ * fetched, to prevent reuse.
+ *
+ */
+
+ ntstatus = schannel_get_challenge(dce_call->conn->dce_ctx->lp_ctx,
+ &challenge.client_challenge,
+ &challenge.server_challenge,
+ r->in.computer_name);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ ZERO_STRUCT(challenge);
+ } else {
+ challenge_valid = true;
+ }
+ }
+
+ server_flags = NETLOGON_NEG_ACCOUNT_LOCKOUT |
+ NETLOGON_NEG_PERSISTENT_SAMREPL |
+ NETLOGON_NEG_ARCFOUR |
+ NETLOGON_NEG_PROMOTION_COUNT |
+ NETLOGON_NEG_CHANGELOG_BDC |
+ NETLOGON_NEG_FULL_SYNC_REPL |
+ NETLOGON_NEG_MULTIPLE_SIDS |
+ NETLOGON_NEG_REDO |
+ NETLOGON_NEG_PASSWORD_CHANGE_REFUSAL |
+ NETLOGON_NEG_SEND_PASSWORD_INFO_PDC |
+ NETLOGON_NEG_GENERIC_PASSTHROUGH |
+ NETLOGON_NEG_CONCURRENT_RPC |
+ NETLOGON_NEG_AVOID_ACCOUNT_DB_REPL |
+ NETLOGON_NEG_AVOID_SECURITYAUTH_DB_REPL |
+ NETLOGON_NEG_STRONG_KEYS |
+ NETLOGON_NEG_TRANSITIVE_TRUSTS |
+ NETLOGON_NEG_DNS_DOMAIN_TRUSTS |
+ NETLOGON_NEG_PASSWORD_SET2 |
+ NETLOGON_NEG_GETDOMAININFO |
+ NETLOGON_NEG_CROSS_FOREST_TRUSTS |
+ NETLOGON_NEG_NEUTRALIZE_NT4_EMULATION |
+ NETLOGON_NEG_RODC_PASSTHROUGH |
+ NETLOGON_NEG_SUPPORTS_AES |
+ NETLOGON_NEG_AUTHENTICATED_RPC_LSASS |
+ NETLOGON_NEG_AUTHENTICATED_RPC;
+
+ /*
+ * If weak cryto is disabled, do not announce that we support RC4.
+ */
+ if (lpcfg_weak_crypto(dce_call->conn->dce_ctx->lp_ctx) ==
+ SAMBA_WEAK_CRYPTO_DISALLOWED) {
+ server_flags &= ~NETLOGON_NEG_ARCFOUR;
+ }
+
+ negotiate_flags = *r->in.negotiate_flags & server_flags;
+
+ switch (r->in.secure_channel_type) {
+ case SEC_CHAN_WKSTA:
+ case SEC_CHAN_DNS_DOMAIN:
+ case SEC_CHAN_DOMAIN:
+ case SEC_CHAN_BDC:
+ case SEC_CHAN_RODC:
+ break;
+ case SEC_CHAN_NULL:
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_INVALID_PARAMETER);
+ default:
+ DEBUG(1, ("Client asked for an invalid secure channel type: %d\n",
+ r->in.secure_channel_type));
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_INVALID_PARAMETER);
+ }
+
+ sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_INVALID_SYSTEM_SERVICE);
+ }
+
+ if (r->in.secure_channel_type == SEC_CHAN_DOMAIN ||
+ r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN)
+ {
+ struct ldb_message *tdo_msg = NULL;
+ const char * const tdo_attrs[] = {
+ "trustAuthIncoming",
+ "trustAttributes",
+ "flatName",
+ NULL
+ };
+ char *encoded_name = NULL;
+ size_t len;
+ const char *flatname = NULL;
+ char trailer = '$';
+ bool require_trailer = true;
+ const char *netbios = NULL;
+ const char *dns = NULL;
+
+ if (r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
+ trailer = '.';
+ require_trailer = false;
+ }
+
+ encoded_name = ldb_binary_encode_string(mem_ctx,
+ r->in.account_name);
+ if (encoded_name == NULL) {
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_NO_MEMORY);
+ }
+
+ len = strlen(encoded_name);
+ if (len < 2) {
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_NO_TRUST_SAM_ACCOUNT);
+ }
+
+ if (require_trailer && encoded_name[len - 1] != trailer) {
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_NO_TRUST_SAM_ACCOUNT);
+ }
+ encoded_name[len - 1] = '\0';
+
+ if (r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
+ dns = encoded_name;
+ } else {
+ netbios = encoded_name;
+ }
+
+ nt_status = dsdb_trust_search_tdo(sam_ctx,
+ netbios, dns,
+ tdo_attrs, mem_ctx, &tdo_msg);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ DEBUG(2, ("Client asked for a trusted domain secure channel, "
+ "but there's no tdo for [%s] => [%s] \n",
+ log_escape(mem_ctx, r->in.account_name),
+ encoded_name));
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_NO_TRUST_SAM_ACCOUNT);
+ }
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ nt_status);
+ }
+
+ nt_status = dsdb_trust_get_incoming_passwords(tdo_msg, mem_ctx,
+ &curNtHash,
+ &prevNtHash);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED)) {
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_NO_TRUST_SAM_ACCOUNT);
+ }
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ nt_status);
+ }
+
+ flatname = ldb_msg_find_attr_as_string(tdo_msg, "flatName", NULL);
+ if (flatname == NULL) {
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_NO_TRUST_SAM_ACCOUNT);
+ }
+
+ *trust_account_for_search = talloc_asprintf(mem_ctx, "%s$", flatname);
+ if (*trust_account_for_search == NULL) {
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_NO_MEMORY);
+ }
+ } else {
+ *trust_account_for_search = r->in.account_name;
+ }
+
+ /* pull the user attributes */
+ num_records = gendb_search(sam_ctx, mem_ctx, NULL, &msgs, attrs,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ ldb_binary_encode_string(mem_ctx,
+ *trust_account_for_search));
+
+ if (num_records == 0) {
+ DEBUG(3,("Couldn't find user [%s] in samdb.\n",
+ log_escape(mem_ctx, r->in.account_name)));
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_NO_TRUST_SAM_ACCOUNT);
+ }
+
+ if (num_records > 1) {
+ DEBUG(0,("Found %d records matching user [%s]\n",
+ num_records,
+ log_escape(mem_ctx, r->in.account_name)));
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_INTERNAL_DB_CORRUPTION);
+ }
+
+ *trust_account_in_db = ldb_msg_find_attr_as_string(msgs[0],
+ "samAccountName",
+ NULL);
+ if (*trust_account_in_db == NULL) {
+ DEBUG(0,("No samAccountName returned in record matching user [%s]\n",
+ r->in.account_name));
+ return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ NULL, /* trust_account_in_db */
+ NT_STATUS_INTERNAL_DB_CORRUPTION);
+ }
+
+ nt_status = dcesrv_netr_ServerAuthenticate3_check_downgrade(
+ dce_call, r, pipe_state, negotiate_flags,
+ *trust_account_in_db,
+ NT_STATUS_OK);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ user_account_control = ldb_msg_find_attr_as_uint(msgs[0], "userAccountControl", 0);
+
+ if (user_account_control & UF_ACCOUNTDISABLE) {
+ DEBUG(1, ("Account [%s] is disabled\n",
+ log_escape(mem_ctx, r->in.account_name)));
+ return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+
+ if (r->in.secure_channel_type == SEC_CHAN_WKSTA) {
+ if (!(user_account_control & UF_WORKSTATION_TRUST_ACCOUNT)) {
+ DEBUG(1, ("Client asked for a workstation secure channel, but is not a workstation (member server) acb flags: 0x%x\n", user_account_control));
+ return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+ } else if (r->in.secure_channel_type == SEC_CHAN_DOMAIN ||
+ r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
+ if (!(user_account_control & UF_INTERDOMAIN_TRUST_ACCOUNT)) {
+ DEBUG(1, ("Client asked for a trusted domain secure channel, but is not a trusted domain: acb flags: 0x%x\n", user_account_control));
+
+ return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+ } else if (r->in.secure_channel_type == SEC_CHAN_BDC) {
+ if (!(user_account_control & UF_SERVER_TRUST_ACCOUNT)) {
+ DEBUG(1, ("Client asked for a server secure channel, but is not a server (domain controller): acb flags: 0x%x\n", user_account_control));
+ return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+ } else if (r->in.secure_channel_type == SEC_CHAN_RODC) {
+ if (!(user_account_control & UF_PARTIAL_SECRETS_ACCOUNT)) {
+ DEBUG(1, ("Client asked for a RODC secure channel, but is not a RODC: acb flags: 0x%x\n", user_account_control));
+ return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+ } else {
+ /* we should never reach this */
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!(user_account_control & UF_INTERDOMAIN_TRUST_ACCOUNT)) {
+ nt_status = samdb_result_passwords_no_lockout(mem_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ msgs[0], &curNtHash);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ if (curNtHash == NULL) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!challenge_valid) {
+ DEBUG(1, ("No challenge requested by client [%s/%s], "
+ "cannot authenticate\n",
+ log_escape(mem_ctx, r->in.computer_name),
+ log_escape(mem_ctx, r->in.account_name)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ creds = netlogon_creds_server_init(mem_ctx,
+ r->in.account_name,
+ r->in.computer_name,
+ r->in.secure_channel_type,
+ &challenge.client_challenge,
+ &challenge.server_challenge,
+ curNtHash,
+ r->in.credentials,
+ r->out.return_credentials,
+ negotiate_flags);
+ if (creds == NULL && prevNtHash != NULL) {
+ /*
+ * We fallback to the previous password for domain trusts.
+ *
+ * Note that lpcfg_old_password_allowed_period() doesn't
+ * apply here.
+ */
+ creds = netlogon_creds_server_init(mem_ctx,
+ r->in.account_name,
+ r->in.computer_name,
+ r->in.secure_channel_type,
+ &challenge.client_challenge,
+ &challenge.server_challenge,
+ prevNtHash,
+ r->in.credentials,
+ r->out.return_credentials,
+ negotiate_flags);
+ }
+
+ if (creds == NULL) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ creds->sid = samdb_result_dom_sid(creds, msgs[0], "objectSid");
+ *sid = talloc_memdup(mem_ctx, creds->sid, sizeof(struct dom_sid));
+
+ nt_status = schannel_save_creds_state(mem_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ creds);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ ZERO_STRUCTP(r->out.return_credentials);
+ return nt_status;
+ }
+
+ *r->out.rid = samdb_result_rid_from_sid(mem_ctx, msgs[0],
+ "objectSid", 0);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * Log a netr_ServerAuthenticate3 request, and then invoke
+ * dcesrv_netr_ServerAuthenticate3_helper to perform the actual processing
+ */
+static NTSTATUS dcesrv_netr_ServerAuthenticate3(
+ struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct netr_ServerAuthenticate3 *r)
+{
+ NTSTATUS status;
+ struct dom_sid *sid = NULL;
+ const char *trust_account_for_search = NULL;
+ const char *trust_account_in_db = NULL;
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+ struct auth_usersupplied_info ui = {
+ .local_host = dce_call->conn->local_address,
+ .remote_host = dce_call->conn->remote_address,
+ .client = {
+ .account_name = r->in.account_name,
+ .domain_name = lpcfg_workgroup(dce_call->conn->dce_ctx->lp_ctx),
+ },
+ .service_description = "NETLOGON",
+ .auth_description = "ServerAuthenticate",
+ .netlogon_trust_account = {
+ .computer_name = r->in.computer_name,
+ .negotiate_flags = *r->in.negotiate_flags,
+ .secure_channel_type = r->in.secure_channel_type,
+ },
+ };
+
+ status = dcesrv_netr_ServerAuthenticate3_helper(dce_call,
+ mem_ctx,
+ r,
+ &trust_account_for_search,
+ &trust_account_in_db,
+ &sid);
+ ui.netlogon_trust_account.sid = sid;
+ ui.netlogon_trust_account.account_name = trust_account_in_db;
+ ui.mapped.account_name = trust_account_for_search;
+ log_authentication_event(
+ imsg_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ NULL,
+ &ui,
+ status,
+ lpcfg_workgroup(dce_call->conn->dce_ctx->lp_ctx),
+ trust_account_in_db,
+ sid);
+
+ return status;
+}
+static NTSTATUS dcesrv_netr_ServerAuthenticate(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerAuthenticate *r)
+{
+ struct netr_ServerAuthenticate3 a;
+ uint32_t rid;
+ /* TODO:
+ * negotiate_flags is used as an [in] parameter
+ * so it need to be initialised.
+ *
+ * (I think ... = 0; seems wrong here --metze)
+ */
+ uint32_t negotiate_flags_in = 0;
+ uint32_t negotiate_flags_out = 0;
+
+ a.in.server_name = r->in.server_name;
+ a.in.account_name = r->in.account_name;
+ a.in.secure_channel_type = r->in.secure_channel_type;
+ a.in.computer_name = r->in.computer_name;
+ a.in.credentials = r->in.credentials;
+ a.in.negotiate_flags = &negotiate_flags_in;
+
+ a.out.return_credentials = r->out.return_credentials;
+ a.out.rid = &rid;
+ a.out.negotiate_flags = &negotiate_flags_out;
+
+ return dcesrv_netr_ServerAuthenticate3(dce_call, mem_ctx, &a);
+}
+
+static NTSTATUS dcesrv_netr_ServerAuthenticate2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerAuthenticate2 *r)
+{
+ struct netr_ServerAuthenticate3 r3;
+ uint32_t rid = 0;
+
+ r3.in.server_name = r->in.server_name;
+ r3.in.account_name = r->in.account_name;
+ r3.in.secure_channel_type = r->in.secure_channel_type;
+ r3.in.computer_name = r->in.computer_name;
+ r3.in.credentials = r->in.credentials;
+ r3.out.return_credentials = r->out.return_credentials;
+ r3.in.negotiate_flags = r->in.negotiate_flags;
+ r3.out.negotiate_flags = r->out.negotiate_flags;
+ r3.out.rid = &rid;
+
+ return dcesrv_netr_ServerAuthenticate3(dce_call, mem_ctx, &r3);
+}
+
+/*
+ Change the machine account password for the currently connected
+ client. Supplies only the NT#.
+*/
+
+static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerPasswordSet *r)
+{
+ struct netlogon_creds_CredentialState *creds;
+ struct ldb_context *sam_ctx;
+ NTSTATUS nt_status;
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call,
+ mem_ctx,
+ r->in.computer_name,
+ r->in.credential, r->out.return_authenticator,
+ &creds);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ nt_status = netlogon_creds_des_decrypt(creds, r->in.new_password);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ /* Using the sid for the account as the key, set the password */
+ nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
+ creds->sid,
+ NULL, /* Don't have version */
+ NULL, /* Don't have plaintext */
+ r->in.new_password,
+ DSDB_PASSWORD_CHECKED_AND_CORRECT, /* Password change */
+ NULL, NULL);
+ return nt_status;
+}
+
+/*
+ Change the machine account password for the currently connected
+ client. Supplies new plaintext.
+*/
+static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerPasswordSet2 *r)
+{
+ struct netlogon_creds_CredentialState *creds;
+ struct ldb_context *sam_ctx;
+ struct NL_PASSWORD_VERSION version = {};
+ const uint32_t *new_version = NULL;
+ NTSTATUS nt_status;
+ DATA_BLOB new_password = data_blob_null;
+ size_t confounder_len;
+ DATA_BLOB dec_blob = data_blob_null;
+ DATA_BLOB enc_blob = data_blob_null;
+ struct samr_CryptPassword password_buf;
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call,
+ mem_ctx,
+ r->in.computer_name,
+ r->in.credential, r->out.return_authenticator,
+ &creds);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ memcpy(password_buf.data, r->in.new_password->data, 512);
+ SIVAL(password_buf.data, 512, r->in.new_password->length);
+
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ nt_status = netlogon_creds_aes_decrypt(creds,
+ password_buf.data,
+ 516);
+ } else {
+ nt_status = netlogon_creds_arcfour_crypt(creds,
+ password_buf.data,
+ 516);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ switch (creds->secure_channel_type) {
+ case SEC_CHAN_DOMAIN:
+ case SEC_CHAN_DNS_DOMAIN: {
+ uint32_t len = IVAL(password_buf.data, 512);
+ if (len <= 500) {
+ uint32_t ofs = 500 - len;
+ uint8_t *p;
+
+ p = password_buf.data + ofs;
+
+ version.ReservedField = IVAL(p, 0);
+ version.PasswordVersionNumber = IVAL(p, 4);
+ version.PasswordVersionPresent = IVAL(p, 8);
+
+ if (version.PasswordVersionPresent == NETLOGON_PASSWORD_VERSION_NUMBER_PRESENT) {
+ new_version = &version.PasswordVersionNumber;
+ }
+ }}
+ break;
+ default:
+ break;
+ }
+
+ if (!extract_pw_from_buffer(mem_ctx, password_buf.data, &new_password)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * Make sure the length field was encrypted,
+ * otherwise we are under attack.
+ */
+ if (new_password.length == r->in.new_password->length) {
+ DBG_WARNING("Length[%zu] field not encrypted\n",
+ new_password.length);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * We don't allow empty passwords for machine accounts.
+ */
+ if (new_password.length < 2) {
+ DBG_WARNING("Empty password Length[%zu]\n",
+ new_password.length);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * Make sure the confounder part of CryptPassword
+ * buffer was encrypted, otherwise we are under attack.
+ */
+ confounder_len = 512 - new_password.length;
+ enc_blob = data_blob_const(r->in.new_password->data, confounder_len);
+ dec_blob = data_blob_const(password_buf.data, confounder_len);
+ if (confounder_len > 0 && data_blob_equal_const_time(&dec_blob, &enc_blob)) {
+ DBG_WARNING("Confounder buffer not encrypted Length[%zu]\n",
+ confounder_len);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * Check that the password part was actually encrypted,
+ * otherwise we are under attack.
+ */
+ enc_blob = data_blob_const(r->in.new_password->data + confounder_len,
+ new_password.length);
+ dec_blob = data_blob_const(password_buf.data + confounder_len,
+ new_password.length);
+ if (data_blob_equal_const_time(&dec_blob, &enc_blob)) {
+ DBG_WARNING("Password buffer not encrypted Length[%zu]\n",
+ new_password.length);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * don't allow zero buffers
+ */
+ if (all_zero(new_password.data, new_password.length)) {
+ DBG_WARNING("Password zero buffer Length[%zu]\n",
+ new_password.length);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* Using the sid for the account as the key, set the password */
+ nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
+ creds->sid,
+ new_version,
+ &new_password, /* we have plaintext */
+ NULL,
+ DSDB_PASSWORD_CHECKED_AND_CORRECT, /* Password change */
+ NULL, NULL);
+ return nt_status;
+}
+
+
+/*
+ netr_LogonUasLogon
+*/
+static WERROR dcesrv_netr_LogonUasLogon(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonUasLogon *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonUasLogoff
+*/
+static WERROR dcesrv_netr_LogonUasLogoff(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonUasLogoff *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+static NTSTATUS dcesrv_netr_LogonSamLogon_check(struct dcesrv_call_state *dce_call,
+ const struct netr_LogonSamLogonEx *r)
+{
+ enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
+
+ switch (r->in.logon_level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceTransitiveInformation:
+ if (r->in.logon->password == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (r->in.validation_level) {
+ case NetlogonValidationSamInfo: /* 2 */
+ case NetlogonValidationSamInfo2: /* 3 */
+ case NetlogonValidationSamInfo4: /* 6 */
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ break;
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+ if (r->in.logon->network == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (r->in.validation_level) {
+ case NetlogonValidationSamInfo: /* 2 */
+ case NetlogonValidationSamInfo2: /* 3 */
+ case NetlogonValidationSamInfo4: /* 6 */
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ break;
+
+ case NetlogonGenericInformation:
+ if (r->in.logon->generic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (r->in.validation_level) {
+ /* TODO: case NetlogonValidationGenericInfo: 4 */
+ case NetlogonValidationGenericInfo2: /* 5 */
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ dcesrv_call_auth_info(dce_call, NULL, &auth_level);
+
+ switch (r->in.validation_level) {
+ case NetlogonValidationSamInfo4: /* 6 */
+ if (auth_level < DCERPC_AUTH_LEVEL_PRIVACY) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct dcesrv_netr_LogonSamLogon_base_state {
+ struct dcesrv_call_state *dce_call;
+
+ TALLOC_CTX *mem_ctx;
+
+ struct netlogon_creds_CredentialState *creds;
+
+ struct netr_LogonSamLogonEx r;
+
+ uint32_t _ignored_flags;
+
+ struct {
+ struct netr_LogonSamLogon *lsl;
+ struct netr_LogonSamLogonWithFlags *lslwf;
+ struct netr_LogonSamLogonEx *lslex;
+ } _r;
+
+ struct kdc_check_generic_kerberos kr;
+};
+
+static void dcesrv_netr_LogonSamLogon_base_auth_done(struct tevent_req *subreq);
+static void dcesrv_netr_LogonSamLogon_base_krb5_done(struct tevent_req *subreq);
+static void dcesrv_netr_LogonSamLogon_base_reply(
+ struct dcesrv_netr_LogonSamLogon_base_state *state);
+
+/*
+ netr_LogonSamLogon_base
+
+ This version of the function allows other wrappers to say 'do not check the credentials'
+
+ We can't do the traditional 'wrapping' format completely, as this
+ function must only run under schannel
+*/
+static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamLogon_base_state *state)
+{
+ struct dcesrv_call_state *dce_call = state->dce_call;
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+ TALLOC_CTX *mem_ctx = state->mem_ctx;
+ struct netr_LogonSamLogonEx *r = &state->r;
+ struct netlogon_creds_CredentialState *creds = state->creds;
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ const char *workgroup = lpcfg_workgroup(lp_ctx);
+ struct auth4_context *auth_context = NULL;
+ struct auth_usersupplied_info *user_info = NULL;
+ NTSTATUS nt_status;
+ struct tevent_req *subreq = NULL;
+ enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE;
+ enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
+
+ dcesrv_call_auth_info(dce_call, &auth_type, &auth_level);
+
+ switch (dce_call->pkt.u.request.opnum) {
+ case NDR_NETR_LOGONSAMLOGON:
+ case NDR_NETR_LOGONSAMLOGONWITHFLAGS:
+ /*
+ * These already called dcesrv_netr_check_schannel()
+ * via dcesrv_netr_creds_server_step_check()
+ */
+ break;
+ case NDR_NETR_LOGONSAMLOGONEX:
+ default:
+ if (auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ nt_status = dcesrv_netr_check_schannel(dce_call,
+ creds,
+ auth_type,
+ auth_level,
+ dce_call->pkt.u.request.opnum);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ break;
+ }
+
+ *r->out.authoritative = 1;
+
+ if (*r->in.flags & NETLOGON_SAMLOGON_FLAG_PASS_TO_FOREST_ROOT) {
+ /*
+ * Currently we're always the forest root ourself.
+ */
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (*r->in.flags & NETLOGON_SAMLOGON_FLAG_PASS_CROSS_FOREST_HOP) {
+ /*
+ * Currently we don't support trusts correctly yet.
+ */
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ user_info = talloc_zero(mem_ctx, struct auth_usersupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(user_info);
+
+ user_info->service_description = "SamLogon";
+
+ nt_status = netlogon_creds_decrypt_samlogon_logon(creds,
+ r->in.logon_level,
+ r->in.logon);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ switch (r->in.logon_level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceTransitiveInformation:
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+
+ nt_status = auth_context_create_for_netlogon(mem_ctx,
+ dce_call->event_ctx,
+ imsg_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ &auth_context);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ user_info->remote_host = dce_call->conn->remote_address;
+ user_info->local_host = dce_call->conn->local_address;
+
+ user_info->netlogon_trust_account.secure_channel_type
+ = creds->secure_channel_type;
+ user_info->netlogon_trust_account.negotiate_flags
+ = creds->negotiate_flags;
+
+ /*
+ * These two can be unrelated when the account is
+ * actually that of a trusted domain, so we want to
+ * know which DC in that trusted domain contacted
+ * us
+ */
+ user_info->netlogon_trust_account.computer_name
+ = creds->computer_name;
+ user_info->netlogon_trust_account.account_name
+ = creds->account_name;
+ user_info->netlogon_trust_account.sid
+ = creds->sid;
+
+ break;
+ default:
+ /* We do not need to set up the user_info in this case */
+ break;
+ }
+
+ switch (r->in.logon_level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceTransitiveInformation:
+ user_info->auth_description = "interactive";
+
+ user_info->logon_parameters
+ = r->in.logon->password->identity_info.parameter_control;
+ user_info->client.account_name
+ = r->in.logon->password->identity_info.account_name.string;
+ user_info->client.domain_name
+ = r->in.logon->password->identity_info.domain_name.string;
+ user_info->workstation_name
+ = r->in.logon->password->identity_info.workstation.string;
+ user_info->flags |= USER_INFO_INTERACTIVE_LOGON;
+ user_info->password_state = AUTH_PASSWORD_HASH;
+
+ user_info->password.hash.lanman = talloc(user_info, struct samr_Password);
+ NT_STATUS_HAVE_NO_MEMORY(user_info->password.hash.lanman);
+ *user_info->password.hash.lanman = r->in.logon->password->lmpassword;
+
+ user_info->password.hash.nt = talloc(user_info, struct samr_Password);
+ NT_STATUS_HAVE_NO_MEMORY(user_info->password.hash.nt);
+ *user_info->password.hash.nt = r->in.logon->password->ntpassword;
+
+ user_info->logon_id
+ = r->in.logon->password->identity_info.logon_id;
+
+ break;
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+ user_info->auth_description = "network";
+
+ nt_status = auth_context_set_challenge(
+ auth_context,
+ r->in.logon->network->challenge,
+ "netr_LogonSamLogonWithFlags");
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ user_info->logon_parameters
+ = r->in.logon->network->identity_info.parameter_control;
+ user_info->client.account_name
+ = r->in.logon->network->identity_info.account_name.string;
+ user_info->client.domain_name
+ = r->in.logon->network->identity_info.domain_name.string;
+ user_info->workstation_name
+ = r->in.logon->network->identity_info.workstation.string;
+
+ user_info->password_state = AUTH_PASSWORD_RESPONSE;
+ user_info->password.response.lanman = data_blob_talloc(mem_ctx, r->in.logon->network->lm.data, r->in.logon->network->lm.length);
+ user_info->password.response.nt = data_blob_talloc(mem_ctx, r->in.logon->network->nt.data, r->in.logon->network->nt.length);
+
+ user_info->logon_id
+ = r->in.logon->network->identity_info.logon_id;
+
+ nt_status = NTLMv2_RESPONSE_verify_netlogon_creds(
+ user_info->client.account_name,
+ user_info->client.domain_name,
+ user_info->password.response.nt,
+ creds, workgroup);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ break;
+
+
+ case NetlogonGenericInformation:
+ {
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ /* OK */
+ } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ /* OK */
+ } else {
+ /* Using DES to verify kerberos tickets makes no sense */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strcmp(r->in.logon->generic->package_name.string, "Kerberos") == 0) {
+ struct dcerpc_binding_handle *irpc_handle;
+ struct netr_GenericInfo2 *generic = talloc_zero(mem_ctx, struct netr_GenericInfo2);
+ NT_STATUS_HAVE_NO_MEMORY(generic);
+
+ r->out.validation->generic = generic;
+
+ user_info->logon_id
+ = r->in.logon->generic->identity_info.logon_id;
+
+ irpc_handle = irpc_binding_handle_by_name(mem_ctx,
+ imsg_ctx,
+ "kdc_server",
+ &ndr_table_irpc);
+ if (irpc_handle == NULL) {
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ state->kr.in.generic_request =
+ data_blob_const(r->in.logon->generic->data,
+ r->in.logon->generic->length);
+
+ /*
+ * 60 seconds should be enough
+ */
+ dcerpc_binding_handle_set_timeout(irpc_handle, 60);
+ subreq = dcerpc_kdc_check_generic_kerberos_r_send(state,
+ state->dce_call->event_ctx,
+ irpc_handle, &state->kr);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ tevent_req_set_callback(subreq,
+ dcesrv_netr_LogonSamLogon_base_krb5_done,
+ state);
+ return NT_STATUS_OK;
+ }
+
+ /* Until we get an implemetnation of these other packages */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ subreq = auth_check_password_send(state, state->dce_call->event_ctx,
+ auth_context, user_info);
+ state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ tevent_req_set_callback(subreq,
+ dcesrv_netr_LogonSamLogon_base_auth_done,
+ state);
+ return NT_STATUS_OK;
+}
+
+static void dcesrv_netr_LogonSamLogon_base_auth_done(struct tevent_req *subreq)
+{
+ struct dcesrv_netr_LogonSamLogon_base_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcesrv_netr_LogonSamLogon_base_state);
+ TALLOC_CTX *mem_ctx = state->mem_ctx;
+ struct netr_LogonSamLogonEx *r = &state->r;
+ struct auth_user_info_dc *user_info_dc = NULL;
+ struct netr_SamInfo2 *sam2 = NULL;
+ struct netr_SamInfo3 *sam3 = NULL;
+ struct netr_SamInfo6 *sam6 = NULL;
+ NTSTATUS nt_status;
+
+ nt_status = auth_check_password_recv(subreq, mem_ctx,
+ &user_info_dc,
+ r->out.authoritative);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ r->out.result = nt_status;
+ dcesrv_netr_LogonSamLogon_base_reply(state);
+ return;
+ }
+
+ switch (r->in.validation_level) {
+ case 2:
+ nt_status = auth_convert_user_info_dc_saminfo2(mem_ctx,
+ user_info_dc,
+ &sam2);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ r->out.result = nt_status;
+ dcesrv_netr_LogonSamLogon_base_reply(state);
+ return;
+ }
+
+ r->out.validation->sam2 = sam2;
+ break;
+
+ case 3:
+ nt_status = auth_convert_user_info_dc_saminfo3(mem_ctx,
+ user_info_dc,
+ &sam3);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ r->out.result = nt_status;
+ dcesrv_netr_LogonSamLogon_base_reply(state);
+ return;
+ }
+
+ r->out.validation->sam3 = sam3;
+ break;
+
+ case 6:
+ nt_status = auth_convert_user_info_dc_saminfo6(mem_ctx,
+ user_info_dc,
+ &sam6);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ r->out.result = nt_status;
+ dcesrv_netr_LogonSamLogon_base_reply(state);
+ return;
+ }
+
+ r->out.validation->sam6 = sam6;
+ break;
+
+ default:
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ r->out.result = NT_STATUS_INVALID_INFO_CLASS;
+ dcesrv_netr_LogonSamLogon_base_reply(state);
+ return;
+ }
+ }
+
+ /* TODO: Describe and deal with these flags */
+ *r->out.flags = 0;
+
+ r->out.result = NT_STATUS_OK;
+
+ dcesrv_netr_LogonSamLogon_base_reply(state);
+}
+
+static void dcesrv_netr_LogonSamLogon_base_krb5_done(struct tevent_req *subreq)
+{
+ struct dcesrv_netr_LogonSamLogon_base_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcesrv_netr_LogonSamLogon_base_state);
+ TALLOC_CTX *mem_ctx = state->mem_ctx;
+ struct netr_LogonSamLogonEx *r = &state->r;
+ struct netr_GenericInfo2 *generic = NULL;
+ NTSTATUS status;
+
+ status = dcerpc_kdc_check_generic_kerberos_r_recv(subreq, mem_ctx);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.result = status;
+ dcesrv_netr_LogonSamLogon_base_reply(state);
+ return;
+ }
+
+ generic = r->out.validation->generic;
+ generic->length = state->kr.out.generic_reply.length;
+ generic->data = state->kr.out.generic_reply.data;
+
+ /* TODO: Describe and deal with these flags */
+ *r->out.flags = 0;
+
+ r->out.result = NT_STATUS_OK;
+
+ dcesrv_netr_LogonSamLogon_base_reply(state);
+}
+
+static void dcesrv_netr_LogonSamLogon_base_reply(
+ struct dcesrv_netr_LogonSamLogon_base_state *state)
+{
+ struct netr_LogonSamLogonEx *r = &state->r;
+ NTSTATUS status;
+
+ if (NT_STATUS_IS_OK(r->out.result)) {
+ status = netlogon_creds_encrypt_samlogon_validation(state->creds,
+ r->in.validation_level,
+ r->out.validation);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("netlogon_creds_encrypt_samlogon_validation() "
+ "failed - %s\n",
+ nt_errstr(status));
+ }
+ }
+
+ if (state->_r.lslex != NULL) {
+ struct netr_LogonSamLogonEx *_r = state->_r.lslex;
+ _r->out.result = r->out.result;
+ } else if (state->_r.lslwf != NULL) {
+ struct netr_LogonSamLogonWithFlags *_r = state->_r.lslwf;
+ _r->out.result = r->out.result;
+ } else if (state->_r.lsl != NULL) {
+ struct netr_LogonSamLogon *_r = state->_r.lsl;
+ _r->out.result = r->out.result;
+ }
+
+ status = dcesrv_reply(state->dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("dcesrv_reply() failed - %s\n",
+ nt_errstr(status));
+ }
+}
+
+static NTSTATUS dcesrv_netr_LogonSamLogonEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogonEx *r)
+{
+ struct dcesrv_netr_LogonSamLogon_base_state *state;
+ NTSTATUS nt_status;
+
+ *r->out.authoritative = 1;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_netr_LogonSamLogon_base_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->r.in.server_name = r->in.server_name;
+ state->r.in.computer_name = r->in.computer_name;
+ state->r.in.logon_level = r->in.logon_level;
+ state->r.in.logon = r->in.logon;
+ state->r.in.validation_level = r->in.validation_level;
+ state->r.in.flags = r->in.flags;
+ state->r.out.validation = r->out.validation;
+ state->r.out.authoritative = r->out.authoritative;
+ state->r.out.flags = r->out.flags;
+
+ state->_r.lslex = r;
+
+ nt_status = dcesrv_netr_LogonSamLogon_check(dce_call, &state->r);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ nt_status = schannel_get_creds_state(mem_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ r->in.computer_name, &state->creds);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ nt_status = dcesrv_netr_LogonSamLogon_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return nt_status;
+ }
+
+ return nt_status;
+}
+
+/*
+ netr_LogonSamLogonWithFlags
+
+*/
+static NTSTATUS dcesrv_netr_LogonSamLogonWithFlags(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogonWithFlags *r)
+{
+ struct dcesrv_netr_LogonSamLogon_base_state *state;
+ NTSTATUS nt_status;
+
+ *r->out.authoritative = 1;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_netr_LogonSamLogon_base_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->r.in.server_name = r->in.server_name;
+ state->r.in.computer_name = r->in.computer_name;
+ state->r.in.logon_level = r->in.logon_level;
+ state->r.in.logon = r->in.logon;
+ state->r.in.validation_level = r->in.validation_level;
+ state->r.in.flags = r->in.flags;
+ state->r.out.validation = r->out.validation;
+ state->r.out.authoritative = r->out.authoritative;
+ state->r.out.flags = r->out.flags;
+
+ state->_r.lslwf = r;
+
+ nt_status = dcesrv_netr_LogonSamLogon_check(dce_call, &state->r);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ r->out.return_authenticator = talloc_zero(mem_ctx,
+ struct netr_Authenticator);
+ if (r->out.return_authenticator == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call,
+ mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &state->creds);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ nt_status = dcesrv_netr_LogonSamLogon_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return nt_status;
+ }
+
+ return nt_status;
+}
+
+/*
+ netr_LogonSamLogon
+*/
+static NTSTATUS dcesrv_netr_LogonSamLogon(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogon *r)
+{
+ struct dcesrv_netr_LogonSamLogon_base_state *state;
+ NTSTATUS nt_status;
+
+ *r->out.authoritative = 1;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_netr_LogonSamLogon_base_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->r.in.server_name = r->in.server_name;
+ state->r.in.computer_name = r->in.computer_name;
+ state->r.in.logon_level = r->in.logon_level;
+ state->r.in.logon = r->in.logon;
+ state->r.in.validation_level = r->in.validation_level;
+ state->r.in.flags = &state->_ignored_flags;
+ state->r.out.validation = r->out.validation;
+ state->r.out.authoritative = r->out.authoritative;
+ state->r.out.flags = &state->_ignored_flags;
+
+ state->_r.lsl = r;
+
+ nt_status = dcesrv_netr_LogonSamLogon_check(dce_call, &state->r);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ r->out.return_authenticator = talloc_zero(mem_ctx,
+ struct netr_Authenticator);
+ if (r->out.return_authenticator == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call,
+ mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &state->creds);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ nt_status = dcesrv_netr_LogonSamLogon_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return nt_status;
+ }
+
+ return nt_status;
+}
+
+
+/*
+ netr_LogonSamLogoff
+*/
+static NTSTATUS dcesrv_netr_LogonSamLogoff(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogoff *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+/*
+ netr_DatabaseDeltas
+*/
+static NTSTATUS dcesrv_netr_DatabaseDeltas(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DatabaseDeltas *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DatabaseSync2
+*/
+static NTSTATUS dcesrv_netr_DatabaseSync2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DatabaseSync2 *r)
+{
+ /* win2k3 native mode returns "NOT IMPLEMENTED" for this call */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ netr_DatabaseSync
+*/
+static NTSTATUS dcesrv_netr_DatabaseSync(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DatabaseSync *r)
+{
+ struct netr_DatabaseSync2 r2;
+ NTSTATUS status;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.logon_server = r->in.logon_server;
+ r2.in.computername = r->in.computername;
+ r2.in.credential = r->in.credential;
+ r2.in.database_id = r->in.database_id;
+ r2.in.restart_state = SYNCSTATE_NORMAL_STATE;
+ r2.in.sync_context = r->in.sync_context;
+ r2.out.sync_context = r->out.sync_context;
+ r2.out.delta_enum_array = r->out.delta_enum_array;
+ r2.in.preferredmaximumlength = r->in.preferredmaximumlength;
+
+ status = dcesrv_netr_DatabaseSync2(dce_call, mem_ctx, &r2);
+
+ return status;
+}
+
+
+/*
+ netr_AccountDeltas
+*/
+static NTSTATUS dcesrv_netr_AccountDeltas(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_AccountDeltas *r)
+{
+ /* w2k3 returns "NOT IMPLEMENTED" for this call */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ netr_AccountSync
+*/
+static NTSTATUS dcesrv_netr_AccountSync(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_AccountSync *r)
+{
+ /* w2k3 returns "NOT IMPLEMENTED" for this call */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ netr_GetDcName
+*/
+static WERROR dcesrv_netr_GetDcName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_GetDcName *r)
+{
+ const char * const attrs[] = { NULL };
+ struct ldb_context *sam_ctx;
+ struct ldb_message **res;
+ struct ldb_dn *domain_dn;
+ int ret;
+ const char *dcname;
+
+ /*
+ * [MS-NRPC] 3.5.5.3.4 NetrGetDCName says
+ * that the domainname needs to be a valid netbios domain
+ * name, if it is not NULL.
+ */
+ if (r->in.domainname) {
+ const char *dot = strchr(r->in.domainname, '.');
+ size_t len = strlen(r->in.domainname);
+
+ if (dot || len > 15) {
+ return WERR_NERR_DCNOTFOUND;
+ }
+
+ /*
+ * TODO: Should we also varify that only valid
+ * netbios name characters are used?
+ */
+ }
+
+ sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ domain_dn = samdb_domain_to_dn(sam_ctx, mem_ctx,
+ r->in.domainname);
+ if (domain_dn == NULL) {
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ ret = gendb_search_dn(sam_ctx, mem_ctx,
+ domain_dn, &res, attrs);
+ if (ret != 1) {
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ /* TODO: - return real IP address
+ * - check all r->in.* parameters (server_unc is ignored by w2k3!)
+ */
+ dcname = talloc_asprintf(mem_ctx, "\\\\%s",
+ lpcfg_netbios_name(dce_call->conn->dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(dcname);
+
+ *r->out.dcname = dcname;
+ return WERR_OK;
+}
+
+struct dcesrv_netr_LogonControl_base_state {
+ struct dcesrv_call_state *dce_call;
+
+ TALLOC_CTX *mem_ctx;
+
+ struct netr_LogonControl2Ex r;
+
+ struct {
+ struct netr_LogonControl *l;
+ struct netr_LogonControl2 *l2;
+ struct netr_LogonControl2Ex *l2ex;
+ } _r;
+};
+
+static void dcesrv_netr_LogonControl_base_done(struct tevent_req *subreq);
+
+static WERROR dcesrv_netr_LogonControl_base_call(struct dcesrv_netr_LogonControl_base_state *state)
+{
+ struct loadparm_context *lp_ctx = state->dce_call->conn->dce_ctx->lp_ctx;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(state->dce_call);
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(state->dce_call->conn);
+ enum security_user_level security_level;
+ struct dcerpc_binding_handle *irpc_handle;
+ struct tevent_req *subreq;
+ bool ok;
+
+ /* TODO: check for WERR_INVALID_COMPUTERNAME ? */
+
+ if (state->_r.l != NULL) {
+ /*
+ * netr_LogonControl
+ */
+ if (state->r.in.level == 0x00000002) {
+ return WERR_NOT_SUPPORTED;
+ } else if (state->r.in.level != 0x00000001) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ switch (state->r.in.function_code) {
+ case NETLOGON_CONTROL_QUERY:
+ case NETLOGON_CONTROL_REPLICATE:
+ case NETLOGON_CONTROL_SYNCHRONIZE:
+ case NETLOGON_CONTROL_PDC_REPLICATE:
+ case NETLOGON_CONTROL_BREAKPOINT:
+ case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
+ case NETLOGON_CONTROL_TRUNCATE_LOG:
+ break;
+ default:
+ return WERR_NOT_SUPPORTED;
+ }
+ }
+
+ if (state->r.in.level < 0x00000001) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ if (state->r.in.level > 0x00000004) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ if (state->r.in.function_code == NETLOGON_CONTROL_QUERY) {
+ struct netr_NETLOGON_INFO_1 *info1 = NULL;
+ struct netr_NETLOGON_INFO_3 *info3 = NULL;
+
+ switch (state->r.in.level) {
+ case 0x00000001:
+ info1 = talloc_zero(state->mem_ctx,
+ struct netr_NETLOGON_INFO_1);
+ if (info1 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->r.out.query->info1 = info1;
+ return WERR_OK;
+
+ case 0x00000003:
+ info3 = talloc_zero(state->mem_ctx,
+ struct netr_NETLOGON_INFO_3);
+ if (info3 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->r.out.query->info3 = info3;
+ return WERR_OK;
+
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+ }
+
+ /*
+ * Some validations are done before the access check
+ * and some after the access check
+ */
+ security_level = security_session_user_level(session_info, NULL);
+ if (security_level < SECURITY_ADMINISTRATOR) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (state->_r.l2 != NULL) {
+ /*
+ * netr_LogonControl2
+ */
+ if (state->r.in.level == 0x00000004) {
+ return WERR_INVALID_LEVEL;
+ }
+ }
+
+ switch (state->r.in.level) {
+ case 0x00000001:
+ break;
+
+ case 0x00000002:
+ switch (state->r.in.function_code) {
+ case NETLOGON_CONTROL_REDISCOVER:
+ case NETLOGON_CONTROL_TC_QUERY:
+ case NETLOGON_CONTROL_TC_VERIFY:
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ break;
+
+ case 0x00000003:
+ break;
+
+ case 0x00000004:
+ if (state->r.in.function_code != NETLOGON_CONTROL_FIND_USER) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ break;
+
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ switch (state->r.in.function_code) {
+ case NETLOGON_CONTROL_REDISCOVER:
+ case NETLOGON_CONTROL_TC_QUERY:
+ case NETLOGON_CONTROL_TC_VERIFY:
+ if (state->r.in.level != 2) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (state->r.in.data == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (state->r.in.data->domain == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ break;
+
+ case NETLOGON_CONTROL_CHANGE_PASSWORD:
+ if (state->r.in.level != 1) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (state->r.in.data == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (state->r.in.data->domain == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ ok = lpcfg_is_my_domain_or_realm(lp_ctx,
+ state->r.in.data->domain);
+ if (!ok) {
+ struct ldb_context *sam_ctx;
+
+ sam_ctx = dcesrv_samdb_connect_as_system(state,
+ state->dce_call);
+ if (sam_ctx == NULL) {
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ /*
+ * Secrets for trusted domains can only be triggered on
+ * the PDC.
+ */
+ ok = samdb_is_pdc(sam_ctx);
+ TALLOC_FREE(sam_ctx);
+ if (!ok) {
+ return WERR_INVALID_DOMAIN_ROLE;
+ }
+ }
+
+ break;
+ default:
+ return WERR_NOT_SUPPORTED;
+ }
+
+ irpc_handle = irpc_binding_handle_by_name(state,
+ imsg_ctx,
+ "winbind_server",
+ &ndr_table_winbind);
+ if (irpc_handle == NULL) {
+ DEBUG(0,("Failed to get binding_handle for winbind_server task\n"));
+ state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ return WERR_SERVICE_NOT_FOUND;
+ }
+
+ /*
+ * 60 seconds timeout should be enough
+ */
+ dcerpc_binding_handle_set_timeout(irpc_handle, 60);
+
+ subreq = dcerpc_winbind_LogonControl_send(state,
+ state->dce_call->event_ctx,
+ irpc_handle,
+ state->r.in.function_code,
+ state->r.in.level,
+ state->r.in.data,
+ state->r.out.query);
+ if (subreq == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ tevent_req_set_callback(subreq,
+ dcesrv_netr_LogonControl_base_done,
+ state);
+
+ return WERR_OK;
+}
+
+static void dcesrv_netr_LogonControl_base_done(struct tevent_req *subreq)
+{
+ struct dcesrv_netr_LogonControl_base_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcesrv_netr_LogonControl_base_state);
+ NTSTATUS status;
+
+ status = dcerpc_winbind_LogonControl_recv(subreq, state->mem_ctx,
+ &state->r.out.result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ state->r.out.result = WERR_TIMEOUT;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ DEBUG(0,(__location__ ": IRPC callback failed %s\n",
+ nt_errstr(status)));
+ }
+
+ if (state->_r.l2ex != NULL) {
+ struct netr_LogonControl2Ex *r = state->_r.l2ex;
+ r->out.result = state->r.out.result;
+ } else if (state->_r.l2 != NULL) {
+ struct netr_LogonControl2 *r = state->_r.l2;
+ r->out.result = state->r.out.result;
+ } else if (state->_r.l != NULL) {
+ struct netr_LogonControl *r = state->_r.l;
+ r->out.result = state->r.out.result;
+ }
+
+ status = dcesrv_reply(state->dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": dcesrv_reply() failed - %s\n", nt_errstr(status)));
+ }
+}
+
+/*
+ netr_LogonControl
+*/
+static WERROR dcesrv_netr_LogonControl(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonControl *r)
+{
+ struct dcesrv_netr_LogonControl_base_state *state;
+ WERROR werr;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_netr_LogonControl_base_state);
+ if (state == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->r.in.logon_server = r->in.logon_server;
+ state->r.in.function_code = r->in.function_code;
+ state->r.in.level = r->in.level;
+ state->r.in.data = NULL;
+ state->r.out.query = r->out.query;
+
+ state->_r.l = r;
+
+ werr = dcesrv_netr_LogonControl_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return werr;
+ }
+
+ return werr;
+}
+
+/*
+ netr_LogonControl2
+*/
+static WERROR dcesrv_netr_LogonControl2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonControl2 *r)
+{
+ struct dcesrv_netr_LogonControl_base_state *state;
+ WERROR werr;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_netr_LogonControl_base_state);
+ if (state == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->r.in.logon_server = r->in.logon_server;
+ state->r.in.function_code = r->in.function_code;
+ state->r.in.level = r->in.level;
+ state->r.in.data = r->in.data;
+ state->r.out.query = r->out.query;
+
+ state->_r.l2 = r;
+
+ werr = dcesrv_netr_LogonControl_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return werr;
+ }
+
+ return werr;
+}
+
+/*
+ netr_LogonControl2Ex
+*/
+static WERROR dcesrv_netr_LogonControl2Ex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonControl2Ex *r)
+{
+ struct dcesrv_netr_LogonControl_base_state *state;
+ WERROR werr;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_netr_LogonControl_base_state);
+ if (state == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->r = *r;
+ state->_r.l2ex = r;
+
+ werr = dcesrv_netr_LogonControl_base_call(state);
+
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return werr;
+ }
+
+ return werr;
+}
+
+static WERROR fill_trusted_domains_array(TALLOC_CTX *mem_ctx,
+ struct ldb_context *sam_ctx,
+ struct netr_DomainTrustList *trusts,
+ uint32_t trust_flags);
+
+/*
+ netr_GetAnyDCName
+*/
+static WERROR dcesrv_netr_GetAnyDCName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_GetAnyDCName *r)
+{
+ struct netr_DomainTrustList *trusts;
+ struct ldb_context *sam_ctx;
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ uint32_t i;
+ WERROR werr;
+
+ *r->out.dcname = NULL;
+
+ if ((r->in.domainname == NULL) || (r->in.domainname[0] == '\0')) {
+ /* if the domainname parameter wasn't set assume our domain */
+ r->in.domainname = lpcfg_workgroup(lp_ctx);
+ }
+
+ sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ if (strcasecmp(r->in.domainname, lpcfg_workgroup(lp_ctx)) == 0) {
+ /* well we asked for a DC of our own domain */
+ if (samdb_is_pdc(sam_ctx)) {
+ /* we are the PDC of the specified domain */
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ *r->out.dcname = talloc_asprintf(mem_ctx, "\\%s",
+ lpcfg_netbios_name(lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(*r->out.dcname);
+
+ return WERR_OK;
+ }
+
+ /* Okay, now we have to consider the trusted domains */
+
+ trusts = talloc_zero(mem_ctx, struct netr_DomainTrustList);
+ W_ERROR_HAVE_NO_MEMORY(trusts);
+
+ trusts->count = 0;
+
+ werr = fill_trusted_domains_array(mem_ctx, sam_ctx, trusts,
+ NETR_TRUST_FLAG_INBOUND
+ | NETR_TRUST_FLAG_OUTBOUND);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ for (i = 0; i < trusts->count; i++) {
+ if (strcasecmp(r->in.domainname, trusts->array[i].netbios_name) == 0) {
+ /* FIXME: Here we need to find a DC for the specified
+ * trusted domain. */
+
+ /* return WERR_OK; */
+ return WERR_NO_SUCH_DOMAIN;
+ }
+ }
+
+ return WERR_NO_SUCH_DOMAIN;
+}
+
+
+/*
+ netr_DatabaseRedo
+*/
+static NTSTATUS dcesrv_netr_DatabaseRedo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DatabaseRedo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NetrEnumerateTrustedDomains
+*/
+static NTSTATUS dcesrv_netr_NetrEnumerateTrustedDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NetrEnumerateTrustedDomains *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonGetCapabilities
+*/
+static NTSTATUS dcesrv_netr_LogonGetCapabilities(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonGetCapabilities *r)
+{
+ struct netlogon_creds_CredentialState *creds;
+ NTSTATUS status;
+
+ switch (r->in.query_level) {
+ case 1:
+ break;
+ case 2:
+ /*
+ * Until we know the details behind KB5028166
+ * just return DCERPC_NCA_S_FAULT_INVALID_TAG
+ * like an unpatched Windows Server.
+ */
+ FALL_THROUGH;
+ default:
+ /*
+ * There would not be a way to marshall the
+ * the response. Which would mean our final
+ * ndr_push would fail an we would return
+ * an RPC-level fault with DCERPC_FAULT_BAD_STUB_DATA.
+ *
+ * But it's important to match a Windows server
+ * especially before KB5028166, see also our bug #15418
+ * Otherwise Windows client would stop talking to us.
+ */
+ DCESRV_FAULT(DCERPC_NCA_S_FAULT_INVALID_TAG);
+ }
+
+ status = dcesrv_netr_creds_server_step_check(dce_call,
+ mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ " Bad credentials - error\n"));
+ }
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ r->out.capabilities->server_capabilities = creds->negotiate_flags;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ netr_NETRLOGONSETSERVICEBITS
+*/
+static WERROR dcesrv_netr_NETRLOGONSETSERVICEBITS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONSETSERVICEBITS *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonGetTrustRid
+*/
+static WERROR dcesrv_netr_LogonGetTrustRid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonGetTrustRid *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NETRLOGONCOMPUTESERVERDIGEST
+*/
+static WERROR dcesrv_netr_NETRLOGONCOMPUTESERVERDIGEST(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONCOMPUTESERVERDIGEST *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NETRLOGONCOMPUTECLIENTDIGEST
+*/
+static WERROR dcesrv_netr_NETRLOGONCOMPUTECLIENTDIGEST(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONCOMPUTECLIENTDIGEST *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+/*
+ netr_DsRGetSiteName
+*/
+static WERROR dcesrv_netr_DsRGetSiteName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetSiteName *r)
+{
+ struct ldb_context *sam_ctx;
+
+ sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ /*
+ * We assume to be a DC when we get called over NETLOGON. Hence we
+ * get our site name always by using "samdb_server_site_name()"
+ * and not "samdb_client_site_name()".
+ */
+ *r->out.site = samdb_server_site_name(sam_ctx, mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(*r->out.site);
+
+ return WERR_OK;
+}
+
+
+/*
+ fill in a netr_OneDomainInfo from our own domain/forest
+*/
+static NTSTATUS fill_our_one_domain_info(TALLOC_CTX *mem_ctx,
+ const struct lsa_TrustDomainInfoInfoEx *our_tdo,
+ struct GUID domain_guid,
+ struct netr_OneDomainInfo *info,
+ bool is_trust_list)
+{
+ ZERO_STRUCTP(info);
+
+ if (is_trust_list) {
+ struct netr_trust_extension *te = NULL;
+ struct netr_trust_extension_info *tei = NULL;
+
+ /* w2k8 only fills this on trusted domains */
+ te = talloc_zero(mem_ctx, struct netr_trust_extension);
+ if (te == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ tei = &te->info;
+ tei->flags |= NETR_TRUST_FLAG_PRIMARY;
+
+ /*
+ * We're always within a native forest
+ */
+ tei->flags |= NETR_TRUST_FLAG_IN_FOREST;
+ tei->flags |= NETR_TRUST_FLAG_NATIVE;
+
+ /* For now we assume we're always the tree root */
+ tei->flags |= NETR_TRUST_FLAG_TREEROOT;
+ tei->parent_index = 0;
+
+ tei->trust_type = our_tdo->trust_type;
+ /*
+ * This needs to be 0 instead of our_tdo->trust_attributes
+ * It means LSA_TRUST_ATTRIBUTE_WITHIN_FOREST won't
+ * be set, while NETR_TRUST_FLAG_IN_FOREST is set above.
+ */
+ tei->trust_attributes = 0;
+
+ info->trust_extension.info = te;
+ }
+
+ if (is_trust_list) {
+ info->dns_domainname.string = our_tdo->domain_name.string;
+
+ /* MS-NRPC 3.5.4.3.9 - must be set to NULL for trust list */
+ info->dns_forestname.string = NULL;
+ } else {
+ info->dns_domainname.string = talloc_asprintf(mem_ctx, "%s.",
+ our_tdo->domain_name.string);
+ if (info->dns_domainname.string == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info->dns_forestname.string = info->dns_domainname.string;
+ }
+
+ info->domainname.string = our_tdo->netbios_name.string;
+ info->domain_sid = our_tdo->sid;
+ info->domain_guid = domain_guid;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ fill in a netr_OneDomainInfo from a trust tdo
+*/
+static NTSTATUS fill_trust_one_domain_info(TALLOC_CTX *mem_ctx,
+ struct GUID domain_guid,
+ const struct lsa_TrustDomainInfoInfoEx *tdo,
+ struct netr_OneDomainInfo *info)
+{
+ struct netr_trust_extension *te = NULL;
+ struct netr_trust_extension_info *tei = NULL;
+
+ ZERO_STRUCTP(info);
+
+ /* w2k8 only fills this on trusted domains */
+ te = talloc_zero(mem_ctx, struct netr_trust_extension);
+ if (te == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ tei = &te->info;
+
+ if (tdo->trust_direction & LSA_TRUST_DIRECTION_INBOUND) {
+ tei->flags |= NETR_TRUST_FLAG_INBOUND;
+ }
+ if (tdo->trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) {
+ tei->flags |= NETR_TRUST_FLAG_OUTBOUND;
+ }
+ if (tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+ tei->flags |= NETR_TRUST_FLAG_IN_FOREST;
+ }
+
+ /*
+ * TODO: once we support multiple domains within our forest,
+ * we need to fill this correct (or let the caller do it
+ * for all domains marked with NETR_TRUST_FLAG_IN_FOREST).
+ */
+ tei->parent_index = 0;
+
+ tei->trust_type = tdo->trust_type;
+ tei->trust_attributes = tdo->trust_attributes;
+
+ info->trust_extension.info = te;
+
+ info->domainname.string = tdo->netbios_name.string;
+ if (tdo->trust_type != LSA_TRUST_TYPE_DOWNLEVEL) {
+ info->dns_domainname.string = tdo->domain_name.string;
+ } else {
+ info->dns_domainname.string = NULL;
+ }
+ info->domain_sid = tdo->sid;
+ info->domain_guid = domain_guid;
+
+ /* MS-NRPC 3.5.4.3.9 - must be set to NULL for trust list */
+ info->dns_forestname.string = NULL;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ netr_LogonGetDomainInfo
+ this is called as part of the ADS domain logon procedure.
+
+ It has an important role in convaying details about the client, such
+ as Operating System, Version, Service Pack etc.
+*/
+static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx, struct netr_LogonGetDomainInfo *r)
+{
+ struct netlogon_creds_CredentialState *creds;
+ const char * const trusts_attrs[] = {
+ "securityIdentifier",
+ "flatName",
+ "trustPartner",
+ "trustAttributes",
+ "trustDirection",
+ "trustType",
+ NULL
+ };
+ const char * const attrs2[] = { "sAMAccountName", "dNSHostName",
+ "msDS-SupportedEncryptionTypes", NULL };
+ const char *sam_account_name, *old_dns_hostname;
+ struct ldb_context *sam_ctx;
+ const struct GUID *our_domain_guid = NULL;
+ struct lsa_TrustDomainInfoInfoEx *our_tdo = NULL;
+ struct ldb_message **res1, *new_msg;
+ struct ldb_result *trusts_res = NULL;
+ struct ldb_dn *workstation_dn;
+ struct netr_DomainInformation *domain_info;
+ struct netr_LsaPolicyInformation *lsa_policy_info;
+ struct auth_session_info *workstation_session_info = NULL;
+ uint32_t default_supported_enc_types = 0xFFFFFFFF;
+ bool update_dns_hostname = true;
+ int ret, i;
+ NTSTATUS status;
+
+ status = dcesrv_netr_creds_server_step_check(dce_call,
+ mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ char* local = NULL;
+ char* remote = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ remote = tsocket_address_string(dce_call->conn->remote_address,
+ frame);
+ local = tsocket_address_string(dce_call->conn->local_address,
+ frame);
+ DBG_ERR(("Bad credentials - "
+ "computer[%s] remote[%s] local[%s]\n"),
+ log_escape(frame, r->in.computer_name),
+ remote,
+ local);
+ talloc_free(frame);
+ }
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* We want to avoid connecting as system. */
+ sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ switch (r->in.level) {
+ case 1: /* Domain information */
+
+ if (r->in.query->workstation_info == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Prepares the workstation DN */
+ workstation_dn = ldb_dn_new_fmt(mem_ctx, sam_ctx, "<SID=%s>",
+ dom_sid_string(mem_ctx, creds->sid));
+ NT_STATUS_HAVE_NO_MEMORY(workstation_dn);
+
+ /* Get the workstation's session info from the database. */
+ status = authsam_get_session_info_principal(mem_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ sam_ctx,
+ NULL, /* principal */
+ workstation_dn,
+ 0, /* session_info_flags */
+ &workstation_session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * Reconnect to samdb as the workstation, now that we have its
+ * session info. We do this so the database update can be
+ * attributed to the workstation account in the audit logs --
+ * otherwise it might be incorrectly attributed to
+ * SID_NT_ANONYMOUS.
+ */
+ sam_ctx = dcesrv_samdb_connect_session_info(mem_ctx,
+ dce_call,
+ workstation_session_info,
+ workstation_session_info);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /* Lookup for attributes in workstation object */
+ ret = gendb_search_dn(sam_ctx, mem_ctx, workstation_dn, &res1,
+ attrs2);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* Gets the sam account name which is checked against the DNS
+ * hostname parameter. */
+ sam_account_name = ldb_msg_find_attr_as_string(res1[0],
+ "sAMAccountName",
+ NULL);
+ if (sam_account_name == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (r->in.query->workstation_info->dns_hostname == NULL) {
+ update_dns_hostname = false;
+ }
+
+ /* Gets the old DNS hostname */
+ old_dns_hostname = ldb_msg_find_attr_as_string(res1[0],
+ "dNSHostName",
+ NULL);
+
+ /*
+ * Updates the DNS hostname when the client wishes that the
+ * server should handle this for him
+ * ("NETR_WS_FLAG_HANDLES_SPN_UPDATE" not set).
+ * See MS-NRPC section 3.5.4.3.9
+ */
+ if ((r->in.query->workstation_info->workstation_flags
+ & NETR_WS_FLAG_HANDLES_SPN_UPDATE) != 0) {
+ update_dns_hostname = false;
+ }
+
+ /* Gets host information and put them into our directory */
+
+ new_msg = ldb_msg_new(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(new_msg);
+
+ new_msg->dn = workstation_dn;
+
+ /* Sets the OS name */
+
+ if (r->in.query->workstation_info->os_name.string == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ret = ldb_msg_add_string(new_msg, "operatingSystem",
+ r->in.query->workstation_info->os_name.string);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * Sets information from "os_version". On an empty structure
+ * the values are cleared.
+ */
+ if (r->in.query->workstation_info->os_version.os != NULL) {
+ struct netr_OsVersionInfoEx *os_version;
+ const char *os_version_str;
+
+ os_version = &r->in.query->workstation_info->os_version.os->os;
+
+ if (os_version->CSDVersion == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ os_version_str = talloc_asprintf(new_msg, "%u.%u (%u)",
+ os_version->MajorVersion,
+ os_version->MinorVersion,
+ os_version->BuildNumber);
+ NT_STATUS_HAVE_NO_MEMORY(os_version_str);
+
+ if (strlen(os_version->CSDVersion) != 0) {
+ ret = ldb_msg_add_string(new_msg,
+ "operatingSystemServicePack",
+ os_version->CSDVersion);
+ } else {
+ ret = samdb_msg_add_delete(sam_ctx, mem_ctx, new_msg,
+ "operatingSystemServicePack");
+ }
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_msg_add_string(new_msg,
+ "operatingSystemVersion",
+ os_version_str);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ ret = samdb_msg_add_delete(sam_ctx, mem_ctx, new_msg,
+ "operatingSystemServicePack");
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = samdb_msg_add_delete(sam_ctx, mem_ctx, new_msg,
+ "operatingSystemVersion");
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /*
+ * If the boolean "update_dns_hostname" remained true, then we
+ * are fine to start the update.
+ */
+ if (update_dns_hostname) {
+ ret = ldb_msg_add_string(new_msg,
+ "dNSHostname",
+ r->in.query->workstation_info->dns_hostname);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* This manual "servicePrincipalName" generation is
+ * still needed! Since the update in the samldb LDB
+ * module does only work if the entries already exist
+ * which isn't always the case. */
+ ret = ldb_msg_add_string(new_msg,
+ "servicePrincipalName",
+ talloc_asprintf(new_msg, "HOST/%s",
+ r->in.computer_name));
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_msg_add_string(new_msg,
+ "servicePrincipalName",
+ talloc_asprintf(new_msg, "HOST/%s",
+ r->in.query->workstation_info->dns_hostname));
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (dsdb_replace(sam_ctx, new_msg, DSDB_FLAG_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE) != LDB_SUCCESS) {
+ DEBUG(3,("Impossible to update samdb: %s\n",
+ ldb_errstring(sam_ctx)));
+ }
+
+ talloc_free(new_msg);
+
+ /* Writes back the domain information */
+
+ our_domain_guid = samdb_domain_guid(sam_ctx);
+ if (our_domain_guid == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = dsdb_trust_local_tdo_info(mem_ctx, sam_ctx, &our_tdo);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dsdb_trust_search_tdos(sam_ctx,
+ NULL, /* exclude */
+ trusts_attrs,
+ mem_ctx,
+ &trusts_res);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ domain_info = talloc(mem_ctx, struct netr_DomainInformation);
+ NT_STATUS_HAVE_NO_MEMORY(domain_info);
+
+ ZERO_STRUCTP(domain_info);
+
+ /* Informations about the local and trusted domains */
+
+ status = fill_our_one_domain_info(mem_ctx,
+ our_tdo,
+ *our_domain_guid,
+ &domain_info->primary_domain,
+ false);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ domain_info->trusted_domain_count = trusts_res->count + 1;
+ domain_info->trusted_domains = talloc_zero_array(mem_ctx,
+ struct netr_OneDomainInfo,
+ domain_info->trusted_domain_count);
+ NT_STATUS_HAVE_NO_MEMORY(domain_info->trusted_domains);
+
+ for (i=0; i < trusts_res->count; i++) {
+ struct netr_OneDomainInfo *o =
+ &domain_info->trusted_domains[i];
+ /* we can't know the guid of trusts outside our forest */
+ struct GUID trust_domain_guid = GUID_zero();
+ struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+
+ status = dsdb_trust_parse_tdo_info(mem_ctx,
+ trusts_res->msgs[i],
+ &tdo);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = fill_trust_one_domain_info(mem_ctx,
+ trust_domain_guid,
+ tdo,
+ o);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ status = fill_our_one_domain_info(mem_ctx,
+ our_tdo,
+ *our_domain_guid,
+ &domain_info->trusted_domains[i],
+ true);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Sets the supported encryption types */
+ domain_info->supported_enc_types = ldb_msg_find_attr_as_uint(res1[0],
+ "msDS-SupportedEncryptionTypes",
+ default_supported_enc_types);
+
+ /* Other host domain information */
+
+ lsa_policy_info = talloc(mem_ctx,
+ struct netr_LsaPolicyInformation);
+ NT_STATUS_HAVE_NO_MEMORY(lsa_policy_info);
+ ZERO_STRUCTP(lsa_policy_info);
+
+ domain_info->lsa_policy = *lsa_policy_info;
+
+ /* The DNS hostname is only returned back when there is a chance
+ * for a change. */
+ if ((r->in.query->workstation_info->workstation_flags
+ & NETR_WS_FLAG_HANDLES_SPN_UPDATE) != 0) {
+ domain_info->dns_hostname.string = old_dns_hostname;
+ } else {
+ domain_info->dns_hostname.string = NULL;
+ }
+
+ domain_info->workstation_flags =
+ r->in.query->workstation_info->workstation_flags & (
+ NETR_WS_FLAG_HANDLES_SPN_UPDATE | NETR_WS_FLAG_HANDLES_INBOUND_TRUSTS);
+
+ r->out.info->domain_info = domain_info;
+ break;
+ case 2: /* LSA policy information - not used at the moment */
+ lsa_policy_info = talloc(mem_ctx,
+ struct netr_LsaPolicyInformation);
+ NT_STATUS_HAVE_NO_MEMORY(lsa_policy_info);
+ ZERO_STRUCTP(lsa_policy_info);
+
+ r->out.info->lsa_policy_info = lsa_policy_info;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ netr_ServerPasswordGet
+*/
+static NTSTATUS dcesrv_netr_ServerPasswordGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerPasswordGet *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static bool sam_rodc_access_check(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct dom_sid *user_sid,
+ struct ldb_dn *obj_dn)
+{
+ const char *rodc_attrs[] = { "msDS-NeverRevealGroup",
+ "msDS-RevealOnDemandGroup",
+ "userAccountControl",
+ NULL };
+ const char *obj_attrs[] = { "tokenGroups", "objectSid", "UserAccountControl", "msDS-KrbTgtLinkBL", NULL };
+ struct ldb_dn *rodc_dn;
+ int ret;
+ struct ldb_result *rodc_res = NULL, *obj_res = NULL;
+ WERROR werr;
+
+ rodc_dn = ldb_dn_new_fmt(mem_ctx, sam_ctx, "<SID=%s>",
+ dom_sid_string(mem_ctx, user_sid));
+ if (!ldb_dn_validate(rodc_dn)) goto denied;
+
+ /*
+ * do the two searches we need
+ * We need DSDB_SEARCH_SHOW_EXTENDED_DN as we get a SID list
+ * out of the extended DNs
+ */
+ ret = dsdb_search_dn(sam_ctx, mem_ctx, &rodc_res, rodc_dn, rodc_attrs,
+ DSDB_SEARCH_SHOW_EXTENDED_DN);
+ if (ret != LDB_SUCCESS || rodc_res->count != 1) goto denied;
+
+ ret = dsdb_search_dn(sam_ctx, mem_ctx, &obj_res, obj_dn, obj_attrs, 0);
+ if (ret != LDB_SUCCESS || obj_res->count != 1) goto denied;
+
+ werr = samdb_confirm_rodc_allowed_to_repl_to(sam_ctx,
+ user_sid,
+ rodc_res->msgs[0],
+ obj_res->msgs[0]);
+
+ if (W_ERROR_IS_OK(werr)) {
+ goto allowed;
+ }
+denied:
+ return false;
+allowed:
+ return true;
+
+}
+
+/*
+ netr_NetrLogonSendToSam
+*/
+static NTSTATUS dcesrv_netr_NetrLogonSendToSam(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NetrLogonSendToSam *r)
+{
+ struct netlogon_creds_CredentialState *creds;
+ struct ldb_context *sam_ctx;
+ NTSTATUS nt_status;
+ DATA_BLOB decrypted_blob;
+ enum ndr_err_code ndr_err;
+ struct netr_SendToSamBase base_msg = { 0 };
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call,
+ mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ switch (creds->secure_channel_type) {
+ case SEC_CHAN_BDC:
+ case SEC_CHAN_RODC:
+ break;
+ case SEC_CHAN_WKSTA:
+ case SEC_CHAN_DNS_DOMAIN:
+ case SEC_CHAN_DOMAIN:
+ case SEC_CHAN_NULL:
+ return NT_STATUS_INVALID_PARAMETER;
+ default:
+ DEBUG(1, ("Client asked for an invalid secure channel type: %d\n",
+ creds->secure_channel_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /* Buffer is meant to be 16-bit aligned */
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ nt_status = netlogon_creds_aes_decrypt(creds,
+ r->in.opaque_buffer,
+ r->in.buffer_len);
+ } else {
+ nt_status = netlogon_creds_arcfour_crypt(creds,
+ r->in.opaque_buffer,
+ r->in.buffer_len);
+ }
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ decrypted_blob.data = r->in.opaque_buffer;
+ decrypted_blob.length = r->in.buffer_len;
+
+ ndr_err = ndr_pull_struct_blob(&decrypted_blob, mem_ctx, &base_msg,
+ (ndr_pull_flags_fn_t)ndr_pull_netr_SendToSamBase);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ /* We only partially implement SendToSam */
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* Now 'send' to SAM */
+ switch (base_msg.message_type) {
+ case SendToSamResetBadPasswordCount:
+ {
+ struct ldb_message *msg = ldb_msg_new(mem_ctx);
+ struct ldb_dn *dn = NULL;
+ int ret = 0;
+
+
+ ret = ldb_transaction_start(sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ret = dsdb_find_dn_by_guid(sam_ctx,
+ mem_ctx,
+ &base_msg.message.reset_bad_password.guid,
+ 0,
+ &dn);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (creds->secure_channel_type == SEC_CHAN_RODC &&
+ !sam_rodc_access_check(sam_ctx, mem_ctx, creds->sid, dn)) {
+ DEBUG(1, ("Client asked to reset bad password on "
+ "an arbitrary user: %s\n",
+ ldb_dn_get_linearized(dn)));
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ msg->dn = dn;
+
+ ret = samdb_msg_add_int(sam_ctx, mem_ctx, msg, "badPwdCount", 0);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ret = dsdb_replace(sam_ctx, msg, 0);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ret = ldb_transaction_commit(sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ break;
+ }
+ default:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct dcesrv_netr_DsRGetDCName_base_state {
+ struct dcesrv_call_state *dce_call;
+ TALLOC_CTX *mem_ctx;
+
+ struct netr_DsRGetDCNameEx2 r;
+ const char *client_site;
+
+ struct {
+ struct netr_DsRGetDCName *dc;
+ struct netr_DsRGetDCNameEx *dcex;
+ struct netr_DsRGetDCNameEx2 *dcex2;
+ } _r;
+};
+
+static void dcesrv_netr_DsRGetDCName_base_done(struct tevent_req *subreq);
+
+/* Returns a nonzero value if multiple bits in 'val' are set. */
+static bool multiple_bits_set(uint32_t val)
+{
+ /*
+ * Subtracting one from an integer has the effect of flipping all the
+ * bits from the least significant bit up to and including the least
+ * significant '1' bit. For example,
+ *
+ * 0b101000 - 1
+ * = 0b100111
+ * ====
+ *
+ * If 'val' is zero, all the bits will be flipped and thus the bitwise
+ * AND of 'val' with 'val - 1' will be zero.
+ *
+ * If the integer is nonzero, the least significant '1' bit will be
+ * ANDed with a '0' bit and so will be reset in the final result, but
+ * all other '1' bits will remain set. In other words, the effect of
+ * this expression is to mask off the least significant bit that is
+ * set. Therefore iff the result of 'val & (val - 1)' is non-zero, 'val'
+ * must contain multiple set bits.
+ */
+ return val & (val - 1);
+}
+
+static WERROR dcesrv_netr_DsRGetDCName_base_call(struct dcesrv_netr_DsRGetDCName_base_state *state)
+{
+ struct dcesrv_call_state *dce_call = state->dce_call;
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+ TALLOC_CTX *mem_ctx = state->mem_ctx;
+ struct netr_DsRGetDCNameEx2 *r = &state->r;
+ struct ldb_context *sam_ctx;
+ struct netr_DsRGetDCNameInfo *info;
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ const struct tsocket_address *local_address;
+ char *local_addr = NULL;
+ const struct tsocket_address *remote_address;
+ char *remote_addr = NULL;
+ const char *server_site_name;
+ char *guid_str;
+ struct netlogon_samlogon_response response;
+ NTSTATUS status;
+ const char *dc_name = NULL;
+ const char *domain_name = NULL;
+ const char *pdc_ip;
+ bool different_domain = true;
+ uint32_t valid_flags;
+ int dc_level;
+
+ ZERO_STRUCTP(r->out.info);
+
+ sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ local_address = dcesrv_connection_get_local_address(dce_call->conn);
+ if (tsocket_address_is_inet(local_address, "ip")) {
+ local_addr = tsocket_address_inet_addr_string(local_address, state);
+ W_ERROR_HAVE_NO_MEMORY(local_addr);
+ }
+
+ remote_address = dcesrv_connection_get_remote_address(dce_call->conn);
+ if (tsocket_address_is_inet(remote_address, "ip")) {
+ remote_addr = tsocket_address_inet_addr_string(remote_address, state);
+ W_ERROR_HAVE_NO_MEMORY(remote_addr);
+ }
+
+ /* "server_unc" is ignored by w2k3 */
+
+ /*
+ * With the following flags:
+ * DS_FORCE_REDISCOVERY (Flag A)
+ * DS_DIRECTORY_SERVICE_REQUIRED (Flag B)
+ * DS_DIRECTORY_SERVICE_PREFERRED (Flag C)
+ * DS_GC_SERVER_REQUIRED (Flag D)
+ * DS_PDC_REQUIRED (Flag E)
+ * DS_BACKGROUND_ONLY (Flag F)
+ * DS_IP_REQUIRED (Flag G)
+ * DS_KDC_REQUIRED (Flag H)
+ * DS_TIMESERV_REQUIRED (Flag I)
+ * DS_WRITABLE_REQUIRED (Flag J)
+ * DS_GOOD_TIMESERV_PREFERRED (Flag K)
+ * DS_AVOID_SELF (Flag L)
+ * DS_ONLY_LDAP_NEEDED (Flag M)
+ * DS_IS_FLAT_NAME (Flag N)
+ * DS_IS_DNS_NAME (Flag O)
+ * DS_TRY_NEXTCLOSEST_SITE (Flag P)
+ * DS_DIRECTORY_SERVICE_6_REQUIRED (Flag Q)
+ * DS_WEB_SERVICE_REQUIRED (Flag T)
+ * DS_DIRECTORY_SERVICE_8_REQUIRED (Flag U)
+ * DS_DIRECTORY_SERVICE_9_REQUIRED (Flag V)
+ * DS_DIRECTORY_SERVICE_10_REQUIRED (Flag W)
+ * DS_RETURN_DNS_NAME (Flag R)
+ * DS_RETURN_FLAT_NAME (Flag S)
+ *
+ * MS-NRPC 3.5.4.3.1 says:
+ * ...
+ * On receiving this call, the server MUST perform the following Flags
+ * parameter validations:
+ * - Flags D, E, and H MUST NOT be combined with each other.
+ * - Flag N MUST NOT be combined with the O flag.
+ * - Flag R MUST NOT be combined with the S flag.
+ * - Flags B, Q, U, V, and W MUST NOT be combined with each other.
+ * - Flag K MUST NOT be combined with any of the flags: B, C, D, E, or H.
+ * - Flag P MUST NOT be set when the SiteName parameter is provided.
+ * The server MUST return ERROR_INVALID_FLAGS for any of the previously
+ * mentioned conflicting combinations.
+ * ...
+ */
+
+ dc_level = dsdb_dc_functional_level(sam_ctx);
+ valid_flags = DSGETDC_VALID_FLAGS;
+ if (dc_level >= DS_DOMAIN_FUNCTION_2012) {
+ valid_flags |= DS_DIRECTORY_SERVICE_8_REQUIRED;
+ }
+ if (dc_level >= DS_DOMAIN_FUNCTION_2012_R2) {
+ valid_flags |= DS_DIRECTORY_SERVICE_9_REQUIRED;
+ }
+ if (dc_level >= DS_DOMAIN_FUNCTION_2016) {
+ valid_flags |= DS_DIRECTORY_SERVICE_10_REQUIRED;
+ }
+ if (r->in.flags & ~valid_flags) {
+ /*
+ * TODO: add tests to prove this (maybe based on the
+ * msDS-Behavior-Version levels of dc, domain and/or forest
+ */
+ return WERR_INVALID_FLAGS;
+ }
+
+ /* Flags D, E, and H MUST NOT be combined with each other. */
+#define _DEH (DS_GC_SERVER_REQUIRED|DS_PDC_REQUIRED|DS_KDC_REQUIRED)
+ if (multiple_bits_set(r->in.flags & _DEH)) {
+ return WERR_INVALID_FLAGS;
+ }
+
+ /* Flag N MUST NOT be combined with the O flag. */
+ if (r->in.flags & DS_IS_FLAT_NAME &&
+ r->in.flags & DS_IS_DNS_NAME) {
+ return WERR_INVALID_FLAGS;
+ }
+
+ /* Flag R MUST NOT be combined with the S flag. */
+ if (r->in.flags & DS_RETURN_DNS_NAME &&
+ r->in.flags & DS_RETURN_FLAT_NAME) {
+ return WERR_INVALID_FLAGS;
+ }
+
+ /* Flags B, Q, U, V, and W MUST NOT be combined with each other */
+#define _BQUVW ( \
+ DS_DIRECTORY_SERVICE_REQUIRED | \
+ DS_DIRECTORY_SERVICE_6_REQUIRED | \
+ DS_DIRECTORY_SERVICE_8_REQUIRED | \
+ DS_DIRECTORY_SERVICE_9_REQUIRED | \
+ DS_DIRECTORY_SERVICE_10_REQUIRED | \
+0)
+ if (multiple_bits_set(r->in.flags & _BQUVW)) {
+ return WERR_INVALID_FLAGS;
+ }
+
+ /*
+ * Flag K MUST NOT be combined with any of the flags:
+ * B, C, D, E, or H.
+ */
+ if (r->in.flags & DS_GOOD_TIMESERV_PREFERRED &&
+ r->in.flags &
+ (DS_DIRECTORY_SERVICE_REQUIRED |
+ DS_DIRECTORY_SERVICE_PREFERRED |
+ DS_GC_SERVER_REQUIRED |
+ DS_PDC_REQUIRED |
+ DS_KDC_REQUIRED)) {
+ return WERR_INVALID_FLAGS;
+ }
+
+ /* Flag P MUST NOT be set when the SiteName parameter is provided. */
+ if (r->in.flags & DS_TRY_NEXTCLOSEST_SITE &&
+ r->in.site_name) {
+ return WERR_INVALID_FLAGS;
+ }
+
+ /*
+ * If we send an all-zero GUID, we should ignore it as winbind actually
+ * checks it with a DNS query. Windows also appears to ignore it.
+ */
+ if (r->in.domain_guid != NULL && GUID_all_zero(r->in.domain_guid)) {
+ r->in.domain_guid = NULL;
+ }
+
+ /* Attempt winbind search only if we suspect the domain is incorrect */
+ if (r->in.domain_name != NULL && strcmp("", r->in.domain_name) != 0) {
+ if (r->in.flags & DS_IS_FLAT_NAME) {
+ if (strcasecmp_m(r->in.domain_name,
+ lpcfg_sam_name(lp_ctx)) == 0) {
+ different_domain = false;
+ }
+ } else if (r->in.flags & DS_IS_DNS_NAME) {
+ if (strcasecmp_m(r->in.domain_name,
+ lpcfg_dnsdomain(lp_ctx)) == 0) {
+ different_domain = false;
+ }
+ } else {
+ if (strcasecmp_m(r->in.domain_name,
+ lpcfg_sam_name(lp_ctx)) == 0 ||
+ strcasecmp_m(r->in.domain_name,
+ lpcfg_dnsdomain(lp_ctx)) == 0) {
+ different_domain = false;
+ }
+ }
+ } else {
+ /*
+ * We need to be able to handle empty domain names, where we
+ * revert to our domain by default.
+ */
+ different_domain = false;
+ }
+
+ /* Proof server site parameter "site_name" if it was specified */
+ server_site_name = samdb_server_site_name(sam_ctx, state);
+ W_ERROR_HAVE_NO_MEMORY(server_site_name);
+ if (different_domain || (r->in.site_name != NULL &&
+ (strcasecmp_m(r->in.site_name,
+ server_site_name) != 0))) {
+
+ struct dcerpc_binding_handle *irpc_handle = NULL;
+ struct tevent_req *subreq = NULL;
+
+ /*
+ * Retrieve the client site to override the winbind response.
+ *
+ * DO NOT use Windows fallback for client site.
+ * In the case of multiple domains, this is plainly wrong.
+ *
+ * Note: It's possible that the client may belong to multiple
+ * subnets across domains. It's not clear what this would mean,
+ * but here we only return what this domain knows.
+ */
+ state->client_site = samdb_client_site_name(sam_ctx,
+ state,
+ remote_addr,
+ NULL,
+ false);
+
+ irpc_handle = irpc_binding_handle_by_name(state,
+ imsg_ctx,
+ "winbind_server",
+ &ndr_table_winbind);
+ if (irpc_handle == NULL) {
+ DEBUG(0,("Failed to get binding_handle for "
+ "winbind_server task\n"));
+ dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ return WERR_SERVICE_NOT_FOUND;
+ }
+
+ dcerpc_binding_handle_set_timeout(irpc_handle, 60);
+
+ dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+
+ subreq = dcerpc_wbint_DsGetDcName_send(state,
+ dce_call->event_ctx,
+ irpc_handle,
+ r->in.domain_name,
+ r->in.domain_guid,
+ r->in.site_name,
+ r->in.flags,
+ r->out.info);
+ if (subreq == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ tevent_req_set_callback(subreq,
+ dcesrv_netr_DsRGetDCName_base_done,
+ state);
+
+ return WERR_OK;
+ }
+
+ guid_str = r->in.domain_guid != NULL ?
+ GUID_string(state, r->in.domain_guid) : NULL;
+
+ status = fill_netlogon_samlogon_response(sam_ctx, mem_ctx,
+ r->in.domain_name,
+ r->in.domain_name,
+ NULL, guid_str,
+ r->in.client_account,
+ r->in.mask, remote_addr,
+ NETLOGON_NT_VERSION_5EX_WITH_IP,
+ lp_ctx, &response, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ /*
+ * According to MS-NRPC 2.2.1.2.1 we should set the "DS_DNS_FOREST_ROOT"
+ * (O) flag when the returned forest name is in DNS format. This is here
+ * always the case (see below).
+ */
+ response.data.nt5_ex.server_type |= DS_DNS_FOREST_ROOT;
+
+ if (r->in.flags & DS_RETURN_DNS_NAME) {
+ dc_name = response.data.nt5_ex.pdc_dns_name;
+ domain_name = response.data.nt5_ex.dns_domain;
+ /*
+ * According to MS-NRPC 2.2.1.2.1 we should set the
+ * "DS_DNS_CONTROLLER" (M) and "DS_DNS_DOMAIN" (N) flags when
+ * the returned information is in DNS form.
+ */
+ response.data.nt5_ex.server_type |=
+ DS_DNS_CONTROLLER | DS_DNS_DOMAIN;
+ } else if (r->in.flags & DS_RETURN_FLAT_NAME) {
+ dc_name = response.data.nt5_ex.pdc_name;
+ domain_name = response.data.nt5_ex.domain_name;
+ } else {
+
+ /*
+ * TODO: autodetect what we need to return
+ * based on the given arguments
+ */
+ dc_name = response.data.nt5_ex.pdc_name;
+ domain_name = response.data.nt5_ex.domain_name;
+ }
+
+ if (!dc_name || !dc_name[0]) {
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ if (!domain_name || !domain_name[0]) {
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ info = talloc(mem_ctx, struct netr_DsRGetDCNameInfo);
+ W_ERROR_HAVE_NO_MEMORY(info);
+ info->dc_unc = talloc_asprintf(mem_ctx, "%s%s",
+ dc_name[0] != '\\'? "\\\\":"",
+ talloc_strdup(mem_ctx, dc_name));
+ W_ERROR_HAVE_NO_MEMORY(info->dc_unc);
+
+ pdc_ip = local_addr;
+ if (pdc_ip == NULL) {
+ pdc_ip = "127.0.0.1";
+ }
+ info->dc_address = talloc_asprintf(mem_ctx, "\\\\%s", pdc_ip);
+ W_ERROR_HAVE_NO_MEMORY(info->dc_address);
+ info->dc_address_type = DS_ADDRESS_TYPE_INET;
+ info->domain_guid = response.data.nt5_ex.domain_uuid;
+ info->domain_name = domain_name;
+ info->forest_name = response.data.nt5_ex.forest;
+ info->dc_flags = response.data.nt5_ex.server_type;
+ if (r->in.flags & DS_RETURN_DNS_NAME) {
+ /* As MS-NRPC.pdf in 2.2.1.2.1 the DS_DNS_CONTROLLER flag should be
+ * returned if we are returning info->dc_unc containing a FQDN.
+ * This attribute is called DomainControllerName in the specs,
+ * it seems that we decide to return FQDN or netbios depending on
+ * DS_RETURN_DNS_NAME.
+ */
+ info->dc_flags |= DS_DNS_CONTROLLER;
+ }
+ info->dc_site_name = response.data.nt5_ex.server_site;
+ info->client_site_name = response.data.nt5_ex.client_site;
+
+ *r->out.info = info;
+
+ return WERR_OK;
+}
+
+static void dcesrv_netr_DsRGetDCName_base_done(struct tevent_req *subreq)
+{
+ struct dcesrv_netr_DsRGetDCName_base_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcesrv_netr_DsRGetDCName_base_state);
+ struct dcesrv_call_state *dce_call = state->dce_call;
+ NTSTATUS result, status;
+
+ status = dcerpc_wbint_DsGetDcName_recv(subreq,
+ state->mem_ctx,
+ &result);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ state->r.out.result = WERR_TIMEOUT;
+ goto finished;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR(__location__ ": IRPC callback failed %s\n",
+ nt_errstr(status));
+ state->r.out.result = WERR_GEN_FAILURE;
+ goto finished;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DBG_NOTICE("DC location via winbind failed - %s\n",
+ nt_errstr(result));
+ state->r.out.result = WERR_NO_SUCH_DOMAIN;
+ goto finished;
+ }
+
+ if (state->r.out.info == NULL || state->r.out.info[0] == NULL) {
+ DBG_ERR("DC location via winbind returned no results\n");
+ state->r.out.result = WERR_GEN_FAILURE;
+ goto finished;
+ }
+
+ if (state->r.out.info[0]->dc_unc == NULL) {
+ DBG_ERR("DC location via winbind returned no DC unc\n");
+ state->r.out.result = WERR_GEN_FAILURE;
+ goto finished;
+ }
+
+ /*
+ * Either the supplied site name is NULL (possibly via
+ * TRY_NEXT_CLOSEST_SITE) or the resulting site name matches
+ * the input match name.
+ *
+ * TODO: Currently this means that requests with NETBIOS domain
+ * names can fail because they do not return the site name.
+ */
+ if (state->r.in.site_name == NULL ||
+ strcasecmp_m("", state->r.in.site_name) == 0 ||
+ (state->r.out.info[0]->dc_site_name != NULL &&
+ strcasecmp_m(state->r.out.info[0]->dc_site_name,
+ state->r.in.site_name) == 0)) {
+
+ state->r.out.info[0]->client_site_name =
+ talloc_move(state->mem_ctx, &state->client_site);
+
+ /*
+ * Make sure to return our DC UNC with // prefix.
+ * Winbind currently doesn't send the leading slashes
+ * for some reason.
+ */
+ if (strlen(state->r.out.info[0]->dc_unc) > 2 &&
+ strncmp("\\\\", state->r.out.info[0]->dc_unc, 2) != 0) {
+ const char *dc_unc = NULL;
+
+ dc_unc = talloc_asprintf(state->mem_ctx,
+ "\\\\%s",
+ state->r.out.info[0]->dc_unc);
+ state->r.out.info[0]->dc_unc = dc_unc;
+ }
+
+ state->r.out.result = WERR_OK;
+ } else {
+ state->r.out.info = NULL;
+ state->r.out.result = WERR_NO_SUCH_DOMAIN;
+ }
+
+finished:
+ if (state->_r.dcex2 != NULL) {
+ struct netr_DsRGetDCNameEx2 *r = state->_r.dcex2;
+ r->out.result = state->r.out.result;
+ } else if (state->_r.dcex != NULL) {
+ struct netr_DsRGetDCNameEx *r = state->_r.dcex;
+ r->out.result = state->r.out.result;
+ } else if (state->_r.dc != NULL) {
+ struct netr_DsRGetDCName *r = state->_r.dc;
+ r->out.result = state->r.out.result;
+ }
+
+ TALLOC_FREE(state);
+ status = dcesrv_reply(dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": dcesrv_reply() failed - %s\n",
+ nt_errstr(status)));
+ }
+}
+
+/*
+ netr_DsRGetDCNameEx2
+*/
+static WERROR dcesrv_netr_DsRGetDCNameEx2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetDCNameEx2 *r)
+{
+ struct dcesrv_netr_DsRGetDCName_base_state *state;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_netr_DsRGetDCName_base_state);
+ if (state == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->r = *r;
+ state->_r.dcex2 = r;
+
+ return dcesrv_netr_DsRGetDCName_base_call(state);
+}
+
+/*
+ netr_DsRGetDCNameEx
+*/
+static WERROR dcesrv_netr_DsRGetDCNameEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetDCNameEx *r)
+{
+ struct dcesrv_netr_DsRGetDCName_base_state *state;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_netr_DsRGetDCName_base_state);
+ if (state == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->r.in.server_unc = r->in.server_unc;
+ state->r.in.client_account = NULL;
+ state->r.in.mask = 0;
+ state->r.in.domain_guid = r->in.domain_guid;
+ state->r.in.domain_name = r->in.domain_name;
+ state->r.in.site_name = r->in.site_name;
+ state->r.in.flags = r->in.flags;
+ state->r.out.info = r->out.info;
+
+ state->_r.dcex = r;
+
+ return dcesrv_netr_DsRGetDCName_base_call(state);
+}
+
+/*
+ * netr_DsRGetDCName
+ *
+ * This function is a predecessor to DsrGetDcNameEx2 according to [MS-NRPC].
+ * Although it has a site-guid parameter, the documentation 3.5.4.3.3 DsrGetDcName
+ * insists that it be ignored.
+ */
+static WERROR dcesrv_netr_DsRGetDCName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetDCName *r)
+{
+ struct dcesrv_netr_DsRGetDCName_base_state *state;
+
+ state = talloc_zero(mem_ctx, struct dcesrv_netr_DsRGetDCName_base_state);
+ if (state == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+
+ state->r.in.server_unc = r->in.server_unc;
+ state->r.in.client_account = NULL;
+ state->r.in.mask = 0;
+ state->r.in.domain_name = r->in.domain_name;
+ state->r.in.domain_guid = r->in.domain_guid;
+
+ state->r.in.site_name = NULL; /* this is correct, we should ignore site GUID */
+ state->r.in.flags = r->in.flags;
+ state->r.out.info = r->out.info;
+
+ state->_r.dc = r;
+
+ return dcesrv_netr_DsRGetDCName_base_call(state);
+}
+/*
+ netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN
+*/
+static WERROR dcesrv_netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NetrEnumerateTrustedDomainsEx
+*/
+static WERROR dcesrv_netr_NetrEnumerateTrustedDomainsEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NetrEnumerateTrustedDomainsEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsRAddressToSitenamesExW
+*/
+static WERROR dcesrv_netr_DsRAddressToSitenamesExW(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRAddressToSitenamesExW *r)
+{
+ struct ldb_context *sam_ctx;
+ struct netr_DsRAddressToSitenamesExWCtr *ctr;
+ sa_family_t sin_family;
+ struct sockaddr_in *addr;
+#ifdef HAVE_IPV6
+ struct sockaddr_in6 *addr6;
+ char addr_str[INET6_ADDRSTRLEN];
+#else
+ char addr_str[INET_ADDRSTRLEN];
+#endif
+ char *subnet_name;
+ const char *res;
+ uint32_t i;
+
+ sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ ctr = talloc(mem_ctx, struct netr_DsRAddressToSitenamesExWCtr);
+ W_ERROR_HAVE_NO_MEMORY(ctr);
+
+ *r->out.ctr = ctr;
+
+ ctr->count = r->in.count;
+ ctr->sitename = talloc_array(ctr, struct lsa_String, ctr->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr->sitename);
+ ctr->subnetname = talloc_array(ctr, struct lsa_String, ctr->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr->subnetname);
+
+ for (i=0; i<ctr->count; i++) {
+ ctr->sitename[i].string = NULL;
+ ctr->subnetname[i].string = NULL;
+
+ if (r->in.addresses[i].size < sizeof(sa_family_t)) {
+ continue;
+ }
+ /* The first two byte of the buffer are reserved for the
+ * "sin_family" but for now only the first one is used. */
+ sin_family = r->in.addresses[i].buffer[0];
+
+ switch (sin_family) {
+ case AF_INET:
+ if (r->in.addresses[i].size < sizeof(struct sockaddr_in)) {
+ continue;
+ }
+ addr = (struct sockaddr_in *) r->in.addresses[i].buffer;
+ res = inet_ntop(AF_INET, &addr->sin_addr,
+ addr_str, sizeof(addr_str));
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ if (r->in.addresses[i].size < sizeof(struct sockaddr_in6)) {
+ continue;
+ }
+ addr6 = (struct sockaddr_in6 *) r->in.addresses[i].buffer;
+ res = inet_ntop(AF_INET6, &addr6->sin6_addr,
+ addr_str, sizeof(addr_str));
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ if (res == NULL) {
+ continue;
+ }
+
+ ctr->sitename[i].string = samdb_client_site_name(sam_ctx,
+ mem_ctx,
+ addr_str,
+ &subnet_name,
+ true);
+ W_ERROR_HAVE_NO_MEMORY(ctr->sitename[i].string);
+ ctr->subnetname[i].string = subnet_name;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ netr_DsRAddressToSitenamesW
+*/
+static WERROR dcesrv_netr_DsRAddressToSitenamesW(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRAddressToSitenamesW *r)
+{
+ struct netr_DsRAddressToSitenamesExW r2;
+ struct netr_DsRAddressToSitenamesWCtr *ctr;
+ uint32_t i;
+ WERROR werr;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.server_name = r->in.server_name;
+ r2.in.count = r->in.count;
+ r2.in.addresses = r->in.addresses;
+
+ r2.out.ctr = talloc(mem_ctx, struct netr_DsRAddressToSitenamesExWCtr *);
+ W_ERROR_HAVE_NO_MEMORY(r2.out.ctr);
+
+ ctr = talloc(mem_ctx, struct netr_DsRAddressToSitenamesWCtr);
+ W_ERROR_HAVE_NO_MEMORY(ctr);
+
+ *r->out.ctr = ctr;
+
+ ctr->count = r->in.count;
+ ctr->sitename = talloc_array(ctr, struct lsa_String, ctr->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr->sitename);
+
+ werr = dcesrv_netr_DsRAddressToSitenamesExW(dce_call, mem_ctx, &r2);
+
+ for (i=0; i<ctr->count; i++) {
+ ctr->sitename[i].string = (*r2.out.ctr)->sitename[i].string;
+ }
+
+ return werr;
+}
+
+
+/*
+ netr_DsrGetDcSiteCoverageW
+*/
+static WERROR dcesrv_netr_DsrGetDcSiteCoverageW(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsrGetDcSiteCoverageW *r)
+{
+ struct ldb_context *sam_ctx;
+ struct DcSitesCtr *ctr;
+
+ sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ ctr = talloc(mem_ctx, struct DcSitesCtr);
+ W_ERROR_HAVE_NO_MEMORY(ctr);
+
+ *r->out.ctr = ctr;
+
+ /* For now only return our default site */
+ ctr->num_sites = 1;
+ ctr->sites = talloc_array(ctr, struct lsa_String, ctr->num_sites);
+ W_ERROR_HAVE_NO_MEMORY(ctr->sites);
+ ctr->sites[0].string = samdb_server_site_name(sam_ctx, mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(ctr->sites[0].string);
+
+ return WERR_OK;
+}
+
+
+static WERROR fill_trusted_domains_array(TALLOC_CTX *mem_ctx,
+ struct ldb_context *sam_ctx,
+ struct netr_DomainTrustList *trusts,
+ uint32_t trust_flags)
+{
+ struct ldb_dn *system_dn;
+ struct ldb_message **dom_res = NULL;
+ const char *trust_attrs[] = { "flatname", "trustPartner",
+ "securityIdentifier", "trustDirection",
+ "trustType", "trustAttributes", NULL };
+ uint32_t n;
+ int i;
+ int ret;
+
+ if (!(trust_flags & (NETR_TRUST_FLAG_INBOUND |
+ NETR_TRUST_FLAG_OUTBOUND))) {
+ return WERR_INVALID_FLAGS;
+ }
+
+ system_dn = samdb_system_container_dn(sam_ctx, mem_ctx);
+ if (system_dn == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ret = gendb_search(sam_ctx, mem_ctx, system_dn,
+ &dom_res, trust_attrs,
+ "(objectclass=trustedDomain)");
+
+ for (i = 0; i < ret; i++) {
+ unsigned int trust_dir;
+ uint32_t flags = 0;
+
+ trust_dir = ldb_msg_find_attr_as_uint(dom_res[i],
+ "trustDirection", 0);
+
+ if (trust_dir & LSA_TRUST_DIRECTION_INBOUND) {
+ flags |= NETR_TRUST_FLAG_INBOUND;
+ }
+ if (trust_dir & LSA_TRUST_DIRECTION_OUTBOUND) {
+ flags |= NETR_TRUST_FLAG_OUTBOUND;
+ }
+
+ if (!(flags & trust_flags)) {
+ /* this trust direction was not requested */
+ continue;
+ }
+
+ n = trusts->count;
+ trusts->array = talloc_realloc(trusts, trusts->array,
+ struct netr_DomainTrust,
+ n + 1);
+ W_ERROR_HAVE_NO_MEMORY(trusts->array);
+
+ trusts->array[n].netbios_name = talloc_steal(trusts->array, ldb_msg_find_attr_as_string(dom_res[i], "flatname", NULL));
+ if (!trusts->array[n].netbios_name) {
+ DEBUG(0, ("DB Error, TrustedDomain entry (%s) "
+ "without flatname\n",
+ ldb_dn_get_linearized(dom_res[i]->dn)));
+ }
+
+ trusts->array[n].dns_name = talloc_steal(trusts->array, ldb_msg_find_attr_as_string(dom_res[i], "trustPartner", NULL));
+
+ trusts->array[n].trust_flags = flags;
+ if ((trust_flags & NETR_TRUST_FLAG_IN_FOREST) &&
+ !(flags & NETR_TRUST_FLAG_TREEROOT)) {
+ /* TODO: find if we have parent in the list */
+ trusts->array[n].parent_index = 0;
+ }
+
+ trusts->array[n].trust_type =
+ ldb_msg_find_attr_as_uint(dom_res[i],
+ "trustType", 0);
+ trusts->array[n].trust_attributes =
+ ldb_msg_find_attr_as_uint(dom_res[i],
+ "trustAttributes", 0);
+
+ if ((trusts->array[n].trust_type == LSA_TRUST_TYPE_MIT) ||
+ (trusts->array[n].trust_type == LSA_TRUST_TYPE_DCE)) {
+ struct dom_sid zero_sid;
+ ZERO_STRUCT(zero_sid);
+ trusts->array[n].sid =
+ dom_sid_dup(trusts, &zero_sid);
+ } else {
+ trusts->array[n].sid =
+ samdb_result_dom_sid(trusts, dom_res[i],
+ "securityIdentifier");
+ }
+ trusts->array[n].guid = GUID_zero();
+
+ trusts->count = n + 1;
+ }
+
+ talloc_free(dom_res);
+ return WERR_OK;
+}
+
+/*
+ netr_DsrEnumerateDomainTrusts
+*/
+static WERROR dcesrv_netr_DsrEnumerateDomainTrusts(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DsrEnumerateDomainTrusts *r)
+{
+ struct netr_DomainTrustList *trusts;
+ struct ldb_context *sam_ctx;
+ int ret;
+ struct ldb_message **dom_res;
+ const char * const dom_attrs[] = { "objectSid", "objectGUID", NULL };
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ const char *dnsdomain = lpcfg_dnsdomain(lp_ctx);
+ const char *p;
+ WERROR werr;
+
+ if (r->in.trust_flags & 0xFFFFFE00) {
+ return WERR_INVALID_FLAGS;
+ }
+
+ /* TODO: turn to hard check once we are sure this is 100% correct */
+ if (!r->in.server_name) {
+ DEBUG(3, ("Invalid domain! Expected name in domain [%s]. "
+ "But received NULL!\n", dnsdomain));
+ } else {
+ p = strchr(r->in.server_name, '.');
+ if (!p) {
+ DEBUG(3, ("Invalid domain! Expected name in domain "
+ "[%s]. But received [%s]!\n",
+ dnsdomain, r->in.server_name));
+ p = r->in.server_name;
+ } else {
+ p++;
+ }
+ if (strcasecmp(p, dnsdomain)) {
+ DEBUG(3, ("Invalid domain! Expected name in domain "
+ "[%s]. But received [%s]!\n",
+ dnsdomain, r->in.server_name));
+ }
+ }
+
+ trusts = talloc_zero(mem_ctx, struct netr_DomainTrustList);
+ W_ERROR_HAVE_NO_MEMORY(trusts);
+
+ trusts->count = 0;
+ r->out.trusts = trusts;
+
+ sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return WERR_GEN_FAILURE;
+ }
+
+ if ((r->in.trust_flags & NETR_TRUST_FLAG_INBOUND) ||
+ (r->in.trust_flags & NETR_TRUST_FLAG_OUTBOUND)) {
+
+ werr = fill_trusted_domains_array(mem_ctx, sam_ctx,
+ trusts, r->in.trust_flags);
+ W_ERROR_NOT_OK_RETURN(werr);
+ }
+
+ /* NOTE: we currently are always the root of the forest */
+ if (r->in.trust_flags & NETR_TRUST_FLAG_IN_FOREST) {
+ uint32_t n = trusts->count;
+
+ ret = gendb_search_dn(sam_ctx, mem_ctx, NULL,
+ &dom_res, dom_attrs);
+ if (ret != 1) {
+ return WERR_GEN_FAILURE;
+ }
+
+ trusts->count = n + 1;
+ trusts->array = talloc_realloc(trusts, trusts->array,
+ struct netr_DomainTrust,
+ trusts->count);
+ W_ERROR_HAVE_NO_MEMORY(trusts->array);
+
+ trusts->array[n].netbios_name = lpcfg_workgroup(lp_ctx);
+ trusts->array[n].dns_name = lpcfg_dnsdomain(lp_ctx);
+ trusts->array[n].trust_flags =
+ NETR_TRUST_FLAG_NATIVE |
+ NETR_TRUST_FLAG_TREEROOT |
+ NETR_TRUST_FLAG_IN_FOREST |
+ NETR_TRUST_FLAG_PRIMARY;
+ /* we are always the root domain for now */
+ trusts->array[n].parent_index = 0;
+ trusts->array[n].trust_type = LSA_TRUST_TYPE_UPLEVEL;
+ trusts->array[n].trust_attributes = 0;
+ trusts->array[n].sid = samdb_result_dom_sid(mem_ctx,
+ dom_res[0],
+ "objectSid");
+ trusts->array[n].guid = samdb_result_guid(dom_res[0],
+ "objectGUID");
+ talloc_free(dom_res);
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ netr_DsrDeregisterDNSHostRecords
+*/
+static WERROR dcesrv_netr_DsrDeregisterDNSHostRecords(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsrDeregisterDNSHostRecords *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+static NTSTATUS dcesrv_netr_ServerGetTrustInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerGetTrustInfo *r);
+
+/*
+ netr_ServerTrustPasswordsGet
+*/
+static NTSTATUS dcesrv_netr_ServerTrustPasswordsGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerTrustPasswordsGet *r)
+{
+ struct netr_ServerGetTrustInfo r2 = {};
+ struct netr_TrustInfo *_ti = NULL;
+ NTSTATUS status;
+
+ r2.in.server_name = r->in.server_name;
+ r2.in.account_name = r->in.account_name;
+ r2.in.secure_channel_type = r->in.secure_channel_type;
+ r2.in.computer_name = r->in.computer_name;
+ r2.in.credential = r->in.credential;
+
+ r2.out.return_authenticator = r->out.return_authenticator;
+ r2.out.new_owf_password = r->out.new_owf_password;
+ r2.out.old_owf_password = r->out.old_owf_password;
+ r2.out.trust_info = &_ti;
+
+ status = dcesrv_netr_ServerGetTrustInfo(dce_call, mem_ctx, &r2);
+
+ r->out.return_authenticator = r2.out.return_authenticator;
+ r->out.new_owf_password = r2.out.new_owf_password;
+ r->out.old_owf_password = r2.out.old_owf_password;
+
+ return status;
+}
+
+/*
+ netr_DsRGetForestTrustInformation
+*/
+struct dcesrv_netr_DsRGetForestTrustInformation_state {
+ struct dcesrv_call_state *dce_call;
+ TALLOC_CTX *mem_ctx;
+ struct netr_DsRGetForestTrustInformation *r;
+};
+
+static void dcesrv_netr_DsRGetForestTrustInformation_done(struct tevent_req *subreq);
+
+static WERROR dcesrv_netr_DsRGetForestTrustInformation(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetForestTrustInformation *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+ enum security_user_level security_level;
+ struct ldb_context *sam_ctx = NULL;
+ struct dcesrv_netr_DsRGetForestTrustInformation_state *state = NULL;
+ struct dcerpc_binding_handle *irpc_handle = NULL;
+ struct tevent_req *subreq = NULL;
+ struct ldb_dn *domain_dn = NULL;
+ struct ldb_dn *forest_dn = NULL;
+ int cmp;
+ int forest_level;
+
+ security_level = security_session_user_level(session_info, NULL);
+ if (security_level < SECURITY_USER) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (r->in.flags & 0xFFFFFFFE) {
+ return WERR_INVALID_FLAGS;
+ }
+
+ sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return WERR_GEN_FAILURE;
+ }
+
+ domain_dn = ldb_get_default_basedn(sam_ctx);
+ if (domain_dn == NULL) {
+ return WERR_GEN_FAILURE;
+ }
+
+ forest_dn = ldb_get_root_basedn(sam_ctx);
+ if (forest_dn == NULL) {
+ return WERR_GEN_FAILURE;
+ }
+
+ cmp = ldb_dn_compare(domain_dn, forest_dn);
+ if (cmp != 0) {
+ return WERR_NERR_ACFNOTLOADED;
+ }
+
+ forest_level = dsdb_forest_functional_level(sam_ctx);
+ if (forest_level < DS_DOMAIN_FUNCTION_2003) {
+ return WERR_INVALID_FUNCTION;
+ }
+
+ if (r->in.flags & DS_GFTI_UPDATE_TDO) {
+ if (!samdb_is_pdc(sam_ctx)) {
+ return WERR_NERR_NOTPRIMARY;
+ }
+
+ if (r->in.trusted_domain_name == NULL) {
+ return WERR_INVALID_FLAGS;
+ }
+ }
+
+ if (r->in.trusted_domain_name == NULL) {
+ NTSTATUS status;
+
+ /*
+ * information about our own domain
+ */
+ status = dsdb_trust_xref_forest_info(mem_ctx, sam_ctx,
+ r->out.forest_trust_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return WERR_OK;
+ }
+
+ /*
+ * Forward the request to winbindd
+ */
+
+ state = talloc_zero(mem_ctx,
+ struct dcesrv_netr_DsRGetForestTrustInformation_state);
+ if (state == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->dce_call = dce_call;
+ state->mem_ctx = mem_ctx;
+ state->r = r;
+
+ irpc_handle = irpc_binding_handle_by_name(state,
+ imsg_ctx,
+ "winbind_server",
+ &ndr_table_winbind);
+ if (irpc_handle == NULL) {
+ DEBUG(0,("Failed to get binding_handle for winbind_server task\n"));
+ state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ return WERR_SERVICE_NOT_FOUND;
+ }
+
+ /*
+ * 60 seconds timeout should be enough
+ */
+ dcerpc_binding_handle_set_timeout(irpc_handle, 60);
+
+ subreq = dcerpc_winbind_GetForestTrustInformation_send(state,
+ state->dce_call->event_ctx,
+ irpc_handle,
+ r->in.trusted_domain_name,
+ r->in.flags,
+ r->out.forest_trust_info);
+ if (subreq == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ tevent_req_set_callback(subreq,
+ dcesrv_netr_DsRGetForestTrustInformation_done,
+ state);
+
+ return WERR_OK;
+}
+
+static void dcesrv_netr_DsRGetForestTrustInformation_done(struct tevent_req *subreq)
+{
+ struct dcesrv_netr_DsRGetForestTrustInformation_state *state =
+ tevent_req_callback_data(subreq,
+ struct dcesrv_netr_DsRGetForestTrustInformation_state);
+ NTSTATUS status;
+
+ status = dcerpc_winbind_GetForestTrustInformation_recv(subreq,
+ state->mem_ctx,
+ &state->r->out.result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ state->r->out.result = WERR_TIMEOUT;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ DEBUG(0,(__location__ ": IRPC callback failed %s\n",
+ nt_errstr(status)));
+ }
+
+ status = dcesrv_reply(state->dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": dcesrv_reply() failed - %s\n", nt_errstr(status)));
+ }
+}
+
+/*
+ netr_GetForestTrustInformation
+*/
+static NTSTATUS dcesrv_netr_GetForestTrustInformation(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct netr_GetForestTrustInformation *r)
+{
+ struct netlogon_creds_CredentialState *creds = NULL;
+ struct ldb_context *sam_ctx = NULL;
+ struct ldb_dn *domain_dn = NULL;
+ struct ldb_dn *forest_dn = NULL;
+ int cmp;
+ int forest_level;
+ NTSTATUS status;
+
+ status = dcesrv_netr_creds_server_step_check(dce_call,
+ mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if ((creds->secure_channel_type != SEC_CHAN_DNS_DOMAIN) &&
+ (creds->secure_channel_type != SEC_CHAN_DOMAIN)) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* TODO: check r->in.server_name is our name */
+
+ domain_dn = ldb_get_default_basedn(sam_ctx);
+ if (domain_dn == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ forest_dn = ldb_get_root_basedn(sam_ctx);
+ if (forest_dn == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ cmp = ldb_dn_compare(domain_dn, forest_dn);
+ if (cmp != 0) {
+ return NT_STATUS_INVALID_DOMAIN_STATE;
+ }
+
+ forest_level = dsdb_forest_functional_level(sam_ctx);
+ if (forest_level < DS_DOMAIN_FUNCTION_2003) {
+ return NT_STATUS_INVALID_DOMAIN_STATE;
+ }
+
+ status = dsdb_trust_xref_forest_info(mem_ctx, sam_ctx,
+ r->out.forest_trust_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ netr_ServerGetTrustInfo
+*/
+static NTSTATUS dcesrv_netr_ServerGetTrustInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerGetTrustInfo *r)
+{
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ struct ldb_context *sam_ctx = NULL;
+ const char * const attrs[] = {
+ "unicodePwd",
+ "sAMAccountName",
+ "userAccountControl",
+ NULL
+ };
+ struct ldb_message **res = NULL;
+ struct samr_Password *curNtHash = NULL, *prevNtHash = NULL;
+ NTSTATUS nt_status;
+ int ret;
+ const char *asid = NULL;
+ uint32_t uac = 0;
+ const char *aname = NULL;
+ struct ldb_message *tdo_msg = NULL;
+ const char * const tdo_attrs[] = {
+ "trustAuthIncoming",
+ "trustAttributes",
+ NULL
+ };
+ struct netr_TrustInfo *trust_info = NULL;
+
+ ZERO_STRUCTP(r->out.new_owf_password);
+ ZERO_STRUCTP(r->out.old_owf_password);
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call,
+ mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ /* TODO: check r->in.server_name is our name */
+
+ if (strcasecmp_m(r->in.account_name, creds->account_name) != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.secure_channel_type != creds->secure_channel_type) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strcasecmp_m(r->in.computer_name, creds->computer_name) != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ asid = ldap_encode_ndr_dom_sid(mem_ctx, creds->sid);
+ if (asid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = gendb_search(sam_ctx, mem_ctx, NULL, &res, attrs,
+ "(&(objectClass=user)(objectSid=%s))",
+ asid);
+ if (ret != 1) {
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ switch (creds->secure_channel_type) {
+ case SEC_CHAN_DNS_DOMAIN:
+ case SEC_CHAN_DOMAIN:
+ uac = ldb_msg_find_attr_as_uint(res[0], "userAccountControl", 0);
+
+ if (uac & UF_ACCOUNTDISABLE) {
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ if (!(uac & UF_INTERDOMAIN_TRUST_ACCOUNT)) {
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ aname = ldb_msg_find_attr_as_string(res[0], "sAMAccountName", NULL);
+ if (aname == NULL) {
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ nt_status = dsdb_trust_search_tdo_by_type(sam_ctx,
+ SEC_CHAN_DOMAIN, aname,
+ tdo_attrs, mem_ctx, &tdo_msg);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ nt_status = dsdb_trust_get_incoming_passwords(tdo_msg, mem_ctx,
+ &curNtHash,
+ &prevNtHash);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ trust_info = talloc_zero(mem_ctx, struct netr_TrustInfo);
+ if (trust_info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ trust_info->count = 1;
+ trust_info->data = talloc_array(trust_info, uint32_t,
+ trust_info->count);
+ if (trust_info->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ trust_info->data[0] = ldb_msg_find_attr_as_uint(tdo_msg,
+ "trustAttributes",
+ 0);
+ break;
+
+ default:
+ nt_status = samdb_result_passwords_no_lockout(mem_ctx, lp_ctx,
+ res[0],
+ &curNtHash);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ prevNtHash = talloc(mem_ctx, struct samr_Password);
+ if (prevNtHash == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ E_md4hash("", prevNtHash->hash);
+ break;
+ }
+
+ if (curNtHash != NULL) {
+ *r->out.new_owf_password = *curNtHash;
+ nt_status = netlogon_creds_des_encrypt(creds, r->out.new_owf_password);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ }
+ if (prevNtHash != NULL) {
+ *r->out.old_owf_password = *prevNtHash;
+ nt_status = netlogon_creds_des_encrypt(creds, r->out.old_owf_password);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ }
+
+ if (trust_info != NULL) {
+ *r->out.trust_info = trust_info;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ netr_Unused47
+*/
+static NTSTATUS dcesrv_netr_Unused47(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_Unused47 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+struct netr_dnsupdate_RODC_state {
+ struct dcesrv_call_state *dce_call;
+ struct netr_DsrUpdateReadOnlyServerDnsRecords *r;
+ struct dnsupdate_RODC *r2;
+};
+
+/*
+ called when the forwarded RODC dns update request is finished
+ */
+static void netr_dnsupdate_RODC_callback(struct tevent_req *subreq)
+{
+ struct netr_dnsupdate_RODC_state *st =
+ tevent_req_callback_data(subreq,
+ struct netr_dnsupdate_RODC_state);
+ NTSTATUS status;
+
+ status = dcerpc_dnsupdate_RODC_r_recv(subreq, st->dce_call);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": IRPC callback failed %s\n", nt_errstr(status)));
+ st->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ }
+
+ st->r->out.dns_names = talloc_steal(st->dce_call, st->r2->out.dns_names);
+
+ status = dcesrv_reply(st->dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": dcesrv_reply() failed - %s\n", nt_errstr(status)));
+ }
+}
+
+/*
+ netr_DsrUpdateReadOnlyServerDnsRecords
+*/
+static NTSTATUS dcesrv_netr_DsrUpdateReadOnlyServerDnsRecords(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DsrUpdateReadOnlyServerDnsRecords *r)
+{
+ struct netlogon_creds_CredentialState *creds;
+ NTSTATUS nt_status;
+ struct dcerpc_binding_handle *binding_handle;
+ struct netr_dnsupdate_RODC_state *st;
+ struct tevent_req *subreq;
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call,
+ mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ if (creds->secure_channel_type != SEC_CHAN_RODC) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ st = talloc_zero(mem_ctx, struct netr_dnsupdate_RODC_state);
+ NT_STATUS_HAVE_NO_MEMORY(st);
+
+ st->dce_call = dce_call;
+ st->r = r;
+ st->r2 = talloc_zero(st, struct dnsupdate_RODC);
+ NT_STATUS_HAVE_NO_MEMORY(st->r2);
+
+ st->r2->in.dom_sid = creds->sid;
+ st->r2->in.site_name = r->in.site_name;
+ st->r2->in.dns_ttl = r->in.dns_ttl;
+ st->r2->in.dns_names = r->in.dns_names;
+ st->r2->out.dns_names = r->out.dns_names;
+
+ binding_handle = irpc_binding_handle_by_name(st,
+ imsg_ctx,
+ "dnsupdate",
+ &ndr_table_irpc);
+ if (binding_handle == NULL) {
+ DEBUG(0,("Failed to get binding_handle for dnsupdate task\n"));
+ dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* forward the call */
+ subreq = dcerpc_dnsupdate_RODC_r_send(st, dce_call->event_ctx,
+ binding_handle, st->r2);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+
+ dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+
+ /* setup the callback */
+ tevent_req_set_callback(subreq, netr_dnsupdate_RODC_callback, st);
+
+ return NT_STATUS_OK;
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_netlogon_s.c"
diff --git a/source4/rpc_server/remote/README b/source4/rpc_server/remote/README
new file mode 100644
index 0000000..0d235a9
--- /dev/null
+++ b/source4/rpc_server/remote/README
@@ -0,0 +1,38 @@
+This is an RPC backend that implements all operations in terms of
+remote RPC operations. This may be useful in certain debugging
+situations, where the traffic is encrypted, or you wish to validate
+that IDL is correct before implementing full test clients, or with
+windows clients.
+
+There are two modes of operation: Password specified and delegated
+credentials.
+
+Password specified:
+-------------------
+
+This uses a static username/password in the config file, example:
+
+[global]
+ dcerpc endpoint servers = remote
+ dcerpc_remote:binding = ncacn_np:win2003
+ dcerpc_remote:username = administrator
+ dcerpc_remote:password = PASSWORD
+ dcerpc_remote:interfaces = samr, lsarpc, netlogon
+
+Delegated credentials:
+----------------------
+
+If your incoming user is authenticated with Kerberos, and the machine
+account for this Samba4 proxy server is 'trusted for delegation', then
+the Samba4 proxy can forward the client's credentials to the target.
+
+You must be joined to the domain (net join <domain> member).
+
+To set 'trusted for delegation' with MMC, see the checkbox in the
+Computer account property page under Users and Computers.
+
+[global]
+ dcerpc endpoint servers = remote
+ dcerpc_remote:binding = ncacn_np:win2003
+ dcerpc_remote:interfaces = samr, lsarpc, netlogon
+
diff --git a/source4/rpc_server/remote/dcesrv_remote.c b/source4/rpc_server/remote/dcesrv_remote.c
new file mode 100644
index 0000000..a57f2a7
--- /dev/null
+++ b/source4/rpc_server/remote/dcesrv_remote.c
@@ -0,0 +1,528 @@
+/*
+ Unix SMB/CIFS implementation.
+ remote dcerpc operations
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Julien Kerihuel 2008-2009
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2010
+
+ 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 "includes.h"
+#include <tevent.h>
+#include "rpc_server/dcerpc_server.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/ndr/ndr_table.h"
+#include "param/param.h"
+
+NTSTATUS dcerpc_server_remote_init(TALLOC_CTX *ctx);
+
+#define DCESRV_REMOTE_ASSOC_MAGIC 0x782f50c4
+struct dcesrv_remote_assoc {
+ uint32_t assoc_group_id;
+};
+
+#define DCESRV_REMOTE_PRIVATE_MAGIC 0x7eceafa6
+struct dcesrv_remote_private {
+ struct dcerpc_pipe *c_pipe;
+};
+
+static NTSTATUS remote_op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)
+{
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_op_bind(struct dcesrv_connection_context *context,
+ const struct dcesrv_interface *iface)
+{
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_get_private(struct dcesrv_call_state *dce_call,
+ struct dcesrv_remote_private **_priv)
+{
+ const struct ndr_interface_table *table =
+ (const struct ndr_interface_table *)dce_call->context->iface->private_data;
+ struct dcesrv_remote_private *priv = NULL;
+ struct dcesrv_remote_assoc *assoc = NULL;
+ const char *binding = NULL;
+ const char *user, *pass, *domain;
+ struct cli_credentials *credentials;
+ bool must_free_credentials = false;
+ bool machine_account;
+ bool allow_anonymous;
+ struct dcerpc_binding *b;
+ struct composite_context *pipe_conn_req;
+ uint32_t flags = 0;
+ NTSTATUS status;
+
+ priv = dcesrv_iface_state_find_conn(dce_call,
+ DCESRV_REMOTE_PRIVATE_MAGIC,
+ struct dcesrv_remote_private);
+ if (priv != NULL) {
+ *_priv = priv;
+ return NT_STATUS_OK;
+ }
+
+ priv = talloc_zero(dce_call, struct dcesrv_remote_private);
+ if (priv == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ assoc = dcesrv_iface_state_find_assoc(dce_call,
+ DCESRV_REMOTE_ASSOC_MAGIC,
+ struct dcesrv_remote_assoc);
+ if (assoc == NULL) {
+ assoc = talloc_zero(dce_call, struct dcesrv_remote_assoc);
+ if (assoc == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ binding = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx,
+ NULL,
+ "dcerpc_remote",
+ "binding");
+ if (binding == NULL) {
+ DEBUG(0,("You must specify a DCE/RPC binding string\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ user = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "dcerpc_remote", "user");
+ pass = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "dcerpc_remote", "password");
+ domain = lpcfg_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "dceprc_remote", "domain");
+
+ machine_account = lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx,
+ NULL,
+ "dcerpc_remote",
+ "use_machine_account",
+ false);
+ allow_anonymous = lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx,
+ NULL,
+ "dcerpc_remote",
+ "allow_anonymous_fallback",
+ false);
+
+ credentials = dcesrv_call_credentials(dce_call);
+
+ if (user && pass) {
+ bool ok;
+
+ DEBUG(5, ("dcerpc_remote: RPC Proxy: Using specified account\n"));
+ credentials = cli_credentials_init(priv);
+ if (!credentials) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ must_free_credentials = true;
+
+ ok = cli_credentials_set_conf(credentials,
+ dce_call->conn->dce_ctx->lp_ctx);
+ if (!ok) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ cli_credentials_set_username(credentials, user, CRED_SPECIFIED);
+ if (domain) {
+ cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED);
+ }
+ cli_credentials_set_password(credentials, pass, CRED_SPECIFIED);
+ } else if (machine_account) {
+ DEBUG(5, ("dcerpc_remote: RPC Proxy: Using machine account\n"));
+ credentials = cli_credentials_init_server(
+ priv,
+ dce_call->conn->dce_ctx->lp_ctx);
+ if (!credentials) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ must_free_credentials = true;
+ } else if (credentials != NULL) {
+ DEBUG(5, ("dcerpc_remote: RPC Proxy: Using delegated credentials\n"));
+ } else if (allow_anonymous) {
+ DEBUG(5, ("dcerpc_remote: RPC Proxy: Using anonymous\n"));
+ credentials = cli_credentials_init_anon(priv);
+ if (!credentials) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ must_free_credentials = true;
+ } else {
+ DEBUG(1,("dcerpc_remote: RPC Proxy: You must supply binding, user and password or have delegated credentials\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* parse binding string to the structure */
+ status = dcerpc_parse_binding(priv, binding, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to parse dcerpc binding '%s'\n", binding));
+ return status;
+ }
+
+ /* If we already have a remote association group ID, then use that */
+ if (assoc->assoc_group_id != 0) {
+ status = dcerpc_binding_set_assoc_group_id(b,
+ assoc->assoc_group_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dcerpc_binding_set_assoc_group_id() - %s'\n",
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ status = dcerpc_binding_set_abstract_syntax(b, &table->syntax_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dcerpc_binding_set_abstract_syntax() - %s'\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (dce_call->conn->state_flags & DCESRV_CALL_STATE_FLAG_MULTIPLEXED) {
+ status = dcerpc_binding_set_flags(b, DCERPC_CONCURRENT_MULTIPLEX, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dcerpc_binding_set_flags(CONC_MPX) - %s'\n",
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ DEBUG(3, ("Using binding %s\n", dcerpc_binding_string(dce_call->context, b)));
+
+ pipe_conn_req = dcerpc_pipe_connect_b_send(priv, b, table,
+ credentials, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx);
+ status = dcerpc_pipe_connect_b_recv(pipe_conn_req, priv, &(priv->c_pipe));
+
+ if (must_free_credentials) {
+ talloc_free(credentials);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (dce_call->conn->state_flags & DCESRV_CALL_STATE_FLAG_MULTIPLEXED) {
+ flags = dcerpc_binding_get_flags(priv->c_pipe->binding);
+ if (!(flags & DCERPC_CONCURRENT_MULTIPLEX)) {
+ DEBUG(1,("dcerpc_remote: RPC Proxy: "
+ "Remote server doesn't support MPX\n"));
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+
+ if (assoc->assoc_group_id == 0) {
+ assoc->assoc_group_id =
+ dcerpc_binding_get_assoc_group_id(priv->c_pipe->binding);
+ if (assoc->assoc_group_id == 0) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ status = dcesrv_iface_state_store_assoc(dce_call,
+ DCESRV_REMOTE_ASSOC_MAGIC,
+ assoc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ status = dcesrv_iface_state_store_conn(dce_call,
+ DCESRV_REMOTE_PRIVATE_MAGIC,
+ priv);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *_priv = priv;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_op_ndr_pull(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_pull *pull, void **r)
+{
+ enum ndr_err_code ndr_err;
+ const struct ndr_interface_table *table = (const struct ndr_interface_table *)dce_call->context->iface->private_data;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+
+ dce_call->fault_code = 0;
+
+ if (opnum >= table->num_calls) {
+ dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ /*
+ * We don't have support for calls with pipes.
+ */
+ if (table->calls[opnum].in_pipes.num_pipes != 0) {
+ dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+ if (table->calls[opnum].out_pipes.num_pipes != 0) {
+ dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ *r = talloc_size(mem_ctx, table->calls[opnum].struct_size);
+ if (!*r) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* unravel the NDR for the packet */
+ ndr_err = table->calls[opnum].ndr_pull(pull, NDR_IN, *r);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ dce_call->fault_code = DCERPC_FAULT_NDR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void remote_op_dispatch_done(struct tevent_req *subreq);
+
+struct dcesrv_remote_call {
+ struct dcesrv_call_state *dce_call;
+ struct dcesrv_remote_private *priv;
+};
+
+static NTSTATUS remote_op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)
+{
+ struct dcesrv_remote_call *rcall = NULL;
+ struct dcesrv_remote_private *priv = NULL;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+ const struct ndr_interface_table *table = dce_call->context->iface->private_data;
+ const struct ndr_interface_call *call;
+ const char *name;
+ struct tevent_req *subreq;
+ NTSTATUS status;
+
+ name = table->calls[opnum].name;
+ call = &table->calls[opnum];
+
+ status = remote_get_private(dce_call, &priv);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_remote: call[%s] %s\n", name, nt_errstr(status)));
+ return status;
+ }
+
+ rcall = talloc_zero(dce_call, struct dcesrv_remote_call);
+ if (rcall == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ rcall->dce_call = dce_call;
+ rcall->priv = priv;
+
+ if (priv->c_pipe->conn->flags & DCERPC_DEBUG_PRINT_IN) {
+ ndr_print_function_debug(call->ndr_print, name, NDR_IN | NDR_SET_VALUES, r);
+ }
+
+ priv->c_pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;
+
+ /* we didn't use the return code of this function as we only check the last_fault_code */
+ subreq = dcerpc_binding_handle_call_send(rcall, dce_call->event_ctx,
+ priv->c_pipe->binding_handle,
+ NULL, table,
+ opnum, mem_ctx, r);
+ if (subreq == NULL) {
+ DEBUG(0,("dcesrv_remote: call[%s] dcerpc_binding_handle_call_send() failed!\n", name));
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(subreq, remote_op_dispatch_done, rcall);
+
+ dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void remote_op_dispatch_done(struct tevent_req *subreq)
+{
+ struct dcesrv_remote_call *rcall =
+ tevent_req_callback_data(subreq,
+ struct dcesrv_remote_call);
+ struct dcesrv_call_state *dce_call = rcall->dce_call;
+ struct dcesrv_remote_private *priv = rcall->priv;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+ const struct ndr_interface_table *table = dce_call->context->iface->private_data;
+ const struct ndr_interface_call *call;
+ const char *name;
+ NTSTATUS status;
+
+ name = table->calls[opnum].name;
+ call = &table->calls[opnum];
+
+ /* we didn't use the return code of this function as we only check the last_fault_code */
+ status = dcerpc_binding_handle_call_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ dce_call->fault_code = priv->c_pipe->last_fault_code;
+ if (dce_call->fault_code != 0) {
+ DEBUG(0,("dcesrv_remote: call[%s] failed with: %s!\n",
+ name, dcerpc_errstr(dce_call, dce_call->fault_code)));
+ goto reply;
+ }
+
+ if (NT_STATUS_IS_OK(status) &&
+ (priv->c_pipe->conn->flags & DCERPC_DEBUG_PRINT_OUT)) {
+ ndr_print_function_debug(call->ndr_print, name, NDR_OUT, dce_call->r);
+ }
+
+reply:
+ status = dcesrv_reply(dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_remote: call[%s]: dcesrv_reply() failed - %s\n",
+ name, nt_errstr(status)));
+ }
+}
+
+static NTSTATUS remote_op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_push *push, const void *r)
+{
+ enum ndr_err_code ndr_err;
+ const struct ndr_interface_table *table = dce_call->context->iface->private_data;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+
+ /* unravel the NDR for the packet */
+ ndr_err = table->calls[opnum].ndr_push(push, NDR_OUT, r);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ dce_call->fault_code = DCERPC_FAULT_NDR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_register_one_iface(struct dcesrv_context *dce_ctx, const struct dcesrv_interface *iface)
+{
+ unsigned int i;
+ const struct ndr_interface_table *table = iface->private_data;
+
+ for (i=0;i<table->endpoints->count;i++) {
+ NTSTATUS ret;
+ const char *name = table->endpoints->names[i];
+
+ ret = dcesrv_interface_register(dce_ctx, name, NULL, iface, NULL);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1,("remote_op_init_server: failed to register endpoint '%s'\n",name));
+ return ret;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server)
+{
+ unsigned int i;
+ char **ifaces = str_list_make(dce_ctx, lpcfg_parm_string(dce_ctx->lp_ctx, NULL, "dcerpc_remote", "interfaces"),NULL);
+
+ if (!ifaces) {
+ DEBUG(3,("remote_op_init_server: no interfaces configured\n"));
+ return NT_STATUS_OK;
+ }
+
+ for (i=0;ifaces[i];i++) {
+ NTSTATUS ret;
+ struct dcesrv_interface iface;
+
+ if (!ep_server->interface_by_name(&iface, ifaces[i])) {
+ DEBUG(0,("remote_op_init_server: failed to find interface = '%s'\n", ifaces[i]));
+ talloc_free(ifaces);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ret = remote_register_one_iface(dce_ctx, &iface);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("remote_op_init_server: failed to register interface = '%s'\n", ifaces[i]));
+ talloc_free(ifaces);
+ return ret;
+ }
+ }
+
+ talloc_free(ifaces);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_op_shutdown_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ return NT_STATUS_OK;
+}
+
+static bool remote_fill_interface(struct dcesrv_interface *iface, const struct ndr_interface_table *if_tabl)
+{
+ iface->name = if_tabl->name;
+ iface->syntax_id = if_tabl->syntax_id;
+
+ iface->bind = remote_op_bind;
+ iface->unbind = NULL;
+
+ iface->ndr_pull = remote_op_ndr_pull;
+ iface->dispatch = remote_op_dispatch;
+ iface->reply = remote_op_reply;
+ iface->ndr_push = remote_op_ndr_push;
+
+ iface->private_data = if_tabl;
+ iface->flags = 0;
+
+ return true;
+}
+
+static bool remote_op_interface_by_uuid(struct dcesrv_interface *iface, const struct GUID *uuid, uint32_t if_version)
+{
+ const struct ndr_interface_list *l;
+
+ for (l=ndr_table_list();l;l=l->next) {
+ if (l->table->syntax_id.if_version == if_version &&
+ GUID_equal(&l->table->syntax_id.uuid, uuid)==0) {
+ return remote_fill_interface(iface, l->table);
+ }
+ }
+
+ return false;
+}
+
+static bool remote_op_interface_by_name(struct dcesrv_interface *iface, const char *name)
+{
+ const struct ndr_interface_table *tbl = ndr_table_by_name(name);
+
+ if (tbl)
+ return remote_fill_interface(iface, tbl);
+
+ return false;
+}
+
+NTSTATUS dcerpc_server_remote_init(TALLOC_CTX *ctx)
+{
+ NTSTATUS ret;
+ static const struct dcesrv_endpoint_server ep_server = {
+ /* fill in our name */
+ .name = "remote",
+
+ .initialized = false,
+
+ /* fill in all the operations */
+ .init_server = remote_op_init_server,
+ .shutdown_server = remote_op_shutdown_server,
+
+ .interface_by_uuid = remote_op_interface_by_uuid,
+ .interface_by_name = remote_op_interface_by_name
+ };
+
+ /* register ourselves with the DCERPC subsystem. */
+ ret = dcerpc_register_ep_server(&ep_server);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'remote' endpoint server!\n"));
+ return ret;
+ }
+
+ /* We need the full DCE/RPC interface table */
+ ndr_table_init();
+
+ return ret;
+}
diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c
new file mode 100644
index 0000000..b1342cb
--- /dev/null
+++ b/source4/rpc_server/samr/dcesrv_samr.c
@@ -0,0 +1,5365 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the samr pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+ Copyright (C) Matthias Dieter Wallnöfer 2009
+
+ 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 "includes.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/samr/dcesrv_samr.h"
+#include "system/time.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "../libds/common/flags.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "libcli/security/security.h"
+#include "rpc_server/samr/proto.h"
+#include "../lib/util/util_ldb.h"
+#include "param/param.h"
+#include "lib/util/tsort.h"
+#include "libds/common/flag_mapping.h"
+
+#undef strcasecmp
+
+#define DCESRV_INTERFACE_SAMR_BIND(context, iface) \
+ dcesrv_interface_samr_bind(context, iface)
+static NTSTATUS dcesrv_interface_samr_bind(struct dcesrv_connection_context *context,
+ const struct dcesrv_interface *iface)
+{
+ return dcesrv_interface_bind_reject_connect(context, iface);
+}
+
+/* these query macros make samr_Query[User|Group|Alias]Info a bit easier to read */
+
+#define QUERY_STRING(msg, field, attr) \
+ info->field.string = ldb_msg_find_attr_as_string(msg, attr, "");
+#define QUERY_UINT(msg, field, attr) \
+ info->field = ldb_msg_find_attr_as_uint(msg, attr, 0);
+#define QUERY_RID(msg, field, attr) \
+ info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0);
+#define QUERY_UINT64(msg, field, attr) \
+ info->field = ldb_msg_find_attr_as_uint64(msg, attr, 0);
+#define QUERY_APASSC(msg, field, attr) \
+ info->field = samdb_result_allow_password_change(sam_ctx, mem_ctx, \
+ a_state->domain_state->domain_dn, msg, attr);
+#define QUERY_BPWDCT(msg, field, attr) \
+ info->field = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx, \
+ a_state->domain_state->domain_dn, msg);
+#define QUERY_LHOURS(msg, field, attr) \
+ info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
+#define QUERY_AFLAGS(msg, field, attr) \
+ info->field = samdb_result_acct_flags(msg, attr);
+
+
+/* these are used to make the Set[User|Group]Info code easier to follow */
+
+#define SET_STRING(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
+ if (r->in.info->field.string[0] == '\0') { \
+ if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL) != LDB_SUCCESS) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ } \
+ if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != LDB_SUCCESS) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_UINT(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_INT64(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_UINT64(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+/* Set account flags, discarding flags that cannot be set with SAMR */
+#define SET_AFLAGS(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_LHOURS(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_PARAMETERS(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (r->in.info->field.length != 0) { \
+ if (samdb_msg_add_parameters(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+ } \
+} while (0)
+
+/*
+ * Clear a GUID cache
+ */
+static void clear_guid_cache(struct samr_guid_cache *cache)
+{
+ cache->handle = 0;
+ cache->size = 0;
+ TALLOC_FREE(cache->entries);
+}
+
+/*
+ * initialize a GUID cache
+ */
+static void initialize_guid_cache(struct samr_guid_cache *cache)
+{
+ cache->handle = 0;
+ cache->size = 0;
+ cache->entries = NULL;
+}
+
+static NTSTATUS load_guid_cache(
+ struct samr_guid_cache *cache,
+ struct samr_domain_state *d_state,
+ unsigned int ldb_cnt,
+ struct ldb_message **res)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ unsigned int i;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ clear_guid_cache(cache);
+
+ /*
+ * Store the GUID's in the cache.
+ */
+ cache->handle = 0;
+ cache->size = ldb_cnt;
+ cache->entries = talloc_array(d_state, struct GUID, ldb_cnt);
+ if (cache->entries == NULL) {
+ clear_guid_cache(cache);
+ status = NT_STATUS_NO_MEMORY;
+ goto exit;
+ }
+
+ /*
+ * Extract a list of the GUIDs for all the matching objects
+ * we cache just the GUIDS to reduce the memory overhead of
+ * the result cache.
+ */
+ for (i = 0; i < ldb_cnt; i++) {
+ cache->entries[i] = samdb_result_guid(res[i], "objectGUID");
+ }
+exit:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/*
+ samr_Connect
+
+ create a connection to the SAM database
+*/
+static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect *r)
+{
+ struct samr_connect_state *c_state;
+ struct dcesrv_handle *handle;
+
+ ZERO_STRUCTP(r->out.connect_handle);
+
+ c_state = talloc(mem_ctx, struct samr_connect_state);
+ if (!c_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* make sure the sam database is accessible */
+ c_state->sam_ctx = dcesrv_samdb_connect_as_user(c_state, dce_call);
+ if (c_state->sam_ctx == NULL) {
+ talloc_free(c_state);
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_CONNECT);
+ if (!handle) {
+ talloc_free(c_state);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, c_state);
+
+ c_state->access_mask = r->in.access_mask;
+ *r->out.connect_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_Close
+*/
+static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Close *r)
+{
+ struct dcesrv_handle *h;
+
+ *r->out.handle = *r->in.handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetSecurity
+*/
+static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetSecurity *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_QuerySecurity
+*/
+static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QuerySecurity *r)
+{
+ struct dcesrv_handle *h;
+ struct sec_desc_buf *sd;
+
+ *r->out.sdbuf = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ sd = talloc(mem_ctx, struct sec_desc_buf);
+ if (sd == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sd->sd = samdb_default_security_descriptor(mem_ctx);
+
+ *r->out.sdbuf = sd;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_Shutdown
+
+ we refuse this operation completely. If a admin wants to shutdown samr
+ in Samba then they should use the samba admin tools to disable the samr pipe
+*/
+static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Shutdown *r)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+ samr_LookupDomain
+
+ this maps from a domain name to a SID
+*/
+static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_LookupDomain *r)
+{
+ struct samr_connect_state *c_state;
+ struct dcesrv_handle *h;
+ struct dom_sid *sid;
+ const char * const dom_attrs[] = { "objectSid", NULL};
+ struct ldb_message **dom_msgs;
+ int ret;
+
+ *r->out.sid = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
+
+ c_state = h->data;
+
+ if (r->in.domain_name->string == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
+ ret = gendb_search(c_state->sam_ctx,
+ mem_ctx, NULL, &dom_msgs, dom_attrs,
+ "(objectClass=builtinDomain)");
+ } else if (strcasecmp_m(r->in.domain_name->string, lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
+ ret = gendb_search_dn(c_state->sam_ctx,
+ mem_ctx, ldb_get_default_basedn(c_state->sam_ctx),
+ &dom_msgs, dom_attrs);
+ } else {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ if (ret != 1) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
+ "objectSid");
+
+ if (sid == NULL) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ *r->out.sid = sid;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_EnumDomains
+
+ list the domains in the SAM
+*/
+static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_EnumDomains *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_SamArray *array;
+ uint32_t i, start_i;
+
+ *r->out.resume_handle = 0;
+ *r->out.sam = NULL;
+ *r->out.num_entries = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
+
+ *r->out.resume_handle = 2;
+
+ start_i = *r->in.resume_handle;
+
+ if (start_i >= 2) {
+ /* search past end of list is not an error for this call */
+ return NT_STATUS_OK;
+ }
+
+ array = talloc(mem_ctx, struct samr_SamArray);
+ if (array == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ array->count = 0;
+ array->entries = NULL;
+
+ array->entries = talloc_array(mem_ctx, struct samr_SamEntry, 2 - start_i);
+ if (array->entries == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<2-start_i;i++) {
+ array->entries[i].idx = start_i + i;
+ if (i == 0) {
+ array->entries[i].name.string = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
+ } else {
+ array->entries[i].name.string = "BUILTIN";
+ }
+ }
+
+ *r->out.sam = array;
+ *r->out.num_entries = i;
+ array->count = *r->out.num_entries;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_OpenDomain
+*/
+static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OpenDomain *r)
+{
+ struct dcesrv_handle *h_conn, *h_domain;
+ struct samr_connect_state *c_state;
+ struct samr_domain_state *d_state;
+ const char * const dom_attrs[] = { "cn", NULL};
+ struct ldb_message **dom_msgs;
+ int ret;
+ unsigned int i;
+
+ ZERO_STRUCTP(r->out.domain_handle);
+
+ DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
+
+ c_state = h_conn->data;
+
+ if (r->in.sid == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ d_state = talloc(mem_ctx, struct samr_domain_state);
+ if (!d_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ d_state->domain_sid = talloc_steal(d_state, r->in.sid);
+
+ if (dom_sid_equal(d_state->domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
+ d_state->builtin = true;
+ d_state->domain_name = "BUILTIN";
+ } else {
+ d_state->builtin = false;
+ d_state->domain_name = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
+ }
+
+ ret = gendb_search(c_state->sam_ctx,
+ mem_ctx, ldb_get_default_basedn(c_state->sam_ctx), &dom_msgs, dom_attrs,
+ "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
+
+ if (ret == 0) {
+ talloc_free(d_state);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ } else if (ret > 1) {
+ talloc_free(d_state);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == -1) {
+ talloc_free(d_state);
+ DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ d_state->domain_dn = talloc_steal(d_state, dom_msgs[0]->dn);
+ d_state->role = lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx);
+ d_state->connect_state = talloc_reference(d_state, c_state);
+ d_state->sam_ctx = c_state->sam_ctx;
+ d_state->access_mask = r->in.access_mask;
+ d_state->domain_users_cached = NULL;
+
+ d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+
+ for (i = 0; i < SAMR_LAST_CACHE; i++) {
+ initialize_guid_cache(&d_state->guid_caches[i]);
+ }
+
+ h_domain = dcesrv_handle_create(dce_call, SAMR_HANDLE_DOMAIN);
+ if (!h_domain) {
+ talloc_free(d_state);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ h_domain->data = talloc_steal(h_domain, d_state);
+
+ *r->out.domain_handle = h_domain->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo1
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo1 *info)
+{
+ info->min_password_length =
+ ldb_msg_find_attr_as_uint(dom_msgs[0], "minPwdLength", 0);
+ info->password_history_length =
+ ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdHistoryLength", 0);
+ info->password_properties =
+ ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdProperties", 0);
+ info->max_password_age =
+ ldb_msg_find_attr_as_int64(dom_msgs[0], "maxPwdAge", 0);
+ info->min_password_age =
+ ldb_msg_find_attr_as_int64(dom_msgs[0], "minPwdAge", 0);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo2
+*/
+static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomGeneralInformation *info)
+{
+ size_t count = 0;
+ const enum ldb_scope scope = LDB_SCOPE_SUBTREE;
+ int ret = 0;
+
+ /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
+ info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
+ "domainReplica",
+ "");
+
+ info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
+ 0x8000000000000000LL);
+
+ info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
+ "oEMInformation",
+ "");
+ info->domain_name.string = state->domain_name;
+
+ info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
+ 0);
+ switch (state->role) {
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ /* This pulls the NetBIOS name from the
+ cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
+ string */
+ if (samdb_is_pdc(state->sam_ctx)) {
+ info->role = SAMR_ROLE_DOMAIN_PDC;
+ } else {
+ info->role = SAMR_ROLE_DOMAIN_BDC;
+ }
+ break;
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ case ROLE_IPA_DC:
+ case ROLE_AUTO:
+ return NT_STATUS_INTERNAL_ERROR;
+ case ROLE_DOMAIN_MEMBER:
+ info->role = SAMR_ROLE_DOMAIN_MEMBER;
+ break;
+ case ROLE_STANDALONE:
+ info->role = SAMR_ROLE_STANDALONE;
+ break;
+ }
+
+ /*
+ * Users are not meant to be in BUILTIN
+ * so to speed up the query we do not filter on domain_sid
+ */
+ ret = dsdb_domain_count(
+ state->sam_ctx,
+ &count,
+ state->domain_dn,
+ NULL,
+ scope,
+ "(objectClass=user)");
+ if (ret != LDB_SUCCESS || count > UINT32_MAX) {
+ goto error;
+ }
+ info->num_users = count;
+
+ /*
+ * Groups are not meant to be in BUILTIN
+ * so to speed up the query we do not filter on domain_sid
+ */
+ ret = dsdb_domain_count(
+ state->sam_ctx,
+ &count,
+ state->domain_dn,
+ NULL,
+ scope,
+ "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
+ GTYPE_SECURITY_UNIVERSAL_GROUP,
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ if (ret != LDB_SUCCESS || count > UINT32_MAX) {
+ goto error;
+ }
+ info->num_groups = count;
+
+ ret = dsdb_domain_count(
+ state->sam_ctx,
+ &count,
+ state->domain_dn,
+ state->domain_sid,
+ scope,
+ "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+ if (ret != LDB_SUCCESS || count > UINT32_MAX) {
+ goto error;
+ }
+ info->num_aliases = count;
+
+ return NT_STATUS_OK;
+
+error:
+ if (count > UINT32_MAX) {
+ return NT_STATUS_INTEGER_OVERFLOW;
+ }
+ return dsdb_ldb_err_to_ntstatus(ret);
+
+}
+
+/*
+ return DomInfo3
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo3 *info)
+{
+ info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
+ 0x8000000000000000LL);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo4
+*/
+static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomOEMInformation *info)
+{
+ info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
+ "oEMInformation",
+ "");
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo5
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo5 *info)
+{
+ info->domain_name.string = state->domain_name;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo6
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo6 *info)
+{
+ /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
+ info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
+ "domainReplica",
+ "");
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo7
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo7 *info)
+{
+
+ switch (state->role) {
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ /* This pulls the NetBIOS name from the
+ cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
+ string */
+ if (samdb_is_pdc(state->sam_ctx)) {
+ info->role = SAMR_ROLE_DOMAIN_PDC;
+ } else {
+ info->role = SAMR_ROLE_DOMAIN_BDC;
+ }
+ break;
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ case ROLE_IPA_DC:
+ case ROLE_AUTO:
+ return NT_STATUS_INTERNAL_ERROR;
+ case ROLE_DOMAIN_MEMBER:
+ info->role = SAMR_ROLE_DOMAIN_MEMBER;
+ break;
+ case ROLE_STANDALONE:
+ info->role = SAMR_ROLE_STANDALONE;
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo8
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo8 *info)
+{
+ info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
+ time(NULL));
+
+ info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
+ 0x0LL);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo9
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo9 *info)
+{
+ info->domain_server_state = DOMAIN_SERVER_ENABLED;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo11
+*/
+static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomGeneralInformation2 *info)
+{
+ NTSTATUS status;
+ status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
+ -18000000000LL);
+ info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
+ -18000000000LL);
+ info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo12
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo12 *info)
+{
+ info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
+ -18000000000LL);
+ info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
+ -18000000000LL);
+ info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo13
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo13 *info)
+{
+ info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
+ time(NULL));
+
+ info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
+ 0x0LL);
+
+ info->modified_count_at_last_promotion = 0;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_QueryDomainInfo
+*/
+static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_QueryDomainInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ union samr_DomainInfo *info;
+
+ struct ldb_message **dom_msgs;
+ const char * const *attrs = NULL;
+
+ *r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ switch (r->in.level) {
+ case 1:
+ {
+ static const char * const attrs2[] = { "minPwdLength",
+ "pwdHistoryLength",
+ "pwdProperties",
+ "maxPwdAge",
+ "minPwdAge",
+ NULL };
+ attrs = attrs2;
+ break;
+ }
+ case 2:
+ {
+ static const char * const attrs2[] = {"forceLogoff",
+ "oEMInformation",
+ "modifiedCount",
+ "domainReplica",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 3:
+ {
+ static const char * const attrs2[] = {"forceLogoff",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 4:
+ {
+ static const char * const attrs2[] = {"oEMInformation",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 5:
+ {
+ attrs = NULL;
+ break;
+ }
+ case 6:
+ {
+ static const char * const attrs2[] = { "domainReplica",
+ NULL };
+ attrs = attrs2;
+ break;
+ }
+ case 7:
+ {
+ attrs = NULL;
+ break;
+ }
+ case 8:
+ {
+ static const char * const attrs2[] = { "modifiedCount",
+ "creationTime",
+ NULL };
+ attrs = attrs2;
+ break;
+ }
+ case 9:
+ {
+ attrs = NULL;
+ break;
+ }
+ case 11:
+ {
+ static const char * const attrs2[] = { "oEMInformation",
+ "forceLogoff",
+ "modifiedCount",
+ "lockoutDuration",
+ "lockOutObservationWindow",
+ "lockoutThreshold",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 12:
+ {
+ static const char * const attrs2[] = { "lockoutDuration",
+ "lockOutObservationWindow",
+ "lockoutThreshold",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 13:
+ {
+ static const char * const attrs2[] = { "modifiedCount",
+ "creationTime",
+ NULL };
+ attrs = attrs2;
+ break;
+ }
+ default:
+ {
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+ }
+
+ /* some levels don't need a search */
+ if (attrs) {
+ int ret;
+ ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &dom_msgs, attrs);
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ /* allocate the info structure */
+ info = talloc_zero(mem_ctx, union samr_DomainInfo);
+ if (info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *r->out.info = info;
+
+ switch (r->in.level) {
+ case 1:
+ return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
+ &info->info1);
+ case 2:
+ return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
+ &info->general);
+ case 3:
+ return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
+ &info->info3);
+ case 4:
+ return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
+ &info->oem);
+ case 5:
+ return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
+ &info->info5);
+ case 6:
+ return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
+ &info->info6);
+ case 7:
+ return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
+ &info->info7);
+ case 8:
+ return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
+ &info->info8);
+ case 9:
+ return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
+ &info->info9);
+ case 11:
+ return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
+ &info->general2);
+ case 12:
+ return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
+ &info->info12);
+ case 13:
+ return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
+ &info->info13);
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+}
+
+
+/*
+ samr_SetDomainInfo
+*/
+static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetDomainInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message *msg;
+ int ret;
+ struct ldb_context *sam_ctx;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+ sam_ctx = d_state->sam_ctx;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ SET_UINT (msg, info1.min_password_length, "minPwdLength");
+ SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
+ SET_UINT (msg, info1.password_properties, "pwdProperties");
+ SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
+ SET_INT64 (msg, info1.min_password_age, "minPwdAge");
+ break;
+ case 3:
+ SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
+ break;
+ case 4:
+ SET_STRING(msg, oem.oem_information, "oEMInformation");
+ break;
+
+ case 6:
+ case 7:
+ case 9:
+ /* No op, we don't know where to set these */
+ return NT_STATUS_OK;
+
+ case 12:
+ /*
+ * It is not possible to set lockout_duration < lockout_window.
+ * (The test is the other way around since the negative numbers
+ * are stored...)
+ *
+ * TODO:
+ * This check should be moved to the backend, i.e. to some
+ * ldb module under dsdb/samdb/ldb_modules/ .
+ *
+ * This constraint is documented here for the samr rpc service:
+ * MS-SAMR 3.1.1.6 Attribute Constraints for Originating Updates
+ * http://msdn.microsoft.com/en-us/library/cc245667%28PROT.10%29.aspx
+ *
+ * And here for the ldap backend:
+ * MS-ADTS 3.1.1.5.3.2 Constraints
+ * http://msdn.microsoft.com/en-us/library/cc223462(PROT.10).aspx
+ */
+ if (r->in.info->info12.lockout_duration >
+ r->in.info->info12.lockout_window)
+ {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
+ SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
+ SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
+ break;
+
+ default:
+ /* many info classes are not valid for SetDomainInfo */
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* modify the samdb record */
+ ret = ldb_modify(sam_ctx, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1,("Failed to modify record %s: %s\n",
+ ldb_dn_get_linearized(d_state->domain_dn),
+ ldb_errstring(sam_ctx)));
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_CreateDomainGroup
+*/
+static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_CreateDomainGroup *r)
+{
+ NTSTATUS status;
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *groupname;
+ struct dom_sid *group_sid;
+ struct ldb_dn *group_dn;
+ struct dcesrv_handle *g_handle;
+
+ ZERO_STRUCTP(r->out.group_handle);
+ *r->out.rid = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (d_state->builtin) {
+ DEBUG(5, ("Cannot create a domain group in the BUILTIN domain"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ groupname = r->in.name->string;
+
+ if (groupname == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dsdb_add_domain_group(d_state->sam_ctx, mem_ctx, groupname, &group_sid, &group_dn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ a_state = talloc(mem_ctx, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, group_dn);
+
+ a_state->account_name = talloc_steal(a_state, groupname);
+
+ /* create the policy handle */
+ g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
+ if (!g_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ g_handle->data = talloc_steal(g_handle, a_state);
+
+ *r->out.group_handle = g_handle->wire_handle;
+ *r->out.rid = group_sid->sub_auths[group_sid->num_auths-1];
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ comparison function for sorting SamEntry array
+*/
+static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
+{
+ return e1->idx - e2->idx;
+}
+
+static int compare_msgRid(struct ldb_message **m1, struct ldb_message **m2) {
+ struct dom_sid *sid1 = NULL;
+ struct dom_sid *sid2 = NULL;
+ uint32_t rid1;
+ uint32_t rid2;
+ int res = 0;
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ sid1 = samdb_result_dom_sid(frame, *m1, "objectSid");
+ sid2 = samdb_result_dom_sid(frame, *m2, "objectSid");
+
+ /*
+ * If entries don't have a SID we want to sort them to the end of
+ * the list.
+ */
+ if (sid1 == NULL && sid2 == NULL) {
+ res = 0;
+ goto exit;
+ } else if (sid2 == NULL) {
+ res = 1;
+ goto exit;
+ } else if (sid1 == NULL) {
+ res = -1;
+ goto exit;
+ }
+
+ /*
+ * Get and compare the rids, if we fail to extract a rid treat it as a
+ * missing SID and sort to the end of the list
+ */
+ status = dom_sid_split_rid(NULL, sid1, NULL, &rid1);
+ if (!NT_STATUS_IS_OK(status)) {
+ res = 1;
+ goto exit;
+ }
+
+ status = dom_sid_split_rid(NULL, sid2, NULL, &rid2);
+ if (!NT_STATUS_IS_OK(status)) {
+ res = -1;
+ goto exit;
+ }
+
+ if (rid1 == rid2) {
+ res = 0;
+ }
+ else if (rid1 > rid2) {
+ res = 1;
+ }
+ else {
+ res = -1;
+ }
+exit:
+ TALLOC_FREE(frame);
+ return res;
+}
+
+/*
+ samr_EnumDomainGroups
+*/
+static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_EnumDomainGroups *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message **res;
+ uint32_t i;
+ uint32_t count;
+ uint32_t results;
+ uint32_t max_entries;
+ uint32_t remaining_entries;
+ uint32_t resume_handle;
+ struct samr_SamEntry *entries;
+ const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
+ const char * const cache_attrs[] = { "objectSid", "objectGUID", NULL };
+ struct samr_SamArray *sam;
+ struct samr_guid_cache *cache = NULL;
+
+ *r->out.resume_handle = 0;
+ *r->out.sam = NULL;
+ *r->out.num_entries = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+ cache = &d_state->guid_caches[SAMR_ENUM_DOMAIN_GROUPS_CACHE];
+
+ /*
+ * If the resume_handle is zero, query the database and cache the
+ * matching GUID's
+ */
+ if (*r->in.resume_handle == 0) {
+ NTSTATUS status;
+ int ldb_cnt;
+ clear_guid_cache(cache);
+ /*
+ * search for all domain groups in this domain.
+ */
+ ldb_cnt = samdb_search_domain(
+ d_state->sam_ctx,
+ mem_ctx,
+ d_state->domain_dn,
+ &res,
+ cache_attrs,
+ d_state->domain_sid,
+ "(&(|(groupType=%d)(groupType=%d))(objectClass=group))",
+ GTYPE_SECURITY_UNIVERSAL_GROUP,
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ if (ldb_cnt < 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ /*
+ * Sort the results into RID order, while the spec states there
+ * is no order, Windows appears to sort the results by RID and
+ * so it is possible that there are clients that depend on
+ * this ordering
+ */
+ TYPESAFE_QSORT(res, ldb_cnt, compare_msgRid);
+
+ /*
+ * cache the sorted GUID's
+ */
+ status = load_guid_cache(cache, d_state, ldb_cnt, res);
+ TALLOC_FREE(res);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ cache->handle = 0;
+ }
+
+
+ /*
+ * If the resume handle is out of range we return an empty response
+ * and invalidate the cache.
+ *
+ * From the specification:
+ * Servers SHOULD validate that EnumerationContext is an expected
+ * value for the server's implementation. Windows does NOT validate
+ * the input, though the result of malformed information merely results
+ * in inconsistent output to the client.
+ */
+ if (*r->in.resume_handle >= cache->size) {
+ clear_guid_cache(cache);
+ sam = talloc(mem_ctx, struct samr_SamArray);
+ if (!sam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ sam->entries = NULL;
+ sam->count = 0;
+
+ *r->out.sam = sam;
+ *r->out.resume_handle = 0;
+ return NT_STATUS_OK;
+ }
+
+
+ /*
+ * Calculate the number of entries to return limit by max_size.
+ * Note that we use the w2k3 element size value of 54
+ */
+ max_entries = 1 + (r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER);
+ remaining_entries = cache->size - *r->in.resume_handle;
+ results = MIN(remaining_entries, max_entries);
+
+ /*
+ * Process the list of result GUID's.
+ * Read the details of each object and populate the Entries
+ * for the current level.
+ */
+ count = 0;
+ resume_handle = *r->in.resume_handle;
+ entries = talloc_array(mem_ctx, struct samr_SamEntry, results);
+ if (entries == NULL) {
+ clear_guid_cache(cache);
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i = 0; i < results; i++) {
+ struct dom_sid *objectsid;
+ uint32_t rid;
+ struct ldb_result *rec;
+ const uint32_t idx = *r->in.resume_handle + i;
+ int ret;
+ NTSTATUS status;
+ const char *name = NULL;
+ resume_handle++;
+ /*
+ * Read an object from disk using the GUID as the key
+ *
+ * If the object can not be read, or it does not have a SID
+ * it is ignored.
+ *
+ * As a consequence of this, if all the remaining GUID's
+ * have been deleted an empty result will be returned.
+ * i.e. even if the previous call returned a non zero
+ * resume_handle it is possible for no results to be returned.
+ *
+ */
+ ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
+ mem_ctx,
+ &rec,
+ &cache->entries[idx],
+ attrs,
+ 0);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ struct GUID_txt_buf guid_buf;
+ DBG_WARNING(
+ "GUID [%s] not found\n",
+ GUID_buf_string(&cache->entries[idx], &guid_buf));
+ continue;
+ } else if (ret != LDB_SUCCESS) {
+ clear_guid_cache(cache);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ objectsid = samdb_result_dom_sid(mem_ctx,
+ rec->msgs[0],
+ "objectSID");
+ if (objectsid == NULL) {
+ struct GUID_txt_buf guid_buf;
+ DBG_WARNING(
+ "objectSID for GUID [%s] not found\n",
+ GUID_buf_string(&cache->entries[idx], &guid_buf));
+ continue;
+ }
+ status = dom_sid_split_rid(NULL,
+ objectsid,
+ NULL,
+ &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ struct dom_sid_buf sid_buf;
+ struct GUID_txt_buf guid_buf;
+ DBG_WARNING(
+ "objectSID [%s] for GUID [%s] invalid\n",
+ dom_sid_str_buf(objectsid, &sid_buf),
+ GUID_buf_string(&cache->entries[idx], &guid_buf));
+ continue;
+ }
+
+ entries[count].idx = rid;
+ name = ldb_msg_find_attr_as_string(
+ rec->msgs[0], "sAMAccountName", "");
+ entries[count].name.string = talloc_strdup(entries, name);
+ count++;
+ }
+
+ sam = talloc(mem_ctx, struct samr_SamArray);
+ if (!sam) {
+ clear_guid_cache(cache);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sam->entries = entries;
+ sam->count = count;
+
+ *r->out.sam = sam;
+ *r->out.resume_handle = resume_handle;
+ *r->out.num_entries = count;
+
+ /*
+ * Signal no more results by returning zero resume handle,
+ * the cache is also cleared at this point
+ */
+ if (*r->out.resume_handle >= cache->size) {
+ *r->out.resume_handle = 0;
+ clear_guid_cache(cache);
+ return NT_STATUS_OK;
+ }
+ /*
+ * There are more results to be returned.
+ */
+ return STATUS_MORE_ENTRIES;
+}
+
+
+/*
+ samr_CreateUser2
+
+ This call uses transactions to ensure we don't get a new conflicting
+ user while we are processing this, and to ensure the user either
+ completly exists, or does not.
+*/
+static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_CreateUser2 *r)
+{
+ NTSTATUS status;
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ struct ldb_dn *dn;
+ struct dom_sid *sid;
+ struct dcesrv_handle *u_handle;
+ const char *account_name;
+
+ ZERO_STRUCTP(r->out.user_handle);
+ *r->out.access_granted = 0;
+ *r->out.rid = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (d_state->builtin) {
+ DEBUG(5, ("Cannot create a user in the BUILTIN domain"));
+ return NT_STATUS_ACCESS_DENIED;
+ } else if (r->in.acct_flags == ACB_DOMTRUST) {
+ /* Domain trust accounts must be created by the LSA calls */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ account_name = r->in.account_name->string;
+
+ if (account_name == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dsdb_add_user(d_state->sam_ctx, mem_ctx, account_name, r->in.acct_flags, NULL,
+ &sid, &dn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ a_state = talloc(mem_ctx, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, dn);
+
+ a_state->account_name = talloc_steal(a_state, account_name);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
+ if (!u_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ u_handle->data = talloc_steal(u_handle, a_state);
+
+ *r->out.user_handle = u_handle->wire_handle;
+ *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
+
+ *r->out.rid = sid->sub_auths[sid->num_auths-1];
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_CreateUser
+*/
+static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_CreateUser *r)
+{
+ struct samr_CreateUser2 r2;
+ uint32_t access_granted = 0;
+
+
+ /* a simple wrapper around samr_CreateUser2 works nicely */
+
+ r2 = (struct samr_CreateUser2) {
+ .in.domain_handle = r->in.domain_handle,
+ .in.account_name = r->in.account_name,
+ .in.acct_flags = ACB_NORMAL,
+ .in.access_mask = r->in.access_mask,
+ .out.user_handle = r->out.user_handle,
+ .out.access_granted = &access_granted,
+ .out.rid = r->out.rid
+ };
+
+ return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
+}
+
+struct enum_dom_users_ctx {
+ struct samr_SamEntry *entries;
+ uint32_t num_entries;
+ uint32_t acct_flags;
+ struct dom_sid *domain_sid;
+};
+
+static int user_iterate_callback(struct ldb_request *req,
+ struct ldb_reply *ares);
+
+/*
+ * Iterate users and add all those that match a domain SID and pass an acct
+ * flags check to an array of SamEntry objects.
+ */
+static int user_iterate_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct enum_dom_users_ctx *ac =\
+ talloc_get_type(req->context, struct enum_dom_users_ctx);
+ int ret = LDB_ERR_OPERATIONS_ERROR;
+
+ if (!ares) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_request_done(req, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ {
+ struct ldb_message *msg = ares->message;
+ const struct ldb_val *val;
+ struct samr_SamEntry *ent;
+ struct dom_sid objectsid;
+ uint32_t rid;
+ size_t entries_array_len = 0;
+ NTSTATUS status;
+ ssize_t sid_size;
+
+ if (ac->acct_flags && ((samdb_result_acct_flags(msg, NULL) &
+ ac->acct_flags) == 0)) {
+ ret = LDB_SUCCESS;
+ break;
+ }
+
+ val = ldb_msg_find_ldb_val(msg, "objectSID");
+ if (val == NULL) {
+ DBG_WARNING("objectSID for DN %s not found\n",
+ ldb_dn_get_linearized(msg->dn));
+ ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ break;
+ }
+
+ sid_size = sid_parse(val->data, val->length, &objectsid);
+ if (sid_size == -1) {
+ struct dom_sid_buf sid_buf;
+ DBG_WARNING("objectsid [%s] for DN [%s] invalid\n",
+ dom_sid_str_buf(&objectsid, &sid_buf),
+ ldb_dn_get_linearized(msg->dn));
+ ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ break;
+ }
+
+ if (!dom_sid_in_domain(ac->domain_sid, &objectsid)) {
+ /* Ignore if user isn't in the domain */
+ ret = LDB_SUCCESS;
+ break;
+ }
+
+ status = dom_sid_split_rid(ares, &objectsid, NULL, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ struct dom_sid_buf sid_buf;
+ DBG_WARNING("Couldn't split RID from "
+ "SID [%s] of DN [%s]\n",
+ dom_sid_str_buf(&objectsid, &sid_buf),
+ ldb_dn_get_linearized(msg->dn));
+ ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ break;
+ }
+
+ entries_array_len = talloc_array_length(ac->entries);
+ if (ac->num_entries >= entries_array_len) {
+ if (entries_array_len * 2 < entries_array_len) {
+ ret = ldb_request_done(req,
+ LDB_ERR_OPERATIONS_ERROR);
+ break;
+ }
+ ac->entries = talloc_realloc(ac,
+ ac->entries,
+ struct samr_SamEntry,
+ entries_array_len * 2);
+ if (ac->entries == NULL) {
+ ret = ldb_request_done(req,
+ LDB_ERR_OPERATIONS_ERROR);
+ break;
+ }
+ }
+
+ ent = &(ac->entries[ac->num_entries++]);
+ val = ldb_msg_find_ldb_val(msg, "samaccountname");
+ if (val == NULL) {
+ DBG_WARNING("samaccountname attribute not found\n");
+ ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ break;
+ }
+ ent->name.string = talloc_steal(ac->entries,
+ (char *)val->data);
+ ent->idx = rid;
+ ret = LDB_SUCCESS;
+ break;
+ }
+ case LDB_REPLY_DONE:
+ {
+ if (ac->num_entries != 0 &&
+ ac->num_entries != talloc_array_length(ac->entries)) {
+ ac->entries = talloc_realloc(ac,
+ ac->entries,
+ struct samr_SamEntry,
+ ac->num_entries);
+ if (ac->entries == NULL) {
+ ret = ldb_request_done(req,
+ LDB_ERR_OPERATIONS_ERROR);
+ break;
+ }
+ }
+ ret = ldb_request_done(req, LDB_SUCCESS);
+ break;
+ }
+ case LDB_REPLY_REFERRAL:
+ {
+ ret = LDB_SUCCESS;
+ break;
+ }
+ default:
+ /* Doesn't happen */
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ }
+ TALLOC_FREE(ares);
+
+ return ret;
+}
+
+/*
+ * samr_EnumDomainUsers
+ * The previous implementation did an initial search and stored a list of
+ * matching GUIDs on the connection handle's domain state, then did direct
+ * GUID lookups for each record in a page indexed by resume_handle. That
+ * approach was memory efficient, requiring only 16 bytes per record, but
+ * was too slow for winbind which needs this RPC call for getpwent.
+ *
+ * Now we use an iterate pattern to populate a cached list of the rids and
+ * names for each record. This improves runtime performance but requires
+ * about 200 bytes per record which will mean for a 100k database we use
+ * about 2MB, which is fine. The speedup achieved by this new approach is
+ * around 50%.
+ */
+static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_EnumDomainUsers *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ uint32_t results;
+ uint32_t max_entries;
+ uint32_t num_entries;
+ uint32_t remaining_entries;
+ struct samr_SamEntry *entries;
+ const char * const attrs[] = { "objectSid", "sAMAccountName",
+ "userAccountControl", NULL };
+ struct samr_SamArray *sam;
+ struct ldb_request *req;
+
+ *r->out.resume_handle = 0;
+ *r->out.sam = NULL;
+ *r->out.num_entries = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+ entries = d_state->domain_users_cached;
+
+ /*
+ * If the resume_handle is zero, query the database and cache the
+ * matching entries.
+ */
+ if (*r->in.resume_handle == 0) {
+ int ret;
+ struct enum_dom_users_ctx *ac;
+ if (entries != NULL) {
+ talloc_free(entries);
+ d_state->domain_users_cached = NULL;
+ }
+
+ ac = talloc(mem_ctx, struct enum_dom_users_ctx);
+ ac->num_entries = 0;
+ ac->domain_sid = d_state->domain_sid;
+ ac->entries = talloc_array(ac,
+ struct samr_SamEntry,
+ 100);
+ if (ac->entries == NULL) {
+ talloc_free(ac);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ac->acct_flags = r->in.acct_flags;
+
+ ret = ldb_build_search_req(&req,
+ d_state->sam_ctx,
+ mem_ctx,
+ d_state->domain_dn,
+ LDB_SCOPE_SUBTREE,
+ "(objectClass=user)",
+ attrs,
+ NULL,
+ ac,
+ user_iterate_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ac);
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ ret = ldb_request(d_state->sam_ctx, req);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ac);
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ if (ret != LDB_SUCCESS) {
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ if (ac->num_entries == 0) {
+ DBG_WARNING("No users in domain %s",
+ ldb_dn_get_linearized(d_state->domain_dn));
+ talloc_free(ac);
+ return NT_STATUS_OK;
+ }
+
+ entries = talloc_steal(d_state, ac->entries);
+ d_state->domain_users_cached = entries;
+ num_entries = ac->num_entries;
+ talloc_free(ac);
+
+ /*
+ * Sort the entries into RID order, while the spec states there
+ * is no order, Windows appears to sort the results by RID and
+ * so it is possible that there are clients that depend on
+ * this ordering
+ */
+ TYPESAFE_QSORT(entries, num_entries, compare_SamEntry);
+ } else {
+ num_entries = talloc_array_length(entries);
+ }
+
+ /*
+ * If the resume handle is out of range we return an empty response
+ * and invalidate the cache.
+ *
+ * From the specification:
+ * Servers SHOULD validate that EnumerationContext is an expected
+ * value for the server's implementation. Windows does NOT validate
+ * the input, though the result of malformed information merely results
+ * in inconsistent output to the client.
+ */
+ if (*r->in.resume_handle >= num_entries) {
+ talloc_free(entries);
+ d_state->domain_users_cached = NULL;
+ sam = talloc(mem_ctx, struct samr_SamArray);
+ if (!sam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ sam->entries = NULL;
+ sam->count = 0;
+
+ *r->out.sam = sam;
+ *r->out.resume_handle = 0;
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * Calculate the number of entries to return limit by max_size.
+ * Note that we use the w2k3 element size value of 54
+ */
+ max_entries = 1 + (r->in.max_size / SAMR_ENUM_USERS_MULTIPLIER);
+ remaining_entries = num_entries - *r->in.resume_handle;
+ results = MIN(remaining_entries, max_entries);
+
+ sam = talloc(mem_ctx, struct samr_SamArray);
+ if (!sam) {
+ d_state->domain_users_cached = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sam->entries = entries + *r->in.resume_handle;
+ sam->count = results;
+
+ *r->out.sam = sam;
+ *r->out.resume_handle = *r->in.resume_handle + results;
+ *r->out.num_entries = results;
+
+ /*
+ * Signal no more results by returning zero resume handle,
+ * the cache is also cleared at this point
+ */
+ if (*r->out.resume_handle >= num_entries) {
+ *r->out.resume_handle = 0;
+ return NT_STATUS_OK;
+ }
+ /*
+ * There are more results to be returned.
+ */
+ return STATUS_MORE_ENTRIES;
+}
+
+
+/*
+ samr_CreateDomAlias
+*/
+static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_CreateDomAlias *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *alias_name;
+ struct dom_sid *sid;
+ struct dcesrv_handle *a_handle;
+ struct ldb_dn *dn;
+ NTSTATUS status;
+
+ ZERO_STRUCTP(r->out.alias_handle);
+ *r->out.rid = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (d_state->builtin) {
+ DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ alias_name = r->in.alias_name->string;
+
+ if (alias_name == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ a_state = talloc(mem_ctx, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, dn);
+
+ a_state->account_name = talloc_steal(a_state, alias_name);
+
+ /* create the policy handle */
+ a_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
+ if (a_handle == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ a_handle->data = talloc_steal(a_handle, a_state);
+
+ *r->out.alias_handle = a_handle->wire_handle;
+
+ *r->out.rid = sid->sub_auths[sid->num_auths-1];
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_EnumDomainAliases
+*/
+static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_EnumDomainAliases *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message **res;
+ int i, ldb_cnt;
+ uint32_t first, count;
+ struct samr_SamEntry *entries;
+ const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
+ struct samr_SamArray *sam;
+
+ *r->out.resume_handle = 0;
+ *r->out.sam = NULL;
+ *r->out.num_entries = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* search for all domain aliases in this domain. This could possibly be
+ cached and resumed based on resume_key */
+ ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
+ &res, attrs,
+ d_state->domain_sid,
+ "(&(|(grouptype=%d)(grouptype=%d)))"
+ "(objectclass=group))",
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+ if (ldb_cnt < 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* convert to SamEntry format */
+ entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ count = 0;
+
+ for (i=0;i<ldb_cnt;i++) {
+ struct dom_sid *alias_sid;
+
+ alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
+ "objectSid");
+
+ if (alias_sid == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ entries[count].idx =
+ alias_sid->sub_auths[alias_sid->num_auths-1];
+ entries[count].name.string =
+ ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
+ count += 1;
+ }
+
+ /* sort the results by rid */
+ TYPESAFE_QSORT(entries, count, compare_SamEntry);
+
+ /* find the first entry to return */
+ for (first=0;
+ first<count && entries[first].idx <= *r->in.resume_handle;
+ first++) ;
+
+ /* return the rest, limit by max_size. Note that we
+ use the w2k3 element size value of 54 */
+ *r->out.num_entries = count - first;
+ *r->out.num_entries = MIN(*r->out.num_entries,
+ 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
+
+ sam = talloc(mem_ctx, struct samr_SamArray);
+ if (!sam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sam->entries = entries+first;
+ sam->count = *r->out.num_entries;
+
+ *r->out.sam = sam;
+
+ if (first == count) {
+ return NT_STATUS_OK;
+ }
+
+ if (*r->out.num_entries < count - first) {
+ *r->out.resume_handle =
+ entries[first+*r->out.num_entries-1].idx;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_GetAliasMembership
+*/
+static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetAliasMembership *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ char *filter;
+ const char * const attrs[] = { "objectSid", NULL };
+ struct ldb_message **res;
+ uint32_t i;
+ int count = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(|(grouptype=%d)(grouptype=%d))"
+ "(objectclass=group)(|",
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<r->in.sids->num_sids; i++) {
+ struct dom_sid_buf buf;
+
+ filter = talloc_asprintf_append(
+ filter,
+ "(member=<SID=%s>)",
+ dom_sid_str_buf(r->in.sids->sids[i].sid, &buf));
+
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* Find out if we had at least one valid member SID passed - otherwise
+ * just skip the search. */
+ if (strstr(filter, "member") != NULL) {
+ count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
+ &res, attrs, d_state->domain_sid,
+ "%s))", filter);
+ if (count < 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ r->out.rids->count = 0;
+ r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
+ if (r->out.rids->ids == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i=0; i<count; i++) {
+ struct dom_sid *alias_sid;
+
+ alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
+ if (alias_sid == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ r->out.rids->ids[r->out.rids->count] =
+ alias_sid->sub_auths[alias_sid->num_auths-1];
+ r->out.rids->count += 1;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_LookupNames
+*/
+static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_LookupNames *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ uint32_t i, num_mapped;
+ NTSTATUS status = NT_STATUS_OK;
+ const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
+ int count;
+
+ ZERO_STRUCTP(r->out.rids);
+ ZERO_STRUCTP(r->out.types);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (r->in.num_names == 0) {
+ return NT_STATUS_OK;
+ }
+
+ r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
+ r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
+ if (!r->out.rids->ids || !r->out.types->ids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.rids->count = r->in.num_names;
+ r->out.types->count = r->in.num_names;
+
+ num_mapped = 0;
+
+ for (i=0;i<r->in.num_names;i++) {
+ struct ldb_message **res;
+ struct dom_sid *sid;
+ uint32_t atype, rtype;
+
+ r->out.rids->ids[i] = 0;
+ r->out.types->ids[i] = SID_NAME_UNKNOWN;
+
+ count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
+ "sAMAccountName=%s",
+ ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
+ if (count != 1) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
+ if (sid == NULL) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
+ if (atype == 0) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ rtype = ds_atype_map(atype);
+
+ if (rtype == SID_NAME_UNKNOWN) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
+ r->out.types->ids[i] = rtype;
+ num_mapped++;
+ }
+
+ if (num_mapped == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ return status;
+}
+
+
+/*
+ samr_LookupRids
+*/
+static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_LookupRids *r)
+{
+ NTSTATUS status;
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ const char **names;
+ struct lsa_String *lsa_names;
+ enum lsa_SidType *ids;
+
+ ZERO_STRUCTP(r->out.names);
+ ZERO_STRUCTP(r->out.types);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (r->in.num_rids == 0)
+ return NT_STATUS_OK;
+
+ lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
+ names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
+ ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
+
+ if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
+ return NT_STATUS_NO_MEMORY;
+
+ r->out.names->names = lsa_names;
+ r->out.names->count = r->in.num_rids;
+
+ r->out.types->ids = (uint32_t *) ids;
+ r->out.types->count = r->in.num_rids;
+
+ status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
+ r->in.num_rids, r->in.rids, names, ids);
+ if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
+ uint32_t i;
+ for (i = 0; i < r->in.num_rids; i++) {
+ lsa_names[i].string = names[i];
+ }
+ }
+ return status;
+}
+
+
+/*
+ samr_OpenGroup
+*/
+static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OpenGroup *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *groupname;
+ struct dom_sid *sid;
+ struct ldb_message **msgs;
+ struct dcesrv_handle *g_handle;
+ const char * const attrs[2] = { "sAMAccountName", NULL };
+ int ret;
+
+ ZERO_STRUCTP(r->out.group_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* form the group SID */
+ sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (!sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the group record */
+ if (d_state->builtin) {
+ ret = gendb_search(d_state->sam_ctx,
+ mem_ctx, d_state->domain_dn, &msgs, attrs,
+ "(&(objectSid=%s)(objectClass=group)"
+ "(groupType=%d))",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid),
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP);
+ } else {
+ ret = gendb_search(d_state->sam_ctx,
+ mem_ctx, d_state->domain_dn, &msgs, attrs,
+ "(&(objectSid=%s)(objectClass=group)"
+ "(|(groupType=%d)(groupType=%d)))",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid),
+ GTYPE_SECURITY_UNIVERSAL_GROUP,
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ }
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching sid %s\n",
+ ret, dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
+ if (groupname == NULL) {
+ DEBUG(0,("sAMAccountName field missing for sid %s\n",
+ dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(mem_ctx, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
+ a_state->account_sid = talloc_steal(a_state, sid);
+ a_state->account_name = talloc_strdup(a_state, groupname);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
+ if (!g_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ g_handle->data = talloc_steal(g_handle, a_state);
+
+ *r->out.group_handle = g_handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_QueryGroupInfo
+*/
+static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryGroupInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg, **res;
+ const char * const attrs[4] = { "sAMAccountName", "description",
+ "numMembers", NULL };
+ int ret;
+ union samr_GroupInfo *info;
+
+ *r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+
+ /* pull all the group attributes */
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ a_state->account_dn, &res, attrs);
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ /* allocate the info structure */
+ info = talloc_zero(mem_ctx, union samr_GroupInfo);
+ if (info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Fill in the level */
+ switch (r->in.level) {
+ case GROUPINFOALL:
+ QUERY_STRING(msg, all.name, "sAMAccountName");
+ info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
+ QUERY_UINT (msg, all.num_members, "numMembers")
+ QUERY_STRING(msg, all.description, "description");
+ break;
+ case GROUPINFONAME:
+ QUERY_STRING(msg, name, "sAMAccountName");
+ break;
+ case GROUPINFOATTRIBUTES:
+ info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
+ break;
+ case GROUPINFODESCRIPTION:
+ QUERY_STRING(msg, description, "description");
+ break;
+ case GROUPINFOALL2:
+ QUERY_STRING(msg, all2.name, "sAMAccountName");
+ info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
+ QUERY_UINT (msg, all2.num_members, "numMembers")
+ QUERY_STRING(msg, all2.description, "description");
+ break;
+ default:
+ talloc_free(info);
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ *r->out.info = info;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetGroupInfo
+*/
+static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetGroupInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *g_state;
+ struct ldb_message *msg;
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ g_state = h->data;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case GROUPINFODESCRIPTION:
+ SET_STRING(msg, description, "description");
+ break;
+ case GROUPINFONAME:
+ /* On W2k3 this does not change the name, it changes the
+ * sAMAccountName attribute */
+ SET_STRING(msg, name, "sAMAccountName");
+ break;
+ case GROUPINFOATTRIBUTES:
+ /* This does not do anything obviously visible in W2k3 LDAP */
+ return NT_STATUS_OK;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* modify the samdb record */
+ ret = ldb_modify(g_state->sam_ctx, msg);
+ if (ret != LDB_SUCCESS) {
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_AddGroupMember
+*/
+static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_AddGroupMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message *mod;
+ struct dom_sid *membersid;
+ const char *memberdn;
+ struct ldb_result *res;
+ const char * const attrs[] = { NULL };
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (membersid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
+ ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
+ d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
+ "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, membersid));
+
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (res->count == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (res->count > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
+
+ if (memberdn == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
+
+ ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
+ memberdn);
+ if (ret != LDB_SUCCESS) {
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ ret = ldb_modify(a_state->sam_ctx, mod);
+ switch (ret) {
+ case LDB_SUCCESS:
+ return NT_STATUS_OK;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ return NT_STATUS_MEMBER_IN_GROUP;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+}
+
+
+/*
+ samr_DeleteDomainGroup
+*/
+static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteDomainGroup *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ int ret;
+
+ *r->out.group_handle = *r->in.group_handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+
+ ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
+ if (ret != LDB_SUCCESS) {
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ talloc_free(h);
+ ZERO_STRUCTP(r->out.group_handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_DeleteGroupMember
+*/
+static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteGroupMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message *mod;
+ struct dom_sid *membersid;
+ const char *memberdn;
+ struct ldb_result *res;
+ const char * const attrs[] = { NULL };
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (membersid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
+ ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
+ d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
+ "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, membersid));
+
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (res->count == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (res->count > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
+
+ if (memberdn == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
+
+ ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
+ memberdn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_modify(a_state->sam_ctx, mod);
+ switch (ret) {
+ case LDB_SUCCESS:
+ return NT_STATUS_OK;
+ case LDB_ERR_UNWILLING_TO_PERFORM:
+ return NT_STATUS_MEMBER_NOT_IN_GROUP;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+}
+
+
+/*
+ samr_QueryGroupMember
+*/
+static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryGroupMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct samr_RidAttrArray *array;
+ unsigned int i, num_members;
+ struct dom_sid *members;
+ NTSTATUS status;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
+ a_state->account_dn, &members,
+ &num_members);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
+ if (array == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (num_members == 0) {
+ *r->out.rids = array;
+
+ return NT_STATUS_OK;
+ }
+
+ array->rids = talloc_array(array, uint32_t, num_members);
+ if (array->rids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ array->attributes = talloc_array(array, uint32_t, num_members);
+ if (array->attributes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ array->count = 0;
+ for (i=0; i<num_members; i++) {
+ if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
+ continue;
+ }
+
+ status = dom_sid_split_rid(NULL, &members[i], NULL,
+ &array->rids[array->count]);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ array->attributes[array->count] = SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED;
+ array->count++;
+ }
+
+ *r->out.rids = array;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetMemberAttributesOfGroup
+*/
+static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetMemberAttributesOfGroup *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_OpenAlias
+*/
+static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OpenAlias *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *alias_name;
+ struct dom_sid *sid;
+ struct ldb_message **msgs;
+ struct dcesrv_handle *g_handle;
+ const char * const attrs[2] = { "sAMAccountName", NULL };
+ int ret;
+
+ ZERO_STRUCTP(r->out.alias_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* form the alias SID */
+ sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (sid == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* search for the group record */
+ ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
+ "(&(objectSid=%s)(objectclass=group)"
+ "(|(grouptype=%d)(grouptype=%d)))",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid),
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching sid %s\n",
+ ret, dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
+ if (alias_name == NULL) {
+ DEBUG(0,("sAMAccountName field missing for sid %s\n",
+ dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(mem_ctx, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
+ a_state->account_sid = talloc_steal(a_state, sid);
+ a_state->account_name = talloc_strdup(a_state, alias_name);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
+ if (!g_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ g_handle->data = talloc_steal(g_handle, a_state);
+
+ *r->out.alias_handle = g_handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_QueryAliasInfo
+*/
+static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryAliasInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg, **res;
+ const char * const attrs[4] = { "sAMAccountName", "description",
+ "numMembers", NULL };
+ int ret;
+ union samr_AliasInfo *info;
+
+ *r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+
+ /* pull all the alias attributes */
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ a_state->account_dn, &res, attrs);
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ /* allocate the info structure */
+ info = talloc_zero(mem_ctx, union samr_AliasInfo);
+ if (info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch(r->in.level) {
+ case ALIASINFOALL:
+ QUERY_STRING(msg, all.name, "sAMAccountName");
+ QUERY_UINT (msg, all.num_members, "numMembers");
+ QUERY_STRING(msg, all.description, "description");
+ break;
+ case ALIASINFONAME:
+ QUERY_STRING(msg, name, "sAMAccountName");
+ break;
+ case ALIASINFODESCRIPTION:
+ QUERY_STRING(msg, description, "description");
+ break;
+ default:
+ talloc_free(info);
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ *r->out.info = info;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetAliasInfo
+*/
+static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetAliasInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg;
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case ALIASINFODESCRIPTION:
+ SET_STRING(msg, description, "description");
+ break;
+ case ALIASINFONAME:
+ /* On W2k3 this does not change the name, it changes the
+ * sAMAccountName attribute */
+ SET_STRING(msg, name, "sAMAccountName");
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* modify the samdb record */
+ ret = ldb_modify(a_state->sam_ctx, msg);
+ if (ret != LDB_SUCCESS) {
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_DeleteDomAlias
+*/
+static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteDomAlias *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ int ret;
+
+ *r->out.alias_handle = *r->in.alias_handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+
+ ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
+ if (ret != LDB_SUCCESS) {
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ talloc_free(h);
+ ZERO_STRUCTP(r->out.alias_handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_AddAliasMember
+*/
+static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_AddAliasMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message *mod;
+ struct ldb_message **msgs;
+ const char * const attrs[] = { NULL };
+ struct ldb_dn *memberdn = NULL;
+ int ret;
+ NTSTATUS status;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
+ &msgs, attrs, "(objectsid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
+
+ if (ret == 1) {
+ memberdn = msgs[0]->dn;
+ } else if (ret == 0) {
+ status = samdb_create_foreign_security_principal(
+ d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else {
+ DEBUG(0,("Found %d records matching sid %s\n",
+ ret, dom_sid_string(mem_ctx, r->in.sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (memberdn == NULL) {
+ DEBUG(0, ("Could not find memberdn\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
+
+ ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
+ ldb_dn_alloc_linearized(mem_ctx, memberdn));
+ if (ret != LDB_SUCCESS) {
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ ret = ldb_modify(a_state->sam_ctx, mod);
+ switch (ret) {
+ case LDB_SUCCESS:
+ return NT_STATUS_OK;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ return NT_STATUS_MEMBER_IN_GROUP;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+}
+
+
+/*
+ samr_DeleteAliasMember
+*/
+static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteAliasMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message *mod;
+ const char *memberdn;
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
+ "distinguishedName", "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
+ if (memberdn == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
+
+ ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
+ memberdn);
+ if (ret != LDB_SUCCESS) {
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ ret = ldb_modify(a_state->sam_ctx, mod);
+ switch (ret) {
+ case LDB_SUCCESS:
+ return NT_STATUS_OK;
+ case LDB_ERR_UNWILLING_TO_PERFORM:
+ return NT_STATUS_MEMBER_NOT_IN_GROUP;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+}
+
+
+/*
+ samr_GetMembersInAlias
+*/
+static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetMembersInAlias *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct lsa_SidPtr *array;
+ unsigned int i, num_members;
+ struct dom_sid *members;
+ NTSTATUS status;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
+ a_state->account_dn, &members,
+ &num_members);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (num_members == 0) {
+ r->out.sids->sids = NULL;
+
+ return NT_STATUS_OK;
+ }
+
+ array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
+ if (array == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_members; i++) {
+ array[i].sid = &members[i];
+ }
+
+ r->out.sids->num_sids = num_members;
+ r->out.sids->sids = array;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_OpenUser
+*/
+static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OpenUser *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *account_name;
+ struct dom_sid *sid;
+ struct ldb_message **msgs;
+ struct dcesrv_handle *u_handle;
+ const char * const attrs[2] = { "sAMAccountName", NULL };
+ int ret;
+
+ ZERO_STRUCTP(r->out.user_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* form the users SID */
+ sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (!sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the user record */
+ ret = gendb_search(d_state->sam_ctx,
+ mem_ctx, d_state->domain_dn, &msgs, attrs,
+ "(&(objectSid=%s)(objectclass=user))",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid));
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching sid %s\n", ret,
+ dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
+ if (account_name == NULL) {
+ DEBUG(0,("sAMAccountName field missing for sid %s\n",
+ dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(mem_ctx, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
+ a_state->account_sid = talloc_steal(a_state, sid);
+ a_state->account_name = talloc_strdup(a_state, account_name);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
+ if (!u_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ u_handle->data = talloc_steal(u_handle, a_state);
+
+ *r->out.user_handle = u_handle->wire_handle;
+
+ return NT_STATUS_OK;
+
+}
+
+
+/*
+ samr_DeleteUser
+*/
+static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteUser *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ int ret;
+
+ *r->out.user_handle = *r->in.user_handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+
+ ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("Failed to delete user: %s: %s\n",
+ ldb_dn_get_linearized(a_state->account_dn),
+ ldb_errstring(a_state->sam_ctx)));
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+
+ talloc_free(h);
+ ZERO_STRUCTP(r->out.user_handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_QueryUserInfo
+*/
+static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryUserInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg, **res;
+ int ret;
+ struct ldb_context *sam_ctx;
+
+ const char * const *attrs = NULL;
+ union samr_UserInfo *info;
+
+ NTSTATUS status;
+
+ *r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+ sam_ctx = a_state->sam_ctx;
+
+ /* fill in the reply */
+ switch (r->in.level) {
+ case 1:
+ {
+ static const char * const attrs2[] = {"sAMAccountName",
+ "displayName",
+ "primaryGroupID",
+ "description",
+ "comment",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 2:
+ {
+ static const char * const attrs2[] = {"comment",
+ "countryCode",
+ "codePage",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 3:
+ {
+ static const char * const attrs2[] = {"sAMAccountName",
+ "displayName",
+ "objectSid",
+ "primaryGroupID",
+ "homeDirectory",
+ "homeDrive",
+ "scriptPath",
+ "profilePath",
+ "userWorkstations",
+ "lastLogon",
+ "lastLogoff",
+ "pwdLastSet",
+ "msDS-UserPasswordExpiryTimeComputed",
+ "logonHours",
+ "badPwdCount",
+ "badPasswordTime",
+ "logonCount",
+ "userAccountControl",
+ "msDS-User-Account-Control-Computed",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 4:
+ {
+ static const char * const attrs2[] = {"logonHours",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 5:
+ {
+ static const char * const attrs2[] = {"sAMAccountName",
+ "displayName",
+ "objectSid",
+ "primaryGroupID",
+ "homeDirectory",
+ "homeDrive",
+ "scriptPath",
+ "profilePath",
+ "description",
+ "userWorkstations",
+ "lastLogon",
+ "lastLogoff",
+ "logonHours",
+ "badPwdCount",
+ "badPasswordTime",
+ "logonCount",
+ "pwdLastSet",
+ "msDS-ResultantPSO",
+ "msDS-UserPasswordExpiryTimeComputed",
+ "accountExpires",
+ "userAccountControl",
+ "msDS-User-Account-Control-Computed",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 6:
+ {
+ static const char * const attrs2[] = {"sAMAccountName",
+ "displayName",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 7:
+ {
+ static const char * const attrs2[] = {"sAMAccountName",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 8:
+ {
+ static const char * const attrs2[] = {"displayName",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 9:
+ {
+ static const char * const attrs2[] = {"primaryGroupID",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 10:
+ {
+ static const char * const attrs2[] = {"homeDirectory",
+ "homeDrive",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 11:
+ {
+ static const char * const attrs2[] = {"scriptPath",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 12:
+ {
+ static const char * const attrs2[] = {"profilePath",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 13:
+ {
+ static const char * const attrs2[] = {"description",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 14:
+ {
+ static const char * const attrs2[] = {"userWorkstations",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 16:
+ {
+ static const char * const attrs2[] = {"userAccountControl",
+ "msDS-User-Account-Control-Computed",
+ "pwdLastSet",
+ "msDS-UserPasswordExpiryTimeComputed",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 17:
+ {
+ static const char * const attrs2[] = {"accountExpires",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 18:
+ {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+ case 20:
+ {
+ static const char * const attrs2[] = {"userParameters",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 21:
+ {
+ static const char * const attrs2[] = {"lastLogon",
+ "lastLogoff",
+ "pwdLastSet",
+ "msDS-ResultantPSO",
+ "msDS-UserPasswordExpiryTimeComputed",
+ "accountExpires",
+ "sAMAccountName",
+ "displayName",
+ "homeDirectory",
+ "homeDrive",
+ "scriptPath",
+ "profilePath",
+ "description",
+ "userWorkstations",
+ "comment",
+ "userParameters",
+ "objectSid",
+ "primaryGroupID",
+ "userAccountControl",
+ "msDS-User-Account-Control-Computed",
+ "logonHours",
+ "badPwdCount",
+ "badPasswordTime",
+ "logonCount",
+ "countryCode",
+ "codePage",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+ default:
+ {
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+ }
+
+ /* pull all the user attributes */
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ a_state->account_dn, &res, attrs);
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ /* allocate the info structure */
+ info = talloc_zero(mem_ctx, union samr_UserInfo);
+ if (info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* fill in the reply */
+ switch (r->in.level) {
+ case 1:
+ QUERY_STRING(msg, info1.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info1.full_name, "displayName");
+ QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
+ QUERY_STRING(msg, info1.description, "description");
+ QUERY_STRING(msg, info1.comment, "comment");
+ break;
+
+ case 2:
+ QUERY_STRING(msg, info2.comment, "comment");
+ QUERY_UINT (msg, info2.country_code, "countryCode");
+ QUERY_UINT (msg, info2.code_page, "codePage");
+ break;
+
+ case 3:
+ QUERY_STRING(msg, info3.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info3.full_name, "displayName");
+ QUERY_RID (msg, info3.rid, "objectSid");
+ QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
+ QUERY_STRING(msg, info3.home_directory, "homeDirectory");
+ QUERY_STRING(msg, info3.home_drive, "homeDrive");
+ QUERY_STRING(msg, info3.logon_script, "scriptPath");
+ QUERY_STRING(msg, info3.profile_path, "profilePath");
+ QUERY_STRING(msg, info3.workstations, "userWorkstations");
+ QUERY_UINT64(msg, info3.last_logon, "lastLogon");
+ QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
+ QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
+ QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
+ QUERY_UINT64(msg, info3.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
+ QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
+ /* level 3 gives the raw badPwdCount value */
+ QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
+ QUERY_UINT (msg, info3.logon_count, "logonCount");
+ QUERY_AFLAGS(msg, info3.acct_flags, "msDS-User-Account-Control-Computed");
+ break;
+
+ case 4:
+ QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
+ break;
+
+ case 5:
+ QUERY_STRING(msg, info5.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info5.full_name, "displayName");
+ QUERY_RID (msg, info5.rid, "objectSid");
+ QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
+ QUERY_STRING(msg, info5.home_directory, "homeDirectory");
+ QUERY_STRING(msg, info5.home_drive, "homeDrive");
+ QUERY_STRING(msg, info5.logon_script, "scriptPath");
+ QUERY_STRING(msg, info5.profile_path, "profilePath");
+ QUERY_STRING(msg, info5.description, "description");
+ QUERY_STRING(msg, info5.workstations, "userWorkstations");
+ QUERY_UINT64(msg, info5.last_logon, "lastLogon");
+ QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
+ QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
+ QUERY_BPWDCT(msg, info5.bad_password_count, "badPwdCount");
+ QUERY_UINT (msg, info5.logon_count, "logonCount");
+ QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
+ QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
+ QUERY_AFLAGS(msg, info5.acct_flags, "msDS-User-Account-Control-Computed");
+ break;
+
+ case 6:
+ QUERY_STRING(msg, info6.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info6.full_name, "displayName");
+ break;
+
+ case 7:
+ QUERY_STRING(msg, info7.account_name, "sAMAccountName");
+ break;
+
+ case 8:
+ QUERY_STRING(msg, info8.full_name, "displayName");
+ break;
+
+ case 9:
+ QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
+ break;
+
+ case 10:
+ QUERY_STRING(msg, info10.home_directory,"homeDirectory");
+ QUERY_STRING(msg, info10.home_drive, "homeDrive");
+ break;
+
+ case 11:
+ QUERY_STRING(msg, info11.logon_script, "scriptPath");
+ break;
+
+ case 12:
+ QUERY_STRING(msg, info12.profile_path, "profilePath");
+ break;
+
+ case 13:
+ QUERY_STRING(msg, info13.description, "description");
+ break;
+
+ case 14:
+ QUERY_STRING(msg, info14.workstations, "userWorkstations");
+ break;
+
+ case 16:
+ QUERY_AFLAGS(msg, info16.acct_flags, "msDS-User-Account-Control-Computed");
+ break;
+
+ case 17:
+ QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
+ break;
+
+ case 20:
+ status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info20.parameters);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(info);
+ return status;
+ }
+ break;
+
+ case 21:
+ QUERY_UINT64(msg, info21.last_logon, "lastLogon");
+ QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
+ QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
+ QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
+ QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
+ QUERY_UINT64(msg, info21.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
+ QUERY_STRING(msg, info21.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info21.full_name, "displayName");
+ QUERY_STRING(msg, info21.home_directory, "homeDirectory");
+ QUERY_STRING(msg, info21.home_drive, "homeDrive");
+ QUERY_STRING(msg, info21.logon_script, "scriptPath");
+ QUERY_STRING(msg, info21.profile_path, "profilePath");
+ QUERY_STRING(msg, info21.description, "description");
+ QUERY_STRING(msg, info21.workstations, "userWorkstations");
+ QUERY_STRING(msg, info21.comment, "comment");
+ status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info21.parameters);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(info);
+ return status;
+ }
+
+ QUERY_RID (msg, info21.rid, "objectSid");
+ QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
+ QUERY_AFLAGS(msg, info21.acct_flags, "msDS-User-Account-Control-Computed");
+ info->info21.fields_present = 0x08FFFFFF;
+ QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
+ QUERY_BPWDCT(msg, info21.bad_password_count, "badPwdCount");
+ QUERY_UINT (msg, info21.logon_count, "logonCount");
+ if ((info->info21.acct_flags & ACB_PW_EXPIRED) != 0) {
+ info->info21.password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
+ } else {
+ info->info21.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
+ }
+ QUERY_UINT (msg, info21.country_code, "countryCode");
+ QUERY_UINT (msg, info21.code_page, "codePage");
+ break;
+
+
+ default:
+ talloc_free(info);
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ *r->out.info = info;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetUserInfo
+*/
+static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetUserInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg;
+ int ret;
+ NTSTATUS status = NT_STATUS_OK;
+ struct ldb_context *sam_ctx;
+ DATA_BLOB session_key = data_blob_null;
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+ sam_ctx = a_state->sam_ctx;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_transaction_start(sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ DBG_ERR("Failed to start a transaction: %s\n",
+ ldb_errstring(sam_ctx));
+ return NT_STATUS_LOCK_NOT_GRANTED;
+ }
+
+ switch (r->in.level) {
+ case 2:
+ SET_STRING(msg, info2.comment, "comment");
+ SET_UINT (msg, info2.country_code, "countryCode");
+ SET_UINT (msg, info2.code_page, "codePage");
+ break;
+
+ case 4:
+ SET_LHOURS(msg, info4.logon_hours, "logonHours");
+ break;
+
+ case 6:
+ SET_STRING(msg, info6.account_name, "samAccountName");
+ SET_STRING(msg, info6.full_name, "displayName");
+ break;
+
+ case 7:
+ SET_STRING(msg, info7.account_name, "samAccountName");
+ break;
+
+ case 8:
+ SET_STRING(msg, info8.full_name, "displayName");
+ break;
+
+ case 9:
+ SET_UINT(msg, info9.primary_gid, "primaryGroupID");
+ break;
+
+ case 10:
+ SET_STRING(msg, info10.home_directory, "homeDirectory");
+ SET_STRING(msg, info10.home_drive, "homeDrive");
+ break;
+
+ case 11:
+ SET_STRING(msg, info11.logon_script, "scriptPath");
+ break;
+
+ case 12:
+ SET_STRING(msg, info12.profile_path, "profilePath");
+ break;
+
+ case 13:
+ SET_STRING(msg, info13.description, "description");
+ break;
+
+ case 14:
+ SET_STRING(msg, info14.workstations, "userWorkstations");
+ break;
+
+ case 16:
+ SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
+ break;
+
+ case 17:
+ SET_UINT64(msg, info17.acct_expiry, "accountExpires");
+ break;
+
+ case 18:
+ status = samr_set_password_buffers(dce_call,
+ sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx,
+ r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
+ r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (r->in.info->info18.password_expired > 0) {
+ struct ldb_message_element *set_el;
+ if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ set_el = ldb_msg_find_element(msg, "pwdLastSet");
+ set_el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+ break;
+
+ case 20:
+ SET_PARAMETERS(msg, info20.parameters, "userParameters");
+ break;
+
+ case 21:
+ if (r->in.info->info21.fields_present == 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+#define IFSET(bit) if (bit & r->in.info->info21.fields_present)
+ IFSET(SAMR_FIELD_LAST_LOGON)
+ SET_UINT64(msg, info21.last_logon, "lastLogon");
+ IFSET(SAMR_FIELD_LAST_LOGOFF)
+ SET_UINT64(msg, info21.last_logoff, "lastLogoff");
+ IFSET(SAMR_FIELD_ACCT_EXPIRY)
+ SET_UINT64(msg, info21.acct_expiry, "accountExpires");
+ IFSET(SAMR_FIELD_ACCOUNT_NAME)
+ SET_STRING(msg, info21.account_name, "samAccountName");
+ IFSET(SAMR_FIELD_FULL_NAME)
+ SET_STRING(msg, info21.full_name, "displayName");
+ IFSET(SAMR_FIELD_HOME_DIRECTORY)
+ SET_STRING(msg, info21.home_directory, "homeDirectory");
+ IFSET(SAMR_FIELD_HOME_DRIVE)
+ SET_STRING(msg, info21.home_drive, "homeDrive");
+ IFSET(SAMR_FIELD_LOGON_SCRIPT)
+ SET_STRING(msg, info21.logon_script, "scriptPath");
+ IFSET(SAMR_FIELD_PROFILE_PATH)
+ SET_STRING(msg, info21.profile_path, "profilePath");
+ IFSET(SAMR_FIELD_DESCRIPTION)
+ SET_STRING(msg, info21.description, "description");
+ IFSET(SAMR_FIELD_WORKSTATIONS)
+ SET_STRING(msg, info21.workstations, "userWorkstations");
+ IFSET(SAMR_FIELD_COMMENT)
+ SET_STRING(msg, info21.comment, "comment");
+ IFSET(SAMR_FIELD_PARAMETERS)
+ SET_PARAMETERS(msg, info21.parameters, "userParameters");
+ IFSET(SAMR_FIELD_PRIMARY_GID)
+ SET_UINT(msg, info21.primary_gid, "primaryGroupID");
+ IFSET(SAMR_FIELD_ACCT_FLAGS)
+ SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
+ IFSET(SAMR_FIELD_LOGON_HOURS)
+ SET_LHOURS(msg, info21.logon_hours, "logonHours");
+ IFSET(SAMR_FIELD_BAD_PWD_COUNT)
+ SET_UINT (msg, info21.bad_password_count, "badPwdCount");
+ IFSET(SAMR_FIELD_NUM_LOGONS)
+ SET_UINT (msg, info21.logon_count, "logonCount");
+ IFSET(SAMR_FIELD_COUNTRY_CODE)
+ SET_UINT (msg, info21.country_code, "countryCode");
+ IFSET(SAMR_FIELD_CODE_PAGE)
+ SET_UINT (msg, info21.code_page, "codePage");
+
+ /* password change fields */
+ IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
+ status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
+ | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
+ uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
+
+ if (r->in.info->info21.lm_password_set) {
+ if ((r->in.info->info21.lm_owf_password.length != 16)
+ || (r->in.info->info21.lm_owf_password.size != 16)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
+ }
+ if (r->in.info->info21.nt_password_set) {
+ if ((r->in.info->info21.nt_owf_password.length != 16)
+ || (r->in.info->info21.nt_owf_password.size != 16)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
+ }
+ status = samr_set_password_buffers(dce_call,
+ sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx,
+ lm_pwd_hash,
+ nt_pwd_hash);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ }
+
+
+ IFSET(SAMR_FIELD_EXPIRED_FLAG) {
+ const char *t = "0";
+ struct ldb_message_element *set_el;
+ if (r->in.info->info21.password_expired
+ == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
+ t = "-1";
+ }
+ if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ set_el = ldb_msg_find_element(msg, "pwdLastSet");
+ set_el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+#undef IFSET
+ break;
+
+ case 23:
+ if (r->in.info->info23.info.fields_present == 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+#define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
+ IFSET(SAMR_FIELD_LAST_LOGON)
+ SET_UINT64(msg, info23.info.last_logon, "lastLogon");
+ IFSET(SAMR_FIELD_LAST_LOGOFF)
+ SET_UINT64(msg, info23.info.last_logoff, "lastLogoff");
+ IFSET(SAMR_FIELD_ACCT_EXPIRY)
+ SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
+ IFSET(SAMR_FIELD_ACCOUNT_NAME)
+ SET_STRING(msg, info23.info.account_name, "samAccountName");
+ IFSET(SAMR_FIELD_FULL_NAME)
+ SET_STRING(msg, info23.info.full_name, "displayName");
+ IFSET(SAMR_FIELD_HOME_DIRECTORY)
+ SET_STRING(msg, info23.info.home_directory, "homeDirectory");
+ IFSET(SAMR_FIELD_HOME_DRIVE)
+ SET_STRING(msg, info23.info.home_drive, "homeDrive");
+ IFSET(SAMR_FIELD_LOGON_SCRIPT)
+ SET_STRING(msg, info23.info.logon_script, "scriptPath");
+ IFSET(SAMR_FIELD_PROFILE_PATH)
+ SET_STRING(msg, info23.info.profile_path, "profilePath");
+ IFSET(SAMR_FIELD_DESCRIPTION)
+ SET_STRING(msg, info23.info.description, "description");
+ IFSET(SAMR_FIELD_WORKSTATIONS)
+ SET_STRING(msg, info23.info.workstations, "userWorkstations");
+ IFSET(SAMR_FIELD_COMMENT)
+ SET_STRING(msg, info23.info.comment, "comment");
+ IFSET(SAMR_FIELD_PARAMETERS)
+ SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
+ IFSET(SAMR_FIELD_PRIMARY_GID)
+ SET_UINT(msg, info23.info.primary_gid, "primaryGroupID");
+ IFSET(SAMR_FIELD_ACCT_FLAGS)
+ SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
+ IFSET(SAMR_FIELD_LOGON_HOURS)
+ SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
+ IFSET(SAMR_FIELD_BAD_PWD_COUNT)
+ SET_UINT (msg, info23.info.bad_password_count, "badPwdCount");
+ IFSET(SAMR_FIELD_NUM_LOGONS)
+ SET_UINT (msg, info23.info.logon_count, "logonCount");
+
+ IFSET(SAMR_FIELD_COUNTRY_CODE)
+ SET_UINT (msg, info23.info.country_code, "countryCode");
+ IFSET(SAMR_FIELD_CODE_PAGE)
+ SET_UINT (msg, info23.info.code_page, "codePage");
+
+ /* password change fields */
+ IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
+ status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
+ status = samr_set_password(dce_call,
+ sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx,
+ &r->in.info->info23.password);
+ } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
+ status = samr_set_password(dce_call,
+ sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx,
+ &r->in.info->info23.password);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ IFSET(SAMR_FIELD_EXPIRED_FLAG) {
+ const char *t = "0";
+ struct ldb_message_element *set_el;
+ if (r->in.info->info23.info.password_expired
+ == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
+ t = "-1";
+ }
+ if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ set_el = ldb_msg_find_element(msg, "pwdLastSet");
+ set_el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+#undef IFSET
+ break;
+
+ /* the set password levels are handled separately */
+ case 24:
+ status = samr_set_password(dce_call,
+ sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx,
+ &r->in.info->info24.password);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (r->in.info->info24.password_expired > 0) {
+ struct ldb_message_element *set_el;
+ if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ set_el = ldb_msg_find_element(msg, "pwdLastSet");
+ set_el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+ break;
+
+ case 25:
+ if (r->in.info->info25.info.fields_present == 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+#define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
+ IFSET(SAMR_FIELD_LAST_LOGON)
+ SET_UINT64(msg, info25.info.last_logon, "lastLogon");
+ IFSET(SAMR_FIELD_LAST_LOGOFF)
+ SET_UINT64(msg, info25.info.last_logoff, "lastLogoff");
+ IFSET(SAMR_FIELD_ACCT_EXPIRY)
+ SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
+ IFSET(SAMR_FIELD_ACCOUNT_NAME)
+ SET_STRING(msg, info25.info.account_name, "samAccountName");
+ IFSET(SAMR_FIELD_FULL_NAME)
+ SET_STRING(msg, info25.info.full_name, "displayName");
+ IFSET(SAMR_FIELD_HOME_DIRECTORY)
+ SET_STRING(msg, info25.info.home_directory, "homeDirectory");
+ IFSET(SAMR_FIELD_HOME_DRIVE)
+ SET_STRING(msg, info25.info.home_drive, "homeDrive");
+ IFSET(SAMR_FIELD_LOGON_SCRIPT)
+ SET_STRING(msg, info25.info.logon_script, "scriptPath");
+ IFSET(SAMR_FIELD_PROFILE_PATH)
+ SET_STRING(msg, info25.info.profile_path, "profilePath");
+ IFSET(SAMR_FIELD_DESCRIPTION)
+ SET_STRING(msg, info25.info.description, "description");
+ IFSET(SAMR_FIELD_WORKSTATIONS)
+ SET_STRING(msg, info25.info.workstations, "userWorkstations");
+ IFSET(SAMR_FIELD_COMMENT)
+ SET_STRING(msg, info25.info.comment, "comment");
+ IFSET(SAMR_FIELD_PARAMETERS)
+ SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
+ IFSET(SAMR_FIELD_PRIMARY_GID)
+ SET_UINT(msg, info25.info.primary_gid, "primaryGroupID");
+ IFSET(SAMR_FIELD_ACCT_FLAGS)
+ SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
+ IFSET(SAMR_FIELD_LOGON_HOURS)
+ SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
+ IFSET(SAMR_FIELD_BAD_PWD_COUNT)
+ SET_UINT (msg, info25.info.bad_password_count, "badPwdCount");
+ IFSET(SAMR_FIELD_NUM_LOGONS)
+ SET_UINT (msg, info25.info.logon_count, "logonCount");
+ IFSET(SAMR_FIELD_COUNTRY_CODE)
+ SET_UINT (msg, info25.info.country_code, "countryCode");
+ IFSET(SAMR_FIELD_CODE_PAGE)
+ SET_UINT (msg, info25.info.code_page, "codePage");
+
+ /* password change fields */
+ IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
+ status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
+ status = samr_set_password_ex(dce_call,
+ sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx,
+ &r->in.info->info25.password);
+ } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
+ status = samr_set_password_ex(dce_call,
+ sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx,
+ &r->in.info->info25.password);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ IFSET(SAMR_FIELD_EXPIRED_FLAG) {
+ const char *t = "0";
+ struct ldb_message_element *set_el;
+ if (r->in.info->info25.info.password_expired
+ == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
+ t = "-1";
+ }
+ if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ set_el = ldb_msg_find_element(msg, "pwdLastSet");
+ set_el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+#undef IFSET
+ break;
+
+ /* the set password levels are handled separately */
+ case 26:
+ status = samr_set_password_ex(dce_call,
+ sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx,
+ &r->in.info->info26.password);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (r->in.info->info26.password_expired > 0) {
+ const char *t = "0";
+ struct ldb_message_element *set_el;
+ if (r->in.info->info26.password_expired
+ == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
+ t = "-1";
+ }
+ if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ set_el = ldb_msg_find_element(msg, "pwdLastSet");
+ set_el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+ break;
+
+ case 31:
+ status = dcesrv_transport_session_key(dce_call, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_NOTICE("samr: failed to get session key: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = samr_set_password_aes(dce_call,
+ mem_ctx,
+ &session_key,
+ sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ &r->in.info->info31.password,
+ DSDB_PASSWORD_RESET);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (r->in.info->info31.password_expired > 0) {
+ const char *t = "0";
+ struct ldb_message_element *set_el = NULL;
+
+ if (r->in.info->info31.password_expired ==
+ PASS_DONT_CHANGE_AT_NEXT_LOGON) {
+ t = "-1";
+ }
+
+ ret = ldb_msg_add_string(msg, "pwdLastSet", t);
+ if (ret != LDB_SUCCESS) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ set_el = ldb_msg_find_element(msg, "pwdLastSet");
+ set_el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ break;
+ case 32:
+ status = dcesrv_transport_session_key(dce_call, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_NOTICE("samr: failed to get session key: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ if (r->in.info->info32.info.fields_present == 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+#define IFSET(bit) if (bit & r->in.info->info32.info.fields_present)
+ IFSET(SAMR_FIELD_LAST_LOGON)
+ {
+ SET_UINT64(msg, info32.info.last_logon, "lastLogon");
+ }
+ IFSET(SAMR_FIELD_LAST_LOGOFF)
+ {
+ SET_UINT64(msg, info32.info.last_logoff, "lastLogoff");
+ }
+ IFSET(SAMR_FIELD_ACCT_EXPIRY)
+ {
+ SET_UINT64(msg,
+ info32.info.acct_expiry,
+ "accountExpires");
+ }
+ IFSET(SAMR_FIELD_ACCOUNT_NAME)
+ {
+ SET_STRING(msg,
+ info32.info.account_name,
+ "samAccountName");
+ }
+ IFSET(SAMR_FIELD_FULL_NAME)
+ {
+ SET_STRING(msg, info32.info.full_name, "displayName");
+ }
+ IFSET(SAMR_FIELD_HOME_DIRECTORY)
+ {
+ SET_STRING(msg,
+ info32.info.home_directory,
+ "homeDirectory");
+ }
+ IFSET(SAMR_FIELD_HOME_DRIVE)
+ {
+ SET_STRING(msg, info32.info.home_drive, "homeDrive");
+ }
+ IFSET(SAMR_FIELD_LOGON_SCRIPT)
+ {
+ SET_STRING(msg, info32.info.logon_script, "scriptPath");
+ }
+ IFSET(SAMR_FIELD_PROFILE_PATH)
+ {
+ SET_STRING(msg,
+ info32.info.profile_path,
+ "profilePath");
+ }
+ IFSET(SAMR_FIELD_DESCRIPTION)
+ {
+ SET_STRING(msg, info32.info.description, "description");
+ }
+ IFSET(SAMR_FIELD_WORKSTATIONS)
+ {
+ SET_STRING(msg,
+ info32.info.workstations,
+ "userWorkstations");
+ }
+ IFSET(SAMR_FIELD_COMMENT)
+ {
+ SET_STRING(msg, info32.info.comment, "comment");
+ }
+ IFSET(SAMR_FIELD_PARAMETERS)
+ {
+ SET_PARAMETERS(msg,
+ info32.info.parameters,
+ "userParameters");
+ }
+ IFSET(SAMR_FIELD_PRIMARY_GID)
+ {
+ SET_UINT(msg,
+ info32.info.primary_gid,
+ "primaryGroupID");
+ }
+ IFSET(SAMR_FIELD_ACCT_FLAGS)
+ {
+ SET_AFLAGS(msg,
+ info32.info.acct_flags,
+ "userAccountControl");
+ }
+ IFSET(SAMR_FIELD_LOGON_HOURS)
+ {
+ SET_LHOURS(msg, info32.info.logon_hours, "logonHours");
+ }
+ IFSET(SAMR_FIELD_BAD_PWD_COUNT)
+ {
+ SET_UINT(msg,
+ info32.info.bad_password_count,
+ "badPwdCount");
+ }
+ IFSET(SAMR_FIELD_NUM_LOGONS)
+ {
+ SET_UINT(msg, info32.info.logon_count, "logonCount");
+ }
+ IFSET(SAMR_FIELD_COUNTRY_CODE)
+ {
+ SET_UINT(msg, info32.info.country_code, "countryCode");
+ }
+ IFSET(SAMR_FIELD_CODE_PAGE)
+ {
+ SET_UINT(msg, info32.info.code_page, "codePage");
+ }
+
+ /* password change fields */
+ IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
+ {
+ status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT)
+ {
+ status = samr_set_password_aes(
+ dce_call,
+ mem_ctx,
+ &session_key,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ &r->in.info->info32.password,
+ DSDB_PASSWORD_RESET);
+ }
+ else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT)
+ {
+ status = samr_set_password_aes(
+ dce_call,
+ mem_ctx,
+ &session_key,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ &r->in.info->info32.password,
+ DSDB_PASSWORD_RESET);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ IFSET(SAMR_FIELD_EXPIRED_FLAG)
+ {
+ const char *t = "0";
+ struct ldb_message_element *set_el;
+ if (r->in.info->info32.info.password_expired ==
+ PASS_DONT_CHANGE_AT_NEXT_LOGON) {
+ t = "-1";
+ }
+ if (ldb_msg_add_string(msg, "pwdLastSet", t) !=
+ LDB_SUCCESS) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ set_el = ldb_msg_find_element(msg, "pwdLastSet");
+ set_el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+#undef IFSET
+
+ break;
+ default:
+ /* many info classes are not valid for SetUserInfo */
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ /* modify the samdb record */
+ if (msg->num_elements > 0) {
+ ret = ldb_modify(sam_ctx, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1,("Failed to modify record %s: %s\n",
+ ldb_dn_get_linearized(a_state->account_dn),
+ ldb_errstring(sam_ctx)));
+
+ status = dsdb_ldb_err_to_ntstatus(ret);
+ goto done;
+ }
+ }
+
+ ret = ldb_transaction_commit(sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ DBG_ERR("Failed to commit transaction modifying account record "
+ "%s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(sam_ctx));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = NT_STATUS_OK;
+done:
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_transaction_cancel(sam_ctx);
+ }
+
+ return status;
+}
+
+
+/*
+ samr_GetGroupsForUser
+*/
+static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetGroupsForUser *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_result *res, *res_memberof;
+ const char * const attrs[] = { "primaryGroupID",
+ "memberOf",
+ NULL };
+ const char * const group_attrs[] = { "objectSid",
+ NULL };
+
+ struct samr_RidWithAttributeArray *array;
+ struct ldb_message_element *memberof_el;
+ int i, ret, count = 0;
+ uint32_t primary_group_id;
+ char *filter;
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ ret = dsdb_search_dn(a_state->sam_ctx, mem_ctx,
+ &res,
+ a_state->account_dn,
+ attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ return NT_STATUS_NO_SUCH_USER;
+ } else if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (res->count != 1) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ primary_group_id = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
+ 0);
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(|(grouptype=%d)(grouptype=%d))"
+ "(objectclass=group)(|",
+ GTYPE_SECURITY_UNIVERSAL_GROUP,
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memberof_el = ldb_msg_find_element(res->msgs[0], "memberOf");
+ if (memberof_el != NULL) {
+ for (i = 0; i < memberof_el->num_values; i++) {
+ const struct ldb_val *memberof_sid_binary;
+ char *memberof_sid_escaped;
+ struct ldb_dn *memberof_dn
+ = ldb_dn_from_ldb_val(mem_ctx,
+ a_state->sam_ctx,
+ &memberof_el->values[i]);
+ if (memberof_dn == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ memberof_sid_binary
+ = ldb_dn_get_extended_component(memberof_dn,
+ "SID");
+ if (memberof_sid_binary == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ memberof_sid_escaped = ldb_binary_encode(mem_ctx,
+ *memberof_sid_binary);
+ if (memberof_sid_escaped == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ filter = talloc_asprintf_append(filter, "(objectSID=%s)",
+ memberof_sid_escaped);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ ret = dsdb_search(a_state->sam_ctx, mem_ctx,
+ &res_memberof,
+ d_state->domain_dn,
+ LDB_SCOPE_SUBTREE,
+ group_attrs, 0,
+ "%s))", filter);
+
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ count = res_memberof->count;
+ }
+
+ array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
+ if (array == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ array->count = 0;
+ array->rids = NULL;
+
+ array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
+ count + 1);
+ if (array->rids == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* Adds the primary group */
+
+ array->rids[0].rid = primary_group_id;
+ array->rids[0].attributes = SE_GROUP_MANDATORY
+ | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
+ array->count += 1;
+
+ /* Adds the additional groups */
+ for (i = 0; i < count; i++) {
+ struct dom_sid *group_sid;
+
+ group_sid = samdb_result_dom_sid(mem_ctx,
+ res_memberof->msgs[i],
+ "objectSid");
+ if (group_sid == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ array->rids[i + 1].rid =
+ group_sid->sub_auths[group_sid->num_auths-1];
+ array->rids[i + 1].attributes = SE_GROUP_MANDATORY
+ | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
+ array->count += 1;
+ }
+
+ *r->out.rids = array;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * samr_QueryDisplayInfo
+ *
+ * A cache of the GUID's matching the last query is maintained
+ * in the SAMR_QUERY_DISPLAY_INFO_CACHE guid_cache maintained o
+ * n the dcesrv_handle.
+ */
+static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDisplayInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_result *res;
+ uint32_t i;
+ uint32_t results = 0;
+ uint32_t count = 0;
+ const char *const cache_attrs[] = {"objectGUID", NULL};
+ const char *const attrs[] = {
+ "objectSID", "sAMAccountName", "displayName", "description", NULL};
+ struct samr_DispEntryFull *entriesFull = NULL;
+ struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
+ struct samr_DispEntryAscii *entriesAscii = NULL;
+ struct samr_DispEntryGeneral *entriesGeneral = NULL;
+ const char *filter;
+ int ret;
+ NTSTATUS status;
+ struct samr_guid_cache *cache = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ cache = &d_state->guid_caches[SAMR_QUERY_DISPLAY_INFO_CACHE];
+ /*
+ * Can the cached results be used?
+ * The cache is discarded if the start index is zero, or the requested
+ * level is different from that in the cache.
+ */
+ if ((r->in.start_idx == 0) || (r->in.level != cache->handle)) {
+ /*
+ * The cached results can not be used, so will need to query
+ * the database.
+ */
+
+ /*
+ * Get the search filter for the current level
+ */
+ switch (r->in.level) {
+ case 1:
+ case 4:
+ filter = talloc_asprintf(mem_ctx,
+ "(&(objectclass=user)"
+ "(sAMAccountType=%d))",
+ ATYPE_NORMAL_ACCOUNT);
+ break;
+ case 2:
+ filter = talloc_asprintf(mem_ctx,
+ "(&(objectclass=user)"
+ "(sAMAccountType=%d))",
+ ATYPE_WORKSTATION_TRUST);
+ break;
+ case 3:
+ case 5:
+ filter =
+ talloc_asprintf(mem_ctx,
+ "(&(|(groupType=%d)(groupType=%d))"
+ "(objectClass=group))",
+ GTYPE_SECURITY_UNIVERSAL_GROUP,
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+ clear_guid_cache(cache);
+
+ /*
+ * search for all requested objects in all domains.
+ */
+ ret = dsdb_search(d_state->sam_ctx,
+ mem_ctx,
+ &res,
+ ldb_get_default_basedn(d_state->sam_ctx),
+ LDB_SCOPE_SUBTREE,
+ cache_attrs,
+ 0,
+ "%s",
+ filter);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if ((res->count == 0) || (r->in.max_entries == 0)) {
+ return NT_STATUS_OK;
+ }
+
+ status = load_guid_cache(cache, d_state, res->count, res->msgs);
+ TALLOC_FREE(res);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ cache->handle = r->in.level;
+ }
+ *r->out.total_size = cache->size;
+
+ /*
+ * if there are no entries or the requested start index is greater
+ * than the number of entries, we return an empty response.
+ */
+ if (r->in.start_idx >= cache->size) {
+ *r->out.returned_size = 0;
+ switch(r->in.level) {
+ case 1:
+ r->out.info->info1.count = *r->out.returned_size;
+ r->out.info->info1.entries = NULL;
+ break;
+ case 2:
+ r->out.info->info2.count = *r->out.returned_size;
+ r->out.info->info2.entries = NULL;
+ break;
+ case 3:
+ r->out.info->info3.count = *r->out.returned_size;
+ r->out.info->info3.entries = NULL;
+ break;
+ case 4:
+ r->out.info->info4.count = *r->out.returned_size;
+ r->out.info->info4.entries = NULL;
+ break;
+ case 5:
+ r->out.info->info5.count = *r->out.returned_size;
+ r->out.info->info5.entries = NULL;
+ break;
+ }
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * Allocate an array of the appropriate result structures for the
+ * current query level.
+ *
+ * r->in.start_idx is always < cache->size due to the check above
+ */
+ results = MIN((cache->size - r->in.start_idx), r->in.max_entries);
+ switch (r->in.level) {
+ case 1:
+ entriesGeneral = talloc_array(
+ mem_ctx, struct samr_DispEntryGeneral, results);
+ break;
+ case 2:
+ entriesFull =
+ talloc_array(mem_ctx, struct samr_DispEntryFull, results);
+ break;
+ case 3:
+ entriesFullGroup = talloc_array(
+ mem_ctx, struct samr_DispEntryFullGroup, results);
+ break;
+ case 4:
+ case 5:
+ entriesAscii =
+ talloc_array(mem_ctx, struct samr_DispEntryAscii, results);
+ break;
+ }
+
+ if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
+ (entriesAscii == NULL) && (entriesFullGroup == NULL))
+ return NT_STATUS_NO_MEMORY;
+
+ /*
+ * Process the list of result GUID's.
+ * Read the details of each object and populate the result structure
+ * for the current level.
+ */
+ count = 0;
+ for (i = 0; i < results; i++) {
+ struct dom_sid *objectsid;
+ struct ldb_result *rec;
+ const uint32_t idx = r->in.start_idx + i;
+ uint32_t rid;
+
+ /*
+ * Read an object from disk using the GUID as the key
+ *
+ * If the object can not be read, or it does not have a SID
+ * it is ignored. In this case the number of entries returned
+ * will be less than the requested size, there will also be
+ * a gap in the idx numbers in the returned elements e.g. if
+ * there are 3 GUIDs a, b, c in the cache and b is deleted from
+ * disk then details for a, and c will be returned with
+ * idx values of 1 and 3 respectively.
+ *
+ */
+ ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
+ mem_ctx,
+ &rec,
+ &cache->entries[idx],
+ attrs,
+ 0);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ struct GUID_txt_buf guid_buf;
+ char *guid_str =
+ GUID_buf_string(&cache->entries[idx],
+ &guid_buf);
+ DBG_WARNING("GUID [%s] not found\n", guid_str);
+ continue;
+ } else if (ret != LDB_SUCCESS) {
+ clear_guid_cache(cache);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ objectsid = samdb_result_dom_sid(mem_ctx,
+ rec->msgs[0],
+ "objectSID");
+ if (objectsid == NULL) {
+ struct GUID_txt_buf guid_buf;
+ DBG_WARNING(
+ "objectSID for GUID [%s] not found\n",
+ GUID_buf_string(&cache->entries[idx], &guid_buf));
+ continue;
+ }
+ status = dom_sid_split_rid(NULL,
+ objectsid,
+ NULL,
+ &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ struct dom_sid_buf sid_buf;
+ struct GUID_txt_buf guid_buf;
+ DBG_WARNING(
+ "objectSID [%s] for GUID [%s] invalid\n",
+ dom_sid_str_buf(objectsid, &sid_buf),
+ GUID_buf_string(&cache->entries[idx], &guid_buf));
+ continue;
+ }
+
+ /*
+ * Populate the result structure for the current object
+ */
+ switch(r->in.level) {
+ case 1:
+
+ entriesGeneral[count].idx = idx + 1;
+ entriesGeneral[count].rid = rid;
+
+ entriesGeneral[count].acct_flags =
+ samdb_result_acct_flags(rec->msgs[0], NULL);
+ entriesGeneral[count].account_name.string =
+ ldb_msg_find_attr_as_string(
+ rec->msgs[0], "sAMAccountName", "");
+ entriesGeneral[count].full_name.string =
+ ldb_msg_find_attr_as_string(
+ rec->msgs[0], "displayName", "");
+ entriesGeneral[count].description.string =
+ ldb_msg_find_attr_as_string(
+ rec->msgs[0], "description", "");
+ break;
+ case 2:
+ entriesFull[count].idx = idx + 1;
+ entriesFull[count].rid = rid;
+
+ /*
+ * No idea why we need to or in ACB_NORMAL here,
+ * but this is what Win2k3 seems to do...
+ */
+ entriesFull[count].acct_flags =
+ samdb_result_acct_flags(rec->msgs[0], NULL) |
+ ACB_NORMAL;
+ entriesFull[count].account_name.string =
+ ldb_msg_find_attr_as_string(
+ rec->msgs[0], "sAMAccountName", "");
+ entriesFull[count].description.string =
+ ldb_msg_find_attr_as_string(
+ rec->msgs[0], "description", "");
+ break;
+ case 3:
+ entriesFullGroup[count].idx = idx + 1;
+ entriesFullGroup[count].rid = rid;
+
+ /*
+ * We get a "7" here for groups
+ */
+ entriesFullGroup[count].acct_flags =
+ SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED;
+ entriesFullGroup[count].account_name.string =
+ ldb_msg_find_attr_as_string(
+ rec->msgs[0], "sAMAccountName", "");
+ entriesFullGroup[count].description.string =
+ ldb_msg_find_attr_as_string(
+ rec->msgs[0], "description", "");
+ break;
+ case 4:
+ case 5:
+ entriesAscii[count].idx = idx + 1;
+ entriesAscii[count].account_name.string =
+ ldb_msg_find_attr_as_string(
+ rec->msgs[0], "sAMAccountName", "");
+ break;
+ }
+ count++;
+ }
+
+ /*
+ * Build the response based on the request level.
+ */
+ *r->out.returned_size = count;
+ switch(r->in.level) {
+ case 1:
+ r->out.info->info1.count = count;
+ r->out.info->info1.entries = entriesGeneral;
+ break;
+ case 2:
+ r->out.info->info2.count = count;
+ r->out.info->info2.entries = entriesFull;
+ break;
+ case 3:
+ r->out.info->info3.count = count;
+ r->out.info->info3.entries = entriesFullGroup;
+ break;
+ case 4:
+ r->out.info->info4.count = count;
+ r->out.info->info4.entries = entriesAscii;
+ break;
+ case 5:
+ r->out.info->info5.count = count;
+ r->out.info->info5.entries = entriesAscii;
+ break;
+ }
+
+ return ((r->in.start_idx + results) < cache->size)
+ ? STATUS_MORE_ENTRIES
+ : NT_STATUS_OK;
+}
+
+
+/*
+ samr_GetDisplayEnumerationIndex
+*/
+static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetDisplayEnumerationIndex *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_TestPrivateFunctionsDomain
+*/
+static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_TestPrivateFunctionsDomain *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ samr_TestPrivateFunctionsUser
+*/
+static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_TestPrivateFunctionsUser *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ samr_GetUserPwInfo
+*/
+static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetUserPwInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+
+ ZERO_STRUCTP(r->out.info);
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+
+ r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
+ mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
+ NULL);
+ r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
+ mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_RemoveMemberFromForeignDomain
+*/
+static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_RemoveMemberFromForeignDomain *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ const char *memberdn;
+ struct ldb_message **res;
+ const char *no_attrs[] = { NULL };
+ int i, count;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
+ "distinguishedName", "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
+ /* Nothing to do */
+ if (memberdn == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &res, no_attrs,
+ d_state->domain_sid,
+ "(&(member=%s)(objectClass=group)"
+ "(|(groupType=%d)(groupType=%d)))",
+ memberdn,
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+
+ if (count < 0)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ for (i=0; i<count; i++) {
+ struct ldb_message *mod;
+ int ret;
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = res[i]->dn;
+
+ if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
+ "member", memberdn) != LDB_SUCCESS)
+ return NT_STATUS_NO_MEMORY;
+
+ ret = ldb_modify(d_state->sam_ctx, mod);
+ talloc_free(mod);
+ if (ret != LDB_SUCCESS) {
+ return dsdb_ldb_err_to_ntstatus(ret);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_QueryDomainInfo2
+
+ just an alias for samr_QueryDomainInfo
+*/
+static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDomainInfo2 *r)
+{
+ struct samr_QueryDomainInfo r1;
+ NTSTATUS status;
+
+ r1 = (struct samr_QueryDomainInfo) {
+ .in.domain_handle = r->in.domain_handle,
+ .in.level = r->in.level,
+ .out.info = r->out.info,
+ };
+
+ status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
+
+ return status;
+}
+
+
+/*
+ samr_QueryUserInfo2
+
+ just an alias for samr_QueryUserInfo
+*/
+static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryUserInfo2 *r)
+{
+ struct samr_QueryUserInfo r1;
+ NTSTATUS status;
+
+ r1 = (struct samr_QueryUserInfo) {
+ .in.user_handle = r->in.user_handle,
+ .in.level = r->in.level,
+ .out.info = r->out.info
+ };
+
+ status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
+
+ return status;
+}
+
+
+/*
+ samr_QueryDisplayInfo2
+*/
+static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDisplayInfo2 *r)
+{
+ struct samr_QueryDisplayInfo q;
+ NTSTATUS result;
+
+ q = (struct samr_QueryDisplayInfo) {
+ .in.domain_handle = r->in.domain_handle,
+ .in.level = r->in.level,
+ .in.start_idx = r->in.start_idx,
+ .in.max_entries = r->in.max_entries,
+ .in.buf_size = r->in.buf_size,
+ .out.total_size = r->out.total_size,
+ .out.returned_size = r->out.returned_size,
+ .out.info = r->out.info,
+ };
+
+ result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
+
+ return result;
+}
+
+
+/*
+ samr_GetDisplayEnumerationIndex2
+*/
+static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetDisplayEnumerationIndex2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_QueryDisplayInfo3
+*/
+static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDisplayInfo3 *r)
+{
+ struct samr_QueryDisplayInfo q;
+ NTSTATUS result;
+
+ q = (struct samr_QueryDisplayInfo) {
+ .in.domain_handle = r->in.domain_handle,
+ .in.level = r->in.level,
+ .in.start_idx = r->in.start_idx,
+ .in.max_entries = r->in.max_entries,
+ .in.buf_size = r->in.buf_size,
+ .out.total_size = r->out.total_size,
+ .out.returned_size = r->out.returned_size,
+ .out.info = r->out.info,
+ };
+
+ result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
+
+ return result;
+}
+
+
+/*
+ samr_AddMultipleMembersToAlias
+*/
+static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_AddMultipleMembersToAlias *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_RemoveMultipleMembersFromAlias
+*/
+static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_RemoveMultipleMembersFromAlias *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_GetDomPwInfo
+
+ this fetches the default password properties for a domain
+
+ note that w2k3 completely ignores the domain name in this call, and
+ always returns the information for the servers primary domain
+*/
+static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetDomPwInfo *r)
+{
+ struct ldb_message **msgs;
+ int ret;
+ const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
+ struct ldb_context *sam_ctx;
+
+ ZERO_STRUCTP(r->out.info);
+
+ sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /* The domain name in this call is ignored */
+ ret = gendb_search_dn(sam_ctx,
+ mem_ctx, NULL, &msgs, attrs);
+ if (ret <= 0) {
+ talloc_free(sam_ctx);
+
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ if (ret > 1) {
+ talloc_free(msgs);
+ talloc_free(sam_ctx);
+
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
+ "minPwdLength", 0);
+ r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
+ "pwdProperties", 1);
+
+ talloc_free(msgs);
+ talloc_unlink(mem_ctx, sam_ctx);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_Connect2
+*/
+static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect2 *r)
+{
+ struct samr_Connect c;
+
+ c = (struct samr_Connect) {
+ .in.system_name = NULL,
+ .in.access_mask = r->in.access_mask,
+ .out.connect_handle = r->out.connect_handle,
+ };
+
+ return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
+}
+
+
+/*
+ samr_SetUserInfo2
+
+ just an alias for samr_SetUserInfo
+*/
+static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetUserInfo2 *r)
+{
+ struct samr_SetUserInfo r2;
+
+ r2 = (struct samr_SetUserInfo) {
+ .in.user_handle = r->in.user_handle,
+ .in.level = r->in.level,
+ .in.info = r->in.info,
+ };
+
+ return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
+}
+
+
+/*
+ samr_SetBootKeyInformation
+*/
+static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetBootKeyInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_GetBootKeyInformation
+*/
+static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetBootKeyInformation *r)
+{
+ /* Windows Server 2008 returns this */
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+
+/*
+ samr_Connect3
+*/
+static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect3 *r)
+{
+ struct samr_Connect c;
+
+ c = (struct samr_Connect) {
+ .in.system_name = NULL,
+ .in.access_mask = r->in.access_mask,
+ .out.connect_handle = r->out.connect_handle,
+ };
+
+ return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
+}
+
+
+/*
+ samr_Connect4
+*/
+static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect4 *r)
+{
+ struct samr_Connect c;
+
+ c = (struct samr_Connect) {
+ .in.system_name = NULL,
+ .in.access_mask = r->in.access_mask,
+ .out.connect_handle = r->out.connect_handle,
+ };
+
+ return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
+}
+
+
+/*
+ samr_Connect5
+*/
+static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect5 *r)
+{
+ struct samr_Connect c;
+ NTSTATUS status;
+
+ c = (struct samr_Connect) {
+ .in.system_name = NULL,
+ .in.access_mask = r->in.access_mask,
+ .out.connect_handle = r->out.connect_handle,
+ };
+
+ status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
+
+ r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
+ r->out.info_out->info1.supported_features = 0;
+ *r->out.level_out = r->in.level_in;
+
+ return status;
+}
+
+
+/*
+ samr_RidToSid
+*/
+static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_RidToSid *r)
+{
+ struct samr_domain_state *d_state;
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* form the users SID */
+ *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (!*r->out.sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetDsrmPassword
+*/
+static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetDsrmPassword *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_ValidatePassword
+
+ For now the call checks the password complexity (if active) and the minimum
+ password length on level 2 and 3. Level 1 is ignored for now.
+*/
+static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_ValidatePassword *r)
+{
+ struct samr_GetDomPwInfo r2;
+ struct samr_PwInfo pwInfo;
+ const char *account = NULL;
+ DATA_BLOB password;
+ enum samr_ValidationStatus res;
+ NTSTATUS status;
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
+ enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
+
+ if (transport != NCACN_IP_TCP && transport != NCALRPC) {
+ DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ dcesrv_call_auth_info(dce_call, NULL, &auth_level);
+ if (auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
+ DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
+
+ r2 = (struct samr_GetDomPwInfo) {
+ .in.domain_name = NULL,
+ .out.info = &pwInfo,
+ };
+
+ status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (r->in.level) {
+ case NetValidateAuthentication:
+ /* we don't support this yet */
+ return NT_STATUS_NOT_SUPPORTED;
+ break;
+ case NetValidatePasswordChange:
+ account = r->in.req->req2.account.string;
+ password = data_blob_const(r->in.req->req2.password.string,
+ r->in.req->req2.password.length);
+ res = samdb_check_password(mem_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ account,
+ NULL, /* userPrincipalName */
+ NULL, /* displayName/full_name */
+ &password,
+ pwInfo.password_properties,
+ pwInfo.min_password_length);
+ (*r->out.rep)->ctr2.status = res;
+ break;
+ case NetValidatePasswordReset:
+ account = r->in.req->req3.account.string;
+ password = data_blob_const(r->in.req->req3.password.string,
+ r->in.req->req3.password.length);
+ res = samdb_check_password(mem_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ account,
+ NULL, /* userPrincipalName */
+ NULL, /* displayName/full_name */
+ &password,
+ pwInfo.password_properties,
+ pwInfo.min_password_length);
+ (*r->out.rep)->ctr3.status = res;
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void dcesrv_samr_Opnum68NotUsedOnWire(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_Opnum68NotUsedOnWire *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static void dcesrv_samr_Opnum69NotUsedOnWire(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_Opnum69NotUsedOnWire *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static void dcesrv_samr_Opnum70NotUsedOnWire(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_Opnum70NotUsedOnWire *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static void dcesrv_samr_Opnum71NotUsedOnWire(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_Opnum71NotUsedOnWire *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static void dcesrv_samr_Opnum72NotUsedOnWire(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_Opnum72NotUsedOnWire *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_samr_s.c"
diff --git a/source4/rpc_server/samr/dcesrv_samr.h b/source4/rpc_server/samr/dcesrv_samr.h
new file mode 100644
index 0000000..66038ed
--- /dev/null
+++ b/source4/rpc_server/samr/dcesrv_samr.h
@@ -0,0 +1,88 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the samr pipe - definitions
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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 "param/param.h"
+#include "libds/common/roles.h"
+
+/*
+ this type allows us to distinguish handle types
+*/
+enum samr_handle {
+ SAMR_HANDLE_CONNECT,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_HANDLE_USER,
+ SAMR_HANDLE_GROUP,
+ SAMR_HANDLE_ALIAS
+};
+
+
+/*
+ state asscoiated with a samr_Connect*() operation
+*/
+struct samr_connect_state {
+ struct ldb_context *sam_ctx;
+ uint32_t access_mask;
+};
+
+/*
+ * Cache of object GUIDS
+ */
+struct samr_guid_cache {
+ unsigned handle;
+ unsigned size;
+ struct GUID *entries;
+};
+
+enum samr_guid_cache_id {
+ SAMR_QUERY_DISPLAY_INFO_CACHE,
+ SAMR_ENUM_DOMAIN_GROUPS_CACHE,
+ SAMR_ENUM_DOMAIN_USERS_CACHE,
+ SAMR_LAST_CACHE
+};
+
+/*
+ state associated with a samr_OpenDomain() operation
+*/
+struct samr_domain_state {
+ struct samr_connect_state *connect_state;
+ void *sam_ctx;
+ uint32_t access_mask;
+ struct dom_sid *domain_sid;
+ const char *domain_name;
+ struct ldb_dn *domain_dn;
+ enum server_role role;
+ bool builtin;
+ struct loadparm_context *lp_ctx;
+ struct samr_guid_cache guid_caches[SAMR_LAST_CACHE];
+ struct samr_SamEntry *domain_users_cached;
+};
+
+/*
+ state associated with a open account handle
+*/
+struct samr_account_state {
+ struct samr_domain_state *domain_state;
+ void *sam_ctx;
+ uint32_t access_mask;
+ struct dom_sid *account_sid;
+ const char *account_name;
+ struct ldb_dn *account_dn;
+};
diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c
new file mode 100644
index 0000000..b581be6
--- /dev/null
+++ b/source4/rpc_server/samr/samr_password.c
@@ -0,0 +1,833 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ samr server password set/change handling
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/samr/dcesrv_samr.h"
+#include "system/time.h"
+#include "lib/crypto/md4.h"
+#include "dsdb/common/util.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "libcli/auth/libcli_auth.h"
+#include "../lib/util/util_ldb.h"
+#include "rpc_server/samr/proto.h"
+#include "auth/auth_sam.h"
+#include "lib/param/loadparm.h"
+#include "librpc/rpc/dcerpc_helper.h"
+#include "librpc/rpc/dcerpc_samr.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+static void log_password_change_event(struct imessaging_context *msg_ctx,
+ struct loadparm_context *lp_ctx,
+ const struct tsocket_address *remote_client_address,
+ const struct tsocket_address *local_server_address,
+ const char *auth_description,
+ const char *password_type,
+ const char *original_client_name,
+ const char *account_name_from_db,
+ NTSTATUS status,
+ struct dom_sid *sid)
+{
+ /*
+ * Forcing this via the NTLM auth structure is not ideal, but
+ * it is the most practical option right now, and ensures the
+ * logs are consistent, even if some elements are always NULL.
+ */
+ struct auth_usersupplied_info ui = {
+ .was_mapped = true,
+ .client = {
+ .account_name = original_client_name,
+ .domain_name = lpcfg_sam_name(lp_ctx),
+ },
+ .mapped = {
+ .account_name = account_name_from_db,
+ .domain_name = lpcfg_sam_name(lp_ctx),
+ },
+ .remote_host = remote_client_address,
+ .local_host = local_server_address,
+ .service_description = "SAMR Password Change",
+ .auth_description = auth_description,
+ .password_type = password_type,
+ };
+
+ log_authentication_event(msg_ctx,
+ lp_ctx,
+ NULL,
+ &ui,
+ status,
+ ui.mapped.domain_name,
+ ui.mapped.account_name,
+ sid);
+}
+/*
+ samr_ChangePasswordUser
+
+ So old it is just not worth implementing
+ because it does not supply a plaintext and so we can't do password
+ complexity checking and cannot update all the other password hashes.
+
+*/
+NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ samr_OemChangePasswordUser2
+
+ No longer implemented as it requires the LM hash
+*/
+NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_OemChangePasswordUser2 *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ samr_ChangePasswordUser4
+*/
+NTSTATUS dcesrv_samr_ChangePasswordUser4(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser4 *r)
+{
+#ifdef HAVE_GNUTLS_PBKDF2
+ struct ldb_context *sam_ctx = NULL;
+ struct ldb_message *msg = NULL;
+ struct ldb_dn *dn = NULL;
+ const char *samAccountName = NULL;
+ struct dom_sid *objectSid = NULL;
+ struct samr_Password *nt_pwd = NULL;
+ gnutls_datum_t nt_key;
+ gnutls_datum_t salt = {
+ .data = r->in.password->salt,
+ .size = sizeof(r->in.password->salt),
+ };
+ uint8_t cdk_data[16] = {0};
+ DATA_BLOB cdk = {
+ .data = cdk_data,
+ .length = sizeof(cdk_data),
+ };
+ struct auth_session_info *call_session_info = NULL;
+ struct auth_session_info *old_session_info = NULL;
+ NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
+ int rc;
+
+ r->out.result = NT_STATUS_WRONG_PASSWORD;
+
+ if (r->in.password == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.password->PBKDF2Iterations < 5000 ||
+ r->in.password->PBKDF2Iterations > 1000000) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ /*
+ * Connect to a SAMDB with system privileges for fetching the old
+ * password hashes.
+ */
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ rc = ldb_transaction_start(sam_ctx);
+ if (rc != LDB_SUCCESS) {
+ DBG_WARNING("Failed to start transaction: %s\n",
+ ldb_errstring(sam_ctx));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ /*
+ * We use authsam_search_account() to be consistent with the
+ * other callers in the bad password and audit log handling
+ * systems. It ensures we get DSDB_SEARCH_SHOW_EXTENDED_DN.
+ */
+ status = authsam_search_account(mem_ctx,
+ sam_ctx,
+ r->in.account->string,
+ ldb_get_default_basedn(sam_ctx),
+ &msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_transaction_cancel(sam_ctx);
+ goto done;
+ }
+
+ dn = msg->dn;
+ samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL);
+ objectSid = samdb_result_dom_sid(msg, msg, "objectSid");
+
+ status = samdb_result_passwords(mem_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ msg,
+ &nt_pwd);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_transaction_cancel(sam_ctx);
+ goto done;
+ }
+
+ if (nt_pwd == NULL) {
+ ldb_transaction_cancel(sam_ctx);
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto done;
+ }
+
+ nt_key = (gnutls_datum_t){
+ .data = nt_pwd->hash,
+ .size = sizeof(nt_pwd->hash),
+ };
+
+ rc = gnutls_pbkdf2(GNUTLS_MAC_SHA512,
+ &nt_key,
+ &salt,
+ r->in.password->PBKDF2Iterations,
+ cdk.data,
+ cdk.length);
+ if (rc < 0) {
+ ldb_transaction_cancel(sam_ctx);
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto done;
+ }
+
+ /* Drop to user privileges for the password change */
+
+ old_session_info = ldb_get_opaque(sam_ctx, DSDB_SESSION_INFO);
+ call_session_info = dcesrv_call_session_info(dce_call);
+
+ rc = ldb_set_opaque(sam_ctx, DSDB_SESSION_INFO, call_session_info);
+ if (rc != LDB_SUCCESS) {
+ ldb_transaction_cancel(sam_ctx);
+ status = NT_STATUS_INVALID_SYSTEM_SERVICE;
+ goto done;
+ }
+
+ status = samr_set_password_aes(dce_call,
+ mem_ctx,
+ &cdk,
+ sam_ctx,
+ dn,
+ NULL,
+ r->in.password,
+ DSDB_PASSWORD_CHECKED_AND_CORRECT);
+ BURN_DATA(cdk_data);
+
+ /* Restore our privileges to system level */
+ if (old_session_info != NULL) {
+ ldb_set_opaque(sam_ctx, DSDB_SESSION_INFO, old_session_info);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_transaction_cancel(sam_ctx);
+ goto done;
+ }
+
+ /* And this confirms it in a transaction commit */
+ rc = ldb_transaction_commit(sam_ctx);
+ if (rc != LDB_SUCCESS) {
+ DBG_WARNING("Failed to commit transaction to change password "
+ "on %s: %s\n",
+ ldb_dn_get_linearized(dn),
+ ldb_errstring(sam_ctx));
+ status = NT_STATUS_TRANSACTION_ABORTED;
+ goto done;
+ }
+
+ status = NT_STATUS_OK;
+done:
+ {
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+
+ log_password_change_event(imsg_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ dce_call->conn->remote_address,
+ dce_call->conn->local_address,
+ "samr_ChangePasswordUser4",
+ "AES using NTLM-hash",
+ r->in.account->string,
+ samAccountName,
+ status,
+ objectSid);
+ }
+
+ /* Only update the badPwdCount if we found the user */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ authsam_update_bad_pwd_count(sam_ctx,
+ msg,
+ ldb_get_default_basedn(sam_ctx));
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ /*
+ * Don't give the game away: (don't allow anonymous users to
+ * prove the existence of usernames)
+ */
+ status = NT_STATUS_WRONG_PASSWORD;
+ }
+
+ return status;
+#else /* HAVE_GNUTLS_PBKDF2 */
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+#endif /* HAVE_GNUTLS_PBKDF2 */
+}
+
+/*
+ samr_ChangePasswordUser3
+*/
+NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser3 *r)
+{
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+ NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
+ DATA_BLOB new_password;
+ struct ldb_context *sam_ctx = NULL;
+ struct ldb_dn *user_dn = NULL;
+ int ret;
+ struct ldb_message *msg = NULL;
+ struct samr_Password *nt_pwd;
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct userPwdChangeFailureInformation *reject = NULL;
+ enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
+ uint8_t new_nt_hash[16];
+ struct samr_Password nt_verifier;
+ const char *user_samAccountName = NULL;
+ struct dom_sid *user_objectSid = NULL;
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ enum ntlm_auth_level ntlm_auth_level
+ = lpcfg_ntlm_auth(lp_ctx);
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t nt_session_key;
+ struct auth_session_info *call_session_info = NULL;
+ struct auth_session_info *old_session_info = NULL;
+ int rc;
+
+ *r->out.dominfo = NULL;
+ *r->out.reject = NULL;
+
+ /* this call should be disabled without NTLM auth */
+ if (ntlm_auth_level == NTLM_AUTH_DISABLED) {
+ DBG_WARNING("NTLM password changes not"
+ "permitted by configuration.\n");
+ return NT_STATUS_NTLM_BLOCKED;
+ }
+
+ if (r->in.nt_password == NULL ||
+ r->in.nt_verifier == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Connect to a SAMDB with system privileges for fetching the old pw
+ * hashes. */
+ sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ ret = ldb_transaction_start(sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ /*
+ * We use authsam_search_account() to be consistent with the
+ * other callers in the bad password and audit log handling
+ * systems. It ensures we get DSDB_SEARCH_SHOW_EXTENDED_DN.
+ */
+ status = authsam_search_account(mem_ctx,
+ sam_ctx,
+ r->in.account->string,
+ ldb_get_default_basedn(sam_ctx),
+ &msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_transaction_cancel(sam_ctx);
+ goto failed;
+ }
+
+ user_dn = msg->dn;
+ user_samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL);
+ user_objectSid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
+
+ status = samdb_result_passwords(mem_ctx, lp_ctx,
+ msg, &nt_pwd);
+ if (!NT_STATUS_IS_OK(status) ) {
+ ldb_transaction_cancel(sam_ctx);
+ goto failed;
+ }
+
+ if (!nt_pwd) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ ldb_transaction_cancel(sam_ctx);
+ goto failed;
+ }
+
+ /* decrypt the password we have been given */
+ nt_session_key = (gnutls_datum_t) {
+ .data = nt_pwd->hash,
+ .size = sizeof(nt_pwd->hash),
+ };
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &nt_session_key,
+ NULL);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ ldb_transaction_cancel(sam_ctx);
+ goto failed;
+ }
+
+ rc = gnutls_cipher_decrypt(cipher_hnd,
+ r->in.nt_password->data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ ldb_transaction_cancel(sam_ctx);
+ goto failed;
+ }
+
+ if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ status = NT_STATUS_WRONG_PASSWORD;
+ ldb_transaction_cancel(sam_ctx);
+ goto failed;
+ }
+
+ if (r->in.nt_verifier == NULL) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ ldb_transaction_cancel(sam_ctx);
+ goto failed;
+ }
+
+ /* check NT verifier */
+ mdfour(new_nt_hash, new_password.data, new_password.length);
+
+ rc = E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ ldb_transaction_cancel(sam_ctx);
+ goto failed;
+ }
+ if (!mem_equal_const_time(nt_verifier.hash, r->in.nt_verifier->hash, 16)) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ ldb_transaction_cancel(sam_ctx);
+ goto failed;
+ }
+
+ /* Drop to user privileges for the password change */
+
+ old_session_info = ldb_get_opaque(sam_ctx, DSDB_SESSION_INFO);
+ call_session_info = dcesrv_call_session_info(dce_call);
+
+ ret = ldb_set_opaque(sam_ctx, DSDB_SESSION_INFO, call_session_info);
+ if (ret != LDB_SUCCESS) {
+ status = NT_STATUS_INVALID_SYSTEM_SERVICE;
+ ldb_transaction_cancel(sam_ctx);
+ goto failed;
+ }
+
+ /* Performs the password modification. We pass the old hashes read out
+ * from the database since they were already checked against the user-
+ * provided ones. */
+ status = samdb_set_password(sam_ctx, mem_ctx,
+ user_dn, NULL,
+ &new_password,
+ NULL,
+ DSDB_PASSWORD_CHECKED_AND_CORRECT,
+ &reason,
+ &dominfo);
+
+ /* Restore our privileges to system level */
+ if (old_session_info != NULL) {
+ ldb_set_opaque(sam_ctx, DSDB_SESSION_INFO, old_session_info);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_transaction_cancel(sam_ctx);
+ goto failed;
+ }
+
+ /* And this confirms it in a transaction commit */
+ ret = ldb_transaction_commit(sam_ctx);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
+ ldb_dn_get_linearized(user_dn),
+ ldb_errstring(sam_ctx)));
+ status = NT_STATUS_TRANSACTION_ABORTED;
+ goto failed;
+ }
+
+ status = NT_STATUS_OK;
+
+failed:
+
+ log_password_change_event(imsg_ctx,
+ lp_ctx,
+ dce_call->conn->remote_address,
+ dce_call->conn->local_address,
+ "samr_ChangePasswordUser3",
+ "RC4/DES using NTLM-hash",
+ r->in.account->string,
+ user_samAccountName,
+ status,
+ user_objectSid);
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OK;
+ }
+
+ /* Only update the badPwdCount if we found the user */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ NTSTATUS bad_pwd_status;
+
+ bad_pwd_status = authsam_update_bad_pwd_count(
+ sam_ctx, msg, ldb_get_default_basedn(sam_ctx));
+ if (NT_STATUS_EQUAL(bad_pwd_status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
+ status = bad_pwd_status;
+ }
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */
+ status = NT_STATUS_WRONG_PASSWORD;
+ }
+
+ reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
+ if (reject != NULL) {
+ reject->extendedFailureReason = reason;
+
+ *r->out.reject = reject;
+ }
+
+ *r->out.dominfo = dominfo;
+
+ return status;
+}
+
+/*
+ samr_ChangePasswordUser2
+
+ easy - just a subset of samr_ChangePasswordUser3
+*/
+NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser2 *r)
+{
+ struct samr_ChangePasswordUser3 r2;
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct userPwdChangeFailureInformation *reject = NULL;
+
+ r2.in.server = r->in.server;
+ r2.in.account = r->in.account;
+ r2.in.nt_password = r->in.nt_password;
+ r2.in.nt_verifier = r->in.nt_verifier;
+ r2.in.lm_change = r->in.lm_change;
+ r2.in.lm_password = r->in.lm_password;
+ r2.in.lm_verifier = r->in.lm_verifier;
+ r2.in.password3 = NULL;
+ r2.out.dominfo = &dominfo;
+ r2.out.reject = &reject;
+
+ return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
+}
+
+
+/*
+ set password via a samr_CryptPassword buffer
+*/
+NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
+ struct ldb_context *sam_ctx,
+ struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
+ TALLOC_CTX *mem_ctx,
+ struct samr_CryptPassword *pwbuf)
+{
+ NTSTATUS nt_status;
+ DATA_BLOB new_password;
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t _session_key;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ int rc;
+ bool encrypted;
+
+ encrypted = dcerpc_is_transport_encrypted(session_info);
+ if (lpcfg_weak_crypto(lp_ctx) == SAMBA_WEAK_CRYPTO_DISALLOWED &&
+ !encrypted) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ nt_status = dcesrv_transport_session_key(dce_call, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DBG_NOTICE("samr: failed to get session key: %s\n",
+ nt_errstr(nt_status));
+ return nt_status;
+ }
+
+ _session_key = (gnutls_datum_t) {
+ .data = session_key.data,
+ .size = session_key.length,
+ };
+
+ /*
+ * This is safe to support as we only have a session key
+ * over a SMB connection which we force to be encrypted.
+ */
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &_session_key,
+ NULL);
+ if (rc < 0) {
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ nt_status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ rc = gnutls_cipher_decrypt(cipher_hnd,
+ pwbuf->data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ if (rc < 0) {
+ nt_status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* set the password - samdb needs to know both the domain and user DNs,
+ so the domain password policy can be used */
+ nt_status = samdb_set_password(sam_ctx,
+ mem_ctx,
+ account_dn,
+ domain_dn,
+ &new_password,
+ NULL,
+ DSDB_PASSWORD_RESET,
+ NULL,
+ NULL);
+out:
+ return nt_status;
+}
+
+
+/*
+ set password via a samr_CryptPasswordEx buffer
+*/
+NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
+ struct ldb_context *sam_ctx,
+ struct ldb_dn *account_dn,
+ struct ldb_dn *domain_dn,
+ TALLOC_CTX *mem_ctx,
+ struct samr_CryptPasswordEx *pwbuf)
+{
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ NTSTATUS nt_status;
+ DATA_BLOB new_password;
+
+ /* The confounder is in the last 16 bytes of the buffer */
+ DATA_BLOB confounder = data_blob_const(&pwbuf->data[516], 16);
+ DATA_BLOB pw_data = data_blob_const(pwbuf->data, 516);
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ int rc;
+ bool encrypted;
+
+ nt_status = dcesrv_transport_session_key(dce_call, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3,("samr: failed to get session key: %s "
+ "=> NT_STATUS_WRONG_PASSWORD\n",
+ nt_errstr(nt_status)));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ encrypted = dcerpc_is_transport_encrypted(session_info);
+ if (lpcfg_weak_crypto(lp_ctx) == SAMBA_WEAK_CRYPTO_DISALLOWED &&
+ !encrypted) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ rc = samba_gnutls_arcfour_confounded_md5(&confounder,
+ &session_key,
+ &pw_data,
+ SAMBA_GNUTLS_DECRYPT);
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ if (rc < 0) {
+ nt_status = gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ goto out;
+ }
+
+ if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ nt_status = NT_STATUS_WRONG_PASSWORD;
+ goto out;
+ }
+
+ /* set the password - samdb needs to know both the domain and user DNs,
+ so the domain password policy can be used */
+ nt_status = samdb_set_password(sam_ctx,
+ mem_ctx,
+ account_dn,
+ domain_dn,
+ &new_password,
+ NULL,
+ DSDB_PASSWORD_RESET,
+ NULL,
+ NULL);
+ ZERO_ARRAY_LEN(new_password.data,
+ new_password.length);
+
+out:
+ return nt_status;
+}
+
+/*
+ set password via encrypted NT and LM hash buffers
+*/
+NTSTATUS samr_set_password_buffers(struct dcesrv_call_state *dce_call,
+ struct ldb_context *sam_ctx,
+ struct ldb_dn *account_dn,
+ struct ldb_dn *domain_dn,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *lm_pwd_hash,
+ const uint8_t *nt_pwd_hash)
+{
+ struct samr_Password *d_lm_pwd_hash = NULL, *d_nt_pwd_hash = NULL;
+ uint8_t random_session_key[16] = { 0, };
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ DATA_BLOB in, out;
+ NTSTATUS nt_status = NT_STATUS_OK;
+ int rc;
+
+ nt_status = dcesrv_transport_session_key(dce_call, &session_key);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_USER_SESSION_KEY)) {
+ DEBUG(3,("samr: failed to get session key: %s "
+ "=> use a random session key\n",
+ nt_errstr(nt_status)));
+
+ /*
+ * Windows just uses a random key
+ */
+ generate_random_buffer(random_session_key,
+ sizeof(random_session_key));
+ session_key = data_blob_const(random_session_key,
+ sizeof(random_session_key));
+ nt_status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ if (nt_pwd_hash != NULL) {
+ in = data_blob_const(nt_pwd_hash, 16);
+ out = data_blob_talloc_zero(mem_ctx, 16);
+
+ rc = sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_DECRYPT);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc,
+ NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ d_nt_pwd_hash = (struct samr_Password *) out.data;
+ }
+
+ if ((d_lm_pwd_hash != NULL) || (d_nt_pwd_hash != NULL)) {
+ nt_status = samdb_set_password(sam_ctx, mem_ctx, account_dn,
+ domain_dn, NULL,
+ d_nt_pwd_hash,
+ DSDB_PASSWORD_RESET,
+ NULL, NULL);
+ }
+
+ return nt_status;
+}
+
+NTSTATUS samr_set_password_aes(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *cdk,
+ struct ldb_context *sam_ctx,
+ struct ldb_dn *account_dn,
+ struct ldb_dn *domain_dn,
+ struct samr_EncryptedPasswordAES *pwbuf,
+ enum dsdb_password_checked old_password_checked)
+{
+ DATA_BLOB pw_data = data_blob_null;
+ DATA_BLOB new_password = data_blob_null;
+ const DATA_BLOB ciphertext =
+ data_blob_const(pwbuf->cipher, pwbuf->cipher_len);
+ DATA_BLOB iv = data_blob_const(pwbuf->salt, sizeof(pwbuf->salt));
+ NTSTATUS nt_status = NT_STATUS_OK;
+ bool ok;
+
+ nt_status = samba_gnutls_aead_aes_256_cbc_hmac_sha512_decrypt(
+ mem_ctx,
+ &ciphertext,
+ cdk,
+ &samr_aes256_enc_key_salt,
+ &samr_aes256_mac_key_salt,
+ &iv,
+ pwbuf->auth_data,
+ &pw_data);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ ok = extract_pwd_blob_from_buffer514(mem_ctx,
+ pw_data.data,
+ &new_password);
+ TALLOC_FREE(pw_data.data);
+ if (!ok) {
+ DBG_NOTICE("samr: failed to decode password buffer\n");
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ nt_status = samdb_set_password(sam_ctx,
+ mem_ctx,
+ account_dn,
+ domain_dn,
+ &new_password,
+ NULL,
+ old_password_checked,
+ NULL,
+ NULL);
+ TALLOC_FREE(new_password.data);
+
+ return nt_status;
+}
diff --git a/source4/rpc_server/service_rpc.c b/source4/rpc_server/service_rpc.c
new file mode 100644
index 0000000..8c6b751
--- /dev/null
+++ b/source4/rpc_server/service_rpc.c
@@ -0,0 +1,220 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ smbd-specific dcerpc server code
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 2004-2005
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2004,2007
+
+ 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 "includes.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "auth/auth.h"
+#include "../lib/util/dlinklist.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/dcerpc_server_proto.h"
+#include "librpc/rpc/dcerpc.h"
+#include "system/filesys.h"
+#include "lib/messaging/irpc.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "libcli/raw/smb.h"
+#include "../libcli/named_pipe_auth/npa_tstream.h"
+#include "samba/process_model.h"
+
+static void skip_become_root(void)
+{
+}
+
+static void skip_unbecome_root(void)
+{
+}
+
+static struct dcesrv_context_callbacks srv_callbacks = {
+ .log.successful_authz = log_successful_dcesrv_authz_event,
+ .auth.gensec_prepare = dcesrv_gensec_prepare,
+ .auth.become_root = skip_become_root,
+ .auth.unbecome_root = skip_unbecome_root,
+ .assoc_group.find = dcesrv_assoc_group_find_s4,
+};
+
+/*
+ * Need to run the majority of the RPC endpoints in a single process to allow
+ * for shared handles, and the sharing of ldb contexts.
+ *
+ * However other endpoints are capable of being run in multiple processes
+ * e.g. NETLOGON.
+ *
+ * To support this the process model is manipulated to force those end points
+ * not supporting multiple processes into the single process model. The code
+ * responsible for this is in dcesrv_init_endpoints
+ *
+ */
+NTSTATUS server_service_rpc_init(TALLOC_CTX *);
+
+/*
+ * Initialise the rpc endpoints.
+ */
+static NTSTATUS dcesrv_init_endpoints(struct task_server *task,
+ struct dcesrv_context *dce_ctx,
+ bool use_single_process)
+{
+
+ struct dcesrv_endpoint *e;
+ const struct model_ops *model_ops = NULL;
+
+ /*
+ * For those RPC services that run with shared context we need to
+ * ensure that they don't fork a new process on accept (standard_model).
+ * And as there is only one process handling these requests we need
+ * to handle accept errors in a similar manner to the single process
+ * model.
+ *
+ * To do this we override the process model operations with the single
+ * process operations. This is not the most elegant solution, but it is
+ * the least ugly, and is confined to the next block of code.
+ */
+ if (use_single_process) {
+ model_ops = process_model_startup("single");
+ if (model_ops == NULL) {
+ DBG_ERR("Unable to load single process model");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ model_ops = task->model_ops;
+ }
+
+ for (e = dce_ctx->endpoint_list; e; e = e->next) {
+
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(e->ep_description);
+
+ if (transport == NCACN_HTTP) {
+ /*
+ * We don't support ncacn_http yet
+ */
+ continue;
+ }
+ if (e->use_single_process == use_single_process) {
+ NTSTATUS status;
+ status = dcesrv_add_ep(dce_ctx,
+ task->lp_ctx,
+ e,
+ task->event_ctx,
+ model_ops,
+ task->process_context);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ * Initialise the RPC service.
+ * And those end points that can be serviced by multiple processes.
+ * The endpoints that need to be run in a single process are setup in the
+ * post_fork hook.
+*/
+static NTSTATUS dcesrv_task_init(struct task_server *task)
+{
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ struct dcesrv_context *dce_ctx;
+ const char **ep_servers = NULL;
+
+ dcerpc_server_init(task->lp_ctx);
+
+ task_server_set_title(task, "task[dcesrv]");
+
+ status = dcesrv_init_context(task->event_ctx,
+ task->lp_ctx,
+ &srv_callbacks,
+ &dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ep_servers = lpcfg_dcerpc_endpoint_servers(task->lp_ctx);
+ status = dcesrv_init_ep_servers(dce_ctx, ep_servers);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Make sure the directory for NCALRPC exists */
+ if (!directory_exist(lpcfg_ncalrpc_dir(task->lp_ctx))) {
+ mkdir(lpcfg_ncalrpc_dir(task->lp_ctx), 0755);
+ }
+ status = dcesrv_init_endpoints(task, dce_ctx, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ task->private_data = dce_ctx;
+ return NT_STATUS_OK;
+}
+
+/*
+ * Initialise the endpoints that need to run in a single process fork.
+ * The endpoint registration is only done for the first process instance.
+ *
+ */
+static void dcesrv_post_fork(struct task_server *task,
+ struct process_details *pd)
+{
+
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ struct dcesrv_context *dce_ctx;
+
+ if (task->private_data == NULL) {
+ task_server_terminate(task, "dcerpc: No dcesrv_context", true);
+ return;
+ }
+ dce_ctx =
+ talloc_get_type_abort(task->private_data, struct dcesrv_context);
+
+ /*
+ * Ensure the single process endpoints are only available to the
+ * first instance.
+ */
+ if (pd->instances == 0) {
+ status = dcesrv_init_endpoints(task, dce_ctx, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(
+ task,
+ "dcerpc: Failed to initialise end points",
+ true);
+ return;
+ }
+ }
+
+ irpc_add_name(task->msg_ctx, "rpc_server");
+}
+
+NTSTATUS server_service_rpc_init(TALLOC_CTX *ctx)
+{
+ static const struct service_details details = {
+ .inhibit_fork_on_accept = false,
+ .inhibit_pre_fork = false,
+ .task_init = dcesrv_task_init,
+ .post_fork = dcesrv_post_fork};
+ return register_server_service(ctx, "rpc", &details);
+}
diff --git a/source4/rpc_server/srvsvc/dcesrv_srvsvc.c b/source4/rpc_server/srvsvc/dcesrv_srvsvc.c
new file mode 100644
index 0000000..d7a2262
--- /dev/null
+++ b/source4/rpc_server/srvsvc/dcesrv_srvsvc.c
@@ -0,0 +1,2300 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the srvsvc pipe
+
+ Copyright (C) Stefan (metze) Metzmacher 2004-2006
+
+ 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 "includes.h"
+#include "ntvfs/ntvfs.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/common/share.h"
+#include "auth/auth.h"
+#include "libcli/security/security.h"
+#include "system/time.h"
+#include "rpc_server/srvsvc/proto.h"
+#include "param/param.h"
+
+#undef strcasecmp
+#undef strncasecmp
+
+#define SRVSVC_CHECK_ADMIN_ACCESS do { \
+ struct auth_session_info *si = dcesrv_call_session_info(dce_call); \
+ struct security_token *t = si->security_token; \
+ if (!security_token_has_builtin_administrators(t) && \
+ !security_token_has_sid(t, &global_sid_Builtin_Server_Operators)) { \
+ return WERR_ACCESS_DENIED; \
+ } \
+} while (0)
+
+/*
+ srvsvc_NetCharDevEnum
+*/
+static WERROR dcesrv_srvsvc_NetCharDevEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevEnum *r)
+{
+ *r->out.totalentries = 0;
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ r->out.info_ctr->ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetCharDevCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr0);
+
+ r->out.info_ctr->ctr.ctr0->count = 0;
+ r->out.info_ctr->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+
+ case 1:
+ r->out.info_ctr->ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetCharDevCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr1);
+
+ r->out.info_ctr->ctr.ctr1->count = 0;
+ r->out.info_ctr->ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetCharDevGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetCharDevGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevGetInfo *r)
+{
+ ZERO_STRUCTP(r->out.info);
+
+ switch (r->in.level) {
+ case 0:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetCharDevControl
+*/
+static WERROR dcesrv_srvsvc_NetCharDevControl(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevControl *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetCharDevQEnum
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQEnum *r)
+{
+ *r->out.totalentries = 0;
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ {
+ r->out.info_ctr->ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetCharDevQCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr0);
+
+ r->out.info_ctr->ctr.ctr0->count = 0;
+ r->out.info_ctr->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ r->out.info_ctr->ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetCharDevQCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr1);
+
+ r->out.info_ctr->ctr.ctr1->count = 0;
+ r->out.info_ctr->ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetCharDevQGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQGetInfo *r)
+{
+ ZERO_STRUCTP(r->out.info);
+
+ switch (r->in.level) {
+ case 0:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetCharDevQSetInfo
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQSetInfo *r)
+{
+ switch (r->in.level) {
+ case 0:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetCharDevQPurge
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQPurge(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQPurge *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetCharDevQPurgeSelf
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQPurgeSelf(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQPurgeSelf *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetConnEnum
+*/
+static WERROR dcesrv_srvsvc_NetConnEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetConnEnum *r)
+{
+ *r->out.totalentries = 0;
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ {
+ r->out.info_ctr->ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetConnCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr0);
+
+ r->out.info_ctr->ctr.ctr0->count = 0;
+ r->out.info_ctr->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ r->out.info_ctr->ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetConnCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr1);
+
+ r->out.info_ctr->ctr.ctr1->count = 0;
+ r->out.info_ctr->ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetFileEnum
+*/
+static WERROR dcesrv_srvsvc_NetFileEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetFileEnum *r)
+{
+ *r->out.totalentries = 0;
+
+ switch (r->in.info_ctr->level) {
+ case 2:
+ {
+ r->out.info_ctr->ctr.ctr2 = talloc(mem_ctx, struct srvsvc_NetFileCtr2);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr2);
+
+ r->out.info_ctr->ctr.ctr2->count = 0;
+ r->out.info_ctr->ctr.ctr2->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 3:
+ {
+ r->out.info_ctr->ctr.ctr3 = talloc(mem_ctx, struct srvsvc_NetFileCtr3);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr3);
+
+ r->out.info_ctr->ctr.ctr3->count = 0;
+ r->out.info_ctr->ctr.ctr3->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetFileGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetFileGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetFileGetInfo *r)
+{
+ ZERO_STRUCTP(r->out.info);
+
+ switch (r->in.level) {
+ case 2:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ case 3:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetFileClose
+*/
+static WERROR dcesrv_srvsvc_NetFileClose(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetFileClose *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetSessEnum
+*/
+static WERROR dcesrv_srvsvc_NetSessEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSessEnum *r)
+{
+ *r->out.totalentries = 0;
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ {
+ r->out.info_ctr->ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetSessCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr0);
+
+ r->out.info_ctr->ctr.ctr0->count = 0;
+ r->out.info_ctr->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ r->out.info_ctr->ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetSessCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr1);
+
+ r->out.info_ctr->ctr.ctr1->count = 0;
+ r->out.info_ctr->ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 2:
+ {
+ r->out.info_ctr->ctr.ctr2 = talloc(mem_ctx, struct srvsvc_NetSessCtr2);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr2);
+
+ r->out.info_ctr->ctr.ctr2->count = 0;
+ r->out.info_ctr->ctr.ctr2->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 10:
+ {
+ r->out.info_ctr->ctr.ctr10 = talloc(mem_ctx, struct srvsvc_NetSessCtr10);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr10);
+
+ r->out.info_ctr->ctr.ctr10->count = 0;
+ r->out.info_ctr->ctr.ctr10->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 502:
+ {
+ r->out.info_ctr->ctr.ctr502 = talloc(mem_ctx, struct srvsvc_NetSessCtr502);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr502);
+
+ r->out.info_ctr->ctr.ctr502->count = 0;
+ r->out.info_ctr->ctr.ctr502->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetSessDel
+*/
+static WERROR dcesrv_srvsvc_NetSessDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSessDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetShareAdd
+*/
+static WERROR dcesrv_srvsvc_NetShareAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareAdd *r)
+{
+ switch (r->in.level) {
+ case 0:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ case 2:
+ {
+ NTSTATUS nterr;
+ struct share_info *info;
+ struct share_context *sctx;
+ unsigned int count = 8;
+ unsigned int i;
+
+ nterr = share_get_context(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ /* there are no more than 8 options in struct srvsvc_NetShareInfo2 */
+ info = talloc_array(mem_ctx, struct share_info, count);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ i = 0;
+
+ info[i].name = SHARE_TYPE;
+ info[i].type = SHARE_INFO_STRING;
+ switch (r->in.info->info2->type) {
+ case STYPE_DISKTREE:
+ info[i].value = talloc_strdup(info, "DISK");
+ break;
+ case STYPE_PRINTQ:
+ info[i].value = talloc_strdup(info, "PRINTER");
+ break;
+ case STYPE_IPC:
+ info[i].value = talloc_strdup(info, "IPC");
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ i++;
+
+ if (r->in.info->info2->path && r->in.info->info2->path[0]) {
+ info[i].name = SHARE_PATH;
+ info[i].type = SHARE_INFO_STRING;
+
+ /* Windows will send a path in a form of C:\example\path */
+ if (r->in.info->info2->path[1] == ':') {
+ info[i].value = talloc_strdup(info, &r->in.info->info2->path[2]);
+ } else {
+ /* very strange let's try to set as is */
+ info[i].value = talloc_strdup(info, r->in.info->info2->path);
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ all_string_sub((char *)info[i].value, "\\", "/", 0);
+
+ i++;
+ }
+
+ if (r->in.info->info2->comment && r->in.info->info2->comment[0]) {
+ info[i].name = SHARE_COMMENT;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, r->in.info->info2->comment);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ if (r->in.info->info2->password && r->in.info->info2->password[0]) {
+ info[i].name = SHARE_PASSWORD;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, r->in.info->info2->password);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ info[i].name = SHARE_MAX_CONNECTIONS;
+ info[i].type = SHARE_INFO_INT;
+ info[i].value = talloc(info, int);
+ *((int *)info[i].value) = r->in.info->info2->max_users;
+ i++;
+
+ /* TODO: security descriptor */
+
+ nterr = share_create(sctx, r->in.info->info2->name, info, i);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+
+ return WERR_OK;
+ }
+ case 501:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ case 502:
+ {
+ NTSTATUS nterr;
+ struct share_info *info;
+ struct share_context *sctx;
+ unsigned int count = 10;
+ unsigned int i;
+
+ nterr = share_get_context(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ /* there are no more than 10 options in struct srvsvc_NetShareInfo502 */
+ info = talloc_array(mem_ctx, struct share_info, count);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ i = 0;
+
+ info[i].name = SHARE_TYPE;
+ info[i].type = SHARE_INFO_STRING;
+ switch (r->in.info->info502->type) {
+ case 0x00:
+ info[i].value = talloc_strdup(info, "DISK");
+ break;
+ case 0x01:
+ info[i].value = talloc_strdup(info, "PRINTER");
+ break;
+ case 0x03:
+ info[i].value = talloc_strdup(info, "IPC");
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ i++;
+
+ if (r->in.info->info502->path && r->in.info->info502->path[0]) {
+ info[i].name = SHARE_PATH;
+ info[i].type = SHARE_INFO_STRING;
+
+ /* Windows will send a path in a form of C:\example\path */
+ if (r->in.info->info502->path[1] == ':') {
+ info[i].value = talloc_strdup(info, &r->in.info->info502->path[2]);
+ } else {
+ /* very strange let's try to set as is */
+ info[i].value = talloc_strdup(info, r->in.info->info502->path);
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ all_string_sub((char *)info[i].value, "\\", "/", 0);
+
+ i++;
+ }
+
+ if (r->in.info->info502->comment && r->in.info->info502->comment[0]) {
+ info[i].name = SHARE_COMMENT;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, r->in.info->info502->comment);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ if (r->in.info->info502->password && r->in.info->info502->password[0]) {
+ info[i].name = SHARE_PASSWORD;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, r->in.info->info502->password);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ info[i].name = SHARE_MAX_CONNECTIONS;
+ info[i].type = SHARE_INFO_INT;
+ info[i].value = talloc(info, int);
+ *((int *)info[i].value) = r->in.info->info502->max_users;
+ i++;
+
+ /* TODO: security descriptor */
+
+ nterr = share_create(sctx, r->in.info->info502->name, info, i);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+static WERROR dcesrv_srvsvc_fiel_ShareInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct share_config *scfg, uint32_t level,
+ union srvsvc_NetShareInfo *info)
+{
+ struct dcesrv_context *dce_ctx = dce_call->conn->dce_ctx;
+
+ switch (level) {
+ case 0:
+ {
+ info->info0->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info0->name);
+
+ return WERR_OK;
+ }
+ case 1:
+ {
+ info->info1->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info1->name);
+ info->info1->type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ info->info1->comment = share_string_option(mem_ctx, scfg, SHARE_COMMENT, "");
+ W_ERROR_HAVE_NO_MEMORY(info->info1->comment);
+
+ return WERR_OK;
+ }
+ case 2:
+ {
+ info->info2->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info2->name);
+ info->info2->type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ info->info2->comment = share_string_option(mem_ctx, scfg, SHARE_COMMENT, "");
+ W_ERROR_HAVE_NO_MEMORY(info->info2->comment);
+ info->info2->permissions = dcesrv_common_get_share_permissions(mem_ctx, dce_ctx, scfg);
+ info->info2->max_users = share_int_option(scfg, SHARE_MAX_CONNECTIONS, SHARE_MAX_CONNECTIONS_DEFAULT);
+ info->info2->current_users = dcesrv_common_get_share_current_users(mem_ctx, dce_ctx, scfg);
+ info->info2->path = dcesrv_common_get_share_path(mem_ctx, dce_ctx, scfg);
+ W_ERROR_HAVE_NO_MEMORY(info->info2->path);
+ info->info2->password = share_string_option(mem_ctx, scfg, SHARE_PASSWORD, NULL);
+
+ return WERR_OK;
+ }
+ case 501:
+ {
+ info->info501->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info501->name);
+ info->info501->type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ info->info501->comment = share_string_option(mem_ctx, scfg, SHARE_COMMENT, "");
+ W_ERROR_HAVE_NO_MEMORY(info->info501->comment);
+ info->info501->csc_policy = share_int_option(scfg, SHARE_CSC_POLICY, SHARE_CSC_POLICY_DEFAULT);
+
+ return WERR_OK;
+ }
+ case 502:
+ {
+ info->info502->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info502->name);
+ info->info502->type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ info->info502->comment = share_string_option(mem_ctx, scfg, SHARE_COMMENT, "");
+ W_ERROR_HAVE_NO_MEMORY(info->info502->comment);
+ info->info502->permissions = dcesrv_common_get_share_permissions(mem_ctx, dce_ctx, scfg);
+ info->info502->max_users = share_int_option(scfg, SHARE_MAX_CONNECTIONS, SHARE_MAX_CONNECTIONS_DEFAULT);
+ info->info502->current_users = dcesrv_common_get_share_current_users(mem_ctx, dce_ctx, scfg);
+ info->info502->path = dcesrv_common_get_share_path(mem_ctx, dce_ctx, scfg);
+ W_ERROR_HAVE_NO_MEMORY(info->info502->path);
+ info->info502->password = share_string_option(mem_ctx, scfg, SHARE_PASSWORD, NULL);
+ info->info502->sd_buf.sd = dcesrv_common_get_security_descriptor(mem_ctx, dce_ctx, scfg);
+
+ return WERR_OK;
+ }
+ case 1005:
+ {
+ info->info1005->dfs_flags = dcesrv_common_get_share_dfs_flags(mem_ctx, dce_ctx, scfg);
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+/*
+ srvsvc_NetShareEnumAll
+*/
+static WERROR dcesrv_srvsvc_NetShareEnumAll(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareEnumAll *r)
+{
+ NTSTATUS nterr;
+ int numshares = 0;
+ const char **snames;
+ struct share_context *sctx;
+ struct share_config *scfg;
+
+ *r->out.totalentries = 0;
+
+ /* TODO: - paging of results
+ */
+
+ nterr = share_get_context(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_list_all(mem_ctx, sctx, &numshares, &snames);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ {
+ unsigned int i;
+ struct srvsvc_NetShareCtr0 *ctr0;
+
+ ctr0 = talloc(mem_ctx, struct srvsvc_NetShareCtr0);
+ W_ERROR_HAVE_NO_MEMORY(ctr0);
+
+ ctr0->count = numshares;
+ ctr0->array = NULL;
+
+ if (ctr0->count == 0) {
+ r->out.info_ctr->ctr.ctr0 = ctr0;
+ return WERR_OK;
+ }
+
+ ctr0->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo0, ctr0->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr0->array);
+
+ for (i = 0; i < ctr0->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GEN_FAILURE;
+ }
+ info.info0 = &ctr0->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr0 = ctr0;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr0->count;
+ return WERR_OK;
+ }
+ case 1:
+ {
+ unsigned int i;
+ struct srvsvc_NetShareCtr1 *ctr1;
+
+ ctr1 = talloc(mem_ctx, struct srvsvc_NetShareCtr1);
+ W_ERROR_HAVE_NO_MEMORY(ctr1);
+
+ ctr1->count = numshares;
+ ctr1->array = NULL;
+
+ if (ctr1->count == 0) {
+ r->out.info_ctr->ctr.ctr1 = ctr1;
+ return WERR_OK;
+ }
+
+ ctr1->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo1, ctr1->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr1->array);
+
+ for (i=0; i < ctr1->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GEN_FAILURE;
+ }
+ info.info1 = &ctr1->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr1 = ctr1;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr1->count;
+
+ return WERR_OK;
+ }
+ case 2:
+ {
+ unsigned int i;
+ struct srvsvc_NetShareCtr2 *ctr2;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr2 = talloc(mem_ctx, struct srvsvc_NetShareCtr2);
+ W_ERROR_HAVE_NO_MEMORY(ctr2);
+
+ ctr2->count = numshares;
+ ctr2->array = NULL;
+
+ if (ctr2->count == 0) {
+ r->out.info_ctr->ctr.ctr2 = ctr2;
+ return WERR_OK;
+ }
+
+ ctr2->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo2, ctr2->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr2->array);
+
+ for (i=0; i < ctr2->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GEN_FAILURE;
+ }
+ info.info2 = &ctr2->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr2 = ctr2;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr2->count;
+
+ return WERR_OK;
+ }
+ case 501:
+ {
+ unsigned int i;
+ struct srvsvc_NetShareCtr501 *ctr501;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr501 = talloc(mem_ctx, struct srvsvc_NetShareCtr501);
+ W_ERROR_HAVE_NO_MEMORY(ctr501);
+
+ ctr501->count = numshares;
+ ctr501->array = NULL;
+
+ if (ctr501->count == 0) {
+ r->out.info_ctr->ctr.ctr501 = ctr501;
+ return WERR_OK;
+ }
+
+ ctr501->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo501, ctr501->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr501->array);
+
+ for (i=0; i < ctr501->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GEN_FAILURE;
+ }
+ info.info501 = &ctr501->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr501 = ctr501;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr501->count;
+
+ return WERR_OK;
+ }
+ case 502:
+ {
+ unsigned int i;
+ struct srvsvc_NetShareCtr502 *ctr502;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr502 = talloc(mem_ctx, struct srvsvc_NetShareCtr502);
+ W_ERROR_HAVE_NO_MEMORY(ctr502);
+
+ ctr502->count = numshares;
+ ctr502->array = NULL;
+
+ if (ctr502->count == 0) {
+ r->out.info_ctr->ctr.ctr502 = ctr502;
+ return WERR_OK;
+ }
+
+ ctr502->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo502, ctr502->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr502->array);
+
+ for (i=0; i < ctr502->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GEN_FAILURE;
+ }
+ info.info502 = &ctr502->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr502 = ctr502;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr502->count;
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetShareGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetShareGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareGetInfo *r)
+{
+ NTSTATUS nterr;
+ struct share_context *sctx = NULL;
+ struct share_config *scfg = NULL;
+
+ ZERO_STRUCTP(r->out.info);
+
+ /* TODO: - access check
+ */
+
+ if (strcmp("", r->in.share_name) == 0) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ nterr = share_get_context(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_get_config(mem_ctx, sctx, r->in.share_name, &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ switch (r->in.level) {
+ case 0:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ info.info0 = talloc(mem_ctx, struct srvsvc_NetShareInfo0);
+ W_ERROR_HAVE_NO_MEMORY(info.info0);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info->info0 = info.info0;
+ return WERR_OK;
+ }
+ case 1:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ info.info1 = talloc(mem_ctx, struct srvsvc_NetShareInfo1);
+ W_ERROR_HAVE_NO_MEMORY(info.info1);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info->info1 = info.info1;
+ return WERR_OK;
+ }
+ case 2:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ info.info2 = talloc(mem_ctx, struct srvsvc_NetShareInfo2);
+ W_ERROR_HAVE_NO_MEMORY(info.info2);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info->info2 = info.info2;
+ return WERR_OK;
+ }
+ case 501:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ info.info501 = talloc(mem_ctx, struct srvsvc_NetShareInfo501);
+ W_ERROR_HAVE_NO_MEMORY(info.info501);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info->info501 = info.info501;
+ return WERR_OK;
+ }
+ case 502:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ info.info502 = talloc(mem_ctx, struct srvsvc_NetShareInfo502);
+ W_ERROR_HAVE_NO_MEMORY(info.info502);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info->info502 = info.info502;
+ return WERR_OK;
+ }
+ case 1005:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ info.info1005 = talloc(mem_ctx, struct srvsvc_NetShareInfo1005);
+ W_ERROR_HAVE_NO_MEMORY(info.info1005);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info->info1005 = info.info1005;
+ return WERR_OK;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+static WERROR dcesrv_srvsvc_fill_share_info(struct share_info *info, int *count,
+ const char *share_name, int level,
+ const char *name,
+ const char *path,
+ const char *comment,
+ const char *password,
+ enum srvsvc_ShareType type,
+ int32_t max_users,
+ uint32_t csc_policy,
+ struct security_descriptor *sd)
+{
+ unsigned int i = 0;
+
+ if (level == 501) {
+ info[i].name = SHARE_CSC_POLICY;
+ info[i].type = SHARE_INFO_INT;
+ info[i].value = talloc(info, int);
+ *((int *)info[i].value) = csc_policy;
+ i++;
+ }
+
+ switch(level) {
+
+ case 502:
+ /* TODO: check if unknown is csc_policy */
+
+ /* TODO: security descriptor */
+
+ case 2:
+ if (path && path[0]) {
+ info[i].name = SHARE_PATH;
+ info[i].type = SHARE_INFO_STRING;
+
+ /* Windows will send a path in a form of C:\example\path */
+ if (path[1] == ':') {
+ info[i].value = talloc_strdup(info, &path[2]);
+ } else {
+ /* very strange let's try to set as is */
+ info[i].value = talloc_strdup(info, path);
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ all_string_sub((char *)info[i].value, "\\", "/", 0);
+
+ i++;
+ }
+
+ if (password && password[0]) {
+ info[i].name = SHARE_PASSWORD;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, password);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ info[i].name = SHARE_MAX_CONNECTIONS;
+ info[i].type = SHARE_INFO_INT;
+ info[i].value = talloc(info, int);
+ *((int *)info[i].value) = max_users;
+ i++;
+
+ FALL_THROUGH;
+ case 501:
+ case 1:
+ info[i].name = SHARE_TYPE;
+ info[i].type = SHARE_INFO_STRING;
+ switch (type) {
+ case 0x00:
+ info[i].value = talloc_strdup(info, "DISK");
+ break;
+ case 0x01:
+ info[i].value = talloc_strdup(info, "PRINTER");
+ break;
+ case 0x03:
+ info[i].value = talloc_strdup(info, "IPC");
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ i++;
+
+ FALL_THROUGH;
+ case 1004:
+ if (comment) {
+ info[i].name = SHARE_COMMENT;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, comment);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ FALL_THROUGH;
+ case 0:
+ if (name &&
+ strcasecmp(share_name, name) != 0) {
+ info[i].name = SHARE_NAME;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, name);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ i++;
+ }
+
+ break;
+
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ *count = i;
+
+ return WERR_OK;
+}
+
+/*
+ srvsvc_NetShareSetInfo
+*/
+static WERROR dcesrv_srvsvc_NetShareSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareSetInfo *r)
+{
+ NTSTATUS nterr;
+ WERROR status;
+ struct share_context *sctx = NULL;
+ struct share_info *info;
+ int count;
+
+ /* TODO: - access check
+ */
+
+ /* there are no more than 10 options in all struct srvsvc_NetShareInfoXXX */
+ info = talloc_array(mem_ctx, struct share_info, 10);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ if (strcmp("", r->in.share_name) == 0) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ nterr = share_get_context(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ switch (r->in.level) {
+ case 0:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info->info0->name,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ 0,
+ NULL);
+ if (!W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 1:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info->info1->name,
+ NULL,
+ r->in.info->info1->comment,
+ NULL,
+ r->in.info->info1->type,
+ 0,
+ 0,
+ NULL);
+ if (!W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 2:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info->info2->name,
+ r->in.info->info2->path,
+ r->in.info->info2->comment,
+ r->in.info->info2->password,
+ r->in.info->info2->type,
+ r->in.info->info2->max_users,
+ 0,
+ NULL);
+ if (!W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 501:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info->info501->name,
+ NULL,
+ r->in.info->info501->comment,
+ NULL,
+ r->in.info->info501->type,
+ 0,
+ r->in.info->info501->csc_policy,
+ NULL);
+ if (!W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 502:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info->info502->name,
+ r->in.info->info502->path,
+ r->in.info->info502->comment,
+ r->in.info->info502->password,
+ r->in.info->info502->type,
+ r->in.info->info502->max_users,
+ 0,
+ r->in.info->info502->sd_buf.sd);
+ if (!W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 1004:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ NULL,
+ NULL,
+ r->in.info->info1004->comment,
+ NULL,
+ 0,
+ 0,
+ 0,
+ NULL);
+ if (!W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 1005:
+ {
+ /* r->in.info.dfs_flags; */
+
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ nterr = share_set(sctx, r->in.share_name, info, count);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ srvsvc_NetShareDelSticky
+*/
+static WERROR dcesrv_srvsvc_NetShareDelSticky(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareDelSticky *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetShareCheck
+*/
+static WERROR dcesrv_srvsvc_NetShareCheck(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareCheck *r)
+{
+ NTSTATUS nterr;
+ struct share_context *sctx = NULL;
+ struct share_config *scfg = NULL;
+ char *device;
+ const char **names;
+ int count;
+ int i;
+
+ *r->out.type = 0;
+
+ /* TODO: - access check
+ */
+
+ if (strcmp("", r->in.device_name) == 0) {
+ *r->out.type = STYPE_IPC;
+ return WERR_OK;
+ }
+
+ /* copy the path skipping C:\ */
+ if (strncasecmp(r->in.device_name, "C:", 2) == 0) {
+ device = talloc_strdup(mem_ctx, &r->in.device_name[2]);
+ } else {
+ /* no chance we have a share that doesn't start with C:\ */
+ return WERR_NERR_DEVICENOTSHARED;
+ }
+ all_string_sub(device, "\\", "/", 0);
+
+ nterr = share_get_context(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_list_all(mem_ctx, sctx, &count, &names);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ for (i = 0; i < count; i++) {
+ const char *path;
+ const char *type;
+
+ nterr = share_get_config(mem_ctx, sctx, names[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+ path = share_string_option(mem_ctx, scfg, SHARE_PATH, NULL);
+ if (!path) continue;
+
+ if (strcmp(device, path) == 0) {
+ type = share_string_option(mem_ctx, scfg, SHARE_TYPE, NULL);
+ if (!type) continue;
+
+ if (strcmp(type, "DISK") == 0) {
+ *r->out.type = STYPE_DISKTREE;
+ return WERR_OK;
+ }
+
+ if (strcmp(type, "IPC") == 0) {
+ *r->out.type = STYPE_IPC;
+ return WERR_OK;
+ }
+
+ if (strcmp(type, "PRINTER") == 0) {
+ *r->out.type = STYPE_PRINTQ;
+ return WERR_OK;
+ }
+ }
+ }
+
+ return WERR_NERR_DEVICENOTSHARED;
+}
+
+
+/*
+ srvsvc_NetSrvGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetSrvGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSrvGetInfo *r)
+{
+ struct dcesrv_context *dce_ctx = dce_call->conn->dce_ctx;
+ struct dcerpc_server_info *server_info = lpcfg_dcerpc_server_info(mem_ctx, dce_ctx->lp_ctx);
+ const struct loadparm_substitution *lp_sub =
+ lpcfg_noop_substitution();
+
+ ZERO_STRUCTP(r->out.info);
+
+ switch (r->in.level) {
+ case 100:
+ {
+ struct srvsvc_NetSrvInfo100 *info100;
+
+ info100 = talloc(mem_ctx, struct srvsvc_NetSrvInfo100);
+ W_ERROR_HAVE_NO_MEMORY(info100);
+
+ info100->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info100->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, r->in.server_unc);
+ W_ERROR_HAVE_NO_MEMORY(info100->server_name);
+
+ r->out.info->info100 = info100;
+ return WERR_OK;
+ }
+ case 101:
+ {
+ struct srvsvc_NetSrvInfo101 *info101;
+
+ info101 = talloc(mem_ctx, struct srvsvc_NetSrvInfo101);
+ W_ERROR_HAVE_NO_MEMORY(info101);
+
+ info101->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info101->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, r->in.server_unc);
+ W_ERROR_HAVE_NO_MEMORY(info101->server_name);
+
+ info101->version_major = server_info->version_major;
+ info101->version_minor = server_info->version_minor;
+ info101->server_type = dcesrv_common_get_server_type(mem_ctx, dce_call->event_ctx, dce_ctx);
+ info101->comment = lpcfg_server_string(dce_ctx->lp_ctx, lp_sub, mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(info101->comment);
+
+ r->out.info->info101 = info101;
+ return WERR_OK;
+ }
+ case 102:
+ {
+ struct srvsvc_NetSrvInfo102 *info102;
+
+ info102 = talloc(mem_ctx, struct srvsvc_NetSrvInfo102);
+ W_ERROR_HAVE_NO_MEMORY(info102);
+
+ info102->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info102->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, r->in.server_unc);
+ W_ERROR_HAVE_NO_MEMORY(info102->server_name);
+
+ info102->version_major = server_info->version_major;
+ info102->version_minor = server_info->version_minor;
+ info102->server_type = dcesrv_common_get_server_type(mem_ctx, dce_call->event_ctx, dce_ctx);
+ info102->comment = lpcfg_server_string(dce_ctx->lp_ctx, lp_sub, mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(info102->comment);
+
+ info102->users = dcesrv_common_get_users(mem_ctx, dce_ctx);
+ info102->disc = dcesrv_common_get_disc(mem_ctx, dce_ctx);
+ info102->hidden = dcesrv_common_get_hidden(mem_ctx, dce_ctx);
+ info102->announce = dcesrv_common_get_announce(mem_ctx, dce_ctx);
+ info102->anndelta = dcesrv_common_get_anndelta(mem_ctx, dce_ctx);
+ info102->licenses = dcesrv_common_get_licenses(mem_ctx, dce_ctx);
+ info102->userpath = dcesrv_common_get_userpath(mem_ctx, dce_ctx);
+ W_ERROR_HAVE_NO_MEMORY(info102->userpath);
+
+ r->out.info->info102 = info102;
+ return WERR_OK;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetSrvSetInfo
+*/
+static WERROR dcesrv_srvsvc_NetSrvSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSrvSetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetDiskEnum
+*/
+static WERROR dcesrv_srvsvc_NetDiskEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetDiskEnum *r)
+{
+ r->out.info->disks = NULL;
+ r->out.info->count = 0;
+ *r->out.totalentries = 0;
+
+ switch (r->in.level) {
+ case 0:
+ {
+ /* we can safely hardcode the reply and report we have only one disk (C:) */
+ /* for some reason Windows wants 2 entries with the second being empty */
+ r->out.info->disks = talloc_array(mem_ctx, struct srvsvc_NetDiskInfo0, 2);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->disks);
+ r->out.info->count = 2;
+
+ r->out.info->disks[0].disk = talloc_strdup(mem_ctx, "C:");
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->disks[0].disk);
+
+ r->out.info->disks[1].disk = talloc_strdup(mem_ctx, "");
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->disks[1].disk);
+
+ *r->out.totalentries = 1;
+ r->out.resume_handle = r->in.resume_handle;
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetServerStatisticsGet
+*/
+static WERROR dcesrv_srvsvc_NetServerStatisticsGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetServerStatisticsGet *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetTransportAdd
+*/
+static WERROR dcesrv_srvsvc_NetTransportAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetTransportAdd *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetTransportEnum
+*/
+static WERROR dcesrv_srvsvc_NetTransportEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetTransportEnum *r)
+{
+ r->out.transports->level = r->in.transports->level;
+ *r->out.totalentries = 0;
+ if (r->out.resume_handle) {
+ *r->out.resume_handle = 0;
+ }
+
+ switch (r->in.transports->level) {
+ case 0:
+ {
+ r->out.transports->ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetTransportCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.transports->ctr.ctr0);
+
+ r->out.transports->ctr.ctr0->count = 0;
+ r->out.transports->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ r->out.transports->ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetTransportCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.transports->ctr.ctr1);
+
+ r->out.transports->ctr.ctr1->count = 0;
+ r->out.transports->ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 2:
+ {
+ r->out.transports->ctr.ctr2 = talloc(mem_ctx, struct srvsvc_NetTransportCtr2);
+ W_ERROR_HAVE_NO_MEMORY(r->out.transports->ctr.ctr2);
+
+ r->out.transports->ctr.ctr2->count = 0;
+ r->out.transports->ctr.ctr2->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 3:
+ {
+ r->out.transports->ctr.ctr3 = talloc(mem_ctx, struct srvsvc_NetTransportCtr3);
+ W_ERROR_HAVE_NO_MEMORY(r->out.transports->ctr.ctr3);
+
+ r->out.transports->ctr.ctr3->count = 0;
+ r->out.transports->ctr.ctr3->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+/*
+ srvsvc_NetTransportDel
+*/
+static WERROR dcesrv_srvsvc_NetTransportDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetTransportDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetRemoteTOD
+*/
+static WERROR dcesrv_srvsvc_NetRemoteTOD(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetRemoteTOD *r)
+{
+ struct timeval tval;
+ time_t t;
+ struct tm tm;
+ struct srvsvc_NetRemoteTODInfo *info;
+
+ info = talloc(mem_ctx, struct srvsvc_NetRemoteTODInfo);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ GetTimeOfDay(&tval);
+ t = tval.tv_sec;
+
+ gmtime_r(&t, &tm);
+
+ info->elapsed = t;
+ /* TODO: fake the uptime: just return the milliseconds till 0:00:00 today */
+ info->msecs = (tm.tm_hour*60*60*1000)
+ + (tm.tm_min*60*1000)
+ + (tm.tm_sec*1000)
+ + (tval.tv_usec/1000);
+ info->hours = tm.tm_hour;
+ info->mins = tm.tm_min;
+ info->secs = tm.tm_sec;
+ info->hunds = tval.tv_usec/10000;
+ info->timezone = get_time_zone(t)/60;
+ info->tinterval = 310; /* just return the same as windows */
+ info->day = tm.tm_mday;
+ info->month = tm.tm_mon + 1;
+ info->year = tm.tm_year + 1900;
+ info->weekday = tm.tm_wday;
+
+ *r->out.info = info;
+
+ return WERR_OK;
+}
+
+/*
+ srvsvc_NetPathType
+*/
+static WERROR dcesrv_srvsvc_NetPathType(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetPathType *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetPathCanonicalize
+*/
+static WERROR dcesrv_srvsvc_NetPathCanonicalize(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetPathCanonicalize *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetPathCompare
+*/
+static WERROR dcesrv_srvsvc_NetPathCompare(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetPathCompare *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetNameValidate
+*/
+static WERROR dcesrv_srvsvc_NetNameValidate(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetNameValidate *r)
+{
+ int len;
+
+ if ((r->in.flags != 0x0) && (r->in.flags != 0x80000000)) {
+ return WERR_INVALID_NAME;
+ }
+
+ switch (r->in.name_type) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ return WERR_NOT_SUPPORTED;
+
+ case 9: /* validate share name */
+
+ len = strlen_m(r->in.name);
+ if ((r->in.flags == 0x0) && (len > 81)) {
+ return WERR_INVALID_NAME;
+ }
+ if ((r->in.flags == 0x80000000) && (len > 13)) {
+ return WERR_INVALID_NAME;
+ }
+ if (! dcesrv_common_validate_share_name(mem_ctx, r->in.name)) {
+ return WERR_INVALID_NAME;
+ }
+ return WERR_OK;
+
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ return WERR_NOT_SUPPORTED;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+}
+
+
+/*
+ srvsvc_NetPRNameCompare
+*/
+static WERROR dcesrv_srvsvc_NetPRNameCompare(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetPRNameCompare *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetShareEnum
+*/
+static WERROR dcesrv_srvsvc_NetShareEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareEnum *r)
+{
+ NTSTATUS nterr;
+ int numshares = 0;
+ const char **snames;
+ struct share_context *sctx;
+ struct share_config *scfg;
+ struct dcesrv_context *dce_ctx = dce_call->conn->dce_ctx;
+
+ *r->out.totalentries = 0;
+
+ /* TODO: - paging of results
+ */
+
+ nterr = share_get_context(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_list_all(mem_ctx, sctx, &numshares, &snames);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ {
+ unsigned int i, y = 0;
+ unsigned int count;
+ struct srvsvc_NetShareCtr0 *ctr0;
+
+ ctr0 = talloc(mem_ctx, struct srvsvc_NetShareCtr0);
+ W_ERROR_HAVE_NO_MEMORY(ctr0);
+
+ count = numshares;
+ ctr0->count = count;
+ ctr0->array = NULL;
+
+ if (ctr0->count == 0) {
+ r->out.info_ctr->ctr.ctr0 = ctr0;
+ return WERR_OK;
+ }
+
+ ctr0->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo0, count);
+ W_ERROR_HAVE_NO_MEMORY(ctr0->array);
+
+ for (i=0; i < count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+ enum srvsvc_ShareType type;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GEN_FAILURE;
+ }
+
+ type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ if (type & STYPE_HIDDEN) {
+ ctr0->count--;
+ talloc_free(scfg);
+ continue;
+ }
+
+ info.info0 = &ctr0->array[y];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ W_ERROR_NOT_OK_RETURN(status);
+ talloc_free(scfg);
+ y++;
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr0 = ctr0;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr0->count;
+
+ return WERR_OK;
+ }
+ case 1:
+ {
+ unsigned int i, y = 0;
+ unsigned int count;
+ struct srvsvc_NetShareCtr1 *ctr1;
+
+ ctr1 = talloc(mem_ctx, struct srvsvc_NetShareCtr1);
+ W_ERROR_HAVE_NO_MEMORY(ctr1);
+
+ count = numshares;
+ ctr1->count = count;
+ ctr1->array = NULL;
+
+ if (ctr1->count == 0) {
+ r->out.info_ctr->ctr.ctr1 = ctr1;
+ return WERR_OK;
+ }
+
+ ctr1->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo1, count);
+ W_ERROR_HAVE_NO_MEMORY(ctr1->array);
+
+ for (i=0; i < count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+ enum srvsvc_ShareType type;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GEN_FAILURE;
+ }
+
+ type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ if (type & STYPE_HIDDEN) {
+ ctr1->count--;
+ talloc_free(scfg);
+ continue;
+ }
+
+ info.info1 = &ctr1->array[y];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ W_ERROR_NOT_OK_RETURN(status);
+ talloc_free(scfg);
+ y++;
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr1 = ctr1;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr1->count;
+
+ return WERR_OK;
+ }
+ case 2:
+ {
+ unsigned int i, y = 0;
+ unsigned int count;
+ struct srvsvc_NetShareCtr2 *ctr2;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr2 = talloc(mem_ctx, struct srvsvc_NetShareCtr2);
+ W_ERROR_HAVE_NO_MEMORY(ctr2);
+
+ count = numshares;
+ ctr2->count = count;
+ ctr2->array = NULL;
+
+ if (ctr2->count == 0) {
+ r->out.info_ctr->ctr.ctr2 = ctr2;
+ return WERR_OK;
+ }
+
+ ctr2->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo2, count);
+ W_ERROR_HAVE_NO_MEMORY(ctr2->array);
+
+ for (i=0; i < count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+ enum srvsvc_ShareType type;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GEN_FAILURE;
+ }
+
+ type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ if (type & STYPE_HIDDEN) {
+ ctr2->count--;
+ talloc_free(scfg);
+ continue;
+ }
+
+ info.info2 = &ctr2->array[y];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ W_ERROR_NOT_OK_RETURN(status);
+ talloc_free(scfg);
+ y++;
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr2 = ctr2;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr2->count;
+
+ return WERR_OK;
+ }
+ case 502:
+ {
+ unsigned int i, y = 0;
+ unsigned int count;
+ struct srvsvc_NetShareCtr502 *ctr502;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr502 = talloc(mem_ctx, struct srvsvc_NetShareCtr502);
+ W_ERROR_HAVE_NO_MEMORY(ctr502);
+
+ count = numshares;
+ ctr502->count = count;
+ ctr502->array = NULL;
+
+ if (ctr502->count == 0) {
+ r->out.info_ctr->ctr.ctr502 = ctr502;
+ return WERR_OK;
+ }
+
+ ctr502->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo502, count);
+ W_ERROR_HAVE_NO_MEMORY(ctr502->array);
+
+ for (i=0; i < count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+ enum srvsvc_ShareType type;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GEN_FAILURE;
+ }
+
+ type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ if (type & STYPE_HIDDEN) {
+ ctr502->count--;
+ talloc_free(scfg);
+ continue;
+ }
+
+ info.info502 = &ctr502->array[y];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ W_ERROR_NOT_OK_RETURN(status);
+ talloc_free(scfg);
+ y++;
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr502 = ctr502;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr502->count;
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ srvsvc_NetShareDelStart
+*/
+static WERROR dcesrv_srvsvc_NetShareDelStart(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareDelStart *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetShareDelCommit
+*/
+static WERROR dcesrv_srvsvc_NetShareDelCommit(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareDelCommit *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetGetFileSecurity
+*/
+static WERROR dcesrv_srvsvc_NetGetFileSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetGetFileSecurity *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct sec_desc_buf *sd_buf;
+ struct ntvfs_context *ntvfs_ctx = NULL;
+ struct ntvfs_request *ntvfs_req;
+ union smb_fileinfo *io;
+ NTSTATUS nt_status;
+
+ nt_status = srvsvc_create_ntvfs_context(dce_call, mem_ctx, r->in.share, &ntvfs_ctx);
+ if (!NT_STATUS_IS_OK(nt_status)) return ntstatus_to_werror(nt_status);
+
+ ntvfs_req = ntvfs_request_create(ntvfs_ctx, mem_ctx,
+ session_info,
+ 0,
+ dce_call->time,
+ NULL, NULL, 0);
+ W_ERROR_HAVE_NO_MEMORY(ntvfs_req);
+
+ sd_buf = talloc(mem_ctx, struct sec_desc_buf);
+ W_ERROR_HAVE_NO_MEMORY(sd_buf);
+
+ io = talloc(mem_ctx, union smb_fileinfo);
+ W_ERROR_HAVE_NO_MEMORY(io);
+
+ io->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ io->query_secdesc.in.file.path = r->in.file;
+ io->query_secdesc.in.secinfo_flags = r->in.securityinformation;
+
+ nt_status = ntvfs_qpathinfo(ntvfs_req, io);
+ if (!NT_STATUS_IS_OK(nt_status)) return ntstatus_to_werror(nt_status);
+
+ sd_buf->sd = io->query_secdesc.out.sd;
+
+ *r->out.sd_buf = sd_buf;
+ return WERR_OK;
+}
+
+
+/*
+ srvsvc_NetSetFileSecurity
+*/
+static WERROR dcesrv_srvsvc_NetSetFileSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSetFileSecurity *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct ntvfs_context *ntvfs_ctx;
+ struct ntvfs_request *ntvfs_req;
+ union smb_setfileinfo *io;
+ NTSTATUS nt_status;
+
+ nt_status = srvsvc_create_ntvfs_context(dce_call, mem_ctx, r->in.share, &ntvfs_ctx);
+ if (!NT_STATUS_IS_OK(nt_status)) return ntstatus_to_werror(nt_status);
+
+ ntvfs_req = ntvfs_request_create(ntvfs_ctx, mem_ctx,
+ session_info,
+ 0,
+ dce_call->time,
+ NULL, NULL, 0);
+ W_ERROR_HAVE_NO_MEMORY(ntvfs_req);
+
+ io = talloc(mem_ctx, union smb_setfileinfo);
+ W_ERROR_HAVE_NO_MEMORY(io);
+
+ io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ io->set_secdesc.in.file.path = r->in.file;
+ io->set_secdesc.in.secinfo_flags = r->in.securityinformation;
+ io->set_secdesc.in.sd = r->in.sd_buf->sd;
+
+ nt_status = ntvfs_setpathinfo(ntvfs_req, io);
+ if (!NT_STATUS_IS_OK(nt_status)) return ntstatus_to_werror(nt_status);
+
+ return WERR_OK;
+}
+
+
+/*
+ srvsvc_NetServerTransportAddEx
+*/
+static WERROR dcesrv_srvsvc_NetServerTransportAddEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetServerTransportAddEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetServerSetServiceBitsEx
+*/
+static WERROR dcesrv_srvsvc_NetServerSetServiceBitsEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetServerSetServiceBitsEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSGETVERSION
+*/
+static WERROR dcesrv_srvsvc_NETRDFSGETVERSION(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSGETVERSION *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSCREATELOCALPARTITION
+*/
+static WERROR dcesrv_srvsvc_NETRDFSCREATELOCALPARTITION(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSCREATELOCALPARTITION *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSDELETELOCALPARTITION
+*/
+static WERROR dcesrv_srvsvc_NETRDFSDELETELOCALPARTITION(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSDELETELOCALPARTITION *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSSETLOCALVOLUMESTATE
+*/
+static WERROR dcesrv_srvsvc_NETRDFSSETLOCALVOLUMESTATE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSSETLOCALVOLUMESTATE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSSETSERVERINFO
+*/
+static WERROR dcesrv_srvsvc_NETRDFSSETSERVERINFO(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSSETSERVERINFO *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSCREATEEXITPOINT
+*/
+static WERROR dcesrv_srvsvc_NETRDFSCREATEEXITPOINT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSCREATEEXITPOINT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSDELETEEXITPOINT
+*/
+static WERROR dcesrv_srvsvc_NETRDFSDELETEEXITPOINT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSDELETEEXITPOINT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSMODIFYPREFIX
+*/
+static WERROR dcesrv_srvsvc_NETRDFSMODIFYPREFIX(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSMODIFYPREFIX *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSFIXLOCALVOLUME
+*/
+static WERROR dcesrv_srvsvc_NETRDFSFIXLOCALVOLUME(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSFIXLOCALVOLUME *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSMANAGERREPORTSITEINFO
+*/
+static WERROR dcesrv_srvsvc_NETRDFSMANAGERREPORTSITEINFO(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSMANAGERREPORTSITEINFO *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRSERVERTRANSPORTDELEX
+*/
+static WERROR dcesrv_srvsvc_NETRSERVERTRANSPORTDELEX(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRSERVERTRANSPORTDELEX *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ srvsvc_NetShareDel
+*/
+static WERROR dcesrv_srvsvc_NetShareDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareDel *r)
+{
+ NTSTATUS nterr;
+ struct share_context *sctx;
+
+ nterr = share_get_context(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_remove(sctx, r->in.share_name);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ return WERR_OK;
+}
+
+/*
+ srvsvc_NetSetServiceBits
+*/
+static WERROR dcesrv_srvsvc_NetSetServiceBits(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSetServiceBits *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ srvsvc_NETRPRNAMECANONICALIZE
+*/
+static WERROR dcesrv_srvsvc_NETRPRNAMECANONICALIZE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRPRNAMECANONICALIZE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_srvsvc_s.c"
diff --git a/source4/rpc_server/srvsvc/srvsvc_ntvfs.c b/source4/rpc_server/srvsvc/srvsvc_ntvfs.c
new file mode 100644
index 0000000..cbd0eb3
--- /dev/null
+++ b/source4/rpc_server/srvsvc/srvsvc_ntvfs.c
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ srvsvc pipe ntvfs helper functions
+
+ Copyright (C) Stefan (metze) Metzmacher 2006
+
+ 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 "includes.h"
+#include "ntvfs/ntvfs.h"
+#include "rpc_server/dcerpc_server.h"
+#include "param/param.h"
+#include "rpc_server/srvsvc/proto.h"
+
+struct srvsvc_ntvfs_ctx {
+ struct ntvfs_context *ntvfs;
+};
+
+static int srvsvc_ntvfs_ctx_destructor(struct srvsvc_ntvfs_ctx *c)
+{
+ ntvfs_disconnect(c->ntvfs);
+ return 0;
+}
+
+NTSTATUS srvsvc_create_ntvfs_context(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ const char *share,
+ struct ntvfs_context **_ntvfs)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct imessaging_context *imsg_ctx =
+ dcesrv_imessaging_context(dce_call->conn);
+ struct server_id server_id = dcesrv_server_id(dce_call->conn);
+ NTSTATUS status;
+ struct srvsvc_ntvfs_ctx *c;
+ struct ntvfs_request *ntvfs_req;
+ enum ntvfs_type type;
+ struct share_context *sctx;
+ struct share_config *scfg;
+ char *sharetype;
+ union smb_tcon tcon;
+ const struct tsocket_address *local_address;
+ const struct tsocket_address *remote_address;
+
+ status = share_get_context(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = share_get_config(mem_ctx, sctx, share, &scfg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("srvsvc_create_ntvfs_context: couldn't find service %s\n", share));
+ return status;
+ }
+
+#if 0 /* TODO: fix access cecking */
+ if (!socket_check_access(dce_call->connection->socket,
+ scfg->name,
+ share_string_list_option(scfg, SHARE_HOSTS_ALLOW),
+ share_string_list_option(scfg, SHARE_HOSTS_DENY))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+#endif
+
+ /* work out what sort of connection this is */
+ sharetype = share_string_option(mem_ctx, scfg, SHARE_TYPE, SHARE_TYPE_DEFAULT);
+ if (sharetype && strcmp(sharetype, "IPC") == 0) {
+ type = NTVFS_IPC;
+ } else if (sharetype && strcmp(sharetype, "PRINTER")) {
+ type = NTVFS_PRINT;
+ } else {
+ type = NTVFS_DISK;
+ }
+
+ TALLOC_FREE(sharetype);
+
+ c = talloc(mem_ctx, struct srvsvc_ntvfs_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(c);
+
+ /* init ntvfs function pointers */
+ status = ntvfs_init_connection(c, scfg, type,
+ PROTOCOL_NT1,
+ 0,/* ntvfs_client_caps */
+ dce_call->event_ctx,
+ imsg_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ server_id,
+ &c->ntvfs);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("srvsvc_create_ntvfs_context: ntvfs_init_connection failed for service %s\n",
+ scfg->name));
+ return status;
+ }
+ talloc_set_destructor(c, srvsvc_ntvfs_ctx_destructor);
+
+ /*
+ * NOTE: we only set the addr callbacks as we're not interesseted in oplocks or in getting file handles
+ */
+ local_address = dcesrv_connection_get_local_address(dce_call->conn);
+ remote_address = dcesrv_connection_get_remote_address(dce_call->conn);
+ status = ntvfs_set_addresses(c->ntvfs, local_address, remote_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("srvsvc_create_ntvfs_context: NTVFS failed to set the addr callbacks!\n"));
+ return status;
+ }
+
+ ntvfs_req = ntvfs_request_create(c->ntvfs, mem_ctx,
+ session_info,
+ 0, /* TODO: fill in PID */
+ dce_call->time,
+ NULL, NULL, 0);
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs_req);
+
+ /* Invoke NTVFS connection hook */
+ tcon.tcon.level = RAW_TCON_TCON;
+ ZERO_STRUCT(tcon.tcon.in);
+ tcon.tcon.in.service = scfg->name;
+ status = ntvfs_connect(ntvfs_req, &tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("srvsvc_create_ntvfs_context: NTVFS ntvfs_connect() failed!\n"));
+ return status;
+ }
+
+ *_ntvfs = c->ntvfs;
+ return NT_STATUS_OK;
+}
diff --git a/source4/rpc_server/tests/rpc_dns_server_dnsutils_test.c b/source4/rpc_server/tests/rpc_dns_server_dnsutils_test.c
new file mode 100644
index 0000000..cdb245e
--- /dev/null
+++ b/source4/rpc_server/tests/rpc_dns_server_dnsutils_test.c
@@ -0,0 +1,304 @@
+/*
+ * Unit tests for source4/rpc_server/dnsserver/dnsutils.c
+ *
+ * Copyright (C) Catalyst.NET Ltd 2018
+ *
+ * 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/>.
+ *
+ */
+
+/*
+ * from cmocka.c:
+ * These headers or their equivalents should be included prior to
+ * including
+ * this header file.
+ *
+ * #include <stdarg.h>
+ * #include <stddef.h>
+ * #include <setjmp.h>
+ *
+ * This allows test applications to use custom definitions of C standard
+ * library functions and types.
+ *
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+
+#include "../dnsserver/dnsutils.c"
+
+
+/*
+ * Test setting of an empty ZONE_MASTER_SERVERS property
+ */
+static void test_dnsserver_init_zoneinfo_master_servers_empty(void **state)
+{
+ struct dnsserver_zone *zone = NULL;
+ struct dnsserver_serverinfo *serverinfo = NULL;
+ struct dnsserver_zoneinfo *zoneinfo = NULL;
+ struct dnsp_DnsProperty *property = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ /*
+ * Setup the zone data
+ */
+ zone = talloc_zero(ctx, struct dnsserver_zone);
+ assert_non_null(zone);
+ zone->name = "test";
+
+ /*
+ * Set up an empty ZONE_MASTER_SERVERS property
+ */
+ property = talloc_zero_array(ctx, struct dnsp_DnsProperty, 1);
+ assert_non_null(property);
+ property->id = DSPROPERTY_ZONE_MASTER_SERVERS;
+ property->data.master_servers.addrCount = 0;
+ property->data.master_servers.addrArray = NULL;
+
+ zone->tmp_props = property;
+ zone->num_props = 1;
+
+
+ /*
+ * Setup the server info
+ */
+ serverinfo = talloc_zero(ctx, struct dnsserver_serverinfo);
+ assert_non_null(serverinfo);
+
+ /*
+ * call dnsserver_init_zoneinfo
+ */
+ zoneinfo = dnsserver_init_zoneinfo(zone, serverinfo);
+
+ /*
+ * Check results
+ */
+ assert_non_null(zoneinfo);
+ assert_non_null(zoneinfo->aipLocalMasters);
+ assert_int_equal(zoneinfo->aipLocalMasters->AddrCount, 0);
+ assert_null(zoneinfo->aipLocalMasters->AddrArray);
+
+ TALLOC_FREE(ctx);
+}
+
+/*
+ * Test setting of a non empty ZONE_MASTER_SERVERS property
+ */
+static void test_dnsserver_init_zoneinfo_master_servers(void **state)
+{
+ struct dnsserver_zone *zone = NULL;
+ struct dnsserver_serverinfo *serverinfo = NULL;
+ struct dnsserver_zoneinfo *zoneinfo = NULL;
+ struct dnsp_DnsProperty *property = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ /*
+ * Setup the zone data
+ */
+ zone = talloc_zero(ctx, struct dnsserver_zone);
+ assert_non_null(zone);
+ zone->name = "test";
+
+ /*
+ * Set up an empty ZONE_MASTER_SERVERS property
+ */
+ property = talloc_zero_array(ctx, struct dnsp_DnsProperty, 1);
+ assert_non_null(property);
+ property->id = DSPROPERTY_ZONE_MASTER_SERVERS;
+ property->data.master_servers.addrCount = 4;
+ property->data.master_servers.addrArray =
+ talloc_zero_array(ctx, uint32_t, 4);
+ property->data.master_servers.addrArray[0] = 1000;
+ property->data.master_servers.addrArray[1] = 1001;
+ property->data.master_servers.addrArray[2] = 1002;
+ property->data.master_servers.addrArray[3] = 1003;
+
+ zone->tmp_props = property;
+ zone->num_props = 1;
+
+
+ /*
+ * Setup the server info
+ */
+ serverinfo = talloc_zero(ctx, struct dnsserver_serverinfo);
+ assert_non_null(serverinfo);
+
+ /*
+ * call dnsserver_init_zoneinfo
+ */
+ zoneinfo = dnsserver_init_zoneinfo(zone, serverinfo);
+
+ /*
+ * Check results
+ */
+ assert_non_null(zoneinfo);
+ assert_non_null(zoneinfo->aipLocalMasters);
+ assert_int_equal(zoneinfo->aipLocalMasters->AddrCount, 4);
+ assert_non_null(zoneinfo->aipLocalMasters->AddrArray);
+ assert_int_equal(zoneinfo->aipLocalMasters->AddrArray[0], 1000);
+ assert_int_equal(zoneinfo->aipLocalMasters->AddrArray[1], 1001);
+ assert_int_equal(zoneinfo->aipLocalMasters->AddrArray[2], 1002);
+ assert_int_equal(zoneinfo->aipLocalMasters->AddrArray[3], 1003);
+
+ /*
+ * Ensure that we're working with a copy of the property data
+ * and not a reference.
+ * The pointers should be different, and we should be able to change
+ * the values in the property without affecting the zoneinfo data
+ */
+ assert_true(zoneinfo->aipLocalMasters->AddrArray !=
+ property->data.master_servers.addrArray);
+ property->data.master_servers.addrArray[0] = 0;
+ property->data.master_servers.addrArray[1] = 1;
+ property->data.master_servers.addrArray[2] = 2;
+ property->data.master_servers.addrArray[3] = 3;
+ assert_int_equal(zoneinfo->aipLocalMasters->AddrArray[0], 1000);
+ assert_int_equal(zoneinfo->aipLocalMasters->AddrArray[1], 1001);
+ assert_int_equal(zoneinfo->aipLocalMasters->AddrArray[2], 1002);
+ assert_int_equal(zoneinfo->aipLocalMasters->AddrArray[3], 1003);
+
+ TALLOC_FREE(ctx);
+}
+
+/*
+ * Test setting of an empty ZONE_SCAVENGING_SERVERS property
+ */
+static void test_dnsserver_init_zoneinfo_scavenging_servers_empty(void **state)
+{
+ struct dnsserver_zone *zone = NULL;
+ struct dnsserver_serverinfo *serverinfo = NULL;
+ struct dnsserver_zoneinfo *zoneinfo = NULL;
+ struct dnsp_DnsProperty *property = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ /*
+ * Setup the zone data
+ */
+ zone = talloc_zero(ctx, struct dnsserver_zone);
+ assert_non_null(zone);
+ zone->name = "test";
+
+ property = talloc_zero_array(ctx, struct dnsp_DnsProperty, 1);
+ assert_non_null(property);
+ property->id = DSPROPERTY_ZONE_SCAVENGING_SERVERS;
+ property->data.servers.addrCount = 0;
+ property->data.servers.addrArray = NULL;
+
+ zone->tmp_props = property;
+ zone->num_props = 1;
+
+
+ serverinfo = talloc_zero(ctx, struct dnsserver_serverinfo);
+ assert_non_null(serverinfo);
+
+ zoneinfo = dnsserver_init_zoneinfo(zone, serverinfo);
+
+ assert_non_null(zoneinfo);
+ assert_non_null(zoneinfo->aipScavengeServers);
+ assert_int_equal(zoneinfo->aipScavengeServers->AddrCount, 0);
+ assert_null(zoneinfo->aipScavengeServers->AddrArray);
+
+ TALLOC_FREE(ctx);
+}
+
+/*
+ * Test setting of a non empty ZONE_SCAVENGING_SERVERS property
+ */
+static void test_dnsserver_init_zoneinfo_scavenging_servers(void **state)
+{
+ struct dnsserver_zone *zone = NULL;
+ struct dnsserver_serverinfo *serverinfo = NULL;
+ struct dnsserver_zoneinfo *zoneinfo = NULL;
+ struct dnsp_DnsProperty *property = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ /*
+ * Setup the zone data
+ */
+ zone = talloc_zero(ctx, struct dnsserver_zone);
+ assert_non_null(zone);
+ zone->name = "test";
+
+ property = talloc_zero_array(ctx, struct dnsp_DnsProperty, 1);
+ assert_non_null(property);
+ property->id = DSPROPERTY_ZONE_SCAVENGING_SERVERS;
+ property->data.servers.addrCount = 4;
+ property->data.servers.addrArray = talloc_zero_array(ctx, uint32_t, 4);
+ property->data.servers.addrArray[0] = 1000;
+ property->data.servers.addrArray[1] = 1001;
+ property->data.servers.addrArray[2] = 1002;
+ property->data.servers.addrArray[3] = 1003;
+
+ zone->tmp_props = property;
+ zone->num_props = 1;
+
+
+ serverinfo = talloc_zero(ctx, struct dnsserver_serverinfo);
+ assert_non_null(serverinfo);
+
+ zoneinfo = dnsserver_init_zoneinfo(zone, serverinfo);
+
+ assert_non_null(zoneinfo);
+ assert_non_null(zoneinfo->aipScavengeServers);
+ assert_int_equal(zoneinfo->aipScavengeServers->AddrCount, 4);
+ assert_non_null(zoneinfo->aipScavengeServers->AddrArray);
+ assert_non_null(zoneinfo->aipScavengeServers->AddrArray);
+ assert_int_equal(zoneinfo->aipScavengeServers->AddrArray[0], 1000);
+ assert_int_equal(zoneinfo->aipScavengeServers->AddrArray[1], 1001);
+ assert_int_equal(zoneinfo->aipScavengeServers->AddrArray[2], 1002);
+ assert_int_equal(zoneinfo->aipScavengeServers->AddrArray[3], 1003);
+
+ /*
+ * Ensure that we're working with a copy of the property data
+ * and not a reference.
+ * The pointers should be different, and we should be able to change
+ * the values in the property without affecting the zoneinfo data
+ */
+ assert_true(zoneinfo->aipScavengeServers->AddrArray !=
+ property->data.servers.addrArray);
+ property->data.servers.addrArray[0] = 0;
+ property->data.servers.addrArray[1] = 1;
+ property->data.servers.addrArray[2] = 2;
+ property->data.servers.addrArray[3] = 3;
+ assert_int_equal(zoneinfo->aipScavengeServers->AddrArray[0], 1000);
+ assert_int_equal(zoneinfo->aipScavengeServers->AddrArray[1], 1001);
+ assert_int_equal(zoneinfo->aipScavengeServers->AddrArray[2], 1002);
+ assert_int_equal(zoneinfo->aipScavengeServers->AddrArray[3], 1003);
+
+
+ TALLOC_FREE(ctx);
+}
+int main(int argc, const char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(
+ test_dnsserver_init_zoneinfo_master_servers_empty),
+ cmocka_unit_test(
+ test_dnsserver_init_zoneinfo_master_servers),
+ cmocka_unit_test(
+ test_dnsserver_init_zoneinfo_scavenging_servers_empty),
+ cmocka_unit_test(
+ test_dnsserver_init_zoneinfo_scavenging_servers),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/source4/rpc_server/unixinfo/dcesrv_unixinfo.c b/source4/rpc_server/unixinfo/dcesrv_unixinfo.c
new file mode 100644
index 0000000..848c11f
--- /dev/null
+++ b/source4/rpc_server/unixinfo/dcesrv_unixinfo.c
@@ -0,0 +1,191 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the unixinfo pipe
+
+ Copyright (C) Volker Lendecke 2005
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_unixinfo.h"
+#include "libcli/wbclient/wbclient.h"
+#include "system/passwd.h"
+
+static NTSTATUS dcesrv_unixinfo_SidToUid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_SidToUid *r)
+{
+ NTSTATUS status;
+ struct id_map *ids;
+
+ DEBUG(5, ("dcesrv_unixinfo_SidToUid called\n"));
+
+ ids = talloc(mem_ctx, struct id_map);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids->sid = &r->in.sid;
+ ids->status = ID_UNKNOWN;
+ ZERO_STRUCT(ids->xid);
+ status = wbc_sids_to_xids(ids, 1);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (ids->xid.type == ID_TYPE_BOTH ||
+ ids->xid.type == ID_TYPE_UID) {
+ *r->out.uid = ids->xid.id;
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_INVALID_SID;
+ }
+}
+
+static NTSTATUS dcesrv_unixinfo_UidToSid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_UidToSid *r)
+{
+ struct id_map *ids;
+ uint32_t uid;
+ NTSTATUS status;
+
+ DEBUG(5, ("dcesrv_unixinfo_UidToSid called\n"));
+
+ uid = r->in.uid; /* This cuts uid to 32 bit */
+ if ((uint64_t)uid != r->in.uid) {
+ DEBUG(10, ("uid out of range\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ids = talloc(mem_ctx, struct id_map);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids->sid = NULL;
+ ids->status = ID_UNKNOWN;
+
+ ids->xid.id = uid;
+ ids->xid.type = ID_TYPE_UID;
+
+ status = wbc_xids_to_sids(ids, 1);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ r->out.sid = ids->sid;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_unixinfo_SidToGid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_SidToGid *r)
+{
+ NTSTATUS status;
+ struct id_map *ids;
+
+ DEBUG(5, ("dcesrv_unixinfo_SidToGid called\n"));
+
+ ids = talloc(mem_ctx, struct id_map);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids->sid = &r->in.sid;
+ ids->status = ID_UNKNOWN;
+ ZERO_STRUCT(ids->xid);
+ status = wbc_sids_to_xids(ids, 1);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (ids->xid.type == ID_TYPE_BOTH ||
+ ids->xid.type == ID_TYPE_GID) {
+ *r->out.gid = ids->xid.id;
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_INVALID_SID;
+ }
+}
+
+static NTSTATUS dcesrv_unixinfo_GidToSid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_GidToSid *r)
+{
+ struct id_map *ids;
+ uint32_t gid;
+ NTSTATUS status;
+
+ DEBUG(5, ("dcesrv_unixinfo_GidToSid called\n"));
+
+ gid = r->in.gid; /* This cuts gid to 32 bit */
+ if ((uint64_t)gid != r->in.gid) {
+ DEBUG(10, ("gid out of range\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ids = talloc(mem_ctx, struct id_map);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids->sid = NULL;
+ ids->status = ID_UNKNOWN;
+
+ ids->xid.id = gid;
+ ids->xid.type = ID_TYPE_GID;
+
+ status = wbc_xids_to_sids(ids, 1);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ r->out.sid = ids->sid;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_unixinfo_GetPWUid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_GetPWUid *r)
+{
+ unsigned int i;
+
+ *r->out.count = 0;
+
+ r->out.infos = talloc_zero_array(mem_ctx, struct unixinfo_GetPWUidInfo,
+ *r->in.count);
+ NT_STATUS_HAVE_NO_MEMORY(r->out.infos);
+ *r->out.count = *r->in.count;
+
+ for (i=0; i < *r->in.count; i++) {
+ uid_t uid;
+ struct passwd *pwd;
+
+ uid = r->in.uids[i];
+ pwd = getpwuid(uid);
+ if (pwd == NULL) {
+ DEBUG(10, ("uid %d not found\n", uid));
+ r->out.infos[i].homedir = "";
+ r->out.infos[i].shell = "";
+ r->out.infos[i].status = NT_STATUS_NO_SUCH_USER;
+ continue;
+ }
+
+ r->out.infos[i].homedir = talloc_strdup(mem_ctx, pwd->pw_dir);
+ r->out.infos[i].shell = talloc_strdup(mem_ctx, pwd->pw_shell);
+
+ if ((r->out.infos[i].homedir == NULL) ||
+ (r->out.infos[i].shell == NULL)) {
+ r->out.infos[i].homedir = "";
+ r->out.infos[i].shell = "";
+ r->out.infos[i].status = NT_STATUS_NO_MEMORY;
+ continue;
+ }
+
+ r->out.infos[i].status = NT_STATUS_OK;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_unixinfo_s.c"
diff --git a/source4/rpc_server/winreg/README b/source4/rpc_server/winreg/README
new file mode 100644
index 0000000..03a81e7
--- /dev/null
+++ b/source4/rpc_server/winreg/README
@@ -0,0 +1,3 @@
+This is the RPC server for the registry for Samba. It is basically a
+front-end for the registry library in lib/registry. See
+lib/registry/README for more details.
diff --git a/source4/rpc_server/winreg/rpc_winreg.c b/source4/rpc_server/winreg/rpc_winreg.c
new file mode 100644
index 0000000..3adaafe
--- /dev/null
+++ b/source4/rpc_server/winreg/rpc_winreg.c
@@ -0,0 +1,742 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the winreg pipe
+
+ Copyright (C) 2004 Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2008 Matthias Dieter Wallnöfer, mwallnoefer@yahoo.de
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "lib/registry/registry.h"
+#include "librpc/gen_ndr/ndr_winreg.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/session.h"
+
+enum handle_types { HTYPE_REGVAL, HTYPE_REGKEY };
+
+static WERROR dcesrv_winreg_openhive(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx, uint32_t hkey,
+ struct policy_handle **outh)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct registry_context *ctx = NULL;
+ struct dcesrv_handle *h;
+ WERROR result;
+
+ h = dcesrv_handle_create(dce_call, HTYPE_REGKEY);
+ W_ERROR_HAVE_NO_MEMORY(h);
+
+ result = reg_open_samba(h, &ctx,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ session_info,
+ NULL);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Error opening registry: %s\n", win_errstr(result)));
+ return result;
+ }
+
+ result = reg_get_predefined_key(ctx, hkey,
+ (struct registry_key **)&h->data);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+ *outh = &h->wire_handle;
+
+ return result;
+}
+
+#define func_winreg_OpenHive(k,n) static WERROR dcesrv_winreg_Open ## k (struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct winreg_Open ## k *r) \
+{ \
+ return dcesrv_winreg_openhive (dce_call, mem_ctx, n, &r->out.handle);\
+}
+
+func_winreg_OpenHive(HKCR,HKEY_CLASSES_ROOT)
+func_winreg_OpenHive(HKCU,HKEY_CURRENT_USER)
+func_winreg_OpenHive(HKLM,HKEY_LOCAL_MACHINE)
+func_winreg_OpenHive(HKPD,HKEY_PERFORMANCE_DATA)
+func_winreg_OpenHive(HKU,HKEY_USERS)
+func_winreg_OpenHive(HKCC,HKEY_CURRENT_CONFIG)
+func_winreg_OpenHive(HKDD,HKEY_DYN_DATA)
+func_winreg_OpenHive(HKPT,HKEY_PERFORMANCE_TEXT)
+func_winreg_OpenHive(HKPN,HKEY_PERFORMANCE_NLSTEXT)
+
+/*
+ winreg_CloseKey
+*/
+static WERROR dcesrv_winreg_CloseKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_CloseKey *r)
+{
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ talloc_unlink(dce_call->context, h);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return WERR_OK;
+}
+
+/*
+ winreg_CreateKey
+*/
+static WERROR dcesrv_winreg_CreateKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_CreateKey *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *h, *newh;
+ struct security_descriptor sd;
+ struct registry_key *key;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ newh = dcesrv_handle_create(dce_call, HTYPE_REGKEY);
+
+ switch (security_session_user_level(session_info, NULL))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ /* we support only non volatile keys */
+ if (r->in.options != REG_OPTION_NON_VOLATILE) {
+ return WERR_NOT_SUPPORTED;
+ }
+
+ /* the security descriptor is optional */
+ if (r->in.secdesc != NULL) {
+ DATA_BLOB sdblob;
+ enum ndr_err_code ndr_err;
+ sdblob.data = r->in.secdesc->sd.data;
+ sdblob.length = r->in.secdesc->sd.len;
+ if (sdblob.data == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+ ndr_err = ndr_pull_struct_blob_all(&sdblob, mem_ctx, &sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INVALID_PARAMETER;
+ }
+ }
+
+ result = reg_key_add_name(newh, key, r->in.name.name, NULL,
+ r->in.secdesc?&sd:NULL, (struct registry_key **)&newh->data);
+
+ r->out.action_taken = talloc(mem_ctx, enum winreg_CreateAction);
+ if (r->out.action_taken == NULL) {
+ talloc_free(newh);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ *r->out.action_taken = REG_ACTION_NONE;
+
+ if (W_ERROR_IS_OK(result)) {
+ r->out.new_handle = &newh->wire_handle;
+ *r->out.action_taken = REG_CREATED_NEW_KEY;
+ } else {
+ talloc_free(newh);
+ }
+
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_DeleteKey
+*/
+static WERROR dcesrv_winreg_DeleteKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_DeleteKey *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(session_info, NULL))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ result = reg_key_del(mem_ctx, key, r->in.key.name);
+ talloc_unlink(dce_call->context, h);
+
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_DeleteValue
+*/
+static WERROR dcesrv_winreg_DeleteValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_DeleteValue *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(session_info, NULL))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ return reg_del_value(mem_ctx, key, r->in.value.name);
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_EnumKey
+*/
+static WERROR dcesrv_winreg_EnumKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_EnumKey *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ const char *name, *classname;
+ NTTIME last_mod;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ result = reg_key_get_subkey_by_index(mem_ctx,
+ key, r->in.enum_index, &name, &classname, &last_mod);
+
+ if (2*strlen_m_term(name) > r->in.name->size) {
+ return WERR_MORE_DATA;
+ }
+
+ if (name != NULL) {
+ r->out.name->name = name;
+ r->out.name->length = 2*strlen_m_term(name);
+ } else {
+ r->out.name->name = r->in.name->name;
+ r->out.name->length = r->in.name->length;
+ }
+ r->out.name->size = r->in.name->size;
+
+ r->out.keyclass = r->in.keyclass;
+ if (classname != NULL) {
+ r->out.keyclass->name = classname;
+ r->out.keyclass->length = 2*strlen_m_term(classname);
+ } else {
+ r->out.keyclass->name = r->in.keyclass->name;
+ r->out.keyclass->length = r->in.keyclass->length;
+ }
+ r->out.keyclass->size = r->in.keyclass->size;
+
+ if (r->in.last_changed_time != NULL)
+ r->out.last_changed_time = &last_mod;
+
+ return result;
+}
+
+
+/*
+ winreg_EnumValue
+*/
+static WERROR dcesrv_winreg_EnumValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_EnumValue *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ const char *data_name;
+ uint32_t data_type;
+ DATA_BLOB data;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ result = reg_key_get_value_by_index(mem_ctx, key,
+ r->in.enum_index, &data_name, &data_type, &data);
+
+ if (!W_ERROR_IS_OK(result)) {
+ /* if the lookup wasn't successful, send client query back */
+ data_name = r->in.name->name;
+ data_type = *r->in.type;
+ data.data = r->in.value;
+ data.length = *r->in.length;
+ }
+
+ /* "data_name" is NULL when we query the default attribute */
+ if (data_name != NULL) {
+ r->out.name->name = data_name;
+ r->out.name->length = 2*strlen_m_term(data_name);
+ } else {
+ r->out.name->name = r->in.name->name;
+ r->out.name->length = r->in.name->length;
+ }
+ r->out.name->size = r->in.name->size;
+
+ r->out.type = talloc(mem_ctx, enum winreg_Type);
+ if (!r->out.type) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ *r->out.type = (enum winreg_Type) data_type;
+
+ /* check the client has enough room for the value */
+ if (r->in.value != NULL &&
+ r->in.size != NULL &&
+ data.length > *r->in.size) {
+ return WERR_MORE_DATA;
+ }
+
+ if (r->in.value != NULL) {
+ r->out.value = data.data;
+ }
+
+ if (r->in.size != NULL) {
+ r->out.size = talloc(mem_ctx, uint32_t);
+ *r->out.size = data.length;
+ r->out.length = r->out.size;
+ }
+
+ return result;
+}
+
+
+/*
+ winreg_FlushKey
+*/
+static WERROR dcesrv_winreg_FlushKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_FlushKey *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(session_info, NULL))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ return reg_key_flush(key);
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_GetKeySecurity
+*/
+static WERROR dcesrv_winreg_GetKeySecurity(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_GetKeySecurity *r)
+{
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_LoadKey
+*/
+static WERROR dcesrv_winreg_LoadKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_LoadKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_NotifyChangeKeyValue
+*/
+static WERROR dcesrv_winreg_NotifyChangeKeyValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_NotifyChangeKeyValue *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_OpenKey
+*/
+static WERROR dcesrv_winreg_OpenKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_OpenKey *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *h, *newh;
+ struct registry_key *key;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.parent_handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(session_info, NULL))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ case SECURITY_USER:
+ if (r->in.keyname.name && strcmp(r->in.keyname.name, "") == 0) {
+ newh = talloc_reference(dce_call->context, h);
+ result = WERR_OK;
+ } else {
+ newh = dcesrv_handle_create(dce_call, HTYPE_REGKEY);
+ result = reg_open_key(newh, key, r->in.keyname.name,
+ (struct registry_key **)&newh->data);
+ }
+
+ if (W_ERROR_IS_OK(result)) {
+ r->out.handle = &newh->wire_handle;
+ } else {
+ talloc_free(newh);
+ }
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_QueryInfoKey
+*/
+static WERROR dcesrv_winreg_QueryInfoKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_QueryInfoKey *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ const char *classname = NULL;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(session_info, NULL))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ case SECURITY_USER:
+ result = reg_key_get_info(mem_ctx, key, &classname,
+ r->out.num_subkeys, r->out.num_values,
+ r->out.last_changed_time, r->out.max_subkeylen,
+ r->out.max_valnamelen, r->out.max_valbufsize);
+
+ if (r->out.max_subkeylen != NULL) {
+ /* for UTF16 encoding */
+ *r->out.max_subkeylen *= 2;
+ }
+ if (r->out.max_valnamelen != NULL) {
+ /* for UTF16 encoding */
+ *r->out.max_valnamelen *= 2;
+ }
+
+ if (classname != NULL) {
+ r->out.classname->name = classname;
+ r->out.classname->name_len = 2*strlen_m_term(classname);
+ } else {
+ r->out.classname->name = r->in.classname->name;
+ r->out.classname->name_len = r->in.classname->name_len;
+ }
+ r->out.classname->name_size = r->in.classname->name_size;
+
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_QueryValue
+*/
+static WERROR dcesrv_winreg_QueryValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_QueryValue *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ uint32_t value_type;
+ DATA_BLOB value_data;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(session_info, NULL))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ case SECURITY_USER:
+ if ((r->in.type == NULL) || (r->in.data_length == NULL) ||
+ (r->in.data_size == NULL)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ result = reg_key_get_value_by_name(mem_ctx, key,
+ r->in.value_name->name, &value_type, &value_data);
+
+ if (!W_ERROR_IS_OK(result)) {
+ /* if the lookup wasn't successful, send client query back */
+ value_type = *r->in.type;
+ value_data.data = r->in.data;
+ value_data.length = *r->in.data_length;
+ } else {
+ if ((r->in.data != NULL)
+ && (*r->in.data_size < value_data.length)) {
+ result = WERR_MORE_DATA;
+ }
+ }
+
+ r->out.type = talloc(mem_ctx, enum winreg_Type);
+ if (!r->out.type) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ *r->out.type = (enum winreg_Type) value_type;
+ r->out.data_length = talloc(mem_ctx, uint32_t);
+ if (!r->out.data_length) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ *r->out.data_length = value_data.length;
+ r->out.data_size = talloc(mem_ctx, uint32_t);
+ if (!r->out.data_size) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ *r->out.data_size = value_data.length;
+ r->out.data = value_data.data;
+
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_ReplaceKey
+*/
+static WERROR dcesrv_winreg_ReplaceKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_ReplaceKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_RestoreKey
+*/
+static WERROR dcesrv_winreg_RestoreKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_RestoreKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_SaveKey
+*/
+static WERROR dcesrv_winreg_SaveKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_SaveKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_SetKeySecurity
+*/
+static WERROR dcesrv_winreg_SetKeySecurity(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_SetKeySecurity *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_SetValue
+*/
+static WERROR dcesrv_winreg_SetValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_SetValue *r)
+{
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ DATA_BLOB data;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(session_info, NULL))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ data.data = r->in.data;
+ data.length = r->in.size;
+ result = reg_val_set(key, r->in.name.name, r->in.type, data);
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_UnLoadKey
+*/
+static WERROR dcesrv_winreg_UnLoadKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_UnLoadKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_InitiateSystemShutdown
+*/
+static WERROR dcesrv_winreg_InitiateSystemShutdown(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_InitiateSystemShutdown *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_AbortSystemShutdown
+*/
+static WERROR dcesrv_winreg_AbortSystemShutdown(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_AbortSystemShutdown *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_GetVersion
+*/
+static WERROR dcesrv_winreg_GetVersion(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_GetVersion *r)
+{
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ r->out.version = talloc(mem_ctx, uint32_t);
+ W_ERROR_HAVE_NO_MEMORY(r->out.version);
+
+ *r->out.version = 5;
+
+ return WERR_OK;
+}
+
+
+/*
+ winreg_QueryMultipleValues
+*/
+static WERROR dcesrv_winreg_QueryMultipleValues(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_QueryMultipleValues *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_InitiateSystemShutdownEx
+*/
+static WERROR dcesrv_winreg_InitiateSystemShutdownEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_InitiateSystemShutdownEx *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_SaveKeyEx
+*/
+static WERROR dcesrv_winreg_SaveKeyEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_SaveKeyEx *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_QueryMultipleValues2
+*/
+static WERROR dcesrv_winreg_QueryMultipleValues2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_QueryMultipleValues2 *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+/*
+ winreg_DeleteKeyEx
+*/
+static WERROR dcesrv_winreg_DeleteKeyEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_DeleteKeyEx *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_winreg_s.c"
diff --git a/source4/rpc_server/wkssvc/dcesrv_wkssvc.c b/source4/rpc_server/wkssvc/dcesrv_wkssvc.c
new file mode 100644
index 0000000..2c0ba81
--- /dev/null
+++ b/source4/rpc_server/wkssvc/dcesrv_wkssvc.c
@@ -0,0 +1,403 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the wkssvc pipe
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ 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 "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_wkssvc.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "rpc_server/common/common.h"
+#include "param/param.h"
+
+/*
+ wkssvc_NetWkstaGetInfo
+*/
+static WERROR dcesrv_wkssvc_NetWkstaGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetWkstaGetInfo *r)
+{
+ struct dcesrv_context *dce_ctx = dce_call->conn->dce_ctx;
+ struct dcerpc_server_info *server_info = lpcfg_dcerpc_server_info(mem_ctx, dce_ctx->lp_ctx);
+
+ /* NOTE: win2k3 ignores r->in.server_name completly so we do --metze */
+
+ switch(r->in.level) {
+ case 100:
+ {
+ struct wkssvc_NetWkstaInfo100 *info100;
+
+ info100 = talloc(mem_ctx, struct wkssvc_NetWkstaInfo100);
+ W_ERROR_HAVE_NO_MEMORY(info100);
+
+ info100->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info100->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, NULL);
+ W_ERROR_HAVE_NO_MEMORY(info100->server_name);
+ info100->domain_name = server_info->domain_name;
+ info100->version_major = server_info->version_major;
+ info100->version_minor = server_info->version_minor;
+
+ r->out.info->info100 = info100;
+ return WERR_OK;
+ }
+ case 101:
+ {
+ struct wkssvc_NetWkstaInfo101 *info101;
+
+ info101 = talloc(mem_ctx, struct wkssvc_NetWkstaInfo101);
+ W_ERROR_HAVE_NO_MEMORY(info101);
+
+ info101->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info101->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, NULL);
+ W_ERROR_HAVE_NO_MEMORY(info101->server_name);
+ info101->domain_name = server_info->domain_name;
+ info101->version_major = server_info->version_major;
+ info101->version_minor = server_info->version_minor;
+ info101->lan_root = dcesrv_common_get_lan_root(mem_ctx, dce_ctx);
+
+ r->out.info->info101 = info101;
+ return WERR_OK;
+ }
+ case 102:
+ {
+ return WERR_ACCESS_DENIED;
+ }
+ case 502:
+ {
+ return WERR_ACCESS_DENIED;
+ }
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ wkssvc_NetWkstaSetInfo
+*/
+static WERROR dcesrv_wkssvc_NetWkstaSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetWkstaSetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetWkstaEnumUsers
+*/
+static WERROR dcesrv_wkssvc_NetWkstaEnumUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetWkstaEnumUsers *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrWkstaUserGetInfo
+*/
+static WERROR dcesrv_wkssvc_NetrWkstaUserGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWkstaUserGetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrWkstaUserSetInfo
+*/
+static WERROR dcesrv_wkssvc_NetrWkstaUserSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWkstaUserSetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetWkstaTransportEnum
+*/
+static WERROR dcesrv_wkssvc_NetWkstaTransportEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetWkstaTransportEnum *r)
+{
+ switch (r->in.info->level) {
+ case 0:
+ r->out.info->ctr.ctr0 = talloc(mem_ctx, struct wkssvc_NetWkstaTransportCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->ctr.ctr0);
+
+ r->out.info->ctr.ctr0->count = 0;
+ r->out.info->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+
+/*
+ wkssvc_NetrWkstaTransportAdd
+*/
+static WERROR dcesrv_wkssvc_NetrWkstaTransportAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWkstaTransportAdd *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrWkstaTransportDel
+*/
+static WERROR dcesrv_wkssvc_NetrWkstaTransportDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWkstaTransportDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUseAdd
+*/
+static WERROR dcesrv_wkssvc_NetrUseAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUseAdd *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUseGetInfo
+*/
+static WERROR dcesrv_wkssvc_NetrUseGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUseGetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUseDel
+*/
+static WERROR dcesrv_wkssvc_NetrUseDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUseDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUseEnum
+*/
+static WERROR dcesrv_wkssvc_NetrUseEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUseEnum *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrMessageBufferSend
+*/
+static WERROR dcesrv_wkssvc_NetrMessageBufferSend(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrMessageBufferSend *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrWorkstationStatisticsGet
+*/
+static WERROR dcesrv_wkssvc_NetrWorkstationStatisticsGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWorkstationStatisticsGet *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrLogonDomainNameAdd
+*/
+static WERROR dcesrv_wkssvc_NetrLogonDomainNameAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrLogonDomainNameAdd *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrLogonDomainNameDel
+*/
+static WERROR dcesrv_wkssvc_NetrLogonDomainNameDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrLogonDomainNameDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrJoinDomain
+*/
+static WERROR dcesrv_wkssvc_NetrJoinDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrJoinDomain *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUnjoinDomain
+*/
+static WERROR dcesrv_wkssvc_NetrUnjoinDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUnjoinDomain *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrRenameMachineInDomain
+*/
+static WERROR dcesrv_wkssvc_NetrRenameMachineInDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrRenameMachineInDomain *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrValidateName
+*/
+static WERROR dcesrv_wkssvc_NetrValidateName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrValidateName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrGetJoinInformation
+*/
+static WERROR dcesrv_wkssvc_NetrGetJoinInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrGetJoinInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrGetJoinableOus
+*/
+static WERROR dcesrv_wkssvc_NetrGetJoinableOus(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrGetJoinableOus *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ WKSSVC_NETRJOINDOMAIN2
+*/
+static WERROR dcesrv_wkssvc_NetrJoinDomain2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrJoinDomain2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ WKSSVC_NETRUNJOINDOMAIN2
+*/
+static WERROR dcesrv_wkssvc_NetrUnjoinDomain2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUnjoinDomain2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrRenameMachineInDomain2
+*/
+static WERROR dcesrv_wkssvc_NetrRenameMachineInDomain2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrRenameMachineInDomain2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrValidateName2
+*/
+static WERROR dcesrv_wkssvc_NetrValidateName2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrValidateName2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrGetJoinableOus2
+*/
+static WERROR dcesrv_wkssvc_NetrGetJoinableOus2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrGetJoinableOus2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrAddAlternateComputername
+*/
+static WERROR dcesrv_wkssvc_NetrAddAlternateComputerName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrAddAlternateComputerName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrRemoveAlternateComputername
+*/
+static WERROR dcesrv_wkssvc_NetrRemoveAlternateComputerName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrRemoveAlternateComputerName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrSetPrimaryComputername
+*/
+static WERROR dcesrv_wkssvc_NetrSetPrimaryComputername(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrSetPrimaryComputername *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrEnumerateComputerNames
+*/
+static WERROR dcesrv_wkssvc_NetrEnumerateComputerNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrEnumerateComputerNames *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_wkssvc_s.c"
diff --git a/source4/rpc_server/wscript_build b/source4/rpc_server/wscript_build
new file mode 100644
index 0000000..31ec4f6
--- /dev/null
+++ b/source4/rpc_server/wscript_build
@@ -0,0 +1,221 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('DCERPC_SHARE',
+ source='common/share_info.c',
+ autoproto='common/share.h',
+ deps='ldb share',
+ enabled=bld.CONFIG_SET('WITH_NTVFS_FILESERVER'),
+ )
+
+bld.SAMBA_SUBSYSTEM('DCERPC_COMMON',
+ source='''
+ common/server_info.c
+ common/forward.c
+ common/loadparm.c
+ ''',
+ autoproto='common/proto.h',
+ deps='ldb DCERPC_SHARE',
+ enabled=bld.AD_DC_BUILD_IS_ENABLED()
+ )
+
+bld.SAMBA_LIBRARY('dcerpc_server',
+ source='dcerpc_server.c',
+ pc_files='dcerpc_server.pc',
+ deps='LIBCLI_AUTH ndr samba_server_gensec service auth',
+ public_deps='dcerpc dcerpc-server-core',
+ autoproto='dcerpc_server_proto.h',
+ public_headers='dcerpc_server.h',
+ vnum='0.0.1',
+ enabled=bld.AD_DC_BUILD_IS_ENABLED()
+ )
+
+bld.SAMBA_MODULE('dcerpc_rpcecho',
+ source='echo/rpc_echo.c',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_rpcecho_init',
+ deps='ndr-standard events',
+ enabled=bld.CONFIG_GET('ENABLE_SELFTEST')
+ )
+
+
+bld.SAMBA_MODULE('dcerpc_epmapper',
+ source='epmapper/rpc_epmapper.c',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_epmapper_init',
+ deps='NDR_EPMAPPER'
+ )
+
+
+bld.SAMBA_MODULE('dcerpc_remote',
+ source='remote/dcesrv_remote.c',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_remote_init',
+ deps='LIBCLI_SMB ndr-table'
+ )
+
+
+bld.SAMBA_MODULE('dcerpc_srvsvc',
+ source='srvsvc/dcesrv_srvsvc.c srvsvc/srvsvc_ntvfs.c',
+ autoproto='srvsvc/proto.h',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_srvsvc_init',
+ deps='DCERPC_COMMON NDR_SRVSVC share ntvfs',
+ enabled=bld.CONFIG_SET('WITH_NTVFS_FILESERVER')
+ )
+
+
+bld.SAMBA_MODULE('dcerpc_wkssvc',
+ source='wkssvc/dcesrv_wkssvc.c',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_wkssvc_init',
+ deps='DCERPC_COMMON ndr-standard'
+ )
+
+
+bld.SAMBA_MODULE('dcerpc_unixinfo',
+ source='unixinfo/dcesrv_unixinfo.c',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_unixinfo_init',
+ deps='DCERPC_COMMON samdb NDR_UNIXINFO LIBWBCLIENT_OLD'
+ )
+
+
+bld.SAMBA_MODULE('dcesrv_samr',
+ source='samr/dcesrv_samr.c samr/samr_password.c',
+ autoproto='samr/proto.h',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_samr_init',
+ deps='''
+ samdb
+ DCERPC_COMMON
+ ndr-standard
+ auth4_sam
+ GNUTLS_HELPERS
+ DCERPC_HELPER
+ '''
+ )
+
+
+bld.SAMBA_MODULE('dcerpc_winreg',
+ source='winreg/rpc_winreg.c',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_winreg_init',
+ deps='registry ndr-standard',
+ internal_module=True,
+ enabled=bld.CONFIG_SET('WITH_NTVFS_FILESERVER')
+ )
+
+
+bld.SAMBA_MODULE('dcerpc_netlogon',
+ source='netlogon/dcerpc_netlogon.c',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_netlogon_init',
+ deps='''
+ DCERPC_COMMON
+ RPC_NDR_IRPC
+ COMMON_SCHANNEL
+ ndr-standard
+ auth4_sam
+ samba-hostconfig
+ DSDB_MODULE_HELPERS
+ util_str_escape
+ DCERPC_SERVER_NETLOGON
+ '''
+ )
+
+bld.SAMBA_MODULE('dcerpc_lsarpc',
+ source='lsa/dcesrv_lsa.c lsa/lsa_init.c lsa/lsa_lookup.c',
+ autoproto='lsa/proto.h',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_lsa_init',
+ deps='''
+ samdb
+ DCERPC_COMMON
+ ndr-standard
+ LIBCLI_AUTH
+ NDR_DSSETUP
+ com_err
+ samba-security
+ UTIL_LSARPC
+ '''
+ )
+
+
+bld.SAMBA_MODULE('dcerpc_backupkey',
+ source='backupkey/dcesrv_backupkey.c ',
+ autoproto='backupkey/proto.h',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_backupkey_init',
+ deps='''
+ samdb
+ DCERPC_COMMON
+ NDR_BACKUPKEY
+ RPC_NDR_BACKUPKEY
+ gnutls
+ GNUTLS_HELPERS
+ ''',
+ )
+
+
+bld.SAMBA_MODULE('dcerpc_drsuapi',
+ source='''
+ drsuapi/dcesrv_drsuapi.c
+ drsuapi/updaterefs.c
+ drsuapi/getncchanges.c
+ drsuapi/addentry.c
+ drsuapi/writespn.c
+ drsuapi/drsutil.c
+ ''',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_drsuapi_init',
+ deps='samdb DCERPC_COMMON NDR_DRSUAPI samba-security'
+ )
+
+
+bld.SAMBA_MODULE('dcerpc_browser',
+ source='browser/dcesrv_browser.c',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_browser_init',
+ deps='DCERPC_COMMON NDR_BROWSER'
+ )
+
+bld.SAMBA_MODULE('dcerpc_eventlog',
+ source='eventlog/dcesrv_eventlog6.c',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_eventlog6_init',
+ deps='DCERPC_COMMON'
+ )
+
+bld.SAMBA_MODULE('dcerpc_dnsserver',
+ source='''
+ dnsserver/dcerpc_dnsserver.c
+ dnsserver/dnsutils.c
+ dnsserver/dnsdata.c
+ dnsserver/dnsdb.c
+ ''',
+ subsystem='dcerpc_server',
+ init_function='dcerpc_server_dnsserver_init',
+ deps='DCERPC_COMMON dnsserver_common netif'
+ )
+
+
+bld.SAMBA_MODULE('service_dcerpc',
+ source='service_rpc.c',
+ autoproto='service_rpc.h',
+ subsystem='service',
+ init_function='server_service_rpc_init',
+ internal_module=False,
+ deps='dcerpc_server'
+ )
+
+bld.SAMBA_BINARY('test_rpc_dns_server_dnsutils',
+ source='tests/rpc_dns_server_dnsutils_test.c',
+ deps='''
+ dnsserver_common
+ dcerpc_server
+ cmocka
+ talloc
+ ''',
+ for_selftest=True,
+ enabled=bld.AD_DC_BUILD_IS_ENABLED()
+ )