diff options
Diffstat (limited to 'source4')
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)); |