summaryrefslogtreecommitdiff
path: root/source4
diff options
context:
space:
mode:
Diffstat (limited to 'source4')
-rw-r--r--source4/Makefile2
-rw-r--r--source4/auth/auth.h23
-rw-r--r--source4/auth/credentials/config.mk2
-rw-r--r--source4/auth/gensec/config.mk2
-rw-r--r--source4/auth/gensec/gensec.c117
-rw-r--r--source4/auth/gensec/gensec.h22
-rw-r--r--source4/auth/ntlm/auth.c271
-rw-r--r--source4/auth/ntlm/auth_proto.h50
-rw-r--r--source4/auth/ntlm/auth_server.c7
-rw-r--r--source4/auth/ntlm/auth_util.c12
-rw-r--r--source4/auth/ntlm/auth_winbind.c6
-rw-r--r--source4/auth/ntlm/config.mk4
-rw-r--r--source4/auth/ntlmssp/ntlmssp.h34
-rw-r--r--source4/auth/ntlmssp/ntlmssp_server.c103
-rw-r--r--source4/auth/ntlmssp/ntlmssp_sign.c31
-rw-r--r--source4/build/smb_build/summary.pm1
-rw-r--r--source4/dsdb/common/dsdb_dn.c69
-rw-r--r--source4/dsdb/common/dsdb_dn.h5
-rw-r--r--source4/dsdb/common/util.c293
-rw-r--r--source4/dsdb/config.mk5
-rw-r--r--source4/dsdb/kcc/kcc_connection.c6
-rw-r--r--source4/dsdb/kcc/kcc_deleted.c156
-rw-r--r--source4/dsdb/kcc/kcc_periodic.c5
-rw-r--r--source4/dsdb/kcc/kcc_service.h2
-rw-r--r--source4/dsdb/repl/drepl_notify.c2
-rw-r--r--source4/dsdb/repl/drepl_partitions.c7
-rw-r--r--source4/dsdb/repl/replicated_objects.c20
-rw-r--r--source4/dsdb/samdb/ldb_modules/config.mk17
-rw-r--r--source4/dsdb/samdb/ldb_modules/descriptor.c8
-rw-r--r--source4/dsdb/samdb/ldb_modules/extended_dn_in.c5
-rw-r--r--source4/dsdb/samdb/ldb_modules/linked_attributes.c1270
-rw-r--r--source4/dsdb/samdb/ldb_modules/repl_meta_data.c1807
-rw-r--r--source4/dsdb/samdb/ldb_modules/samba_dsdb.c98
-rw-r--r--source4/dsdb/samdb/ldb_modules/schema_data.c175
-rw-r--r--source4/dsdb/samdb/ldb_modules/simple_ldap_map.c6
-rw-r--r--source4/dsdb/samdb/ldb_modules/util.c109
-rw-r--r--source4/dsdb/samdb/ldb_modules/util.h4
-rw-r--r--source4/dsdb/schema/prefixmap.h11
-rw-r--r--source4/dsdb/schema/schema_init.c23
-rw-r--r--source4/dsdb/schema/schema_prefixmap.c24
-rw-r--r--source4/dsdb/schema/schema_query.c3
-rw-r--r--source4/dsdb/schema/schema_set.c12
-rw-r--r--source4/dsdb/schema/schema_syntax.c38
-rw-r--r--source4/heimdal_build/external.m48
-rw-r--r--source4/kdc/config.mk3
-rw-r--r--source4/kdc/hdb-samba4.c239
-rw-r--r--source4/kdc/hdb-samba4.h8
-rw-r--r--source4/kdc/kdc.c458
-rw-r--r--source4/kdc/kdc.h12
-rw-r--r--source4/kdc/kpasswdd.c216
-rw-r--r--source4/kdc/pac-glue.c30
-rw-r--r--source4/kdc/pac-glue.h6
-rw-r--r--source4/lib/ldb-samba/ldif_handlers.c55
-rw-r--r--source4/lib/ldb/common/attrib_handlers.c61
-rw-r--r--source4/lib/ldb/common/ldb_modules.c10
-rw-r--r--source4/lib/ldb/common/ldb_msg.c27
-rw-r--r--source4/lib/ldb/config.mk13
-rw-r--r--source4/lib/ldb/external/libtalloc.m42
-rw-r--r--source4/lib/ldb/include/ldb.h6
-rw-r--r--source4/lib/ldb/include/ldb_module.h1
-rw-r--r--source4/lib/ldb/ldb.mk28
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_cache.c2
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_tdb.c17
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_tdb.h2
-rw-r--r--source4/lib/ldb/pyldb.c4
-rw-r--r--source4/lib/ldb/pyldb.h1
-rwxr-xr-xsource4/lib/ldb/tests/python/ldap.py160
-rwxr-xr-xsource4/lib/ldb/tests/python/ldap_schema.py500
-rw-r--r--source4/lib/ldb/tests/sample_module.c30
-rwxr-xr-xsource4/lib/ldb/tests/test-controls.sh46
-rwxr-xr-xsource4/lib/ldb/tests/test-tdb.sh2
-rw-r--r--source4/lib/ldb/tools/config.mk13
-rw-r--r--source4/lib/ldb/tools/ldbadd.c14
-rw-r--r--source4/lib/ldb/tools/ldbdel.c25
-rw-r--r--source4/lib/ldb/tools/ldbmodify.c17
-rw-r--r--source4/lib/ldb/tools/ldbutil.c149
-rw-r--r--source4/lib/ldb/tools/ldbutil.h41
-rw-r--r--source4/lib/ldb_wrap.c2
-rw-r--r--source4/lib/registry/util.c2
-rw-r--r--source4/lib/socket/config.mk2
-rw-r--r--source4/lib/socket/socket.c72
-rw-r--r--source4/lib/socket/socket.h7
-rw-r--r--source4/libcli/finddcs.c3
-rw-r--r--source4/libcli/security/sddl.c2
-rw-r--r--source4/libcli/security/tests/bindings.py11
-rw-r--r--source4/libnet/libnet.c3
-rw-r--r--source4/libnet/libnet_passwd.c8
-rw-r--r--source4/libnet/py_net.c78
-rw-r--r--source4/librpc/ndr/py_security.c36
-rw-r--r--source4/min_versions.m42
-rw-r--r--source4/param/config.mk2
-rw-r--r--source4/param/provision.c31
-rw-r--r--source4/param/pyparam.c3
-rw-r--r--source4/param/tests/bindings.py10
-rw-r--r--source4/rpc_server/drsuapi/addentry.c3
-rw-r--r--source4/rpc_server/drsuapi/dcesrv_drsuapi.c5
-rw-r--r--source4/rpc_server/drsuapi/dcesrv_drsuapi.h1
-rw-r--r--source4/rpc_server/drsuapi/drsutil.c22
-rw-r--r--source4/rpc_server/drsuapi/getncchanges.c421
-rw-r--r--source4/rpc_server/drsuapi/updaterefs.c4
-rwxr-xr-xsource4/script/installmisc.sh42
-rw-r--r--source4/scripting/python/modules.c43
-rw-r--r--source4/scripting/python/modules.h2
-rw-r--r--source4/scripting/python/pyglue.c23
-rw-r--r--source4/scripting/python/samba/__init__.py62
-rw-r--r--source4/scripting/python/samba/getopt.py14
-rw-r--r--source4/scripting/python/samba/netcmd/__init__.py145
-rw-r--r--source4/scripting/python/samba/netcmd/domainlevel.py229
-rwxr-xr-xsource4/scripting/python/samba/netcmd/enableaccount.py65
-rwxr-xr-xsource4/scripting/python/samba/netcmd/newuser.py70
-rw-r--r--source4/scripting/python/samba/netcmd/pwsettings.py190
-rw-r--r--source4/scripting/python/samba/netcmd/setexpiry.py72
-rw-r--r--source4/scripting/python/samba/netcmd/setpassword.py77
-rw-r--r--source4/scripting/python/samba/provision.py2
-rw-r--r--source4/scripting/python/samba/tests/netcmd.py34
-rwxr-xr-xsource4/selftest/tests.sh2
-rwxr-xr-xsource4/setup/domainlevel250
-rwxr-xr-xsource4/setup/enableaccount65
-rwxr-xr-xsource4/setup/newuser69
-rwxr-xr-xsource4/setup/pwsettings198
-rwxr-xr-xsource4/setup/setexpiry72
-rwxr-xr-xsource4/setup/setpassword74
-rwxr-xr-xsource4/setup/tests/blackbox_newuser.sh11
-rwxr-xr-xsource4/setup/tests/blackbox_setpassword.sh10
-rw-r--r--source4/smb_server/smb/negprot.c5
-rw-r--r--source4/smb_server/smb/sesssetup.c74
-rw-r--r--source4/smb_server/smb2/sesssetup.c17
-rw-r--r--source4/smbd/config.mk2
-rw-r--r--source4/smbd/server.c2
-rw-r--r--source4/smbd/service_stream.c32
-rw-r--r--source4/smbd/service_stream.h3
-rw-r--r--source4/torture/drs/unit/prefixmap_tests.c45
-rw-r--r--source4/torture/libnet/python/samr-test.py62
-rw-r--r--source4/torture/ndr/ndr.c13
-rw-r--r--source4/torture/raw/oplock.c265
-rw-r--r--source4/torture/raw/qfileinfo.c2
-rw-r--r--source4/torture/raw/setfileinfo.c153
-rw-r--r--source4/torture/raw/streams.c3
-rw-r--r--source4/utils/net/net.c178
139 files changed, 6930 insertions, 3550 deletions
diff --git a/source4/Makefile b/source4/Makefile
index 6a99ea3160..0da1ee2626 100644
--- a/source4/Makefile
+++ b/source4/Makefile
@@ -243,7 +243,7 @@ installman:: manpages installdirs
@$(SHELL) $(srcdir)/script/installman.sh $(DESTDIR)$(mandir) $(MANPAGES)
installmisc:: installdirs
- @$(SHELL) $(srcdir)/script/installmisc.sh $(srcdir) $(DESTDIR)$(setupdir) $(DESTDIR)$(bindir) $(DESTDIR)$(sbindir) $(pythondir)
+ @$(SHELL) $(srcdir)/script/installmisc.sh "$(DESTDIR)" $(srcdir) $(DESTDIR)$(setupdir) $(DESTDIR)$(bindir) $(DESTDIR)$(sbindir) $(pythondir) $(PYTHON)
installpc:: installdirs
@$(SHELL) $(srcdir)/script/installpc.sh $(builddir) $(DESTDIR)$(pkgconfigdir) $(PC_FILES)
diff --git a/source4/auth/auth.h b/source4/auth/auth.h
index c31ed2f5fd..fa2329df32 100644
--- a/source4/auth/auth.h
+++ b/source4/auth/auth.h
@@ -135,7 +135,7 @@ struct auth_operations {
* security=server, and makes a number of compromises to allow
* that. It is not compatible with being a PDC. */
- NTSTATUS (*get_challenge)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge);
+ NTSTATUS (*get_challenge)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, uint8_t chal[8]);
/* Given the user supplied info, check if this backend want to handle the password checking */
@@ -190,7 +190,7 @@ struct auth_context {
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **server_info);
- NTSTATUS (*get_challenge)(struct auth_context *auth_ctx, const uint8_t **_chal);
+ NTSTATUS (*get_challenge)(struct auth_context *auth_ctx, uint8_t chal[8]);
bool (*challenge_may_be_modified)(struct auth_context *auth_ctx);
@@ -226,7 +226,7 @@ struct ldb_context;
struct ldb_dn;
struct gensec_security;
-NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, const uint8_t **_chal);
+NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, uint8_t chal[8]);
NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
struct ldb_context *sam_ctx,
uint32_t logon_parameters,
@@ -275,16 +275,23 @@ NTSTATUS authenticate_username_pw(TALLOC_CTX *mem_ctx,
const char *nt4_username,
const char *password,
struct auth_session_info **session_info);
-NTSTATUS auth_check_password_recv(struct auth_check_password_request *req,
+
+struct tevent_req *auth_check_password_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct auth_context *auth_ctx,
+ const struct auth_usersupplied_info *user_info);
+NTSTATUS auth_check_password_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct auth_serversupplied_info **server_info);
-void auth_check_password_send(struct auth_context *auth_ctx,
- const struct auth_usersupplied_info *user_info,
- void (*callback)(struct auth_check_password_request *req, void *private_data),
- void *private_data);
+bool auth_challenge_may_be_modified(struct auth_context *auth_ctx);
NTSTATUS auth_context_set_challenge(struct auth_context *auth_ctx, const uint8_t chal[8], const char *set_by);
+NTSTATUS auth_get_server_info_principal(TALLOC_CTX *mem_ctx,
+ struct auth_context *auth_ctx,
+ const char *principal,
+ struct auth_serversupplied_info **server_info);
+
NTSTATUS samba_server_gensec_start(TALLOC_CTX *mem_ctx,
struct tevent_context *event_ctx,
struct messaging_context *msg_ctx,
diff --git a/source4/auth/credentials/config.mk b/source4/auth/credentials/config.mk
index 6b051ef46c..32f415a8bf 100644
--- a/source4/auth/credentials/config.mk
+++ b/source4/auth/credentials/config.mk
@@ -4,7 +4,7 @@
PUBLIC_DEPENDENCIES = \
LIBCLI_AUTH SECRETS LIBCRYPTO KERBEROS UTIL_LDB HEIMDAL_GSSAPI
PRIVATE_DEPENDENCIES = \
- SECRETS
+ SECRETS SAMDB
CREDENTIALS_OBJ_FILES = $(addprefix $(authsrcdir)/credentials/, credentials.o credentials_files.o credentials_ntlm.o credentials_krb5.o ../kerberos/kerberos_util.o)
diff --git a/source4/auth/gensec/config.mk b/source4/auth/gensec/config.mk
index f7cbd5b197..947a91e852 100644
--- a/source4/auth/gensec/config.mk
+++ b/source4/auth/gensec/config.mk
@@ -2,7 +2,7 @@
# Start SUBSYSTEM gensec
[LIBRARY::gensec]
PUBLIC_DEPENDENCIES = \
- CREDENTIALS LIBSAMBA-UTIL LIBCRYPTO ASN1_UTIL samba_socket LIBPACKET LIBTSOCKET
+ CREDENTIALS LIBSAMBA-UTIL LIBCRYPTO ASN1_UTIL samba_socket LIBPACKET LIBTSOCKET UTIL_TEVENT
# End SUBSYSTEM gensec
#################################
diff --git a/source4/auth/gensec/gensec.c b/source4/auth/gensec/gensec.c
index ecd0778ee6..3de0e1c935 100644
--- a/source4/auth/gensec/gensec.c
+++ b/source4/auth/gensec/gensec.c
@@ -25,6 +25,7 @@
#include "lib/events/events.h"
#include "lib/socket/socket.h"
#include "lib/tsocket/tsocket.h"
+#include "../lib/util/tevent_ntstatus.h"
#include "librpc/rpc/dcerpc.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
@@ -982,71 +983,105 @@ _PUBLIC_ NTSTATUS gensec_update(struct gensec_security *gensec_security, TALLOC_
return gensec_security->ops->update(gensec_security, out_mem_ctx, in, out);
}
-static void gensec_update_async_timed_handler(struct tevent_context *ev, struct tevent_timer *te,
- struct timeval t, void *ptr)
-{
- struct gensec_update_request *req = talloc_get_type(ptr, struct gensec_update_request);
- req->status = req->gensec_security->ops->update(req->gensec_security, req, req->in, &req->out);
- req->callback.fn(req, req->callback.private_data);
-}
+struct gensec_update_state {
+ struct tevent_immediate *im;
+ struct gensec_security *gensec_security;
+ DATA_BLOB in;
+ DATA_BLOB out;
+};
+static void gensec_update_async_trigger(struct tevent_context *ctx,
+ struct tevent_immediate *im,
+ void *private_data);
/**
* Next state function for the GENSEC state machine async version
- *
+ *
+ * @param mem_ctx The memory context for the request
+ * @param ev The event context for the request
* @param gensec_security GENSEC State
* @param in The request, as a DATA_BLOB
- * @param callback The function that will be called when the operation is
- * finished, it should return gensec_update_recv() to get output
- * @param private_data A private pointer that will be passed to the callback function
+ *
+ * @return The request handle or NULL on no memory failure
*/
-_PUBLIC_ void gensec_update_send(struct gensec_security *gensec_security, const DATA_BLOB in,
- void (*callback)(struct gensec_update_request *req, void *private_data),
- void *private_data)
+_PUBLIC_ struct tevent_req *gensec_update_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct gensec_security *gensec_security,
+ const DATA_BLOB in)
+{
+ struct tevent_req *req;
+ struct gensec_update_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct gensec_update_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->gensec_security = gensec_security;
+ state->in = in;
+ state->out = data_blob(NULL, 0);
+ state->im = tevent_create_immediate(state);
+ if (tevent_req_nomem(state->im, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_schedule_immediate(state->im, ev,
+ gensec_update_async_trigger,
+ req);
+
+ return req;
+}
+
+static void gensec_update_async_trigger(struct tevent_context *ctx,
+ struct tevent_immediate *im,
+ void *private_data)
{
- struct gensec_update_request *req = NULL;
- struct tevent_timer *te = NULL;
-
- req = talloc(gensec_security, struct gensec_update_request);
- if (!req) goto failed;
- req->gensec_security = gensec_security;
- req->in = in;
- req->out = data_blob(NULL, 0);
- req->callback.fn = callback;
- req->callback.private_data = private_data;
-
- te = event_add_timed(gensec_security->event_ctx, req,
- timeval_zero(),
- gensec_update_async_timed_handler, req);
- if (!te) goto failed;
-
- return;
-
-failed:
- talloc_free(req);
- callback(NULL, private_data);
+ struct tevent_req *req =
+ talloc_get_type_abort(private_data, struct tevent_req);
+ struct gensec_update_state *state =
+ tevent_req_data(req, struct gensec_update_state);
+ NTSTATUS status;
+
+ status = gensec_update(state->gensec_security, state,
+ state->in, &state->out);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
}
/**
* Next state function for the GENSEC state machine
*
- * @param req GENSEC update request state
+ * @param req request state
* @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
* @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
* @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
* or NT_STATUS_OK if the user is authenticated.
*/
-_PUBLIC_ NTSTATUS gensec_update_recv(struct gensec_update_request *req, TALLOC_CTX *out_mem_ctx, DATA_BLOB *out)
+_PUBLIC_ NTSTATUS gensec_update_recv(struct tevent_req *req,
+ TALLOC_CTX *out_mem_ctx,
+ DATA_BLOB *out)
{
+ struct gensec_update_state *state =
+ tevent_req_data(req, struct gensec_update_state);
NTSTATUS status;
- NT_STATUS_HAVE_NO_MEMORY(req);
+ if (tevent_req_is_nterror(req, &status)) {
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ tevent_req_received(req);
+ return status;
+ }
+ } else {
+ status = NT_STATUS_OK;
+ }
- *out = req->out;
+ *out = state->out;
talloc_steal(out_mem_ctx, out->data);
- status = req->status;
- talloc_free(req);
+ tevent_req_received(req);
return status;
}
diff --git a/source4/auth/gensec/gensec.h b/source4/auth/gensec/gensec.h
index 2ea2402064..232f1a4500 100644
--- a/source4/auth/gensec/gensec.h
+++ b/source4/auth/gensec/gensec.h
@@ -69,18 +69,7 @@ struct auth_session_info;
struct cli_credentials;
struct gensec_settings;
struct tevent_context;
-
-struct gensec_update_request {
- struct gensec_security *gensec_security;
- void *private_data;
- DATA_BLOB in;
- DATA_BLOB out;
- NTSTATUS status;
- struct {
- void (*fn)(struct gensec_update_request *req, void *private_data);
- void *private_data;
- } callback;
-};
+struct tevent_req;
struct gensec_settings {
struct loadparm_context *lp_ctx;
@@ -231,10 +220,11 @@ NTSTATUS gensec_start_mech_by_sasl_list(struct gensec_security *gensec_security,
const char **sasl_names);
NTSTATUS gensec_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
const DATA_BLOB in, DATA_BLOB *out);
-void gensec_update_send(struct gensec_security *gensec_security, const DATA_BLOB in,
- void (*callback)(struct gensec_update_request *req, void *private_data),
- void *private_data);
-NTSTATUS gensec_update_recv(struct gensec_update_request *req, TALLOC_CTX *out_mem_ctx, DATA_BLOB *out);
+struct tevent_req *gensec_update_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct gensec_security *gensec_security,
+ const DATA_BLOB in);
+NTSTATUS gensec_update_recv(struct tevent_req *req, TALLOC_CTX *out_mem_ctx, DATA_BLOB *out);
void gensec_want_feature(struct gensec_security *gensec_security,
uint32_t feature);
bool gensec_have_feature(struct gensec_security *gensec_security,
diff --git a/source4/auth/ntlm/auth.c b/source4/auth/ntlm/auth.c
index 5520c9d01f..d5df387d80 100644
--- a/source4/auth/ntlm/auth.c
+++ b/source4/auth/ntlm/auth.c
@@ -19,10 +19,11 @@
*/
#include "includes.h"
+#include <tevent.h>
+#include "../lib/util/tevent_ntstatus.h"
#include "../lib/util/dlinklist.h"
#include "auth/auth.h"
#include "auth/ntlm/auth_proto.h"
-#include "lib/events/events.h"
#include "param/param.h"
/***************************************************************************
@@ -42,7 +43,7 @@ _PUBLIC_ NTSTATUS auth_context_set_challenge(struct auth_context *auth_ctx, cons
/***************************************************************************
Set a fixed challenge
***************************************************************************/
-bool auth_challenge_may_be_modified(struct auth_context *auth_ctx)
+_PUBLIC_ bool auth_challenge_may_be_modified(struct auth_context *auth_ctx)
{
return auth_ctx->challenge.may_be_modified;
}
@@ -51,42 +52,34 @@ bool auth_challenge_may_be_modified(struct auth_context *auth_ctx)
Try to get a challenge out of the various authentication modules.
Returns a const char of length 8 bytes.
****************************************************************************/
-_PUBLIC_ NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, const uint8_t **_chal)
+_PUBLIC_ NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, uint8_t chal[8])
{
NTSTATUS nt_status;
struct auth_method_context *method;
- if (auth_ctx->challenge.data.length) {
+ if (auth_ctx->challenge.data.length == 8) {
DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
auth_ctx->challenge.set_by));
- *_chal = auth_ctx->challenge.data.data;
+ memcpy(chal, auth_ctx->challenge.data.data, 8);
return NT_STATUS_OK;
}
for (method = auth_ctx->methods; method; method = method->next) {
- DATA_BLOB challenge = data_blob(NULL,0);
-
- nt_status = method->ops->get_challenge(method, auth_ctx, &challenge);
+ nt_status = method->ops->get_challenge(method, auth_ctx, chal);
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
continue;
}
NT_STATUS_NOT_OK_RETURN(nt_status);
- if (challenge.length != 8) {
- DEBUG(0, ("auth_get_challenge: invalid challenge (length %u) by mothod [%s]\n",
- (unsigned)challenge.length, method->ops->name));
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- auth_ctx->challenge.data = challenge;
+ auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
+ NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
auth_ctx->challenge.set_by = method->ops->name;
break;
}
if (!auth_ctx->challenge.set_by) {
- uint8_t chal[8];
generate_random_buffer(chal, 8);
auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
@@ -99,7 +92,6 @@ _PUBLIC_ NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, const uint8_
DEBUG(10,("auth_get_challenge: challenge set by %s\n",
auth_ctx->challenge.set_by));
- *_chal = auth_ctx->challenge.data.data;
return NT_STATUS_OK;
}
@@ -133,22 +125,6 @@ _PUBLIC_ NTSTATUS auth_get_server_info_principal(TALLOC_CTX *mem_ctx,
return NT_STATUS_OK;
}
-struct auth_check_password_sync_state {
- bool finished;
- NTSTATUS status;
- struct auth_serversupplied_info *server_info;
-};
-
-static void auth_check_password_sync_callback(struct auth_check_password_request *req,
- void *private_data)
-{
- struct auth_check_password_sync_state *s = talloc_get_type(private_data,
- struct auth_check_password_sync_state);
-
- s->finished = true;
- s->status = auth_check_password_recv(req, s, &s->server_info);
-}
-
/**
* Check a user's Plaintext, LM or NTLM password.
* (sync version)
@@ -181,48 +157,43 @@ _PUBLIC_ NTSTATUS auth_check_password(struct auth_context *auth_ctx,
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **server_info)
{
- struct auth_check_password_sync_state *sync_state;
+ struct tevent_req *subreq;
+ struct tevent_context *ev;
+ bool ok;
NTSTATUS status;
- sync_state = talloc_zero(auth_ctx, struct auth_check_password_sync_state);
- NT_STATUS_HAVE_NO_MEMORY(sync_state);
-
- auth_check_password_send(auth_ctx, user_info, auth_check_password_sync_callback, sync_state);
+ /*TODO: create a new event context here! */
+ ev = auth_ctx->event_ctx;
- while (!sync_state->finished) {
- event_loop_once(auth_ctx->event_ctx);
+ subreq = auth_check_password_send(mem_ctx,
+ ev,
+ auth_ctx,
+ user_info);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
}
- status = sync_state->status;
-
- if (NT_STATUS_IS_OK(status)) {
- *server_info = talloc_steal(mem_ctx, sync_state->server_info);
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ return NT_STATUS_INTERNAL_ERROR;
}
- talloc_free(sync_state);
+ status = auth_check_password_recv(subreq, mem_ctx, server_info);
+ TALLOC_FREE(subreq);
+
return status;
}
-struct auth_check_password_request {
+struct auth_check_password_state {
struct auth_context *auth_ctx;
const struct auth_usersupplied_info *user_info;
struct auth_serversupplied_info *server_info;
struct auth_method_context *method;
- NTSTATUS status;
- struct {
- void (*fn)(struct auth_check_password_request *req, void *private_data);
- void *private_data;
- } callback;
};
-static void auth_check_password_async_timed_handler(struct tevent_context *ev, struct tevent_timer *te,
- struct timeval t, void *ptr)
-{
- struct auth_check_password_request *req = talloc_get_type(ptr, struct auth_check_password_request);
- req->status = req->method->ops->check_password(req->method, req, req->user_info, &req->server_info);
- req->callback.fn(req, req->callback.private_data);
-}
-
+static void auth_check_password_async_trigger(struct tevent_context *ev,
+ struct tevent_immediate *im,
+ void *private_data);
/**
* Check a user's Plaintext, LM or NTLM password.
* async send hook
@@ -234,6 +205,10 @@ static void auth_check_password_async_timed_handler(struct tevent_context *ev, s
* struct. When the return is other than NT_STATUS_OK the contents
* of that structure is undefined.
*
+ * @param mem_ctx The memory context the request should operate on
+ *
+ * @param ev The tevent context the request should operate on
+ *
* @param auth_ctx Supplies the challenges and some other data.
* Must be created with make_auth_context(), and the challenges should be
* filled in, either at creation or by calling the challenge geneation
@@ -241,93 +216,131 @@ static void auth_check_password_async_timed_handler(struct tevent_context *ev, s
*
* @param user_info Contains the user supplied components, including the passwords.
*
- * @param callback A callback function which will be called when the operation is finished.
- * The callback function needs to call auth_check_password_recv() to get the return values
- *
- * @param private_data A private pointer which will ba passed to the callback function
+ * @return The request handle or NULL on no memory error.
*
**/
-_PUBLIC_ void auth_check_password_send(struct auth_context *auth_ctx,
- const struct auth_usersupplied_info *user_info,
- void (*callback)(struct auth_check_password_request *req, void *private_data),
- void *private_data)
+_PUBLIC_ struct tevent_req *auth_check_password_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct auth_context *auth_ctx,
+ const struct auth_usersupplied_info *user_info)
{
+ struct tevent_req *req;
+ struct auth_check_password_state *state;
/* if all the modules say 'not for me' this is reasonable */
NTSTATUS nt_status;
struct auth_method_context *method;
- const uint8_t *challenge;
+ uint8_t chal[8];
struct auth_usersupplied_info *user_info_tmp;
- struct auth_check_password_request *req = NULL;
+ struct tevent_immediate *im;
- DEBUG(3, ("auth_check_password_send: Checking password for unmapped user [%s]\\[%s]@[%s]\n",
- user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
+ DEBUG(3,("auth_check_password_send: "
+ "Checking password for unmapped user [%s]\\[%s]@[%s]\n",
+ user_info->client.domain_name, user_info->client.account_name,
+ user_info->workstation_name));
- req = talloc_zero(auth_ctx, struct auth_check_password_request);
- if (!req) {
- callback(NULL, private_data);
- return;
+ req = tevent_req_create(mem_ctx, &state,
+ struct auth_check_password_state);
+ if (req == NULL) {
+ return NULL;
}
- req->auth_ctx = auth_ctx;
- req->user_info = user_info;
- req->callback.fn = callback;
- req->callback.private_data = private_data;
+
+ state->auth_ctx = auth_ctx;
+ state->user_info = user_info;
+ state->method = NULL;
if (!user_info->mapped_state) {
- nt_status = map_user_info(req, lp_workgroup(auth_ctx->lp_ctx), user_info, &user_info_tmp);
- if (!NT_STATUS_IS_OK(nt_status)) goto failed;
+ nt_status = map_user_info(req, lp_workgroup(auth_ctx->lp_ctx),
+ user_info, &user_info_tmp);
+ if (tevent_req_nterror(req, nt_status)) {
+ return tevent_req_post(req, ev);
+ }
user_info = user_info_tmp;
- req->user_info = user_info_tmp;
+ state->user_info = user_info_tmp;
}
- DEBUGADD(3,("auth_check_password_send: mapped user is: [%s]\\[%s]@[%s]\n",
- user_info->mapped.domain_name, user_info->mapped.account_name, user_info->workstation_name));
-
- nt_status = auth_get_challenge(auth_ctx, &challenge);
- if (!NT_STATUS_IS_OK(nt_status)) {
- DEBUG(0, ("auth_check_password_send: Invalid challenge (length %u) stored for this auth context set_by %s - cannot continue: %s\n",
- (unsigned)auth_ctx->challenge.data.length, auth_ctx->challenge.set_by, nt_errstr(nt_status)));
- goto failed;
+ DEBUGADD(3,("auth_check_password_send: "
+ "mapped user is: [%s]\\[%s]@[%s]\n",
+ user_info->mapped.domain_name,
+ user_info->mapped.account_name,
+ user_info->workstation_name));
+
+ nt_status = auth_get_challenge(auth_ctx, chal);
+ if (tevent_req_nterror(req, nt_status)) {
+ DEBUG(0,("auth_check_password_send: "
+ "Invalid challenge (length %u) stored for "
+ "this auth context set_by %s - cannot continue: %s\n",
+ (unsigned)auth_ctx->challenge.data.length,
+ auth_ctx->challenge.set_by,
+ nt_errstr(nt_status)));
+ return tevent_req_post(req, ev);
}
if (auth_ctx->challenge.set_by) {
- DEBUG(10, ("auth_check_password_send: auth_context challenge created by %s\n",
- auth_ctx->challenge.set_by));
+ DEBUG(10,("auth_check_password_send: "
+ "auth_context challenge created by %s\n",
+ auth_ctx->challenge.set_by));
}
DEBUG(10, ("auth_check_password_send: challenge is: \n"));
- dump_data(5, auth_ctx->challenge.data.data, auth_ctx->challenge.data.length);
+ dump_data(5, auth_ctx->challenge.data.data,
+ auth_ctx->challenge.data.length);
+
+ im = tevent_create_immediate(state);
+ if (tevent_req_nomem(im, req)) {
+ return tevent_req_post(req, ev);
+ }
- nt_status = NT_STATUS_NO_SUCH_USER; /* If all the modules say 'not for me', then this is reasonable */
for (method = auth_ctx->methods; method; method = method->next) {
NTSTATUS result;
- struct tevent_timer *te = NULL;
/* check if the module wants to chek the password */
result = method->ops->want_check(method, req, user_info);
if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
- DEBUG(11,("auth_check_password_send: %s had nothing to say\n", method->ops->name));
+ DEBUG(11,("auth_check_password_send: "
+ "%s had nothing to say\n",
+ method->ops->name));
continue;
}
- nt_status = result;
- req->method = method;
-
- if (!NT_STATUS_IS_OK(nt_status)) break;
+ state->method = method;
- te = event_add_timed(auth_ctx->event_ctx, req,
- timeval_zero(),
- auth_check_password_async_timed_handler, req);
- if (!te) {
- nt_status = NT_STATUS_NO_MEMORY;
- goto failed;
+ if (tevent_req_nterror(req, result)) {
+ return tevent_req_post(req, ev);
}
+
+ tevent_schedule_immediate(im,
+ auth_ctx->event_ctx,
+ auth_check_password_async_trigger,
+ req);
+
+ return req;
+ }
+
+ /* If all the modules say 'not for me', then this is reasonable */
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ return tevent_req_post(req, ev);
+}
+
+static void auth_check_password_async_trigger(struct tevent_context *ev,
+ struct tevent_immediate *im,
+ void *private_data)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(private_data, struct tevent_req);
+ struct auth_check_password_state *state =
+ tevent_req_data(req, struct auth_check_password_state);
+ NTSTATUS status;
+
+ status = state->method->ops->check_password(state->method,
+ state,
+ state->user_info,
+ &state->server_info);
+ if (tevent_req_nterror(req, status)) {
return;
}
-failed:
- req->status = nt_status;
- req->callback.fn(req, req->callback.private_data);
+ tevent_req_done(req);
}
/**
@@ -339,7 +352,7 @@ failed:
* of that structure is undefined.
*
*
- * @param req The async auth_check_password state, passes to the callers callback function
+ * @param req The async request state
*
* @param mem_ctx The parent memory context for the server_info structure
*
@@ -350,30 +363,36 @@ failed:
*
**/
-_PUBLIC_ NTSTATUS auth_check_password_recv(struct auth_check_password_request *req,
+_PUBLIC_ NTSTATUS auth_check_password_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct auth_serversupplied_info **server_info)
{
+ struct auth_check_password_state *state =
+ tevent_req_data(req, struct auth_check_password_state);
NTSTATUS status;
- NT_STATUS_HAVE_NO_MEMORY(req);
+ if (tevent_req_is_nterror(req, &status)) {
+ DEBUG(2,("auth_check_password_recv: "
+ "%s authentication for user [%s\\%s]"
+ "FAILED with error %s\n",
+ (state->method ? state->method->ops->name : "NO_METHOD"),
+ state->user_info->mapped.domain_name,
+ state->user_info->mapped.account_name,
+ nt_errstr(status)));
+ tevent_req_received(req);
+ return status;
+ }
- if (NT_STATUS_IS_OK(req->status)) {
- DEBUG(5,("auth_check_password_recv: %s authentication for user [%s\\%s] succeeded\n",
- req->method->ops->name, req->server_info->domain_name, req->server_info->account_name));
+ DEBUG(5,("auth_check_password_recv: "
+ "%s authentication for user [%s\\%s] succeeded\n",
+ state->method->ops->name,
+ state->server_info->domain_name,
+ state->server_info->account_name));
- *server_info = talloc_steal(mem_ctx, req->server_info);
- } else {
- DEBUG(2,("auth_check_password_recv: %s authentication for user [%s\\%s] FAILED with error %s\n",
- (req->method ? req->method->ops->name : "NO_METHOD"),
- req->user_info->mapped.domain_name,
- req->user_info->mapped.account_name,
- nt_errstr(req->status)));
- }
+ *server_info = talloc_move(mem_ctx, &state->server_info);
- status = req->status;
- talloc_free(req);
- return status;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
}
/***************************************************************************
diff --git a/source4/auth/ntlm/auth_proto.h b/source4/auth/ntlm/auth_proto.h
deleted file mode 100644
index 572c1a4ca7..0000000000
--- a/source4/auth/ntlm/auth_proto.h
+++ /dev/null
@@ -1,50 +0,0 @@
-#ifndef __AUTH_NTLM_AUTH_PROTO_H__
-#define __AUTH_NTLM_AUTH_PROTO_H__
-
-#undef _PRINTF_ATTRIBUTE
-#define _PRINTF_ATTRIBUTE(a1, a2) PRINTF_ATTRIBUTE(a1, a2)
-/* This file was automatically generated by mkproto.pl. DO NOT EDIT */
-
-/* this file contains prototypes for functions that are private
- * to this subsystem or library. These functions should not be
- * used outside this particular subsystem! */
-
-
-/* The following definitions come from auth/ntlm/auth.c */
-
-
-/***************************************************************************
- Set a fixed challenge
-***************************************************************************/
-bool auth_challenge_may_be_modified(struct auth_context *auth_ctx) ;
-const struct auth_operations *auth_backend_byname(const char *name);
-const struct auth_critical_sizes *auth_interface_version(void);
-NTSTATUS server_service_auth_init(void);
-
-/* The following definitions come from auth/ntlm/auth_util.c */
-
-NTSTATUS auth_get_challenge_not_implemented(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge);
-
-/****************************************************************************
- Create an auth_usersupplied_data structure after appropriate mapping.
-****************************************************************************/
-NTSTATUS map_user_info(TALLOC_CTX *mem_ctx,
- const char *default_domain,
- const struct auth_usersupplied_info *user_info,
- struct auth_usersupplied_info **user_info_mapped);
-
-/****************************************************************************
- Create an auth_usersupplied_data structure after appropriate mapping.
-****************************************************************************/
-NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_context,
- enum auth_password_state to_state,
- const struct auth_usersupplied_info *user_info_in,
- const struct auth_usersupplied_info **user_info_encrypted);
-
-/* The following definitions come from auth/ntlm/auth_simple.c */
-
-#undef _PRINTF_ATTRIBUTE
-#define _PRINTF_ATTRIBUTE(a1, a2)
-
-#endif /* __AUTH_NTLM_AUTH_PROTO_H__ */
-
diff --git a/source4/auth/ntlm/auth_server.c b/source4/auth/ntlm/auth_server.c
index 12849aa420..ae7b7dd3a8 100644
--- a/source4/auth/ntlm/auth_server.c
+++ b/source4/auth/ntlm/auth_server.c
@@ -40,7 +40,7 @@ static NTSTATUS server_want_check(struct auth_method_context *ctx,
/**
* The challenge from the target server, when operating in security=server
**/
-static NTSTATUS server_get_challenge(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *_blob)
+static NTSTATUS server_get_challenge(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, uint8_t chal[8])
{
struct smb_composite_connect io;
struct smbcli_options smb_options;
@@ -88,7 +88,10 @@ static NTSTATUS server_get_challenge(struct auth_method_context *ctx, TALLOC_CTX
ctx->auth_ctx->event_ctx);
NT_STATUS_NOT_OK_RETURN(status);
- *_blob = io.out.tree->session->transport->negotiate.secblob;
+ if (io.out.tree->session->transport->negotiate.secblob.length != 8) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ memcpy(chal, io.out.tree->session->transport->negotiate.secblob.data, 8);
ctx->private_data = talloc_steal(ctx, io.out.tree->session);
return NT_STATUS_OK;
}
diff --git a/source4/auth/ntlm/auth_util.c b/source4/auth/ntlm/auth_util.c
index 5543cbebea..92df0bfe80 100644
--- a/source4/auth/ntlm/auth_util.c
+++ b/source4/auth/ntlm/auth_util.c
@@ -29,7 +29,7 @@
/* this default function can be used by mostly all backends
* which don't want to set a challenge
*/
-NTSTATUS auth_get_challenge_not_implemented(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge)
+NTSTATUS auth_get_challenge_not_implemented(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, uint8_t chal[8])
{
/* we don't want to set a challenge */
return NT_STATUS_NOT_IMPLEMENTED;
@@ -122,7 +122,7 @@ NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_contex
}
case AUTH_PASSWORD_HASH:
{
- const uint8_t *challenge;
+ uint8_t chal[8];
DATA_BLOB chall_blob;
user_info_temp = talloc(mem_ctx, struct auth_usersupplied_info);
if (!user_info_temp) {
@@ -134,12 +134,12 @@ NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_contex
*user_info_temp = *user_info_in;
user_info_temp->mapped_state = to_state;
- nt_status = auth_get_challenge(auth_context, &challenge);
+ nt_status = auth_get_challenge(auth_context, chal);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
- chall_blob = data_blob_talloc(mem_ctx, challenge, 8);
+ chall_blob = data_blob_talloc(mem_ctx, chal, 8);
if (lp_client_ntlmv2_auth(auth_context->lp_ctx)) {
DATA_BLOB names_blob = NTLMv2_generate_names_blob(mem_ctx, lp_netbios_name(auth_context->lp_ctx), lp_workgroup(auth_context->lp_ctx));
DATA_BLOB lmv2_response, ntlmv2_response, lmv2_session_key, ntlmv2_session_key;
@@ -162,12 +162,12 @@ NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_contex
data_blob_free(&ntlmv2_session_key);
} else {
DATA_BLOB blob = data_blob_talloc(mem_ctx, NULL, 24);
- SMBOWFencrypt(user_info_in->password.hash.nt->hash, challenge, blob.data);
+ SMBOWFencrypt(user_info_in->password.hash.nt->hash, chal, blob.data);
user_info_temp->password.response.nt = blob;
if (lp_client_lanman_auth(auth_context->lp_ctx) && user_info_in->password.hash.lanman) {
DATA_BLOB lm_blob = data_blob_talloc(mem_ctx, NULL, 24);
- SMBOWFencrypt(user_info_in->password.hash.lanman->hash, challenge, blob.data);
+ SMBOWFencrypt(user_info_in->password.hash.lanman->hash, chal, blob.data);
user_info_temp->password.response.lanman = lm_blob;
} else {
/* if not sending the LM password, send the NT password twice */
diff --git a/source4/auth/ntlm/auth_winbind.c b/source4/auth/ntlm/auth_winbind.c
index 568226dd87..173a895390 100644
--- a/source4/auth/ntlm/auth_winbind.c
+++ b/source4/auth/ntlm/auth_winbind.c
@@ -271,7 +271,7 @@ static NTSTATUS winbind_check_password(struct auth_method_context *ctx,
s->req.in.logon.password= password_info;
} else {
struct netr_NetworkInfo *network_info;
- const uint8_t *challenge;
+ uint8_t chal[8];
status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_RESPONSE,
user_info, &user_info_new);
@@ -281,10 +281,10 @@ static NTSTATUS winbind_check_password(struct auth_method_context *ctx,
network_info = talloc(s, struct netr_NetworkInfo);
NT_STATUS_HAVE_NO_MEMORY(network_info);
- status = auth_get_challenge(ctx->auth_ctx, &challenge);
+ status = auth_get_challenge(ctx->auth_ctx, chal);
NT_STATUS_NOT_OK_RETURN(status);
- memcpy(network_info->challenge, challenge, sizeof(network_info->challenge));
+ memcpy(network_info->challenge, chal, sizeof(network_info->challenge));
network_info->nt.length = user_info->password.response.nt.length;
network_info->nt.data = user_info->password.response.nt.data;
diff --git a/source4/auth/ntlm/config.mk b/source4/auth/ntlm/config.mk
index a0d668f748..6c75ae842d 100644
--- a/source4/auth/ntlm/config.mk
+++ b/source4/auth/ntlm/config.mk
@@ -71,10 +71,10 @@ PAM_ERRORS_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, pam_errors.o)
INIT_FUNCTION = server_service_auth_init
SUBSYSTEM = service
OUTPUT_TYPE = MERGED_OBJ
-PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBSECURITY SAMDB CREDENTIALS
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBSECURITY SAMDB CREDENTIALS UTIL_TEVENT
auth_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth.o auth_util.o auth_simple.o)
-$(eval $(call proto_header_template,$(authsrcdir)/auth_proto.h,$(auth_OBJ_FILES:.o=.c)))
+$(eval $(call proto_header_template,$(authsrcdir)/ntlm/auth_proto.h,$(auth_OBJ_FILES:.o=.c)))
# PUBLIC_HEADERS += auth/auth.h
diff --git a/source4/auth/ntlmssp/ntlmssp.h b/source4/auth/ntlmssp/ntlmssp.h
index 7743e7697e..7bed54d6d8 100644
--- a/source4/auth/ntlmssp/ntlmssp.h
+++ b/source4/auth/ntlmssp/ntlmssp.h
@@ -1,20 +1,20 @@
-/*
+/*
Unix SMB/CIFS implementation.
SMB parameters and setup
Copyright (C) Andrew Tridgell 1992-1997
Copyright (C) Luke Kenneth Casson Leighton 1996-1997
Copyright (C) Paul Ashton 1997
-
+
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/>.
*/
@@ -37,7 +37,7 @@ enum ntlmssp_message_type
NTLMSSP_CHALLENGE = 2,
NTLMSSP_AUTH = 3,
NTLMSSP_UNKNOWN = 4,
- NTLMSSP_DONE = 5 /* samba final state */
+ NTLMSSP_DONE = 5 /* samba final state */
};
struct gensec_ntlmssp_state
@@ -65,29 +65,30 @@ struct gensec_ntlmssp_state
DATA_BLOB internal_chal; /* Random challenge as supplied to the client for NTLM authentication */
DATA_BLOB chal; /* Random challenge as input into the actual NTLM (or NTLM2) authentication */
- DATA_BLOB lm_resp;
+ DATA_BLOB lm_resp;
DATA_BLOB nt_resp;
DATA_BLOB session_key;
-
+
uint32_t neg_flags; /* the current state of negotiation with the NTLMSSP partner */
/* internal variables used by KEY_EXCH (client-supplied user session key */
DATA_BLOB encrypted_session_key;
/**
- * Callback to get the 'challenge' used for NTLM authentication.
+ * Callback to get the 'challenge' used for NTLM authentication.
*
* @param ntlmssp_state This structure
* @return 8 bytes of challenge data, determined by the server to be the challenge for NTLM authentication
*
*/
- const uint8_t *(*get_challenge)(const struct gensec_ntlmssp_state *);
+ NTSTATUS (*get_challenge)(const struct gensec_ntlmssp_state *,
+ uint8_t challenge[8]);
/**
- * Callback to find if the challenge used by NTLM authentication may be modified
+ * Callback to find if the challenge used by NTLM authentication may be modified
*
* The NTLM2 authentication scheme modifies the effective challenge, but this is not compatiable with the
- * current 'security=server' implementation..
+ * current 'security=server' implementation..
*
* @param ntlmssp_state This structure
* @return Can the challenge be set to arbitary values?
@@ -96,7 +97,7 @@ struct gensec_ntlmssp_state
bool (*may_set_challenge)(const struct gensec_ntlmssp_state *);
/**
- * Callback to set the 'challenge' used for NTLM authentication.
+ * Callback to set the 'challenge' used for NTLM authentication.
*
* The callback may use the void *auth_context to store state information, but the same value is always available
* from the DATA_BLOB chal on this structure.
@@ -108,21 +109,20 @@ struct gensec_ntlmssp_state
NTSTATUS (*set_challenge)(struct gensec_ntlmssp_state *, DATA_BLOB *challenge);
/**
- * Callback to check the user's password.
+ * Callback to check the user's password.
*
- * The callback must reads the feilds of this structure for the information it needs on the user
+ * The callback must reads the feilds of this structure for the information it needs on the user
* @param ntlmssp_state This structure
* @param nt_session_key If an NT session key is returned by the authentication process, return it here
* @param lm_session_key If an LM session key is returned by the authentication process, return it here
*
*/
- NTSTATUS (*check_password)(struct gensec_ntlmssp_state *,
- TALLOC_CTX *mem_ctx,
+ NTSTATUS (*check_password)(struct gensec_ntlmssp_state *,
DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key);
const char *server_name;
- bool doing_ntlm2;
+ bool doing_ntlm2;
union {
/* NTLM */
diff --git a/source4/auth/ntlmssp/ntlmssp_server.c b/source4/auth/ntlmssp/ntlmssp_server.c
index 94de920772..c49bf2fea7 100644
--- a/source4/auth/ntlmssp/ntlmssp_server.c
+++ b/source4/auth/ntlmssp/ntlmssp_server.c
@@ -124,8 +124,9 @@ NTSTATUS ntlmssp_server_negotiate(struct gensec_security *gensec_security,
DATA_BLOB struct_blob;
uint32_t neg_flags = 0;
uint32_t ntlmssp_command, chal_flags;
- const uint8_t *cryptkey;
+ uint8_t cryptkey[8];
const char *target_name;
+ NTSTATUS status;
/* parse the NTLMSSP packet */
#if 0
@@ -150,10 +151,11 @@ NTSTATUS ntlmssp_server_negotiate(struct gensec_security *gensec_security,
ntlmssp_handle_neg_flags(gensec_ntlmssp_state, neg_flags, gensec_ntlmssp_state->allow_lm_key);
/* Ask our caller what challenge they would like in the packet */
- cryptkey = gensec_ntlmssp_state->get_challenge(gensec_ntlmssp_state);
- if (!cryptkey) {
- DEBUG(1, ("ntlmssp_server_negotiate: backend doesn't give a challenge\n"));
- return NT_STATUS_INTERNAL_ERROR;
+ status = gensec_ntlmssp_state->get_challenge(gensec_ntlmssp_state, cryptkey);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("ntlmssp_server_negotiate: backend doesn't give a challenge: %s\n",
+ nt_errstr(status)));
+ return status;
}
/* Check if we may set the challenge */
@@ -180,7 +182,6 @@ NTSTATUS ntlmssp_server_negotiate(struct gensec_security *gensec_security,
/* This creates the 'blob' of names that appears at the end of the packet */
if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) {
char dnsdomname[MAXHOSTNAMELEN], dnsname[MAXHOSTNAMELEN];
- const char *target_name_dns = "";
/* Find out the DNS domain name */
dnsdomname[0] = '\0';
@@ -194,12 +195,6 @@ NTSTATUS ntlmssp_server_negotiate(struct gensec_security *gensec_security,
}
strlower_m(dnsname);
- if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) {
- target_name_dns = dnsdomname;
- } else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) {
- target_name_dns = dnsname;
- }
-
msrpc_gen(out_mem_ctx,
&struct_blob, "aaaaa",
MsvAvNbDomainName, target_name,
@@ -268,6 +263,7 @@ static NTSTATUS ntlmssp_server_preauth(struct gensec_ntlmssp_state *gensec_ntlms
}
/* zero these out */
+ data_blob_free(&gensec_ntlmssp_state->session_key);
data_blob_free(&gensec_ntlmssp_state->lm_resp);
data_blob_free(&gensec_ntlmssp_state->nt_resp);
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
@@ -406,6 +402,11 @@ static NTSTATUS ntlmssp_server_postauth(struct gensec_security *gensec_security,
NTSTATUS nt_status;
DATA_BLOB session_key = data_blob(NULL, 0);
+ if (!(gensec_security->want_features
+ & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL|GENSEC_FEATURE_SESSION_KEY))) {
+ return NT_STATUS_OK;
+ }
+
if (user_session_key)
dump_data_pw("USER session key:\n", user_session_key->data, user_session_key->length);
@@ -548,20 +549,15 @@ NTSTATUS ntlmssp_server_auth(struct gensec_security *gensec_security,
const DATA_BLOB in, DATA_BLOB *out)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
- DATA_BLOB user_session_key = data_blob(NULL, 0);
- DATA_BLOB lm_session_key = data_blob(NULL, 0);
+ DATA_BLOB user_session_key = data_blob_null;
+ DATA_BLOB lm_session_key = data_blob_null;
NTSTATUS nt_status;
- TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx);
- if (!mem_ctx) {
- return NT_STATUS_NO_MEMORY;
- }
-
/* zero the outbound NTLMSSP packet */
- *out = data_blob_talloc(out_mem_ctx, NULL, 0);
+ *out = data_blob_null;
- if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_preauth(gensec_ntlmssp_state, in))) {
- talloc_free(mem_ctx);
+ nt_status = ntlmssp_server_preauth(gensec_ntlmssp_state, in);
+ if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
@@ -573,23 +569,21 @@ NTSTATUS ntlmssp_server_auth(struct gensec_security *gensec_security,
*/
/* Finally, actually ask if the password is OK */
-
- if (!NT_STATUS_IS_OK(nt_status = gensec_ntlmssp_state->check_password(gensec_ntlmssp_state, mem_ctx,
- &user_session_key, &lm_session_key))) {
- talloc_free(mem_ctx);
+ nt_status = gensec_ntlmssp_state->check_password(gensec_ntlmssp_state,
+ &user_session_key,
+ &lm_session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
-
- if (gensec_security->want_features
- & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL|GENSEC_FEATURE_SESSION_KEY)) {
- nt_status = ntlmssp_server_postauth(gensec_security, &user_session_key, &lm_session_key);
- talloc_free(mem_ctx);
+
+ nt_status = ntlmssp_server_postauth(gensec_security,
+ &user_session_key,
+ &lm_session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
- } else {
- gensec_ntlmssp_state->session_key = data_blob(NULL, 0);
- talloc_free(mem_ctx);
- return NT_STATUS_OK;
}
+
+ return NT_STATUS_OK;
}
/**
@@ -597,19 +591,19 @@ NTSTATUS ntlmssp_server_auth(struct gensec_security *gensec_security,
* @return an 8 byte random challenge
*/
-static const uint8_t *auth_ntlmssp_get_challenge(const struct gensec_ntlmssp_state *gensec_ntlmssp_state)
+static NTSTATUS auth_ntlmssp_get_challenge(const struct gensec_ntlmssp_state *gensec_ntlmssp_state,
+ uint8_t chal[8])
{
NTSTATUS status;
- const uint8_t *chal;
- status = gensec_ntlmssp_state->auth_context->get_challenge(gensec_ntlmssp_state->auth_context, &chal);
+ status = gensec_ntlmssp_state->auth_context->get_challenge(gensec_ntlmssp_state->auth_context, chal);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1, ("auth_ntlmssp_get_challenge: failed to get challenge: %s\n",
nt_errstr(status)));
- return NULL;
+ return status;
}
- return chal;
+ return NT_STATUS_OK;
}
/**
@@ -651,12 +645,13 @@ static NTSTATUS auth_ntlmssp_set_challenge(struct gensec_ntlmssp_state *gensec_n
* Return the session keys used on the connection.
*/
-static NTSTATUS auth_ntlmssp_check_password(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
- TALLOC_CTX *mem_ctx,
- DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
+static NTSTATUS auth_ntlmssp_check_password(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
+ DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
{
NTSTATUS nt_status;
- struct auth_usersupplied_info *user_info = talloc(mem_ctx, struct auth_usersupplied_info);
+ struct auth_usersupplied_info *user_info;
+
+ user_info = talloc(gensec_ntlmssp_state, struct auth_usersupplied_info);
if (!user_info) {
return NT_STATUS_NO_MEMORY;
}
@@ -675,31 +670,21 @@ static NTSTATUS auth_ntlmssp_check_password(struct gensec_ntlmssp_state *gensec_
user_info->password.response.nt = gensec_ntlmssp_state->nt_resp;
user_info->password.response.nt.data = talloc_steal(user_info, gensec_ntlmssp_state->nt_resp.data);
- nt_status = gensec_ntlmssp_state->auth_context->check_password(gensec_ntlmssp_state->auth_context,
- mem_ctx,
- user_info,
+ nt_status = gensec_ntlmssp_state->auth_context->check_password(gensec_ntlmssp_state->auth_context,
+ gensec_ntlmssp_state,
+ user_info,
&gensec_ntlmssp_state->server_info);
talloc_free(user_info);
NT_STATUS_NOT_OK_RETURN(nt_status);
- talloc_steal(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info);
-
if (gensec_ntlmssp_state->server_info->user_session_key.length) {
- DEBUG(10, ("Got NT session key of length %u\n",
+ DEBUG(10, ("Got NT session key of length %u\n",
(unsigned)gensec_ntlmssp_state->server_info->user_session_key.length));
- if (!talloc_reference(mem_ctx, gensec_ntlmssp_state->server_info->user_session_key.data)) {
- return NT_STATUS_NO_MEMORY;
- }
-
*user_session_key = gensec_ntlmssp_state->server_info->user_session_key;
}
if (gensec_ntlmssp_state->server_info->lm_session_key.length) {
- DEBUG(10, ("Got LM session key of length %u\n",
+ DEBUG(10, ("Got LM session key of length %u\n",
(unsigned)gensec_ntlmssp_state->server_info->lm_session_key.length));
- if (!talloc_reference(mem_ctx, gensec_ntlmssp_state->server_info->lm_session_key.data)) {
- return NT_STATUS_NO_MEMORY;
- }
-
*lm_session_key = gensec_ntlmssp_state->server_info->lm_session_key;
}
return nt_status;
diff --git a/source4/auth/ntlmssp/ntlmssp_sign.c b/source4/auth/ntlmssp/ntlmssp_sign.c
index 957d0a8fbc..9e0d80f788 100644
--- a/source4/auth/ntlmssp/ntlmssp_sign.c
+++ b/source4/auth/ntlmssp/ntlmssp_sign.c
@@ -171,7 +171,7 @@ NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security,
NTLMSSP_RECEIVE, &local_sig, true);
if (!NT_STATUS_IS_OK(nt_status)) {
- DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status)));
+ DEBUG(0, ("NTLMSSP packet sig creation failed with %s\n", nt_errstr(nt_status)));
return nt_status;
}
@@ -179,26 +179,25 @@ NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security,
if (local_sig.length != sig->length ||
memcmp(local_sig.data,
sig->data, sig->length) != 0) {
- DEBUG(5, ("BAD SIG NTLM2: wanted signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
- dump_data(5, local_sig.data, local_sig.length);
+
+ DEBUG(10, ("BAD SIG NTLM2: wanted signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
+ dump_data(10, local_sig.data, local_sig.length);
- DEBUG(5, ("BAD SIG: got signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
- dump_data(5, sig->data, sig->length);
+ DEBUG(10, ("BAD SIG: got signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
+ dump_data(10, sig->data, sig->length);
- DEBUG(1, ("NTLMSSP NTLM2 packet check failed due to invalid signature on %llu bytes of input!\n", (unsigned long long)pdu_length));
return NT_STATUS_ACCESS_DENIED;
}
} else {
if (local_sig.length != sig->length ||
memcmp(local_sig.data + 8,
sig->data + 8, sig->length - 8) != 0) {
- DEBUG(5, ("BAD SIG NTLM1: wanted signature of %llu bytes of input:\n", (unsigned long long)length));
+ DEBUG(10, ("BAD SIG NTLM1: wanted signature of %llu bytes of input:\n", (unsigned long long)length));
dump_data(5, local_sig.data, local_sig.length);
- DEBUG(5, ("BAD SIG: got signature of %llu bytes of input:\n", (unsigned long long)length));
- dump_data(5, sig->data, sig->length);
+ DEBUG(10, ("BAD SIG: got signature of %llu bytes of input:\n", (unsigned long long)length));
+ dump_data(10, sig->data, sig->length);
- DEBUG(1, ("NTLMSSP NTLM1 packet check failed due to invalid signature on %llu bytes of input:\n", (unsigned long long)length));
return NT_STATUS_ACCESS_DENIED;
}
}
@@ -281,6 +280,7 @@ NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security,
const uint8_t *whole_pdu, size_t pdu_length,
const DATA_BLOB *sig)
{
+ NTSTATUS status;
struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
if (!gensec_ntlmssp_state->session_key.length) {
DEBUG(3, ("NO session key, cannot unseal packet\n"));
@@ -294,7 +294,12 @@ NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security,
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, data, length);
}
dump_data_pw("ntlmssp clear data\n", data, length);
- return gensec_ntlmssp_check_packet(gensec_security, sig_mem_ctx, data, length, whole_pdu, pdu_length, sig);
+ status = gensec_ntlmssp_check_packet(gensec_security, sig_mem_ctx, data, length, whole_pdu, pdu_length, sig);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("NTLMSSP packet check for unseal failed due to invalid signature on %llu bytes of input:\n", (unsigned long long)length));
+ }
+ return status;
}
/**
@@ -585,6 +590,10 @@ NTSTATUS gensec_ntlmssp_unwrap(struct gensec_security *gensec_security,
status = check_status;
}
}
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("NTLMSSP packet check for unwrap failed due to invalid signature\n"));
+ }
return status;
} else {
*out = *in;
diff --git a/source4/build/smb_build/summary.pm b/source4/build/smb_build/summary.pm
index 153c3e20e9..09f5ff83ce 100644
--- a/source4/build/smb_build/summary.pm
+++ b/source4/build/smb_build/summary.pm
@@ -65,6 +65,7 @@ sub show($$)
showisexternal($output, "tdb", "LIBTDB");
showisexternal($output, "tevent", "LIBTEVENT");
showisexternal($output, "ldb", "LIBLDB");
+ showisexternal($output, "heimdal", "HEIMDAL_KRB5");
print "Developer mode: ".(enabled($config->{developer})?"yes":"no")."\n";
print "Automatic dependencies: ".
(enabled($config->{automatic_dependencies})
diff --git a/source4/dsdb/common/dsdb_dn.c b/source4/dsdb/common/dsdb_dn.c
index 660eaf7d40..9023b0347a 100644
--- a/source4/dsdb/common/dsdb_dn.c
+++ b/source4/dsdb/common/dsdb_dn.c
@@ -325,3 +325,72 @@ int dsdb_dn_string_comparison(struct ldb_context *ldb, void *mem_ctx,
{
return ldb_any_comparison(ldb, mem_ctx, dsdb_dn_string_canonicalise, v1, v2);
}
+
+
+/*
+ convert a dsdb_dn to a linked attribute data blob
+*/
+WERROR dsdb_dn_la_to_blob(struct ldb_context *sam_ctx,
+ const struct dsdb_attribute *schema_attrib,
+ const struct dsdb_schema *schema,
+ TALLOC_CTX *mem_ctx,
+ struct dsdb_dn *dsdb_dn, DATA_BLOB **blob)
+{
+ struct ldb_val v;
+ WERROR werr;
+ struct ldb_message_element val_el;
+ struct drsuapi_DsReplicaAttribute drs;
+
+ /* we need a message_element with just one value in it */
+ v = data_blob_string_const(dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1));
+
+ val_el.name = schema_attrib->lDAPDisplayName;
+ val_el.values = &v;
+ val_el.num_values = 1;
+
+ werr = schema_attrib->syntax->ldb_to_drsuapi(sam_ctx, schema, schema_attrib, &val_el, mem_ctx, &drs);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ if (drs.value_ctr.num_values != 1) {
+ DEBUG(1,(__location__ ": Failed to build DRS blob for linked attribute %s\n",
+ schema_attrib->lDAPDisplayName));
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ *blob = drs.value_ctr.values[0].blob;
+ return WERR_OK;
+}
+
+/*
+ convert a data blob to a dsdb_dn
+ */
+WERROR dsdb_dn_la_from_blob(struct ldb_context *sam_ctx,
+ const struct dsdb_attribute *schema_attrib,
+ const struct dsdb_schema *schema,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ struct dsdb_dn **dsdb_dn)
+{
+ WERROR werr;
+ struct ldb_message_element new_el;
+ struct drsuapi_DsReplicaAttribute drs;
+ struct drsuapi_DsAttributeValue val;
+
+ drs.value_ctr.num_values = 1;
+ drs.value_ctr.values = &val;
+ val.blob = blob;
+
+ werr = schema_attrib->syntax->drsuapi_to_ldb(sam_ctx, schema, schema_attrib, &drs, mem_ctx, &new_el);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ if (new_el.num_values != 1) {
+ return WERR_INTERNAL_ERROR;
+ }
+
+ *dsdb_dn = dsdb_dn_parse(mem_ctx, sam_ctx, &new_el.values[0], schema_attrib->syntax->ldap_oid);
+ if (!*dsdb_dn) {
+ return WERR_INTERNAL_ERROR;
+ }
+
+ return WERR_OK;
+}
diff --git a/source4/dsdb/common/dsdb_dn.h b/source4/dsdb/common/dsdb_dn.h
index 53e10535c8..b713bdd27b 100644
--- a/source4/dsdb/common/dsdb_dn.h
+++ b/source4/dsdb/common/dsdb_dn.h
@@ -15,3 +15,8 @@ struct dsdb_dn {
#define DSDB_SYNTAX_BINARY_DN "1.2.840.113556.1.4.903"
#define DSDB_SYNTAX_STRING_DN "1.2.840.113556.1.4.904"
#define DSDB_SYNTAX_OR_NAME "1.2.840.113556.1.4.1221"
+
+
+/* RMD_FLAGS component in a DN */
+#define DSDB_RMD_FLAG_DELETED 1
+#define DSDB_RMD_FLAG_INVISIBLE 2
diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c
index 61d065b85c..b8ba26a4ec 100644
--- a/source4/dsdb/common/util.c
+++ b/source4/dsdb/common/util.c
@@ -997,6 +997,81 @@ int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_m
}
/*
+ * Handle ldb_request in transaction
+ */
+static int dsdb_autotransaction_request(struct ldb_context *sam_ldb,
+ struct ldb_request *req)
+{
+ int ret;
+
+ ret = ldb_transaction_start(sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_request(sam_ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ if (ret == LDB_SUCCESS) {
+ return ldb_transaction_commit(sam_ldb);
+ }
+ ldb_transaction_cancel(sam_ldb);
+
+ return ret;
+}
+
+/*
+ * replace elements in a record using LDB_CONTROL_AS_SYSTEM
+ * used to skip access checks on operations
+ * that are performed by the system
+ */
+int samdb_replace_as_system(struct ldb_context *sam_ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg)
+{
+ int i;
+ int ldb_ret;
+ struct ldb_request *req = NULL;
+
+ /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+
+ ldb_ret = ldb_msg_sanity_check(sam_ldb, msg);
+ if (ldb_ret != LDB_SUCCESS) {
+ return ldb_ret;
+ }
+
+ ldb_ret = ldb_build_mod_req(&req, sam_ldb, mem_ctx,
+ msg,
+ NULL,
+ NULL,
+ ldb_op_default_callback,
+ NULL);
+
+ if (ldb_ret != LDB_SUCCESS) {
+ talloc_free(req);
+ return ldb_ret;
+ }
+
+ ldb_ret = ldb_request_add_control(req, LDB_CONTROL_AS_SYSTEM_OID, false, NULL);
+ if (ldb_ret != LDB_SUCCESS) {
+ talloc_free(req);
+ return ldb_ret;
+ }
+
+ /* do request and auto start a transaction */
+ ldb_ret = dsdb_autotransaction_request(sam_ldb, req);
+
+ talloc_free(req);
+ return ldb_ret;
+}
+
+/*
return a default security descriptor
*/
struct security_descriptor *samdb_default_security_descriptor(TALLOC_CTX *mem_ctx)
@@ -1986,7 +2061,7 @@ NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TA
{
struct ldb_message *msg;
struct ldb_dn *basedn;
- const char *sidstr;
+ char *sidstr;
int ret;
sidstr = dom_sid_string(mem_ctx, sid);
@@ -1995,45 +2070,47 @@ NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TA
/* We might have to create a ForeignSecurityPrincipal, even if this user
* is in our own domain */
- msg = ldb_msg_new(mem_ctx);
+ msg = ldb_msg_new(sidstr);
if (msg == NULL) {
+ talloc_free(sidstr);
return NT_STATUS_NO_MEMORY;
}
- /* TODO: Hmmm. This feels wrong. How do I find the base dn to
- * put the ForeignSecurityPrincipals? d_state->domain_dn does
- * not work, this is wrong for the Builtin domain, there's no
- * cn=For...,cn=Builtin,dc={BASEDN}. -- vl
- */
-
- basedn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
- "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
-
- if (basedn == NULL) {
+ ret = dsdb_wellknown_dn(sam_ctx, sidstr, samdb_base_dn(sam_ctx),
+ DS_GUID_FOREIGNSECURITYPRINCIPALS_CONTAINER,
+ &basedn);
+ if (ret != LDB_SUCCESS) {
DEBUG(0, ("Failed to find DN for "
- "ForeignSecurityPrincipal container\n"));
+ "ForeignSecurityPrincipal container - %s\n", ldb_errstring(sam_ctx)));
+ talloc_free(sidstr);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
/* add core elements to the ldb_message for the alias */
- msg->dn = ldb_dn_copy(mem_ctx, basedn);
- if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
+ msg->dn = basedn;
+ if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr)) {
+ talloc_free(sidstr);
return NT_STATUS_NO_MEMORY;
+ }
- samdb_msg_add_string(sam_ctx, mem_ctx, msg,
+ samdb_msg_add_string(sam_ctx, msg, msg,
"objectClass",
"foreignSecurityPrincipal");
/* create the alias */
ret = ldb_add(sam_ctx, msg);
- if (ret != 0) {
+ if (ret != LDB_SUCCESS) {
DEBUG(0,("Failed to create foreignSecurityPrincipal "
"record %s: %s\n",
ldb_dn_get_linearized(msg->dn),
ldb_errstring(sam_ctx)));
+ talloc_free(sidstr);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- *ret_dn = msg->dn;
+
+ *ret_dn = talloc_steal(mem_ctx, msg->dn);
+ talloc_free(sidstr);
+
return NT_STATUS_OK;
}
@@ -2072,14 +2149,16 @@ struct ldb_dn *samdb_dns_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_c
if (!ldb_dn_validate(dn)) {
DEBUG(2, ("Failed to validated DN %s\n",
ldb_dn_get_linearized(dn)));
+ talloc_free(tmp_ctx);
return NULL;
}
+ talloc_free(tmp_ctx);
return dn;
}
+
/*
Find the DN of a domain, be it the netbios or DNS name
*/
-
struct ldb_dn *samdb_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
const char *domain_name)
{
@@ -2151,13 +2230,14 @@ int dsdb_find_dn_by_guid(struct ldb_context *ldb,
return LDB_ERR_OPERATIONS_ERROR;
}
- res = talloc_zero(mem_ctx, struct ldb_result);
+ res = talloc_zero(expression, struct ldb_result);
if (!res) {
DEBUG(0, (__location__ ": out of memory\n"));
+ talloc_free(expression);
return LDB_ERR_OPERATIONS_ERROR;
}
- ret = ldb_build_search_req(&search_req, ldb, mem_ctx,
+ ret = ldb_build_search_req(&search_req, ldb, expression,
ldb_get_default_basedn(ldb),
LDB_SCOPE_SUBTREE,
expression, attrs,
@@ -2165,6 +2245,7 @@ int dsdb_find_dn_by_guid(struct ldb_context *ldb,
res, ldb_search_default_callback,
NULL);
if (ret != LDB_SUCCESS) {
+ talloc_free(expression);
return ret;
}
@@ -2173,12 +2254,14 @@ int dsdb_find_dn_by_guid(struct ldb_context *ldb,
options = talloc(search_req, struct ldb_search_options_control);
if (options == NULL) {
DEBUG(0, (__location__ ": out of memory\n"));
+ talloc_free(expression);
return LDB_ERR_OPERATIONS_ERROR;
}
options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
ret = ldb_request_add_control(search_req, LDB_CONTROL_EXTENDED_DN_OID, true, NULL);
if (ret != LDB_SUCCESS) {
+ talloc_free(expression);
return ret;
}
@@ -2186,16 +2269,19 @@ int dsdb_find_dn_by_guid(struct ldb_context *ldb,
LDB_CONTROL_SEARCH_OPTIONS_OID,
true, options);
if (ret != LDB_SUCCESS) {
+ talloc_free(expression);
return ret;
}
ret = ldb_request(ldb, search_req);
if (ret != LDB_SUCCESS) {
+ talloc_free(expression);
return ret;
}
ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
if (ret != LDB_SUCCESS) {
+ talloc_free(expression);
return ret;
}
@@ -2203,10 +2289,12 @@ int dsdb_find_dn_by_guid(struct ldb_context *ldb,
partitions module that can return two here with the
search_options control set */
if (res->count < 1) {
+ talloc_free(expression);
return LDB_ERR_NO_SUCH_OBJECT;
}
- *dn = res->msgs[0]->dn;
+ *dn = talloc_steal(mem_ctx, res->msgs[0]->dn);
+ talloc_free(expression);
return LDB_SUCCESS;
}
@@ -2229,6 +2317,7 @@ int dsdb_search_dn_with_deleted(struct ldb_context *ldb,
res = talloc_zero(tmp_ctx, struct ldb_result);
if (!res) {
+ talloc_free(tmp_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
@@ -2248,6 +2337,7 @@ int dsdb_search_dn_with_deleted(struct ldb_context *ldb,
ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
return ret;
}
@@ -2256,8 +2346,8 @@ int dsdb_search_dn_with_deleted(struct ldb_context *ldb,
ret = ldb_wait(req->handle, LDB_WAIT_ALL);
}
- talloc_free(req);
*_res = talloc_steal(mem_ctx, res);
+ talloc_free(tmp_ctx);
return ret;
}
@@ -2720,13 +2810,32 @@ int dsdb_functional_level(struct ldb_context *ldb)
}
/*
+ set a GUID in an extended DN structure
+ */
+int dsdb_set_extended_dn_guid(struct ldb_dn *dn, const struct GUID *guid, const char *component_name)
+{
+ struct ldb_val v;
+ NTSTATUS status;
+ int ret;
+
+ status = GUID_to_ndr_blob(guid, dn, &v);
+ if (!NT_STATUS_IS_OK(status)) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ ret = ldb_dn_set_extended_component(dn, component_name, &v);
+ data_blob_free(&v);
+ return ret;
+}
+
+/*
return a GUID from a extended DN structure
*/
-NTSTATUS dsdb_get_extended_dn_guid(struct ldb_dn *dn, struct GUID *guid)
+NTSTATUS dsdb_get_extended_dn_guid(struct ldb_dn *dn, struct GUID *guid, const char *component_name)
{
const struct ldb_val *v;
- v = ldb_dn_get_extended_component(dn, "GUID");
+ v = ldb_dn_get_extended_component(dn, component_name);
if (v == NULL) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
@@ -2735,17 +2844,111 @@ NTSTATUS dsdb_get_extended_dn_guid(struct ldb_dn *dn, struct GUID *guid)
}
/*
+ return a uint64_t from a extended DN structure
+ */
+NTSTATUS dsdb_get_extended_dn_uint64(struct ldb_dn *dn, uint64_t *val, const char *component_name)
+{
+ const struct ldb_val *v;
+ char *s;
+
+ v = ldb_dn_get_extended_component(dn, component_name);
+ if (v == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ s = talloc_strndup(dn, (const char *)v->data, v->length);
+ NT_STATUS_HAVE_NO_MEMORY(s);
+
+ *val = strtoull(s, NULL, 0);
+
+ talloc_free(s);
+ return NT_STATUS_OK;
+}
+
+/*
+ return a NTTIME from a extended DN structure
+ */
+NTSTATUS dsdb_get_extended_dn_nttime(struct ldb_dn *dn, NTTIME *nttime, const char *component_name)
+{
+ return dsdb_get_extended_dn_uint64(dn, nttime, component_name);
+}
+
+/*
+ return a uint32_t from a extended DN structure
+ */
+NTSTATUS dsdb_get_extended_dn_uint32(struct ldb_dn *dn, uint32_t *val, const char *component_name)
+{
+ const struct ldb_val *v;
+ char *s;
+
+ v = ldb_dn_get_extended_component(dn, component_name);
+ if (v == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ s = talloc_strndup(dn, (const char *)v->data, v->length);
+ NT_STATUS_HAVE_NO_MEMORY(s);
+
+ *val = strtoul(s, NULL, 0);
+
+ talloc_free(s);
+ return NT_STATUS_OK;
+}
+
+/*
+ return RMD_FLAGS directly from a ldb_dn
+ returns 0 if not found
+ */
+uint32_t dsdb_dn_rmd_flags(struct ldb_dn *dn)
+{
+ const struct ldb_val *v;
+ char buf[32];
+ v = ldb_dn_get_extended_component(dn, "RMD_FLAGS");
+ if (!v || v->length > sizeof(buf)-1) return 0;
+ strncpy(buf, (const char *)v->data, v->length);
+ buf[v->length] = 0;
+ return strtoul(buf, NULL, 10);
+}
+
+/*
+ return RMD_FLAGS directly from a ldb_val for a DN
+ returns 0 if RMD_FLAGS is not found
+ */
+uint32_t dsdb_dn_val_rmd_flags(struct ldb_val *val)
+{
+ const char *p;
+ uint32_t flags;
+ char *end;
+
+ if (val->length < 13) {
+ return 0;
+ }
+ p = memmem(val->data, val->length-2, "<RMD_FLAGS=", 11);
+ if (!p) {
+ return 0;
+ }
+ flags = strtoul(p+11, &end, 10);
+ if (!end || *end != '>') {
+ /* it must end in a > */
+ return 0;
+ }
+ return flags;
+}
+
+/*
return true if a ldb_val containing a DN in storage form is deleted
*/
bool dsdb_dn_is_deleted_val(struct ldb_val *val)
{
- /* this relies on the sort order and exact format of
- linearized extended DNs */
- if (val->length >= 12 &&
- strncmp((const char *)val->data, "<DELETED=1>;", 12) == 0) {
- return true;
- }
- return false;
+ return (dsdb_dn_val_rmd_flags(val) & DSDB_RMD_FLAG_DELETED) != 0;
+}
+
+/*
+ return true if a ldb_val containing a DN in storage form is
+ in the upgraded w2k3 linked attribute format
+ */
+bool dsdb_dn_is_upgraded_link_val(struct ldb_val *val)
+{
+ return memmem(val->data, val->length, "<RMD_ADDTIME=", 13) != NULL;
}
/*
@@ -2870,3 +3073,29 @@ int dsdb_get_deleted_objects_dn(struct ldb_context *ldb,
talloc_free(nc_root);
return ret;
}
+
+/*
+ return the tombstoneLifetime, in days
+ */
+int dsdb_tombstone_lifetime(struct ldb_context *ldb, uint32_t *lifetime)
+{
+ struct ldb_dn *dn;
+ dn = samdb_config_dn(ldb);
+ if (!dn) {
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+ dn = ldb_dn_copy(ldb, dn);
+ if (!dn) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ /* see MS-ADTS section 7.1.1.2.4.1.1. There doesn't appear to
+ be a wellknown GUID for this */
+ if (!ldb_dn_add_child_fmt(dn, "CN=Directory Service,CN=Windows NT")) {
+ talloc_free(dn);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *lifetime = samdb_search_uint(ldb, dn, 180, dn, "tombstoneLifetime", "objectClass=nTDSService");
+ talloc_free(dn);
+ return LDB_SUCCESS;
+}
diff --git a/source4/dsdb/config.mk b/source4/dsdb/config.mk
index dfc5def64e..35a0c84903 100644
--- a/source4/dsdb/config.mk
+++ b/source4/dsdb/config.mk
@@ -22,7 +22,7 @@ $(eval $(call proto_header_template,$(dsdbsrcdir)/samdb/samdb_proto.h,$(SAMDB_OB
# PUBLIC_HEADERS += dsdb/samdb/samdb.h
[SUBSYSTEM::SAMDB_COMMON]
-PRIVATE_DEPENDENCIES = LIBLDB
+PRIVATE_DEPENDENCIES = LIBLDB NDR_DRSBLOBS LIBCLI_LDAP_NDR UTIL_LDB LIBCLI_AUTH
SAMDB_COMMON_OBJ_FILES = $(addprefix $(dsdbsrcdir)/common/, \
util.o \
@@ -31,7 +31,7 @@ SAMDB_COMMON_OBJ_FILES = $(addprefix $(dsdbsrcdir)/common/, \
$(eval $(call proto_header_template,$(dsdbsrcdir)/common/proto.h,$(SAMDB_COMMON_OBJ_FILES:.o=.c)))
[SUBSYSTEM::SAMDB_SCHEMA]
-PRIVATE_DEPENDENCIES = SAMDB_COMMON NDR_DRSUAPI NDR_DRSBLOBS
+PRIVATE_DEPENDENCIES = SAMDB_COMMON NDR_DRSUAPI NDR_DRSBLOBS LDBSAMBA
SAMDB_SCHEMA_OBJ_FILES = $(addprefix $(dsdbsrcdir)/schema/, \
schema_init.o \
@@ -83,6 +83,7 @@ PRIVATE_DEPENDENCIES = \
KCC_SRV_OBJ_FILES = $(addprefix $(dsdbsrcdir)/kcc/, \
kcc_service.o \
kcc_connection.o \
+ kcc_deleted.o \
kcc_periodic.o)
$(eval $(call proto_header_template,$(dsdbsrcdir)/kcc/kcc_service_proto.h,$(KCC_SRV_OBJ_FILES:.o=.c)))
diff --git a/source4/dsdb/kcc/kcc_connection.c b/source4/dsdb/kcc/kcc_connection.c
index ee9a05a21e..73198040c4 100644
--- a/source4/dsdb/kcc/kcc_connection.c
+++ b/source4/dsdb/kcc/kcc_connection.c
@@ -133,7 +133,7 @@ void kccsrv_apply_connections(struct kccsrv_service *s,
{
int i, j, deleted = 0, added = 0, ret;
- for (i = 0; i < ntds_list->count; i++) {
+ for (i = 0; ntds_list && i < ntds_list->count; i++) {
struct kcc_connection *ntds = &ntds_list->servers[i];
for (j = 0; j < dsa_list->count; j++) {
struct kcc_connection *dsa = &dsa_list->servers[j];
@@ -152,13 +152,13 @@ void kccsrv_apply_connections(struct kccsrv_service *s,
for (i = 0; i < dsa_list->count; i++) {
struct kcc_connection *dsa = &dsa_list->servers[i];
- for (j = 0; j < ntds_list->count; j++) {
+ for (j = 0; ntds_list && j < ntds_list->count; j++) {
struct kcc_connection *ntds = &ntds_list->servers[j];
if (GUID_equal(&dsa->dsa_guid, &ntds->dsa_guid)) {
break;
}
}
- if (j == ntds_list->count) {
+ if (ntds_list == NULL || j == ntds_list->count) {
ret = kccsrv_add_connection(s, dsa);
if (ret == LDB_SUCCESS) {
added++;
diff --git a/source4/dsdb/kcc/kcc_deleted.c b/source4/dsdb/kcc/kcc_deleted.c
new file mode 100644
index 0000000000..d19ac0cac2
--- /dev/null
+++ b/source4/dsdb/kcc/kcc_deleted.c
@@ -0,0 +1,156 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ handle removal of deleted objects
+
+ Copyright (C) 2009 Andrew Tridgell
+
+ 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/events/events.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "smbd/service.h"
+#include "lib/messaging/irpc.h"
+#include "dsdb/kcc/kcc_connection.h"
+#include "dsdb/kcc/kcc_service.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "param/param.h"
+
+/*
+ onelevel search with SHOW_DELETED control
+ */
+static int search_onelevel_with_deleted(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_result **_res,
+ struct ldb_dn *basedn,
+ const char * const *attrs)
+{
+ struct ldb_request *req;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res;
+ int ret;
+
+ 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,
+ LDB_SCOPE_ONELEVEL,
+ NULL,
+ 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_SHOW_DELETED_OID, true, NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ 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;
+}
+
+/*
+ check to see if any deleted objects need scavenging
+ */
+NTSTATUS kccsrv_check_deleted(struct kccsrv_service *s, TALLOC_CTX *mem_ctx)
+{
+ struct kccsrv_partition *part;
+ int ret;
+ uint32_t tombstoneLifetime;
+
+ time_t t = time(NULL);
+ if (t - s->last_deleted_check < lp_parm_int(s->task->lp_ctx, NULL, "kccsrv",
+ "check_deleted_interval", 600)) {
+ return NT_STATUS_OK;
+ }
+ s->last_deleted_check = t;
+
+ ret = dsdb_tombstone_lifetime(s->samdb, &tombstoneLifetime);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1,(__location__ ": Failed to get tombstone lifetime\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ for (part=s->partitions; part; part=part->next) {
+ struct ldb_dn *do_dn;
+ struct ldb_result *res;
+ const char *attrs[] = { "whenChanged", NULL };
+ int i;
+
+ ret = dsdb_get_deleted_objects_dn(s->samdb, mem_ctx, part->dn, &do_dn);
+ if (ret != LDB_SUCCESS) {
+ /* some partitions have no Deleted Objects
+ container */
+ continue;
+ }
+ ret = search_onelevel_with_deleted(s->samdb, do_dn, &res, do_dn, attrs);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1,(__location__ ": Failed to search for deleted objects in %s\n",
+ ldb_dn_get_linearized(do_dn)));
+ talloc_free(do_dn);
+ continue;
+ }
+
+ for (i=0; i<res->count; i++) {
+ const char *tstring;
+ time_t whenChanged = 0;
+
+ tstring = samdb_result_string(res->msgs[i], "whenChanged", NULL);
+ if (tstring) {
+ whenChanged = ldb_string_to_time(tstring);
+ }
+ if (t - whenChanged > tombstoneLifetime*60*60*24) {
+ ret = ldb_delete(s->samdb, res->msgs[i]->dn);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1,(__location__ ": Failed to remove deleted object %s\n",
+ ldb_dn_get_linearized(res->msgs[i]->dn)));
+ } else {
+ DEBUG(4,("Removed deleted object %s\n",
+ ldb_dn_get_linearized(res->msgs[i]->dn)));
+ }
+ }
+ }
+
+ talloc_free(do_dn);
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/dsdb/kcc/kcc_periodic.c b/source4/dsdb/kcc/kcc_periodic.c
index d24e5e90a5..3b0d8a0551 100644
--- a/source4/dsdb/kcc/kcc_periodic.c
+++ b/source4/dsdb/kcc/kcc_periodic.c
@@ -257,5 +257,10 @@ static void kccsrv_periodic_run(struct kccsrv_service *service)
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("kccsrv_simple_update failed - %s\n", nt_errstr(status)));
}
+
+ status = kccsrv_check_deleted(service, mem_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("kccsrv_check_deleted failed - %s\n", nt_errstr(status)));
+ }
talloc_free(mem_ctx);
}
diff --git a/source4/dsdb/kcc/kcc_service.h b/source4/dsdb/kcc/kcc_service.h
index 6a78d37a91..b4ce37bfc5 100644
--- a/source4/dsdb/kcc/kcc_service.h
+++ b/source4/dsdb/kcc/kcc_service.h
@@ -78,6 +78,8 @@ struct kccsrv_service {
/* here we have a reference to the timed event the schedules the periodic stuff */
struct tevent_timer *te;
} periodic;
+
+ time_t last_deleted_check;
};
#include "dsdb/kcc/kcc_service_proto.h"
diff --git a/source4/dsdb/repl/drepl_notify.c b/source4/dsdb/repl/drepl_notify.c
index e8652dcaf1..fe3b2d2497 100644
--- a/source4/dsdb/repl/drepl_notify.c
+++ b/source4/dsdb/repl/drepl_notify.c
@@ -404,7 +404,7 @@ WERROR dreplsrv_notify_schedule(struct dreplsrv_service *service, uint32_t next_
W_ERROR_HAVE_NO_MEMORY(new_te);
tmp_mem = talloc_new(service);
- DEBUG(2,("dreplsrv_notify_schedule(%u) %sscheduled for: %s\n",
+ DEBUG(4,("dreplsrv_notify_schedule(%u) %sscheduled for: %s\n",
next_interval,
(service->notify.te?"re":""),
nt_time_string(tmp_mem, timeval_to_nttime(&next_time))));
diff --git a/source4/dsdb/repl/drepl_partitions.c b/source4/dsdb/repl/drepl_partitions.c
index 85412a793c..5b8227e7de 100644
--- a/source4/dsdb/repl/drepl_partitions.c
+++ b/source4/dsdb/repl/drepl_partitions.c
@@ -39,16 +39,15 @@ WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
struct ldb_dn *basedn;
struct ldb_result *r;
struct ldb_message_element *el;
- static const char *attrs[] = { "namingContexts", NULL };
+ static const char *attrs[] = { "hasMasterNCs", NULL };
uint32_t i;
int ret;
- basedn = ldb_dn_new(s, s->samdb, NULL);
+ basedn = samdb_ntds_settings_dn(s->samdb);
W_ERROR_HAVE_NO_MEMORY(basedn);
ret = ldb_search(s->samdb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
"(objectClass=*)");
- talloc_free(basedn);
if (ret != LDB_SUCCESS) {
return WERR_FOOBAR;
} else if (r->count != 1) {
@@ -56,7 +55,7 @@ WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
return WERR_FOOBAR;
}
- el = ldb_msg_find_element(r->msgs[0], "namingContexts");
+ el = ldb_msg_find_element(r->msgs[0], "hasMasterNCs");
if (!el) {
return WERR_FOOBAR;
}
diff --git a/source4/dsdb/repl/replicated_objects.c b/source4/dsdb/repl/replicated_objects.c
index 1efbd29d93..c72b107b75 100644
--- a/source4/dsdb/repl/replicated_objects.c
+++ b/source4/dsdb/repl/replicated_objects.c
@@ -128,15 +128,6 @@ static WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
}
status = dsdb_attribute_drsuapi_to_ldb(ldb, schema, a, msg->elements, e);
- if (!NT_STATUS_IS_OK(status) && a->value_ctr.num_values == 0) {
- /* w2k8-r2 occasionally sends bogus empty
- attributes with rubbish attribute IDs. The
- only think we can do is discard these */
- DEBUG(0,(__location__ ": Discarding bogus empty DsReplicaAttribute with attid 0x%x\n",
- a->attid));
- ZERO_STRUCTP(e);
- continue;
- }
W_ERROR_NOT_OK_RETURN(status);
m->attid = a->attid;
@@ -157,14 +148,6 @@ static WERROR dsdb_convert_object_ex(struct ldb_context *ldb,
}
}
- /* delete any empty elements */
- for (i=0; i < msg->num_elements; i++) {
- if (msg->elements[i].name == NULL) {
- ldb_msg_remove_element(msg, &msg->elements[i]);
- i--;
- }
- }
-
if (rdn_m) {
struct ldb_message_element *el;
el = ldb_msg_find_element(msg, rdn_attr->lDAPDisplayName);
@@ -319,7 +302,8 @@ WERROR dsdb_extended_replicated_objects_commit(struct ldb_context *ldb,
ret = ldb_transaction_prepare_commit(ldb);
if (ret != LDB_SUCCESS) {
- DEBUG(0,(__location__ " Failed to prepare commit of transaction\n"));
+ DEBUG(0,(__location__ " Failed to prepare commit of transaction: %s\n",
+ ldb_errstring(ldb)));
return WERR_FOOBAR;
}
diff --git a/source4/dsdb/samdb/ldb_modules/config.mk b/source4/dsdb/samdb/ldb_modules/config.mk
index 3bd38606ea..6128dc9d65 100644
--- a/source4/dsdb/samdb/ldb_modules/config.mk
+++ b/source4/dsdb/samdb/ldb_modules/config.mk
@@ -1,7 +1,7 @@
################################################
# Start SUBSYSTEM DSDB_MODULE_HELPERS
[SUBSYSTEM::DSDB_MODULE_HELPERS]
-PRIVATE_DEPENDENCIES = LIBLDB
+PRIVATE_DEPENDENCIES = LIBLDB LIBNDR SAMDB_SCHEMA
DSDB_MODULE_HELPERS_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/util.o
@@ -12,7 +12,7 @@ $(eval $(call proto_header_template,$(dsdbsrcdir)/samdb/ldb_modules/util_proto.h
# Start MODULE ldb_samba_dsdb
[MODULE::ldb_samba_dsdb]
SUBSYSTEM = LIBLDB
-PRIVATE_DEPENDENCIES = SAMDB LIBTALLOC LIBEVENTS LIBNDR
+PRIVATE_DEPENDENCIES = SAMDB LIBTALLOC LIBEVENTS LIBNDR DSDB_MODULE_HELPERS
INIT_FUNCTION = LDB_MODULE(samba_dsdb)
# End MODULE ldb_samba_dsdb
################################################
@@ -119,7 +119,7 @@ ldb_pdc_fsmo_OBJ_FILES = \
# Start MODULE ldb_samldb
[MODULE::ldb_samldb]
SUBSYSTEM = LIBLDB
-PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LDAP_ENCODE SAMDB
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LDAP_ENCODE SAMDB DSDB_MODULE_HELPERS
INIT_FUNCTION = LDB_MODULE(samldb)
#
# End MODULE ldb_samldb
@@ -242,7 +242,7 @@ ldb_extended_dn_out_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/extended_dn_out.
# Start MODULE ldb_extended_dn_store
[MODULE::ldb_extended_dn_store]
SUBSYSTEM = LIBLDB
-PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSAMBA-UTIL SAMDB
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSAMBA-UTIL SAMDB DSDB_MODULE_HELPERS
INIT_FUNCTION = LDB_MODULE(extended_dn_store)
# End MODULE ldb_extended_dn_store
################################################
@@ -301,7 +301,7 @@ ldb_update_keytab_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/update_keytab.o
[MODULE::ldb_objectclass]
INIT_FUNCTION = LDB_MODULE(objectclass)
CFLAGS = -Ilib/ldb/include
-PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSECURITY NDR_SECURITY SAMDB
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSECURITY NDR_SECURITY SAMDB DSDB_MODULE_HELPERS LIBSAMBA-UTIL
SUBSYSTEM = LIBLDB
# End MODULE ldb_objectclass
################################################
@@ -325,7 +325,7 @@ ldb_subtree_rename_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/subtree_rename.o
[MODULE::ldb_subtree_delete]
INIT_FUNCTION = LDB_MODULE(subtree_delete)
CFLAGS = -Ilib/ldb/include
-PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSAMBA-UTIL
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSAMBA-UTIL DSDB_MODULE_HELPERS
SUBSYSTEM = LIBLDB
# End MODULE ldb_subtree_rename
################################################
@@ -385,7 +385,7 @@ ldb_instancetype_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/instancetype.o
[MODULE::ldb_operational]
SUBSYSTEM = LIBLDB
CFLAGS = -Ilib/ldb/include
-PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT LIBSAMBA-UTIL SAMDB_COMMON DSDB_MODULE_HELPERS
INIT_FUNCTION = LDB_MODULE(operational)
# End MODULE ldb_operational
################################################
@@ -397,7 +397,8 @@ ldb_operational_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/operational.o
[MODULE::ldb_descriptor]
INIT_FUNCTION = LDB_MODULE(descriptor)
CFLAGS = -Ilib/ldb/include
-PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSECURITY NDR_SECURITY SAMDB
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSECURITY NDR_SECURITY SAMDB \
+ DSDB_MODULE_HELPERS
SUBSYSTEM = LIBLDB
# End MODULE ldb_descriptor
################################################
diff --git a/source4/dsdb/samdb/ldb_modules/descriptor.c b/source4/dsdb/samdb/ldb_modules/descriptor.c
index d5a5e36e0e..f07743c4a2 100644
--- a/source4/dsdb/samdb/ldb_modules/descriptor.c
+++ b/source4/dsdb/samdb/ldb_modules/descriptor.c
@@ -285,6 +285,14 @@ static DATA_BLOB *get_new_descriptor(struct ldb_module *module,
if (!final_sd) {
return NULL;
}
+
+ if (final_sd->dacl) {
+ final_sd->dacl->revision = SECURITY_ACL_REVISION_ADS;
+ }
+ if (final_sd->sacl) {
+ final_sd->sacl->revision = SECURITY_ACL_REVISION_ADS;
+ }
+
sddl_sd = sddl_encode(mem_ctx, final_sd, domain_sid);
DEBUG(10, ("Object %s created with desriptor %s\n\n", ldb_dn_get_linearized(dn), sddl_sd));
diff --git a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c
index 89ba7bb04b..33931167c5 100644
--- a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c
+++ b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c
@@ -34,6 +34,11 @@
#include "ldb/include/ldb_errors.h"
#include "ldb/include/ldb_module.h"
+/*
+ TODO: if relax is not set then we need to reject the fancy RMD_* and
+ DELETED extended DN codes
+ */
+
/* search */
struct extended_search_context {
struct ldb_module *module;
diff --git a/source4/dsdb/samdb/ldb_modules/linked_attributes.c b/source4/dsdb/samdb/ldb_modules/linked_attributes.c
index f54693d809..4c326bc240 100644
--- a/source4/dsdb/samdb/ldb_modules/linked_attributes.c
+++ b/source4/dsdb/samdb/ldb_modules/linked_attributes.c
@@ -33,1227 +33,155 @@
#include "dlinklist.h"
#include "dsdb/samdb/samdb.h"
#include "librpc/gen_ndr/ndr_misc.h"
+#include "dsdb/samdb/ldb_modules/util.h"
-struct la_private {
- struct la_context *la_list;
-};
-
-struct la_op_store {
- struct la_op_store *next;
- struct la_op_store *prev;
- enum la_op {LA_OP_ADD, LA_OP_DEL} op;
- struct GUID guid;
- char *name;
- char *value;
-};
-
-struct replace_context {
- struct la_context *ac;
- unsigned int num_elements;
- struct ldb_message_element *el;
-};
-
-struct la_context {
- struct la_context *next, *prev;
- const struct dsdb_schema *schema;
- struct ldb_module *module;
- struct ldb_request *req;
- struct ldb_dn *partition_dn;
- struct ldb_dn *add_dn;
- struct ldb_dn *del_dn;
- struct replace_context *rc;
- struct la_op_store *ops;
- struct ldb_extended *op_response;
- struct ldb_control **op_controls;
-};
-
-static struct la_context *linked_attributes_init(struct ldb_module *module,
- struct ldb_request *req)
-{
- struct ldb_context *ldb;
- struct la_context *ac;
- const struct ldb_control *partition_ctrl;
-
- ldb = ldb_module_get_ctx(module);
-
- ac = talloc_zero(req, struct la_context);
- if (ac == NULL) {
- ldb_oom(ldb);
- return NULL;
- }
-
- ac->schema = dsdb_get_schema(ldb);
- ac->module = module;
- ac->req = req;
-
- /* remember the partition DN that came in, if given */
- partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
- if (partition_ctrl) {
- const struct dsdb_control_current_partition *partition;
- partition = talloc_get_type(partition_ctrl->data,
- struct dsdb_control_current_partition);
- SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
-
- ac->partition_dn = ldb_dn_copy(ac, partition->dn);
- }
-
- return ac;
-}
-/*
- turn a DN into a GUID
- */
-static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
+static int linked_attributes_fix_links(struct ldb_module *module,
+ struct ldb_dn *old_dn, struct ldb_dn *new_dn,
+ struct ldb_message_element *el, struct dsdb_schema *schema,
+ const struct dsdb_attribute *schema_attr)
{
- int ret;
- NTSTATUS status;
+ int i;
+ TALLOC_CTX *tmp_ctx = talloc_new(module);
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ const struct dsdb_attribute *target;
+ const char *attrs[2];
- status = dsdb_get_extended_dn_guid(dn, guid);
- if (NT_STATUS_IS_OK(status)) {
+ target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
+ if (target == NULL) {
+ /* there is no counterpart link to change */
return LDB_SUCCESS;
}
- if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
- DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
- ldb_dn_get_linearized(dn)));
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid);
- if (ret != LDB_SUCCESS) {
- DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
- ldb_dn_get_linearized(dn)));
- return ret;
- }
- return LDB_SUCCESS;
-}
-
-
-/* Common routine to handle reading the attributes and creating a
- * series of modify requests */
-static int la_store_op(struct la_context *ac,
- enum la_op op, struct ldb_val *dn,
- const char *name)
-{
- struct ldb_context *ldb;
- struct la_op_store *os;
- struct ldb_dn *op_dn;
- int ret;
-
- ldb = ldb_module_get_ctx(ac->module);
-
- op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
- if (!op_dn) {
- ldb_asprintf_errstring(ldb,
- "could not parse attribute as a DN");
- return LDB_ERR_INVALID_DN_SYNTAX;
- }
-
- os = talloc_zero(ac, struct la_op_store);
- if (!os) {
- ldb_oom(ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- os->op = op;
-
- ret = la_guid_from_dn(ac, op_dn, &os->guid);
- if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
- /* we are deleting an object, and we've found it has a
- * forward link to a target that no longer
- * exists. This is not an error in the delete, and we
- * should just not do the deferred delete of the
- * target attribute
- */
- talloc_free(os);
- return LDB_SUCCESS;
- }
- if (ret != LDB_SUCCESS) {
- return ret;
- }
-
- os->name = talloc_strdup(os, name);
- if (!os->name) {
- ldb_oom(ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- /* Do deletes before adds */
- if (op == LA_OP_ADD) {
- DLIST_ADD_END(ac->ops, os, struct la_op_store *);
- } else {
- /* By adding to the head of the list, we do deletes before
- * adds when processing a replace */
- DLIST_ADD(ac->ops, os);
- }
-
- return LDB_SUCCESS;
-}
-
-static int la_op_search_callback(struct ldb_request *req,
- struct ldb_reply *ares);
-static int la_queue_mod_request(struct la_context *ac);
-static int la_down_req(struct la_context *ac);
-
-
-
-/* add */
-static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
-{
- struct ldb_context *ldb;
- const struct dsdb_attribute *target_attr;
- struct la_context *ac;
- const char *attr_name;
- int ret;
- int i, j;
-
- ldb = ldb_module_get_ctx(module);
-
- if (ldb_dn_is_special(req->op.add.message->dn)) {
- /* do not manipulate our control entries */
- return ldb_next_request(module, req);
- }
-
- ac = linked_attributes_init(module, req);
- if (!ac) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
+ attrs[0] = target->lDAPDisplayName;
+ attrs[1] = NULL;
- if (!ac->schema) {
- /* without schema, this doesn't make any sense */
- talloc_free(ac);
- return ldb_next_request(module, req);
- }
+ for (i=0; i<el->num_values; i++) {
+ struct dsdb_dn *dsdb_dn;
+ int ret, j;
+ struct ldb_result *res;
+ struct ldb_message *msg;
+ struct ldb_message_element *el2;
- /* Need to ensure we only have forward links being specified */
- for (i=0; i < req->op.add.message->num_elements; i++) {
- const struct ldb_message_element *el = &req->op.add.message->elements[i];
- const struct dsdb_attribute *schema_attr
- = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
- if (!schema_attr) {
- ldb_asprintf_errstring(ldb,
- "attribute %s is not a valid attribute in schema", el->name);
- return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
+ if (dsdb_dn == NULL) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_INVALID_DN_SYNTAX;
}
- /* We have a valid attribute, now find out if it is linked */
- if (schema_attr->linkID == 0) {
- continue;
- }
-
- if ((schema_attr->linkID & 1) == 1) {
- /* Odd is for the target. Illegal to modify */
- ldb_asprintf_errstring(ldb,
- "attribute %s must not be modified directly, it is a linked attribute", el->name);
- return LDB_ERR_UNWILLING_TO_PERFORM;
- }
-
- /* Even link IDs are for the originating attribute */
- target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
- if (!target_attr) {
- /*
- * windows 2003 has a broken schema where
- * the definition of msDS-IsDomainFor
- * is missing (which is supposed to be
- * the backlink of the msDS-HasDomainNCs
- * attribute
- */
- continue;
- }
-
- attr_name = target_attr->lDAPDisplayName;
- for (j = 0; j < el->num_values; j++) {
- ret = la_store_op(ac, LA_OP_ADD,
- &el->values[j],
- attr_name);
- if (ret != LDB_SUCCESS) {
- return ret;
- }
+ ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
+ attrs,
+ DSDB_SEARCH_SHOW_DELETED |
+ DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
+ DSDB_SEARCH_REVEAL_INTERNALS);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
+ el->name, target->lDAPDisplayName,
+ ldb_dn_get_linearized(old_dn),
+ ldb_dn_get_linearized(dsdb_dn->dn),
+ ldb_errstring(ldb));
+ talloc_free(tmp_ctx);
+ return ret;
}
- }
-
- /* if no linked attributes are present continue */
- if (ac->ops == NULL) {
- /* nothing to do for this module, proceed */
- talloc_free(ac);
- return ldb_next_request(module, req);
- }
-
- /* start with the original request */
- return la_down_req(ac);
-}
-
-/* For a delete or rename, we need to find out what linked attributes
- * are currently on this DN, and then deal with them. This is the
- * callback to the base search */
+ msg = res->msgs[0];
-static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
-{
- struct ldb_context *ldb;
- const struct dsdb_attribute *schema_attr;
- const struct dsdb_attribute *target_attr;
- struct ldb_message_element *search_el;
- struct replace_context *rc;
- struct la_context *ac;
- const char *attr_name;
- int i, j;
- int ret = LDB_SUCCESS;
-
- ac = talloc_get_type(req->context, struct la_context);
- ldb = ldb_module_get_ctx(ac->module);
- rc = ac->rc;
-
- if (!ares) {
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
- if (ares->error != LDB_SUCCESS) {
- return ldb_module_done(ac->req, ares->controls,
- ares->response, ares->error);
- }
-
- /* Only entries are interesting, and we only want the olddn */
- switch (ares->type) {
- case LDB_REPLY_ENTRY:
-
- if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
- ldb_asprintf_errstring(ldb,
- "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn));
- /* Guh? We only asked for this DN */
- talloc_free(ares);
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
+ if (msg->num_elements != 1 ||
+ ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
+ ldb_set_errstring(ldb, "Bad msg elements in linked_attributes_fix_links");
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
}
+ el2 = &msg->elements[0];
- ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
-
- /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
- for (i = 0; rc && i < rc->num_elements; i++) {
-
- schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
- if (!schema_attr) {
- ldb_asprintf_errstring(ldb,
- "attribute %s is not a valid attribute in schema",
- rc->el[i].name);
- talloc_free(ares);
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OBJECT_CLASS_VIOLATION);
- }
-
- search_el = ldb_msg_find_element(ares->message,
- rc->el[i].name);
+ el2->flags = LDB_FLAG_MOD_REPLACE;
- /* See if this element already exists */
- /* otherwise just ignore as
- * the add has already been scheduled */
- if ( ! search_el) {
- continue;
+ /* find our DN in the values */
+ for (j=0; j<el2->num_values; j++) {
+ struct dsdb_dn *dsdb_dn2;
+ dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
+ if (dsdb_dn2 == NULL) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_INVALID_DN_SYNTAX;
}
-
- target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
- if (!target_attr) {
- /*
- * windows 2003 has a broken schema where
- * the definition of msDS-IsDomainFor
- * is missing (which is supposed to be
- * the backlink of the msDS-HasDomainNCs
- * attribute
- */
+ if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
continue;
}
- attr_name = target_attr->lDAPDisplayName;
-
- /* Now we know what was there, we can remove it for the re-add */
- for (j = 0; j < search_el->num_values; j++) {
- ret = la_store_op(ac, LA_OP_DEL,
- &search_el->values[j],
- attr_name);
- if (ret != LDB_SUCCESS) {
- talloc_free(ares);
- return ldb_module_done(ac->req,
- NULL, NULL, ret);
- }
- }
- }
-
- break;
-
- case LDB_REPLY_REFERRAL:
- /* ignore */
- break;
-
- case LDB_REPLY_DONE:
-
- talloc_free(ares);
-
- if (ac->req->operation == LDB_ADD) {
- /* Start the modifies to the backlinks */
- ret = la_queue_mod_request(ac);
-
- if (ret != LDB_SUCCESS) {
- return ldb_module_done(ac->req, NULL, NULL,
- ret);
- }
- } else {
- /* Start with the original request */
- ret = la_down_req(ac);
+ ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
if (ret != LDB_SUCCESS) {
- return ldb_module_done(ac->req, NULL, NULL, ret);
- }
- }
- return LDB_SUCCESS;
- }
-
- talloc_free(ares);
- return ret;
-}
-
-
-/* modify */
-static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
-{
- /* Look over list of modifications */
- /* Find if any are for linked attributes */
- /* Determine the effect of the modification */
- /* Apply the modify to the linked entry */
-
- struct ldb_context *ldb;
- int i, j;
- struct la_context *ac;
- struct ldb_request *search_req;
- const char **attrs;
-
- int ret;
-
- ldb = ldb_module_get_ctx(module);
-
- if (ldb_dn_is_special(req->op.mod.message->dn)) {
- /* do not manipulate our control entries */
- return ldb_next_request(module, req);
- }
-
- ac = linked_attributes_init(module, req);
- if (!ac) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- if (!ac->schema) {
- /* without schema, this doesn't make any sense */
- return ldb_next_request(module, req);
- }
-
- ac->rc = talloc_zero(ac, struct replace_context);
- if (!ac->rc) {
- ldb_oom(ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- for (i=0; i < req->op.mod.message->num_elements; i++) {
- bool store_el = false;
- const char *attr_name;
- const struct dsdb_attribute *target_attr;
- const struct ldb_message_element *el = &req->op.mod.message->elements[i];
- const struct dsdb_attribute *schema_attr
- = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
- if (!schema_attr) {
- ldb_asprintf_errstring(ldb,
- "attribute %s is not a valid attribute in schema", el->name);
- return LDB_ERR_OBJECT_CLASS_VIOLATION;
- }
- /* We have a valid attribute, now find out if it is linked */
- if (schema_attr->linkID == 0) {
- continue;
- }
-
- if ((schema_attr->linkID & 1) == 1) {
- /* Odd is for the target. Illegal to modify */
- ldb_asprintf_errstring(ldb,
- "attribute %s must not be modified directly, it is a linked attribute", el->name);
- return LDB_ERR_UNWILLING_TO_PERFORM;
- }
-
- /* Even link IDs are for the originating attribute */
-
- /* Now find the target attribute */
- target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
- if (!target_attr) {
- /*
- * windows 2003 has a broken schema where
- * the definition of msDS-IsDomainFor
- * is missing (which is supposed to be
- * the backlink of the msDS-HasDomainNCs
- * attribute
- */
- continue;
- }
-
- attr_name = target_attr->lDAPDisplayName;
-
- switch (el->flags & LDB_FLAG_MOD_MASK) {
- case LDB_FLAG_MOD_REPLACE:
- /* treat as just a normal add the delete part is handled by the callback */
- store_el = true;
-
- /* break intentionally missing */
-
- case LDB_FLAG_MOD_ADD:
-
- /* For each value being added, we need to setup the adds */
- for (j = 0; j < el->num_values; j++) {
- ret = la_store_op(ac, LA_OP_ADD,
- &el->values[j],
- attr_name);
- if (ret != LDB_SUCCESS) {
- return ret;
- }
- }
- break;
-
- case LDB_FLAG_MOD_DELETE:
-
- if (el->num_values) {
- /* For each value being deleted, we need to setup the delete */
- for (j = 0; j < el->num_values; j++) {
- ret = la_store_op(ac, LA_OP_DEL,
- &el->values[j],
- attr_name);
- if (ret != LDB_SUCCESS) {
- return ret;
- }
- }
- } else {
- /* Flag that there was a DELETE
- * without a value specified, so we
- * need to look for the old value */
- store_el = true;
+ talloc_free(tmp_ctx);
+ return ret;
}
- break;
+ el2->values[j] = data_blob_string_const(
+ dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
}
- if (store_el) {
- struct ldb_message_element *search_el;
-
- search_el = talloc_realloc(ac->rc, ac->rc->el,
- struct ldb_message_element,
- ac->rc->num_elements +1);
- if (!search_el) {
- ldb_oom(ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- ac->rc->el = search_el;
-
- ac->rc->el[ac->rc->num_elements] = *el;
- ac->rc->num_elements++;
- }
- }
-
- if (ac->ops || ac->rc->el) {
- /* both replace and delete without values are handled in the callback
- * after the search on the entry to be modified is performed */
-
- attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
- if (!attrs) {
- ldb_oom(ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
- attrs[i] = ac->rc->el[i].name;
+ ret = dsdb_check_single_valued_link(target, el2);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
}
- attrs[i] = NULL;
-
- /* The callback does all the hard work here */
- ret = ldb_build_search_req(&search_req, ldb, ac,
- req->op.mod.message->dn,
- LDB_SCOPE_BASE,
- "(objectClass=*)", attrs,
- NULL,
- ac, la_mod_search_callback,
- req);
- /* We need to figure out our own extended DN, to fill in as the backlink target */
- if (ret == LDB_SUCCESS) {
- ret = ldb_request_add_control(search_req,
- LDB_CONTROL_EXTENDED_DN_OID,
- false, NULL);
- }
- if (ret == LDB_SUCCESS) {
- talloc_steal(search_req, attrs);
-
- ret = ldb_next_request(module, search_req);
+ ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
+ el->name, target->lDAPDisplayName,
+ ldb_dn_get_linearized(old_dn),
+ ldb_dn_get_linearized(dsdb_dn->dn),
+ ldb_errstring(ldb));
+ talloc_free(tmp_ctx);
+ return ret;
}
-
- } else {
- /* nothing to do for this module, proceed */
- talloc_free(ac);
- ret = ldb_next_request(module, req);
}
- return ret;
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
}
-/* delete */
-static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req)
-{
- struct ldb_context *ldb;
- struct ldb_request *search_req;
- struct la_context *ac;
- const char **attrs;
- WERROR werr;
- int ret;
-
- /* This gets complex: We need to:
- - Do a search for the entry
- - Wait for these result to appear
- - In the callback for the result, issue a modify
- request based on the linked attributes found
- - Wait for each modify result
- - Regain our sainity
- */
-
- ldb = ldb_module_get_ctx(module);
-
- ac = linked_attributes_init(module, req);
- if (!ac) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- if (!ac->schema) {
- /* without schema, this doesn't make any sense */
- return ldb_next_request(module, req);
- }
-
- werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
- if (!W_ERROR_IS_OK(werr)) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- ret = ldb_build_search_req(&search_req, ldb, req,
- req->op.del.dn, LDB_SCOPE_BASE,
- "(objectClass=*)", attrs,
- NULL,
- ac, la_op_search_callback,
- req);
-
- if (ret != LDB_SUCCESS) {
- return ret;
- }
-
- talloc_steal(search_req, attrs);
-
- return ldb_next_request(module, search_req);
-}
/* rename */
static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
{
- struct la_context *ac;
-
- /* This gets complex: We need to:
- - Do a search for the entry
- - Wait for these result to appear
- - In the callback for the result, issue a modify
- request based on the linked attributes found
- - Wait for each modify result
- - Regain our sainity
+ struct ldb_result *res;
+ struct ldb_message *msg;
+ int ret, i;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct dsdb_schema *schema = dsdb_get_schema(ldb);
+ /*
+ - load the current msg
+ - find any linked attributes
+ - if its a link then find the target object
+ - modify the target linked attributes with the new DN
*/
-
- ac = linked_attributes_init(module, req);
- if (!ac) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- if (!ac->schema) {
- /* without schema, this doesn't make any sense */
- return ldb_next_request(module, req);
- }
-
- /* start with the original request */
- return la_down_req(ac);
-}
-
-
-static int la_op_search_callback(struct ldb_request *req,
- struct ldb_reply *ares)
-{
- struct ldb_context *ldb;
- struct la_context *ac;
- const struct dsdb_attribute *schema_attr;
- const struct dsdb_attribute *target_attr;
- const struct ldb_message_element *el;
- const char *attr_name;
- int i, j;
- int ret;
-
- ac = talloc_get_type(req->context, struct la_context);
- ldb = ldb_module_get_ctx(ac->module);
-
- if (!ares) {
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
- if (ares->error != LDB_SUCCESS) {
- return ldb_module_done(ac->req, ares->controls,
- ares->response, ares->error);
- }
-
- /* Only entries are interesting, and we only want the olddn */
- switch (ares->type) {
- case LDB_REPLY_ENTRY:
- ret = ldb_dn_compare(ares->message->dn, req->op.search.base);
- if (ret != 0) {
- /* Guh? We only asked for this DN */
- talloc_free(ares);
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
- if (ares->message->num_elements == 0) {
- /* only bother at all if there were some
- * linked attributes found */
- talloc_free(ares);
- return LDB_SUCCESS;
- }
-
- switch (ac->req->operation) {
- case LDB_DELETE:
- ac->del_dn = talloc_steal(ac, ares->message->dn);
- break;
- case LDB_RENAME:
- ac->add_dn = talloc_steal(ac, ares->message->dn);
- ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn);
- break;
- default:
- talloc_free(ares);
- ldb_set_errstring(ldb,
- "operations must be delete or rename");
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
-
- for (i = 0; i < ares->message->num_elements; i++) {
- el = &ares->message->elements[i];
-
- schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
- if (!schema_attr) {
- ldb_asprintf_errstring(ldb,
- "attribute %s is not a valid attribute"
- " in schema", el->name);
- talloc_free(ares);
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OBJECT_CLASS_VIOLATION);
- }
-
- /* Valid attribute, now find out if it is linked */
- if (schema_attr->linkID == 0) {
- /* Not a linked attribute, skip */
- continue;
- }
-
- if ((schema_attr->linkID & 1) == 0) {
- /* Odd is for the target. */
- target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
- if (!target_attr) {
- continue;
- }
- attr_name = target_attr->lDAPDisplayName;
- } else {
- target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
- if (!target_attr) {
- continue;
- }
- attr_name = target_attr->lDAPDisplayName;
- }
- for (j = 0; j < el->num_values; j++) {
- ret = la_store_op(ac, LA_OP_DEL,
- &el->values[j],
- attr_name);
-
- /* for renames, ensure we add it back */
- if (ret == LDB_SUCCESS
- && ac->req->operation == LDB_RENAME) {
- ret = la_store_op(ac, LA_OP_ADD,
- &el->values[j],
- attr_name);
- }
- if (ret != LDB_SUCCESS) {
- talloc_free(ares);
- return ldb_module_done(ac->req,
- NULL, NULL, ret);
- }
- }
- }
-
- break;
-
- case LDB_REPLY_REFERRAL:
- /* ignore */
- break;
-
- case LDB_REPLY_DONE:
-
- talloc_free(ares);
-
-
- switch (ac->req->operation) {
- case LDB_DELETE:
- /* start the mod requests chain */
- ret = la_down_req(ac);
- if (ret != LDB_SUCCESS) {
- return ldb_module_done(ac->req, NULL, NULL, ret);
- }
- return ret;
-
- case LDB_RENAME:
- /* start the mod requests chain */
- ret = la_queue_mod_request(ac);
- if (ret != LDB_SUCCESS) {
- return ldb_module_done(ac->req, NULL, NULL,
- ret);
- }
- return ret;
-
- default:
- talloc_free(ares);
- ldb_set_errstring(ldb,
- "operations must be delete or rename");
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
- }
-
- talloc_free(ares);
- return LDB_SUCCESS;
-}
-
-/* queue a linked attributes modify request in the la_private
- structure */
-static int la_queue_mod_request(struct la_context *ac)
-{
- struct la_private *la_private =
- talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
-
- if (la_private == NULL) {
- ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- talloc_steal(la_private, ac);
- DLIST_ADD(la_private->la_list, ac);
-
- return ldb_module_done(ac->req, ac->op_controls,
- ac->op_response, LDB_SUCCESS);
-}
-
-/* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
-static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
-{
- int ret;
- struct la_context *ac;
- struct ldb_context *ldb;
-
- ac = talloc_get_type(req->context, struct la_context);
- ldb = ldb_module_get_ctx(ac->module);
-
- if (!ares) {
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
- if (ares->error != LDB_SUCCESS) {
- return ldb_module_done(ac->req, ares->controls,
- ares->response, ares->error);
- }
-
- if (ares->type != LDB_REPLY_DONE) {
- ldb_set_errstring(ldb,
- "invalid ldb_reply_type in callback");
- talloc_free(ares);
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
-
- ac->op_controls = talloc_steal(ac, ares->controls);
- ac->op_response = talloc_steal(ac, ares->response);
-
- /* If we have modfies to make, this is the time to do them for modify and delete */
- ret = la_queue_mod_request(ac);
-
- if (ret != LDB_SUCCESS) {
- return ldb_module_done(ac->req, NULL, NULL, ret);
- }
- talloc_free(ares);
-
- /* la_queue_mod_request has already sent the callbacks */
- return LDB_SUCCESS;
-
-}
-
-/* Having done the original rename try to fix up all the linked attributes */
-static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
-{
- int ret;
- struct la_context *ac;
- struct ldb_request *search_req;
- const char **attrs;
- WERROR werr;
- struct ldb_context *ldb;
-
- ac = talloc_get_type(req->context, struct la_context);
- ldb = ldb_module_get_ctx(ac->module);
-
- if (!ares) {
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
- if (ares->error != LDB_SUCCESS) {
- return ldb_module_done(ac->req, ares->controls,
- ares->response, ares->error);
- }
-
- if (ares->type != LDB_REPLY_DONE) {
- ldb_set_errstring(ldb,
- "invalid ldb_reply_type in callback");
- talloc_free(ares);
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
-
- werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
- if (!W_ERROR_IS_OK(werr)) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- ret = ldb_build_search_req(&search_req, ldb, req,
- ac->req->op.rename.newdn, LDB_SCOPE_BASE,
- "(objectClass=*)", attrs,
- NULL,
- ac, la_op_search_callback,
- req);
-
- if (ret != LDB_SUCCESS) {
- return ret;
- }
-
- talloc_steal(search_req, attrs);
-
- if (ret == LDB_SUCCESS) {
- ret = ldb_request_add_control(search_req,
- LDB_CONTROL_EXTENDED_DN_OID,
- false, NULL);
- }
- if (ret != LDB_SUCCESS) {
- return ldb_module_done(ac->req, NULL, NULL,
- ret);
- }
-
- ac->op_controls = talloc_steal(ac, ares->controls);
- ac->op_response = talloc_steal(ac, ares->response);
-
- return ldb_next_request(ac->module, search_req);
-}
-
-/* Having done the original add, then try to fix up all the linked attributes
-
- This is done after the add so the links can get the extended DNs correctly.
- */
-static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
-{
- int ret;
- struct la_context *ac;
- struct ldb_context *ldb;
-
- ac = talloc_get_type(req->context, struct la_context);
- ldb = ldb_module_get_ctx(ac->module);
-
- if (!ares) {
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
- if (ares->error != LDB_SUCCESS) {
- return ldb_module_done(ac->req, ares->controls,
- ares->response, ares->error);
- }
-
- if (ares->type != LDB_REPLY_DONE) {
- ldb_set_errstring(ldb,
- "invalid ldb_reply_type in callback");
- talloc_free(ares);
- return ldb_module_done(ac->req, NULL, NULL,
- LDB_ERR_OPERATIONS_ERROR);
- }
-
- if (ac->ops) {
- struct ldb_request *search_req;
- static const char *attrs[] = { NULL };
-
- /* The callback does all the hard work here - we need
- * the objectGUID and SID of the added record */
- ret = ldb_build_search_req(&search_req, ldb, ac,
- ac->req->op.add.message->dn,
- LDB_SCOPE_BASE,
- "(objectClass=*)", attrs,
- NULL,
- ac, la_mod_search_callback,
- ac->req);
-
- if (ret == LDB_SUCCESS) {
- ret = ldb_request_add_control(search_req,
- LDB_CONTROL_EXTENDED_DN_OID,
- false, NULL);
- }
- if (ret != LDB_SUCCESS) {
- return ldb_module_done(ac->req, NULL, NULL,
- ret);
- }
-
- ac->op_controls = talloc_steal(ac, ares->controls);
- ac->op_response = talloc_steal(ac, ares->response);
-
- return ldb_next_request(ac->module, search_req);
-
- } else {
- return ldb_module_done(ac->req, ares->controls,
- ares->response, ares->error);
- }
-}
-
-/* Reconstruct the original request, but pointing at our local callback to finish things off */
-static int la_down_req(struct la_context *ac)
-{
- struct ldb_request *down_req;
- int ret;
- struct ldb_context *ldb;
-
- ldb = ldb_module_get_ctx(ac->module);
-
- switch (ac->req->operation) {
- case LDB_ADD:
- ret = ldb_build_add_req(&down_req, ldb, ac,
- ac->req->op.add.message,
- ac->req->controls,
- ac, la_add_callback,
- ac->req);
- break;
- case LDB_MODIFY:
- ret = ldb_build_mod_req(&down_req, ldb, ac,
- ac->req->op.mod.message,
- ac->req->controls,
- ac, la_mod_del_callback,
- ac->req);
- break;
- case LDB_DELETE:
- ret = ldb_build_del_req(&down_req, ldb, ac,
- ac->req->op.del.dn,
- ac->req->controls,
- ac, la_mod_del_callback,
- ac->req);
- break;
- case LDB_RENAME:
- ret = ldb_build_rename_req(&down_req, ldb, ac,
- ac->req->op.rename.olddn,
- ac->req->op.rename.newdn,
- ac->req->controls,
- ac, la_rename_callback,
- ac->req);
- break;
- default:
- ret = LDB_ERR_OPERATIONS_ERROR;
- }
- if (ret != LDB_SUCCESS) {
- return ret;
- }
-
- return ldb_next_request(ac->module, down_req);
-}
-
-/*
- use the GUID part of an extended DN to find the target DN, in case
- it has moved
- */
-static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
- struct GUID *guid, struct ldb_dn **dn)
-{
- return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, GUID_string(ac, guid), dn);
-}
-
-/* apply one la_context op change */
-static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
-{
- struct ldb_message_element *ret_el;
- struct ldb_request *mod_req;
- struct ldb_message *new_msg;
- struct ldb_context *ldb;
- int ret;
-
- ldb = ldb_module_get_ctx(ac->module);
-
- /* Create the modify request */
- new_msg = ldb_msg_new(ac);
- if (!new_msg) {
- ldb_oom(ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
- if (ret != LDB_SUCCESS) {
- return ret;
- }
-
- if (op->op == LA_OP_ADD) {
- ret = ldb_msg_add_empty(new_msg, op->name,
- LDB_FLAG_MOD_ADD, &ret_el);
- } else {
- ret = ldb_msg_add_empty(new_msg, op->name,
- LDB_FLAG_MOD_DELETE, &ret_el);
- }
- if (ret != LDB_SUCCESS) {
- return ret;
- }
- ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
- if (!ret_el->values) {
- ldb_oom(ldb);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- ret_el->num_values = 1;
- if (op->op == LA_OP_ADD) {
- ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
- } else {
- ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
- }
-
-#if 0
- ldb_debug(ldb, LDB_DEBUG_WARNING,
- "link on %s %s: %s %s\n",
- ldb_dn_get_linearized(new_msg->dn), ret_el->name,
- ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
-#endif
-
- ret = ldb_build_mod_req(&mod_req, ldb, op,
- new_msg,
- NULL,
- NULL,
- ldb_op_default_callback,
- NULL);
+ ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
+ NULL, DSDB_SEARCH_SHOW_DELETED);
if (ret != LDB_SUCCESS) {
return ret;
}
- talloc_steal(mod_req, new_msg);
-
- if (DEBUGLVL(4)) {
- DEBUG(4,("Applying linked attribute change:\n%s\n",
- ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
- }
-
- /* Run the new request */
- ret = ldb_next_request(module, mod_req);
-
- /* we need to wait for this to finish, as we are being called
- from the synchronous end_transaction hook of this module */
- if (ret == LDB_SUCCESS) {
- ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
- }
-
- if (ret != LDB_SUCCESS) {
- ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n",
- ldb_errstring(ldb),
- ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
- }
-
- return ret;
-}
-
-/* apply one set of la_context changes */
-static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
-{
- struct la_op_store *op;
+ msg = res->msgs[0];
- for (op = ac->ops; op; op=op->next) {
- int ret = la_do_op_request(module, ac, op);
- if (ret != LDB_SUCCESS) {
- if (ret != LDB_ERR_NO_SUCH_OBJECT) {
- return ret;
- }
+ for (i=0; i<msg->num_elements; i++) {
+ struct ldb_message_element *el = &msg->elements[i];
+ const struct dsdb_attribute *schema_attr
+ = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
+ if (!schema_attr || schema_attr->linkID == 0) {
+ continue;
}
- }
-
- return LDB_SUCCESS;
-}
-
-
-/*
- we hook into the transaction operations to allow us to
- perform the linked attribute updates at the end of the whole
- transaction. This allows a forward linked attribute to be created
- before the target is created, as long as the target is created
- in the same transaction
- */
-static int linked_attributes_start_transaction(struct ldb_module *module)
-{
- /* create our private structure for this transaction */
- struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
- struct la_private);
- talloc_free(la_private);
- la_private = talloc(module, struct la_private);
- if (la_private == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
- la_private->la_list = NULL;
- ldb_module_set_private(module, la_private);
- return ldb_next_start_trans(module);
-}
-
-/*
- on prepare commit we loop over our queued la_context structures
- and apply each of them
- */
-static int linked_attributes_prepare_commit(struct ldb_module *module)
-{
- struct la_private *la_private =
- talloc_get_type(ldb_module_get_private(module), struct la_private);
- struct la_context *ac;
-
- if (!la_private) {
- /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
- return ldb_next_prepare_commit(module);
- }
- /* walk the list backwards, to do the first entry first, as we
- * added the entries with DLIST_ADD() which puts them at the
- * start of the list */
- for (ac = la_private->la_list; ac && ac->next; ac=ac->next) ;
-
- for (; ac; ac=ac->prev) {
- int ret;
- ac->req = NULL;
- ret = la_do_mod_request(module, ac);
+ ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
+ schema, schema_attr);
if (ret != LDB_SUCCESS) {
- DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
- talloc_free(la_private);
- ldb_module_set_private(module, NULL);
+ talloc_free(res);
return ret;
}
}
- talloc_free(la_private);
- ldb_module_set_private(module, NULL);
+ talloc_free(res);
- return ldb_next_prepare_commit(module);
-}
-
-static int linked_attributes_del_transaction(struct ldb_module *module)
-{
- struct la_private *la_private =
- talloc_get_type(ldb_module_get_private(module), struct la_private);
- talloc_free(la_private);
- ldb_module_set_private(module, NULL);
- return ldb_next_del_trans(module);
+ return ldb_next_request(module, req);
}
_PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
.name = "linked_attributes",
- .add = linked_attributes_add,
- .modify = linked_attributes_modify,
- .del = linked_attributes_del,
- .rename = linked_attributes_rename,
- .start_transaction = linked_attributes_start_transaction,
- .prepare_commit = linked_attributes_prepare_commit,
- .del_transaction = linked_attributes_del_transaction,
+ .rename = linked_attributes_rename,
};
diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
index 3ae165c6da..890eb91d6d 100644
--- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
@@ -46,10 +46,16 @@
#include "libcli/security/dom_sid.h"
#include "lib/util/dlinklist.h"
#include "dsdb/samdb/ldb_modules/util.h"
+#include "lib/util/binsearch.h"
+#include "libcli/security/security.h"
+
+#define W2K3_LINKED_ATTRIBUTES 1
struct replmd_private {
TALLOC_CTX *la_ctx;
struct la_entry *la_list;
+ TALLOC_CTX *bl_ctx;
+ struct la_backlink *la_backlinks;
struct nc_entry {
struct nc_entry *prev, *next;
struct ldb_dn *dn;
@@ -84,6 +90,7 @@ struct replmd_replicated_request {
static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
+
/*
initialise the module
allocate the private structure and build the list
@@ -104,6 +111,176 @@ static int replmd_init(struct ldb_module *module)
return ldb_next_init(module);
}
+/*
+ cleanup our per-transaction contexts
+ */
+static void replmd_txn_cleanup(struct replmd_private *replmd_private)
+{
+ talloc_free(replmd_private->la_ctx);
+ replmd_private->la_list = NULL;
+ replmd_private->la_ctx = NULL;
+
+ talloc_free(replmd_private->bl_ctx);
+ replmd_private->la_backlinks = NULL;
+ replmd_private->bl_ctx = NULL;
+}
+
+
+struct la_backlink {
+ struct la_backlink *next, *prev;
+ const char *attr_name;
+ struct GUID forward_guid, target_guid;
+ bool active;
+};
+
+/*
+ process a backlinks we accumulated during a transaction, adding and
+ deleting the backlinks from the target objects
+ */
+static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl)
+{
+ struct ldb_dn *target_dn, *source_dn;
+ int ret;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct ldb_message *msg;
+ TALLOC_CTX *tmp_ctx = talloc_new(bl);
+ char *dn_string;
+
+ /*
+ - find DN of target
+ - find DN of source
+ - construct ldb_message
+ - either an add or a delete
+ */
+ ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Failed to find target DN for linked attribute with GUID %s\n",
+ GUID_string(bl, &bl->target_guid));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
+ GUID_string(bl, &bl->forward_guid));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* construct a ldb_message for adding/deleting the backlink */
+ msg->dn = target_dn;
+ dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
+ if (!dn_string) {
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
+
+ ret = dsdb_module_modify(module, msg, 0);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
+ bl->active?"add":"remove",
+ ldb_dn_get_linearized(source_dn),
+ ldb_dn_get_linearized(target_dn),
+ ldb_errstring(ldb));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
+ add a backlink to the list of backlinks to add/delete in the prepare
+ commit
+ */
+static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
+ struct GUID *forward_guid, struct GUID *target_guid,
+ bool active, const struct dsdb_attribute *schema_attr, bool immediate)
+{
+ const struct dsdb_attribute *target_attr;
+ struct la_backlink *bl;
+ struct replmd_private *replmd_private =
+ talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
+
+ target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
+ if (!target_attr) {
+ /*
+ * windows 2003 has a broken schema where the
+ * definition of msDS-IsDomainFor is missing (which is
+ * supposed to be the backlink of the
+ * msDS-HasDomainNCs attribute
+ */
+ return LDB_SUCCESS;
+ }
+
+ /* see if its already in the list */
+ for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
+ if (GUID_equal(forward_guid, &bl->forward_guid) &&
+ GUID_equal(target_guid, &bl->target_guid) &&
+ (target_attr->lDAPDisplayName == bl->attr_name ||
+ strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
+ break;
+ }
+ }
+
+ if (bl) {
+ /* we found an existing one */
+ if (bl->active == active) {
+ return LDB_SUCCESS;
+ }
+ DLIST_REMOVE(replmd_private->la_backlinks, bl);
+ talloc_free(bl);
+ return LDB_SUCCESS;
+ }
+
+ if (replmd_private->bl_ctx == NULL) {
+ replmd_private->bl_ctx = talloc_new(replmd_private);
+ if (replmd_private->bl_ctx == NULL) {
+ ldb_module_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ /* its a new one */
+ bl = talloc(replmd_private->bl_ctx, struct la_backlink);
+ if (bl == NULL) {
+ ldb_module_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ bl->attr_name = target_attr->lDAPDisplayName;
+ bl->forward_guid = *forward_guid;
+ bl->target_guid = *target_guid;
+ bl->active = active;
+
+ /* the caller may ask for this backlink to be processed
+ immediately */
+ if (immediate) {
+ int ret = replmd_process_backlink(module, bl);
+ talloc_free(bl);
+ return ret;
+ }
+
+ DLIST_ADD(replmd_private->la_backlinks, bl);
+
+ return LDB_SUCCESS;
+}
+
/*
* Callback for most write operations in this module:
@@ -142,6 +319,7 @@ static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
}
if (!partition_ctrl) {
+ ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
return ldb_module_done(ac->req, NULL,
NULL, LDB_ERR_OPERATIONS_ERROR);
}
@@ -336,7 +514,7 @@ static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMeta
return -1;
}
- return m1->attid - m2->attid;
+ return m1->attid > m2->attid ? 1 : -1;
}
static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
@@ -390,8 +568,10 @@ static int replmd_ldb_message_element_attid_sort(const struct ldb_message_elemen
if (!a1 || !a2) {
return strcasecmp(e1->name, e2->name);
}
-
- return a1->attributeID_id - a2->attributeID_id;
+ if (a1->attributeID_id == a2->attributeID_id) {
+ return 0;
+ }
+ return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
}
static void replmd_ldb_message_sort(struct ldb_message *msg,
@@ -401,6 +581,73 @@ static void replmd_ldb_message_sort(struct ldb_message *msg,
discard_const_p(void, schema), (ldb_qsort_cmp_fn_t)replmd_ldb_message_element_attid_sort);
}
+static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
+ const struct GUID *invocation_id, uint64_t seq_num,
+ uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
+
+
+/*
+ fix up linked attributes in replmd_add.
+ This involves setting up the right meta-data in extended DN
+ components, and creating backlinks to the object
+ */
+static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
+ uint64_t seq_num, const struct GUID *invocationId, time_t t,
+ struct GUID *guid, const struct dsdb_attribute *sa)
+{
+ int i;
+ TALLOC_CTX *tmp_ctx = talloc_new(el->values);
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct dsdb_schema *schema = dsdb_get_schema(ldb);
+ NTTIME now;
+
+ unix_to_nt_time(&now, t);
+
+ for (i=0; i<el->num_values; i++) {
+ struct ldb_val *v = &el->values[i];
+ struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
+ struct GUID target_guid;
+ NTSTATUS status;
+ int ret;
+
+ /* note that the DN already has the extended
+ components from the extended_dn_store module */
+ status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
+ if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
+ ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+
+ ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
+ seq_num, seq_num, now, 0, false);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+}
+
+
+/*
+ intercept add requests
+ */
static int replmd_add(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_context *ldb;
@@ -557,6 +804,19 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
continue;
}
+#if W2K3_LINKED_ATTRIBUTES
+ if (sa->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
+ ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ac);
+ return ret;
+ }
+ /* linked attributes are not stored in
+ replPropertyMetaData in FL above w2k */
+ continue;
+ }
+#endif
+
m->attid = sa->attributeID_id;
m->version = 1;
m->originating_change_time = now;
@@ -678,6 +938,20 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
for (i=0; i<omd->ctr.ctr1.count; i++) {
if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
}
+
+#if W2K3_LINKED_ATTRIBUTES
+ if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
+ /* linked attributes are not stored in
+ replPropertyMetaData in FL above w2k, but we do
+ raise the seqnum for the object */
+ if (*seq_num == 0 &&
+ ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
+ }
+#endif
+
if (i == omd->ctr.ctr1.count) {
/* we need to add a new one */
omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
@@ -719,13 +993,13 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
*/
static int replmd_update_rpmd(struct ldb_module *module,
const struct dsdb_schema *schema,
- struct ldb_message *msg, uint64_t *seq_num)
+ struct ldb_message *msg, uint64_t *seq_num,
+ time_t t)
{
const struct ldb_val *omd_value;
enum ndr_err_code ndr_err;
struct replPropertyMetaDataBlob omd;
int i;
- time_t t = time(NULL);
NTTIME now;
const struct GUID *our_invocation_id;
int ret;
@@ -828,13 +1102,822 @@ static int replmd_update_rpmd(struct ldb_module *module,
}
+struct parsed_dn {
+ struct dsdb_dn *dsdb_dn;
+ struct GUID *guid;
+ struct ldb_val *v;
+};
+
+static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
+{
+ return GUID_compare(pdn1->guid, pdn2->guid);
+}
+
+static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid, struct ldb_dn *dn)
+{
+ struct parsed_dn *ret;
+ if (dn && GUID_all_zero(guid)) {
+ /* when updating a link using DRS, we sometimes get a
+ NULL GUID. We then need to try and match by DN */
+ int i;
+ for (i=0; i<count; i++) {
+ if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
+ dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
+ return &pdn[i];
+ }
+ }
+ return NULL;
+ }
+ BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
+ return ret;
+}
+
+/*
+ get a series of message element values as an array of DNs and GUIDs
+ the result is sorted by GUID
+ */
+static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *el, struct parsed_dn **pdn,
+ const char *ldap_oid)
+{
+ int i;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+
+ if (el == NULL) {
+ *pdn = NULL;
+ return LDB_SUCCESS;
+ }
+
+ (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
+ if (!*pdn) {
+ ldb_module_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ struct ldb_val *v = &el->values[i];
+ NTSTATUS status;
+ struct ldb_dn *dn;
+ struct parsed_dn *p;
+
+ p = &(*pdn)[i];
+
+ p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
+ if (p->dsdb_dn == NULL) {
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ dn = p->dsdb_dn->dn;
+
+ p->guid = talloc(*pdn, struct GUID);
+ if (p->guid == NULL) {
+ ldb_module_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ /* we got a DN without a GUID - go find the GUID */
+ int ret = dsdb_module_guid_by_dn(module, dn, p->guid);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
+ ldb_dn_get_linearized(dn));
+ return ret;
+ }
+ ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* keep a pointer to the original ldb_val */
+ p->v = v;
+ }
+
+ qsort(*pdn, el->num_values, sizeof((*pdn)[0]), (comparison_fn_t)parsed_dn_compare);
+
+ return LDB_SUCCESS;
+}
+
+/*
+ build a new extended DN, including all meta data fields
+
+ RMD_FLAGS = DSDB_RMD_FLAG_* bits
+ RMD_ADDTIME = originating_add_time
+ RMD_INVOCID = originating_invocation_id
+ RMD_CHANGETIME = originating_change_time
+ RMD_ORIGINATING_USN = originating_usn
+ RMD_LOCAL_USN = local_usn
+ RMD_VERSION = version
+ */
+static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
+ const struct GUID *invocation_id, uint64_t seq_num,
+ uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
+{
+ struct ldb_dn *dn = dsdb_dn->dn;
+ const char *tstring, *usn_string, *flags_string;
+ struct ldb_val tval;
+ struct ldb_val iid;
+ struct ldb_val usnv, local_usnv;
+ struct ldb_val vers, flagsv;
+ NTSTATUS status;
+ int ret;
+ const char *dnstring;
+ char *vstring;
+ uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
+
+ tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
+ if (!tstring) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ tval = data_blob_string_const(tstring);
+
+ usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
+ if (!usn_string) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ usnv = data_blob_string_const(usn_string);
+
+ usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
+ if (!usn_string) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ local_usnv = data_blob_string_const(usn_string);
+
+ vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
+ if (!vstring) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ vers = data_blob_string_const(vstring);
+
+ status = GUID_to_ndr_blob(invocation_id, dn, &iid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
+ if (!flags_string) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ flagsv = data_blob_string_const(flags_string);
+
+ ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
+ if (ret != LDB_SUCCESS) return ret;
+ ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
+ if (ret != LDB_SUCCESS) return ret;
+ ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
+ if (ret != LDB_SUCCESS) return ret;
+ ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
+ if (ret != LDB_SUCCESS) return ret;
+ ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
+ if (ret != LDB_SUCCESS) return ret;
+ ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
+ if (ret != LDB_SUCCESS) return ret;
+ ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
+ if (ret != LDB_SUCCESS) return ret;
+
+ dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
+ if (dnstring == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ *v = data_blob_string_const(dnstring);
+
+ return LDB_SUCCESS;
+}
+
+static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
+ struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
+ uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
+ uint32_t version, bool deleted);
+
+/*
+ check if any links need upgrading from w2k format
+ */
+static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, const struct GUID *invocation_id)
+{
+ int i;
+ for (i=0; i<count; i++) {
+ NTSTATUS status;
+ uint32_t version;
+ int ret;
+
+ status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ continue;
+ }
+
+ /* it's an old one that needs upgrading */
+ ret = replmd_update_la_val(dns, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
+ 1, 1, 0, 0, false);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ return LDB_SUCCESS;
+}
+
+/*
+ update an extended DN, including all meta data fields
+
+ see replmd_build_la_val for value names
+ */
+static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
+ struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
+ uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
+ uint32_t version, bool deleted)
+{
+ struct ldb_dn *dn = dsdb_dn->dn;
+ const char *tstring, *usn_string, *flags_string;
+ struct ldb_val tval;
+ struct ldb_val iid;
+ struct ldb_val usnv, local_usnv;
+ struct ldb_val vers, flagsv;
+ const struct ldb_val *old_addtime;
+ uint32_t old_version;
+ NTSTATUS status;
+ int ret;
+ const char *dnstring;
+ char *vstring;
+ uint32_t rmd_flags = deleted?DSDB_RMD_FLAG_DELETED:0;
+
+ tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
+ if (!tstring) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ tval = data_blob_string_const(tstring);
+
+ usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
+ if (!usn_string) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ usnv = data_blob_string_const(usn_string);
+
+ usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
+ if (!usn_string) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ local_usnv = data_blob_string_const(usn_string);
+
+ status = GUID_to_ndr_blob(invocation_id, dn, &iid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ flags_string = talloc_asprintf(mem_ctx, "%u", rmd_flags);
+ if (!flags_string) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ flagsv = data_blob_string_const(flags_string);
+
+ ret = ldb_dn_set_extended_component(dn, "RMD_FLAGS", &flagsv);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* get the ADDTIME from the original */
+ old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
+ if (old_addtime == NULL) {
+ old_addtime = &tval;
+ }
+ if (dsdb_dn != old_dsdb_dn) {
+ ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
+ if (ret != LDB_SUCCESS) return ret;
+ }
+
+ /* use our invocation id */
+ ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* changetime is the current time */
+ ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* update the USN */
+ ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
+ if (ret != LDB_SUCCESS) return ret;
+
+ ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* increase the version by 1 */
+ status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
+ if (NT_STATUS_IS_OK(status) && old_version >= version) {
+ version = old_version+1;
+ }
+ vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
+ vers = data_blob_string_const(vstring);
+ ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
+ if (ret != LDB_SUCCESS) return ret;
+
+ dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
+ if (dnstring == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ *v = data_blob_string_const(dnstring);
+
+ return LDB_SUCCESS;
+}
+
+/*
+ handle adding a linked attribute
+ */
+static int replmd_modify_la_add(struct ldb_module *module,
+ struct dsdb_schema *schema,
+ struct ldb_message *msg,
+ struct ldb_message_element *el,
+ struct ldb_message_element *old_el,
+ const struct dsdb_attribute *schema_attr,
+ uint64_t seq_num,
+ time_t t,
+ struct GUID *msg_guid)
+{
+ int i;
+ struct parsed_dn *dns, *old_dns;
+ TALLOC_CTX *tmp_ctx = talloc_new(msg);
+ int ret;
+ struct ldb_val *new_values = NULL;
+ unsigned int num_new_values = 0;
+ unsigned old_num_values = old_el?old_el->num_values:0;
+ const struct GUID *invocation_id;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ NTTIME now;
+
+ unix_to_nt_time(&now, t);
+
+ ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ invocation_id = samdb_ntds_invocation_id(ldb);
+ if (!invocation_id) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ /* for each new value, see if it exists already with the same GUID */
+ for (i=0; i<el->num_values; i++) {
+ struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
+ if (p == NULL) {
+ /* this is a new linked attribute value */
+ new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
+ if (new_values == NULL) {
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
+ invocation_id, seq_num, seq_num, now, 0, false);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ num_new_values++;
+ } else {
+ /* this is only allowed if the GUID was
+ previously deleted. */
+ uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
+
+ if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
+ ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
+ el->name, GUID_string(tmp_ctx, p->guid));
+ talloc_free(tmp_ctx);
+ return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+ }
+ ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
+ invocation_id, seq_num, seq_num, now, 0, false);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+
+ ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+
+ /* add the new ones on to the end of the old values, constructing a new el->values */
+ el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
+ struct ldb_val,
+ old_num_values+num_new_values);
+ if (el->values == NULL) {
+ ldb_module_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
+ el->num_values = old_num_values + num_new_values;
+
+ talloc_steal(msg->elements, el->values);
+ talloc_steal(el->values, new_values);
+
+ talloc_free(tmp_ctx);
+
+ /* we now tell the backend to replace all existing values
+ with the one we have constructed */
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ return LDB_SUCCESS;
+}
+
+
+/*
+ handle deleting all active linked attributes
+ */
+static int replmd_modify_la_delete(struct ldb_module *module,
+ struct dsdb_schema *schema,
+ struct ldb_message *msg,
+ struct ldb_message_element *el,
+ struct ldb_message_element *old_el,
+ const struct dsdb_attribute *schema_attr,
+ uint64_t seq_num,
+ time_t t,
+ struct GUID *msg_guid)
+{
+ int i;
+ struct parsed_dn *dns, *old_dns;
+ TALLOC_CTX *tmp_ctx = talloc_new(msg);
+ int ret;
+ const struct GUID *invocation_id;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ NTTIME now;
+
+ unix_to_nt_time(&now, t);
+
+ /* check if there is nothing to delete */
+ if ((!old_el || old_el->num_values == 0) &&
+ el->num_values == 0) {
+ return LDB_SUCCESS;
+ }
+
+ if (!old_el || old_el->num_values == 0) {
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+
+ ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ invocation_id = samdb_ntds_invocation_id(ldb);
+ if (!invocation_id) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = replmd_check_upgrade_links(old_dns, old_el->num_values, invocation_id);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ el->values = NULL;
+
+ /* see if we are being asked to delete any links that
+ don't exist or are already deleted */
+ for (i=0; i<el->num_values; i++) {
+ struct parsed_dn *p = &dns[i];
+ struct parsed_dn *p2;
+ uint32_t rmd_flags;
+
+ p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
+ if (!p2) {
+ ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
+ el->name, GUID_string(tmp_ctx, p->guid));
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+ rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
+ if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
+ ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
+ el->name, GUID_string(tmp_ctx, p->guid));
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+ }
+
+ /* for each new value, see if it exists already with the same GUID
+ if it is not already deleted and matches the delete list then delete it
+ */
+ for (i=0; i<old_el->num_values; i++) {
+ struct parsed_dn *p = &old_dns[i];
+ uint32_t rmd_flags;
+
+ if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
+ continue;
+ }
+
+ rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
+ if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
+
+ ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
+ invocation_id, seq_num, seq_num, now, 0, true);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+
+ el->values = talloc_steal(msg->elements, old_el->values);
+ el->num_values = old_el->num_values;
+
+ talloc_free(tmp_ctx);
+
+ /* we now tell the backend to replace all existing values
+ with the one we have constructed */
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ return LDB_SUCCESS;
+}
+
+/*
+ handle replacing a linked attribute
+ */
+static int replmd_modify_la_replace(struct ldb_module *module,
+ struct dsdb_schema *schema,
+ struct ldb_message *msg,
+ struct ldb_message_element *el,
+ struct ldb_message_element *old_el,
+ const struct dsdb_attribute *schema_attr,
+ uint64_t seq_num,
+ time_t t,
+ struct GUID *msg_guid)
+{
+ int i;
+ struct parsed_dn *dns, *old_dns;
+ TALLOC_CTX *tmp_ctx = talloc_new(msg);
+ int ret;
+ const struct GUID *invocation_id;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct ldb_val *new_values = NULL;
+ uint32_t num_new_values = 0;
+ unsigned old_num_values = old_el?old_el->num_values:0;
+ NTTIME now;
+
+ unix_to_nt_time(&now, t);
+
+ /* check if there is nothing to replace */
+ if ((!old_el || old_el->num_values == 0) &&
+ el->num_values == 0) {
+ return LDB_SUCCESS;
+ }
+
+ ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ invocation_id = samdb_ntds_invocation_id(ldb);
+ if (!invocation_id) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ /* mark all the old ones as deleted */
+ for (i=0; i<old_num_values; i++) {
+ struct parsed_dn *old_p = &old_dns[i];
+ struct parsed_dn *p;
+ uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
+
+ if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
+
+ ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
+ if (p) {
+ /* we don't delete it if we are re-adding it */
+ continue;
+ }
+
+ ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
+ invocation_id, seq_num, seq_num, now, 0, true);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+
+ /* for each new value, either update its meta-data, or add it
+ * to old_el
+ */
+ for (i=0; i<el->num_values; i++) {
+ struct parsed_dn *p = &dns[i], *old_p;
+
+ if (old_dns &&
+ (old_p = parsed_dn_find(old_dns,
+ old_num_values, p->guid, NULL)) != NULL) {
+ /* update in place */
+ ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
+ old_p->dsdb_dn, invocation_id,
+ seq_num, seq_num, now, 0, false);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ } else {
+ /* add a new one */
+ new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
+ num_new_values+1);
+ if (new_values == NULL) {
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
+ invocation_id, seq_num, seq_num, now, 0, false);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ num_new_values++;
+ }
+
+ ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+
+ /* add the new values to the end of old_el */
+ if (num_new_values != 0) {
+ el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
+ struct ldb_val, old_num_values+num_new_values);
+ if (el->values == NULL) {
+ ldb_module_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ memcpy(&el->values[old_num_values], &new_values[0],
+ sizeof(struct ldb_val)*num_new_values);
+ el->num_values = old_num_values + num_new_values;
+ talloc_steal(msg->elements, new_values);
+ } else {
+ el->values = old_el->values;
+ el->num_values = old_el->num_values;
+ talloc_steal(msg->elements, el->values);
+ }
+
+ talloc_free(tmp_ctx);
+
+ /* we now tell the backend to replace all existing values
+ with the one we have constructed */
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ return LDB_SUCCESS;
+}
+
+
+/*
+ handle linked attributes in modify requests
+ */
+static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
+ struct ldb_message *msg,
+ uint64_t seq_num, time_t t)
+{
+ struct ldb_result *res;
+ int ret, i;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct ldb_message *old_msg;
+ struct dsdb_schema *schema = dsdb_get_schema(ldb);
+ struct GUID old_guid;
+
+ if (seq_num == 0) {
+ /* there the replmd_update_rpmd code has already
+ * checked and saw that there are no linked
+ * attributes */
+ return LDB_SUCCESS;
+ }
+
+#if !W2K3_LINKED_ATTRIBUTES
+ return LDB_SUCCESS;
+#endif
+
+ if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
+ /* don't do anything special for linked attributes */
+ return LDB_SUCCESS;
+ }
+
+ ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
+ DSDB_SEARCH_SHOW_DELETED |
+ DSDB_SEARCH_REVEAL_INTERNALS |
+ DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ old_msg = res->msgs[0];
+
+ old_guid = samdb_result_guid(old_msg, "objectGUID");
+
+ for (i=0; i<msg->num_elements; i++) {
+ struct ldb_message_element *el = &msg->elements[i];
+ struct ldb_message_element *old_el, *new_el;
+ const struct dsdb_attribute *schema_attr
+ = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
+ if (!schema_attr) {
+ ldb_asprintf_errstring(ldb,
+ "attribute %s is not a valid attribute in schema", el->name);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+ if (schema_attr->linkID == 0) {
+ continue;
+ }
+ if ((schema_attr->linkID & 1) == 1) {
+ /* Odd is for the target. Illegal to modify */
+ ldb_asprintf_errstring(ldb,
+ "attribute %s must not be modified directly, it is a linked attribute", el->name);
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ old_el = ldb_msg_find_element(old_msg, el->name);
+ switch (el->flags & LDB_FLAG_MOD_MASK) {
+ case LDB_FLAG_MOD_REPLACE:
+ ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
+ break;
+ case LDB_FLAG_MOD_DELETE:
+ ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
+ break;
+ case LDB_FLAG_MOD_ADD:
+ ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
+ break;
+ default:
+ ldb_asprintf_errstring(ldb,
+ "invalid flags 0x%x for %s linked attribute",
+ el->flags, el->name);
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (old_el) {
+ ldb_msg_remove_attr(old_msg, el->name);
+ }
+ ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
+ new_el->num_values = el->num_values;
+ new_el->values = talloc_steal(msg->elements, el->values);
+
+ /* TODO: this relises a bit too heavily on the exact
+ behaviour of ldb_msg_find_element and
+ ldb_msg_remove_element */
+ old_el = ldb_msg_find_element(msg, el->name);
+ if (old_el != el) {
+ ldb_msg_remove_element(msg, old_el);
+ i--;
+ }
+ }
+
+ talloc_free(res);
+ return ret;
+}
+
+
+
static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_context *ldb;
struct replmd_replicated_request *ac;
struct ldb_request *down_req;
struct ldb_message *msg;
- struct ldb_result *res;
time_t t = time(NULL);
int ret;
@@ -860,24 +1943,16 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
return LDB_ERR_OPERATIONS_ERROR;
}
- /* TODO:
- * - give an error when a readonly attribute should
- * be modified
- * - merge the changed into the old object
- * if the caller set values to the same value
- * ignore the attribute, return success when no
- * attribute was changed
- */
+ ldb_msg_remove_attr(msg, "whenChanged");
+ ldb_msg_remove_attr(msg, "uSNChanged");
- ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
- DSDB_SEARCH_SHOW_DELETED |
- DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
+ ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t);
if (ret != LDB_SUCCESS) {
talloc_free(ac);
return ret;
}
- ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num);
+ ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t);
if (ret != LDB_SUCCESS) {
talloc_free(ac);
return ret;
@@ -1030,6 +2105,329 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are
return ldb_next_request(ac->module, down_req);
}
+/*
+ remove links from objects that point at this object when an object
+ is deleted
+ */
+static int replmd_delete_remove_link(struct ldb_module *module,
+ struct dsdb_schema *schema,
+ struct ldb_dn *dn,
+ struct ldb_message_element *el,
+ const struct dsdb_attribute *sa)
+{
+ int i;
+ TALLOC_CTX *tmp_ctx = talloc_new(module);
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+
+ for (i=0; i<el->num_values; i++) {
+ struct dsdb_dn *dsdb_dn;
+ NTSTATUS status;
+ int ret;
+ struct GUID guid2;
+ struct ldb_message *msg;
+ const struct dsdb_attribute *target_attr;
+ struct ldb_message_element *el2;
+ struct ldb_val dn_val;
+
+ if (dsdb_dn_is_deleted_val(&el->values[i])) {
+ continue;
+ }
+
+ dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
+ if (!dsdb_dn) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* remove the link */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+
+ msg->dn = dsdb_dn->dn;
+
+ target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
+ if (target_attr == NULL) {
+ continue;
+ }
+
+ ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
+ if (ret != LDB_SUCCESS) {
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
+ el2->values = &dn_val;
+ el2->num_values = 1;
+
+ ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+}
+
+
+/*
+ handle update of replication meta data for deletion of objects
+
+ This also handles the mapping of delete to a rename operation
+ to allow deletes to be replicated.
+ */
+static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
+{
+ int ret = LDB_ERR_OTHER;
+ bool retb;
+ struct ldb_dn *old_dn, *new_dn;
+ const char *rdn_name;
+ const struct ldb_val *rdn_value, *new_rdn_value;
+ struct GUID guid;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct dsdb_schema *schema = dsdb_get_schema(ldb);
+ struct ldb_message *msg, *old_msg;
+ struct ldb_message_element *el;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_result *res, *parent_res;
+ const char *preserved_attrs[] = {
+ /* yes, this really is a hard coded list. See MS-ADTS
+ section 3.1.1.5.5.1.1 */
+ "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
+ "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
+ "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
+ "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
+ "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
+ "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
+ "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
+ "whenChanged", NULL};
+ uint32_t el_count = 0;
+ int i;
+
+ if (ldb_dn_is_special(req->op.del.dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ tmp_ctx = talloc_new(ldb);
+
+ old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
+
+ /* we need the complete msg off disk, so we can work out which
+ attributes need to be removed */
+ ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
+ DSDB_SEARCH_SHOW_DELETED |
+ DSDB_SEARCH_REVEAL_INTERNALS |
+ DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ old_msg = res->msgs[0];
+
+ if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
+ struct auth_session_info *session_info =
+ (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
+ if (security_session_user_level(session_info) != SECURITY_SYSTEM) {
+ ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
+ ldb_dn_get_linearized(old_msg->dn));
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ /* it is already deleted - really remove it this time */
+ talloc_free(tmp_ctx);
+ return ldb_next_request(module, req);
+ }
+
+ /* work out where we will be renaming this object to */
+ ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, &new_dn);
+ if (ret != LDB_SUCCESS) {
+ /* this is probably an attempted delete on a partition
+ * that doesn't allow delete operations, such as the
+ * schema partition */
+ ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
+ ldb_dn_get_linearized(old_dn));
+ talloc_free(tmp_ctx);
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ rdn_name = ldb_dn_get_rdn_name(old_dn);
+ rdn_value = ldb_dn_get_rdn_val(old_dn);
+
+ /* get the objects GUID from the search we just did */
+ guid = samdb_result_guid(old_msg, "objectGUID");
+
+ /* Add a formatted child */
+ retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
+ rdn_name,
+ rdn_value->data,
+ GUID_string(tmp_ctx, &guid));
+ if (!retb) {
+ DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
+ ldb_dn_get_linearized(new_dn)));
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ now we need to modify the object in the following ways:
+
+ - add isDeleted=TRUE
+ - update rDN and name, with new rDN
+ - remove linked attributes
+ - remove objectCategory and sAMAccountType
+ - remove attribs not on the preserved list
+ - preserved if in above list, or is rDN
+ - remove all linked attribs from this object
+ - remove all links from other objects to this object
+ - add lastKnownParent
+ - update replPropertyMetaData?
+
+ see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
+ */
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->dn = old_dn;
+
+ ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
+
+ /* we also mark it as recycled, meaning this object can't be
+ recovered (we are stripping its attributes) */
+ if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
+ ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
+ }
+
+ /* we need the storage form of the parent GUID */
+ ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
+ ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
+ DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
+ DSDB_SEARCH_REVEAL_INTERNALS);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
+ ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
+
+ /* work out which of the old attributes we will be removing */
+ for (i=0; i<old_msg->num_elements; i++) {
+ const struct dsdb_attribute *sa;
+ el = &old_msg->elements[i];
+ sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
+ if (!sa) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (ldb_attr_cmp(el->name, rdn_name) == 0) {
+ /* don't remove the rDN */
+ continue;
+ }
+
+ if (sa->linkID && sa->linkID & 1) {
+ ret = replmd_delete_remove_link(module, schema, old_dn, el, sa);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ continue;
+ }
+
+ if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
+ continue;
+ }
+
+ ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ ldb_module_oom(module);
+ return ret;
+ }
+ }
+
+ /* work out what the new rdn value is, for updating the
+ rDN and name fields */
+ new_rdn_value = ldb_dn_get_rdn_val(new_dn);
+ ret = ldb_msg_add_value(msg, rdn_name, new_rdn_value, &el);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ el = ldb_msg_find_element(old_msg, "name");
+ if (el) {
+ ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
+ ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ /* now rename onto the new DN */
+ ret = dsdb_module_rename(module, old_dn, new_dn, 0);
+ if (ret != LDB_SUCCESS){
+ DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
+ ldb_dn_get_linearized(old_dn),
+ ldb_dn_get_linearized(new_dn),
+ ldb_errstring(ldb)));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
+}
+
+
static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
{
@@ -1094,14 +2492,15 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
/* remove any message elements that have zero values */
for (i=0; i<msg->num_elements; i++) {
- if (msg->elements[i].num_values == 0) {
+ struct ldb_message_element *el = &msg->elements[i];
+
+ if (el->num_values == 0) {
DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
- msg->elements[i].name));
- memmove(&msg->elements[i],
- &msg->elements[i+1],
- sizeof(msg->elements[i])*(msg->num_elements - (i+1)));
+ el->name));
+ memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
msg->num_elements--;
i--;
+ continue;
}
}
@@ -1145,25 +2544,38 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
return ldb_next_request(ar->module, change_req);
}
-static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1 *m1,
- struct replPropertyMetaData1 *m2)
+/*
+ return true if an update is newer than an existing entry
+ see section 5.11 of MS-ADTS
+*/
+static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
+ const struct GUID *update_invocation_id,
+ uint32_t current_version,
+ uint32_t update_version,
+ NTTIME current_change_time,
+ NTTIME update_change_time)
{
- int ret;
-
- if (m1->version != m2->version) {
- return m1->version - m2->version;
+ if (update_version != current_version) {
+ return update_version > current_version;
}
-
- if (m1->originating_change_time != m2->originating_change_time) {
- return m1->originating_change_time - m2->originating_change_time;
+ if (update_change_time > current_change_time) {
+ return true;
}
-
- ret = GUID_compare(&m1->originating_invocation_id, &m2->originating_invocation_id);
- if (ret != 0) {
- return ret;
+ if (update_change_time == current_change_time) {
+ return GUID_compare(update_invocation_id, current_invocation_id) > 0;
}
+ return false;
+}
- return m1->originating_usn - m2->originating_usn;
+static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
+ struct replPropertyMetaData1 *new_m)
+{
+ return replmd_update_is_newer(&cur_m->originating_invocation_id,
+ &new_m->originating_invocation_id,
+ cur_m->version,
+ new_m->version,
+ cur_m->originating_change_time,
+ new_m->originating_change_time);
}
static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
@@ -1194,7 +2606,9 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
ldb_dn_get_linearized(ar->search_msg->dn),
ldb_dn_get_linearized(msg->dn));
- if (ldb_rename(ldb, ar->search_msg->dn, msg->dn) != LDB_SUCCESS) {
+ if (dsdb_module_rename(ar->module,
+ ar->search_msg->dn, msg->dn,
+ DSDB_FLAG_OWN_MODULE) != LDB_SUCCESS) {
ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
ldb_dn_get_linearized(ar->search_msg->dn),
ldb_dn_get_linearized(msg->dn),
@@ -1238,21 +2652,26 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
bool found = false;
for (j=0; j < ni; j++) {
- int cmp;
+ bool cmp;
if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
continue;
}
- cmp = replmd_replPropertyMetaData1_conflict_compare(&rmd->ctr.ctr1.array[i],
- &nmd.ctr.ctr1.array[j]);
- if (cmp > 0) {
+ cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
+ &rmd->ctr.ctr1.array[i]);
+ if (cmp) {
/* replace the entry */
nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
found = true;
break;
}
+ DEBUG(1,("Discarding older DRS attribute update to %s on %s from %s\n",
+ msg->elements[i-removed_attrs].name,
+ ldb_dn_get_linearized(msg->dn),
+ GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
+
/* we don't want to apply this change so remove the attribute */
ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
removed_attrs++;
@@ -1954,24 +3373,22 @@ static int replmd_process_linked_attribute(struct ldb_module *module,
struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
struct ldb_context *ldb = ldb_module_get_ctx(module);
struct dsdb_schema *schema = dsdb_get_schema(ldb);
- struct drsuapi_DsReplicaObjectIdentifier3 target;
struct ldb_message *msg;
TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
- struct ldb_request *mod_req;
int ret;
const struct dsdb_attribute *attr;
- struct ldb_dn *target_dn;
struct dsdb_dn *dsdb_dn;
uint64_t seq_num = 0;
- struct drsuapi_DsReplicaAttribute drs;
- struct drsuapi_DsAttributeValue val;
- struct ldb_message_element el;
- const struct ldb_val *guid;
+ struct ldb_message_element *old_el;
WERROR status;
-
- drs.value_ctr.num_values = 1;
- drs.value_ctr.values = &val;
- val.blob = la->value.blob;
+ time_t t = time(NULL);
+ struct ldb_result *res;
+ const char *attrs[2];
+ struct parsed_dn *pdn_list, *pdn;
+ struct GUID guid = GUID_zero();
+ NTSTATUS ntstatus;
+ bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
+ const struct GUID *our_invocation_id;
/*
linked_attributes[0]:
@@ -2016,155 +3433,212 @@ linked_attributes[0]:
return LDB_ERR_OPERATIONS_ERROR;
}
- status = attr->syntax->drsuapi_to_ldb(ldb, schema, attr, &drs, tmp_ctx, &el);
+ attrs[0] = attr->lDAPDisplayName;
+ attrs[1] = NULL;
- /* construct a modify request for this attribute change */
- msg = ldb_msg_new(tmp_ctx);
- if (!msg) {
- ldb_oom(ldb);
- talloc_free(tmp_ctx);
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- ret = dsdb_find_dn_by_guid(ldb, tmp_ctx,
- GUID_string(tmp_ctx, &la->identifier->guid), &msg->dn);
+ /* get the existing message from the db for the object with
+ this GUID, returning attribute being modified. We will then
+ use this msg as the basis for a modify call */
+ ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
+ DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
+ DSDB_SEARCH_SHOW_DELETED |
+ DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
+ DSDB_SEARCH_REVEAL_INTERNALS,
+ "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
}
+ if (res->count != 1) {
+ ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
+ GUID_string(tmp_ctx, &la->identifier->guid));
+ talloc_free(tmp_ctx);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+ msg = res->msgs[0];
- el.name = attr->lDAPDisplayName;
- if (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) {
- ret = ldb_msg_add(msg, &el, LDB_FLAG_MOD_ADD);
+ if (msg->num_elements == 0) {
+ ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
+ if (ret != LDB_SUCCESS) {
+ ldb_module_oom(module);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
} else {
- ret = ldb_msg_add(msg, &el, LDB_FLAG_MOD_DELETE);
+ old_el = &msg->elements[0];
+ old_el->flags = LDB_FLAG_MOD_REPLACE;
}
+
+ /* parse the existing links */
+ ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
}
- dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el.values[0], attr->syntax->ldap_oid);
- if (!dsdb_dn) {
- DEBUG(0,(__location__ ": Failed to parse just-generated DN\n"));
+ /* get our invocationId */
+ our_invocation_id = samdb_ntds_invocation_id(ldb);
+ if (!our_invocation_id) {
+ ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
talloc_free(tmp_ctx);
- return LDB_ERR_INVALID_DN_SYNTAX;
+ return LDB_ERR_OPERATIONS_ERROR;
}
- guid = ldb_dn_get_extended_component(dsdb_dn->dn, "GUID");
- if (!guid) {
- DEBUG(0,(__location__ ": Failed to parse GUID from just-generated DN\n"));
+ ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, our_invocation_id);
+ if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
- return LDB_ERR_INVALID_DN_SYNTAX;
+ return ret;
}
- ret = dsdb_find_dn_by_guid(ldb, tmp_ctx, ldb_binary_encode(tmp_ctx, *guid), &target_dn);
- if (ret != LDB_SUCCESS) {
- /* If this proves to be a problem in the future, then
- * just remove the return - perhaps we can just use
- * the details the replication peer supplied */
-
- DEBUG(0,(__location__ ": Failed to map GUID %s to DN\n", GUID_string(tmp_ctx, &target.guid)));
- talloc_free(tmp_ctx);
+ status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
+ if (!W_ERROR_IS_OK(status)) {
+ ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
+ old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
return LDB_ERR_OPERATIONS_ERROR;
- } else {
+ }
- /* Now update with full DN we just found in the DB (including extended components) */
- dsdb_dn->dn = target_dn;
- /* Now make a linearized version, using the original binary components (if any) */
- el.values[0] = data_blob_string_const(dsdb_dn_get_extended_linearized(tmp_ctx, dsdb_dn, 1));
+ ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
+ if (!NT_STATUS_IS_OK(ntstatus) && active) {
+ ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
+ old_el->name,
+ ldb_dn_get_linearized(dsdb_dn->dn),
+ ldb_dn_get_linearized(msg->dn));
+ return LDB_ERR_OPERATIONS_ERROR;
}
- ret = replmd_update_rpmd(module, schema, msg, &seq_num);
+ /* re-resolve the DN by GUID, as the DRS server may give us an
+ old DN value */
+ ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn);
if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, __location__ ": Failed to re-resolve GUID %s",
+ GUID_string(tmp_ctx, &guid));
talloc_free(tmp_ctx);
return ret;
}
- /* we only change whenChanged and uSNChanged if the seq_num
- has changed */
- if (seq_num != 0) {
- time_t t = time(NULL);
-
- if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
+ /* see if this link already exists */
+ pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
+ if (pdn != NULL) {
+ /* see if this update is newer than what we have already */
+ struct GUID invocation_id = GUID_zero();
+ uint32_t version = 0;
+ NTTIME change_time = 0;
+ uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);
+
+ dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
+ dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
+ dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
+
+ if (!replmd_update_is_newer(&invocation_id,
+ &la->meta_data.originating_invocation_id,
+ version,
+ la->meta_data.version,
+ change_time,
+ la->meta_data.originating_change_time)) {
+ DEBUG(1,("Discarding older DRS linked attribute update to %s on %s from %s\n",
+ old_el->name, ldb_dn_get_linearized(msg->dn),
+ GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
talloc_free(tmp_ctx);
- return LDB_ERR_OPERATIONS_ERROR;
+ return LDB_SUCCESS;
}
- if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
+ /* get a seq_num for this change */
+ ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
+ if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
- return LDB_ERR_OPERATIONS_ERROR;
+ return ret;
}
- }
-
- ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
- msg,
- NULL,
- NULL,
- ldb_op_default_callback,
- NULL);
- if (ret != LDB_SUCCESS) {
- talloc_free(tmp_ctx);
- return ret;
- }
- talloc_steal(mod_req, msg);
-
- if (DEBUGLVL(4)) {
- DEBUG(4,("Applying DRS linked attribute change:\n%s\n",
- ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg)));
- }
- /* Run the new request */
- ret = ldb_next_request(module, mod_req);
+ if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
+ /* remove the existing backlink */
+ ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
- /* we need to wait for this to finish, as we are being called
- from the synchronous end_transaction hook of this module */
- if (ret == LDB_SUCCESS) {
- ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
- }
+ ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
+ &la->meta_data.originating_invocation_id,
+ la->meta_data.originating_usn, seq_num,
+ la->meta_data.originating_change_time,
+ la->meta_data.version,
+ !active);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
- if (ret == LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) {
- /* the link destination exists, we need to update it
- * by deleting the old one for the same DN then adding
- * the new one */
- msg->elements = talloc_realloc(msg, msg->elements,
- struct ldb_message_element,
- msg->num_elements+1);
- if (msg->elements == NULL) {
- ldb_oom(ldb);
+ if (active) {
+ /* add the new backlink */
+ ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+ } else {
+ /* get a seq_num for this change */
+ ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
+ if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ old_el->values = talloc_realloc(msg->elements, old_el->values,
+ struct ldb_val, old_el->num_values+1);
+ if (!old_el->values) {
+ ldb_module_oom(module);
return LDB_ERR_OPERATIONS_ERROR;
}
- /* this relies on the backend matching the old entry
- only by the DN portion of the extended DN */
- msg->elements[1] = msg->elements[0];
- msg->elements[0].flags = LDB_FLAG_MOD_DELETE;
- msg->num_elements++;
-
- ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
- msg,
- NULL,
- NULL,
- ldb_op_default_callback,
- NULL);
+ old_el->num_values++;
+
+ ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
+ &la->meta_data.originating_invocation_id,
+ la->meta_data.originating_usn, seq_num,
+ la->meta_data.originating_change_time,
+ la->meta_data.version,
+ (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
}
- /* Run the new request */
- ret = ldb_next_request(module, mod_req);
-
- if (ret == LDB_SUCCESS) {
- ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
+ if (active) {
+ ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
+ true, attr, false);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
}
}
+ /* we only change whenChanged and uSNChanged if the seq_num
+ has changed */
+ if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = dsdb_check_single_valued_link(attr, old_el);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
if (ret != LDB_SUCCESS) {
- ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n",
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
ldb_errstring(ldb),
ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
- ret = LDB_SUCCESS;
+ talloc_free(tmp_ctx);
+ return ret;
}
talloc_free(tmp_ctx);
@@ -2194,9 +3668,7 @@ static int replmd_start_transaction(struct ldb_module *module)
/* create our private structure for this transaction */
struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
struct replmd_private);
- talloc_free(replmd_private->la_ctx);
- replmd_private->la_list = NULL;
- replmd_private->la_ctx = NULL;
+ replmd_txn_cleanup(replmd_private);
/* free any leftover mod_usn records from cancelled
transactions */
@@ -2218,6 +3690,7 @@ static int replmd_prepare_commit(struct ldb_module *module)
struct replmd_private *replmd_private =
talloc_get_type(ldb_module_get_private(module), struct replmd_private);
struct la_entry *la, *prev;
+ struct la_backlink *bl;
int ret;
/* walk the list backwards, to do the first entry first, as we
@@ -2230,16 +3703,22 @@ static int replmd_prepare_commit(struct ldb_module *module)
DLIST_REMOVE(replmd_private->la_list, la);
ret = replmd_process_linked_attribute(module, la);
if (ret != LDB_SUCCESS) {
- talloc_free(replmd_private->la_ctx);
- replmd_private->la_list = NULL;
- replmd_private->la_ctx = NULL;
+ replmd_txn_cleanup(replmd_private);
return ret;
}
}
- talloc_free(replmd_private->la_ctx);
- replmd_private->la_list = NULL;
- replmd_private->la_ctx = NULL;
+ /* process our backlink list, creating and deleting backlinks
+ as necessary */
+ for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
+ ret = replmd_process_backlink(module, bl);
+ if (ret != LDB_SUCCESS) {
+ replmd_txn_cleanup(replmd_private);
+ return ret;
+ }
+ }
+
+ replmd_txn_cleanup(replmd_private);
/* possibly change @REPLCHANGED */
ret = replmd_notify_store(module);
@@ -2254,9 +3733,8 @@ static int replmd_del_transaction(struct ldb_module *module)
{
struct replmd_private *replmd_private =
talloc_get_type(ldb_module_get_private(module), struct replmd_private);
- talloc_free(replmd_private->la_ctx);
- replmd_private->la_list = NULL;
- replmd_private->la_ctx = NULL;
+ replmd_txn_cleanup(replmd_private);
+
return ldb_next_del_trans(module);
}
@@ -2267,6 +3745,7 @@ _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
.add = replmd_add,
.modify = replmd_modify,
.rename = replmd_rename,
+ .del = replmd_delete,
.extended = replmd_extended,
.start_transaction = replmd_start_transaction,
.prepare_commit = replmd_prepare_commit,
diff --git a/source4/dsdb/samdb/ldb_modules/samba_dsdb.c b/source4/dsdb/samdb/ldb_modules/samba_dsdb.c
index ee7e42ef9b..a461a94806 100644
--- a/source4/dsdb/samdb/ldb_modules/samba_dsdb.c
+++ b/source4/dsdb/samdb/ldb_modules/samba_dsdb.c
@@ -38,6 +38,7 @@
#include "dsdb/samdb/ldb_modules/util.h"
#include "dsdb/samdb/samdb.h"
+#include "librpc/ndr/libndr.h"
static int read_at_rootdse_record(struct ldb_context *ldb, struct ldb_module *module, TALLOC_CTX *mem_ctx,
struct ldb_message **msg)
@@ -135,6 +136,55 @@ static int prepare_modules_line(struct ldb_context *ldb,
return ret;
}
+
+
+/*
+ initialise the invocationID for a standalone server
+ */
+static int initialise_invocation_id(struct ldb_module *module, struct GUID *guid)
+{
+ struct ldb_message *msg;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ int ret;
+
+ *guid = GUID_random();
+
+ msg = ldb_msg_new(module);
+ if (msg == NULL) {
+ ldb_module_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ msg->dn = ldb_dn_new(msg, ldb, "@SAMBA_DSDB");
+ if (!msg->dn) {
+ ldb_module_oom(module);
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = dsdb_msg_add_guid(msg, guid, "invocationID");
+ if (ret != LDB_SUCCESS) {
+ ldb_module_oom(module);
+ talloc_free(msg);
+ return ret;
+ }
+ msg->elements[0].flags = LDB_FLAG_MOD_ADD;
+
+ ret = ldb_modify(ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Failed to setup standalone invocationID - %s",
+ ldb_errstring(ldb));
+ talloc_free(msg);
+ return ret;
+ }
+
+ DEBUG(1,("Initialised standalone invocationID to %s\n",
+ GUID_string(msg, guid)));
+
+ talloc_free(msg);
+
+ return LDB_SUCCESS;
+}
+
+
static int samba_dsdb_init(struct ldb_module *module)
{
struct ldb_context *ldb = ldb_module_get_ctx(module);
@@ -184,16 +234,11 @@ static int samba_dsdb_init(struct ldb_module *module)
"instancetype",
NULL };
- const char *objectguid_module;
- /* if serverrole == "domain controller": */
- const char *repl_meta_data = "repl_meta_data";
- /* else: */
- const char *objectguid = "objectguid";
-
const char **link_modules;
static const char *tdb_modules_list[] = {
- "subtree_rename",
"subtree_delete",
+ "repl_meta_data",
+ "subtree_rename",
"linked_attributes",
NULL};
@@ -213,7 +258,7 @@ static int samba_dsdb_init(struct ldb_module *module)
static const char *openldap_backend_modules[] = {
"entryuuid", "paged_searches", NULL };
- static const char *samba_dsdb_attrs[] = { "backendType", "serverRole", NULL };
+ static const char *samba_dsdb_attrs[] = { "backendType", "serverRole", "invocationID", NULL };
const char *backendType, *serverRole;
if (!tmp_ctx) {
@@ -248,17 +293,39 @@ static int samba_dsdb_init(struct ldb_module *module)
return ret;
}
+ if (strcmp(serverRole, "standalone") == 0 ||
+ strcmp(serverRole, "member server") == 0) {
+ struct GUID *guid;
+
+ guid = talloc(module, struct GUID);
+ if (!guid) {
+ ldb_module_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *guid = samdb_result_guid(res->msgs[0], "invocationID");
+ if (GUID_all_zero(guid)) {
+ ret = initialise_invocation_id(module, guid);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+
+ /* cache the domain_sid in the ldb. See the matching
+ * code in samdb_ntds_invocation_id() */
+ ret = ldb_set_opaque(ldb, "cache.invocation_id", guid);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+
backend_modules = NULL;
if (strcasecmp(backendType, "ldb") == 0) {
- if (strcasecmp(serverRole, "dc") == 0 || strcasecmp(serverRole, "domain controller") == 0) {
- objectguid_module = repl_meta_data;
- } else {
- objectguid_module = objectguid;
- }
extended_dn_module = extended_dn_module_ldb;
link_modules = tdb_modules_list;
} else {
- objectguid_module = NULL;
link_modules = NULL;
if (strcasecmp(backendType, "fedora-ds") == 0) {
backend_modules = fedora_ds_backend_modules;
@@ -281,9 +348,6 @@ static int samba_dsdb_init(struct ldb_module *module)
final_module_list = str_list_copy_const(tmp_ctx, modules_list);
CHECK_MODULE_LIST;
- final_module_list = str_list_add_const(final_module_list, objectguid_module);
- CHECK_MODULE_LIST;
-
final_module_list = str_list_append_const(final_module_list, link_modules);
CHECK_MODULE_LIST;
diff --git a/source4/dsdb/samdb/ldb_modules/schema_data.c b/source4/dsdb/samdb/ldb_modules/schema_data.c
index ed10ae6d69..2e99113953 100644
--- a/source4/dsdb/samdb/ldb_modules/schema_data.c
+++ b/source4/dsdb/samdb/ldb_modules/schema_data.c
@@ -92,6 +92,37 @@ struct schema_data_search_data {
const struct dsdb_schema *schema;
};
+/* context to be used during async operations */
+struct schema_data_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ const struct dsdb_schema *schema;
+};
+
+/* Create new context using
+ * ldb_request as memory context */
+static int _schema_data_context_new(struct ldb_module *module,
+ struct ldb_request *req,
+ struct schema_data_context **pac)
+{
+ struct schema_data_context *ac;
+ struct ldb_context *ldb;
+
+ ldb = ldb_module_get_ctx(module);
+
+ *pac = ac = talloc_zero(req, struct schema_data_context);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->module = module;
+ ac->req = req;
+ ac->schema = dsdb_get_schema(ldb);
+
+ return LDB_SUCCESS;
+}
+
static int schema_data_init(struct ldb_module *module)
{
struct ldb_context *ldb;
@@ -132,6 +163,57 @@ static int schema_data_init(struct ldb_module *module)
return LDB_SUCCESS;
}
+
+/* Generate new value for msDs-IntId
+ * Value should be in 0x80000000..0xBFFFFFFF range
+ * Generated value is added ldb_msg */
+static int _schema_data_gen_msds_intid(struct schema_data_context *ac,
+ struct ldb_message *ldb_msg)
+{
+ uint32_t id;
+
+ /* generate random num in 0x80000000..0xBFFFFFFF */
+ id = generate_random() % 0X3FFFFFFF;
+ id += 0x80000000;
+
+ /* make sure id is unique and adjust if not */
+ while (dsdb_attribute_by_attributeID_id(ac->schema, id)) {
+ id++;
+ if (id > 0xBFFFFFFF) {
+ id = 0x80000001;
+ }
+ }
+
+ /* add generated msDS-IntId value to ldb_msg */
+ return ldb_msg_add_fmt(ldb_msg, "msDS-IntId", "%d", id);
+}
+
+static int _schema_data_add_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct schema_data_context *ac;
+
+ ac = talloc_get_type(req->context, struct schema_data_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+}
+
static int schema_data_add(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_context *ldb;
@@ -140,7 +222,6 @@ static int schema_data_add(struct ldb_module *module, struct ldb_request *req)
const struct ldb_val *governsID = NULL;
const char *oid_attr = NULL;
const char *oid = NULL;
- uint32_t attid;
WERROR status;
ldb = ldb_module_get_ctx(module);
@@ -170,6 +251,11 @@ static int schema_data_add(struct ldb_module *module, struct ldb_request *req)
governsID = ldb_msg_find_ldb_val(req->op.add.message, "governsID");
if (attributeID) {
+ /* Sanity check for not allowed attributes */
+ if (ldb_msg_find_ldb_val(req->op.add.message, "msDS-IntId")) {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
oid_attr = "attributeID";
oid = talloc_strndup(req, (const char *)attributeID->data, attributeID->length);
} else if (governsID) {
@@ -183,25 +269,83 @@ static int schema_data_add(struct ldb_module *module, struct ldb_request *req)
ldb_oom(ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
-
- status = dsdb_schema_pfm_make_attid(schema->prefixmap, oid, &attid);
- if (W_ERROR_IS_OK(status)) {
+
+ status = dsdb_schema_pfm_find_oid(schema->prefixmap, oid, NULL);
+ if (!W_ERROR_IS_OK(status)) {
+ /* check for internal errors */
+ if (!W_ERROR_EQUAL(WERR_DS_NO_MSDS_INTID, status)) {
+ ldb_debug_set(ldb, LDB_DEBUG_ERROR,
+ "schema_data_add: failed to map %s[%s]: %s\n",
+ oid_attr, oid, win_errstr(status));
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ /* Update prefixMap and save it */
+ status = dsdb_create_prefix_mapping(ldb, schema, oid);
+ if (!W_ERROR_IS_OK(status)) {
+ ldb_debug_set(ldb, LDB_DEBUG_ERROR,
+ "schema_data_add: failed to create prefix mapping for %s[%s]: %s\n",
+ oid_attr, oid, win_errstr(status));
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ }
+
+ /* generate and add msDS-IntId attr value */
+ if (attributeID
+ && (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2003)
+ && !(ldb_msg_find_attr_as_uint(req->op.add.message, "systemFlags", 0) & SYSTEM_FLAG_SCHEMA_BASE_OBJECT)) {
+ struct ldb_message *msg;
+ struct schema_data_context *ac;
+ struct ldb_request *add_req;
+
+ if (_schema_data_context_new(module, req, &ac) != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* we have to copy the message as the caller might have it as a const */
+ msg = ldb_msg_copy_shallow(ac, req->op.add.message);
+ if (msg == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* generate unique value for msDS-IntId attr value */
+ if (_schema_data_gen_msds_intid(ac, msg) != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_ERROR,
+ "_schema_data_gen_msds_intid() failed to generate msDS-IntId value\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ldb_build_add_req(&add_req, ldb, ac,
+ msg,
+ req->controls,
+ ac, _schema_data_add_callback,
+ req);
+
+ return ldb_next_request(module, add_req);
+ }
+
+ return ldb_next_request(module, req);
+}
+
+static int schema_data_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ /* special objects should always go through */
+ if (ldb_dn_is_special(req->op.mod.message->dn)) {
return ldb_next_request(module, req);
- } else if (!W_ERROR_EQUAL(WERR_DS_NO_MSDS_INTID, status)) {
- ldb_debug_set(ldb, LDB_DEBUG_ERROR,
- "schema_data_add: failed to map %s[%s]: %s\n",
- oid_attr, oid, win_errstr(status));
- return LDB_ERR_UNWILLING_TO_PERFORM;
}
- status = dsdb_create_prefix_mapping(ldb, schema, oid);
- if (!W_ERROR_IS_OK(status)) {
- ldb_debug_set(ldb, LDB_DEBUG_ERROR,
- "schema_data_add: failed to create prefix mapping for %s[%s]: %s\n",
- oid_attr, oid, win_errstr(status));
- return LDB_ERR_UNWILLING_TO_PERFORM;
+ /* replicated update should always go through */
+ if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* msDS-IntId is not allowed to be modified */
+ if (ldb_msg_find_ldb_val(req->op.mod.message, "msDS-IntId")) {
+ return LDB_ERR_CONSTRAINT_VIOLATION;
}
+ /* go on with the call chain */
return ldb_next_request(module, req);
}
@@ -460,5 +604,6 @@ _PUBLIC_ const struct ldb_module_ops ldb_schema_data_module_ops = {
.name = "schema_data",
.init_context = schema_data_init,
.add = schema_data_add,
+ .modify = schema_data_modify,
.search = schema_data_search
};
diff --git a/source4/dsdb/samdb/ldb_modules/simple_ldap_map.c b/source4/dsdb/samdb/ldb_modules/simple_ldap_map.c
index 43402248c0..bf9cd4fdda 100644
--- a/source4/dsdb/samdb/ldb_modules/simple_ldap_map.c
+++ b/source4/dsdb/samdb/ldb_modules/simple_ldap_map.c
@@ -181,7 +181,7 @@ static struct ldb_val usn_to_entryCSN(struct ldb_module *module, TALLOC_CTX *ctx
static unsigned long long entryCSN_to_usn_int(TALLOC_CTX *ctx, const struct ldb_val *val)
{
- char *entryCSN = talloc_strdup(ctx, (const char *)val->data);
+ char *entryCSN = talloc_strndup(ctx, (const char *)val->data, val->length);
char *mod_per_sec;
time_t t;
unsigned long long usn;
@@ -232,10 +232,10 @@ static struct ldb_val usn_to_timestamp(struct ldb_module *module, TALLOC_CTX *ct
static struct ldb_val timestamp_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
{
struct ldb_val out;
- time_t t;
+ time_t t=0;
unsigned long long usn;
- t = ldb_string_to_time((const char *)val->data);
+ ldb_val_to_time(val, &t);
usn = ((unsigned long long)t <<24);
diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c
index 8d9930a81f..32b79a6701 100644
--- a/source4/dsdb/samdb/ldb_modules/util.c
+++ b/source4/dsdb/samdb/ldb_modules/util.c
@@ -87,6 +87,13 @@ int dsdb_request_add_controls(struct ldb_module *module, struct ldb_request *req
}
}
+ if (dsdb_flags & DSDB_MODIFY_RELAX) {
+ ret = ldb_request_add_control(req, LDB_CONTROL_RELAX_OID, false, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
return LDB_SUCCESS;
}
@@ -164,15 +171,21 @@ int dsdb_module_search(struct ldb_module *module,
struct ldb_dn *basedn, enum ldb_scope scope,
const char * const *attrs,
int dsdb_flags,
- const char *expression)
+ const char *format, ...) _PRINTF_ATTRIBUTE(8, 9)
{
int ret;
struct ldb_request *req;
TALLOC_CTX *tmp_ctx;
struct ldb_result *res;
+ va_list ap;
+ char *expression;
tmp_ctx = talloc_new(mem_ctx);
+ va_start(ap, format);
+ expression = talloc_vasprintf(tmp_ctx, format, ap);
+ va_end(ap);
+
res = talloc_zero(tmp_ctx, struct ldb_result);
if (!res) {
return LDB_ERR_OPERATIONS_ERROR;
@@ -198,7 +211,12 @@ int dsdb_module_search(struct ldb_module *module,
return ret;
}
- ret = ldb_next_request(module, req);
+ if (dsdb_flags & DSDB_FLAG_OWN_MODULE) {
+ const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+ ret = ops->search(module, req);
+ } else {
+ ret = ldb_next_request(module, req);
+ }
if (ret == LDB_SUCCESS) {
ret = ldb_wait(req->handle, LDB_WAIT_ALL);
}
@@ -219,22 +237,15 @@ int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
{
struct ldb_result *res;
const char *attrs[] = { NULL };
- char *expression;
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
int ret;
- expression = talloc_asprintf(tmp_ctx, "objectGUID=%s", GUID_string(tmp_ctx, guid));
- if (!expression) {
- ldb_module_oom(module);
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
attrs,
DSDB_SEARCH_SHOW_DELETED |
DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
- expression);
+ "objectGUID=%s", GUID_string(tmp_ctx, guid));
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
@@ -244,8 +255,8 @@ int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
return LDB_ERR_NO_SUCH_OBJECT;
}
if (res->count != 1) {
- ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching %s\n",
- expression);
+ ldb_asprintf_errstring(ldb_module_get_ctx(module), "More than one object found matching objectGUID %s\n",
+ GUID_string(tmp_ctx, guid));
talloc_free(tmp_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
@@ -257,6 +268,37 @@ int dsdb_module_dn_by_guid(struct ldb_module *module, TALLOC_CTX *mem_ctx,
}
/*
+ find a GUID given a DN.
+ */
+int dsdb_module_guid_by_dn(struct ldb_module *module, struct ldb_dn *dn, struct GUID *guid)
+{
+ const char *attrs[] = { NULL };
+ struct ldb_result *res;
+ TALLOC_CTX *tmp_ctx = talloc_new(module);
+ int ret;
+ NTSTATUS status;
+
+ ret = dsdb_module_search_dn(module, tmp_ctx, &res, dn, attrs,
+ DSDB_SEARCH_SHOW_DELETED|
+ DSDB_SEARCH_SHOW_EXTENDED_DN);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb_module_get_ctx(module), "Failed to find GUID for %s",
+ ldb_dn_get_linearized(dn));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ status = dsdb_get_extended_dn_guid(res->msgs[0]->dn, guid, "GUID");
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+}
+
+/*
a ldb_modify request operating on modules below the
current module
*/
@@ -287,7 +329,12 @@ int dsdb_module_modify(struct ldb_module *module,
}
/* Run the new request */
- ret = ldb_next_request(module, mod_req);
+ if (dsdb_flags & DSDB_FLAG_OWN_MODULE) {
+ const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+ ret = ops->modify(module, mod_req);
+ } else {
+ ret = ldb_next_request(module, mod_req);
+ }
if (ret == LDB_SUCCESS) {
ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
}
@@ -330,7 +377,12 @@ int dsdb_module_rename(struct ldb_module *module,
}
/* Run the new request */
- ret = ldb_next_request(module, req);
+ if (dsdb_flags & DSDB_FLAG_OWN_MODULE) {
+ const struct ldb_module_ops *ops = ldb_module_get_ops(module);
+ ret = ops->rename(module, req);
+ } else {
+ ret = ldb_next_request(module, req);
+ }
if (ret == LDB_SUCCESS) {
ret = ldb_wait(req->handle, LDB_WAIT_ALL);
}
@@ -365,3 +417,32 @@ const struct dsdb_class * get_last_structural_class(const struct dsdb_schema *sc
return last_class;
}
+
+/*
+ check if a single valued link has multiple non-deleted values
+
+ This is needed when we will be using the RELAX control to stop
+ ldb_tdb from checking single valued links
+ */
+int dsdb_check_single_valued_link(const struct dsdb_attribute *attr,
+ const struct ldb_message_element *el)
+{
+ bool found_active = false;
+ int i;
+
+ if (!(attr->ldb_schema_attribute->flags & LDB_ATTR_FLAG_SINGLE_VALUE) ||
+ el->num_values < 2) {
+ return LDB_SUCCESS;
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ if (!dsdb_dn_is_deleted_val(&el->values[i])) {
+ if (found_active) {
+ return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+ }
+ found_active = true;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
diff --git a/source4/dsdb/samdb/ldb_modules/util.h b/source4/dsdb/samdb/ldb_modules/util.h
index 41ed883dc2..add39e110a 100644
--- a/source4/dsdb/samdb/ldb_modules/util.h
+++ b/source4/dsdb/samdb/ldb_modules/util.h
@@ -21,6 +21,8 @@
struct dsdb_schema; /* predeclare schema struct */
struct GUID;
+struct dsdb_attribute;
+
#include "dsdb/samdb/ldb_modules/util_proto.h"
#define DSDB_SEARCH_SEARCH_ALL_PARTITIONS 0x0001
@@ -28,3 +30,5 @@ struct GUID;
#define DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT 0x0004
#define DSDB_SEARCH_REVEAL_INTERNALS 0x0008
#define DSDB_SEARCH_SHOW_EXTENDED_DN 0x0010
+#define DSDB_MODIFY_RELAX 0x0020
+#define DSDB_FLAG_OWN_MODULE 0x0040
diff --git a/source4/dsdb/schema/prefixmap.h b/source4/dsdb/schema/prefixmap.h
index 816ddcfbb3..74acecb4ff 100644
--- a/source4/dsdb/schema/prefixmap.h
+++ b/source4/dsdb/schema/prefixmap.h
@@ -23,6 +23,17 @@
#define _DSDB_PREFIXMAP_H
/**
+ * ATTRTYP ranges
+ * Ref: MS-ADTS, 3.1.1.2.6 ATTRTYP
+ */
+enum dsdb_attid_type {
+ dsdb_attid_type_pfm = 1, /* attid in [0x00000000..0x7FFFFFFF] */
+ dsdb_attid_type_intid = 2, /* attid in [0x80000000..0xBFFFFFFF] */
+ dsdb_attid_type_reserved = 3, /* attid in [0xC0000000..0xFFFEFFFF] */
+ dsdb_attid_type_internal = 4, /* attid in [0xFFFF0000..0xFFFFFFFF] */
+};
+
+/**
* oid-prefix in prefixmap
*/
struct dsdb_schema_prefixmap_oid {
diff --git a/source4/dsdb/schema/schema_init.c b/source4/dsdb/schema/schema_init.c
index 4af36838cd..ccdf97cf2d 100644
--- a/source4/dsdb/schema/schema_init.c
+++ b/source4/dsdb/schema/schema_init.c
@@ -310,7 +310,7 @@ WERROR dsdb_write_prefixes_from_schema_to_ldb(TALLOC_CTX *mem_ctx, struct ldb_co
return WERR_NOMEM;
}
- ldb_ret = samdb_replace( ldb, msg, msg );
+ ldb_ret = samdb_replace_as_system(ldb, temp_ctx, msg);
talloc_free(temp_ctx);
@@ -558,14 +558,19 @@ WERROR dsdb_attribute_from_ldb(struct ldb_context *ldb,
/* set an invalid value */
attr->attributeID_id = 0xFFFFFFFF;
} else {
- status = dsdb_schema_pfm_make_attid(schema->prefixmap,
- attr->attributeID_oid,
- &attr->attributeID_id);
- if (!W_ERROR_IS_OK(status)) {
- DEBUG(0,("%s: '%s': unable to map attributeID %s: %s\n",
- __location__, attr->lDAPDisplayName, attr->attributeID_oid,
- win_errstr(status)));
- return status;
+ /* check if msDS-IntId element is set */
+ attr->attributeID_id = samdb_result_uint(msg, "msDS-IntId", 0xFFFFFFFF);
+ if (attr->attributeID_id == 0xFFFFFFFF) {
+ /* msDS-IntId is not set, make */
+ status = dsdb_schema_pfm_make_attid(schema->prefixmap,
+ attr->attributeID_oid,
+ &attr->attributeID_id);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("%s: '%s': unable to map attributeID %s: %s\n",
+ __location__, attr->lDAPDisplayName, attr->attributeID_oid,
+ win_errstr(status)));
+ return status;
+ }
}
}
GET_GUID_LDB(msg, "schemaIDGUID", attr, schemaIDGUID);
diff --git a/source4/dsdb/schema/schema_prefixmap.c b/source4/dsdb/schema/schema_prefixmap.c
index 969b357a39..89d33779e4 100644
--- a/source4/dsdb/schema/schema_prefixmap.c
+++ b/source4/dsdb/schema/schema_prefixmap.c
@@ -27,6 +27,25 @@
/**
+ * Determine range type for supplied ATTID
+ */
+enum dsdb_attid_type dsdb_pfm_get_attid_type(uint32_t attid)
+{
+ if (attid <= 0x7FFFFFFF) {
+ return dsdb_attid_type_pfm;
+ }
+ else if (attid <= 0xBFFFFFFF) {
+ return dsdb_attid_type_intid;
+ }
+ else if (attid <= 0xFFFEFFFF) {
+ return dsdb_attid_type_reserved;
+ }
+ else {
+ return dsdb_attid_type_internal;
+ }
+}
+
+/**
* Allocates schema_prefixMap object in supplied memory context
*/
static struct dsdb_schema_prefixmap *_dsdb_schema_prefixmap_talloc(TALLOC_CTX *mem_ctx,
@@ -303,6 +322,11 @@ WERROR dsdb_schema_pfm_oid_from_attid(struct dsdb_schema_prefixmap *pfm, uint32_
struct dsdb_schema_prefixmap_oid *pfm_entry;
WERROR werr = WERR_OK;
+ /* sanity check for attid requested */
+ if (dsdb_pfm_get_attid_type(attid) != dsdb_attid_type_pfm) {
+ return WERR_INVALID_PARAMETER;
+ }
+
/* crack attid value */
hi_word = attid >> 16;
lo_word = attid & 0xFFFF;
diff --git a/source4/dsdb/schema/schema_query.c b/source4/dsdb/schema/schema_query.c
index f563f01272..df17787f38 100644
--- a/source4/dsdb/schema/schema_query.c
+++ b/source4/dsdb/schema/schema_query.c
@@ -31,7 +31,8 @@ static const char **dsdb_full_attribute_list_internal(TALLOC_CTX *mem_ctx,
static int uint32_cmp(uint32_t c1, uint32_t c2)
{
- return c1 - c2;
+ if (c1 == c2) return 0;
+ return c1 > c2 ? 1 : -1;
}
static int strcasecmp_with_ldb_val(const struct ldb_val *target, const char *str)
diff --git a/source4/dsdb/schema/schema_set.c b/source4/dsdb/schema/schema_set.c
index e65e372623..f59fc32696 100644
--- a/source4/dsdb/schema/schema_set.c
+++ b/source4/dsdb/schema/schema_set.c
@@ -180,13 +180,19 @@ op_error:
return LDB_ERR_OPERATIONS_ERROR;
}
+static int uint32_cmp(uint32_t c1, uint32_t c2)
+{
+ if (c1 == c2) return 0;
+ return c1 > c2 ? 1 : -1;
+}
+
static int dsdb_compare_class_by_lDAPDisplayName(struct dsdb_class **c1, struct dsdb_class **c2)
{
return strcasecmp((*c1)->lDAPDisplayName, (*c2)->lDAPDisplayName);
}
static int dsdb_compare_class_by_governsID_id(struct dsdb_class **c1, struct dsdb_class **c2)
{
- return (*c1)->governsID_id - (*c2)->governsID_id;
+ return uint32_cmp((*c1)->governsID_id, (*c2)->governsID_id);
}
static int dsdb_compare_class_by_governsID_oid(struct dsdb_class **c1, struct dsdb_class **c2)
{
@@ -203,7 +209,7 @@ static int dsdb_compare_attribute_by_lDAPDisplayName(struct dsdb_attribute **a1,
}
static int dsdb_compare_attribute_by_attributeID_id(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
{
- return (*a1)->attributeID_id - (*a2)->attributeID_id;
+ return uint32_cmp((*a1)->attributeID_id, (*a2)->attributeID_id);
}
static int dsdb_compare_attribute_by_attributeID_oid(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
{
@@ -211,7 +217,7 @@ static int dsdb_compare_attribute_by_attributeID_oid(struct dsdb_attribute **a1,
}
static int dsdb_compare_attribute_by_linkID(struct dsdb_attribute **a1, struct dsdb_attribute **a2)
{
- return (*a1)->linkID - (*a2)->linkID;
+ return uint32_cmp((*a1)->linkID, (*a2)->linkID);
}
/*
diff --git a/source4/dsdb/schema/schema_syntax.c b/source4/dsdb/schema/schema_syntax.c
index 1989db0699..de52b9c628 100644
--- a/source4/dsdb/schema/schema_syntax.c
+++ b/source4/dsdb/schema/schema_syntax.c
@@ -488,13 +488,17 @@ static WERROR dsdb_syntax_NTTIME_ldb_to_drsuapi(struct ldb_context *ldb,
for (i=0; i < in->num_values; i++) {
NTTIME v;
time_t t;
+ int ret;
out->value_ctr.values[i].blob = &blobs[i];
blobs[i] = data_blob_talloc(blobs, NULL, 8);
W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
- t = ldb_string_to_time((const char *)in->values[i].data);
+ ret = ldb_val_to_time(&in->values[i], &t);
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_INVALID_ATTRIBUTE_SYNTAX;
+ }
unix_to_nt_time(&v, t);
v /= 10000000;
@@ -735,7 +739,10 @@ static WERROR _dsdb_syntax_OID_obj_ldb_to_drsuapi(struct ldb_context *ldb,
blobs[i] = data_blob_talloc(blobs, NULL, 4);
W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
- obj_class = dsdb_class_by_lDAPDisplayName(schema, (const char *)in->values[i].data);
+ /* in DRS windows puts the classes in the opposite
+ order to the order used in ldap */
+ obj_class = dsdb_class_by_lDAPDisplayName(schema,
+ (const char *)in->values[(in->num_values-1)-i].data);
if (!obj_class) {
return WERR_FOOBAR;
}
@@ -1074,20 +1081,21 @@ WERROR dsdb_syntax_one_DN_drsuapi_to_ldb(TALLOC_CTX *mem_ctx, struct ldb_context
W_ERROR_HAVE_NO_MEMORY(dn);
}
- status = GUID_to_ndr_blob(&id3.guid, tmp_ctx, &guid_blob);
- if (!NT_STATUS_IS_OK(status)) {
- talloc_free(tmp_ctx);
- return ntstatus_to_werror(status);
- }
+ if (!GUID_all_zero(&id3.guid)) {
+ status = GUID_to_ndr_blob(&id3.guid, tmp_ctx, &guid_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
- ret = ldb_dn_set_extended_component(dn, "GUID", &guid_blob);
- if (ret != LDB_SUCCESS) {
- talloc_free(tmp_ctx);
- return WERR_FOOBAR;
+ ret = ldb_dn_set_extended_component(dn, "GUID", &guid_blob);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+ talloc_free(guid_blob.data);
}
- talloc_free(guid_blob.data);
-
if (id3.__ndr_size_sid) {
DATA_BLOB sid_blob;
ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, iconv_convenience, &id3.sid,
@@ -1183,7 +1191,7 @@ static WERROR dsdb_syntax_DN_ldb_to_drsuapi(struct ldb_context *ldb,
ZERO_STRUCT(id3);
- status = dsdb_get_extended_dn_guid(dn, &id3.guid);
+ status = dsdb_get_extended_dn_guid(dn, &id3.guid, "GUID");
if (!NT_STATUS_IS_OK(status) &&
!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
talloc_free(tmp_ctx);
@@ -1367,7 +1375,7 @@ static WERROR dsdb_syntax_DN_BINARY_ldb_to_drsuapi(struct ldb_context *ldb,
ZERO_STRUCT(id3);
- status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &id3.guid);
+ status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &id3.guid, "GUID");
if (!NT_STATUS_IS_OK(status) &&
!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
talloc_free(tmp_ctx);
diff --git a/source4/heimdal_build/external.m4 b/source4/heimdal_build/external.m4
index 3cd8900a12..ad9443f999 100644
--- a/source4/heimdal_build/external.m4
+++ b/source4/heimdal_build/external.m4
@@ -12,7 +12,7 @@ SMB_EXT_LIB(HEIMDAL_KRB5,
[${HEIMDAL_KRB5_CFLAGS}],
[${HEIMDAL_KRB5_CPPFLAGS}],
[${HEIMDAL_KRB5_LDFLAGS}])
-SMB_ENABLE(HEIMDAL_KRB5)
+SMB_ENABLE(HEIMDAL_KRB5,YES)
AC_DEFINE(HAVE_KRB5,1,[Whether kerberos is available])
HAVE_KRB5=YES
AC_DEFINE(HAVE_COM_ERR,1,[Whether com_err is available])
@@ -27,7 +27,7 @@ SMB_EXT_LIB(HEIMDAL_GSSAPI,
[${HEIMDAL_GSSAPI_CFLAGS}],
[${HEIMDAL_GSSAPI_CPPFLAGS}],
[${HEIMDAL_GSSAPI_LDFLAGS}])
-SMB_ENABLE(HEIMDAL_GSSAPI)
+SMB_ENABLE(HEIMDAL_GSSAPI,YES)
AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available])
HAVE_GSSAPI=YES
@@ -40,7 +40,7 @@ SMB_EXT_LIB(HEIMDAL_HDB,
[${HEIMDAL_HDB_CFLAGS}],
[${HEIMDAL_HDB_CPPFLAGS}],
[${HEIMDAL_HDB_LDFLAGS}])
-SMB_ENABLE(HEIMDAL_HDB)
+SMB_ENABLE(HEIMDAL_HDB,YES)
HEIMDAL_KDC_LIBS="-lkdc"
HEIMDAL_KDC_CFLAGS=""
@@ -51,4 +51,4 @@ SMB_EXT_LIB(HEIMDAL_KDC,
[${HEIMDAL_KDC_CFLAGS}],
[${HEIMDAL_KDC_CPPFLAGS}],
[${HEIMDAL_KDC_LDFLAGS}])
-SMB_ENABLE(HEIMDAL_KDC)
+SMB_ENABLE(HEIMDAL_KDC,YES)
diff --git a/source4/kdc/config.mk b/source4/kdc/config.mk
index 7a99857248..a9d01585f0 100644
--- a/source4/kdc/config.mk
+++ b/source4/kdc/config.mk
@@ -6,7 +6,8 @@
INIT_FUNCTION = server_service_kdc_init
SUBSYSTEM = service
PRIVATE_DEPENDENCIES = \
- HEIMDAL_KDC HDB_SAMBA4 PAC_GLUE LIBSAMBA-HOSTCONFIG
+ HEIMDAL_KDC HDB_SAMBA4 PAC_GLUE LIBSAMBA-HOSTCONFIG \
+ LIBTSOCKET
# End SUBSYSTEM KDC
#######################
diff --git a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c
index 2418e5c63f..2f3c30c283 100644
--- a/source4/kdc/hdb-samba4.c
+++ b/source4/kdc/hdb-samba4.c
@@ -53,13 +53,13 @@
#include "kdc/kdc.h"
#include "../lib/crypto/md4.h"
-enum hdb_samba4_ent_type
-{ HDB_SAMBA4_ENT_TYPE_CLIENT, HDB_SAMBA4_ENT_TYPE_SERVER,
+enum hdb_samba4_ent_type
+{ HDB_SAMBA4_ENT_TYPE_CLIENT, HDB_SAMBA4_ENT_TYPE_SERVER,
HDB_SAMBA4_ENT_TYPE_KRBTGT, HDB_SAMBA4_ENT_TYPE_TRUST, HDB_SAMBA4_ENT_TYPE_ANY };
enum trust_direction {
UNKNOWN = 0,
- INBOUND = LSA_TRUST_DIRECTION_INBOUND,
+ INBOUND = LSA_TRUST_DIRECTION_INBOUND,
OUTBOUND = LSA_TRUST_DIRECTION_OUTBOUND
};
@@ -93,7 +93,7 @@ static KerberosTime ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, con
return timegm(&tm);
}
-static HDBFlags uf2HDBFlags(krb5_context context, int userAccountControl, enum hdb_samba4_ent_type ent_type)
+static HDBFlags uf2HDBFlags(krb5_context context, int userAccountControl, enum hdb_samba4_ent_type ent_type)
{
HDBFlags flags = int2HDBFlags(0);
@@ -115,7 +115,7 @@ static HDBFlags uf2HDBFlags(krb5_context context, int userAccountControl, enum h
}
flags.invalid = 0;
}
-
+
if (userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT) {
if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) {
flags.client = 1;
@@ -166,7 +166,7 @@ static HDBFlags uf2HDBFlags(krb5_context context, int userAccountControl, enum h
}
if (userAccountControl & UF_TRUSTED_FOR_DELEGATION) {
flags.ok_as_delegate = 1;
- }
+ }
if (!(userAccountControl & UF_NOT_DELEGATED)) {
flags.forwardable = 1;
flags.proxiable = 1;
@@ -334,7 +334,7 @@ static krb5_error_code hdb_samba4_message2entry_keys(krb5_context context,
ret = krb5_keyblock_init(context,
ENCTYPE_ARCFOUR_HMAC,
- hash->hash, sizeof(hash->hash),
+ hash->hash, sizeof(hash->hash),
&key.key);
if (ret) {
goto out;
@@ -490,8 +490,8 @@ out:
/*
* Construct an hdb_entry from a directory entry.
*/
-static krb5_error_code hdb_samba4_message2entry(krb5_context context, HDB *db,
- struct loadparm_context *lp_ctx,
+static krb5_error_code hdb_samba4_message2entry(krb5_context context, HDB *db,
+ struct loadparm_context *lp_ctx,
TALLOC_CTX *mem_ctx, krb5_const_principal principal,
enum hdb_samba4_ent_type ent_type,
struct ldb_dn *realm_dn,
@@ -514,7 +514,7 @@ static krb5_error_code hdb_samba4_message2entry(krb5_context context, HDB *db,
const char *samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL);
computer_val.data = discard_const_p(uint8_t,"computer");
computer_val.length = strlen((const char *)computer_val.data);
-
+
if (!samAccountName) {
ret = ENOENT;
krb5_set_error_message(context, ret, "hdb_samba4_message2entry: no samAccountName present");
@@ -522,7 +522,7 @@ static krb5_error_code hdb_samba4_message2entry(krb5_context context, HDB *db,
}
objectclasses = ldb_msg_find_element(msg, "objectClass");
-
+
if (objectclasses && ldb_msg_find_val(objectclasses, &computer_val)) {
is_computer = TRUE;
}
@@ -534,7 +534,7 @@ static krb5_error_code hdb_samba4_message2entry(krb5_context context, HDB *db,
krb5_set_error_message(context, ret, "talloc_strdup: out of memory");
goto out;
}
-
+
p = talloc(mem_ctx, struct hdb_samba4_private);
if (!p) {
ret = ENOMEM;
@@ -557,7 +557,7 @@ static krb5_error_code hdb_samba4_message2entry(krb5_context context, HDB *db,
userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
-
+
entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
if (ent_type == HDB_SAMBA4_ENT_TYPE_ANY && principal == NULL) {
krb5_make_principal(context, &entry_ex->entry.principal, realm, samAccountName, NULL);
@@ -573,7 +573,7 @@ static krb5_error_code hdb_samba4_message2entry(krb5_context context, HDB *db,
* the client-specified realm. This code attempts to
* replace the client principal's realm with the one
* we determine from our records */
-
+
/* this has to be with malloc() */
krb5_principal_set_realm(context, entry_ex->entry.principal, realm);
}
@@ -598,19 +598,19 @@ static krb5_error_code hdb_samba4_message2entry(krb5_context context, HDB *db,
* of the Heimdal KDC. They are stored in a the traditional
* DB for audit purposes, and still form part of the structure
* we must return */
-
+
/* use 'whenCreated' */
entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0);
/* use '???' */
entry_ex->entry.created_by.principal = NULL;
-
+
entry_ex->entry.modified_by = (Event *) malloc(sizeof(Event));
if (entry_ex->entry.modified_by == NULL) {
ret = ENOMEM;
krb5_set_error_message(context, ret, "malloc: out of memory");
goto out;
}
-
+
/* use 'whenChanged' */
entry_ex->entry.modified_by->time = ldb_msg_find_krb5time_ldap_time(msg, "whenChanged", 0);
/* use '???' */
@@ -661,7 +661,7 @@ static krb5_error_code hdb_samba4_message2entry(krb5_context context, HDB *db,
} else {
NTTIME must_change_time
- = samdb_result_force_password_change((struct ldb_context *)db->hdb_db, mem_ctx,
+ = samdb_result_force_password_change((struct ldb_context *)db->hdb_db, mem_ctx,
realm_dn, msg);
if (must_change_time == 0x7FFFFFFFFFFFFFFFULL) {
entry_ex->entry.pw_end = NULL;
@@ -722,7 +722,7 @@ static krb5_error_code hdb_samba4_message2entry(krb5_context context, HDB *db,
p->msg = talloc_steal(p, msg);
p->samdb = (struct ldb_context *)db->hdb_db;
-
+
out:
if (ret != 0) {
/* This doesn't free ent itself, that is for the eventual caller to do */
@@ -737,7 +737,7 @@ out:
/*
* Construct an hdb_entry from a directory entry.
*/
-static krb5_error_code hdb_samba4_trust_message2entry(krb5_context context, HDB *db,
+static krb5_error_code hdb_samba4_trust_message2entry(krb5_context context, HDB *db,
struct loadparm_context *lp_ctx,
TALLOC_CTX *mem_ctx, krb5_const_principal principal,
enum trust_direction direction,
@@ -745,7 +745,7 @@ static krb5_error_code hdb_samba4_trust_message2entry(krb5_context context, HDB
struct ldb_message *msg,
hdb_entry_ex *entry_ex)
{
-
+
const char *dnsdomain;
char *realm;
DATA_BLOB password_utf16;
@@ -843,16 +843,16 @@ static krb5_error_code hdb_samba4_trust_message2entry(krb5_context context, HDB
ret = ENOMEM;
goto out;
}
-
+
ret = krb5_keyblock_init(context,
ENCTYPE_ARCFOUR_HMAC,
- password_hash.hash, sizeof(password_hash.hash),
+ password_hash.hash, sizeof(password_hash.hash),
&key.key);
-
+
entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
entry_ex->entry.keys.len++;
}
-
+
entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
ret = copy_Principal(principal, entry_ex->entry.principal);
@@ -860,13 +860,13 @@ static krb5_error_code hdb_samba4_trust_message2entry(krb5_context context, HDB
krb5_clear_error_message(context);
goto out;
}
-
+
/* While we have copied the client principal, tests
* show that Win2k3 returns the 'corrected' realm, not
* the client-specified realm. This code attempts to
* replace the client principal's realm with the one
* we determine from our records */
-
+
krb5_principal_set_realm(context, entry_ex->entry.principal, realm);
entry_ex->entry.flags = int2HDBFlags(0);
entry_ex->entry.flags.immutable = 1;
@@ -875,7 +875,7 @@ static krb5_error_code hdb_samba4_trust_message2entry(krb5_context context, HDB
entry_ex->entry.flags.require_preauth = 1;
entry_ex->entry.pw_end = NULL;
-
+
entry_ex->entry.max_life = NULL;
entry_ex->entry.max_renew = NULL;
@@ -902,7 +902,7 @@ static krb5_error_code hdb_samba4_trust_message2entry(krb5_context context, HDB
p->msg = talloc_steal(p, msg);
p->samdb = (struct ldb_context *)db->hdb_db;
-
+
out:
if (ret != 0) {
/* This doesn't free ent itself, that is for the eventual caller to do */
@@ -915,7 +915,7 @@ out:
}
-static krb5_error_code hdb_samba4_lookup_trust(krb5_context context, struct ldb_context *ldb_ctx,
+static krb5_error_code hdb_samba4_lookup_trust(krb5_context context, struct ldb_context *ldb_ctx,
TALLOC_CTX *mem_ctx,
const char *realm,
struct ldb_dn *realm_dn,
@@ -959,7 +959,7 @@ static krb5_error_code hdb_samba4_open(krb5_context context, HDB *db, int flags,
krb5_warnx(context, "hdb_samba4_open: use of a master key incompatible with LDB\n");
krb5_set_error_message(context, ret, "hdb_samba4_open: use of a master key incompatible with LDB\n");
return ret;
- }
+ }
return 0;
}
@@ -984,25 +984,25 @@ static krb5_error_code hdb_samba4_rename(krb5_context context, HDB *db, const ch
return HDB_ERR_DB_INUSE;
}
-static krb5_error_code hdb_samba4_lookup_client(krb5_context context, HDB *db,
- struct loadparm_context *lp_ctx,
- TALLOC_CTX *mem_ctx,
+static krb5_error_code hdb_samba4_lookup_client(krb5_context context, HDB *db,
+ struct loadparm_context *lp_ctx,
+ TALLOC_CTX *mem_ctx,
krb5_const_principal principal,
const char **attrs,
- struct ldb_dn **realm_dn,
+ struct ldb_dn **realm_dn,
struct ldb_message **msg) {
NTSTATUS nt_status;
char *principal_string;
krb5_error_code ret;
ret = krb5_unparse_name(context, principal, &principal_string);
-
+
if (ret != 0) {
return ret;
}
-
+
nt_status = sam_get_results_principal((struct ldb_context *)db->hdb_db,
- mem_ctx, principal_string, attrs,
+ mem_ctx, principal_string, attrs,
realm_dn, msg);
free(principal_string);
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) {
@@ -1012,13 +1012,13 @@ static krb5_error_code hdb_samba4_lookup_client(krb5_context context, HDB *db,
} else if (!NT_STATUS_IS_OK(nt_status)) {
return EINVAL;
}
-
+
return ret;
}
-static krb5_error_code hdb_samba4_fetch_client(krb5_context context, HDB *db,
- struct loadparm_context *lp_ctx,
- TALLOC_CTX *mem_ctx,
+static krb5_error_code hdb_samba4_fetch_client(krb5_context context, HDB *db,
+ struct loadparm_context *lp_ctx,
+ TALLOC_CTX *mem_ctx,
krb5_const_principal principal,
unsigned flags,
hdb_entry_ex *entry_ex) {
@@ -1026,22 +1026,22 @@ static krb5_error_code hdb_samba4_fetch_client(krb5_context context, HDB *db,
krb5_error_code ret;
struct ldb_message *msg = NULL;
- ret = hdb_samba4_lookup_client(context, db, lp_ctx,
- mem_ctx, principal, user_attrs,
+ ret = hdb_samba4_lookup_client(context, db, lp_ctx,
+ mem_ctx, principal, user_attrs,
&realm_dn, &msg);
if (ret != 0) {
return ret;
}
-
- ret = hdb_samba4_message2entry(context, db, lp_ctx, mem_ctx,
+
+ ret = hdb_samba4_message2entry(context, db, lp_ctx, mem_ctx,
principal, HDB_SAMBA4_ENT_TYPE_CLIENT,
realm_dn, msg, entry_ex);
return ret;
}
-static krb5_error_code hdb_samba4_fetch_krbtgt(krb5_context context, HDB *db,
- struct loadparm_context *lp_ctx,
- TALLOC_CTX *mem_ctx,
+static krb5_error_code hdb_samba4_fetch_krbtgt(krb5_context context, HDB *db,
+ struct loadparm_context *lp_ctx,
+ TALLOC_CTX *mem_ctx,
krb5_const_principal principal,
unsigned flags,
hdb_entry_ex *entry_ex)
@@ -1062,18 +1062,18 @@ static krb5_error_code hdb_samba4_fetch_krbtgt(krb5_context context, HDB *db,
if (lp_is_my_domain_or_realm(lp_ctx, principal->realm)
&& lp_is_my_domain_or_realm(lp_ctx, principal->name.name_string.val[1])) {
- /* us */
+ /* us */
/* Cludge, cludge cludge. If the realm part of krbtgt/realm,
* is in our db, then direct the caller at our primary
* krbtgt */
int lret;
char *realm_fixed;
-
- lret = gendb_search_single_extended_dn(db->hdb_db, mem_ctx,
+
+ lret = gendb_search_single_extended_dn(db->hdb_db, mem_ctx,
realm_dn, LDB_SCOPE_SUBTREE,
- &msg, krbtgt_attrs,
- "(&(objectClass=user)(samAccountName=krbtgt))");
+ &msg, krbtgt_attrs,
+ "(&(objectClass=user)(samAccountName=krbtgt))");
if (lret == LDB_ERR_NO_SUCH_OBJECT) {
krb5_warnx(context, "hdb_samba4_fetch: could not find own KRBTGT in DB!");
krb5_set_error_message(context, HDB_ERR_NOENTRY, "hdb_samba4_fetch: could not find own KRBTGT in DB!");
@@ -1083,19 +1083,19 @@ static krb5_error_code hdb_samba4_fetch_krbtgt(krb5_context context, HDB *db,
krb5_set_error_message(context, HDB_ERR_NOENTRY, "hdb_samba4_fetch: could not find own KRBTGT in DB: %s", ldb_errstring(db->hdb_db));
return HDB_ERR_NOENTRY;
}
-
+
realm_fixed = strupper_talloc(mem_ctx, lp_realm(lp_ctx));
if (!realm_fixed) {
ret = ENOMEM;
krb5_set_error_message(context, ret, "strupper_talloc: out of memory");
return ret;
}
-
+
ret = krb5_copy_principal(context, principal, &alloc_principal);
if (ret) {
return ret;
}
-
+
free(alloc_principal->name.name_string.val[1]);
alloc_principal->name.name_string.val[1] = strdup(realm_fixed);
talloc_free(realm_fixed);
@@ -1106,11 +1106,11 @@ static krb5_error_code hdb_samba4_fetch_krbtgt(krb5_context context, HDB *db,
}
principal = alloc_principal;
- ret = hdb_samba4_message2entry(context, db, lp_ctx, mem_ctx,
- principal, HDB_SAMBA4_ENT_TYPE_KRBTGT,
+ ret = hdb_samba4_message2entry(context, db, lp_ctx, mem_ctx,
+ principal, HDB_SAMBA4_ENT_TYPE_KRBTGT,
realm_dn, msg, entry_ex);
if (ret != 0) {
- krb5_warnx(context, "hdb_samba4_fetch: self krbtgt message2entry failed");
+ krb5_warnx(context, "hdb_samba4_fetch: self krbtgt message2entry failed");
}
return ret;
@@ -1132,35 +1132,35 @@ static krb5_error_code hdb_samba4_fetch_krbtgt(krb5_context context, HDB *db,
}
/* Trusted domains are under CN=system */
-
- ret = hdb_samba4_lookup_trust(context, (struct ldb_context *)db->hdb_db,
- mem_ctx,
+
+ ret = hdb_samba4_lookup_trust(context, (struct ldb_context *)db->hdb_db,
+ mem_ctx,
realm, realm_dn, &msg);
-
+
if (ret != 0) {
krb5_warnx(context, "hdb_samba4_fetch: could not find principal in DB");
krb5_set_error_message(context, ret, "hdb_samba4_fetch: could not find principal in DB");
return ret;
}
-
- ret = hdb_samba4_trust_message2entry(context, db, lp_ctx, mem_ctx,
- principal, direction,
+
+ ret = hdb_samba4_trust_message2entry(context, db, lp_ctx, mem_ctx,
+ principal, direction,
realm_dn, msg, entry_ex);
if (ret != 0) {
- krb5_warnx(context, "hdb_samba4_fetch: trust_message2entry failed");
+ krb5_warnx(context, "hdb_samba4_fetch: trust_message2entry failed");
}
return ret;
-
+
/* we should lookup trusted domains */
return HDB_ERR_NOENTRY;
}
}
-static krb5_error_code hdb_samba4_lookup_server(krb5_context context, HDB *db,
+static krb5_error_code hdb_samba4_lookup_server(krb5_context context, HDB *db,
struct loadparm_context *lp_ctx,
- TALLOC_CTX *mem_ctx,
+ TALLOC_CTX *mem_ctx,
krb5_const_principal principal,
const char **attrs,
struct ldb_dn **realm_dn,
@@ -1174,35 +1174,35 @@ static krb5_error_code hdb_samba4_lookup_server(krb5_context context, HDB *db,
NTSTATUS nt_status;
struct ldb_dn *user_dn;
char *principal_string;
-
- ret = krb5_unparse_name_flags(context, principal,
- KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+
+ ret = krb5_unparse_name_flags(context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM,
&principal_string);
if (ret != 0) {
return ret;
}
-
+
/* At this point we may find the host is known to be
* in a different realm, so we should generate a
* referral instead */
nt_status = crack_service_principal_name((struct ldb_context *)db->hdb_db,
- mem_ctx, principal_string,
+ mem_ctx, principal_string,
&user_dn, realm_dn);
free(principal_string);
-
+
if (!NT_STATUS_IS_OK(nt_status)) {
return HDB_ERR_NOENTRY;
}
-
+
ldb_ret = gendb_search_single_extended_dn((struct ldb_context *)db->hdb_db,
- mem_ctx,
+ mem_ctx,
user_dn, LDB_SCOPE_BASE,
msg, attrs,
"(objectClass=*)");
if (ldb_ret != LDB_SUCCESS) {
return HDB_ERR_NOENTRY;
}
-
+
} else {
int lret;
char *filter = NULL;
@@ -1210,20 +1210,20 @@ static krb5_error_code hdb_samba4_lookup_server(krb5_context context, HDB *db,
/* server as client principal case, but we must not lookup userPrincipalNames */
*realm_dn = ldb_get_default_basedn(db->hdb_db);
realm = krb5_principal_get_realm(context, principal);
-
+
/* TODO: Check if it is our realm, otherwise give referall */
-
+
ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &short_princ);
-
+
if (ret != 0) {
krb5_set_error_message(context, ret, "hdb_samba4_lookup_principal: could not parse principal");
krb5_warnx(context, "hdb_samba4_lookup_principal: could not parse principal");
return ret;
}
-
- lret = gendb_search_single_extended_dn(db->hdb_db, mem_ctx,
+
+ lret = gendb_search_single_extended_dn(db->hdb_db, mem_ctx,
*realm_dn, LDB_SCOPE_SUBTREE,
- msg, attrs, "(&(objectClass=user)(samAccountName=%s))",
+ msg, attrs, "(&(objectClass=user)(samAccountName=%s))",
ldb_binary_encode_string(mem_ctx, short_princ));
free(short_princ);
if (lret == LDB_ERR_NO_SUCH_OBJECT) {
@@ -1231,7 +1231,7 @@ static krb5_error_code hdb_samba4_lookup_server(krb5_context context, HDB *db,
return HDB_ERR_NOENTRY;
}
if (lret != LDB_SUCCESS) {
- DEBUG(3, ("Failed single search for for %s - %s\n",
+ DEBUG(3, ("Failed single search for for %s - %s\n",
filter, ldb_errstring(db->hdb_db)));
return HDB_ERR_NOENTRY;
}
@@ -1240,9 +1240,9 @@ static krb5_error_code hdb_samba4_lookup_server(krb5_context context, HDB *db,
return 0;
}
-static krb5_error_code hdb_samba4_fetch_server(krb5_context context, HDB *db,
+static krb5_error_code hdb_samba4_fetch_server(krb5_context context, HDB *db,
struct loadparm_context *lp_ctx,
- TALLOC_CTX *mem_ctx,
+ TALLOC_CTX *mem_ctx,
krb5_const_principal principal,
unsigned flags,
hdb_entry_ex *entry_ex)
@@ -1251,23 +1251,23 @@ static krb5_error_code hdb_samba4_fetch_server(krb5_context context, HDB *db,
struct ldb_dn *realm_dn;
struct ldb_message *msg;
- ret = hdb_samba4_lookup_server(context, db, lp_ctx, mem_ctx, principal,
+ ret = hdb_samba4_lookup_server(context, db, lp_ctx, mem_ctx, principal,
server_attrs, &realm_dn, &msg);
if (ret != 0) {
return ret;
}
- ret = hdb_samba4_message2entry(context, db, lp_ctx, mem_ctx,
+ ret = hdb_samba4_message2entry(context, db, lp_ctx, mem_ctx,
principal, HDB_SAMBA4_ENT_TYPE_SERVER,
realm_dn, msg, entry_ex);
if (ret != 0) {
- krb5_warnx(context, "hdb_samba4_fetch: message2entry failed");
+ krb5_warnx(context, "hdb_samba4_fetch: message2entry failed");
}
return ret;
}
-
-static krb5_error_code hdb_samba4_fetch(krb5_context context, HDB *db,
+
+static krb5_error_code hdb_samba4_fetch(krb5_context context, HDB *db,
krb5_const_principal principal,
unsigned flags,
hdb_entry_ex *entry_ex)
@@ -1345,9 +1345,9 @@ static krb5_error_code hdb_samba4_seq(krb5_context context, HDB *db, unsigned fl
}
if (priv->index < priv->count) {
- ret = hdb_samba4_message2entry(context, db, priv->lp_ctx,
- mem_ctx,
- NULL, HDB_SAMBA4_ENT_TYPE_ANY,
+ ret = hdb_samba4_message2entry(context, db, priv->lp_ctx,
+ mem_ctx,
+ NULL, HDB_SAMBA4_ENT_TYPE_ANY,
priv->realm_dn, priv->msgs[priv->index++], entry);
} else {
ret = HDB_ERR_NOENTRY;
@@ -1366,7 +1366,7 @@ static krb5_error_code hdb_samba4_firstkey(krb5_context context, HDB *db, unsign
hdb_entry_ex *entry)
{
struct ldb_context *ldb_ctx = (struct ldb_context *)db->hdb_db;
- struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb_ctx, "loadparm"),
+ struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb_ctx, "loadparm"),
struct loadparm_context);
struct hdb_samba4_seq *priv = (struct hdb_samba4_seq *)db->hdb_dbc;
char *realm;
@@ -1407,7 +1407,7 @@ static krb5_error_code hdb_samba4_firstkey(krb5_context context, HDB *db, unsign
talloc_free(priv);
return ret;
}
-
+
lret = ldb_search(ldb_ctx, priv, &res,
priv->realm_dn, LDB_SCOPE_SUBTREE, user_attrs,
"(objectClass=user)");
@@ -1449,14 +1449,14 @@ static krb5_error_code hdb_samba4_destroy(krb5_context context, HDB *db)
/* Check if a given entry may delegate to this target principal
*
- * This is currently a very nasty hack - allowing only delegation to itself.
+ * This is currently a very nasty hack - allowing only delegation to itself.
*/
-krb5_error_code hdb_samba4_check_constrained_delegation(krb5_context context, HDB *db,
+krb5_error_code hdb_samba4_check_constrained_delegation(krb5_context context, HDB *db,
hdb_entry_ex *entry,
krb5_const_principal target_principal)
{
struct ldb_context *ldb_ctx = (struct ldb_context *)db->hdb_db;
- struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb_ctx, "loadparm"),
+ struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb_ctx, "loadparm"),
struct loadparm_context);
krb5_error_code ret;
krb5_principal enterprise_prinicpal = NULL;
@@ -1468,7 +1468,7 @@ krb5_error_code hdb_samba4_check_constrained_delegation(krb5_context context, HD
const char *delegation_check_attrs[] = {
"objectSid", NULL
};
-
+
TALLOC_CTX *mem_ctx = talloc_named(db, 0, "hdb_samba4_check_constrained_delegation");
if (!mem_ctx) {
@@ -1481,12 +1481,12 @@ krb5_error_code hdb_samba4_check_constrained_delegation(krb5_context context, HD
/* Need to reparse the enterprise principal to find the real target */
if (target_principal->name.name_string.len != 1) {
ret = KRB5_PARSE_MALFORMED;
- krb5_set_error_message(context, ret, "hdb_samba4_check_constrained_delegation: request for delegation to enterprise principal with wrong (%d) number of components",
- target_principal->name.name_string.len);
+ krb5_set_error_message(context, ret, "hdb_samba4_check_constrained_delegation: request for delegation to enterprise principal with wrong (%d) number of components",
+ target_principal->name.name_string.len);
talloc_free(mem_ctx);
return ret;
}
- ret = krb5_parse_name(context, target_principal->name.name_string.val[0],
+ ret = krb5_parse_name(context, target_principal->name.name_string.val[0],
&enterprise_prinicpal);
if (ret) {
talloc_free(mem_ctx);
@@ -1495,7 +1495,7 @@ krb5_error_code hdb_samba4_check_constrained_delegation(krb5_context context, HD
target_principal = enterprise_prinicpal;
}
- ret = hdb_samba4_lookup_server(context, db, lp_ctx, mem_ctx, target_principal,
+ ret = hdb_samba4_lookup_server(context, db, lp_ctx, mem_ctx, target_principal,
delegation_check_attrs, &realm_dn, &msg);
krb5_free_principal(context, enterprise_prinicpal);
@@ -1525,12 +1525,12 @@ krb5_error_code hdb_samba4_check_constrained_delegation(krb5_context context, HD
* database. Allow a mismatch where they both refer to the same
* SID */
-krb5_error_code hdb_samba4_check_pkinit_ms_upn_match(krb5_context context, HDB *db,
+krb5_error_code hdb_samba4_check_pkinit_ms_upn_match(krb5_context context, HDB *db,
hdb_entry_ex *entry,
krb5_const_principal certificate_principal)
{
struct ldb_context *ldb_ctx = (struct ldb_context *)db->hdb_db;
- struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb_ctx, "loadparm"),
+ struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb_ctx, "loadparm"),
struct loadparm_context);
krb5_error_code ret;
struct ldb_dn *realm_dn;
@@ -1541,7 +1541,7 @@ krb5_error_code hdb_samba4_check_pkinit_ms_upn_match(krb5_context context, HDB *
const char *ms_upn_check_attrs[] = {
"objectSid", NULL
};
-
+
TALLOC_CTX *mem_ctx = talloc_named(db, 0, "hdb_samba4_check_constrained_delegation");
if (!mem_ctx) {
@@ -1550,10 +1550,10 @@ krb5_error_code hdb_samba4_check_pkinit_ms_upn_match(krb5_context context, HDB *
return ret;
}
- ret = hdb_samba4_lookup_client(context, db, lp_ctx,
+ ret = hdb_samba4_lookup_client(context, db, lp_ctx,
mem_ctx, certificate_principal,
ms_upn_check_attrs, &realm_dn, &msg);
-
+
if (ret != 0) {
talloc_free(mem_ctx);
return ret;
@@ -1579,8 +1579,8 @@ krb5_error_code hdb_samba4_check_pkinit_ms_upn_match(krb5_context context, HDB *
* (hdb_samba4_create) from the kpasswdd -> krb5 -> keytab_hdb -> hdb
* code */
-NTSTATUS hdb_samba4_create_kdc(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev_ctx,
+NTSTATUS hdb_samba4_create_kdc(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
struct loadparm_context *lp_ctx,
krb5_context context, struct HDB **db)
{
@@ -1602,7 +1602,7 @@ NTSTATUS hdb_samba4_create_kdc(TALLOC_CTX *mem_ctx,
* allow us to share the samdb backend context with other parts of the
* system. For now we can't as we need to override the
* credentials to set CRED_DONT_USE_KERBEROS, which would
- * break other users of the system_session */
+ * break other users of the system_session */
DEBUG(0,("FIXME: Using new system session for hdb\n"));
nt_status = auth_system_session_info(*db, lp_ctx, &session_info);
if (!NT_STATUS_IS_OK(nt_status)) {
@@ -1614,7 +1614,7 @@ NTSTATUS hdb_samba4_create_kdc(TALLOC_CTX *mem_ctx,
return NT_STATUS_INTERNAL_ERROR;
}
#endif
-
+
/* The idea here is very simple. Using Kerberos to
* authenticate the KDC to the LDAP server is higly likely to
* be circular.
@@ -1622,8 +1622,8 @@ NTSTATUS hdb_samba4_create_kdc(TALLOC_CTX *mem_ctx,
* In future we may set this up to use EXERNAL and SSL
* certificates, for now it will almost certainly be NTLMSSP_SET_USERNAME
*/
-
- cli_credentials_set_kerberos_state(session_info->credentials,
+
+ cli_credentials_set_kerberos_state(session_info->credentials,
CRED_DONT_USE_KERBEROS);
/* Setup the link to LDB */
@@ -1663,6 +1663,7 @@ static krb5_error_code hdb_samba4_create(krb5_context context, struct HDB **db,
NTSTATUS nt_status;
void *ptr;
struct hdb_samba4_context *hdb_samba4_context;
+
if (sscanf(arg, "&%p", &ptr) != 1) {
return EINVAL;
}
@@ -1686,6 +1687,6 @@ static krb5_error_code hdb_samba4_create(krb5_context context, struct HDB **db,
*/
struct hdb_method hdb_samba4 = {
.interface_version = HDB_INTERFACE_VERSION,
- .prefix = "samba4",
+ .prefix = "samba4",
.create = hdb_samba4_create
};
diff --git a/source4/kdc/hdb-samba4.h b/source4/kdc/hdb-samba4.h
index fc2f9c1310..5f85ce188c 100644
--- a/source4/kdc/hdb-samba4.h
+++ b/source4/kdc/hdb-samba4.h
@@ -1,21 +1,21 @@
-/*
+/*
Unix SMB/CIFS implementation.
KDC structures
Copyright (C) Andrew Tridgell 2005
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/>.
*/
diff --git a/source4/kdc/kdc.c b/source4/kdc/kdc.c
index 6a9df0bf02..93f1c7d6ec 100644
--- a/source4/kdc/kdc.c
+++ b/source4/kdc/kdc.c
@@ -1,4 +1,4 @@
-/*
+/*
Unix SMB/CIFS implementation.
KDC Server startup
@@ -11,12 +11,12 @@
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/>.
*/
@@ -42,22 +42,15 @@
#include "librpc/gen_ndr/ndr_misc.h"
-/* Disgusting hack to get a mem_ctx and lp_ctx into the hdb plugin, when
+/* Disgusting hack to get a mem_ctx and lp_ctx into the hdb plugin, when
* used as a keytab */
TALLOC_CTX *hdb_samba4_mem_ctx;
struct tevent_context *hdb_samba4_ev_ctx;
struct loadparm_context *hdb_samba4_lp_ctx;
-/* hold all the info needed to send a reply */
-struct kdc_reply {
- struct kdc_reply *next, *prev;
- struct socket_address *dest;
- DATA_BLOB packet;
-};
-
typedef bool (*kdc_process_fn_t)(struct kdc_server *kdc,
- TALLOC_CTX *mem_ctx,
- DATA_BLOB *input,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *input,
DATA_BLOB *reply,
struct tsocket_address *peer_addr,
struct tsocket_address *my_addr,
@@ -65,15 +58,11 @@ typedef bool (*kdc_process_fn_t)(struct kdc_server *kdc,
/* hold information about one kdc socket */
struct kdc_socket {
- struct socket_context *sock;
struct kdc_server *kdc;
- struct tevent_fd *fde;
-
- /* a queue of outgoing replies that have been deferred */
- struct kdc_reply *send_queue;
-
+ struct tsocket_address *local_address;
kdc_process_fn_t process;
};
+
/*
state of an open tcp connection
*/
@@ -82,152 +71,11 @@ struct kdc_tcp_connection {
struct stream_connection *conn;
/* the kdc_server the connection belongs to */
- struct kdc_server *kdc;
+ struct kdc_socket *kdc_socket;
struct packet_context *packet;
-
- kdc_process_fn_t process;
};
-/*
- handle fd send events on a KDC socket
-*/
-static void kdc_send_handler(struct kdc_socket *kdc_socket)
-{
- while (kdc_socket->send_queue) {
- struct kdc_reply *rep = kdc_socket->send_queue;
- NTSTATUS status;
- size_t sendlen;
-
- status = socket_sendto(kdc_socket->sock, &rep->packet, &sendlen,
- rep->dest);
- if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
- break;
- }
- if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_BUFFER_SIZE)) {
- /* Replace with a krb err, response to big */
- }
-
- DLIST_REMOVE(kdc_socket->send_queue, rep);
- talloc_free(rep);
- }
-
- if (kdc_socket->send_queue == NULL) {
- EVENT_FD_NOT_WRITEABLE(kdc_socket->fde);
- }
-}
-
-
-/*
- handle fd recv events on a KDC socket
-*/
-static void kdc_recv_handler(struct kdc_socket *kdc_socket)
-{
- NTSTATUS status;
- TALLOC_CTX *tmp_ctx = talloc_new(kdc_socket);
- DATA_BLOB blob;
- struct kdc_reply *rep;
- DATA_BLOB reply;
- size_t nread, dsize;
- struct socket_address *src;
- struct socket_address *my_addr;
- struct tsocket_address *tsrcaddr;
- struct tsocket_address *tmyaddr;
- int ret;
-
- status = socket_pending(kdc_socket->sock, &dsize);
- if (!NT_STATUS_IS_OK(status)) {
- talloc_free(tmp_ctx);
- return;
- }
-
- blob = data_blob_talloc(tmp_ctx, NULL, dsize);
- if (blob.data == NULL) {
- /* hope this is a temporary low memory condition */
- talloc_free(tmp_ctx);
- return;
- }
-
- status = socket_recvfrom(kdc_socket->sock, blob.data, blob.length, &nread,
- tmp_ctx, &src);
- if (!NT_STATUS_IS_OK(status)) {
- talloc_free(tmp_ctx);
- return;
- }
- blob.length = nread;
-
- DEBUG(10,("Received krb5 UDP packet of length %lu from %s:%u\n",
- (long)blob.length, src->addr, (uint16_t)src->port));
-
- my_addr = socket_get_my_addr(kdc_socket->sock, tmp_ctx);
- if (!my_addr) {
- talloc_free(tmp_ctx);
- return;
- }
-
- ret = tsocket_address_bsd_from_sockaddr(tmp_ctx, src->sockaddr,
- src->sockaddrlen, &tsrcaddr);
- if (ret < 0) {
- talloc_free(tmp_ctx);
- return;
- }
-
- ret = tsocket_address_bsd_from_sockaddr(tmp_ctx, my_addr->sockaddr,
- my_addr->sockaddrlen, &tmyaddr);
- if (ret < 0) {
- talloc_free(tmp_ctx);
- return;
- }
-
- /* Call krb5 */
- ret = kdc_socket->process(kdc_socket->kdc,
- tmp_ctx,
- &blob,
- &reply,
- tsrcaddr,
- tmyaddr,
- 1 /* Datagram */);
- if (!ret) {
- talloc_free(tmp_ctx);
- return;
- }
-
- /* queue a pending reply */
- rep = talloc(kdc_socket, struct kdc_reply);
- if (rep == NULL) {
- talloc_free(tmp_ctx);
- return;
- }
- rep->dest = talloc_steal(rep, src);
- rep->packet = reply;
- talloc_steal(rep, reply.data);
-
- if (rep->packet.data == NULL) {
- talloc_free(rep);
- talloc_free(tmp_ctx);
- return;
- }
-
- DLIST_ADD_END(kdc_socket->send_queue, rep, struct kdc_reply *);
- EVENT_FD_WRITEABLE(kdc_socket->fde);
- talloc_free(tmp_ctx);
-}
-
-/*
- handle fd events on a KDC socket
-*/
-static void kdc_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
- uint16_t flags, void *private_data)
-{
- struct kdc_socket *kdc_socket = talloc_get_type(private_data, struct kdc_socket);
- if (flags & EVENT_FD_WRITE) {
- kdc_send_handler(kdc_socket);
- }
- if (flags & EVENT_FD_READ) {
- kdc_recv_handler(kdc_socket);
- }
-}
-
static void kdc_tcp_terminate_connection(struct kdc_tcp_connection *kdcconn, const char *reason)
{
stream_terminate_connection(kdcconn->conn, reason);
@@ -244,49 +92,18 @@ static NTSTATUS kdc_tcp_recv(void *private_data, DATA_BLOB blob)
TALLOC_CTX *tmp_ctx = talloc_new(kdcconn);
int ret;
DATA_BLOB input, reply;
- struct socket_address *src_addr;
- struct socket_address *my_addr;
- struct tsocket_address *tsrcaddr;
- struct tsocket_address *tmyaddr;
-
talloc_steal(tmp_ctx, blob.data);
- src_addr = socket_get_peer_addr(kdcconn->conn->socket, tmp_ctx);
- if (!src_addr) {
- talloc_free(tmp_ctx);
- return NT_STATUS_NO_MEMORY;
- }
-
- my_addr = socket_get_my_addr(kdcconn->conn->socket, tmp_ctx);
- if (!my_addr) {
- talloc_free(tmp_ctx);
- return NT_STATUS_NO_MEMORY;
- }
-
- ret = tsocket_address_bsd_from_sockaddr(tmp_ctx, src_addr->sockaddr,
- src_addr->sockaddrlen, &tsrcaddr);
- if (ret < 0) {
- talloc_free(tmp_ctx);
- return NT_STATUS_NO_MEMORY;
- }
-
- ret = tsocket_address_bsd_from_sockaddr(tmp_ctx, my_addr->sockaddr,
- my_addr->sockaddrlen, &tmyaddr);
- if (ret < 0) {
- talloc_free(tmp_ctx);
- return NT_STATUS_NO_MEMORY;
- }
-
/* Call krb5 */
- input = data_blob_const(blob.data + 4, blob.length - 4);
-
- ret = kdcconn->process(kdcconn->kdc,
- tmp_ctx,
- &input,
- &reply,
- tsrcaddr,
- tmyaddr,
- 0 /* Not datagram */);
+ input = data_blob_const(blob.data + 4, blob.length - 4);
+
+ ret = kdcconn->kdc_socket->process(kdcconn->kdc_socket->kdc,
+ tmp_ctx,
+ &input,
+ &reply,
+ kdcconn->conn->remote_address,
+ kdcconn->conn->local_address,
+ 0 /* Not datagram */);
if (!ret) {
talloc_free(tmp_ctx);
return NT_STATUS_INTERNAL_ERROR;
@@ -300,7 +117,7 @@ static NTSTATUS kdc_tcp_recv(void *private_data, DATA_BLOB blob)
}
RSIVAL(blob.data, 0, reply.length);
- memcpy(blob.data + 4, reply.data, reply.length);
+ memcpy(blob.data + 4, reply.data, reply.length);
status = packet_send(kdcconn->packet, blob);
if (!NT_STATUS_IS_OK(status)) {
@@ -349,8 +166,8 @@ static void kdc_tcp_send(struct stream_connection *conn, uint16_t flags)
*/
static bool kdc_process(struct kdc_server *kdc,
- TALLOC_CTX *mem_ctx,
- DATA_BLOB *input,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *input,
DATA_BLOB *reply,
struct tsocket_address *peer_addr,
struct tsocket_address *my_addr,
@@ -377,7 +194,7 @@ static bool kdc_process(struct kdc_server *kdc,
DEBUG(10,("Received KDC packet of length %lu from %s\n",
(long)input->length - 4, pa));
- ret = krb5_kdc_process_krb5_request(kdc->smb_krb5_context->krb5_context,
+ ret = krb5_kdc_process_krb5_request(kdc->smb_krb5_context->krb5_context,
kdc->config,
input->data, input->length,
&k5_reply,
@@ -392,7 +209,7 @@ static bool kdc_process(struct kdc_server *kdc,
*reply = data_blob_talloc(mem_ctx, k5_reply.data, k5_reply.length);
krb5_data_free(&k5_reply);
} else {
- *reply = data_blob(NULL, 0);
+ *reply = data_blob(NULL, 0);
}
return true;
}
@@ -410,9 +227,8 @@ static void kdc_tcp_accept(struct stream_connection *conn)
stream_terminate_connection(conn, "kdc_tcp_accept: out of memory");
return;
}
- kdcconn->conn = conn;
- kdcconn->kdc = kdc_socket->kdc;
- kdcconn->process = kdc_socket->process;
+ kdcconn->conn = conn;
+ kdcconn->kdc_socket = kdc_socket;
conn->private_data = kdcconn;
kdcconn->packet = packet_init(kdcconn);
@@ -437,6 +253,105 @@ static const struct stream_server_ops kdc_tcp_stream_ops = {
.send_handler = kdc_tcp_send
};
+/* hold information about one kdc/kpasswd udp socket */
+struct kdc_udp_socket {
+ struct kdc_socket *kdc_socket;
+ struct tdgram_context *dgram;
+ struct tevent_queue *send_queue;
+};
+
+struct kdc_udp_call {
+ struct tsocket_address *src;
+ DATA_BLOB in;
+ DATA_BLOB out;
+};
+
+static void kdc_udp_call_sendto_done(struct tevent_req *subreq);
+
+static void kdc_udp_call_loop(struct tevent_req *subreq)
+{
+ struct kdc_udp_socket *sock = tevent_req_callback_data(subreq,
+ struct kdc_udp_socket);
+ struct kdc_udp_call *call;
+ uint8_t *buf;
+ ssize_t len;
+ int sys_errno;
+ int ret;
+
+ call = talloc(sock, struct kdc_udp_call);
+ if (call == NULL) {
+ talloc_free(call);
+ goto done;
+ }
+
+ len = tdgram_recvfrom_recv(subreq, &sys_errno,
+ call, &buf, &call->src);
+ TALLOC_FREE(subreq);
+ if (len == -1) {
+ talloc_free(call);
+ goto done;
+ }
+
+ call->in.data = buf;
+ call->in.length = len;
+
+ DEBUG(10,("Received krb5 UDP packet of length %lu from %s\n",
+ (long)call->in.length,
+ tsocket_address_string(call->src, call)));
+
+ /* Call krb5 */
+ ret = sock->kdc_socket->process(sock->kdc_socket->kdc,
+ call,
+ &call->in,
+ &call->out,
+ call->src,
+ sock->kdc_socket->local_address,
+ 1 /* Datagram */);
+ if (!ret) {
+ talloc_free(call);
+ goto done;
+ }
+
+ subreq = tdgram_sendto_queue_send(call,
+ sock->kdc_socket->kdc->task->event_ctx,
+ sock->dgram,
+ sock->send_queue,
+ call->out.data,
+ call->out.length,
+ call->src);
+ if (subreq == NULL) {
+ talloc_free(call);
+ goto done;
+ }
+ tevent_req_set_callback(subreq, kdc_udp_call_sendto_done, call);
+
+done:
+ subreq = tdgram_recvfrom_send(sock,
+ sock->kdc_socket->kdc->task->event_ctx,
+ sock->dgram);
+ if (subreq == NULL) {
+ task_server_terminate(sock->kdc_socket->kdc->task,
+ "no memory for tdgram_recvfrom_send",
+ true);
+ return;
+ }
+ tevent_req_set_callback(subreq, kdc_udp_call_loop, sock);
+}
+
+static void kdc_udp_call_sendto_done(struct tevent_req *subreq)
+{
+ struct kdc_udp_call *call = tevent_req_callback_data(subreq,
+ struct kdc_udp_call);
+ ssize_t ret;
+ int sys_errno;
+
+ ret = tdgram_sendto_queue_recv(subreq, &sys_errno);
+
+ /* We don't care about errors */
+
+ talloc_free(call);
+}
+
/*
start listening on the given address
*/
@@ -447,47 +362,32 @@ static NTSTATUS kdc_add_socket(struct kdc_server *kdc,
uint16_t port,
kdc_process_fn_t process)
{
- struct kdc_socket *kdc_socket;
- struct socket_address *socket_address;
+ struct kdc_socket *kdc_socket;
+ struct kdc_udp_socket *kdc_udp_socket;
+ struct tevent_req *udpsubreq;
NTSTATUS status;
+ int ret;
kdc_socket = talloc(kdc, struct kdc_socket);
NT_STATUS_HAVE_NO_MEMORY(kdc_socket);
- status = socket_create("ip", SOCKET_TYPE_DGRAM, &kdc_socket->sock, 0);
- if (!NT_STATUS_IS_OK(status)) {
- talloc_free(kdc_socket);
- return status;
- }
-
kdc_socket->kdc = kdc;
- kdc_socket->send_queue = NULL;
kdc_socket->process = process;
- talloc_steal(kdc_socket, kdc_socket->sock);
-
- kdc_socket->fde = event_add_fd(kdc->task->event_ctx, kdc,
- socket_get_fd(kdc_socket->sock), EVENT_FD_READ,
- kdc_socket_handler, kdc_socket);
-
- socket_address = socket_address_from_strings(kdc_socket, kdc_socket->sock->backend_name,
- address, port);
- NT_STATUS_HAVE_NO_MEMORY_AND_FREE(socket_address, kdc_socket);
-
- status = socket_listen(kdc_socket->sock, socket_address, 0, 0);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0,("Failed to bind to %s:%d UDP for %s - %s\n",
- address, port, name, nt_errstr(status)));
- talloc_free(kdc_socket);
+ ret = tsocket_address_inet_from_strings(kdc_socket, "ip",
+ address, port,
+ &kdc_socket->local_address);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
return status;
}
- status = stream_setup_socket(kdc->task->event_ctx,
+ status = stream_setup_socket(kdc->task->event_ctx,
kdc->task->lp_ctx,
- model_ops,
- &kdc_tcp_stream_ops,
- "ip", address, &port,
- lp_socket_options(kdc->task->lp_ctx),
+ model_ops,
+ &kdc_tcp_stream_ops,
+ "ip", address, &port,
+ lp_socket_options(kdc->task->lp_ctx),
kdc_socket);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
@@ -496,6 +396,32 @@ static NTSTATUS kdc_add_socket(struct kdc_server *kdc,
return status;
}
+ kdc_udp_socket = talloc(kdc_socket, struct kdc_udp_socket);
+ NT_STATUS_HAVE_NO_MEMORY(kdc_udp_socket);
+
+ kdc_udp_socket->kdc_socket = kdc_socket;
+
+ ret = tdgram_inet_udp_socket(kdc_socket->local_address,
+ NULL,
+ kdc_udp_socket,
+ &kdc_udp_socket->dgram);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
+ DEBUG(0,("Failed to bind to %s:%u UDP - %s\n",
+ address, port, nt_errstr(status)));
+ return status;
+ }
+
+ kdc_udp_socket->send_queue = tevent_queue_create(kdc_udp_socket,
+ "kdc_udp_send_queue");
+ NT_STATUS_HAVE_NO_MEMORY(kdc_udp_socket->send_queue);
+
+ udpsubreq = tdgram_recvfrom_send(kdc_udp_socket,
+ kdc->task->event_ctx,
+ kdc_udp_socket->dgram);
+ NT_STATUS_HAVE_NO_MEMORY(udpsubreq);
+ tevent_req_set_callback(udpsubreq, kdc_udp_call_loop, kdc_udp_socket);
+
return NT_STATUS_OK;
}
@@ -522,7 +448,7 @@ static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc, struct loadparm_c
}
num_interfaces = iface_count(ifaces);
-
+
for (i=0; i<num_interfaces; i++) {
const char *address = talloc_strdup(tmp_ctx, iface_n_ip(ifaces, i));
uint16_t kdc_port = lp_krb5_port(lp_ctx);
@@ -549,7 +475,7 @@ static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc, struct loadparm_c
}
-static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
+static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
struct kdc_check_generic_kerberos *r)
{
struct PAC_Validate pac_validate;
@@ -567,14 +493,14 @@ static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
/* There is no reply to this request */
r->out.generic_reply = data_blob(NULL, 0);
- ndr_err = ndr_pull_struct_blob(&r->in.generic_request, msg,
- lp_iconv_convenience(kdc->task->lp_ctx),
+ ndr_err = ndr_pull_struct_blob(&r->in.generic_request, msg,
+ lp_iconv_convenience(kdc->task->lp_ctx),
&pac_validate,
(ndr_pull_flags_fn_t)ndr_pull_PAC_Validate);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
return NT_STATUS_INVALID_PARAMETER;
}
-
+
if (pac_validate.MessageType != 3) {
/* We don't implement any other message types - such as certificate validation - yet */
return NT_STATUS_INVALID_PARAMETER;
@@ -585,10 +511,10 @@ static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
|| pac_validate.ChecksumAndSignature.length < pac_validate.SignatureLength ) {
return NT_STATUS_INVALID_PARAMETER;
}
-
- srv_sig = data_blob_const(pac_validate.ChecksumAndSignature.data,
+
+ srv_sig = data_blob_const(pac_validate.ChecksumAndSignature.data,
pac_validate.ChecksumLength);
-
+
if (pac_validate.SignatureType == CKSUMTYPE_HMAC_MD5) {
etype = ETYPE_ARCFOUR_HMAC_MD5;
} else {
@@ -599,16 +525,16 @@ static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
}
}
- ret = krb5_make_principal(kdc->smb_krb5_context->krb5_context, &principal,
+ ret = krb5_make_principal(kdc->smb_krb5_context->krb5_context, &principal,
lp_realm(kdc->task->lp_ctx),
- "krbtgt", lp_realm(kdc->task->lp_ctx),
+ "krbtgt", lp_realm(kdc->task->lp_ctx),
NULL);
if (ret != 0) {
return NT_STATUS_NO_MEMORY;
}
- ret = kdc->config->db[0]->hdb_fetch(kdc->smb_krb5_context->krb5_context,
+ ret = kdc->config->db[0]->hdb_fetch(kdc->smb_krb5_context->krb5_context,
kdc->config->db[0],
principal,
HDB_F_GET_KRBTGT | HDB_F_DECRYPT,
@@ -617,10 +543,10 @@ static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
if (ret != 0) {
hdb_free_entry(kdc->smb_krb5_context->krb5_context, &ent);
krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal);
-
+
return NT_STATUS_LOGON_FAILURE;
}
-
+
ret = hdb_enctype2key(kdc->smb_krb5_context->krb5_context, &ent.entry, etype, &key);
if (ret != 0) {
@@ -630,11 +556,11 @@ static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
}
keyblock = key->key;
-
+
kdc_sig.type = pac_validate.SignatureType;
kdc_sig.signature = data_blob_const(&pac_validate.ChecksumAndSignature.data[pac_validate.ChecksumLength],
pac_validate.SignatureLength);
- ret = check_pac_checksum(msg, srv_sig, &kdc_sig,
+ ret = check_pac_checksum(msg, srv_sig, &kdc_sig,
kdc->smb_krb5_context->krb5_context, &keyblock);
hdb_free_entry(kdc->smb_krb5_context->krb5_context, &ent);
@@ -643,7 +569,7 @@ static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
if (ret != 0) {
return NT_STATUS_LOGON_FAILURE;
}
-
+
return NT_STATUS_OK;
}
@@ -691,21 +617,21 @@ static void kdc_task_init(struct task_server *task)
ret = smb_krb5_init_context(kdc, task->event_ctx, task->lp_ctx, &kdc->smb_krb5_context);
if (ret) {
- DEBUG(1,("kdc_task_init: krb5_init_context failed (%s)\n",
+ DEBUG(1,("kdc_task_init: krb5_init_context failed (%s)\n",
error_message(ret)));
task_server_terminate(task, "kdc: krb5_init_context failed", true);
- return;
+ return;
}
krb5_add_et_list(kdc->smb_krb5_context->krb5_context, initialize_hdb_error_table_r);
- ret = krb5_kdc_get_config(kdc->smb_krb5_context->krb5_context,
+ ret = krb5_kdc_get_config(kdc->smb_krb5_context->krb5_context,
&kdc->config);
if(ret) {
task_server_terminate(task, "kdc: failed to get KDC configuration", true);
return;
}
-
+
kdc->config->logf = kdc->smb_krb5_context->logf;
kdc->config->db = talloc(kdc, struct HDB *);
if (!kdc->config->db) {
@@ -713,13 +639,13 @@ static void kdc_task_init(struct task_server *task)
return;
}
kdc->config->num_db = 1;
-
- status = hdb_samba4_create_kdc(kdc, task->event_ctx, task->lp_ctx,
- kdc->smb_krb5_context->krb5_context,
+
+ status = hdb_samba4_create_kdc(kdc, task->event_ctx, task->lp_ctx,
+ kdc->smb_krb5_context->krb5_context,
&kdc->config->db[0]);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "kdc: hdb_samba4_create_kdc (setup KDC database) failed", true);
- return;
+ return;
}
/* Register hdb-samba4 hooks for use as a keytab */
@@ -727,13 +653,13 @@ static void kdc_task_init(struct task_server *task)
kdc->hdb_samba4_context = talloc(kdc, struct hdb_samba4_context);
if (!kdc->hdb_samba4_context) {
task_server_terminate(task, "kdc: out of memory", true);
- return;
+ return;
}
kdc->hdb_samba4_context->ev_ctx = task->event_ctx;
kdc->hdb_samba4_context->lp_ctx = task->lp_ctx;
- ret = krb5_plugin_register(kdc->smb_krb5_context->krb5_context,
+ ret = krb5_plugin_register(kdc->smb_krb5_context->krb5_context,
PLUGIN_TYPE_DATA, "hdb",
&hdb_samba4);
if(ret) {
@@ -748,7 +674,7 @@ static void kdc_task_init(struct task_server *task)
}
/* Registar WinDC hooks */
- ret = krb5_plugin_register(kdc->smb_krb5_context->krb5_context,
+ ret = krb5_plugin_register(kdc->smb_krb5_context->krb5_context,
PLUGIN_TYPE_DATA, "windc",
&windc_plugin_table);
if(ret) {
@@ -765,7 +691,7 @@ static void kdc_task_init(struct task_server *task)
return;
}
- status = IRPC_REGISTER(task->msg_ctx, irpc, KDC_CHECK_GENERIC_KERBEROS,
+ status = IRPC_REGISTER(task->msg_ctx, irpc, KDC_CHECK_GENERIC_KERBEROS,
kdc_check_generic_kerberos, kdc);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "nbtd failed to setup monitoring", true);
diff --git a/source4/kdc/kdc.h b/source4/kdc/kdc.h
index 4a715fd498..b9cf621537 100644
--- a/source4/kdc/kdc.h
+++ b/source4/kdc/kdc.h
@@ -1,21 +1,21 @@
-/*
+/*
Unix SMB/CIFS implementation.
KDC structures
Copyright (C) Andrew Tridgell 2005
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/>.
*/
@@ -33,8 +33,8 @@ struct tsocket_address;
bool kpasswdd_process(struct kdc_server *kdc,
- TALLOC_CTX *mem_ctx,
- DATA_BLOB *input,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *input,
DATA_BLOB *reply,
struct tsocket_address *peer_addr,
struct tsocket_address *my_addr,
diff --git a/source4/kdc/kpasswdd.c b/source4/kdc/kpasswdd.c
index 9b3336a7a1..8406887dad 100644
--- a/source4/kdc/kpasswdd.c
+++ b/source4/kdc/kpasswdd.c
@@ -1,4 +1,4 @@
-/*
+/*
Unix SMB/CIFS implementation.
kpasswd Server implementation
@@ -10,12 +10,12 @@
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/>.
*/
@@ -45,15 +45,15 @@
#endif
/* Return true if there is a valid error packet formed in the error_blob */
-static bool kpasswdd_make_error_reply(struct kdc_server *kdc,
- TALLOC_CTX *mem_ctx,
- uint16_t result_code,
- const char *error_string,
- DATA_BLOB *error_blob)
+static bool kpasswdd_make_error_reply(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ uint16_t result_code,
+ const char *error_string,
+ DATA_BLOB *error_blob)
{
char *error_string_utf8;
size_t len;
-
+
DEBUG(result_code ? 3 : 10, ("kpasswdd: %s\n", error_string));
if (!push_utf8_talloc(mem_ctx, &error_string_utf8, error_string, &len)) {
@@ -70,17 +70,17 @@ static bool kpasswdd_make_error_reply(struct kdc_server *kdc,
}
/* Return true if there is a valid error packet formed in the error_blob */
-static bool kpasswdd_make_unauth_error_reply(struct kdc_server *kdc,
- TALLOC_CTX *mem_ctx,
- uint16_t result_code,
- const char *error_string,
- DATA_BLOB *error_blob)
+static bool kpasswdd_make_unauth_error_reply(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ uint16_t result_code,
+ const char *error_string,
+ DATA_BLOB *error_blob)
{
bool ret;
int kret;
DATA_BLOB error_bytes;
krb5_data k5_error_bytes, k5_error_blob;
- ret = kpasswdd_make_error_reply(kdc, mem_ctx, result_code, error_string,
+ ret = kpasswdd_make_error_reply(kdc, mem_ctx, result_code, error_string,
&error_bytes);
if (!ret) {
return false;
@@ -88,7 +88,7 @@ static bool kpasswdd_make_unauth_error_reply(struct kdc_server *kdc,
k5_error_bytes.data = error_bytes.data;
k5_error_bytes.length = error_bytes.length;
kret = krb5_mk_error(kdc->smb_krb5_context->krb5_context,
- result_code, NULL, &k5_error_bytes,
+ result_code, NULL, &k5_error_bytes,
NULL, NULL, NULL, NULL, &k5_error_blob);
if (kret) {
return false;
@@ -101,21 +101,21 @@ static bool kpasswdd_make_unauth_error_reply(struct kdc_server *kdc,
return true;
}
-static bool kpasswd_make_pwchange_reply(struct kdc_server *kdc,
- TALLOC_CTX *mem_ctx,
- NTSTATUS status,
+static bool kpasswd_make_pwchange_reply(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ NTSTATUS status,
enum samPwdChangeReason reject_reason,
struct samr_DomInfo1 *dominfo,
- DATA_BLOB *error_blob)
+ DATA_BLOB *error_blob)
{
if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
- return kpasswdd_make_error_reply(kdc, mem_ctx,
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_ACCESSDENIED,
"No such user when changing password",
error_blob);
}
if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
- return kpasswdd_make_error_reply(kdc, mem_ctx,
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_ACCESSDENIED,
"Not permitted to change password",
error_blob);
@@ -138,31 +138,31 @@ static bool kpasswd_make_pwchange_reply(struct kdc_server *kdc,
dominfo->min_password_length, dominfo->password_history_length);
break;
}
- return kpasswdd_make_error_reply(kdc, mem_ctx,
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_SOFTERROR,
reject_string,
error_blob);
}
if (!NT_STATUS_IS_OK(status)) {
- return kpasswdd_make_error_reply(kdc, mem_ctx,
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_HARDERROR,
talloc_asprintf(mem_ctx, "failed to set password: %s", nt_errstr(status)),
error_blob);
-
+
}
return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_SUCCESS,
"Password changed",
error_blob);
}
-/*
+/*
A user password change
-
+
Return true if there is a valid error packet (or sucess) formed in
the error_blob
*/
static bool kpasswdd_change_password(struct kdc_server *kdc,
- TALLOC_CTX *mem_ctx,
+ TALLOC_CTX *mem_ctx,
struct auth_session_info *session_info,
const DATA_BLOB *password,
DATA_BLOB *reply)
@@ -174,45 +174,45 @@ static bool kpasswdd_change_password(struct kdc_server *kdc,
samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, system_session(kdc->task->lp_ctx));
if (!samdb) {
- return kpasswdd_make_error_reply(kdc, mem_ctx,
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_HARDERROR,
"Failed to open samdb",
reply);
}
-
- DEBUG(3, ("Changing password of %s\\%s (%s)\n",
+
+ DEBUG(3, ("Changing password of %s\\%s (%s)\n",
session_info->server_info->domain_name,
session_info->server_info->account_name,
dom_sid_string(mem_ctx, session_info->security_token->user_sid)));
/* User password change */
- status = samdb_set_password_sid(samdb, mem_ctx,
+ status = samdb_set_password_sid(samdb, mem_ctx,
session_info->security_token->user_sid,
- password, NULL, NULL,
+ password, NULL, NULL,
true, /* this is a user password change */
&reject_reason,
&dominfo);
- return kpasswd_make_pwchange_reply(kdc, mem_ctx,
- status,
+ return kpasswd_make_pwchange_reply(kdc, mem_ctx,
+ status,
reject_reason,
- dominfo,
+ dominfo,
reply);
}
static bool kpasswd_process_request(struct kdc_server *kdc,
- TALLOC_CTX *mem_ctx,
+ TALLOC_CTX *mem_ctx,
struct gensec_security *gensec_security,
uint16_t version,
- DATA_BLOB *input,
+ DATA_BLOB *input,
DATA_BLOB *reply)
{
struct auth_session_info *session_info;
size_t pw_len;
- if (!NT_STATUS_IS_OK(gensec_session_info(gensec_security,
+ if (!NT_STATUS_IS_OK(gensec_session_info(gensec_security,
&session_info))) {
- return kpasswdd_make_error_reply(kdc, mem_ctx,
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_HARDERROR,
"gensec_session_info failed!",
reply);
@@ -222,16 +222,16 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
case KRB5_KPASSWD_VERS_CHANGEPW:
{
DATA_BLOB password;
- if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx),
- CH_UTF8, CH_UTF16,
- (const char *)input->data,
+ if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx),
+ CH_UTF8, CH_UTF16,
+ (const char *)input->data,
input->length,
(void **)&password.data, &pw_len, false)) {
return false;
}
password.length = pw_len;
-
- return kpasswdd_change_password(kdc, mem_ctx, session_info,
+
+ return kpasswdd_change_password(kdc, mem_ctx, session_info,
&password, reply);
break;
}
@@ -262,26 +262,26 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
ret = decode_ChangePasswdDataMS(input->data, input->length,
&chpw, &len);
if (ret) {
- return kpasswdd_make_error_reply(kdc, mem_ctx,
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_MALFORMED,
"failed to decode password change structure",
reply);
}
-
- if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx),
- CH_UTF8, CH_UTF16,
- (const char *)chpw.newpasswd.data,
+
+ if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx),
+ CH_UTF8, CH_UTF16,
+ (const char *)chpw.newpasswd.data,
chpw.newpasswd.length,
(void **)&password.data, &pw_len, false)) {
free_ChangePasswdDataMS(&chpw);
return false;
}
-
+
password.length = pw_len;
-
- if ((chpw.targname && !chpw.targrealm)
+
+ if ((chpw.targname && !chpw.targrealm)
|| (!chpw.targname && chpw.targrealm)) {
- return kpasswdd_make_error_reply(kdc, mem_ctx,
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_MALFORMED,
"Realm and principal must be both present, or neither present",
reply);
@@ -289,14 +289,14 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
if (chpw.targname && chpw.targrealm) {
#ifdef SAMBA4_INTERNAL_HEIMDAL
if (_krb5_principalname2krb5_principal(kdc->smb_krb5_context->krb5_context,
- &principal, *chpw.targname,
+ &principal, *chpw.targname,
*chpw.targrealm) != 0) {
free_ChangePasswdDataMS(&chpw);
- return kpasswdd_make_error_reply(kdc, mem_ctx,
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_MALFORMED,
"failed to extract principal to set",
reply);
-
+
}
#else /* SAMBA4_INTERNAL_HEIMDAL */
return kpasswdd_make_error_reply(kdc, mem_ctx,
@@ -306,54 +306,54 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
#endif /* SAMBA4_INTERNAL_HEIMDAL */
} else {
free_ChangePasswdDataMS(&chpw);
- return kpasswdd_change_password(kdc, mem_ctx, session_info,
+ return kpasswdd_change_password(kdc, mem_ctx, session_info,
&password, reply);
}
free_ChangePasswdDataMS(&chpw);
if (krb5_unparse_name(context, principal, &set_password_on_princ) != 0) {
krb5_free_principal(context, principal);
- return kpasswdd_make_error_reply(kdc, mem_ctx,
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_MALFORMED,
"krb5_unparse_name failed!",
reply);
}
-
+
krb5_free_principal(context, principal);
-
+
samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, session_info);
if (!samdb) {
- return kpasswdd_make_error_reply(kdc, mem_ctx,
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_HARDERROR,
"Unable to open database!",
reply);
}
- DEBUG(3, ("%s\\%s (%s) is changing password of %s\n",
+ DEBUG(3, ("%s\\%s (%s) is changing password of %s\n",
session_info->server_info->domain_name,
session_info->server_info->account_name,
- dom_sid_string(mem_ctx, session_info->security_token->user_sid),
+ dom_sid_string(mem_ctx, session_info->security_token->user_sid),
set_password_on_princ));
ret = ldb_transaction_start(samdb);
if (ret) {
status = NT_STATUS_TRANSACTION_ABORTED;
- return kpasswd_make_pwchange_reply(kdc, mem_ctx,
+ return kpasswd_make_pwchange_reply(kdc, mem_ctx,
status,
SAM_PWD_CHANGE_NO_ERROR,
- NULL,
+ NULL,
reply);
}
- status = crack_user_principal_name(samdb, mem_ctx,
- set_password_on_princ,
+ status = crack_user_principal_name(samdb, mem_ctx,
+ set_password_on_princ,
&set_password_on_dn, NULL);
free(set_password_on_princ);
if (!NT_STATUS_IS_OK(status)) {
ldb_transaction_cancel(samdb);
- return kpasswd_make_pwchange_reply(kdc, mem_ctx,
+ return kpasswd_make_pwchange_reply(kdc, mem_ctx,
status,
SAM_PWD_CHANGE_NO_ERROR,
- NULL,
+ NULL,
reply);
}
@@ -372,7 +372,7 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
/* Admin password set */
status = samdb_set_password(samdb, mem_ctx,
set_password_on_dn, NULL,
- msg, &password, NULL, NULL,
+ msg, &password, NULL, NULL,
false, /* this is not a user password change */
&reject_reason, &dominfo);
}
@@ -398,17 +398,17 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
} else {
ldb_transaction_cancel(samdb);
}
- return kpasswd_make_pwchange_reply(kdc, mem_ctx,
+ return kpasswd_make_pwchange_reply(kdc, mem_ctx,
status,
- reject_reason,
- dominfo,
+ reject_reason,
+ dominfo,
reply);
}
default:
- return kpasswdd_make_error_reply(kdc, mem_ctx,
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_BAD_VERSION,
- talloc_asprintf(mem_ctx,
- "Protocol version %u not supported",
+ talloc_asprintf(mem_ctx,
+ "Protocol version %u not supported",
version),
reply);
}
@@ -416,8 +416,8 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
}
bool kpasswdd_process(struct kdc_server *kdc,
- TALLOC_CTX *mem_ctx,
- DATA_BLOB *input,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *input,
DATA_BLOB *reply,
struct tsocket_address *peer_addr,
struct tsocket_address *my_addr,
@@ -466,11 +466,11 @@ bool kpasswdd_process(struct kdc_server *kdc,
talloc_free(tmp_ctx);
return false;
}
-
+
krb_priv_len = len - ap_req_len;
ap_req = data_blob_const(&input->data[header_len], ap_req_len);
krb_priv_req = data_blob_const(&input->data[header_len + ap_req_len], krb_priv_len);
-
+
server_credentials = cli_credentials_init(tmp_ctx);
if (!server_credentials) {
DEBUG(1, ("Failed to init server credentials\n"));
@@ -478,7 +478,7 @@ bool kpasswdd_process(struct kdc_server *kdc,
}
/* We want the credentials subsystem to use the krb5 context
- * we already have, rather than a new context */
+ * we already have, rather than a new context */
cli_credentials_set_krb5_context(server_credentials, kdc->smb_krb5_context);
cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx);
@@ -487,10 +487,10 @@ bool kpasswdd_process(struct kdc_server *kdc,
cli_credentials_set_username(server_credentials, "kadmin/changepw", CRED_SPECIFIED);
ret = cli_credentials_set_keytab_name(server_credentials, kdc->task->event_ctx, kdc->task->lp_ctx, keytab_name, CRED_SPECIFIED);
if (ret != 0) {
- ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
+ ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_HARDERROR,
- talloc_asprintf(mem_ctx,
- "Failed to obtain server credentials for kadmin/changepw: %s\n",
+ talloc_asprintf(mem_ctx,
+ "Failed to obtain server credentials for kadmin/changepw: %s\n",
nt_errstr(nt_status)),
&krb_priv_rep);
ap_rep.length = 0;
@@ -500,16 +500,16 @@ bool kpasswdd_process(struct kdc_server *kdc,
talloc_free(tmp_ctx);
return ret;
}
-
+
/* We don't strictly need to call this wrapper, and could call
* gensec_server_start directly, as we have no need for NTLM
* and we have a PAC, but this ensures that the wrapper can be
* safely extended for other helpful things in future */
- nt_status = samba_server_gensec_start(tmp_ctx, kdc->task->event_ctx,
+ nt_status = samba_server_gensec_start(tmp_ctx, kdc->task->event_ctx,
kdc->task->msg_ctx,
kdc->task->lp_ctx,
server_credentials,
- "kpasswd",
+ "kpasswd",
&gensec_security);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(tmp_ctx);
@@ -549,11 +549,11 @@ bool kpasswdd_process(struct kdc_server *kdc,
/* Accept the AP-REQ and generate teh AP-REP we need for the reply */
nt_status = gensec_update(gensec_security, tmp_ctx, ap_req, &ap_rep);
if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-
- ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
+
+ ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_HARDERROR,
- talloc_asprintf(mem_ctx,
- "gensec_update failed: %s",
+ talloc_asprintf(mem_ctx,
+ "gensec_update failed: %s",
nt_errstr(nt_status)),
&krb_priv_rep);
ap_rep.length = 0;
@@ -567,10 +567,10 @@ bool kpasswdd_process(struct kdc_server *kdc,
/* Extract the data from the KRB-PRIV half of the message */
nt_status = gensec_unwrap(gensec_security, tmp_ctx, &krb_priv_req, &kpasswd_req);
if (!NT_STATUS_IS_OK(nt_status)) {
- ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
+ ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_HARDERROR,
- talloc_asprintf(mem_ctx,
- "gensec_unwrap failed: %s",
+ talloc_asprintf(mem_ctx,
+ "gensec_unwrap failed: %s",
nt_errstr(nt_status)),
&krb_priv_rep);
ap_rep.length = 0;
@@ -582,10 +582,10 @@ bool kpasswdd_process(struct kdc_server *kdc,
}
/* Figure out something to do with it (probably changing a password...) */
- ret = kpasswd_process_request(kdc, tmp_ctx,
- gensec_security,
- version,
- &kpasswd_req, &kpasswd_rep);
+ ret = kpasswd_process_request(kdc, tmp_ctx,
+ gensec_security,
+ version,
+ &kpasswd_req, &kpasswd_rep);
if (!ret) {
/* Argh! */
return false;
@@ -593,13 +593,13 @@ bool kpasswdd_process(struct kdc_server *kdc,
/* And wrap up the reply: This ensures that the error message
* or success can be verified by the client */
- nt_status = gensec_wrap(gensec_security, tmp_ctx,
+ nt_status = gensec_wrap(gensec_security, tmp_ctx,
&kpasswd_rep, &krb_priv_rep);
if (!NT_STATUS_IS_OK(nt_status)) {
- ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
+ ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
KRB5_KPASSWD_HARDERROR,
- talloc_asprintf(mem_ctx,
- "gensec_wrap failed: %s",
+ talloc_asprintf(mem_ctx,
+ "gensec_wrap failed: %s",
nt_errstr(nt_status)),
&krb_priv_rep);
ap_rep.length = 0;
@@ -609,7 +609,7 @@ bool kpasswdd_process(struct kdc_server *kdc,
talloc_free(tmp_ctx);
return ret;
}
-
+
reply:
*reply = data_blob_talloc(mem_ctx, NULL, krb_priv_rep.length + ap_rep.length + header_len);
if (!reply->data) {
@@ -619,11 +619,11 @@ reply:
RSSVAL(reply->data, 0, reply->length);
RSSVAL(reply->data, 2, 1); /* This is a version 1 reply, MS change/set or otherwise */
RSSVAL(reply->data, 4, ap_rep.length);
- memcpy(reply->data + header_len,
- ap_rep.data,
+ memcpy(reply->data + header_len,
+ ap_rep.data,
ap_rep.length);
- memcpy(reply->data + header_len + ap_rep.length,
- krb_priv_rep.data,
+ memcpy(reply->data + header_len + ap_rep.length,
+ krb_priv_rep.data,
krb_priv_rep.length);
talloc_free(tmp_ctx);
diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c
index 3d542d9a94..d921899c61 100644
--- a/source4/kdc/pac-glue.c
+++ b/source4/kdc/pac-glue.c
@@ -1,21 +1,21 @@
-/*
+/*
Unix SMB/CIFS implementation.
PAC Glue between Samba and the KDC
-
+
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-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/>.
*/
@@ -34,22 +34,22 @@
struct krb5_dh_moduli;
struct _krb5_krb_auth_data;
-static krb5_error_code samba_kdc_plugin_init(krb5_context context, void **ptr)
+static krb5_error_code samba_kdc_plugin_init(krb5_context context, void **ptr)
{
*ptr = NULL;
return 0;
}
-static void samba_kdc_plugin_fini(void *ptr)
+static void samba_kdc_plugin_fini(void *ptr)
{
return;
}
static krb5_error_code make_pac(krb5_context context,
- TALLOC_CTX *mem_ctx,
+ TALLOC_CTX *mem_ctx,
struct smb_iconv_convenience *iconv_convenience,
struct auth_serversupplied_info *server_info,
- krb5_pac *pac)
+ krb5_pac *pac)
{
union PAC_INFO info;
struct netr_SamInfo3 *info3;
@@ -105,7 +105,7 @@ static krb5_error_code make_pac(krb5_context context,
/* Given the right private pointer from hdb_samba4, get a PAC from the attached ldb messages */
static krb5_error_code samba_kdc_get_pac(void *priv,
- krb5_context context,
+ krb5_context context,
struct hdb_entry_ex *client,
krb5_pac *pac)
{
@@ -151,7 +151,7 @@ static krb5_error_code samba_kdc_get_pac(void *priv,
static krb5_error_code samba_kdc_reget_pac(void *priv, krb5_context context,
const krb5_principal client_principal,
- struct hdb_entry_ex *client,
+ struct hdb_entry_ex *client,
struct hdb_entry_ex *server, krb5_pac *pac)
{
krb5_error_code ret;
@@ -163,7 +163,7 @@ static krb5_error_code samba_kdc_reget_pac(void *priv, krb5_context context,
struct auth_serversupplied_info *server_info_out;
TALLOC_CTX *mem_ctx = talloc_named(p, 0, "samba_get_pac context");
-
+
if (!mem_ctx) {
return ENOMEM;
}
@@ -230,8 +230,8 @@ static void samba_kdc_build_edata_reply(TALLOC_CTX *tmp_ctx, krb5_data *e_data,
* the account_ok routine in auth/auth_sam.c for consistancy */
-static krb5_error_code samba_kdc_check_client_access(void *priv,
- krb5_context context,
+static krb5_error_code samba_kdc_check_client_access(void *priv,
+ krb5_context context,
krb5_kdc_configuration *config,
hdb_entry_ex *client_ex, const char *client_name,
hdb_entry_ex *server_ex, const char *server_name,
@@ -253,7 +253,7 @@ static krb5_error_code samba_kdc_check_client_access(void *priv,
if (!tmp_ctx) {
return ENOMEM;
}
-
+
if (addresses) {
for (i=0; i < addresses->len; i++) {
if (addresses->val->addr_type == KRB5_ADDRESS_NETBIOS) {
@@ -276,7 +276,7 @@ static krb5_error_code samba_kdc_check_client_access(void *priv,
password_change = (server_ex && server_ex->entry.flags.change_pw);
/* we allow all kinds of trusts here */
- nt_status = authsam_account_ok(tmp_ctx,
+ nt_status = authsam_account_ok(tmp_ctx,
p->samdb,
MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT,
p->realm_dn,
diff --git a/source4/kdc/pac-glue.h b/source4/kdc/pac-glue.h
index 1c14f7fdb6..f838ec353a 100644
--- a/source4/kdc/pac-glue.h
+++ b/source4/kdc/pac-glue.h
@@ -1,4 +1,4 @@
-/*
+/*
Unix SMB/CIFS implementation.
KDC Server startup
@@ -9,12 +9,12 @@
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/>.
*/
diff --git a/source4/lib/ldb-samba/ldif_handlers.c b/source4/lib/ldb-samba/ldif_handlers.c
index 5f709e6320..4611eba3f1 100644
--- a/source4/lib/ldb-samba/ldif_handlers.c
+++ b/source4/lib/ldb-samba/ldif_handlers.c
@@ -678,20 +678,43 @@ static int ldif_comparison_prefixMap(struct ldb_context *ldb, void *mem_ctx,
v1, v2);
}
-/* Canonicalisation of two 32-bit integers */
-static int ldif_canonicalise_int32(struct ldb_context *ldb, void *mem_ctx,
- const struct ldb_val *in, struct ldb_val *out)
+/* length limited conversion of a ldb_val to a int32_t */
+static int val_to_int32(const struct ldb_val *in, int32_t *v)
{
char *end;
+ char buf[64];
+
+ /* make sure we don't read past the end of the data */
+ if (in->length > sizeof(buf)-1) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ strncpy(buf, (char *)in->data, in->length);
+ buf[in->length] = 0;
+
/* We've to use "strtoll" here to have the intended overflows.
* Otherwise we may get "LONG_MAX" and the conversion is wrong. */
- int32_t i = (int32_t) strtoll((char *)in->data, &end, 0);
+ *v = (int32_t) strtoll(buf, &end, 0);
if (*end != 0) {
- return -1;
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ return LDB_SUCCESS;
+}
+
+/* Canonicalisation of two 32-bit integers */
+static int ldif_canonicalise_int32(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ int32_t i;
+ int ret;
+
+ ret = val_to_int32(in, &i);
+ if (ret != LDB_SUCCESS) {
+ return ret;
}
out->data = (uint8_t *) talloc_asprintf(mem_ctx, "%d", i);
if (out->data == NULL) {
- return -1;
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
}
out->length = strlen((char *)out->data);
return 0;
@@ -699,12 +722,13 @@ static int ldif_canonicalise_int32(struct ldb_context *ldb, void *mem_ctx,
/* Comparison of two 32-bit integers */
static int ldif_comparison_int32(struct ldb_context *ldb, void *mem_ctx,
- const struct ldb_val *v1, const struct ldb_val *v2)
+ const struct ldb_val *v1, const struct ldb_val *v2)
{
- /* We've to use "strtoll" here to have the intended overflows.
- * Otherwise we may get "LONG_MAX" and the conversion is wrong. */
- return (int32_t) strtoll((char *)v1->data, NULL, 0)
- - (int32_t) strtoll((char *)v2->data, NULL, 0);
+ int32_t i1=0, i2=0;
+ val_to_int32(v1, &i1);
+ val_to_int32(v2, &i2);
+ if (i1 == i2) return 0;
+ return i1 > i2? 1 : -1;
}
/*
@@ -846,7 +870,7 @@ static const struct ldb_dn_extended_syntax samba_dn_syntax[] = {
.write_clear_fn = ldif_write_objectGUID,
.write_hex_fn = extended_dn_write_hex
},{
- .name = "DELETED",
+ .name = "RMD_FLAGS",
.read_fn = ldb_handler_copy,
.write_clear_fn = ldb_handler_copy,
.write_hex_fn = ldb_handler_copy
@@ -861,7 +885,12 @@ static const struct ldb_dn_extended_syntax samba_dn_syntax[] = {
.write_clear_fn = ldb_handler_copy,
.write_hex_fn = ldb_handler_copy
},{
- .name = "RMD_USN",
+ .name = "RMD_LOCAL_USN",
+ .read_fn = ldb_handler_copy,
+ .write_clear_fn = ldb_handler_copy,
+ .write_hex_fn = ldb_handler_copy
+ },{
+ .name = "RMD_ORIGINATING_USN",
.read_fn = ldb_handler_copy,
.write_clear_fn = ldb_handler_copy,
.write_hex_fn = ldb_handler_copy
diff --git a/source4/lib/ldb/common/attrib_handlers.c b/source4/lib/ldb/common/attrib_handlers.c
index 1c08741f7d..464707530e 100644
--- a/source4/lib/ldb/common/attrib_handlers.c
+++ b/source4/lib/ldb/common/attrib_handlers.c
@@ -100,6 +100,27 @@ int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx,
return 0;
}
+/* length limited conversion of a ldb_val to a int32_t */
+static int val_to_int64(const struct ldb_val *in, int64_t *v)
+{
+ char *end;
+ char buf[64];
+
+ /* make sure we don't read past the end of the data */
+ if (in->length > sizeof(buf)-1) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ strncpy(buf, (char *)in->data, in->length);
+ buf[in->length] = 0;
+
+ /* We've to use "strtoll" here to have the intended overflows.
+ * Otherwise we may get "LONG_MAX" and the conversion is wrong. */
+ *v = (int64_t) strtoll(buf, &end, 0);
+ if (*end != 0) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ return LDB_SUCCESS;
+}
/*
@@ -109,14 +130,17 @@ int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx,
static int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx,
const struct ldb_val *in, struct ldb_val *out)
{
- char *end;
- long long i = strtoll((char *)in->data, &end, 0);
- if (*end != 0) {
- return -1;
+ int64_t i;
+ int ret;
+
+ ret = val_to_int64(in, &i);
+ if (ret != LDB_SUCCESS) {
+ return ret;
}
- out->data = (uint8_t *)talloc_asprintf(mem_ctx, "%lld", i);
+ out->data = (uint8_t *) talloc_asprintf(mem_ctx, "%lld", (long long)i);
if (out->data == NULL) {
- return -1;
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
}
out->length = strlen((char *)out->data);
return 0;
@@ -128,7 +152,11 @@ static int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx,
static int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx,
const struct ldb_val *v1, const struct ldb_val *v2)
{
- return strtoll((char *)v1->data, NULL, 0) - strtoll((char *)v2->data, NULL, 0);
+ int64_t i1=0, i2=0;
+ val_to_int64(v1, &i1);
+ val_to_int64(v2, &i2);
+ if (i1 == i2) return 0;
+ return i1 > i2? 1 : -1;
}
/*
@@ -338,10 +366,11 @@ static int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
const struct ldb_val *v1, const struct ldb_val *v2)
{
- time_t t1, t2;
- t1 = ldb_string_to_time((char *)v1->data);
- t2 = ldb_string_to_time((char *)v2->data);
- return (int)t2 - (int)t1;
+ time_t t1=0, t2=0;
+ ldb_val_to_time(v1, &t1);
+ ldb_val_to_time(v2, &t2);
+ if (t1 == t2) return 0;
+ return t1 > t2? 1 : -1;
}
/*
@@ -350,10 +379,16 @@ static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
static int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
const struct ldb_val *in, struct ldb_val *out)
{
- time_t t = ldb_string_to_time((char *)in->data);
+ time_t t;
+ int ret;
+ ret = ldb_val_to_time(in, &t);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
if (out->data == NULL) {
- return -1;
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
}
out->length = strlen((char *)out->data);
return 0;
diff --git a/source4/lib/ldb/common/ldb_modules.c b/source4/lib/ldb/common/ldb_modules.c
index e79f072d50..3b8934702a 100644
--- a/source4/lib/ldb/common/ldb_modules.c
+++ b/source4/lib/ldb/common/ldb_modules.c
@@ -527,6 +527,11 @@ struct ldb_context *ldb_module_get_ctx(struct ldb_module *module)
return module->ldb;
}
+const struct ldb_module_ops *ldb_module_get_ops(struct ldb_module *module)
+{
+ return module->ops;
+}
+
void *ldb_module_get_private(struct ldb_module *module)
{
return module->private_data;
@@ -601,7 +606,7 @@ int ldb_next_request(struct ldb_module *module, struct ldb_request *request)
* all our modules, and leaves us one less sharp
* corner for module developers to cut themselves on
*/
- ldb_module_done(request, NULL, NULL, ret);
+ ret = ldb_module_done(request, NULL, NULL, ret);
}
return ret;
}
@@ -824,8 +829,7 @@ int ldb_module_done(struct ldb_request *req,
ldb_debug_end(req->handle->ldb, LDB_DEBUG_TRACE);
}
- req->callback(req, ares);
- return error;
+ return req->callback(req, ares);
}
/* to be used *only* in modules init functions.
diff --git a/source4/lib/ldb/common/ldb_msg.c b/source4/lib/ldb/common/ldb_msg.c
index fbf49fbb23..9b33d7e351 100644
--- a/source4/lib/ldb/common/ldb_msg.c
+++ b/source4/lib/ldb/common/ldb_msg.c
@@ -832,6 +832,33 @@ time_t ldb_string_to_time(const char *s)
}
/*
+ convert a LDAP GeneralizedTime string in ldb_val format to a
+ time_t.
+*/
+int ldb_val_to_time(const struct ldb_val *v, time_t *t)
+{
+ struct tm tm;
+
+ if (v == NULL || !v->data || v->length < 14) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ memset(&tm, 0, sizeof(tm));
+
+ if (sscanf((char *)v->data, "%04u%02u%02u%02u%02u%02u",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ tm.tm_year -= 1900;
+ tm.tm_mon -= 1;
+
+ *t = timegm(&tm);
+
+ return LDB_SUCCESS;
+}
+
+/*
return a LDAP formatted UTCTime string
*/
char *ldb_timestring_utc(TALLOC_CTX *mem_ctx, time_t t)
diff --git a/source4/lib/ldb/config.mk b/source4/lib/ldb/config.mk
index 4a1f814baa..7d110fc618 100644
--- a/source4/lib/ldb/config.mk
+++ b/source4/lib/ldb/config.mk
@@ -11,6 +11,19 @@ ldb_asq_OBJ_FILES = $(ldbsrcdir)/modules/asq.o
################################################
################################################
+# Start MODULE sample_module
+[MODULE::sample]
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT
+CFLAGS = -I$(ldbsrcdir)/include
+INIT_FUNCTION = LDB_MODULE(sample)
+SUBSYSTEM = LIBTESTLDB
+
+# End MODULE sample_module
+################################################
+sample_OBJ_FILES = $(ldbsrcdir)/tests/sample_module.o
+
+
+################################################
# Start MODULE ldb_server_sort
[MODULE::ldb_server_sort]
PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT
diff --git a/source4/lib/ldb/external/libtalloc.m4 b/source4/lib/ldb/external/libtalloc.m4
index 8c63fcc041..dfccaf4fb7 100644
--- a/source4/lib/ldb/external/libtalloc.m4
+++ b/source4/lib/ldb/external/libtalloc.m4
@@ -2,7 +2,7 @@ AC_SUBST(TALLOC_OBJ)
AC_SUBST(TALLOC_CFLAGS)
AC_SUBST(TALLOC_LIBS)
-PKG_CHECK_MODULES(TALLOC, talloc >= 2.0.0,
+PKG_CHECK_MODULES(TALLOC, talloc >= 2.0.1,
[ ],
[ AC_CHECK_HEADER(talloc.h,
[ AC_CHECK_LIB(talloc, talloc_init, [TALLOC_LIBS="-ltalloc"])])])
diff --git a/source4/lib/ldb/include/ldb.h b/source4/lib/ldb/include/ldb.h
index c8bfa24832..1958fd740b 100644
--- a/source4/lib/ldb/include/ldb.h
+++ b/source4/lib/ldb/include/ldb.h
@@ -1951,6 +1951,12 @@ char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t);
time_t ldb_string_to_time(const char *s);
/**
+ convert a LDAP GeneralizedTime string in ldb_val format to a
+ time_t.
+*/
+int ldb_val_to_time(const struct ldb_val *v, time_t *t);
+
+/**
Convert a time structure to a string
This function converts a time_t structure to an LDAP formatted
diff --git a/source4/lib/ldb/include/ldb_module.h b/source4/lib/ldb/include/ldb_module.h
index 0b0f863fec..2d14634ca8 100644
--- a/source4/lib/ldb/include/ldb_module.h
+++ b/source4/lib/ldb/include/ldb_module.h
@@ -140,6 +140,7 @@ const char * ldb_module_get_name(struct ldb_module *module);
struct ldb_context *ldb_module_get_ctx(struct ldb_module *module);
void *ldb_module_get_private(struct ldb_module *module);
void ldb_module_set_private(struct ldb_module *module, void *private_data);
+const struct ldb_module_ops *ldb_module_get_ops(struct ldb_module *module);
int ldb_next_request(struct ldb_module *module, struct ldb_request *request);
int ldb_next_start_trans(struct ldb_module *module);
diff --git a/source4/lib/ldb/ldb.mk b/source4/lib/ldb/ldb.mk
index e87db64574..0c9b115672 100644
--- a/source4/lib/ldb/ldb.mk
+++ b/source4/lib/ldb/ldb.mk
@@ -34,26 +34,26 @@ lib/libldb.a: $(OBJS)
sample.$(SHLIBEXT): tests/sample_module.o
$(MDLD) $(MDLD_FLAGS) -o $@ tests/sample_module.o
-bin/ldbadd: tools/ldbadd.o tools/cmdline.o
- $(CC) -o bin/ldbadd tools/ldbadd.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+bin/ldbadd: tools/ldbadd.o tools/cmdline.o tools/ldbutil.o
+ $(CC) -o bin/ldbadd tools/ldbadd.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
-bin/ldbsearch: tools/ldbsearch.o tools/cmdline.o
- $(CC) -o bin/ldbsearch tools/ldbsearch.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+bin/ldbsearch: tools/ldbsearch.o tools/cmdline.o tools/ldbutil.o
+ $(CC) -o bin/ldbsearch tools/ldbsearch.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
-bin/ldbdel: tools/ldbdel.o tools/cmdline.o
- $(CC) -o bin/ldbdel tools/ldbdel.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+bin/ldbdel: tools/ldbdel.o tools/cmdline.o tools/ldbutil.o
+ $(CC) -o bin/ldbdel tools/ldbdel.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
-bin/ldbmodify: tools/ldbmodify.o tools/cmdline.o
- $(CC) -o bin/ldbmodify tools/ldbmodify.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+bin/ldbmodify: tools/ldbmodify.o tools/cmdline.o tools/ldbutil.o
+ $(CC) -o bin/ldbmodify tools/ldbmodify.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
-bin/ldbedit: tools/ldbedit.o tools/cmdline.o
- $(CC) -o bin/ldbedit tools/ldbedit.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+bin/ldbedit: tools/ldbedit.o tools/cmdline.o tools/ldbutil.o
+ $(CC) -o bin/ldbedit tools/ldbedit.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
-bin/ldbrename: tools/ldbrename.o tools/cmdline.o
- $(CC) -o bin/ldbrename tools/ldbrename.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+bin/ldbrename: tools/ldbrename.o tools/cmdline.o tools/ldbutil.o
+ $(CC) -o bin/ldbrename tools/ldbrename.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
-bin/ldbtest: tools/ldbtest.o tools/cmdline.o
- $(CC) -o bin/ldbtest tools/ldbtest.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+bin/ldbtest: tools/ldbtest.o tools/cmdline.o
+ $(CC) -o bin/ldbtest tools/ldbtest.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
examples/ldbreader: examples/ldbreader.o
$(CC) -o examples/ldbreader examples/ldbreader.o $(LIB_FLAGS)
diff --git a/source4/lib/ldb/ldb_tdb/ldb_cache.c b/source4/lib/ldb/ldb_tdb/ldb_cache.c
index cd2249d4a2..aa19f75e9c 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_cache.c
+++ b/source4/lib/ldb/ldb_tdb/ldb_cache.c
@@ -463,7 +463,7 @@ int ltdb_increase_sequence_number(struct ldb_module *module)
val_time.data = (uint8_t *)s;
val_time.length = strlen(s);
- ret = ltdb_modify_internal(module, msg);
+ ret = ltdb_modify_internal(module, msg, NULL);
talloc_free(msg);
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
index 227a202a6f..a146b96b20 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_tdb.c
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
@@ -592,9 +592,12 @@ static int msg_delete_element(struct ldb_module *module,
yuck - this is O(n^2). Luckily n is usually small so we probably
get away with it, but if we ever have really large attribute lists
then we'll need to look at this again
+
+ 'req' is optional, and is used to specify controls if supplied
*/
int ltdb_modify_internal(struct ldb_module *module,
- const struct ldb_message *msg)
+ const struct ldb_message *msg,
+ struct ldb_request *req)
{
struct ldb_context *ldb = ldb_module_get_ctx(module);
void *data = ldb_module_get_private(module);
@@ -731,7 +734,15 @@ int ltdb_modify_internal(struct ldb_module *module,
case LDB_FLAG_MOD_REPLACE:
if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) {
- if (el->num_values > 1) {
+ /* the RELAX control overrides this
+ check for replace. This is needed as
+ DRS replication can produce multiple
+ values here for a single valued
+ attribute when the values are deleted
+ links
+ */
+ if (el->num_values > 1 &&
+ (!req || !ldb_request_get_control(req, LDB_CONTROL_RELAX_OID))) {
ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s specified more than once",
el->name, ldb_dn_get_linearized(msg2->dn));
ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
@@ -852,7 +863,7 @@ static int ltdb_modify(struct ltdb_context *ctx)
return LDB_ERR_OPERATIONS_ERROR;
}
- ret = ltdb_modify_internal(module, req->op.mod.message);
+ ret = ltdb_modify_internal(module, req->op.mod.message, req);
return ret;
}
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.h b/source4/lib/ldb/ldb_tdb/ldb_tdb.h
index bb4cb3f8b5..70b99c340c 100644
--- a/source4/lib/ldb/ldb_tdb/ldb_tdb.h
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.h
@@ -130,7 +130,7 @@ int ltdb_lock_read(struct ldb_module *module);
int ltdb_unlock_read(struct ldb_module *module);
struct TDB_DATA ltdb_key(struct ldb_module *module, struct ldb_dn *dn);
int ltdb_store(struct ldb_module *module, const struct ldb_message *msg, int flgs);
-int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *msg);
+int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *msg, struct ldb_request *req);
int ltdb_delete_noindex(struct ldb_module *module, struct ldb_dn *dn);
int ltdb_err_map(enum TDB_ERROR tdb_code);
diff --git a/source4/lib/ldb/pyldb.c b/source4/lib/ldb/pyldb.c
index 0ba69e1c48..a19768d41b 100644
--- a/source4/lib/ldb/pyldb.c
+++ b/source4/lib/ldb/pyldb.c
@@ -61,6 +61,8 @@ PyAPI_DATA(PyTypeObject) PyLdb;
PyAPI_DATA(PyTypeObject) PyLdbMessageElement;
PyAPI_DATA(PyTypeObject) PyLdbTree;
+static PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx);
+
static PyObject *PyObject_FromLdbValue(struct ldb_context *ldb_ctx,
struct ldb_message_element *el,
struct ldb_val *val)
@@ -1357,7 +1359,7 @@ static PySequenceMethods py_ldb_seq = {
.sq_contains = (objobjproc)py_ldb_contains,
};
-PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx)
+static PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx)
{
PyLdbObject *ret;
diff --git a/source4/lib/ldb/pyldb.h b/source4/lib/ldb/pyldb.h
index a0954158bd..289159c5b3 100644
--- a/source4/lib/ldb/pyldb.h
+++ b/source4/lib/ldb/pyldb.h
@@ -35,7 +35,6 @@ typedef struct {
TALLOC_CTX *mem_ctx;
} PyLdbObject;
-PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx);
#define PyLdb_AsLdbContext(pyobj) ((PyLdbObject *)pyobj)->ldb_ctx
#define PyLdb_Check(ob) PyObject_TypeCheck(ob, &PyLdb)
diff --git a/source4/lib/ldb/tests/python/ldap.py b/source4/lib/ldb/tests/python/ldap.py
index 54b623a903..c90727d720 100755
--- a/source4/lib/ldb/tests/python/ldap.py
+++ b/source4/lib/ldb/tests/python/ldap.py
@@ -26,7 +26,7 @@ from ldb import ERR_NAMING_VIOLATION, ERR_CONSTRAINT_VIOLATION
from ldb import ERR_UNDEFINED_ATTRIBUTE_TYPE
from ldb import Message, MessageElement, Dn
from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
-from samba import Ldb, param, dom_sid_to_rid
+from samba import Ldb, param
from samba import UF_NORMAL_ACCOUNT, UF_TEMP_DUPLICATE_ACCOUNT
from samba import UF_SERVER_TRUST_ACCOUNT, UF_WORKSTATION_TRUST_ACCOUNT
from samba import UF_INTERDOMAIN_TRUST_ACCOUNT
@@ -456,7 +456,7 @@ class BasicTests(unittest.TestCase):
self.fail()
except LdbError, (num, _):
self.assertEquals(num, ERR_NAMING_VIOLATION)
-
+
self.delete_force(self.ldb, "description=xyz,cn=users," + self.base_dn)
self.ldb.add({
@@ -642,17 +642,17 @@ objectClass: container
res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
scope=SCOPE_BASE, attrs=["objectSID"])
self.assertTrue(len(res1) == 1)
- group_rid_1 = dom_sid_to_rid(ldb.schema_format_value("objectSID",
- res1[0]["objectSID"][0]))
+ group_rid_1 = security.dom_sid(ldb.schema_format_value("objectSID",
+ res1[0]["objectSID"][0])).split()[1]
res1 = ldb.search("cn=ldaptestgroup2,cn=users," + self.base_dn,
scope=SCOPE_BASE, attrs=["objectSID"])
self.assertTrue(len(res1) == 1)
- group_rid_2 = dom_sid_to_rid(ldb.schema_format_value("objectSID",
- res1[0]["objectSID"][0]))
+ group_rid_2 = security.dom_sid(ldb.schema_format_value("objectSID",
+ res1[0]["objectSID"][0])).split()[1]
# Try to create a user with an invalid primary group
- try:
+ try:
ldb.add({
"dn": "cn=ldaptestuser,cn=users," + self.base_dn,
"objectclass": ["user", "person"],
@@ -704,7 +704,8 @@ objectClass: container
# Make group 1 secondary
m = Message()
m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
- m["member"] = "cn=ldaptestuser,cn=users," + self.base_dn
+ m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
+ FLAG_MOD_REPLACE, "member")
ldb.modify(m)
# Make group 1 primary
@@ -724,7 +725,8 @@ objectClass: container
# Try to add group 1 also as secondary - should be denied
m = Message()
m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
- m["member"] = "cn=ldaptestuser,cn=users," + self.base_dn
+ m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
+ FLAG_MOD_ADD, "member")
try:
ldb.modify(m)
self.fail()
@@ -746,7 +748,8 @@ objectClass: container
# Make group 2 secondary
m = Message()
m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
- m["member"] = "cn=ldaptestuser,cn=users," + self.base_dn
+ m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
+ FLAG_MOD_ADD, "member")
ldb.modify(m)
# Swap the groups
@@ -833,7 +836,7 @@ objectClass: container
self.assertTrue(len(res1) == 1)
self.assertFalse("primaryGroupToken" in res1[0])
- res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
+ res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
scope=SCOPE_BASE)
self.assertTrue(len(res1) == 1)
self.assertFalse("primaryGroupToken" in res1[0])
@@ -843,7 +846,7 @@ objectClass: container
self.assertTrue(len(res1) == 1)
primary_group_token = int(res1[0]["primaryGroupToken"][0])
- rid = dom_sid_to_rid(ldb.schema_format_value("objectSID", res1[0]["objectSID"][0]))
+ rid = security.dom_sid(ldb.schema_format_value("objectSID", res1[0]["objectSID"][0])).split()[1]
self.assertEquals(primary_group_token, rid)
m = Message()
@@ -1990,137 +1993,6 @@ class BaseDnTests(unittest.TestCase):
self.assertTrue(res[0]["configurationNamingContext"][0] in ncs)
self.assertTrue(res[0]["schemaNamingContext"][0] in ncs)
-class SchemaTests(unittest.TestCase):
- def delete_force(self, ldb, dn):
- try:
- ldb.delete(dn)
- except LdbError, (num, _):
- self.assertEquals(num, ERR_NO_SUCH_OBJECT)
-
- def find_schemadn(self, ldb):
- res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["schemaNamingContext"])
- self.assertEquals(len(res), 1)
- return res[0]["schemaNamingContext"][0]
-
- def find_basedn(self, ldb):
- res = ldb.search(base="", expression="", scope=SCOPE_BASE,
- attrs=["defaultNamingContext"])
- self.assertEquals(len(res), 1)
- return res[0]["defaultNamingContext"][0]
-
- def setUp(self):
- self.ldb = ldb
- self.schema_dn = self.find_schemadn(ldb)
- self.base_dn = self.find_basedn(ldb)
-
- def test_generated_schema(self):
- """Testing we can read the generated schema via LDAP"""
- res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
- attrs=["objectClasses", "attributeTypes", "dITContentRules"])
- self.assertEquals(len(res), 1)
- self.assertTrue("dITContentRules" in res[0])
- self.assertTrue("objectClasses" in res[0])
- self.assertTrue("attributeTypes" in res[0])
-
- def test_generated_schema_is_operational(self):
- """Testing we don't get the generated schema via LDAP by default"""
- res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
- attrs=["*"])
- self.assertEquals(len(res), 1)
- self.assertFalse("dITContentRules" in res[0])
- self.assertFalse("objectClasses" in res[0])
- self.assertFalse("attributeTypes" in res[0])
-
-
- def test_schemaUpdateNow(self):
- """Testing schemaUpdateNow"""
- attr_name = "test-Attr" + time.strftime("%s", time.gmtime())
- attr_ldap_display_name = attr_name.replace("-", "")
-
- ldif = """
-dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
-objectClass: top
-objectClass: attributeSchema
-adminDescription: """ + attr_name + """
-adminDisplayName: """ + attr_name + """
-cn: """ + attr_name + """
-attributeId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9940
-attributeSyntax: 2.5.5.12
-omSyntax: 64
-instanceType: 4
-isSingleValued: TRUE
-systemOnly: FALSE
-"""
- self.ldb.add_ldif(ldif)
-
- class_name = "test-Class" + time.strftime("%s", time.gmtime())
- class_ldap_display_name = class_name.replace("-", "")
-
- ldif = """
-dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
-objectClass: top
-objectClass: classSchema
-adminDescription: """ + class_name + """
-adminDisplayName: """ + class_name + """
-cn: """ + class_name + """
-governsId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939
-instanceType: 4
-objectClassCategory: 1
-subClassOf: organizationalPerson
-systemFlags: 16
-rDNAttID: cn
-systemMustContain: cn
-systemMustContain: """ + attr_ldap_display_name + """
-systemOnly: FALSE
-"""
- self.ldb.add_ldif(ldif)
-
- ldif = """
-dn:
-changetype: modify
-add: schemaUpdateNow
-schemaUpdateNow: 1
-"""
- self.ldb.modify_ldif(ldif)
-
- object_name = "obj" + time.strftime("%s", time.gmtime())
-
- ldif = """
-dn: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
-objectClass: organizationalPerson
-objectClass: person
-objectClass: """ + class_ldap_display_name + """
-objectClass: top
-cn: """ + object_name + """
-instanceType: 4
-objectCategory: CN=%s,%s"""% (class_name, self.schema_dn) + """
-distinguishedName: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
-name: """ + object_name + """
-""" + attr_ldap_display_name + """: test
-"""
- self.ldb.add_ldif(ldif)
-
- # Search for created attribute
- res = []
- res = self.ldb.search("cn=%s,%s" % (attr_name, self.schema_dn), scope=SCOPE_BASE, attrs=["*"])
- self.assertEquals(len(res), 1)
- self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_display_name)
- self.assertTrue("schemaIDGUID" in res[0])
-
- # Search for created objectclass
- res = []
- res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE, attrs=["*"])
- self.assertEquals(len(res), 1)
- self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
- self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
- self.assertTrue("schemaIDGUID" in res[0])
-
- # Search for created object
- res = []
- res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["*"])
- self.assertEquals(len(res), 1)
- # Delete the object
- self.delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn))
if not "://" in host:
if os.path.isfile(host):
@@ -2141,6 +2013,4 @@ if not runner.run(unittest.makeSuite(BaseDnTests)).wasSuccessful():
rc = 1
if not runner.run(unittest.makeSuite(BasicTests)).wasSuccessful():
rc = 1
-if not runner.run(unittest.makeSuite(SchemaTests)).wasSuccessful():
- rc = 1
sys.exit(rc)
diff --git a/source4/lib/ldb/tests/python/ldap_schema.py b/source4/lib/ldb/tests/python/ldap_schema.py
new file mode 100755
index 0000000000..0a31db82f7
--- /dev/null
+++ b/source4/lib/ldb/tests/python/ldap_schema.py
@@ -0,0 +1,500 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# This is a port of the original in testprogs/ejs/ldap.js
+
+import getopt
+import optparse
+import sys
+import time
+import random
+import base64
+import os
+
+sys.path.append("bin/python")
+sys.path.append("../lib/subunit/python")
+
+import samba.getopt as options
+
+from samba.auth import system_session
+from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
+from ldb import ERR_NO_SUCH_OBJECT, ERR_ATTRIBUTE_OR_VALUE_EXISTS
+from ldb import ERR_ENTRY_ALREADY_EXISTS, ERR_UNWILLING_TO_PERFORM
+from ldb import ERR_NOT_ALLOWED_ON_NON_LEAF, ERR_OTHER, ERR_INVALID_DN_SYNTAX
+from ldb import ERR_NO_SUCH_ATTRIBUTE, ERR_INSUFFICIENT_ACCESS_RIGHTS
+from ldb import ERR_OBJECT_CLASS_VIOLATION, ERR_NOT_ALLOWED_ON_RDN
+from ldb import ERR_NAMING_VIOLATION, ERR_CONSTRAINT_VIOLATION
+from ldb import ERR_UNDEFINED_ATTRIBUTE_TYPE
+from ldb import Message, MessageElement, Dn
+from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
+from samba import Ldb
+from samba import UF_NORMAL_ACCOUNT, UF_TEMP_DUPLICATE_ACCOUNT
+from samba import UF_SERVER_TRUST_ACCOUNT, UF_WORKSTATION_TRUST_ACCOUNT
+from samba import UF_INTERDOMAIN_TRUST_ACCOUNT
+from samba import UF_PASSWD_NOTREQD, UF_ACCOUNTDISABLE
+from samba import GTYPE_SECURITY_BUILTIN_LOCAL_GROUP
+from samba import GTYPE_SECURITY_GLOBAL_GROUP, GTYPE_SECURITY_DOMAIN_LOCAL_GROUP
+from samba import GTYPE_SECURITY_UNIVERSAL_GROUP
+from samba import GTYPE_DISTRIBUTION_GLOBAL_GROUP
+from samba import GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP
+from samba import GTYPE_DISTRIBUTION_UNIVERSAL_GROUP
+from samba import ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST
+from samba import ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP
+from samba import ATYPE_SECURITY_UNIVERSAL_GROUP
+from samba import ATYPE_DISTRIBUTION_GLOBAL_GROUP
+from samba import ATYPE_DISTRIBUTION_LOCAL_GROUP
+from samba import ATYPE_DISTRIBUTION_UNIVERSAL_GROUP
+from samba import DS_DC_FUNCTION_2003
+
+from subunit import SubunitTestRunner
+import unittest
+
+from samba.ndr import ndr_pack, ndr_unpack
+from samba.dcerpc import security
+
+parser = optparse.OptionParser("ldap [options] <host>")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+# use command line creds if available
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+opts, args = parser.parse_args()
+
+if len(args) < 1:
+ parser.print_usage()
+ sys.exit(1)
+
+host = args[0]
+
+lp = sambaopts.get_loadparm()
+creds = credopts.get_credentials(lp)
+
+
+class SchemaTests(unittest.TestCase):
+ def delete_force(self, ldb, dn):
+ try:
+ ldb.delete(dn)
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_NO_SUCH_OBJECT)
+
+ def find_schemadn(self, ldb):
+ res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["schemaNamingContext"])
+ self.assertEquals(len(res), 1)
+ return res[0]["schemaNamingContext"][0]
+
+ def find_basedn(self, ldb):
+ res = ldb.search(base="", expression="", scope=SCOPE_BASE,
+ attrs=["defaultNamingContext"])
+ self.assertEquals(len(res), 1)
+ return res[0]["defaultNamingContext"][0]
+
+ def setUp(self):
+ self.ldb = ldb
+ self.schema_dn = self.find_schemadn(ldb)
+ self.base_dn = self.find_basedn(ldb)
+
+ def test_generated_schema(self):
+ """Testing we can read the generated schema via LDAP"""
+ res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
+ attrs=["objectClasses", "attributeTypes", "dITContentRules"])
+ self.assertEquals(len(res), 1)
+ self.assertTrue("dITContentRules" in res[0])
+ self.assertTrue("objectClasses" in res[0])
+ self.assertTrue("attributeTypes" in res[0])
+
+ def test_generated_schema_is_operational(self):
+ """Testing we don't get the generated schema via LDAP by default"""
+ res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
+ attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.assertFalse("dITContentRules" in res[0])
+ self.assertFalse("objectClasses" in res[0])
+ self.assertFalse("attributeTypes" in res[0])
+
+ def test_schemaUpdateNow(self):
+ """Testing schemaUpdateNow"""
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime())
+ attr_ldap_display_name = attr_name.replace("-", "")
+
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9940
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ class_name = "test-Class" + time.strftime("%s", time.gmtime())
+ class_ldap_display_name = class_name.replace("-", "")
+
+ ldif = """
+dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
+objectClass: top
+objectClass: classSchema
+adminDescription: """ + class_name + """
+adminDisplayName: """ + class_name + """
+cn: """ + class_name + """
+governsId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939
+instanceType: 4
+objectClassCategory: 1
+subClassOf: organizationalPerson
+systemFlags: 16
+rDNAttID: cn
+systemMustContain: cn
+systemMustContain: """ + attr_ldap_display_name + """
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn:
+changetype: modify
+add: schemaUpdateNow
+schemaUpdateNow: 1
+"""
+ self.ldb.modify_ldif(ldif)
+
+ object_name = "obj" + time.strftime("%s", time.gmtime())
+
+ ldif = """
+dn: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
+objectClass: organizationalPerson
+objectClass: person
+objectClass: """ + class_ldap_display_name + """
+objectClass: top
+cn: """ + object_name + """
+instanceType: 4
+objectCategory: CN=%s,%s"""% (class_name, self.schema_dn) + """
+distinguishedName: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
+name: """ + object_name + """
+""" + attr_ldap_display_name + """: test
+"""
+ self.ldb.add_ldif(ldif)
+
+ # Search for created attribute
+ res = []
+ res = self.ldb.search("cn=%s,%s" % (attr_name, self.schema_dn), scope=SCOPE_BASE, attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_display_name)
+ self.assertTrue("schemaIDGUID" in res[0])
+
+ # Search for created objectclass
+ res = []
+ res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE, attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
+ self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
+ self.assertTrue("schemaIDGUID" in res[0])
+
+ # Search for created object
+ res = []
+ res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["*"])
+ self.assertEquals(len(res), 1)
+ # Delete the object
+ self.delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn))
+
+
+class SchemaTests_msDS_IntId(unittest.TestCase):
+
+ def setUp(self):
+ self.ldb = ldb
+ res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.schema_dn = res[0]["schemaNamingContext"][0]
+ self.base_dn = res[0]["defaultNamingContext"][0]
+ self.forest_level = int(res[0]["forestFunctionality"][0])
+
+ def _ldap_schemaUpdateNow(self):
+ ldif = """
+dn:
+changetype: modify
+add: schemaUpdateNow
+schemaUpdateNow: 1
+"""
+ self.ldb.modify_ldif(ldif)
+
+ def _make_obj_names(self, prefix):
+ class_name = prefix + time.strftime("%s", time.gmtime())
+ class_ldap_name = class_name.replace("-", "")
+ class_dn = "CN=%s,%s" % (class_name, self.schema_dn)
+ return (class_name, class_ldap_name, class_dn)
+
+ def _is_schema_base_object(self, ldb_msg):
+ """Test systemFlags for SYSTEM_FLAG_SCHEMA_BASE_OBJECT (16)"""
+ systemFlags = 0
+ if "systemFlags" in ldb_msg:
+ systemFlags = int(ldb_msg["systemFlags"][0])
+ return (systemFlags & 16) != 0
+
+ def _make_attr_ldif(self, attr_name, attr_dn):
+ ldif = """
+dn: """ + attr_dn + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9940
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ return ldif
+
+ def test_msDS_IntId_on_attr(self):
+ """Testing msDs-IntId creation for Attributes.
+ See MS-ADTS - 3.1.1.Attributes
+
+ This test should verify that:
+ - Creating attribute with 'msDS-IntId' fails with ERR_UNWILLING_TO_PERFORM
+ - Adding 'msDS-IntId' on existing attribute fails with ERR_CONSTRAINT_VIOLATION
+ - Creating attribute with 'msDS-IntId' set and FLAG_SCHEMA_BASE_OBJECT flag
+ set fails with ERR_UNWILLING_TO_PERFORM
+ - Attributes created with FLAG_SCHEMA_BASE_OBJECT not set have
+ 'msDS-IntId' attribute added internally
+ """
+
+ # 1. Create attribute without systemFlags
+ # msDS-IntId should be created if forest functional
+ # level is >= DS_DC_FUNCTION_2003
+ # and missing otherwise
+ (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-1-")
+ ldif = self._make_attr_ldif(attr_name, attr_dn)
+
+ # try to add msDS-IntId during Attribute creation
+ ldif_fail = ldif + "msDS-IntId: -1993108831\n"
+ try:
+ self.ldb.add_ldif(ldif_fail)
+ self.fail("Adding attribute with preset msDS-IntId should fail")
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+ # add the new attribute and update schema
+ self.ldb.add_ldif(ldif)
+ self._ldap_schemaUpdateNow()
+
+ # Search for created attribute
+ res = []
+ res = self.ldb.search(attr_dn, scope=SCOPE_BASE, attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name)
+ if self.forest_level >= DS_DC_FUNCTION_2003:
+ if self._is_schema_base_object(res[0]):
+ self.assertTrue("msDS-IntId" not in res[0])
+ else:
+ self.assertTrue("msDS-IntId" in res[0])
+ else:
+ self.assertTrue("msDS-IntId" not in res[0])
+
+ msg = Message()
+ msg.dn = Dn(self.ldb, attr_dn)
+ msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
+ try:
+ self.ldb.modify(msg)
+ self.fail("Modifying msDS-IntId should return error")
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+ # 2. Create attribute with systemFlags = FLAG_SCHEMA_BASE_OBJECT
+ # msDS-IntId should be created if forest functional
+ # level is >= DS_DC_FUNCTION_2003
+ # and missing otherwise
+ (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-2-")
+ ldif = self._make_attr_ldif(attr_name, attr_dn)
+ ldif += "systemFlags: 16\n"
+
+ # try to add msDS-IntId during Attribute creation
+ ldif_fail = ldif + "msDS-IntId: -1993108831\n"
+ try:
+ self.ldb.add_ldif(ldif_fail)
+ self.fail("Adding attribute with preset msDS-IntId should fail")
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+ # add the new attribute and update schema
+ self.ldb.add_ldif(ldif)
+ self._ldap_schemaUpdateNow()
+
+ # Search for created attribute
+ res = []
+ res = self.ldb.search(attr_dn, scope=SCOPE_BASE, attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name)
+ if self.forest_level >= DS_DC_FUNCTION_2003:
+ if self._is_schema_base_object(res[0]):
+ self.assertTrue("msDS-IntId" not in res[0])
+ else:
+ self.assertTrue("msDS-IntId" in res[0])
+ else:
+ self.assertTrue("msDS-IntId" not in res[0])
+
+ msg = Message()
+ msg.dn = Dn(self.ldb, attr_dn)
+ msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
+ try:
+ self.ldb.modify(msg)
+ self.fail("Modifying msDS-IntId should return error")
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+
+ def _make_class_ldif(self, class_dn, class_name):
+ ldif = """
+dn: """ + class_dn + """
+objectClass: top
+objectClass: classSchema
+adminDescription: """ + class_name + """
+adminDisplayName: """ + class_name + """
+cn: """ + class_name + """
+governsId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939
+instanceType: 4
+objectClassCategory: 1
+subClassOf: organizationalPerson
+rDNAttID: cn
+systemMustContain: cn
+systemOnly: FALSE
+"""
+ return ldif
+
+ def test_msDS_IntId_on_class(self):
+ """Testing msDs-IntId creation for Class
+ Reference: MS-ADTS - 3.1.1.2.4.8 Class classSchema"""
+
+ # 1. Create Class without systemFlags
+ # msDS-IntId should be created if forest functional
+ # level is >= DS_DC_FUNCTION_2003
+ # and missing otherwise
+ (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-1-")
+ ldif = self._make_class_ldif(class_dn, class_name)
+
+ # try to add msDS-IntId during Class creation
+ ldif_add = ldif + "msDS-IntId: -1993108831\n"
+ self.ldb.add_ldif(ldif_add)
+ self._ldap_schemaUpdateNow()
+
+ res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(res[0]["msDS-IntId"][0], "-1993108831")
+
+ # add a new Class and update schema
+ (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-2-")
+ ldif = self._make_class_ldif(class_dn, class_name)
+
+ self.ldb.add_ldif(ldif)
+ self._ldap_schemaUpdateNow()
+
+ # Search for created Class
+ res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.assertFalse("msDS-IntId" in res[0])
+
+ msg = Message()
+ msg.dn = Dn(self.ldb, class_dn)
+ msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
+ try:
+ self.ldb.modify(msg)
+ self.fail("Modifying msDS-IntId should return error")
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+ # 2. Create Class with systemFlags = FLAG_SCHEMA_BASE_OBJECT
+ # msDS-IntId should be created if forest functional
+ # level is >= DS_DC_FUNCTION_2003
+ # and missing otherwise
+ (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-3-")
+ ldif = self._make_class_ldif(class_dn, class_name)
+ ldif += "systemFlags: 16\n"
+
+ # try to add msDS-IntId during Class creation
+ ldif_add = ldif + "msDS-IntId: -1993108831\n"
+ self.ldb.add_ldif(ldif_add)
+
+ res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(res[0]["msDS-IntId"][0], "-1993108831")
+
+ # add the new Class and update schema
+ (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-4-")
+ ldif = self._make_class_ldif(class_dn, class_name)
+ ldif += "systemFlags: 16\n"
+
+ self.ldb.add_ldif(ldif)
+ self._ldap_schemaUpdateNow()
+
+ # Search for created Class
+ res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.assertFalse("msDS-IntId" in res[0])
+
+ msg = Message()
+ msg.dn = Dn(self.ldb, class_dn)
+ msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
+ try:
+ self.ldb.modify(msg)
+ self.fail("Modifying msDS-IntId should return error")
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+ res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.assertFalse("msDS-IntId" in res[0])
+
+
+ def test_verify_msDS_IntId(self):
+ """Verify msDS-IntId exists only on attributes without FLAG_SCHEMA_BASE_OBJECT flag set"""
+ count = 0
+ res = self.ldb.search(self.schema_dn, scope=SCOPE_ONELEVEL,
+ expression="objectClass=attributeSchema",
+ attrs=["systemFlags", "msDS-IntId", "attributeID", "cn"])
+ self.assertTrue(len(res) > 1)
+ for ldb_msg in res:
+ if self.forest_level >= DS_DC_FUNCTION_2003:
+ if self._is_schema_base_object(ldb_msg):
+ self.assertTrue("msDS-IntId" not in ldb_msg)
+ else:
+ # don't assert here as there are plenty of
+ # attributes under w2k8 that are not part of
+ # Base Schema (SYSTEM_FLAG_SCHEMA_BASE_OBJECT flag not set)
+ # has not msDS-IntId attribute set
+ #self.assertTrue("msDS-IntId" in ldb_msg, "msDS-IntId expected on: %s" % ldb_msg.dn)
+ if "msDS-IntId" not in ldb_msg:
+ count = count + 1
+ print "%3d warning: msDS-IntId expected on: %-30s %s" % (count, ldb_msg["attributeID"], ldb_msg["cn"])
+ else:
+ self.assertTrue("msDS-IntId" not in ldb_msg)
+
+
+if not "://" in host:
+ if os.path.isfile(host):
+ host = "tdb://%s" % host
+ else:
+ host = "ldap://%s" % host
+
+ldb_options = []
+if host.startswith("ldap://"):
+ # user 'paged_search' module when connecting remotely
+ ldb_options = ["modules:paged_searches"]
+
+ldb = Ldb(host, credentials=creds, session_info=system_session(), lp=lp, options=ldb_options)
+if not "tdb://" in host:
+ gc_ldb = Ldb("%s:3268" % host, credentials=creds,
+ session_info=system_session(), lp=lp)
+else:
+ gc_ldb = None
+
+runner = SubunitTestRunner()
+rc = 0
+if not runner.run(unittest.makeSuite(SchemaTests)).wasSuccessful():
+ rc = 1
+if not runner.run(unittest.makeSuite(SchemaTests_msDS_IntId)).wasSuccessful():
+ rc = 1
+sys.exit(rc)
diff --git a/source4/lib/ldb/tests/sample_module.c b/source4/lib/ldb/tests/sample_module.c
index bbe4419b59..bb7906e7ba 100644
--- a/source4/lib/ldb/tests/sample_module.c
+++ b/source4/lib/ldb/tests/sample_module.c
@@ -25,12 +25,40 @@
int sample_add(struct ldb_module *mod, struct ldb_request *req)
{
+ struct ldb_control *control;
+ struct ldb_control *controls;
ldb_msg_add_fmt(req->op.add.message, "touchedBy", "sample");
- return ldb_next_request(mod, req);
+
+ /* check if there's a relax control */
+ control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(mod, req);
+ } else {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+}
+
+int sample_modify(struct ldb_module *mod, struct ldb_request *req)
+{
+ struct ldb_control *control;
+ struct ldb_control *controls;
+
+ /* check if there's a relax control */
+ control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(mod, req);
+ } else {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
}
+
const struct ldb_module_ops ldb_sample_module_ops = {
.name = "sample",
.add = sample_add,
+ .del = sample_modify,
+ .modify = sample_modify,
};
diff --git a/source4/lib/ldb/tests/test-controls.sh b/source4/lib/ldb/tests/test-controls.sh
new file mode 100755
index 0000000000..db139bbec7
--- /dev/null
+++ b/source4/lib/ldb/tests/test-controls.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+if [ -n "$TEST_DATA_PREFIX" ]; then
+ LDB_URL="$TEST_DATA_PREFIX/tdbtest.ldb"
+else
+ LDB_URL="tdbtest.ldb"
+fi
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+rm -f $LDB_URL*
+LDB_MODULES_PATH=`dirname $0`/../../../bin/modules/testldb
+echo $LDB_MODULES_PATH
+
+echo "LDB_URL: $LDB_URL"
+cat <<EOF | $VALGRIND ldbadd || exit 1
+dn: @MODULES
+@LIST: sample
+EOF
+
+cat <<EOF | $VALGRIND ldbadd || exit 1
+dn: dc=bar
+dc: bar
+someThing: someThingElse
+EOF
+
+$VALGRIND ldbsearch "(touchedBy=sample)" | grep "touchedBy: sample" || exit 1
+# This action are expected to fails because the sample module return an error when presented the relax control
+
+cat <<EOF | $VALGRIND ldbadd --controls "relax:0" && exit 1
+dn: dc=foobar
+dc: foobar
+someThing: someThingElse
+EOF
+
+cat <<EOF | $VALGRIND ldbmodify --controls "relax:0" && exit 1
+dn: dc=bar
+changetype: replace
+replace someThing
+someThing: someThingElseBetter
+EOF
+
+
+set
diff --git a/source4/lib/ldb/tests/test-tdb.sh b/source4/lib/ldb/tests/test-tdb.sh
index 1c35451962..9da1e57060 100755
--- a/source4/lib/ldb/tests/test-tdb.sh
+++ b/source4/lib/ldb/tests/test-tdb.sh
@@ -29,3 +29,5 @@ $VALGRIND ldbadd$EXEEXT $LDBDIR/tests/init.ldif || exit 1
. $LDBDIR/tests/test-extended.sh
. $LDBDIR/tests/test-tdb-features.sh
+
+. $LDBDIR/tests/test-controls.sh
diff --git a/source4/lib/ldb/tools/config.mk b/source4/lib/ldb/tools/config.mk
index 6b57929df0..f0d0e85e6f 100644
--- a/source4/lib/ldb/tools/config.mk
+++ b/source4/lib/ldb/tools/config.mk
@@ -1,4 +1,14 @@
################################################
+# Start SUBSYSTEM LIBLDB_UTIL
+[SUBSYSTEM::LIBLDB_UTIL]
+CFLAGS = -I$(ldbsrcdir) -I$(ldbsrcdir)/include
+PUBLIC_DEPENDENCIES = LIBLDB
+# End SUBSYSTEM LIBLDB_UTIL
+################################################
+
+LIBLDB_UTIL_OBJ_FILES = $(ldbsrcdir)/tools/ldbutil.o
+
+################################################
# Start SUBSYSTEM LIBLDB_CMDLINE
[SUBSYSTEM::LIBLDB_CMDLINE]
CFLAGS = -I$(ldbsrcdir) -I$(ldbsrcdir)/include
@@ -14,6 +24,7 @@ LIBLDB_CMDLINE_OBJ_FILES = $(ldbsrcdir)/tools/cmdline.o
[BINARY::ldbadd]
INSTALLDIR = BINDIR
PRIVATE_DEPENDENCIES = \
+ LIBLDB_UTIL \
LIBLDB_CMDLINE LIBCLI_RESOLVE
# End BINARY ldbadd
################################################
@@ -28,6 +39,7 @@ MANPAGES += $(ldbsrcdir)/man/ldbadd.1
[BINARY::ldbdel]
INSTALLDIR = BINDIR
PRIVATE_DEPENDENCIES = \
+ LIBLDB_UTIL \
LIBLDB_CMDLINE
# End BINARY ldbdel
################################################
@@ -41,6 +53,7 @@ MANPAGES += $(ldbsrcdir)/man/ldbdel.1
[BINARY::ldbmodify]
INSTALLDIR = BINDIR
PRIVATE_DEPENDENCIES = \
+ LIBLDB_UTIL \
LIBLDB_CMDLINE
# End BINARY ldbmodify
################################################
diff --git a/source4/lib/ldb/tools/ldbadd.c b/source4/lib/ldb/tools/ldbadd.c
index a87c99aaee..e618ab52f7 100644
--- a/source4/lib/ldb/tools/ldbadd.c
+++ b/source4/lib/ldb/tools/ldbadd.c
@@ -33,6 +33,7 @@
#include "ldb.h"
#include "tools/cmdline.h"
+#include "ldbutil.h"
static int failures;
static struct ldb_cmdline *options;
@@ -53,6 +54,12 @@ static int process_file(struct ldb_context *ldb, FILE *f, int *count)
{
struct ldb_ldif *ldif;
int ret = LDB_SUCCESS;
+ struct ldb_control **req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls);
+ if (options->controls != NULL && req_ctrls== NULL) {
+ printf("parsing controls failed: %s\n", ldb_errstring(ldb));
+ return -1;
+ }
+
while ((ldif = ldb_ldif_read_file(ldb, f))) {
if (ldif->changetype != LDB_CHANGETYPE_ADD &&
@@ -63,10 +70,11 @@ static int process_file(struct ldb_context *ldb, FILE *f, int *count)
ldif->msg = ldb_msg_canonicalize(ldb, ldif->msg);
- ret = ldb_add(ldb, ldif->msg);
+ ret = ldb_add_ctrl(ldb, ldif->msg,req_ctrls);
if (ret != LDB_SUCCESS) {
- fprintf(stderr, "ERR: \"%s\" on DN %s\n",
- ldb_errstring(ldb), ldb_dn_get_linearized(ldif->msg->dn));
+ fprintf(stderr, "ERR: %s : \"%s\" on DN %s\n",
+ ldb_strerror(ret), ldb_errstring(ldb),
+ ldb_dn_get_linearized(ldif->msg->dn));
failures++;
} else {
(*count)++;
diff --git a/source4/lib/ldb/tools/ldbdel.c b/source4/lib/ldb/tools/ldbdel.c
index 5740f22503..6de15ee042 100644
--- a/source4/lib/ldb/tools/ldbdel.c
+++ b/source4/lib/ldb/tools/ldbdel.c
@@ -33,6 +33,7 @@
#include "ldb.h"
#include "tools/cmdline.h"
+#include "ldbutil.h"
static int dn_cmp(const void *p1, const void *p2)
{
@@ -42,7 +43,7 @@ static int dn_cmp(const void *p1, const void *p2)
return ldb_dn_compare(msg1->dn, msg2->dn);
}
-static int ldb_delete_recursive(struct ldb_context *ldb, struct ldb_dn *dn)
+static int ldb_delete_recursive(struct ldb_context *ldb, struct ldb_dn *dn,struct ldb_control **req_ctrls)
{
int ret, i, total=0;
const char *attrs[] = { NULL };
@@ -55,7 +56,7 @@ static int ldb_delete_recursive(struct ldb_context *ldb, struct ldb_dn *dn)
qsort(res->msgs, res->count, sizeof(res->msgs[0]), dn_cmp);
for (i = 0; i < res->count; i++) {
- if (ldb_delete(ldb, res->msgs[i]->dn) == 0) {
+ if (ldb_delete_ctrl(ldb, res->msgs[i]->dn,req_ctrls) == 0) {
total++;
} else {
printf("Failed to delete '%s' - %s\n",
@@ -83,9 +84,10 @@ static void usage(void)
int main(int argc, const char **argv)
{
+ struct ldb_control **req_ctrls;
+ struct ldb_cmdline *options;
struct ldb_context *ldb;
int ret = 0, i;
- struct ldb_cmdline *options;
ldb = ldb_init(NULL, NULL);
@@ -96,6 +98,12 @@ int main(int argc, const char **argv)
exit(1);
}
+ req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls);
+ if (options->controls != NULL && req_ctrls== NULL) {
+ printf("parsing controls failed: %s\n", ldb_errstring(ldb));
+ return -1;
+ }
+
for (i=0;i<options->argc;i++) {
struct ldb_dn *dn;
@@ -105,17 +113,18 @@ int main(int argc, const char **argv)
exit(1);
}
if (options->recursive) {
- ret = ldb_delete_recursive(ldb, dn);
+ ret = ldb_delete_recursive(ldb, dn,req_ctrls);
} else {
- ret = ldb_delete(ldb, dn);
+ ret = ldb_delete_ctrl(ldb, dn,req_ctrls);
if (ret == 0) {
printf("Deleted 1 record\n");
}
}
if (ret != 0) {
- printf("delete of '%s' failed - %s\n",
- ldb_dn_get_linearized(dn),
- ldb_errstring(ldb));
+ printf("delete of '%s' failed - (%s) %s\n",
+ ldb_dn_get_linearized(dn),
+ ldb_strerror(ret),
+ ldb_errstring(ldb));
}
}
diff --git a/source4/lib/ldb/tools/ldbmodify.c b/source4/lib/ldb/tools/ldbmodify.c
index 4936880d09..57988cbb30 100644
--- a/source4/lib/ldb/tools/ldbmodify.c
+++ b/source4/lib/ldb/tools/ldbmodify.c
@@ -33,6 +33,7 @@
#include "ldb.h"
#include "tools/cmdline.h"
+#include "ldbutil.h"
static int failures;
static struct ldb_cmdline *options;
@@ -52,22 +53,28 @@ static int process_file(struct ldb_context *ldb, FILE *f, int *count)
{
struct ldb_ldif *ldif;
int ret = LDB_SUCCESS;
-
+ struct ldb_control **req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls);
+ if (options->controls != NULL && req_ctrls== NULL) {
+ printf("parsing controls failed: %s\n", ldb_errstring(ldb));
+ return -1;
+ }
+
while ((ldif = ldb_ldif_read_file(ldb, f))) {
switch (ldif->changetype) {
case LDB_CHANGETYPE_NONE:
case LDB_CHANGETYPE_ADD:
- ret = ldb_add(ldb, ldif->msg);
+ ret = ldb_add_ctrl(ldb, ldif->msg,req_ctrls);
break;
case LDB_CHANGETYPE_DELETE:
- ret = ldb_delete(ldb, ldif->msg->dn);
+ ret = ldb_delete_ctrl(ldb, ldif->msg->dn,req_ctrls);
break;
case LDB_CHANGETYPE_MODIFY:
- ret = ldb_modify(ldb, ldif->msg);
+ ret = ldb_modify_ctrl(ldb, ldif->msg,req_ctrls);
break;
}
if (ret != LDB_SUCCESS) {
- fprintf(stderr, "ERR: \"%s\" on DN %s\n",
+ fprintf(stderr, "ERR: (%s) \"%s\" on DN %s\n",
+ ldb_strerror(ret),
ldb_errstring(ldb), ldb_dn_get_linearized(ldif->msg->dn));
failures++;
} else {
diff --git a/source4/lib/ldb/tools/ldbutil.c b/source4/lib/ldb/tools/ldbutil.c
new file mode 100644
index 0000000000..5f7ea894df
--- /dev/null
+++ b/source4/lib/ldb/tools/ldbutil.c
@@ -0,0 +1,149 @@
+/*
+ ldb database library utility
+
+ Copyright (C) Matthieu Patou 2009
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Description: Common function used by ldb_add/ldb_modify/ldb_delete
+ *
+ * Author: Matthieu Patou
+ */
+
+#include "ldb.h"
+#include "ldb_module.h"
+
+/* autostarts a transacion if none active */
+static int ldb_do_autotransaction(struct ldb_context *ldb,
+ struct ldb_request *req)
+{
+ int ret;
+
+ ret = ldb_transaction_start(ldb);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_request(ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ if (ret == LDB_SUCCESS) {
+ return ldb_transaction_commit(ldb);
+ }
+ ldb_transaction_cancel(ldb);
+
+ if (ldb_errstring(ldb) == NULL) {
+ /* no error string was setup by the backend */
+ ldb_asprintf_errstring(ldb, "%s (%d)", ldb_strerror(ret), ret);
+ }
+
+ return ret;
+}
+/*
+ Same as ldb_add but accept control
+*/
+int ldb_add_ctrl(struct ldb_context *ldb,
+ const struct ldb_message *message,
+ struct ldb_control **controls)
+{
+ struct ldb_request *req;
+ int ret;
+
+ ret = ldb_msg_sanity_check(ldb, message);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_build_add_req(&req, ldb, ldb,
+ message,
+ controls,
+ NULL,
+ ldb_op_default_callback,
+ NULL);
+
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* do request and autostart a transaction */
+ ret = ldb_do_autotransaction(ldb, req);
+
+ talloc_free(req);
+ return ret;
+}
+
+/*
+ same as ldb_delete but accept control
+*/
+int ldb_delete_ctrl(struct ldb_context *ldb, struct ldb_dn *dn,
+ struct ldb_control **controls)
+{
+ struct ldb_request *req;
+ int ret;
+
+ ret = ldb_build_del_req(&req, ldb, ldb,
+ dn,
+ controls,
+ NULL,
+ ldb_op_default_callback,
+ NULL);
+
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* do request and autostart a transaction */
+ ret = ldb_do_autotransaction(ldb, req);
+
+ talloc_free(req);
+ return ret;
+}
+
+
+/*
+ same as ldb_modify, but accepts controls
+*/
+int ldb_modify_ctrl(struct ldb_context *ldb,
+ const struct ldb_message *message,
+ struct ldb_control **controls)
+{
+ struct ldb_request *req;
+ int ret;
+
+ ret = ldb_msg_sanity_check(ldb, message);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_build_mod_req(&req, ldb, ldb,
+ message,
+ controls,
+ NULL,
+ ldb_op_default_callback,
+ NULL);
+
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* do request and autostart a transaction */
+ ret = ldb_do_autotransaction(ldb, req);
+
+ talloc_free(req);
+ return ret;
+}
diff --git a/source4/lib/ldb/tools/ldbutil.h b/source4/lib/ldb/tools/ldbutil.h
new file mode 100644
index 0000000000..7747dbec64
--- /dev/null
+++ b/source4/lib/ldb/tools/ldbutil.h
@@ -0,0 +1,41 @@
+/*
+ ldb database library utility header file
+
+ Copyright (C) Matthieu Patou 2009
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Description: Common function used by ldb_add/ldb_modify/ldb_delete
+ *
+ * Author: Matthieu Patou
+ */
+
+#include "ldb.h"
+
+int ldb_add_ctrl(struct ldb_context *ldb,
+ const struct ldb_message *message,
+ struct ldb_control **controls);
+int ldb_delete_ctrl(struct ldb_context *ldb, struct ldb_dn *dn,
+ struct ldb_control **controls);
+int ldb_modify_ctrl(struct ldb_context *ldb,
+ const struct ldb_message *message,
+ struct ldb_control **controls);
diff --git a/source4/lib/ldb_wrap.c b/source4/lib/ldb_wrap.c
index 134c2271fd..d5178467b1 100644
--- a/source4/lib/ldb_wrap.c
+++ b/source4/lib/ldb_wrap.c
@@ -34,7 +34,7 @@
#include "ldb_wrap.h"
#include "dsdb/samdb/samdb.h"
#include "param/param.h"
-#include "dlinklist.h"
+#include "../lib/util/dlinklist.h"
#include "../tdb/include/tdb.h"
/*
diff --git a/source4/lib/registry/util.c b/source4/lib/registry/util.c
index f9ea2a1934..5d451df33a 100644
--- a/source4/lib/registry/util.c
+++ b/source4/lib/registry/util.c
@@ -89,6 +89,7 @@ _PUBLIC_ char *reg_val_data_string(TALLOC_CTX *mem_ctx,
/* FIXME: We don't support this yet */
break;
default:
+ /* FIXME */
/* Other datatypes aren't supported -> return "NULL" */
break;
}
@@ -156,6 +157,7 @@ _PUBLIC_ bool reg_string_to_val(TALLOC_CTX *mem_ctx,
/* FIXME: We don't support this yet */
return false;
default:
+ /* FIXME */
/* Other datatypes aren't supported -> return no success */
return false;
}
diff --git a/source4/lib/socket/config.mk b/source4/lib/socket/config.mk
index ac515c8f6d..07491e115a 100644
--- a/source4/lib/socket/config.mk
+++ b/source4/lib/socket/config.mk
@@ -34,7 +34,7 @@ socket_unix_OBJ_FILES = $(libsocketsrcdir)/socket_unix.o
################################################
# Start SUBSYSTEM SOCKET
[SUBSYSTEM::samba_socket]
-PUBLIC_DEPENDENCIES = LIBTALLOC
+PUBLIC_DEPENDENCIES = LIBTALLOC LIBTSOCKET
PRIVATE_DEPENDENCIES = SOCKET_WRAPPER LIBCLI_COMPOSITE LIBCLI_RESOLVE
# End SUBSYSTEM SOCKET
################################################
diff --git a/source4/lib/socket/socket.c b/source4/lib/socket/socket.c
index 8e2f1683f2..30db03fd97 100644
--- a/source4/lib/socket/socket.c
+++ b/source4/lib/socket/socket.c
@@ -24,6 +24,7 @@
#include "system/filesys.h"
#include "system/network.h"
#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
/*
auto-close sockets on free
@@ -344,6 +345,77 @@ _PUBLIC_ struct socket_address *socket_get_my_addr(struct socket_context *sock,
return sock->ops->fn_get_my_addr(sock, mem_ctx);
}
+_PUBLIC_ struct tsocket_address *socket_address_to_tsocket_address(TALLOC_CTX *mem_ctx,
+ const struct socket_address *a)
+{
+ struct tsocket_address *r;
+ int ret;
+
+ if (a->sockaddr) {
+ ret = tsocket_address_bsd_from_sockaddr(mem_ctx,
+ a->sockaddr,
+ a->sockaddrlen,
+ &r);
+ } else {
+ ret = tsocket_address_inet_from_strings(mem_ctx,
+ a->family,
+ a->addr,
+ a->port,
+ &r);
+ }
+
+ if (ret != 0) {
+ return NULL;
+ }
+
+ return r;
+}
+
+_PUBLIC_ struct socket_address *tsocket_address_to_socket_address(TALLOC_CTX *mem_ctx,
+ const struct tsocket_address *a)
+{
+ ssize_t ret;
+ struct sockaddr_storage ss;
+ size_t sslen = sizeof(ss);
+
+ ret = tsocket_address_bsd_sockaddr(a, (struct sockaddr *)(void *)&ss, sslen);
+ if (ret < 0) {
+ return NULL;
+ }
+
+ return socket_address_from_sockaddr(mem_ctx, (struct sockaddr *)(void *)&ss, ret);
+}
+
+_PUBLIC_ struct tsocket_address *socket_get_remote_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct socket_address *a;
+ struct tsocket_address *r;
+
+ a = socket_get_peer_addr(sock, mem_ctx);
+ if (a == NULL) {
+ return NULL;
+ }
+
+ r = socket_address_to_tsocket_address(mem_ctx, a);
+ talloc_free(a);
+ return r;
+}
+
+_PUBLIC_ struct tsocket_address *socket_get_local_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct socket_address *a;
+ struct tsocket_address *r;
+
+ a = socket_get_my_addr(sock, mem_ctx);
+ if (a == NULL) {
+ return NULL;
+ }
+
+ r = socket_address_to_tsocket_address(mem_ctx, a);
+ talloc_free(a);
+ return r;
+}
+
_PUBLIC_ int socket_get_fd(struct socket_context *sock)
{
if (!sock->ops->fn_get_fd) {
diff --git a/source4/lib/socket/socket.h b/source4/lib/socket/socket.h
index 02872457b5..8f8922bcea 100644
--- a/source4/lib/socket/socket.h
+++ b/source4/lib/socket/socket.h
@@ -127,6 +127,7 @@ struct socket_context {
};
struct resolve_context;
+struct tsocket_address;
/* prototypes */
NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
@@ -158,6 +159,12 @@ NTSTATUS socket_set_option(struct socket_context *sock, const char *option, cons
char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx);
struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+struct tsocket_address *socket_address_to_tsocket_address(TALLOC_CTX *mem_ctx,
+ const struct socket_address *a);
+struct socket_address *tsocket_address_to_socket_address(TALLOC_CTX *mem_ctx,
+ const struct tsocket_address *a);
+struct tsocket_address *socket_get_remote_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+struct tsocket_address *socket_get_local_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
int socket_get_fd(struct socket_context *sock);
NTSTATUS socket_dup(struct socket_context *sock);
struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
diff --git a/source4/libcli/finddcs.c b/source4/libcli/finddcs.c
index 8330042ea1..04b25d37ef 100644
--- a/source4/libcli/finddcs.c
+++ b/source4/libcli/finddcs.c
@@ -158,6 +158,9 @@ static void finddcs_name_resolved(struct composite_context *ctx)
if (composite_nomem(state->r.in.my_accountname, state->ctx)) return;
state->r.in.account_control = ACB_WSTRUST;
state->r.in.domain_sid = state->domain_sid;
+ if (state->r.in.domain_sid == NULL) {
+ state->r.in.domain_sid = talloc_zero(state, struct dom_sid);
+ }
ireq = irpc_call_send(state->msg_ctx, nbt_servers[0],
&ndr_table_irpc, NDR_NBTD_GETDCNAME,
diff --git a/source4/libcli/security/sddl.c b/source4/libcli/security/sddl.c
index 2244a3d3ed..c4f8c5667b 100644
--- a/source4/libcli/security/sddl.c
+++ b/source4/libcli/security/sddl.c
@@ -304,7 +304,7 @@ static struct security_acl *sddl_decode_acl(struct security_descriptor *sd,
acl = talloc_zero(sd, struct security_acl);
if (acl == NULL) return NULL;
- acl->revision = SECURITY_ACL_REVISION_NT4;
+ acl->revision = SECURITY_ACL_REVISION_ADS;
if (isupper(sddl[0]) && sddl[1] == ':') {
/* its an empty ACL */
diff --git a/source4/libcli/security/tests/bindings.py b/source4/libcli/security/tests/bindings.py
index 00fa05d070..6fe3d5471d 100644
--- a/source4/libcli/security/tests/bindings.py
+++ b/source4/libcli/security/tests/bindings.py
@@ -2,17 +2,17 @@
# Unix SMB/CIFS implementation.
# 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/>.
#
@@ -46,6 +46,7 @@ class SecurityTokenTests(unittest.TestCase):
class SecurityDescriptorTests(unittest.TestCase):
+
def setUp(self):
self.descriptor = security.descriptor()
@@ -100,6 +101,10 @@ class SecurityDescriptorTests(unittest.TestCase):
desc1 = security.descriptor.from_sddl(text, dom)
self.assertNotEqual(desc1.as_sddl(), desc1.as_sddl(dom))
+ def test_split(self):
+ dom = security.dom_sid("S-2-0-7")
+ self.assertEquals((security.dom_sid("S-2-0"), 7), dom.split())
+
class DomSidTests(unittest.TestCase):
def test_parse_sid(self):
diff --git a/source4/libnet/libnet.c b/source4/libnet/libnet.c
index b10fb65df6..86cf80b4da 100644
--- a/source4/libnet/libnet.c
+++ b/source4/libnet/libnet.c
@@ -42,6 +42,9 @@ struct libnet_context *libnet_context_init(struct tevent_context *ev,
ctx->event_ctx = ev;
ctx->lp_ctx = lp_ctx;
+ /* make sure dcerpc is initialized */
+ dcerpc_init(lp_ctx);
+
/* name resolution methods */
ctx->resolve_ctx = lp_resolve_context(lp_ctx);
diff --git a/source4/libnet/libnet_passwd.c b/source4/libnet/libnet_passwd.c
index cb09e3041e..8195347b83 100644
--- a/source4/libnet/libnet_passwd.c
+++ b/source4/libnet/libnet_passwd.c
@@ -109,11 +109,11 @@ static NTSTATUS libnet_ChangePassword_samr(struct libnet_context *ctx, TALLOC_CT
nt_errstr(status));
r->samr.out.error_string = talloc_asprintf(mem_ctx,
"samr_ChangePasswordUser3 for '%s\\%s' failed: %s",
- r->samr.in.domain_name, r->samr.in.account_name,
+ r->samr.in.domain_name, r->samr.in.account_name,
nt_errstr(status));
}
goto disconnect;
- }
+ }
/* prepare samr_ChangePasswordUser2 */
encode_pw_buffer(lm_pass.data, r->samr.in.newpassword, STR_ASCII|STR_TERMINATE);
@@ -216,7 +216,7 @@ static NTSTATUS libnet_ChangePassword_samr(struct libnet_context *ctx, TALLOC_CT
#endif
disconnect:
/* close connection */
- talloc_free(c.out.dcerpc_pipe);
+ talloc_unlink(ctx, c.out.dcerpc_pipe);
return status;
}
@@ -627,7 +627,7 @@ static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX *
disconnect:
/* close connection */
- talloc_free(c.out.dcerpc_pipe);
+ talloc_unlink(ctx, c.out.dcerpc_pipe);
return status;
}
diff --git a/source4/libnet/py_net.c b/source4/libnet/py_net.c
index 4d3e81ce26..e5ca5e1c97 100644
--- a/source4/libnet/py_net.c
+++ b/source4/libnet/py_net.c
@@ -1,18 +1,19 @@
-/*
+/*
Unix SMB/CIFS implementation.
Samba utility functions
Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
-
+ Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 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/>.
*/
@@ -65,10 +66,16 @@ static PyObject *py_net_join(PyObject *cls, PyObject *args, PyObject *kwargs)
creds = cli_credentials_from_py_object(py_creds);
if (creds == NULL) {
PyErr_SetString(PyExc_TypeError, "Expected credentials object");
+ talloc_free(mem_ctx);
return NULL;
}
libnet_ctx = py_net_ctx(cls, ev, creds);
+ if (libnet_ctx == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "Unable to initialize libnet");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
status = libnet_Join(libnet_ctx, mem_ctx, &r);
if (NT_STATUS_IS_ERR(status)) {
@@ -77,7 +84,7 @@ static PyObject *py_net_join(PyObject *cls, PyObject *args, PyObject *kwargs)
return NULL;
}
- result = Py_BuildValue("sss", r.out.join_password,
+ result = Py_BuildValue("sss", r.out.join_password,
dom_sid_string(mem_ctx, r.out.domain_sid),
r.out.domain_name);
@@ -89,15 +96,72 @@ static PyObject *py_net_join(PyObject *cls, PyObject *args, PyObject *kwargs)
return result;
}
-static char py_net_join_doc[] = "join(domain_name, netbios_name, join_type, level) -> (join_password, domain_sid, domain_name)\n\n" \
+static PyObject *py_net_set_password(PyObject *cls, PyObject *args, PyObject *kwargs)
+{
+ union libnet_SetPassword r;
+ NTSTATUS status;
+ PyObject *py_creds;
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *ev;
+ struct libnet_context *libnet_ctx;
+ struct cli_credentials *creds;
+ const char *kwnames[] = { "account_name", "domain_name", "newpassword", "credentials", NULL };
+
+ r.generic.level = LIBNET_SET_PASSWORD_GENERIC;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sssO:SetPassword", discard_const_p(char *, kwnames),
+ &r.generic.in.account_name, &r.generic.in.domain_name,
+ &r.generic.in.newpassword, &py_creds)) {
+ return NULL;
+ }
+
+ /* FIXME: we really need to get a context from the caller or we may end
+ * up with 2 event contexts */
+ ev = s4_event_context_init(NULL);
+ mem_ctx = talloc_new(ev);
+
+ creds = cli_credentials_from_py_object(py_creds);
+ if (creds == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials object");
+ return NULL;
+ }
+
+ libnet_ctx = py_net_ctx(cls, ev, creds);
+
+ status = libnet_SetPassword(libnet_ctx, mem_ctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetString(PyExc_RuntimeError, r.generic.out.error_string);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static const char py_net_join_doc[] = "join(domain_name, netbios_name, join_type, level) -> (join_password, domain_sid, domain_name)\n\n" \
"Join the domain with the specified name.";
+static const char py_net_set_password_doc[] = "SetPassword(account_name, domain_name, newpassword) -> True\n\n" \
+"Set password for a user. You must supply credential with enough rights to do this.\n\n" \
+"Sample usage is:\n" \
+"creds = samba.credentials.Credentials()\n" \
+"creds.set_username('admin_user')\n" \
+"creds.set_domain('domain_name')\n" \
+"creds.set_password('pass')\n\n" \
+"net.SetPassword(account_name=<account_name>,\n" \
+" domain_name=creds.get_domain(),\n" \
+" newpassword=new_pass,\n" \
+" credentials=creds)\n";
+
+
static struct PyMethodDef net_methods[] = {
{"Join", (PyCFunction)py_net_join, METH_VARARGS|METH_KEYWORDS, py_net_join_doc},
+ {"SetPassword", (PyCFunction)py_net_set_password, METH_VARARGS|METH_KEYWORDS, py_net_set_password_doc},
{NULL }
};
void initnet(void)
{
- Py_InitModule("net", net_methods);
+ Py_InitModule3("net", net_methods, NULL);
}
+
diff --git a/source4/librpc/ndr/py_security.c b/source4/librpc/ndr/py_security.c
index 02dc059f05..d04e2579f5 100644
--- a/source4/librpc/ndr/py_security.c
+++ b/source4/librpc/ndr/py_security.c
@@ -41,6 +41,33 @@ static void PyType_AddMethods(PyTypeObject *type, PyMethodDef *methods)
}
}
+static PyObject *py_dom_sid_split(PyObject *py_self, PyObject *args)
+{
+ struct dom_sid *self = py_talloc_get_ptr(py_self);
+ struct dom_sid *domain_sid;
+ TALLOC_CTX *mem_ctx;
+ uint32_t rid;
+ NTSTATUS status;
+ PyObject *py_domain_sid;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ status = dom_sid_split_rid(mem_ctx, self, &domain_sid, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetString(PyExc_RuntimeError, "dom_sid_split_rid failed");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ py_domain_sid = py_talloc_steal(&dom_sid_Type, domain_sid);
+ talloc_free(mem_ctx);
+ return Py_BuildValue("(OI)", py_domain_sid, rid);
+}
+
static int py_dom_sid_cmp(PyObject *py_self, PyObject *py_other)
{
struct dom_sid *self = py_talloc_get_ptr(py_self), *other;
@@ -86,12 +113,21 @@ static int py_dom_sid_init(PyObject *self, PyObject *args, PyObject *kwargs)
return 0;
}
+static PyMethodDef py_dom_sid_extra_methods[] = {
+ { "split", (PyCFunction)py_dom_sid_split, METH_NOARGS,
+ "S.split() -> (domain_sid, rid)\n"
+ "Split a domain sid" },
+ { NULL }
+};
+
+
static void py_dom_sid_patch(PyTypeObject *type)
{
type->tp_init = py_dom_sid_init;
type->tp_str = py_dom_sid_str;
type->tp_repr = py_dom_sid_repr;
type->tp_compare = py_dom_sid_cmp;
+ PyType_AddMethods(type, py_dom_sid_extra_methods);
}
#define PY_DOM_SID_PATCH py_dom_sid_patch
diff --git a/source4/min_versions.m4 b/source4/min_versions.m4
index 5bb3b0b3a9..af8c4dd8fb 100644
--- a/source4/min_versions.m4
+++ b/source4/min_versions.m4
@@ -1,6 +1,6 @@
# Minimum and exact required versions for various libraries
# if we use the ones installed in the system.
define(TDB_MIN_VERSION,1.2.0)
-define(TALLOC_MIN_VERSION,2.0.0)
+define(TALLOC_MIN_VERSION,2.0.1)
define(LDB_REQUIRED_VERSION,0.9.10)
define(TEVENT_REQUIRED_VERSION,0.9.8)
diff --git a/source4/param/config.mk b/source4/param/config.mk
index 6e5290b64d..a5bcea691b 100644
--- a/source4/param/config.mk
+++ b/source4/param/config.mk
@@ -13,7 +13,7 @@ PUBLIC_HEADERS += param/param.h
PC_FILES += $(paramsrcdir)/samba-hostconfig.pc
[SUBSYSTEM::PROVISION]
-PRIVATE_DEPENDENCIES = LIBPYTHON pyldb pyparam_util
+PRIVATE_DEPENDENCIES = LIBPYTHON pyparam_util LIBLDB
PROVISION_OBJ_FILES = $(paramsrcdir)/provision.o $(param_OBJ_FILES)
diff --git a/source4/param/provision.c b/source4/param/provision.c
index 8c6e1c0f68..7bd10ca522 100644
--- a/source4/param/provision.c
+++ b/source4/param/provision.c
@@ -52,6 +52,34 @@ static PyObject *schema_module(void)
return PyImport_Import(name);
}
+static PyObject *ldb_module(void)
+{
+ PyObject *name = PyString_FromString("ldb");
+ if (name == NULL)
+ return NULL;
+ return PyImport_Import(name);
+}
+
+static PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx)
+{
+ PyLdbObject *ret;
+ PyObject *ldb_mod = ldb_module();
+ PyTypeObject *ldb_ctx_type;
+ if (ldb_mod == NULL)
+ return NULL;
+
+ ldb_ctx_type = PyObject_GetAttrString(ldb_mod, "Ldb");
+
+ ret = (PyLdbObject *)ldb_ctx_type->tp_alloc(ldb_ctx_type, 0);
+ if (ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ ret->mem_ctx = talloc_new(NULL);
+ ret->ldb_ctx = talloc_reference(ret->mem_ctx, ldb_ctx);
+ return (PyObject *)ret;
+}
+
NTSTATUS provision_bare(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
struct provision_settings *settings,
struct provision_result *result)
@@ -167,8 +195,6 @@ NTSTATUS provision_bare(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
return NT_STATUS_OK;
}
-extern void initldb(void);
-
static PyObject *py_dom_sid_FromSid(struct dom_sid *sid)
{
PyObject *mod_security, *dom_sid_Type;
@@ -220,7 +246,6 @@ NTSTATUS provision_store_self_join(TALLOC_CTX *mem_ctx, struct loadparm_context
py_load_samba_modules();
Py_Initialize();
py_update_path("bin"); /* FIXME: Can't assume this is always the case */
- initldb();
provision_mod = provision_module();
if (provision_mod == NULL) {
diff --git a/source4/param/pyparam.c b/source4/param/pyparam.c
index eb2da11bb0..b4255002d2 100644
--- a/source4/param/pyparam.c
+++ b/source4/param/pyparam.c
@@ -374,6 +374,9 @@ void initparam(void)
if (PyType_Ready(&PyLoadparmContext) < 0)
return;
+ if (PyType_Ready(&PyLoadparmService) < 0)
+ return;
+
m = Py_InitModule3("param", pyparam_methods, "Parsing and writing Samba configuration files.");
if (m == NULL)
return;
diff --git a/source4/param/tests/bindings.py b/source4/param/tests/bindings.py
index 41a67f93fc..1915e79223 100644
--- a/source4/param/tests/bindings.py
+++ b/source4/param/tests/bindings.py
@@ -2,17 +2,17 @@
# Unix SMB/CIFS implementation.
# 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/>.
#
@@ -50,3 +50,7 @@ class LoadParmTestCase(unittest.TestCase):
file = param.LoadParm()
file.load_default()
+ def test_section_nonexistant(self):
+ samba_lp = param.LoadParm()
+ samba_lp.load_default()
+ self.assertRaises(KeyError, samba_lp.__getitem__, "nonexistant")
diff --git a/source4/rpc_server/drsuapi/addentry.c b/source4/rpc_server/drsuapi/addentry.c
index 89b664d691..ac94daa6a6 100644
--- a/source4/rpc_server/drsuapi/addentry.c
+++ b/source4/rpc_server/drsuapi/addentry.c
@@ -199,7 +199,8 @@ WERROR dcesrv_drsuapi_DsAddEntry(struct dcesrv_call_state *dce_call, TALLOC_CTX
ret = ldb_transaction_commit(b_state->sam_ctx);
if (ret != LDB_SUCCESS) {
- DEBUG(0,(__location__ ": DsAddEntry commit failed\n"));
+ DEBUG(0,(__location__ ": DsAddEntry commit failed: %s\n",
+ ldb_errstring(b_state->sam_ctx)));
return WERR_DS_DRA_INTERNAL_ERROR;
}
diff --git a/source4/rpc_server/drsuapi/dcesrv_drsuapi.c b/source4/rpc_server/drsuapi/dcesrv_drsuapi.c
index 3d92880162..6a6bc8be7e 100644
--- a/source4/rpc_server/drsuapi/dcesrv_drsuapi.c
+++ b/source4/rpc_server/drsuapi/dcesrv_drsuapi.c
@@ -159,10 +159,7 @@ static WERROR dcesrv_drsuapi_DsBind(struct dcesrv_call_state *dce_call, TALLOC_C
b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
- if (0 /*domain.behavior_version == 2*/) {
- /* TODO: find out how this is really triggered! */
- b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
- }
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
diff --git a/source4/rpc_server/drsuapi/dcesrv_drsuapi.h b/source4/rpc_server/drsuapi/dcesrv_drsuapi.h
index e42d9569e7..3a64ef5c9c 100644
--- a/source4/rpc_server/drsuapi/dcesrv_drsuapi.h
+++ b/source4/rpc_server/drsuapi/dcesrv_drsuapi.h
@@ -57,7 +57,6 @@ int drsuapi_search_with_extended_dn(struct ldb_context *ldb,
struct ldb_dn *basedn,
enum ldb_scope scope,
const char * const *attrs,
- const char *sort_attrib,
const char *filter);
WERROR drs_security_level_check(struct dcesrv_call_state *dce_call,
diff --git a/source4/rpc_server/drsuapi/drsutil.c b/source4/rpc_server/drsuapi/drsutil.c
index a62f911537..0a8a576d60 100644
--- a/source4/rpc_server/drsuapi/drsutil.c
+++ b/source4/rpc_server/drsuapi/drsutil.c
@@ -47,7 +47,6 @@ int drsuapi_search_with_extended_dn(struct ldb_context *ldb,
struct ldb_dn *basedn,
enum ldb_scope scope,
const char * const *attrs,
- const char *sort_attrib,
const char *filter)
{
int ret;
@@ -86,26 +85,11 @@ int drsuapi_search_with_extended_dn(struct ldb_context *ldb,
return ret;
}
- if (sort_attrib) {
- struct ldb_server_sort_control **sort_control;
- sort_control = talloc_array(req, struct ldb_server_sort_control *, 2);
- if (sort_control == NULL) {
- talloc_free(tmp_ctx);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- sort_control[0] = talloc(req, struct ldb_server_sort_control);
- sort_control[0]->attributeName = sort_attrib;
- sort_control[0]->orderingRule = NULL;
- sort_control[0]->reverse = 0;
- sort_control[1] = NULL;
-
- ret = ldb_request_add_control(req, LDB_CONTROL_SERVER_SORT_OID, true, sort_control);
- 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);
diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index 488ecd129e..437dc87ae8 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -25,11 +25,38 @@
#include "dsdb/samdb/samdb.h"
#include "param/param.h"
#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
#include "rpc_server/drsuapi/dcesrv_drsuapi.h"
#include "rpc_server/dcerpc_server_proto.h"
#include "../libcli/drsuapi/drsuapi.h"
#include "libcli/security/security.h"
+/*
+ build a DsReplicaObjectIdentifier from a ldb msg
+ */
+static struct drsuapi_DsReplicaObjectIdentifier *get_object_identifier(TALLOC_CTX *mem_ctx,
+ 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;
+}
+
/*
drsuapi_DsGetNCChanges for one object
*/
@@ -44,9 +71,7 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
{
const struct ldb_val *md_value;
int i, n;
- struct ldb_dn *obj_dn;
struct replPropertyMetaDataBlob md;
- struct dom_sid *sid;
uint32_t rid = 0;
enum ndr_err_code ndr_err;
uint32_t *attids;
@@ -104,20 +129,15 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
obj->meta_data_ctr = talloc(obj, struct drsuapi_DsReplicaMetaDataCtr);
attids = talloc_array(obj, uint32_t, md.ctr.ctr1.count);
- obj->object.identifier = talloc(obj, struct drsuapi_DsReplicaObjectIdentifier);
- obj_dn = ldb_msg_find_attr_as_dn(sam_ctx, obj, msg, "distinguishedName");
- obj->object.identifier->dn = ldb_dn_get_linearized(obj_dn);
- obj->object.identifier->guid = samdb_result_guid(msg, "objectGUID");
- sid = samdb_result_dom_sid(obj, msg, "objectSid");
- if (sid) {
- dom_sid_split_rid(NULL, sid, NULL, &rid);
- obj->object.identifier->sid = *sid;
- } else {
- ZERO_STRUCT(obj->object.identifier->sid);
+ obj->object.identifier = get_object_identifier(obj, msg);
+ if (obj->object.identifier == NULL) {
+ return WERR_NOMEM;
}
+ 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);
for (n=i=0; i<md.ctr.ctr1.count; i++) {
+ const struct dsdb_attribute *sa;
/* 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 &&
@@ -126,6 +146,16 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
/* 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->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;
+ }
+ }
+
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;
@@ -199,6 +229,149 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
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,
+ struct ldb_message *msg,
+ struct dsdb_dn *dsdb_dn,
+ struct drsuapi_DsReplicaLinkedAttribute **la_list,
+ uint32_t *la_count)
+{
+ 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;
+
+ la->attid = sa->attributeID_id;
+ la->flags = active?DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE:0;
+
+ status = dsdb_get_extended_dn_nttime(dsdb_dn->dn, &la->originating_add_time, "RMD_ADDTIME");
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+ status = dsdb_get_extended_dn_uint32(dsdb_dn->dn, &la->meta_data.version, "RMD_VERSION");
+ if (!NT_STATUS_IS_OK(status)) {
+ 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)) {
+ 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)) {
+ 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)) {
+ return ntstatus_to_werror(status);
+ }
+
+ 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,
+ struct ldb_dn *ncRoot_dn,
+ struct dsdb_schema *schema,
+ uint64_t highest_usn,
+ uint32_t replica_flags,
+ struct ldb_message *msg,
+ struct drsuapi_DsReplicaLinkedAttribute **la_list,
+ uint32_t *la_count)
+{
+ int i;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ uint64_t uSNChanged = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1);
+
+ for (i=0; i<msg->num_elements; i++) {
+ struct ldb_message_element *el = &msg->elements[i];
+ const struct dsdb_attribute *sa;
+ 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;
+ NTSTATUS status;
+ WERROR werr;
+
+ 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;
+ }
+
+ werr = get_nc_changes_add_la(mem_ctx, sam_ctx, schema, sa, msg,
+ dsdb_dn, la_list, la_count);
+ if (!W_ERROR_IS_OK(werr)) {
+ talloc_free(tmp_ctx);
+ return werr;
+ }
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return WERR_OK;
+}
+
/*
load replUpToDateVector from a DN
*/
@@ -255,6 +428,7 @@ static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
NTTIME now;
time_t t = time(NULL);
struct replUpToDateVectorBlob ouv;
+ int i;
werr = load_udv(sam_ctx, udv, ncRoot_dn, &ouv);
if (!W_ERROR_IS_OK(werr)) {
@@ -267,13 +441,24 @@ static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
unix_to_nt_time(&now, t);
tmp_cursor->last_sync_success = now;
- udv->count = ouv.ctr.ctr2.count + 1;
+ udv->count = ouv.ctr.ctr2.count;
udv->cursors = talloc_steal(udv, ouv.ctr.ctr2.cursors);
- udv->cursors = talloc_realloc(udv, udv->cursors, struct drsuapi_DsReplicaCursor2, udv->count);
- if (!udv->cursors) {
- return WERR_DS_DRA_INTERNAL_ERROR;
+
+ for (i=0; i<udv->count; i++) {
+ if (GUID_equal(&tmp_cursor->source_dsa_invocation_id,
+ &udv->cursors[i].source_dsa_invocation_id)) {
+ udv->cursors[i] = *tmp_cursor;
+ break;
+ }
+ }
+ if (i == udv->count) {
+ udv->cursors = talloc_realloc(udv, udv->cursors, struct drsuapi_DsReplicaCursor2, udv->count+1);
+ if (!udv->cursors) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ udv->cursors[udv->count] = *tmp_cursor;
+ udv->count++;
}
- udv->cursors[udv->count - 1] = *tmp_cursor;
qsort(udv->cursors, udv->count,
sizeof(struct drsuapi_DsReplicaCursor2),
@@ -282,6 +467,103 @@ static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
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 drsuapi_DsReplicaLinkedAttribute *la1,
+ const struct drsuapi_DsReplicaLinkedAttribute *la2,
+ struct ldb_context *sam_ctx)
+{
+ int c;
+ WERROR werr;
+ TALLOC_CTX *tmp_ctx;
+ const struct dsdb_schema *schema;
+ const struct dsdb_attribute *schema_attrib;
+ struct dsdb_dn *dn1, *dn2;
+ struct GUID guid1, guid2;
+ NTSTATUS status;
+
+ c = GUID_compare(&la1->identifier->guid,
+ &la2->identifier->guid);
+ if (c != 0) return c;
+
+ if (la1->attid != la2->attid) {
+ return la1->attid < la2->attid? -1:1;
+ }
+
+ if ((la1->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) !=
+ (la2->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)) {
+ return (la1->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)? 1:-1;
+ }
+
+ /* we need to get the target GUIDs to compare */
+ tmp_ctx = talloc_new(sam_ctx);
+
+ schema = dsdb_get_schema(sam_ctx);
+ schema_attrib = dsdb_attribute_by_attributeID_id(schema, la1->attid);
+
+ werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, tmp_ctx, la1->value.blob, &dn1);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,(__location__ ": Bad la1 blob in sort\n"));
+ talloc_free(tmp_ctx);
+ return 0;
+ }
+
+ werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, tmp_ctx, la2->value.blob, &dn2);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,(__location__ ": Bad la2 blob in sort\n"));
+ talloc_free(tmp_ctx);
+ return 0;
+ }
+
+ status = dsdb_get_extended_dn_guid(dn1->dn, &guid1, "GUID");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": Bad la1 guid in sort\n"));
+ talloc_free(tmp_ctx);
+ return 0;
+ }
+ status = dsdb_get_extended_dn_guid(dn2->dn, &guid2, "GUID");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": Bad la2 guid in sort\n"));
+ talloc_free(tmp_ctx);
+ return 0;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return GUID_compare(&guid1, &guid2);
+}
+
+
+/*
+ sort the objects we send by tree order
+ */
+static int site_res_cmp_parent_order(const struct ldb_message **m1, const struct ldb_message **m2)
+{
+ return ldb_dn_compare((*m2)->dn, (*m1)->dn);
+}
+
+/*
+ sort the objects we send first by uSNChanged
+ */
+static int site_res_cmp_usn_order(const struct ldb_message **m1, const struct ldb_message **m2)
+{
+ unsigned usnchanged1, usnchanged2;
+ unsigned cn1, cn2;
+ cn1 = ldb_dn_get_comp_num((*m1)->dn);
+ cn2 = ldb_dn_get_comp_num((*m2)->dn);
+ if (cn1 != cn2) {
+ return cn1 > cn2 ? 1 : -1;
+ }
+ usnchanged1 = ldb_msg_find_attr_as_uint(*m1, "uSNChanged", 0);
+ usnchanged2 = ldb_msg_find_attr_as_uint(*m2, "uSNChanged", 0);
+ if (usnchanged1 == usnchanged2) {
+ return 0;
+ }
+ return usnchanged1 > usnchanged2 ? 1 : -1;
+}
+
+
/* state of a partially completed getncchanges call */
struct drsuapi_getncchanges_state {
struct ldb_result *site_res;
@@ -289,6 +571,9 @@ struct drsuapi_getncchanges_state {
struct ldb_dn *ncRoot_dn;
uint64_t min_usn;
uint64_t highest_usn;
+ struct ldb_dn *last_dn;
+ struct drsuapi_DsReplicaLinkedAttribute *la_list;
+ uint32_t la_count;
};
/*
@@ -323,6 +608,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
struct drsuapi_getncchanges_state *getnc_state;
struct drsuapi_DsGetNCChangesRequest8 *req8;
uint32_t options;
+ uint32_t max_objects;
DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
b_state = h->data;
@@ -389,9 +675,10 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
if (getnc_state) {
struct ldb_dn *new_dn = ldb_dn_new(getnc_state, b_state->sam_ctx, ncRoot->dn);
if (ldb_dn_compare(new_dn, getnc_state->ncRoot_dn) != 0) {
- DEBUG(0,(__location__ ": DsGetNCChanges 2nd replication on different DN %s %s\n",
+ 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->ncRoot_dn),
+ ldb_dn_get_linearized(getnc_state->last_dn)));
talloc_free(getnc_state);
getnc_state = NULL;
}
@@ -433,6 +720,9 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
if (getnc_state->site_res == NULL) {
char* search_filter;
enum ldb_scope scope = LDB_SCOPE_SUBTREE;
+ const char *extra_filter;
+
+ extra_filter = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "object filter");
getnc_state->min_usn = req8->highwatermark.highest_usn;
@@ -441,6 +731,10 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
"(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 (req8->replica_flags & DRSUAPI_DS_REPLICA_NEIGHBOUR_CRITICAL_ONLY) {
search_filter = talloc_asprintf(mem_ctx,
"(&%s(isCriticalSystemObject=TRUE))",
@@ -451,15 +745,27 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
scope = LDB_SCOPE_BASE;
}
- DEBUG(6,(__location__ ": getncchanges on %s using filter %s\n",
+ DEBUG(1,(__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, &getnc_state->site_res,
getnc_state->ncRoot_dn, scope, attrs,
- "uSNChanged",
search_filter);
if (ret != LDB_SUCCESS) {
return WERR_DS_DRA_INTERNAL_ERROR;
}
+
+ if (req8->replica_flags & DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS) {
+ qsort(getnc_state->site_res->msgs,
+ getnc_state->site_res->count,
+ sizeof(getnc_state->site_res->msgs[0]),
+ (comparison_fn_t)site_res_cmp_parent_order);
+ } else {
+ qsort(getnc_state->site_res->msgs,
+ getnc_state->site_res->count,
+ sizeof(getnc_state->site_res->msgs[0]),
+ (comparison_fn_t)site_res_cmp_usn_order);
+ }
+
}
/* Prefix mapping */
@@ -494,23 +800,25 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
r->out.ctr->ctr6.first_object = NULL;
currentObject = &r->out.ctr->ctr6.first_object;
+ /* use this to force single objects at a time, which is useful
+ * for working out what object is giving problems
+ */
+ max_objects = lp_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max object sync", 1000);
+ if (req8->max_object_count < max_objects) {
+ max_objects = req8->max_object_count;
+ }
+
for(i=getnc_state->num_sent;
i<getnc_state->site_res->count &&
- (r->out.ctr->ctr6.object_count < req8->max_object_count);
+ (r->out.ctr->ctr6.object_count < max_objects);
i++) {
int uSN;
struct drsuapi_DsReplicaObjectListItemEx *obj;
- obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
+ struct ldb_message *msg = getnc_state->site_res->msgs[i];
- uSN = ldb_msg_find_attr_as_int(getnc_state->site_res->msgs[i], "uSNChanged", -1);
- if (uSN > r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn) {
- r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn = uSN;
- }
- if (uSN > getnc_state->highest_usn) {
- getnc_state->highest_usn = uSN;
- }
+ obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
- werr = get_nc_changes_build_object(obj, getnc_state->site_res->msgs[i],
+ werr = get_nc_changes_build_object(obj, msg,
b_state->sam_ctx, getnc_state->ncRoot_dn,
schema, &session_key, getnc_state->min_usn,
req8->replica_flags);
@@ -518,9 +826,28 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
return werr;
}
+ werr = get_nc_changes_add_links(b_state->sam_ctx, getnc_state,
+ getnc_state->ncRoot_dn,
+ schema, getnc_state->min_usn,
+ req8->replica_flags,
+ msg,
+ &getnc_state->la_list,
+ &getnc_state->la_count);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ uSN = ldb_msg_find_attr_as_int(msg, "uSNChanged", -1);
+ if (uSN > r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn) {
+ r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn = uSN;
+ }
+ if (uSN > getnc_state->highest_usn) {
+ getnc_state->highest_usn = uSN;
+ }
+
if (obj->meta_data_ctr == NULL) {
DEBUG(0,(__location__ ": getncchanges skipping send of object %s\n",
- ldb_dn_get_linearized(getnc_state->site_res->msgs[i]->dn)));
+ ldb_dn_get_linearized(msg->dn)));
/* no attributes to send */
talloc_free(obj);
continue;
@@ -530,6 +857,11 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
*currentObject = obj;
currentObject = &obj->next_object;
+
+ talloc_free(getnc_state->last_dn);
+ getnc_state->last_dn = ldb_dn_copy(getnc_state, msg->dn);
+
+ DEBUG(8,(__location__ ": replicating object %s\n", ldb_dn_get_linearized(msg->dn)));
}
getnc_state->num_sent += r->out.ctr->ctr6.object_count;
@@ -562,6 +894,13 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
if (i < getnc_state->site_res->count) {
r->out.ctr->ctr6.more_data = true;
} else {
+ r->out.ctr->ctr6.linked_attributes_count = getnc_state->la_count;
+ r->out.ctr->ctr6.linked_attributes = talloc_steal(mem_ctx, getnc_state->la_list);
+
+ ldb_qsort(r->out.ctr->ctr6.linked_attributes, r->out.ctr->ctr6.linked_attributes_count,
+ sizeof(r->out.ctr->ctr6.linked_attributes[0]),
+ b_state->sam_ctx, (ldb_qsort_cmp_fn_t)linked_attribute_compare);
+
r->out.ctr->ctr6.uptodateness_vector = talloc(mem_ctx, struct drsuapi_DsReplicaCursor2CtrEx);
r->out.ctr->ctr6.uptodateness_vector->version = 2;
r->out.ctr->ctr6.uptodateness_vector->reserved1 = 0;
@@ -580,11 +919,19 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
b_state->getncchanges_state = NULL;
}
- DEBUG(2,("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %d/%d)\n",
- (unsigned long long)(req8->highwatermark.highest_usn+1),
- req8->replica_flags,
- ncRoot->dn, r->out.ctr->ctr6.object_count,
- i, r->out.ctr->ctr6.more_data?getnc_state->site_res->count:i));
+ DEBUG(r->out.ctr->ctr6.more_data?2:1,
+ ("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %d/%d la=%d)\n",
+ (unsigned long long)(req8->highwatermark.highest_usn+1),
+ req8->replica_flags,
+ ncRoot->dn, r->out.ctr->ctr6.object_count,
+ i, r->out.ctr->ctr6.more_data?getnc_state->site_res->count:i,
+ r->out.ctr->ctr6.linked_attributes_count));
+
+#if 0
+ if (!r->out.ctr->ctr6.more_data) {
+ 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
index 6e2efed4f9..b1e3d6c352 100644
--- a/source4/rpc_server/drsuapi/updaterefs.c
+++ b/source4/rpc_server/drsuapi/updaterefs.c
@@ -105,7 +105,9 @@ static WERROR uref_del_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
return werr;
}
- if (!found && !(options & DRSUAPI_DS_REPLICA_UPDATE_GETCHG_CHECK)) {
+ if (!found &&
+ !(options & DRSUAPI_DS_REPLICA_UPDATE_GETCHG_CHECK) &&
+ !(options & DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE)) {
return WERR_DS_DRA_REF_NOT_FOUND;
}
diff --git a/source4/script/installmisc.sh b/source4/script/installmisc.sh
index cb618628b0..15fc64544c 100755
--- a/source4/script/installmisc.sh
+++ b/source4/script/installmisc.sh
@@ -1,28 +1,42 @@
#!/bin/sh
# install miscellaneous files
-[ $# -eq 5 ] || {
- echo "Usage: installmisc.sh SRCDIR SETUPDIR BINDDIR SBINDDIR PYTHONDIR"
+[ $# -eq 7 ] || {
+ echo "Usage: installmisc.sh DESTDIR SRCDIR SETUPDIR BINDDIR SBINDDIR PYTHONDIR PYTHON"
exit 1
}
-SRCDIR="$1"
-SETUPDIR="$2"
-BINDIR="$3"
-SBINDIR="$4"
-PYTHONDIR="$5"
+DESTDIR="$1"
+SRCDIR="$2"
+SETUPDIR="$3"
+BINDIR="$4"
+SBINDIR="$5"
+PYTHONDIR="$6"
+PYTHON="$7"
cd $SRCDIR || exit 1
+if $PYTHON -c "import sys; sys.exit('$PYTHONDIR' in sys.path)"; then
+ PYTHON_PATH_NEEDS_FIXING=yes
+ echo "sys.path in python scripts will be updated to include $PYTHONDIR"
+else
+ PYTHON_PATH_NEEDS_FIXING=no
+fi
+
# fixup a python script to use the right path
fix_python_path() {
f="$1"
- egrep 'sys.path.insert.*bin/python' $f > /dev/null && {
- # old systems don't have sed -i :-(
- sed "s|\(sys.path.insert.*\)bin/python\(.*\)$|\1$PYTHONDIR\2|g" < $f > $f.$$ || exit 1
- mv -f $f.$$ $f || exit 1
- chmod +x $f
- }
+ if egrep 'sys.path.insert.*bin/python' $f > /dev/null; then
+ if [ "$PYTHON_PATH_NEEDS_FIXING" = "yes" ]; then
+ # old systems don't have sed -i :-(
+ sed "s|\(sys.path.insert.*\)bin/python\(.*\)$|\1$PYTHONDIR\2|g" < $f > $f.$$ || exit 1
+ else
+ # old systems don't have sed -i :-(
+ sed "s|\(sys.path.insert.*\)bin/python\(.*\)$||g" < $f > $f.$$ || exit 1
+ fi
+ mv -f $f.$$ $f || exit 1
+ chmod +x $f
+ fi
}
echo "Installing setup templates"
@@ -35,7 +49,7 @@ cp setup/ad-schema/*.txt $SETUPDIR/ad-schema || exit 1
cp setup/display-specifiers/*.txt $SETUPDIR/display-specifiers || exit 1
echo "Installing sbin scripts from setup/*"
-for p in domainlevel enableaccount newuser provision setexpiry setpassword pwsettings
+for p in provision
do
cp setup/$p $SBINDIR || exit 1
chmod a+x $SBINDIR/$p
diff --git a/source4/scripting/python/modules.c b/source4/scripting/python/modules.c
index 5365c5007d..198b3ccf12 100644
--- a/source4/scripting/python/modules.c
+++ b/source4/scripting/python/modules.c
@@ -61,10 +61,47 @@ void py_load_samba_modules(void)
}
}
-void py_update_path(const char *bindir)
+static bool PySys_PathPrepend(PyObject *list, const char *path)
+{
+ PyObject *py_path = PyString_FromString(path);
+ if (py_path == NULL)
+ return false;
+
+ return (PyList_Insert(list, 0, py_path) == 0);
+}
+
+bool py_update_path(const char *bindir)
{
char *newpath;
- asprintf(&newpath, "%s/python:%s/../scripting/python:%s", bindir, bindir, Py_GetPath());
- PySys_SetPath(newpath);
+ PyObject *mod_sys, *py_path;
+
+ mod_sys = PyImport_ImportModule("sys");
+ if (mod_sys == NULL) {
+ return false;
+ }
+
+ py_path = PyObject_GetAttrString(mod_sys, "path");
+ if (py_path == NULL) {
+ return false;
+ }
+
+ if (!PyList_Check(py_path)) {
+ return false;
+ }
+
+ asprintf(&newpath, "%s/../scripting/python", bindir);
+ if (!PySys_PathPrepend(py_path, newpath)) {
+ free(newpath);
+ return false;
+ }
free(newpath);
+
+ asprintf(&newpath, "%s/python", bindir);
+ if (!PySys_PathPrepend(py_path, newpath)) {
+ free(newpath);
+ return false;
+ }
+ free(newpath);
+
+ return true;
}
diff --git a/source4/scripting/python/modules.h b/source4/scripting/python/modules.h
index 6b242ee257..4d1067cdd4 100644
--- a/source4/scripting/python/modules.h
+++ b/source4/scripting/python/modules.h
@@ -21,7 +21,7 @@
#define __SAMBA_PYTHON_MODULES_H__
void py_load_samba_modules(void);
-void py_update_path(const char *bindir);
+bool py_update_path(const char *bindir);
#define py_iconv_convenience(mem_ctx) smb_iconv_convenience_init(mem_ctx, "ASCII", PyUnicode_GetDefaultEncoding(), true)
diff --git a/source4/scripting/python/pyglue.c b/source4/scripting/python/pyglue.c
index 9f01102316..3d33e605db 100644
--- a/source4/scripting/python/pyglue.c
+++ b/source4/scripting/python/pyglue.c
@@ -442,27 +442,6 @@ static PyObject *py_dsdb_make_schema_global(PyObject *self, PyObject *args)
Py_RETURN_NONE;
}
-static PyObject *py_dom_sid_to_rid(PyLdbObject *self, PyObject *args)
-{
- PyObject *py_sid;
- struct dom_sid *sid;
- uint32_t rid;
- NTSTATUS status;
-
- if(!PyArg_ParseTuple(args, "O", &py_sid))
- return NULL;
-
- sid = dom_sid_parse_talloc(NULL, PyString_AsString(py_sid));
-
- status = dom_sid_split_rid(NULL, sid, NULL, &rid);
- if (!NT_STATUS_IS_OK(status)) {
- PyErr_SetString(PyExc_RuntimeError, "dom_sid_split_rid failed");
- return NULL;
- }
-
- return PyInt_FromLong(rid);
-}
-
static PyMethodDef py_misc_methods[] = {
{ "generate_random_str", (PyCFunction)py_generate_random_str, METH_VARARGS,
"random_password(len) -> string\n"
@@ -506,8 +485,6 @@ static PyMethodDef py_misc_methods[] = {
NULL },
{ "dsdb_make_schema_global", (PyCFunction)py_dsdb_make_schema_global, METH_VARARGS,
NULL },
- { "dom_sid_to_rid", (PyCFunction)py_dom_sid_to_rid, METH_VARARGS,
- NULL },
{ "set_debug_level", (PyCFunction)py_set_debug_level, METH_VARARGS,
"set debug level" },
{ NULL }
diff --git a/source4/scripting/python/samba/__init__.py b/source4/scripting/python/samba/__init__.py
index f74304c01c..5d61c1bd8c 100644
--- a/source4/scripting/python/samba/__init__.py
+++ b/source4/scripting/python/samba/__init__.py
@@ -2,20 +2,20 @@
# Unix SMB/CIFS implementation.
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
-#
+#
# Based on the original in EJS:
# Copyright (C) Andrew Tridgell <tridge@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/>.
#
@@ -45,11 +45,11 @@ import ldb
import glue
class Ldb(ldb.Ldb):
- """Simple Samba-specific LDB subclass that takes care
+ """Simple Samba-specific LDB subclass that takes care
of setting up the modules dir, credentials pointers, etc.
-
- Please note that this is intended to be for all Samba LDB files,
- not necessarily the Sam database. For Sam-specific helper
+
+ Please note that this is intended to be for all Samba LDB files,
+ not necessarily the Sam database. For Sam-specific helper
functions see samdb.py.
"""
def __init__(self, url=None, lp=None, modules_dir=None, session_info=None,
@@ -65,7 +65,7 @@ class Ldb(ldb.Ldb):
:param options: Additional options (optional)
This is different from a regular Ldb file in that the Samba-specific
- modules-dir is used by default and that credentials and session_info
+ modules-dir is used by default and that credentials and session_info
can be passed through (required by some modules).
"""
@@ -122,10 +122,10 @@ class Ldb(ldb.Ldb):
# need one public, we will have to change this here
super(Ldb, self).set_create_perms(perms)
- def searchone(self, attribute, basedn=None, expression=None,
+ def searchone(self, attribute, basedn=None, expression=None,
scope=ldb.SCOPE_BASE):
"""Search for one attribute as a string.
-
+
:param basedn: BaseDN for the search.
:param attribute: Name of the attribute
:param expression: Optional search expression.
@@ -166,7 +166,7 @@ class Ldb(ldb.Ldb):
self.erase_users_computers(basedn)
# Delete the 'visible' records, and the invisble 'deleted' records (if this DB supports it)
- for msg in self.search(basedn, ldb.SCOPE_SUBTREE,
+ for msg in self.search(basedn, ldb.SCOPE_SUBTREE,
"(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))",
[], controls=["show_deleted:0"]):
try:
@@ -174,14 +174,14 @@ class Ldb(ldb.Ldb):
except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _):
# Ignore no such object errors
pass
-
- res = self.search(basedn, ldb.SCOPE_SUBTREE,
+
+ res = self.search(basedn, ldb.SCOPE_SUBTREE,
"(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))",
[], controls=["show_deleted:0"])
assert len(res) == 0
# delete the specials
- for attr in ["@SUBCLASSES", "@MODULES",
+ for attr in ["@SUBCLASSES", "@MODULES",
"@OPTIONS", "@PARTITION", "@KLUDGEACL"]:
try:
self.delete(attr)
@@ -191,7 +191,7 @@ class Ldb(ldb.Ldb):
def erase(self):
"""Erase this ldb, removing all records."""
-
+
self.erase_except_schema_controlled()
# delete the specials
@@ -207,13 +207,12 @@ class Ldb(ldb.Ldb):
def erase_recursive(self, dn):
try:
- res = self.search(base=dn, scope=ldb.SCOPE_ONELEVEL, attrs=[],
+ res = self.search(base=dn, scope=ldb.SCOPE_ONELEVEL, attrs=[],
controls=["show_deleted:0"])
except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _):
# Ignore no such object errors
return
- pass
-
+
for msg in res:
erase_recursive(self, msg.dn)
@@ -223,7 +222,7 @@ class Ldb(ldb.Ldb):
# Ignore no such object errors
pass
- res = self.search("", ldb.SCOPE_BASE, "(objectClass=*)",
+ res = self.search("", ldb.SCOPE_BASE, "(objectClass=*)",
["namingContexts"])
assert len(res) == 1
if not "namingContexts" in res[0]:
@@ -285,14 +284,14 @@ class Ldb(ldb.Ldb):
def set_invocation_id(self, invocation_id):
"""Set the invocation id for this SamDB handle.
-
+
:param invocation_id: GUID of the invocation id.
"""
glue.dsdb_set_ntds_invocation_id(self, invocation_id)
def set_opaque_integer(self, name, value):
"""Set an integer as an opaque (a flag or other value) value on the database
-
+
:param name: The name for the opaque value
:param value: The integer value
"""
@@ -302,7 +301,7 @@ class Ldb(ldb.Ldb):
def substitute_var(text, values):
"""substitute strings of the form ${NAME} in str, replacing
with substitutions from subobj.
-
+
:param text: Text in which to subsitute.
:param values: Dictionary with keys and values.
"""
@@ -318,21 +317,21 @@ def substitute_var(text, values):
def check_all_substituted(text):
"""Make sure that all substitution variables in a string have been replaced.
If not, raise an exception.
-
+
:param text: The text to search for substitution variables
"""
if not "${" in text:
return
-
+
var_start = text.find("${")
var_end = text.find("}", var_start)
-
+
raise Exception("Not all variables substituted: %s" % text[var_start:var_end+1])
def read_and_sub_file(file, subst_vars):
"""Read a file and sub in variables found in it
-
+
:param file: File to be read (typically from setup directory)
param subst_vars: Optional variables to subsitute in the file.
"""
@@ -370,15 +369,6 @@ def valid_netbios_name(name):
return True
-def dom_sid_to_rid(sid_str):
- """Converts a domain SID to the relative RID.
-
- :param sid_str: The domain SID formatted as string
- """
-
- return glue.dom_sid_to_rid(sid_str)
-
-
version = glue.version
# "userAccountControl" flags
diff --git a/source4/scripting/python/samba/getopt.py b/source4/scripting/python/samba/getopt.py
index 8b756b2d6f..48d48dc260 100644
--- a/source4/scripting/python/samba/getopt.py
+++ b/source4/scripting/python/samba/getopt.py
@@ -2,17 +2,17 @@
# Samba-specific bits for optparse
# 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/>.
#
@@ -30,7 +30,7 @@ class SambaOptions(optparse.OptionGroup):
def __init__(self, parser):
optparse.OptionGroup.__init__(self, parser, "Samba Common Options")
self.add_option("-s", "--configfile", action="callback",
- type=str, metavar="FILE", help="Configuration file",
+ type=str, metavar="FILE", help="Configuration file",
callback=self._load_configfile)
self._configfile = None
@@ -73,15 +73,15 @@ class CredentialsOptions(optparse.OptionGroup):
help="DN to use for a simple bind")
self.add_option("--password", metavar="PASSWORD", action="callback",
help="Password", type=str, callback=self._set_password)
- self.add_option("-U", "--username", metavar="USERNAME",
+ self.add_option("-U", "--username", metavar="USERNAME",
action="callback", type=str,
help="Username", callback=self._parse_username)
- self.add_option("-W", "--workgroup", metavar="WORKGROUP",
+ self.add_option("-W", "--workgroup", metavar="WORKGROUP",
action="callback", type=str,
help="Workgroup", callback=self._parse_workgroup)
self.add_option("-N", "--no-pass", action="store_true",
help="Don't ask for a password")
- self.add_option("-k", "--kerberos", metavar="KERBEROS",
+ self.add_option("-k", "--kerberos", metavar="KERBEROS",
action="callback", type=str,
help="Use Kerberos", callback=self._set_kerberos)
self.creds = Credentials()
diff --git a/source4/scripting/python/samba/netcmd/__init__.py b/source4/scripting/python/samba/netcmd/__init__.py
new file mode 100644
index 0000000000..a204ab897b
--- /dev/null
+++ b/source4/scripting/python/samba/netcmd/__init__.py
@@ -0,0 +1,145 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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/>.
+#
+
+import optparse
+from samba import getopt as options, Ldb
+import sys
+
+
+class Option(optparse.Option):
+ pass
+
+
+class Command(object):
+ """A net command."""
+
+ def _get_description(self):
+ return self.__doc__.splitlines()[0].rstrip("\n")
+
+ def _get_name(self):
+ name = self.__class__.__name__
+ if name.startswith("cmd_"):
+ return name[4:]
+ return name
+
+ name = property(_get_name)
+
+ def usage(self, *args):
+ parser, _ = self._create_parser()
+ parser.print_usage()
+
+ description = property(_get_description)
+
+ def _get_synopsis(self):
+ ret = self.name
+ if self.takes_args:
+ ret += " " + " ".join([x.upper() for x in self.takes_args])
+ return ret
+
+ synopsis = property(_get_synopsis)
+
+ takes_args = []
+ takes_options = []
+ takes_optiongroups = {}
+
+ def _create_parser(self):
+ parser = optparse.OptionParser(self.synopsis)
+ parser.prog = "net"
+ parser.add_options(self.takes_options)
+ optiongroups = {}
+ for name, optiongroup in self.takes_optiongroups.iteritems():
+ optiongroups[name] = optiongroup(parser)
+ parser.add_option_group(optiongroups[name])
+ return parser, optiongroups
+
+ def message(self, text):
+ print text
+
+ def _run(self, *argv):
+ parser, optiongroups = self._create_parser()
+ opts, args = parser.parse_args(list(argv))
+ # Filter out options from option groups
+ args = args[1:]
+ kwargs = dict(opts.__dict__)
+ for option_group in parser.option_groups:
+ for option in option_group.option_list:
+ del kwargs[option.dest]
+ kwargs.update(optiongroups)
+ min_args = 0
+ max_args = 0
+ for i, arg in enumerate(self.takes_args):
+ if arg[-1] not in ("?", "*"):
+ min_args += 1
+ max_args += 1
+ if arg[-1] == "*":
+ max_args = -1
+ if len(args) < min_args or (max_args != -1 and len(args) > max_args):
+ self.usage(*args)
+ return -1
+ try:
+ return self.run(*args, **kwargs)
+ except CommandError, e:
+ print >>sys.stderr, "ERROR: %s" % e
+ return -1
+
+ def run(self):
+ """Run the command. This should be overriden by all subclasses."""
+ raise NotImplementedError(self.run)
+
+
+class SuperCommand(Command):
+ """A command with subcommands."""
+
+ subcommands = {}
+
+ def _run(self, myname, subcommand=None, *args):
+ if subcommand is None:
+ print "Available subcommands:"
+ for subcommand in self.subcommands:
+ print "\t%s" % subcommand
+ return 0
+ if not subcommand in self.subcommands:
+ raise CommandError("No such subcommand '%s'" % subcommand)
+ return self.subcommands[subcommand]._run(subcommand, *args)
+
+ def usage(self, myname, subcommand=None, *args):
+ if subcommand is None or not subcommand in self.subcommands:
+ print "Usage: %s (%s) [options]" % (myname,
+ " | ".join(self.subcommands.keys()))
+ else:
+ return self.subcommands[subcommand].usage(*args)
+
+
+class CommandError(Exception):
+ pass
+
+
+commands = {}
+from samba.netcmd.pwsettings import cmd_pwsettings
+commands["pwsettings"] = cmd_pwsettings()
+from samba.netcmd.domainlevel import cmd_domainlevel
+commands["domainlevel"] = cmd_domainlevel()
+from samba.netcmd.setpassword import cmd_setpassword
+commands["setpassword"] = cmd_setpassword()
+from samba.netcmd.setexpiry import cmd_setexpiry
+commands["setexpiry"] = cmd_setexpiry()
+from samba.netcmd.enableaccount import cmd_enableaccount
+commands["enableaccount"] = cmd_enableaccount()
+from samba.netcmd.newuser import cmd_newuser
+commands["newuser"] = cmd_newuser()
diff --git a/source4/scripting/python/samba/netcmd/domainlevel.py b/source4/scripting/python/samba/netcmd/domainlevel.py
new file mode 100644
index 0000000000..1c12bde0c6
--- /dev/null
+++ b/source4/scripting/python/samba/netcmd/domainlevel.py
@@ -0,0 +1,229 @@
+#!/usr/bin/python
+#
+# Raises domain and forest function levels
+#
+# Copyright Matthias Dieter Wallnoefer 2009
+# Copyright Jelmer Vernooij 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/>.
+#
+
+# Notice: At the moment we have some more checks to do here on the special
+# attributes (consider attribute "msDS-Behavior-Version). This is due to the
+# fact that we on s4 LDB don't implement their change policy (only certain
+# values, only increments possible...) yet.
+
+import samba.getopt as options
+import ldb
+
+from samba.auth import system_session
+from samba.netcmd import (
+ Command,
+ CommandError,
+ Option,
+ )
+from samba.samdb import SamDB
+from samba import (
+ DS_DOMAIN_FUNCTION_2000,
+ DS_DOMAIN_FUNCTION_2003,
+ DS_DOMAIN_FUNCTION_2003_MIXED,
+ DS_DOMAIN_FUNCTION_2008,
+ DS_DOMAIN_FUNCTION_2008_R2,
+ DS_DC_FUNCTION_2000,
+ DS_DC_FUNCTION_2003,
+ DS_DC_FUNCTION_2008,
+ DS_DC_FUNCTION_2008_R2,
+ )
+
+class cmd_domainlevel(Command):
+ """Raises domain and forest function levels."""
+
+ synopsis = "(show | raise <options>)"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "credopts": options.CredentialsOptions,
+ "versionopts": options.VersionOptions,
+ }
+
+ takes_options = [
+ Option("-H", help="LDB URL for database or target server", type=str),
+ Option("--quiet", help="Be quiet", action="store_true"),
+ Option("--forest", type="choice", choices=["2003", "2008", "2008_R2"],
+ help="The forest function level (2003 | 2008 | 2008_R2)"),
+ Option("--domain", type="choice", choices=["2003", "2008", "2008_R2"],
+ help="The domain function level (2003 | 2008 | 2008_R2)"),
+ ]
+
+ takes_args = ["subcommand"]
+
+ def run(self, subcommand, H=None, forest=None, domain=None, quiet=False,
+ credopts=None, sambaopts=None, versionopts=None):
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp)
+
+ if H is not None:
+ url = H
+ else:
+ url = lp.get("sam database")
+
+ samdb = SamDB(url=url, session_info=system_session(),
+ credentials=creds, lp=lp)
+
+ domain_dn = SamDB.domain_dn(samdb)
+
+ res_forest = samdb.search("CN=Partitions,CN=Configuration," + domain_dn,
+ scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
+ assert(len(res_forest) == 1)
+
+ res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
+ attrs=["msDS-Behavior-Version", "nTMixedDomain"])
+ assert(len(res_domain) == 1)
+
+ res_dc_s = samdb.search("CN=Sites,CN=Configuration," + domain_dn,
+ scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
+ attrs=["msDS-Behavior-Version"])
+ assert(len(res_dc_s) >= 1)
+
+ try:
+ level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
+ level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
+ level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
+
+ min_level_dc = int(res_dc_s[0]["msDS-Behavior-Version"][0]) # Init value
+ for msg in res_dc_s:
+ if int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
+ min_level_dc = int(msg["msDS-Behavior-Version"][0])
+
+ if level_forest < 0 or level_domain < 0:
+ raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
+ if min_level_dc < 0:
+ raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
+ if level_forest > level_domain:
+ raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
+ if level_domain > min_level_dc:
+ raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")
+
+ except KeyError:
+ raise CommandError("Could not retrieve the actual domain, forest level and/or lowest DC function level!")
+
+ if subcommand == "show":
+ self.message("Domain and forest function level for domain '" + domain_dn + "'")
+ if level_forest < DS_DOMAIN_FUNCTION_2003:
+ self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2003 (Native). This isn't supported! Please raise!")
+ if level_domain < DS_DOMAIN_FUNCTION_2003:
+ self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2003 (Native). This isn't supported! Please raise!")
+ if min_level_dc < DS_DC_FUNCTION_2003:
+ self.message("\nATTENTION: You run SAMBA 4 on a lowest function level of a DC lower than Windows 2003. This isn't supported! Please step-up or upgrade the concerning DC(s)!")
+
+ self.message("")
+
+ if level_forest == DS_DOMAIN_FUNCTION_2000:
+ outstr = "2000"
+ elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
+ outstr = "2003 with mixed domains/interim (NT4 DC support)"
+ elif level_forest == DS_DOMAIN_FUNCTION_2003:
+ outstr = "2003"
+ elif level_forest == DS_DOMAIN_FUNCTION_2008:
+ outstr = "2008"
+ elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
+ outstr = "2008 R2"
+ else:
+ outstr = "higher than 2008 R2"
+ self.message("Forest function level: (Windows) " + outstr)
+
+ if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
+ outstr = "2000 mixed (NT4 DC support)"
+ elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
+ outstr = "2000"
+ elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
+ outstr = "2003 with mixed domains/interim (NT4 DC support)"
+ elif level_domain == DS_DOMAIN_FUNCTION_2003:
+ outstr = "2003"
+ elif level_domain == DS_DOMAIN_FUNCTION_2008:
+ outstr = "2008"
+ elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
+ outstr = "2008 R2"
+ else:
+ outstr = "higher than 2008 R2"
+ self.message("Domain function level: (Windows) " + outstr)
+
+ if min_level_dc == DS_DC_FUNCTION_2000:
+ outstr = "2000"
+ elif min_level_dc == DS_DC_FUNCTION_2003:
+ outstr = "2003"
+ elif min_level_dc == DS_DC_FUNCTION_2008:
+ outstr = "2008"
+ elif min_level_dc == DS_DC_FUNCTION_2008_R2:
+ outstr = "2008 R2"
+ else:
+ outstr = "higher than 2008 R2"
+ self.message("Lowest function level of a DC: (Windows) " + outstr)
+
+ elif subcommand == "raise":
+ msgs = []
+
+ if domain is not None:
+ if domain == "2003":
+ new_level_domain = DS_DOMAIN_FUNCTION_2003
+ elif domain == "2008":
+ new_level_domain = DS_DOMAIN_FUNCTION_2008
+ elif domain == "2008_R2":
+ new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
+
+ if new_level_domain <= level_domain and level_domain_mixed == 0:
+ raise CommandError("Domain function level can't be smaller equal to the actual one!")
+
+ if new_level_domain > min_level_dc:
+ raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")
+
+ # Deactivate mixed/interim domain support
+ if level_domain_mixed != 0:
+ m = ldb.Message()
+ m.dn = ldb.Dn(samdb, domain_dn)
+ m["nTMixedDomain"] = ldb.MessageElement("0",
+ ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
+ samdb.modify(m)
+ m = ldb.Message()
+ m.dn = ldb.Dn(samdb, domain_dn)
+ m["msDS-Behavior-Version"]= ldb.MessageElement(
+ str(new_level_domain), ldb.FLAG_MOD_REPLACE,
+ "msDS-Behavior-Version")
+ samdb.modify(m)
+ level_domain = new_level_domain
+ msgs.append("Domain function level changed!")
+
+ if forest is not None:
+ if forest == "2003":
+ new_level_forest = DS_DOMAIN_FUNCTION_2003
+ elif forest == "2008":
+ new_level_forest = DS_DOMAIN_FUNCTION_2008
+ elif forest == "2008_R2":
+ new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
+ if new_level_forest <= level_forest:
+ raise CommandError("Forest function level can't be smaller equal to the actual one!")
+ if new_level_forest > level_domain:
+ raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!")
+ m = ldb.Message()
+ m.dn = ldb.Dn(samdb, "CN=Partitions,CN=Configuration,"
+ + domain_dn)
+ m["msDS-Behavior-Version"]= ldb.MessageElement(
+ str(new_level_forest), ldb.FLAG_MOD_REPLACE,
+ "msDS-Behavior-Version")
+ samdb.modify(m)
+ msgs.append("Forest function level changed!")
+ msgs.append("All changes applied successfully!")
+ self.message("\n".join(msgs))
+ else:
+ raise CommandError("Wrong argument '%s'!" % subcommand)
diff --git a/source4/scripting/python/samba/netcmd/enableaccount.py b/source4/scripting/python/samba/netcmd/enableaccount.py
new file mode 100755
index 0000000000..d4af0a84f1
--- /dev/null
+++ b/source4/scripting/python/samba/netcmd/enableaccount.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+#
+# Enables an user account on a Samba4 server
+# Copyright Jelmer Vernooij 2008
+#
+# Based on the original in EJS:
+# Copyright Andrew Tridgell 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/>.
+#
+
+import samba.getopt as options
+
+from samba.auth import system_session
+from samba.netcmd import Command, CommandError, Option
+from samba.samdb import SamDB
+
+class cmd_enableaccount(Command):
+ """Enable an account."""
+
+ synopsis = "enableaccount [username] [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "versionopts": options.VersionOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", help="LDB URL for database or target server", type=str),
+ Option("--filter", help="LDAP Filter to set password on", type=str),
+ ]
+
+ takes_args = ["username?"]
+
+ def run(self, username=None, sambaopts=None, credopts=None,
+ versionopts=None, filter=None, H=None):
+ if username is None and filter is None:
+ raise CommandError("Either the username or '--filter' must be specified!")
+
+ if filter is None:
+ filter = "(&(objectClass=user)(sAMAccountName=%s))" % (username)
+
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp)
+
+ if H is not None:
+ url = H
+ else:
+ url = lp.get("sam database")
+
+ samdb = SamDB(url=url, session_info=system_session(),
+ credentials=creds, lp=lp)
+ samdb.enable_account(filter)
diff --git a/source4/scripting/python/samba/netcmd/newuser.py b/source4/scripting/python/samba/netcmd/newuser.py
new file mode 100755
index 0000000000..651313a345
--- /dev/null
+++ b/source4/scripting/python/samba/netcmd/newuser.py
@@ -0,0 +1,70 @@
+#!/usr/bin/python
+#
+# Adds a new user to a Samba4 server
+# Copyright Jelmer Vernooij 2008
+#
+# Based on the original in EJS:
+# Copyright Andrew Tridgell 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/>.
+
+import samba.getopt as options
+from samba.netcmd import Command, CommandError, Option
+
+from getpass import getpass
+from samba.auth import system_session
+from samba.samdb import SamDB
+
+class cmd_newuser(Command):
+ """Create a new user."""
+
+ synopsis = "newuser [options] <username> [<password>]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "versionopts": options.VersionOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", help="LDB URL for database or target server", type=str),
+ Option("--unixname", help="Unix Username", type=str),
+ Option("--must-change-at-next-login",
+ help="Force password to be changed on next login",
+ action="store_true"),
+ ]
+
+ takes_args = ["username", "password?"]
+
+ def run(self, username, password=None, credopts=None, sambaopts=None,
+ versionopts=None, H=None, unixname=None,
+ must_change_at_next_login=None):
+ if password is None:
+ password = getpass("New Password: ")
+
+ if unixname is None:
+ unixname = username
+
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp)
+
+ if H is not None:
+ url = H
+ else:
+ url = lp.get("sam database")
+
+ samdb = SamDB(url=url, session_info=system_session(), credentials=creds,
+ lp=lp)
+ samdb.newuser(username, unixname, password,
+ force_password_change_at_next_login_req=must_change_at_next_login)
diff --git a/source4/scripting/python/samba/netcmd/pwsettings.py b/source4/scripting/python/samba/netcmd/pwsettings.py
new file mode 100644
index 0000000000..30f6f20d68
--- /dev/null
+++ b/source4/scripting/python/samba/netcmd/pwsettings.py
@@ -0,0 +1,190 @@
+#!/usr/bin/python
+#
+# Sets password settings.
+# (Password complexity, history length, minimum password length, the minimum
+# and maximum password age) on a Samba4 server
+#
+# Copyright Matthias Dieter Wallnoefer 2009
+# Copyright Andrew Kroeger 2009
+# Copyright Jelmer Vernooij 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/>.
+#
+
+import sys
+
+import samba.getopt as options
+import optparse
+import ldb
+
+from samba.auth import system_session
+from samba.samdb import SamDB
+from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX
+from samba.netcmd import Command, CommandError, Option
+
+class cmd_pwsettings(Command):
+ """Sets password settings.
+
+ Password complexity, history length, minimum password length, the minimum
+ and maximum password age) on a Samba4 server.
+ """
+
+ synopsis = "(show | set <options>)"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "versionopts": options.VersionOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", help="LDB URL for database or target server", type=str),
+ Option("--quiet", help="Be quiet", action="store_true"),
+ Option("--complexity", type="choice", choices=["on","off","default"],
+ help="The password complexity (on | off | default). Default is 'on'"),
+ Option("--history-length",
+ help="The password history length (<integer> | default). Default is 24.", type=str),
+ Option("--min-pwd-length",
+ help="The minimum password length (<integer> | default). Default is 7.", type=str),
+ Option("--min-pwd-age",
+ help="The minimum password age (<integer in days> | default). Default is 0.", type=str),
+ Option("--max-pwd-age",
+ help="The maximum password age (<integer in days> | default). Default is 43.", type=str),
+ ]
+
+ takes_args = ["subcommand"]
+
+ def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None,
+ quiet=False, complexity=None, history_length=None,
+ min_pwd_length=None, credopts=None, sambaopts=None,
+ versionopts=None):
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp)
+
+ if H is not None:
+ url = H
+ else:
+ url = lp.get("sam database")
+
+ samdb = SamDB(url=url, session_info=system_session(),
+ credentials=creds, lp=lp)
+
+ domain_dn = SamDB.domain_dn(samdb)
+ res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
+ attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength",
+ "minPwdAge", "maxPwdAge"])
+ assert(len(res) == 1)
+ try:
+ pwd_props = int(res[0]["pwdProperties"][0])
+ pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
+ cur_min_pwd_len = int(res[0]["minPwdLength"][0])
+ # ticks -> days
+ cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
+ cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
+ except KeyError:
+ raise CommandError("Could not retrieve password properties!")
+
+ if subcommand == "show":
+ self.message("Password informations for domain '%s'" % domain_dn)
+ self.message("")
+ if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
+ self.message("Password complexity: on")
+ else:
+ self.message("Password complexity: off")
+ self.message("Password history length: %d" % pwd_hist_len)
+ self.message("Minimum password length: %d" % cur_min_pwd_len)
+ self.message("Minimum password age (days): %d" % cur_min_pwd_age)
+ self.message("Maximum password age (days): %d" % cur_max_pwd_age)
+ elif subcommand == "set":
+ msgs = []
+ m = ldb.Message()
+ m.dn = ldb.Dn(samdb, domain_dn)
+
+ if complexity is not None:
+ if complexity == "on" or complexity == "default":
+ pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
+ msgs.append("Password complexity activated!")
+ elif complexity == "off":
+ pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
+ msgs.append("Password complexity deactivated!")
+
+ m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
+ ldb.FLAG_MOD_REPLACE, "pwdProperties")
+
+ if history_length is not None:
+ if history_length == "default":
+ pwd_hist_len = 24
+ else:
+ pwd_hist_len = int(history_length)
+
+ if pwd_hist_len < 0 or pwd_hist_len > 24:
+ raise CommandError("Password history length must be in the range of 0 to 24!")
+
+ m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
+ ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
+ msgs.append("Password history length changed!")
+
+ if min_pwd_length is not None:
+ if min_pwd_length == "default":
+ min_pwd_len = 7
+ else:
+ min_pwd_len = int(min_pwd_length)
+
+ if min_pwd_len < 0 or min_pwd_len > 14:
+ raise CommandError("Minimum password length must be in the range of 0 to 14!")
+
+ m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
+ ldb.FLAG_MOD_REPLACE, "minPwdLength")
+ msgs.append("Minimum password length changed!")
+
+ if min_pwd_age is not None:
+ if min_pwd_age == "default":
+ min_pwd_age = 0
+ else:
+ min_pwd_age = int(min_pwd_age)
+
+ if min_pwd_age < 0 or min_pwd_age > 998:
+ raise CommandError("Minimum password age must be in the range of 0 to 998!")
+
+ # days -> ticks
+ min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
+
+ m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
+ ldb.FLAG_MOD_REPLACE, "minPwdAge")
+ msgs.append("Minimum password age changed!")
+
+ if max_pwd_age is not None:
+ if max_pwd_age == "default":
+ max_pwd_age = 43
+ else:
+ max_pwd_age = int(max_pwd_age)
+
+ if max_pwd_age < 0 or max_pwd_age > 999:
+ raise CommandError("Maximum password age must be in the range of 0 to 999!")
+
+ # days -> ticks
+ max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
+
+ m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
+ ldb.FLAG_MOD_REPLACE, "maxPwdAge")
+ msgs.append("Maximum password age changed!")
+
+ if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
+ raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age))
+
+ samdb.modify(m)
+ msgs.append("All changes applied successfully!")
+ self.message("\n".join(msgs))
+ else:
+ raise CommandError("Wrong argument '%s'!" % subcommand)
diff --git a/source4/scripting/python/samba/netcmd/setexpiry.py b/source4/scripting/python/samba/netcmd/setexpiry.py
new file mode 100644
index 0000000000..0c5dc5afff
--- /dev/null
+++ b/source4/scripting/python/samba/netcmd/setexpiry.py
@@ -0,0 +1,72 @@
+#!/usr/bin/python
+#
+# Sets the user password expiry on a Samba4 server
+# Copyright Jelmer Vernooij 2008
+#
+# Based on the original in EJS:
+# Copyright Andrew Tridgell 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/>.
+#
+
+from samba.netcmd import Command, CommandError, Option
+
+import samba.getopt as options
+
+from samba.auth import system_session
+from samba.samdb import SamDB
+
+class cmd_setexpiry(Command):
+ """Set the expiration of a user account."""
+
+ synopsis = "setexpiry [username] [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "versionopts": options.VersionOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", help="LDB URL for database or target server", type=str),
+ Option("--filter", help="LDAP Filter to set password on", type=str),
+ Option("--days", help="Days to expiry", type=int),
+ Option("--noexpiry", help="Password does never expire", action="store_true"),
+ ]
+
+ takes_args = ["username?"]
+
+ def run(self, username=None, sambaopts=None, credopts=None,
+ versionopts=None, H=None, filter=None, days=None, noexpiry=None):
+ if username is None and filter is None:
+ raise CommandError("Either the username or '--filter' must be specified!")
+
+ if filter is None:
+ filter = "(&(objectClass=user)(sAMAccountName=%s))" % (username)
+
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp)
+
+ if days is None:
+ days = 0
+
+ if H is not None:
+ url = H
+ else:
+ url = lp.get("sam database")
+
+ samdb = SamDB(url=url, session_info=system_session(),
+ credentials=creds, lp=lp)
+
+ samdb.setexpiry(filter, days*24*3600, no_expiry_req=noexpiry)
diff --git a/source4/scripting/python/samba/netcmd/setpassword.py b/source4/scripting/python/samba/netcmd/setpassword.py
new file mode 100644
index 0000000000..6393e47414
--- /dev/null
+++ b/source4/scripting/python/samba/netcmd/setpassword.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python
+#
+# Sets a user password on a Samba4 server
+# Copyright Jelmer Vernooij 2008
+#
+# Based on the original in EJS:
+# Copyright Andrew Tridgell 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/>.
+#
+
+import samba.getopt as options
+from samba.netcmd import Command, CommandError, Option
+
+from getpass import getpass
+from samba.auth import system_session
+from samba.samdb import SamDB
+
+class cmd_setpassword(Command):
+ """Change the password of a user."""
+
+ synopsis = "setpassword [username] [options]"
+
+ takes_optiongroups = {
+ "sambaopts": options.SambaOptions,
+ "versionopts": options.VersionOptions,
+ "credopts": options.CredentialsOptions,
+ }
+
+ takes_options = [
+ Option("-H", help="LDB URL for database or target server", type=str),
+ Option("--filter", help="LDAP Filter to set password on", type=str),
+ Option("--newpassword", help="Set password", type=str),
+ Option("--must-change-at-next-login",
+ help="Force password to be changed on next login",
+ action="store_true"),
+ ]
+
+ takes_args = ["username?"]
+
+ def run(self, username=None, filter=None, credopts=None, sambaopts=None,
+ versionopts=None, H=None, newpassword=None,
+ must_change_at_next_login=None):
+ if filter is None and username is None:
+ raise CommandError("Either the username or '--filter' must be specified!")
+
+ password = newpassword
+ if password is None:
+ password = getpass("New Password: ")
+
+ if filter is None:
+ filter = "(&(objectClass=user)(sAMAccountName=%s))" % (username)
+
+ lp = sambaopts.get_loadparm()
+ creds = credopts.get_credentials(lp)
+
+ if H is not None:
+ url = H
+ else:
+ url = lp.get("sam database")
+
+ samdb = SamDB(url=url, session_info=system_session(),
+ credentials=creds, lp=lp)
+
+ samdb.setpassword(filter, password,
+ force_password_change_at_next_login_req=must_change_at_next_login)
diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py
index 3e4e90a746..d7d0a790ca 100644
--- a/source4/scripting/python/samba/provision.py
+++ b/source4/scripting/python/samba/provision.py
@@ -894,6 +894,8 @@ def setup_samdb(path, setup_path, session_info, provision_backend, lp,
samdb.set_domain_sid(str(domainsid))
if serverrole == "domain controller":
samdb.set_invocation_id(invocationid)
+ # NOTE: the invocationid for standalone and member server
+ # cases is setup in the sambd_dsdb module init function
message("Adding DomainDN: %s" % names.domaindn)
diff --git a/source4/scripting/python/samba/tests/netcmd.py b/source4/scripting/python/samba/tests/netcmd.py
new file mode 100644
index 0000000000..ecd8dc439e
--- /dev/null
+++ b/source4/scripting/python/samba/tests/netcmd.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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/>.
+#
+
+import unittest
+
+from samba.netcmd import Command
+
+class CommandTests(unittest.TestCase):
+
+ def test_name(self):
+ class cmd_foo(Command):
+ pass
+ self.assertEquals("foo", cmd_foo().name)
+
+ def test_description(self):
+ class cmd_foo(Command):
+ """Mydescription"""
+ self.assertEquals("Mydescription", cmd_foo().description)
diff --git a/source4/selftest/tests.sh b/source4/selftest/tests.sh
index 73209efefe..cad97b7ad3 100755
--- a/source4/selftest/tests.sh
+++ b/source4/selftest/tests.sh
@@ -450,6 +450,7 @@ plantest "samba.python" none $SUBUNITRUN samba.tests
plantest "provision.python" none $SUBUNITRUN samba.tests.provision
plantest "samba3.python" none $SUBUNITRUN samba.tests.samba3
plantest "samr.python" dc:local $SUBUNITRUN samba.tests.dcerpc.sam
+plantest "netcmd.python" none $SUBUNITRUN samba.tests.netcmd
plantest "dcerpc.bare.python" dc:local $SUBUNITRUN samba.tests.dcerpc.bare
plantest "unixinfo.python" dc:local $SUBUNITRUN samba.tests.dcerpc.unix
plantest "samdb.python" none $SUBUNITRUN samba.tests.samdb
@@ -460,6 +461,7 @@ plantest "subunit.python" none $SUBUNITRUN subunit
plantest "rpcecho.python" dc:local $SUBUNITRUN samba.tests.dcerpc.rpcecho
plantest "winreg.python" dc:local $SUBUNITRUN -U\$USERNAME%\$PASSWORD samba.tests.dcerpc.registry
plantest "ldap.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python" $PYTHON $samba4srcdir/lib/ldb/tests/python/ldap.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
+plantest "ldap_schema.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python" $PYTHON $samba4srcdir/lib/ldb/tests/python/ldap_schema.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
plantest "ldap.possibleInferiors.python" dc $PYTHON $samba4srcdir/dsdb/samdb/ldb_modules/tests/possibleinferiors.py $CONFIGURATION ldap://\$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
plantest "ldap.secdesc.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python" $PYTHON $samba4srcdir/lib/ldb/tests/python/sec_descriptor.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
plantest "ldap.acl.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python" $PYTHON $samba4srcdir/lib/ldb/tests/python/acl.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
diff --git a/source4/setup/domainlevel b/source4/setup/domainlevel
deleted file mode 100755
index c37d811dd8..0000000000
--- a/source4/setup/domainlevel
+++ /dev/null
@@ -1,250 +0,0 @@
-#!/usr/bin/python
-#
-# Raises domain and forest function levels
-#
-# Copyright Matthias Dieter Wallnoefer 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/>.
-#
-
-# Notice: At the moment we have some more checks to do here on the special
-# attributes (consider attribute "msDS-Behavior-Version). This is due to the
-# fact that we on s4 LDB don't implement their change policy (only certain
-# values, only increments possible...) yet.
-
-import sys
-
-# Find right directory when running from source tree
-sys.path.insert(0, "bin/python")
-
-import samba.getopt as options
-import optparse
-import ldb
-
-from samba.auth import system_session
-from samba.samdb import SamDB
-from samba import DS_DOMAIN_FUNCTION_2000, DS_DOMAIN_FUNCTION_2003
-from samba import DS_DOMAIN_FUNCTION_2003_MIXED, DS_DOMAIN_FUNCTION_2008
-from samba import DS_DOMAIN_FUNCTION_2008_R2
-from samba import DS_DC_FUNCTION_2000, DS_DC_FUNCTION_2003, DS_DC_FUNCTION_2008
-from samba import DS_DC_FUNCTION_2008_R2
-
-parser = optparse.OptionParser("domainlevel (show | raise <options>)")
-sambaopts = options.SambaOptions(parser)
-parser.add_option_group(sambaopts)
-parser.add_option_group(options.VersionOptions(parser))
-credopts = options.CredentialsOptions(parser)
-parser.add_option_group(credopts)
-parser.add_option("-H", help="LDB URL for database or target server", type=str)
-parser.add_option("--quiet", help="Be quiet", action="store_true")
-parser.add_option("--forest", type="choice",
- choices=["2003", "2008", "2008_R2"],
- help="The forest function level (2003 | 2008 | 2008_R2)")
-parser.add_option("--domain", type="choice",
- choices=["2003", "2008", "2008_R2"],
- help="The domain function level (2003 | 2008 | 2008_R2)")
-opts, args = parser.parse_args()
-
-#
-# print a message if quiet is not set
-#
-def message(text):
- if not opts.quiet:
- print text
-
-if len(args) == 0:
- parser.print_usage()
- sys.exit(1)
-
-lp = sambaopts.get_loadparm()
-creds = credopts.get_credentials(lp)
-
-if opts.H is not None:
- url = opts.H
-else:
- url = lp.get("sam database")
-
-samdb = SamDB(url=url, session_info=system_session(), credentials=creds, lp=lp)
-
-domain_dn = SamDB.domain_dn(samdb)
-
-res_forest = samdb.search("CN=Partitions,CN=Configuration," + domain_dn,
- scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
-assert(len(res_forest) == 1)
-
-res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
- attrs=["msDS-Behavior-Version", "nTMixedDomain"])
-assert(len(res_domain) == 1)
-
-res_dc_s = samdb.search("CN=Sites,CN=Configuration," + domain_dn,
- scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
- attrs=["msDS-Behavior-Version"])
-assert(len(res_dc_s) >= 1)
-
-try:
- level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
- level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
- level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])
-
- min_level_dc = int(res_dc_s[0]["msDS-Behavior-Version"][0]) # Init value
- for msg in res_dc_s:
- if int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
- min_level_dc = int(msg["msDS-Behavior-Version"][0])
-
- if level_forest < 0 or level_domain < 0:
- print >>sys.stderr, "ERROR: Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!"
- sys.exit(1)
- if min_level_dc < 0:
- print >>sys.stderr, "ERROR: Lowest function level of a DC is invalid. Correct this or reprovision!"
- sys.exit(1)
- if level_forest > level_domain:
- print >>sys.stderr, "ERROR: Forest function level is higher than the domain level(s). Correct this or reprovision!"
- sys.exit(1)
- if level_domain > min_level_dc:
- print >>sys.stderr, "ERROR: Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!"
- sys.exit(1)
-
-except KeyError:
- print >>sys.stderr, "ERROR: Could not retrieve the actual domain, forest level and/or lowest DC function level!"
- if args[0] == "show":
- print >>sys.stderr, "So the levels can't be displayed!"
- sys.exit(1)
-
-if args[0] == "show":
- message("Domain and forest function level for domain '" + domain_dn + "'")
- if level_forest < DS_DOMAIN_FUNCTION_2003:
- message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2003 (Native). This isn't supported! Please raise!")
- if level_domain < DS_DOMAIN_FUNCTION_2003:
- message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2003 (Native). This isn't supported! Please raise!")
- if min_level_dc < DS_DC_FUNCTION_2003:
- message("\nATTENTION: You run SAMBA 4 on a lowest function level of a DC lower than Windows 2003. This isn't supported! Please step-up or upgrade the concerning DC(s)!")
-
- message("")
-
- if level_forest == DS_DOMAIN_FUNCTION_2000:
- outstr = "2000"
- elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
- outstr = "2003 with mixed domains/interim (NT4 DC support)"
- elif level_forest == DS_DOMAIN_FUNCTION_2003:
- outstr = "2003"
- elif level_forest == DS_DOMAIN_FUNCTION_2008:
- outstr = "2008"
- elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
- outstr = "2008 R2"
- else:
- outstr = "higher than 2008 R2"
- message("Forest function level: (Windows) " + outstr)
-
- if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
- outstr = "2000 mixed (NT4 DC support)"
- elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
- outstr = "2000"
- elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
- outstr = "2003 with mixed domains/interim (NT4 DC support)"
- elif level_domain == DS_DOMAIN_FUNCTION_2003:
- outstr = "2003"
- elif level_domain == DS_DOMAIN_FUNCTION_2008:
- outstr = "2008"
- elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
- outstr = "2008 R2"
- else:
- outstr = "higher than 2008 R2"
- message("Domain function level: (Windows) " + outstr)
-
- if min_level_dc == DS_DC_FUNCTION_2000:
- outstr = "2000"
- elif min_level_dc == DS_DC_FUNCTION_2003:
- outstr = "2003"
- elif min_level_dc == DS_DC_FUNCTION_2008:
- outstr = "2008"
- elif min_level_dc == DS_DC_FUNCTION_2008_R2:
- outstr = "2008 R2"
- else:
- outstr = "higher than 2008 R2"
- message("Lowest function level of a DC: (Windows) " + outstr)
-
-elif args[0] == "raise":
- msgs = []
-
- if opts.domain is not None:
- arg = opts.domain
-
- if arg == "2003":
- new_level_domain = DS_DOMAIN_FUNCTION_2003
- elif arg == "2008":
- new_level_domain = DS_DOMAIN_FUNCTION_2008
- elif arg == "2008_R2":
- new_level_domain = DS_DOMAIN_FUNCTION_2008_R2
-
- if new_level_domain <= level_domain and level_domain_mixed == 0:
- print >>sys.stderr, "ERROR: Domain function level can't be smaller equal to the actual one!"
- sys.exit(1)
-
- if new_level_domain > min_level_dc:
- print >>sys.stderr, "ERROR: Domain function level can't be higher than the lowest function level of a DC!"
- sys.exit(1)
-
- # Deactivate mixed/interim domain support
- if level_domain_mixed != 0:
- m = ldb.Message()
- m.dn = ldb.Dn(samdb, domain_dn)
- m["nTMixedDomain"] = ldb.MessageElement("0",
- ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
- samdb.modify(m)
-
- m = ldb.Message()
- m.dn = ldb.Dn(samdb, domain_dn)
- m["msDS-Behavior-Version"]= ldb.MessageElement(
- str(new_level_domain), ldb.FLAG_MOD_REPLACE,
- "msDS-Behavior-Version")
- samdb.modify(m)
-
- level_domain = new_level_domain
-
- msgs.append("Domain function level changed!")
-
- if opts.forest is not None:
- arg = opts.forest
-
- if arg == "2003":
- new_level_forest = DS_DOMAIN_FUNCTION_2003
- elif arg == "2008":
- new_level_forest = DS_DOMAIN_FUNCTION_2008
- elif arg == "2008_R2":
- new_level_forest = DS_DOMAIN_FUNCTION_2008_R2
-
- if new_level_forest <= level_forest:
- print >>sys.stderr, "ERROR: Forest function level can't be smaller equal to the actual one!"
- sys.exit(1)
-
- if new_level_forest > level_domain:
- print >>sys.stderr, "ERROR: Forest function level can't be higher than the domain function level(s). Please raise it/them first!"
- sys.exit(1)
-
- m = ldb.Message()
- m.dn = ldb.Dn(samdb, "CN=Partitions,CN=Configuration,"
- + domain_dn)
- m["msDS-Behavior-Version"]= ldb.MessageElement(
- str(new_level_forest), ldb.FLAG_MOD_REPLACE,
- "msDS-Behavior-Version")
- samdb.modify(m)
-
- msgs.append("Forest function level changed!")
-
- msgs.append("All changes applied successfully!")
-
- message("\n".join(msgs))
-else:
- print >>sys.stderr, "ERROR: Wrong argument '" + args[0] + "'!"
- sys.exit(1)
diff --git a/source4/setup/enableaccount b/source4/setup/enableaccount
deleted file mode 100755
index f8f727c1ee..0000000000
--- a/source4/setup/enableaccount
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/python
-#
-# Enables an user account on a Samba4 server
-# Copyright Jelmer Vernooij 2008
-#
-# Based on the original in EJS:
-# Copyright Andrew Tridgell 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/>.
-#
-
-import sys
-
-sys.path.insert(0, "bin/python")
-
-import samba.getopt as options
-import optparse
-
-from samba.auth import system_session
-from samba.samdb import SamDB
-
-parser = optparse.OptionParser("enableaccount [username] [options]")
-sambaopts = options.SambaOptions(parser)
-parser.add_option_group(sambaopts)
-parser.add_option_group(options.VersionOptions(parser))
-credopts = options.CredentialsOptions(parser)
-parser.add_option_group(credopts)
-parser.add_option("-H", help="LDB URL for database or target server", type=str)
-parser.add_option("--filter", help="LDAP Filter to set password on", type=str)
-
-opts, args = parser.parse_args()
-
-filter = opts.filter
-
-if (len(args) == 0) and (filter is None):
- print "Either the username or '--filter' must be specified!"
- parser.print_usage()
- sys.exit(1)
-
-if filter is None:
- username = args[0]
- filter = "(&(objectClass=user)(sAMAccountName=%s))" % (username)
-
-lp = sambaopts.get_loadparm()
-creds = credopts.get_credentials(lp)
-
-if opts.H is not None:
- url = opts.H
-else:
- url = lp.get("sam database")
-
-samdb = SamDB(url=url, session_info=system_session(), credentials=creds, lp=lp)
-
-samdb.enable_account(filter)
diff --git a/source4/setup/newuser b/source4/setup/newuser
deleted file mode 100755
index ef65d36dfb..0000000000
--- a/source4/setup/newuser
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/python
-#
-# Adds a new user to a Samba4 server
-# Copyright Jelmer Vernooij 2008
-#
-# Based on the original in EJS:
-# Copyright Andrew Tridgell 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/>.
-
-import sys
-
-# Find right directory when running from source tree
-sys.path.insert(0, "bin/python")
-
-import samba.getopt as options
-import optparse
-
-from getpass import getpass
-from samba.auth import system_session
-from samba.samdb import SamDB
-
-parser = optparse.OptionParser("newuser [options] <username> [<password>]")
-sambaopts = options.SambaOptions(parser)
-parser.add_option_group(sambaopts)
-parser.add_option_group(options.VersionOptions(parser))
-credopts = options.CredentialsOptions(parser)
-parser.add_option_group(credopts)
-parser.add_option("-H", help="LDB URL for database or target server", type=str)
-parser.add_option("--unixname", help="Unix Username", type=str)
-parser.add_option("--must-change-at-next-login", help="Force password to be changed on next login", action="store_true")
-
-opts, args = parser.parse_args()
-
-if len(args) == 0:
- parser.print_usage()
- sys.exit(1)
-
-username = args[0]
-if len(args) > 1:
- password = args[1]
-else:
- password = getpass("New Password: ")
-
-if opts.unixname is None:
- opts.unixname = username
-
-lp = sambaopts.get_loadparm()
-creds = credopts.get_credentials(lp)
-
-if opts.H is not None:
- url = opts.H
-else:
- url = lp.get("sam database")
-
-samdb = SamDB(url=url, session_info=system_session(), credentials=creds, lp=lp)
-
-samdb.newuser(username, opts.unixname, password, force_password_change_at_next_login_req=opts.must_change_at_next_login)
diff --git a/source4/setup/pwsettings b/source4/setup/pwsettings
deleted file mode 100755
index 59ed5d29bf..0000000000
--- a/source4/setup/pwsettings
+++ /dev/null
@@ -1,198 +0,0 @@
-#!/usr/bin/python
-#
-# Sets password settings (Password complexity, history length, minimum password
-# length, the minimum and maximum password age) on a Samba4 server
-#
-# Copyright Matthias Dieter Wallnoefer 2009
-# Copyright Andrew Kroeger 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/>.
-#
-
-import sys
-
-# Find right directory when running from source tree
-sys.path.insert(0, "bin/python")
-
-import samba.getopt as options
-import optparse
-import ldb
-
-from samba.auth import system_session
-from samba.samdb import SamDB
-from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX
-
-parser = optparse.OptionParser("pwsettings (show | set <options>)")
-sambaopts = options.SambaOptions(parser)
-parser.add_option_group(sambaopts)
-parser.add_option_group(options.VersionOptions(parser))
-credopts = options.CredentialsOptions(parser)
-parser.add_option_group(credopts)
-parser.add_option("-H", help="LDB URL for database or target server", type=str)
-parser.add_option("--quiet", help="Be quiet", action="store_true")
-parser.add_option("--complexity", type="choice", choices=["on","off","default"],
- help="The password complexity (on | off | default). Default is 'on'")
-parser.add_option("--history-length",
- help="The password history length (<integer> | default). Default is 24.", type=str)
-parser.add_option("--min-pwd-length",
- help="The minimum password length (<integer> | default). Default is 7.", type=str)
-parser.add_option("--min-pwd-age",
- help="The minimum password age (<integer in days> | default). Default is 0.", type=str)
-parser.add_option("--max-pwd-age",
- help="The maximum password age (<integer in days> | default). Default is 43.", type=str)
-
-opts, args = parser.parse_args()
-
-#
-# print a message if quiet is not set
-#
-def message(text):
- if not opts.quiet:
- print text
-
-if len(args) == 0:
- parser.print_usage()
- sys.exit(1)
-
-lp = sambaopts.get_loadparm()
-creds = credopts.get_credentials(lp)
-
-if opts.H is not None:
- url = opts.H
-else:
- url = lp.get("sam database")
-
-samdb = SamDB(url=url, session_info=system_session(), credentials=creds, lp=lp)
-
-domain_dn = SamDB.domain_dn(samdb)
-res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
- attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength", "minPwdAge",
- "maxPwdAge"])
-assert(len(res) == 1)
-try:
- pwd_props = int(res[0]["pwdProperties"][0])
- pwd_hist_len = int(res[0]["pwdHistoryLength"][0])
- min_pwd_len = int(res[0]["minPwdLength"][0])
- # ticks -> days
- min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24))
- max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24))
-except KeyError:
- print >>sys.stderr, "ERROR: Could not retrieve password properties!"
- if args[0] == "show":
- print >>sys.stderr, "So no settings can be displayed!"
- sys.exit(1)
-
-if args[0] == "show":
- message("Password informations for domain '" + domain_dn + "'")
- message("")
- if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0:
- message("Password complexity: on")
- else:
- message("Password complexity: off")
- message("Password history length: " + str(pwd_hist_len))
- message("Minimum password length: " + str(min_pwd_len))
- message("Minimum password age (days): " + str(min_pwd_age))
- message("Maximum password age (days): " + str(max_pwd_age))
-
-elif args[0] == "set":
-
- msgs = []
- m = ldb.Message()
- m.dn = ldb.Dn(samdb, domain_dn)
-
- if opts.complexity is not None:
- if opts.complexity == "on" or opts.complexity == "default":
- pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX
- msgs.append("Password complexity activated!")
- elif opts.complexity == "off":
- pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX)
- msgs.append("Password complexity deactivated!")
-
- m["pwdProperties"] = ldb.MessageElement(str(pwd_props),
- ldb.FLAG_MOD_REPLACE, "pwdProperties")
-
- if opts.history_length is not None:
- if opts.history_length == "default":
- pwd_hist_len = 24
- else:
- pwd_hist_len = int(opts.history_length)
-
- if pwd_hist_len < 0 or pwd_hist_len > 24:
- print >>sys.stderr, "ERROR: Password history length must be in the range of 0 to 24!"
- sys.exit(1)
-
- m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len),
- ldb.FLAG_MOD_REPLACE, "pwdHistoryLength")
- msgs.append("Password history length changed!")
-
- if opts.min_pwd_length is not None:
- if opts.min_pwd_length == "default":
- min_pwd_len = 7
- else:
- min_pwd_len = int(opts.min_pwd_length)
-
- if min_pwd_len < 0 or min_pwd_len > 14:
- print >>sys.stderr, "ERROR: Minimum password length must be in the range of 0 to 14!"
- sys.exit(1)
-
- m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len),
- ldb.FLAG_MOD_REPLACE, "minPwdLength")
- msgs.append("Minimum password length changed!")
-
- if opts.min_pwd_age is not None:
- if opts.min_pwd_age == "default":
- min_pwd_age = 0
- else:
- min_pwd_age = int(opts.min_pwd_age)
-
- if min_pwd_age < 0 or min_pwd_age > 998:
- print >>sys.stderr, "ERROR: Minimum password age must be in the range of 0 to 998!"
- sys.exit(1)
-
- # days -> ticks
- min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7))
-
- m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks),
- ldb.FLAG_MOD_REPLACE, "minPwdAge")
- msgs.append("Minimum password age changed!")
-
- if opts.max_pwd_age is not None:
- if opts.max_pwd_age == "default":
- max_pwd_age = 43
- else:
- max_pwd_age = int(opts.max_pwd_age)
-
- if max_pwd_age < 0 or max_pwd_age > 999:
- print >>sys.stderr, "ERROR: Maximum password age must be in the range of 0 to 999!"
- sys.exit(1)
-
- # days -> ticks
- max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7))
-
- m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks),
- ldb.FLAG_MOD_REPLACE, "maxPwdAge")
- msgs.append("Maximum password age changed!")
-
- if max_pwd_age > 0 and min_pwd_age >= max_pwd_age:
- print "ERROR: Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age)
- sys.exit(1)
-
- samdb.modify(m)
-
- msgs.append("All changes applied successfully!")
-
- message("\n".join(msgs))
-else:
- print >>sys.stderr, "ERROR: Wrong argument '" + args[0] + "'!"
- sys.exit(1)
diff --git a/source4/setup/setexpiry b/source4/setup/setexpiry
deleted file mode 100755
index 2740326f2b..0000000000
--- a/source4/setup/setexpiry
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/python
-#
-# Sets the user password expiry on a Samba4 server
-# Copyright Jelmer Vernooij 2008
-#
-# Based on the original in EJS:
-# Copyright Andrew Tridgell 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/>.
-#
-
-import sys
-
-# Find right directory when running from source tree
-sys.path.insert(0, "bin/python")
-
-import samba.getopt as options
-import optparse
-
-from samba.auth import system_session
-from samba.samdb import SamDB
-
-parser = optparse.OptionParser("setexpiry [username] [options]")
-sambaopts = options.SambaOptions(parser)
-parser.add_option_group(sambaopts)
-parser.add_option_group(options.VersionOptions(parser))
-credopts = options.CredentialsOptions(parser)
-parser.add_option_group(credopts)
-parser.add_option("-H", help="LDB URL for database or target server", type=str)
-parser.add_option("--filter", help="LDAP Filter to set password on", type=str)
-parser.add_option("--days", help="Days to expiry", type=int)
-parser.add_option("--noexpiry", help="Password does never expire", action="store_true")
-
-opts, args = parser.parse_args()
-
-filter = opts.filter
-
-if (len(args) == 0) and (filter is None):
- print "Either the username or '--filter' must be specified!"
- parser.print_usage()
- sys.exit(1)
-
-days = opts.days
-if days is None:
- days = 0
-
-if filter is None:
- username = args[0]
- filter = "(&(objectClass=user)(sAMAccountName=%s))" % (username)
-
-lp = sambaopts.get_loadparm()
-creds = credopts.get_credentials(lp)
-
-if opts.H is not None:
- url = opts.H
-else:
- url = lp.get("sam database")
-
-samdb = SamDB(url=url, session_info=system_session(), credentials=creds, lp=lp)
-
-samdb.setexpiry(filter, days*24*3600, no_expiry_req=opts.noexpiry)
diff --git a/source4/setup/setpassword b/source4/setup/setpassword
deleted file mode 100755
index 57772be7a7..0000000000
--- a/source4/setup/setpassword
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/python
-#
-# Sets a user password on a Samba4 server
-# Copyright Jelmer Vernooij 2008
-#
-# Based on the original in EJS:
-# Copyright Andrew Tridgell 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/>.
-#
-
-import sys
-
-# Find right directory when running from source tree
-sys.path.insert(0, "bin/python")
-
-import samba.getopt as options
-import optparse
-
-from getpass import getpass
-from samba.auth import system_session
-from samba.samdb import SamDB
-
-parser = optparse.OptionParser("setpassword [username] [options]")
-sambaopts = options.SambaOptions(parser)
-parser.add_option_group(sambaopts)
-parser.add_option_group(options.VersionOptions(parser))
-credopts = options.CredentialsOptions(parser)
-parser.add_option_group(credopts)
-parser.add_option("-H", help="LDB URL for database or target server", type=str)
-parser.add_option("--filter", help="LDAP Filter to set password on", type=str)
-parser.add_option("--newpassword", help="Set password", type=str)
-parser.add_option("--must-change-at-next-login", help="Force password to be changed on next login", action="store_true")
-
-opts, args = parser.parse_args()
-
-filter = opts.filter
-
-if (len(args) == 0) and (filter is None):
- print "Either the username or '--filter' must be specified!"
- parser.print_usage()
- sys.exit(1)
-
-password = opts.newpassword;
-if password is None:
- password = getpass("New Password: ")
-
-if filter is None:
- username = args[0]
- filter = "(&(objectClass=user)(sAMAccountName=%s))" % (username)
-
-lp = sambaopts.get_loadparm()
-creds = credopts.get_credentials(lp)
-
-if opts.H is not None:
- url = opts.H
-else:
- url = lp.get("sam database")
-
-samdb = SamDB(url=url, session_info=system_session(), credentials=creds, lp=lp)
-
-samdb.setpassword(filter, password, force_password_change_at_next_login_req=opts.must_change_at_next_login)
-
diff --git a/source4/setup/tests/blackbox_newuser.sh b/source4/setup/tests/blackbox_newuser.sh
index d25c70669b..30e6830be5 100755
--- a/source4/setup/tests/blackbox_newuser.sh
+++ b/source4/setup/tests/blackbox_newuser.sh
@@ -14,19 +14,20 @@ shift 1
testit "simple-dc" $PYTHON ./setup/provision --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc
+net="./bin/net"
CONFIG="--configfile=$PREFIX/simple-dc/etc/smb.conf"
-testit "newuser" $PYTHON ./setup/newuser $CONFIG testuser testpass
+testit "newuser" $net newuser $CONFIG testuser testpass
# check the enable account script
-testit "enableaccount" $PYTHON ./setup/enableaccount $CONFIG testuser
+testit "enableaccount" $net enableaccount $CONFIG testuser
# check the enable account script
-testit "setpassword" $PYTHON ./setup/setpassword $CONFIG testuser --newpassword=testpass2
+testit "setpassword" $net setpassword $CONFIG testuser --newpassword=testpass2
# check the setexpiry script
-testit "noexpiry" $PYTHON ./setup/setexpiry $CONFIG testuser --noexpiry
-testit "expiry" $PYTHON ./setup/setexpiry $CONFIG testuser --days=7
+testit "noexpiry" $net setexpiry $CONFIG testuser --noexpiry
+testit "expiry" $net setexpiry $CONFIG testuser --days=7
exit $failed
diff --git a/source4/setup/tests/blackbox_setpassword.sh b/source4/setup/tests/blackbox_setpassword.sh
index 77b41a2424..9f8fa6d2c1 100755
--- a/source4/setup/tests/blackbox_setpassword.sh
+++ b/source4/setup/tests/blackbox_setpassword.sh
@@ -12,14 +12,16 @@ shift 1
. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+net="./bin/net"
+
testit "simple-dc" $PYTHON ./setup/provision --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc
-testit "newuser" $PYTHON ./setup/newuser --configfile=$PREFIX/simple-dc/etc/smb.conf testuser testpass
+testit "newuser" $net newuser --configfile=$PREFIX/simple-dc/etc/smb.conf testuser testpass
-testit "setpassword" $PYTHON ./setup/setpassword --configfile=$PREFIX/simple-dc/etc/smb.conf testuser --newpassword=testpass
+testit "setpassword" $net setpassword --configfile=$PREFIX/simple-dc/etc/smb.conf testuser --newpassword=testpass
-testit "setpassword" $PYTHON ./setup/setpassword --configfile=$PREFIX/simple-dc/etc/smb.conf testuser --newpassword=testpass --must-change-at-next-login
+testit "setpassword" $net setpassword --configfile=$PREFIX/simple-dc/etc/smb.conf testuser --newpassword=testpass --must-change-at-next-login
-testit "pwsettings" $PYTHON ./setup/pwsettings --quiet set --configfile=$PREFIX/simple-dc/etc/smb.conf --complexity=default --history-length=default --min-pwd-length=default --min-pwd-age=default --max-pwd-age=default
+testit "pwsettings" $net pwsettings --quiet set --configfile=$PREFIX/simple-dc/etc/smb.conf --complexity=default --history-length=default --min-pwd-length=default --min-pwd-age=default --max-pwd-age=default
exit $failed
diff --git a/source4/smb_server/smb/negprot.c b/source4/smb_server/smb/negprot.c
index ab763e39c3..fe6cd68f6e 100644
--- a/source4/smb_server/smb/negprot.c
+++ b/source4/smb_server/smb/negprot.c
@@ -33,7 +33,6 @@
static NTSTATUS get_challenge(struct smbsrv_connection *smb_conn, uint8_t buff[8])
{
NTSTATUS nt_status;
- const uint8_t *challenge;
/* muliple negprots are not premitted */
if (smb_conn->negotiate.auth_context) {
@@ -53,14 +52,12 @@ static NTSTATUS get_challenge(struct smbsrv_connection *smb_conn, uint8_t buff[8
return nt_status;
}
- nt_status = auth_get_challenge(smb_conn->negotiate.auth_context, &challenge);
+ nt_status = auth_get_challenge(smb_conn->negotiate.auth_context, buff);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(0, ("auth_get_challenge() returned %s", nt_errstr(nt_status)));
return nt_status;
}
- memcpy(buff, challenge, 8);
-
return NT_STATUS_OK;
}
diff --git a/source4/smb_server/smb/sesssetup.c b/source4/smb_server/smb/sesssetup.c
index 37f69abc12..aecd49fb0b 100644
--- a/source4/smb_server/smb/sesssetup.c
+++ b/source4/smb_server/smb/sesssetup.c
@@ -23,12 +23,14 @@
*/
#include "includes.h"
+#include <tevent.h>
#include "version.h"
#include "auth/gensec/gensec.h"
#include "auth/auth.h"
#include "smb_server/smb_server.h"
#include "smbd/service_stream.h"
#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
/*
setup the OS, Lanman and domain portions of a session setup reply
@@ -54,17 +56,18 @@ static void smbsrv_sesssetup_backend_send(struct smbsrv_request *req,
smbsrv_reply_sesssetup_send(req, sess, status);
}
-static void sesssetup_old_send(struct auth_check_password_request *areq,
- void *private_data)
+static void sesssetup_old_send(struct tevent_req *subreq)
{
- struct smbsrv_request *req = talloc_get_type(private_data, struct smbsrv_request);
+ struct smbsrv_request *req =
+ tevent_req_callback_data(subreq, struct smbsrv_request);
union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
struct auth_serversupplied_info *server_info = NULL;
struct auth_session_info *session_info;
struct smbsrv_session *smb_sess;
NTSTATUS status;
- status = auth_check_password_recv(areq, req, &server_info);
+ status = auth_check_password_recv(subreq, req, &server_info);
+ TALLOC_FREE(subreq);
if (!NT_STATUS_IS_OK(status)) goto failed;
/* This references server_info into session_info */
@@ -100,8 +103,9 @@ failed:
static void sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess)
{
struct auth_usersupplied_info *user_info = NULL;
- struct socket_address *remote_address;
+ struct tsocket_address *remote_address;
const char *remote_machine = NULL;
+ struct tevent_req *subreq;
sess->old.out.vuid = 0;
sess->old.out.action = 0;
@@ -119,11 +123,12 @@ static void sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess)
remote_machine = req->smb_conn->negotiate.calling_name->name;
}
- remote_address = socket_get_peer_addr(req->smb_conn->connection->socket, req);
+ remote_address = socket_get_remote_addr(req->smb_conn->connection->socket, req);
if (!remote_address) goto nomem;
if (!remote_machine) {
- remote_machine = remote_address->addr;
+ remote_machine = tsocket_address_inet_addr_string(remote_address, req);
+ if (!remote_machine) goto nomem;
}
user_info = talloc(req, struct auth_usersupplied_info);
@@ -142,25 +147,30 @@ static void sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess)
user_info->password.response.lanman.data = talloc_steal(user_info, sess->old.in.password.data);
user_info->password.response.nt = data_blob(NULL, 0);
- auth_check_password_send(req->smb_conn->negotiate.auth_context, user_info,
- sesssetup_old_send, req);
+ subreq = auth_check_password_send(req,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->negotiate.auth_context,
+ user_info);
+ if (!subreq) goto nomem;
+ tevent_req_set_callback(subreq, sesssetup_old_send, req);
return;
nomem:
smbsrv_sesssetup_backend_send(req, sess, NT_STATUS_NO_MEMORY);
}
-static void sesssetup_nt1_send(struct auth_check_password_request *areq,
- void *private_data)
+static void sesssetup_nt1_send(struct tevent_req *subreq)
{
- struct smbsrv_request *req = talloc_get_type(private_data, struct smbsrv_request);
+ struct smbsrv_request *req =
+ tevent_req_callback_data(subreq, struct smbsrv_request);
union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
struct auth_serversupplied_info *server_info = NULL;
struct auth_session_info *session_info;
struct smbsrv_session *smb_sess;
NTSTATUS status;
- status = auth_check_password_recv(areq, req, &server_info);
+ status = auth_check_password_recv(subreq, req, &server_info);
+ TALLOC_FREE(subreq);
if (!NT_STATUS_IS_OK(status)) goto failed;
/* This references server_info into session_info */
@@ -206,9 +216,10 @@ static void sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
NTSTATUS status;
struct auth_context *auth_context;
struct auth_usersupplied_info *user_info = NULL;
- struct socket_address *remote_address;
+ struct tsocket_address *remote_address;
const char *remote_machine = NULL;
-
+ struct tevent_req *subreq;
+
sess->nt1.out.vuid = 0;
sess->nt1.out.action = 0;
@@ -245,11 +256,12 @@ static void sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
remote_machine = req->smb_conn->negotiate.calling_name->name;
}
- remote_address = socket_get_peer_addr(req->smb_conn->connection->socket, req);
+ remote_address = socket_get_remote_addr(req->smb_conn->connection->socket, req);
if (!remote_address) goto nomem;
if (!remote_machine) {
- remote_machine = remote_address->addr;
+ remote_machine = tsocket_address_inet_addr_string(remote_address, req);
+ if (!remote_machine) goto nomem;
}
user_info = talloc(req, struct auth_usersupplied_info);
@@ -269,8 +281,13 @@ static void sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
user_info->password.response.nt = sess->nt1.in.password2;
user_info->password.response.nt.data = talloc_steal(user_info, sess->nt1.in.password2.data);
- auth_check_password_send(auth_context, user_info,
- sesssetup_nt1_send, req);
+ subreq = auth_check_password_send(req,
+ req->smb_conn->connection->event.ctx,
+ auth_context,
+ user_info);
+ if (!subreq) goto nomem;
+ tevent_req_set_callback(subreq, sesssetup_nt1_send, req);
+
return;
nomem:
@@ -286,9 +303,9 @@ struct sesssetup_spnego_state {
struct smbsrv_session *smb_sess;
};
-static void sesssetup_spnego_send(struct gensec_update_request *greq, void *private_data)
+static void sesssetup_spnego_send(struct tevent_req *subreq)
{
- struct sesssetup_spnego_state *s = talloc_get_type(private_data,
+ struct sesssetup_spnego_state *s = tevent_req_callback_data(subreq,
struct sesssetup_spnego_state);
struct smbsrv_request *req = s->req;
union smb_sesssetup *sess = s->sess;
@@ -298,7 +315,8 @@ static void sesssetup_spnego_send(struct gensec_update_request *greq, void *priv
NTSTATUS skey_status;
DATA_BLOB session_key;
- status = gensec_update_recv(greq, req, &sess->spnego.out.secblob);
+ status = gensec_update_recv(subreq, req, &sess->spnego.out.secblob);
+ TALLOC_FREE(subreq);
if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
goto done;
} else if (!NT_STATUS_IS_OK(status)) {
@@ -340,6 +358,7 @@ static void sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *se
struct smbsrv_session *smb_sess = NULL;
struct sesssetup_spnego_state *s = NULL;
uint16_t vuid;
+ struct tevent_req *subreq;
sess->spnego.out.vuid = 0;
sess->spnego.out.action = 0;
@@ -407,8 +426,15 @@ static void sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *se
s->sess = sess;
s->smb_sess = smb_sess;
- gensec_update_send(smb_sess->gensec_ctx, sess->spnego.in.secblob,
- sesssetup_spnego_send, s);
+ subreq = gensec_update_send(s,
+ req->smb_conn->connection->event.ctx,
+ smb_sess->gensec_ctx,
+ sess->spnego.in.secblob);
+ if (!subreq) {
+ goto nomem;
+ }
+ tevent_req_set_callback(subreq, sesssetup_spnego_send, s);
+
return;
nomem:
diff --git a/source4/smb_server/smb2/sesssetup.c b/source4/smb_server/smb2/sesssetup.c
index 58090305a0..9b601d17c0 100644
--- a/source4/smb_server/smb2/sesssetup.c
+++ b/source4/smb_server/smb2/sesssetup.c
@@ -19,6 +19,7 @@
*/
#include "includes.h"
+#include <tevent.h>
#include "auth/gensec/gensec.h"
#include "auth/auth.h"
#include "libcli/smb2/smb2.h"
@@ -57,9 +58,9 @@ struct smb2srv_sesssetup_callback_ctx {
struct smbsrv_session *smb_sess;
};
-static void smb2srv_sesssetup_callback(struct gensec_update_request *greq, void *private_data)
+static void smb2srv_sesssetup_callback(struct tevent_req *subreq)
{
- struct smb2srv_sesssetup_callback_ctx *ctx = talloc_get_type(private_data,
+ struct smb2srv_sesssetup_callback_ctx *ctx = tevent_req_callback_data(subreq,
struct smb2srv_sesssetup_callback_ctx);
struct smb2srv_request *req = ctx->req;
union smb_sesssetup *io = ctx->io;
@@ -67,7 +68,8 @@ static void smb2srv_sesssetup_callback(struct gensec_update_request *greq, void
struct auth_session_info *session_info = NULL;
NTSTATUS status;
- status = gensec_update_recv(greq, req, &io->smb2.out.secblob);
+ status = gensec_update_recv(subreq, req, &io->smb2.out.secblob);
+ TALLOC_FREE(subreq);
if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
goto done;
} else if (!NT_STATUS_IS_OK(status)) {
@@ -108,6 +110,7 @@ static void smb2srv_sesssetup_backend(struct smb2srv_request *req, union smb_ses
struct smb2srv_sesssetup_callback_ctx *callback_ctx;
struct smbsrv_session *smb_sess = NULL;
uint64_t vuid;
+ struct tevent_req *subreq;
io->smb2.out.session_flags = 0;
io->smb2.out.uid = 0;
@@ -174,8 +177,12 @@ static void smb2srv_sesssetup_backend(struct smb2srv_request *req, union smb_ses
callback_ctx->io = io;
callback_ctx->smb_sess = smb_sess;
- gensec_update_send(smb_sess->gensec_ctx, io->smb2.in.secblob,
- smb2srv_sesssetup_callback, callback_ctx);
+ subreq = gensec_update_send(callback_ctx,
+ req->smb_conn->connection->event.ctx,
+ smb_sess->gensec_ctx,
+ io->smb2.in.secblob);
+ if (!subreq) goto nomem;
+ tevent_req_set_callback(subreq, smb2srv_sesssetup_callback, callback_ctx);
/* note that we ignore SMB2_NEGOTIATE_SIGNING_ENABLED from the client.
This is deliberate as windows does not set it even when it does
diff --git a/source4/smbd/config.mk b/source4/smbd/config.mk
index b85beb0bc0..a77a18602f 100644
--- a/source4/smbd/config.mk
+++ b/source4/smbd/config.mk
@@ -4,7 +4,7 @@
PRIVATE_DEPENDENCIES = \
LIBTEVENT MESSAGING samba_socket \
NDR_NAMED_PIPE_AUTH NAMED_PIPE_AUTH_TSTREAM \
- HEIMDAL_GSSAPI CREDENTIALS
+ HEIMDAL_GSSAPI CREDENTIALS LIBTSOCKET
service_OBJ_FILES = $(addprefix $(smbdsrcdir)/, \
service.o \
diff --git a/source4/smbd/server.c b/source4/smbd/server.c
index ddfa220a72..e73cdfd659 100644
--- a/source4/smbd/server.c
+++ b/source4/smbd/server.c
@@ -326,7 +326,7 @@ static int binary_smbd_main(const char *binary_name, int argc, const char *argv[
umask(0);
DEBUG(0,("%s version %s started.\n", binary_name, SAMBA_VERSION_STRING));
- DEBUGADD(0,("Copyright Andrew Tridgell and the Samba Team 1992-2009\n"));
+ DEBUGADD(0,("Copyright Andrew Tridgell and the Samba Team 1992-2010\n"));
if (sizeof(uint16_t) < 2 || sizeof(uint32_t) < 4 || sizeof(uint64_t) < 8) {
DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n"));
diff --git a/source4/smbd/service_stream.c b/source4/smbd/service_stream.c
index ab35eb04bf..2d8b9559f4 100644
--- a/source4/smbd/service_stream.c
+++ b/source4/smbd/service_stream.c
@@ -26,6 +26,7 @@
#include "lib/messaging/irpc.h"
#include "cluster/cluster.h"
#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
/* the range of ports to try for dcerpc over tcp endpoints */
#define SERVER_TCP_LOW_PORT 1024
@@ -164,7 +165,6 @@ static void stream_new_connection(struct tevent_context *ev,
{
struct stream_socket *stream_socket = talloc_get_type(private_data, struct stream_socket);
struct stream_connection *srv_conn;
- struct socket_address *c, *s;
srv_conn = talloc_zero(ev, struct stream_connection);
if (!srv_conn) {
@@ -205,20 +205,34 @@ static void stream_new_connection(struct tevent_context *ev,
return;
}
- c = socket_get_peer_addr(sock, ev);
- s = socket_get_my_addr(sock, ev);
- if (s && c) {
+ srv_conn->remote_address = socket_get_remote_addr(srv_conn->socket, srv_conn);
+ if (!srv_conn->remote_address) {
+ stream_terminate_connection(srv_conn, "socket_get_remote_addr() failed");
+ return;
+ }
+
+ srv_conn->local_address = socket_get_local_addr(srv_conn->socket, srv_conn);
+ if (!srv_conn->local_address) {
+ stream_terminate_connection(srv_conn, "socket_get_local_addr() failed");
+ return;
+ }
+
+ {
+ TALLOC_CTX *tmp_ctx;
const char *title;
- title = talloc_asprintf(s, "conn[%s] c[%s:%u] s[%s:%u] server_id[%s]",
+
+ tmp_ctx = talloc_new(srv_conn);
+
+ title = talloc_asprintf(tmp_ctx, "conn[%s] c[%s] s[%s] server_id[%s]",
stream_socket->ops->name,
- c->addr, c->port, s->addr, s->port,
- cluster_id_string(s, server_id));
+ tsocket_address_string(srv_conn->remote_address, tmp_ctx),
+ tsocket_address_string(srv_conn->local_address, tmp_ctx),
+ cluster_id_string(tmp_ctx, server_id));
if (title) {
stream_connection_set_title(srv_conn, title);
}
+ talloc_free(tmp_ctx);
}
- talloc_free(c);
- talloc_free(s);
/* we're now ready to start receiving events on this stream */
TEVENT_FD_READABLE(srv_conn->event.fde);
diff --git a/source4/smbd/service_stream.h b/source4/smbd/service_stream.h
index 5d577d4dd8..685502a8ba 100644
--- a/source4/smbd/service_stream.h
+++ b/source4/smbd/service_stream.h
@@ -50,6 +50,9 @@ struct stream_connection {
struct messaging_context *msg_ctx;
struct loadparm_context *lp_ctx;
+ struct tsocket_address *local_address;
+ struct tsocket_address *remote_address;
+
/*
* this transport layer session info, normally NULL
* which means the same as an anonymous session info
diff --git a/source4/torture/drs/unit/prefixmap_tests.c b/source4/torture/drs/unit/prefixmap_tests.c
index 03f30de106..4bfdcc0d8b 100644
--- a/source4/torture/drs/unit/prefixmap_tests.c
+++ b/source4/torture/drs/unit/prefixmap_tests.c
@@ -383,6 +383,48 @@ static bool torture_drs_unit_pfm_oid_from_attid(struct torture_context *tctx, st
}
/**
+ * Tests dsdb_schema_pfm_oid_from_attid() for handling
+ * correctly different type of attid values.
+ * See: MS-ADTS, 3.1.1.2.6 ATTRTYP
+ */
+static bool torture_drs_unit_pfm_oid_from_attid_check_attid(struct torture_context *tctx,
+ struct drsut_prefixmap_data *priv)
+{
+ WERROR werr;
+ const char *oid;
+
+ /* Test with valid prefixMap attid */
+ werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0x00000000, tctx, &oid);
+ torture_assert_werr_ok(tctx, werr, "Testing prefixMap type attid = 0x0000000");
+
+ /* Test with attid in msDS-IntId range */
+ werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0x80000000, tctx, &oid);
+ torture_assert_werr_equal(tctx, werr, WERR_INVALID_PARAMETER,
+ "Testing msDS-IntId type attid = 0x80000000");
+ werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0xBFFFFFFF, tctx, &oid);
+ torture_assert_werr_equal(tctx, werr, WERR_INVALID_PARAMETER,
+ "Testing msDS-IntId type attid = 0xBFFFFFFF");
+
+ /* Test with attid in RESERVED range */
+ werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0xC0000000, tctx, &oid);
+ torture_assert_werr_equal(tctx, werr, WERR_INVALID_PARAMETER,
+ "Testing RESERVED type attid = 0xC0000000");
+ werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0xFFFEFFFF, tctx, &oid);
+ torture_assert_werr_equal(tctx, werr, WERR_INVALID_PARAMETER,
+ "Testing RESERVED type attid = 0xFFFEFFFF");
+
+ /* Test with attid in INTERNAL range */
+ werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0xFFFF0000, tctx, &oid);
+ torture_assert_werr_equal(tctx, werr, WERR_INVALID_PARAMETER,
+ "Testing INTERNAL type attid = 0xFFFF0000");
+ werr = dsdb_schema_pfm_oid_from_attid(priv->pfm_full, 0xFFFFFFFF, tctx, &oid);
+ torture_assert_werr_equal(tctx, werr, WERR_INVALID_PARAMETER,
+ "Testing INTERNAL type attid = 0xFFFFFFFF");
+
+ return true;
+}
+
+/**
* Test Schema prefixMap conversions to/from drsuapi prefixMap
* representation.
*/
@@ -663,7 +705,6 @@ static bool torture_drs_unit_ldb_setup(struct torture_context *tctx, struct drsu
}
DONE:
- unlink(ldb_url);
talloc_free(mem_ctx);
return bret;
}
@@ -722,6 +763,8 @@ struct torture_tcase * torture_drs_unit_prefixmap(struct torture_suite *suite)
torture_tcase_add_simple_test(tc, "make_attid_full_map", (pfn_run)torture_drs_unit_pfm_make_attid_full_map);
torture_tcase_add_simple_test(tc, "make_attid_small_map", (pfn_run)torture_drs_unit_pfm_make_attid_small_map);
torture_tcase_add_simple_test(tc, "oid_from_attid_full_map", (pfn_run)torture_drs_unit_pfm_oid_from_attid);
+ torture_tcase_add_simple_test(tc, "oid_from_attid_check_attid",
+ (pfn_run)torture_drs_unit_pfm_oid_from_attid_check_attid);
torture_tcase_add_simple_test(tc, "pfm_to_from_drsuapi", (pfn_run)torture_drs_unit_pfm_to_from_drsuapi);
diff --git a/source4/torture/libnet/python/samr-test.py b/source4/torture/libnet/python/samr-test.py
new file mode 100644
index 0000000000..d68456b79d
--- /dev/null
+++ b/source4/torture/libnet/python/samr-test.py
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Unix SMB/CIFS implementation.
+# Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 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/>.
+#
+
+#
+# Usage:
+# export ACCOUNT_NAME=kamen
+# export NEW_PASS=test
+# export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun
+# PYTHONPATH="$samba4srcdir/torture/libnet/python" $SUBUNITRUN samr-test -Ukma-exch.devel/Administrator%333
+#
+
+import sys
+import os
+
+from samba import net
+import unittest
+import samba.tests
+
+if not "ACCOUNT_NAME" in os.environ.keys():
+ parser.error("Please supply ACCOUNT_NAME in environment")
+
+if not "NEW_PASS" in os.environ.keys():
+ parser.error("Please supply NEW_PASS in environment")
+
+account_name = os.environ["ACCOUNT_NAME"]
+new_pass = os.environ["NEW_PASS"]
+
+creds = samba.tests.cmdline_credentials
+
+#
+# Tests start here
+#
+
+class Libnet_SetPwdTest(unittest.TestCase):
+
+ ########################################################################################
+
+ def test_SetPassword(self):
+ net.SetPassword(account_name=account_name,
+ domain_name=creds.get_domain(),
+ newpassword=new_pass,
+ credentials=creds)
+
+ ########################################################################################
+
diff --git a/source4/torture/ndr/ndr.c b/source4/torture/ndr/ndr.c
index 8602003017..471c398b77 100644
--- a/source4/torture/ndr/ndr.c
+++ b/source4/torture/ndr/ndr.c
@@ -233,12 +233,21 @@ static bool test_compare_uuid(struct torture_context *tctx)
"GUID diff invalid");
g1.time_low = 10;
- torture_assert_int_equal(tctx, 10, GUID_compare(&g1, &g2),
+ torture_assert_int_equal(tctx, 1, GUID_compare(&g1, &g2),
"GUID diff invalid");
g1.time_low = 0;
g1.clock_seq[1] = 20;
- torture_assert_int_equal(tctx, 20, GUID_compare(&g1, &g2),
+ torture_assert_int_equal(tctx, 1, GUID_compare(&g1, &g2),
+ "GUID diff invalid");
+
+ g1.time_low = ~0;
+ torture_assert_int_equal(tctx, 1, GUID_compare(&g1, &g2),
+ "GUID diff invalid");
+
+ g1.time_low = 0;
+ g2.time_low = ~0;
+ torture_assert_int_equal(tctx, -1, GUID_compare(&g1, &g2),
"GUID diff invalid");
return true;
}
diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c
index 78987e3121..8bf3a2d97f 100644
--- a/source4/torture/raw/oplock.c
+++ b/source4/torture/raw/oplock.c
@@ -697,6 +697,176 @@ done:
return ret;
}
+/**
+ * Exclusive version of batch19
+ */
+static bool test_raw_oplock_exclusive7(struct torture_context *tctx,
+ struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname1 = BASEDIR "\\test_exclusiv6_1.dat";
+ const char *fname2 = BASEDIR "\\test_exclusiv6_2.dat";
+ const char *fname3 = BASEDIR "\\test_exclusiv6_3.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_fileinfo qfi;
+ union smb_setfileinfo sfi;
+ uint16_t fnum=0;
+ uint16_t fnum2 = 0;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname1);
+ smbcli_unlink(cli1->tree, fname2);
+ smbcli_unlink(cli1->tree, fname3);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given,
+ cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid.fnum = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname1;
+
+ torture_comment(tctx, "open a file with an exclusive oplock (share "
+ "mode: none)\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
+
+ torture_comment(tctx, "setpathinfo rename info should trigger a break "
+ "to none\n");
+ ZERO_STRUCT(sfi);
+ sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
+ sfi.generic.in.file.path = fname1;
+ sfi.rename_information.in.overwrite = 0;
+ sfi.rename_information.in.root_fid = 0;
+ sfi.rename_information.in.new_name = fname2+strlen(BASEDIR)+1;
+
+ status = smb_raw_setpathinfo(cli2->tree, &sfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_wait_for_oplock_break(tctx);
+ CHECK_VAL(break_info.failures, 0);
+
+ if (TARGET_IS_WINXP(tctx)) {
+ /* XP incorrectly breaks to level2. */
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ } else {
+ /* Exclusive oplocks should not be broken on rename. */
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.count, 0);
+ }
+
+ ZERO_STRUCT(qfi);
+ qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qfi.generic.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(cli1->tree, tctx, &qfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_STRMATCH(qfi.all_info.out.fname.s, fname2);
+
+ /* Try breaking to level2 and then see if rename breaks the level2.*/
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.fname = fname2;
+ status = smb_raw_open(cli2->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+
+ torture_wait_for_oplock_break(tctx);
+ CHECK_VAL(break_info.failures, 0);
+
+ if (TARGET_IS_WINXP(tctx)) {
+ /* XP already broke to level2. */
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.count, 0);
+ } else {
+ /* Break to level 2 expected. */
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ }
+
+ ZERO_STRUCT(break_info);
+ sfi.generic.in.file.path = fname2;
+ sfi.rename_information.in.overwrite = 0;
+ sfi.rename_information.in.root_fid = 0;
+ sfi.rename_information.in.new_name = fname1+strlen(BASEDIR)+1;
+
+ status = smb_raw_setpathinfo(cli2->tree, &sfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ /* Level2 oplocks are not broken on rename. */
+ torture_wait_for_oplock_break(tctx);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.count, 0);
+
+ /* Close and re-open file with oplock. */
+ smbcli_close(cli1->tree, fnum);
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
+
+ torture_comment(tctx, "setfileinfo rename info on a client's own fid "
+ "should not trigger a break nor a violation\n");
+ ZERO_STRUCT(break_info);
+ ZERO_STRUCT(sfi);
+ sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
+ sfi.generic.in.file.fnum = fnum;
+ sfi.rename_information.in.overwrite = 0;
+ sfi.rename_information.in.root_fid = 0;
+ sfi.rename_information.in.new_name = fname3+strlen(BASEDIR)+1;
+
+ status = smb_raw_setfileinfo(cli1->tree, &sfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ torture_wait_for_oplock_break(tctx);
+ if (TARGET_IS_WINXP(tctx)) {
+ /* XP incorrectly breaks to level2. */
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ } else {
+ CHECK_VAL(break_info.count, 0);
+ }
+
+ ZERO_STRUCT(qfi);
+ qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
+ qfi.generic.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(cli1->tree, tctx, &qfi);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3);
+
+done:
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli2->tree, fnum2);
+
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
static bool test_raw_oplock_batch1(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
{
const char *fname = BASEDIR "\\test_batch1.dat";
@@ -2117,7 +2287,8 @@ static bool test_raw_oplock_batch19(struct torture_context *tctx, struct smbcli_
io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
io.ntcreatex.in.alloc_size = 0;
io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
- io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
io.ntcreatex.in.create_options = 0;
io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
@@ -2134,7 +2305,8 @@ static bool test_raw_oplock_batch19(struct torture_context *tctx, struct smbcli_
fnum = io.ntcreatex.out.file.fnum;
CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
- torture_comment(tctx, "setpathinfo rename info should not trigger a break nor a violation\n");
+ torture_comment(tctx, "setpathinfo rename info should trigger a break "
+ "to none\n");
ZERO_STRUCT(sfi);
sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
sfi.generic.in.file.path = fname1;
@@ -2146,7 +2318,22 @@ static bool test_raw_oplock_batch19(struct torture_context *tctx, struct smbcli_
CHECK_STATUS(tctx, status, NT_STATUS_OK);
torture_wait_for_oplock_break(tctx);
- CHECK_VAL(break_info.count, 0);
+
+ CHECK_VAL(break_info.failures, 0);
+
+ if (TARGET_IS_WINXP(tctx)) {
+ /* Win XP breaks to level2. */
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ } else if (TARGET_IS_W2K3(tctx) || TARGET_IS_W2K8(tctx) ||
+ TARGET_IS_SAMBA3(tctx) || TARGET_IS_SAMBA4(tctx)) {
+ /* Win2K3/2k8 incorrectly doesn't break at all. */
+ CHECK_VAL(break_info.count, 0);
+ } else {
+ /* win7/2k8r2 break to none. */
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE);
+ }
ZERO_STRUCT(qfi);
qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
@@ -2156,7 +2343,16 @@ static bool test_raw_oplock_batch19(struct torture_context *tctx, struct smbcli_
CHECK_STATUS(tctx, status, NT_STATUS_OK);
CHECK_STRMATCH(qfi.all_info.out.fname.s, fname2);
- torture_comment(tctx, "setfileinfo rename info should not trigger a break nor a violation\n");
+ /* Close and re-open file with oplock. */
+ smbcli_close(cli1->tree, fnum);
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ torture_comment(tctx, "setfileinfo rename info on a client's own fid "
+ "should not trigger a break nor a violation\n");
+ ZERO_STRUCT(break_info);
ZERO_STRUCT(sfi);
sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
sfi.generic.in.file.fnum = fnum;
@@ -2168,7 +2364,13 @@ static bool test_raw_oplock_batch19(struct torture_context *tctx, struct smbcli_
CHECK_STATUS(tctx, status, NT_STATUS_OK);
torture_wait_for_oplock_break(tctx);
- CHECK_VAL(break_info.count, 0);
+ if (TARGET_IS_WINXP(tctx)) {
+ /* XP incorrectly breaks to level2. */
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ } else {
+ CHECK_VAL(break_info.count, 0);
+ }
ZERO_STRUCT(qfi);
qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
@@ -2178,9 +2380,8 @@ static bool test_raw_oplock_batch19(struct torture_context *tctx, struct smbcli_
CHECK_STATUS(tctx, status, NT_STATUS_OK);
CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3);
- smbcli_close(cli1->tree, fnum);
-
done:
+ smbcli_close(cli1->tree, fnum);
smb_raw_exit(cli1->session);
smb_raw_exit(cli2->session);
smbcli_deltree(cli1->tree, BASEDIR);
@@ -2223,7 +2424,8 @@ bool test_trans2rename(struct torture_context *tctx, struct smbcli_state *cli1,
io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
io.ntcreatex.in.alloc_size = 0;
io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
- io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
io.ntcreatex.in.create_options = 0;
io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
@@ -2284,9 +2486,8 @@ bool test_trans2rename(struct torture_context *tctx, struct smbcli_state *cli1,
CHECK_STATUS(tctx, status, NT_STATUS_OK);
CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3);
- smbcli_close(cli1->tree, fnum);
-
done:
+ smbcli_close(cli1->tree, fnum);
smb_raw_exit(cli1->session);
smb_raw_exit(cli2->session);
smbcli_deltree(cli1->tree, BASEDIR);
@@ -2469,7 +2670,6 @@ static bool test_raw_oplock_batch20(struct torture_context *tctx, struct smbcli_
fnum = io.ntcreatex.out.file.fnum;
CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
- torture_comment(tctx, "setpathinfo rename info should not trigger a break nor a violation\n");
ZERO_STRUCT(sfi);
sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
sfi.generic.in.file.path = fname1;
@@ -2481,7 +2681,21 @@ static bool test_raw_oplock_batch20(struct torture_context *tctx, struct smbcli_
CHECK_STATUS(tctx, status, NT_STATUS_OK);
torture_wait_for_oplock_break(tctx);
- CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ if (TARGET_IS_WINXP(tctx)) {
+ /* Win XP breaks to level2. */
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ } else if (TARGET_IS_W2K3(tctx) || TARGET_IS_W2K8(tctx) ||
+ TARGET_IS_SAMBA3(tctx) || TARGET_IS_SAMBA4(tctx)) {
+ /* Win2K3/2k8 incorrectly doesn't break at all. */
+ CHECK_VAL(break_info.count, 0);
+ } else {
+ /* win7/2k8r2 break to none. */
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE);
+ }
ZERO_STRUCT(qfi);
qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
@@ -2506,11 +2720,22 @@ static bool test_raw_oplock_batch20(struct torture_context *tctx, struct smbcli_
CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
torture_wait_for_oplock_break(tctx);
- CHECK_VAL(break_info.count, 1);
- CHECK_VAL(break_info.failures, 0);
- CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
- torture_comment(tctx, "setfileinfo rename info should not trigger a break nor a violation\n");
+ if (TARGET_IS_WINXP(tctx)) {
+ /* XP broke to level2, and doesn't break again. */
+ CHECK_VAL(break_info.count, 0);
+ } else if (TARGET_IS_W2K3(tctx) || TARGET_IS_W2K8(tctx) ||
+ TARGET_IS_SAMBA3(tctx) || TARGET_IS_SAMBA4(tctx)) {
+ /* Win2K3 incorrectly didn't break before so break now. */
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ } else {
+ /* win7/2k8r2 broke to none, and doesn't break again. */
+ CHECK_VAL(break_info.count, 0);
+ }
+
+ ZERO_STRUCT(break_info);
+
ZERO_STRUCT(sfi);
sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
sfi.generic.in.file.fnum = fnum;
@@ -2522,9 +2747,7 @@ static bool test_raw_oplock_batch20(struct torture_context *tctx, struct smbcli_
CHECK_STATUS(tctx, status, NT_STATUS_OK);
torture_wait_for_oplock_break(tctx);
- CHECK_VAL(break_info.count, 1);
- CHECK_VAL(break_info.failures, 0);
- CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ CHECK_VAL(break_info.count, 0);
ZERO_STRUCT(qfi);
qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
@@ -2542,9 +2765,10 @@ static bool test_raw_oplock_batch20(struct torture_context *tctx, struct smbcli_
CHECK_STATUS(tctx, status, NT_STATUS_OK);
CHECK_STRMATCH(qfi.all_info.out.fname.s, fname3);
- smbcli_close(cli1->tree, fnum);
done:
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli2->tree, fnum2);
smb_raw_exit(cli1->session);
smb_raw_exit(cli2->session);
smbcli_deltree(cli1->tree, BASEDIR);
@@ -3639,6 +3863,7 @@ struct torture_suite *torture_raw_oplock(TALLOC_CTX *mem_ctx)
torture_suite_add_2smb_test(suite, "EXCLUSIVE4", test_raw_oplock_exclusive4);
torture_suite_add_2smb_test(suite, "EXCLUSIVE5", test_raw_oplock_exclusive5);
torture_suite_add_2smb_test(suite, "EXCLUSIVE6", test_raw_oplock_exclusive6);
+ torture_suite_add_2smb_test(suite, "EXCLUSIVE7", test_raw_oplock_exclusive7);
torture_suite_add_2smb_test(suite, "BATCH1", test_raw_oplock_batch1);
torture_suite_add_2smb_test(suite, "BATCH2", test_raw_oplock_batch2);
torture_suite_add_2smb_test(suite, "BATCH3", test_raw_oplock_batch3);
diff --git a/source4/torture/raw/qfileinfo.c b/source4/torture/raw/qfileinfo.c
index 85f9f1b093..032df87a4d 100644
--- a/source4/torture/raw/qfileinfo.c
+++ b/source4/torture/raw/qfileinfo.c
@@ -140,7 +140,7 @@ static int dos_nt_time_cmp(time_t t, NTTIME nt)
{
time_t t2 = nt_time_to_unix(nt);
if (abs(t2 - t) <= 2) return 0;
- return t2 - t;
+ return t2 > t ? 1 : -1;
}
diff --git a/source4/torture/raw/setfileinfo.c b/source4/torture/raw/setfileinfo.c
index 42f4f320c2..1f8adfbe9a 100644
--- a/source4/torture/raw/setfileinfo.c
+++ b/source4/torture/raw/setfileinfo.c
@@ -970,6 +970,158 @@ done:
return ret;
}
+static bool
+torture_raw_sfileinfo_archive(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ const char *fname = BASEDIR "\\test_archive.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_setfileinfo sfinfo;
+ union smb_fileinfo finfo;
+ uint16_t fnum=0;
+ uint32_t access_mask = 0;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli->tree, fname);
+
+ /*
+ * create a normal file, verify archive bit
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid.fnum = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ io.ntcreatex.in.flags = 0;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK,
+ ret, done, "open failed");
+ fnum = io.ntcreatex.out.file.fnum;
+
+ torture_assert_int_equal(tctx,
+ io.ntcreatex.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED,
+ FILE_ATTRIBUTE_ARCHIVE,
+ "archive bit not set");
+
+ /*
+ * try to turn off archive bit
+ */
+ ZERO_STRUCT(sfinfo);
+ sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFO;
+ sfinfo.generic.in.file.fnum = fnum;
+ sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK,
+ ret, done, "setfileinfo failed");
+
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, tctx, &finfo);
+ torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK,
+ ret, done, "fileinfo failed");
+
+ torture_assert_int_equal(tctx,
+ finfo.all_info.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED,
+ FILE_ATTRIBUTE_NORMAL,
+ "archive bit set");
+
+ status = smbcli_close(cli->tree, fnum);
+ torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK,
+ ret, done, "close failed");
+
+ status = smbcli_unlink(cli->tree, fname);
+ torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK,
+ ret, done, "unlink failed");
+
+ /*
+ * create a directory, verify no archive bit
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid.fnum = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_DIR_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+ io.ntcreatex.in.flags = 0;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK,
+ ret, done, "directory open failed");
+ fnum = io.ntcreatex.out.file.fnum;
+
+ torture_assert_int_equal(tctx,
+ io.ntcreatex.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED,
+ FILE_ATTRIBUTE_DIRECTORY,
+ "archive bit set");
+
+ /*
+ * verify you can turn on archive bit
+ */
+ sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFO;
+ sfinfo.generic.in.file.fnum = fnum;
+ sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_ARCHIVE;
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK,
+ ret, done, "setfileinfo failed");
+
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, tctx, &finfo);
+ torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK,
+ ret, done, "fileinfo failed");
+
+ torture_assert_int_equal(tctx,
+ finfo.all_info.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED,
+ FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_ARCHIVE,
+ "archive bit not set");
+
+ /*
+ * and try to turn it back off
+ */
+ sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFO;
+ sfinfo.generic.in.file.fnum = fnum;
+ sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+ status = smb_raw_setfileinfo(cli->tree, &sfinfo);
+ torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK,
+ ret, done, "setfileinfo failed");
+
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, tctx, &finfo);
+ torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK,
+ ret, done, "fileinfo failed");
+
+ torture_assert_int_equal(tctx,
+ finfo.all_info.out.attrib & ~FILE_ATTRIBUTE_NONINDEXED,
+ FILE_ATTRIBUTE_DIRECTORY,
+ "archive bit set");
+
+ status = smbcli_close(cli->tree, fnum);
+ torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OK,
+ ret, done, "close failed");
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
+
struct torture_suite *torture_raw_sfileinfo(TALLOC_CTX *mem_ctx)
{
struct torture_suite *suite = torture_suite_create(mem_ctx,
@@ -983,6 +1135,7 @@ struct torture_suite *torture_raw_sfileinfo(TALLOC_CTX *mem_ctx)
torture_raw_sfileinfo_eof);
torture_suite_add_2smb_test(suite, "END-OF-FILE-ACCESS",
torture_raw_sfileinfo_eof_access);
+ torture_suite_add_1smb_test(suite, "ARCHIVE", torture_raw_sfileinfo_archive);
return suite;
}
diff --git a/source4/torture/raw/streams.c b/source4/torture/raw/streams.c
index 79cacffe10..a55575b6a3 100644
--- a/source4/torture/raw/streams.c
+++ b/source4/torture/raw/streams.c
@@ -609,7 +609,8 @@ static bool test_stream_delete(struct torture_context *tctx,
CHECK_STATUS(status, NT_STATUS_OK);
/* w2k and w2k3 return 0 and w2k8 returns 1 */
- if (TARGET_IS_WINXP(tctx) || TARGET_IS_W2K3(tctx)) {
+ if (TARGET_IS_WINXP(tctx) || TARGET_IS_W2K3(tctx) ||
+ TARGET_IS_SAMBA3(tctx)) {
CHECK_VALUE(finfo.all_info.out.delete_pending, 0);
} else {
CHECK_VALUE(finfo.all_info.out.delete_pending, 1);
diff --git a/source4/utils/net/net.c b/source4/utils/net/net.c
index 828e46889e..ba935b9986 100644
--- a/source4/utils/net/net.c
+++ b/source4/utils/net/net.c
@@ -6,6 +6,7 @@
Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
Copyright (C) 2004 Stefan Metzmacher (metze@samba.org)
+ Copyright (C) 2009 Jelmer Vernooij (jelmer@samba.org)
Largely rewritten by metze in August 2004
@@ -48,6 +49,83 @@
#include "param/param.h"
#include "lib/events/events.h"
#include "auth/credentials/credentials.h"
+#include <Python.h>
+#include "scripting/python/modules.h"
+
+static PyObject *py_tuple_from_argv(int argc, const char *argv[])
+{
+ PyObject *l;
+ Py_ssize_t i;
+
+ l = PyTuple_New(argc);
+ if (l == NULL) {
+ return NULL;
+ }
+
+ for (i = 0; i < argc; i++) {
+ PyTuple_SetItem(l, i, PyString_FromString(argv[i]));
+ }
+
+ return l;
+}
+
+static int py_call_with_string_args(PyObject *self, const char *method, int argc, const char *argv[])
+{
+ PyObject *ret, *args, *py_method;
+
+ args = py_tuple_from_argv(argc, argv);
+ if (args == NULL) {
+ PyErr_Print();
+ return 1;
+ }
+
+ py_method = PyObject_GetAttrString(self, method);
+ if (py_method == NULL) {
+ PyErr_Print();
+ return 1;
+ }
+
+ ret = PyObject_CallObject(py_method, args);
+
+ Py_DECREF(args);
+
+ if (ret == NULL) {
+ PyErr_Print();
+ return 1;
+ }
+
+ if (ret == Py_None) {
+ return 0;
+ } else if (PyInt_Check(ret)) {
+ return PyInt_AsLong(ret);
+ } else {
+ fprintf(stderr, "Function return value type unexpected.\n");
+ return -1;
+ }
+}
+
+static PyObject *py_commands(void)
+{
+ PyObject *netcmd_module, *py_cmds;
+ netcmd_module = PyImport_ImportModule("samba.netcmd");
+ if (netcmd_module == NULL) {
+ PyErr_Print();
+ return NULL;
+ }
+
+ py_cmds = PyObject_GetAttrString(netcmd_module, "commands");
+ if (py_cmds == NULL) {
+ PyErr_Print();
+ return NULL;
+ }
+
+ if (!PyDict_Check(py_cmds)) {
+ d_printf("Python net commands is not a dictionary\n");
+ return NULL;
+ }
+
+ return py_cmds;
+}
/*
run a function from a function table. If not found then
@@ -84,6 +162,7 @@ int net_run_usage(struct net_context *ctx,
const struct net_functable *functable)
{
int i;
+ PyObject *py_cmds, *py_cmd;
for (i=0; functable[i].name; i++) {
if (strcasecmp_m(argv[0], functable[i].name) == 0)
@@ -92,6 +171,17 @@ int net_run_usage(struct net_context *ctx,
}
}
+ py_cmds = py_commands();
+ if (py_cmds == NULL) {
+ return 1;
+ }
+
+ py_cmd = PyDict_GetItemString(py_cmds, argv[0]);
+ if (py_cmd != NULL) {
+ return py_call_with_string_args(py_cmd, "usage", argc-1,
+ argv+1);
+ }
+
d_printf("No usage information for command: %s\n", argv[0]);
return 1;
@@ -112,13 +202,12 @@ static const struct net_functable net_functable[] = {
{NULL, NULL, NULL, NULL}
};
-int net_help(struct net_context *ctx, const struct net_functable *ftable)
+static int net_help_builtin(const struct net_functable *ftable)
{
int i = 0;
const char *name = ftable[i].name;
const char *desc = ftable[i].desc;
- d_printf("Available commands:\n");
while (name && desc) {
if (strlen(name) > 7) {
d_printf("\t%s\t%s", name, desc);
@@ -128,7 +217,53 @@ int net_help(struct net_context *ctx, const struct net_functable *ftable)
name = ftable[++i].name;
desc = ftable[i].desc;
}
+ return 0;
+}
+
+static int net_help_python(void)
+{
+ PyObject *py_cmds;
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+
+ py_cmds = py_commands();
+ if (py_cmds == NULL) {
+ return 1;
+ }
+
+ while (PyDict_Next(py_cmds, &pos, &key, &value)) {
+ char *name, *desc;
+ PyObject *py_desc;
+ if (!PyString_Check(key)) {
+ d_printf("Command name not a string\n");
+ return 1;
+ }
+ name = PyString_AsString(key);
+ py_desc = PyObject_GetAttrString(value, "description");
+ if (py_desc == NULL) {
+ PyErr_Print();
+ return 1;
+ }
+ if (!PyString_Check(py_desc)) {
+ d_printf("Command description for %s not a string\n",
+ name);
+ return 1;
+ }
+ desc = PyString_AsString(py_desc);
+ if (strlen(name) > 7) {
+ d_printf("\t%s\t%s\n", name, desc);
+ } else {
+ d_printf("\t%s\t\t%s\n", name, desc);
+ }
+ }
+ return 0;
+}
+int net_help(struct net_context *ctx, const struct net_functable *ftable)
+{
+ d_printf("Available commands:\n");
+ net_help_builtin(ftable);
+ net_help_python();
return 0;
}
@@ -148,6 +283,7 @@ static int binary_net(int argc, const char **argv)
int opt,i;
int rc;
int argc_new;
+ PyObject *py_cmds, *py_cmd;
const char **argv_new;
struct tevent_context *ev;
struct net_context *ctx = NULL;
@@ -163,6 +299,33 @@ static int binary_net(int argc, const char **argv)
setlinebuf(stdout);
+ dcerpc_init(cmdline_lp_ctx);
+
+ ev = s4_event_context_init(NULL);
+ if (!ev) {
+ d_printf("Failed to create an event context\n");
+ exit(1);
+ }
+ py_load_samba_modules();
+ Py_Initialize();
+ PySys_SetArgv(argc, argv);
+ py_update_path("bin"); /* FIXME: Can't assume this is always the case */
+
+ py_cmds = py_commands();
+ if (py_cmds == NULL) {
+ return 1;
+ }
+
+ if (argc > 1) {
+ py_cmd = PyDict_GetItemString(py_cmds, argv[1]);
+ if (py_cmd != NULL) {
+ rc = py_call_with_string_args(py_cmd, "_run",
+ argc-1, argv+1);
+ talloc_free(ev);
+ return rc;
+ }
+ }
+
pc = poptGetContext("net", argc, (const char **) argv, long_options,
POPT_CONTEXT_KEEP_FIRST);
@@ -190,13 +353,7 @@ static int binary_net(int argc, const char **argv)
return net_usage(ctx, argc, argv);
}
- dcerpc_init(cmdline_lp_ctx);
- ev = s4_event_context_init(NULL);
- if (!ev) {
- d_printf("Failed to create an event context\n");
- exit(1);
- }
ctx = talloc(ev, struct net_context);
if (!ctx) {
d_printf("Failed to talloc a net_context\n");
@@ -208,7 +365,10 @@ static int binary_net(int argc, const char **argv)
ctx->credentials = cmdline_credentials;
ctx->event_ctx = ev;
- rc = net_run_function(ctx, argc_new-1, argv_new+1, net_functable, net_usage);
+
+
+ rc = net_run_function(ctx, argc_new-1, argv_new+1, net_functable,
+ net_usage);
if (rc != 0) {
DEBUG(0,("return code = %d\n", rc));