#include "../lib/util/dlinklist.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_internal.h" /* TODO: remove this */
+#include "auth/common_auth.h"
#include "param/param.h"
#include "smbd/service_stream.h"
#include "dsdb/samdb/samdb.h"
}
*errstring = talloc_asprintf(mem_ctx, "%08X: %s", W_ERROR_V(err),
- ldb_strerror(ldb_err));
- if (add_err_string != NULL) {
- *errstring = talloc_asprintf(mem_ctx, "%s - %s", *errstring,
- add_err_string);
- }
-
+ add_err_string != NULL ? add_err_string : ldb_strerror(ldb_err));
+
/* result is 1:1 for now */
return ldb_err;
}
if (conn->server_credentials) {
char **sasl_mechs = NULL;
- struct gensec_security_ops **backends = gensec_security_all();
- struct gensec_security_ops **ops
+ const struct gensec_security_ops * const *backends = gensec_security_all();
+ const struct gensec_security_ops **ops
= gensec_use_kerberos_mechs(conn, backends, conn->server_credentials);
unsigned int i, j = 0;
for (i = 0; ops && ops[i]; i++) {
ldb_set_opaque(conn->ldb, "supportedSASLMechanisms", sasl_mechs);
}
+ ldb_set_opaque(conn->ldb, "remoteAddress",
+ conn->connection->remote_address);
+
return NT_STATUS_OK;
}
void ldapsrv_queue_reply(struct ldapsrv_call *call, struct ldapsrv_reply *reply)
{
- DLIST_ADD_END(call->replies, reply, struct ldapsrv_reply *);
+ DLIST_ADD_END(call->replies, reply);
}
static NTSTATUS ldapsrv_unwilling(struct ldapsrv_call *call, int error)
static int ldapsrv_add_with_controls(struct ldapsrv_call *call,
const struct ldb_message *message,
struct ldb_control **controls,
- void *context)
+ struct ldb_result *res)
{
struct ldb_context *ldb = call->conn->ldb;
struct ldb_request *req;
ret = ldb_build_add_req(&req, ldb, ldb,
message,
controls,
- context,
+ res,
ldb_modify_default_callback,
NULL);
if (ret != LDB_SUCCESS) return ret;
+ if (call->conn->global_catalog) {
+ return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM, "modify forbidden on global catalog port");
+ }
+ ldb_request_add_control(req, DSDB_CONTROL_NO_GLOBAL_CATALOG, false, NULL);
+
ret = ldb_transaction_start(ldb);
if (ret != LDB_SUCCESS) {
return ret;
static int ldapsrv_mod_with_controls(struct ldapsrv_call *call,
const struct ldb_message *message,
struct ldb_control **controls,
- void *context)
+ struct ldb_result *res)
{
struct ldb_context *ldb = call->conn->ldb;
struct ldb_request *req;
ret = ldb_build_mod_req(&req, ldb, ldb,
message,
controls,
- context,
+ res,
ldb_modify_default_callback,
NULL);
return ret;
}
+ if (call->conn->global_catalog) {
+ return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM, "modify forbidden on global catalog port");
+ }
+ ldb_request_add_control(req, DSDB_CONTROL_NO_GLOBAL_CATALOG, false, NULL);
+
ret = ldb_transaction_start(ldb);
if (ret != LDB_SUCCESS) {
return ret;
static int ldapsrv_del_with_controls(struct ldapsrv_call *call,
struct ldb_dn *dn,
struct ldb_control **controls,
- void *context)
+ struct ldb_result *res)
{
struct ldb_context *ldb = call->conn->ldb;
struct ldb_request *req;
ret = ldb_build_del_req(&req, ldb, ldb,
dn,
controls,
- context,
+ res,
ldb_modify_default_callback,
NULL);
if (ret != LDB_SUCCESS) return ret;
+ if (call->conn->global_catalog) {
+ return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM, "modify forbidden on global catalog port");
+ }
+ ldb_request_add_control(req, DSDB_CONTROL_NO_GLOBAL_CATALOG, false, NULL);
+
ret = ldb_transaction_start(ldb);
if (ret != LDB_SUCCESS) {
return ret;
struct ldb_dn *olddn,
struct ldb_dn *newdn,
struct ldb_control **controls,
- void *context)
+ struct ldb_result *res)
{
struct ldb_context *ldb = call->conn->ldb;
struct ldb_request *req;
ret = ldb_build_rename_req(&req, ldb, ldb,
olddn,
newdn,
- NULL,
- context,
+ controls,
+ res,
ldb_modify_default_callback,
NULL);
if (ret != LDB_SUCCESS) return ret;
+ if (call->conn->global_catalog) {
+ return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM, "modify forbidden on global catalog port");
+ }
+ ldb_request_add_control(req, DSDB_CONTROL_NO_GLOBAL_CATALOG, false, NULL);
+
ret = ldb_transaction_start(ldb);
if (ret != LDB_SUCCESS) {
return ret;
struct ldb_search_options_control *search_options;
struct ldb_control *extended_dn_control;
struct ldb_extended_dn_control *extended_dn_decoded = NULL;
+ struct ldb_control *notification_control = NULL;
enum ldb_scope scope = LDB_SCOPE_DEFAULT;
const char **attrs = NULL;
const char *scope_str, *errstr = NULL;
search_options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
ldb_request_add_control(lreq, LDB_CONTROL_SEARCH_OPTIONS_OID, false, search_options);
}
+ } else {
+ ldb_request_add_control(lreq, DSDB_CONTROL_NO_GLOBAL_CATALOG, false, NULL);
}
extended_dn_control = ldb_request_get_control(lreq, LDB_CONTROL_EXTENDED_DN_OID);
}
}
+ notification_control = ldb_request_get_control(lreq, LDB_CONTROL_NOTIFICATION_OID);
+ if (notification_control != NULL) {
+ const struct ldapsrv_call *pc = NULL;
+ size_t count = 0;
+
+ for (pc = call->conn->pending_calls; pc != NULL; pc = pc->next) {
+ count += 1;
+ }
+
+ if (count >= call->conn->limits.max_notifications) {
+ DEBUG(10,("SearchRequest: error MaxNotificationPerConn\n"));
+ result = map_ldb_error(local_ctx,
+ LDB_ERR_ADMIN_LIMIT_EXCEEDED,
+ "MaxNotificationPerConn reached",
+ &errstr);
+ goto reply;
+ }
+
+ /*
+ * For now we need to do periodic retries on our own.
+ * As the dsdb_notification module will return after each run.
+ */
+ call->notification.busy = true;
+ }
+
ldb_set_timeout(samdb, lreq, req->timelimit);
if (!call->conn->is_privileged) {
ldapsrv_queue_reply(call, ent_r);
}
+ if (call->notification.busy) {
+ /* Move/Add it to the end */
+ DLIST_DEMOTE(call->conn->pending_calls, call);
+ call->notification.generation =
+ call->conn->service->notification.generation;
+
+ if (res->count != 0) {
+ call->notification.generation += 1;
+ ldapsrv_notification_retry_setup(call->conn->service,
+ true);
+ }
+
+ talloc_free(local_ctx);
+ return NT_STATUS_OK;
+ }
+
/* Send back referrals if they do exist (search operations) */
if (res->refs != NULL) {
char **ref;
}
reply:
+ DLIST_REMOVE(call->conn->pending_calls, call);
+ call->notification.busy = false;
+
done_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultDone);
NT_STATUS_HAVE_NO_MEMORY(done_r);
static NTSTATUS ldapsrv_AbandonRequest(struct ldapsrv_call *call)
{
-/* struct ldap_AbandonRequest *req = &call->request.r.AbandonRequest;*/
+ struct ldap_AbandonRequest *req = &call->request->r.AbandonRequest;
+ struct ldapsrv_call *c = NULL;
+ struct ldapsrv_call *n = NULL;
+
DEBUG(10, ("AbandonRequest\n"));
+
+ for (c = call->conn->pending_calls; c != NULL; c = n) {
+ n = c->next;
+
+ if (c->request->messageid != req->messageid) {
+ continue;
+ }
+
+ DLIST_REMOVE(call->conn->pending_calls, c);
+ TALLOC_FREE(c);
+ }
+
return NT_STATUS_OK;
}
{
unsigned int i;
struct ldap_message *msg = call->request;
+ NTSTATUS status;
+ bool log = true;
+
/* Check for undecoded critical extensions */
for (i=0; msg->controls && msg->controls[i]; i++) {
if (!msg->controls_decoded[i] &&
}
}
+ if (call->conn->authz_logged == false) {
+
+ /*
+ * We do not want to log anonymous access if the query
+ * is just for the rootDSE, or it is a startTLS or a
+ * Bind.
+ *
+ * A rootDSE search could also be done over
+ * CLDAP anonymously for example, so these don't
+ * really count.
+ * Essentially we want to know about
+ * access beyond that normally done prior to a
+ * bind.
+ */
+
+ switch(call->request->type) {
+ case LDAP_TAG_BindRequest:
+ log = false;
+ break;
+ case LDAP_TAG_ExtendedResponse: {
+ struct ldap_ExtendedRequest *req = &call->request->r.ExtendedRequest;
+ if (strcmp(req->oid, LDB_EXTENDED_START_TLS_OID) == 0) {
+ log = false;
+ }
+ break;
+ }
+ case LDAP_TAG_SearchRequest: {
+ struct ldap_SearchRequest *req = &call->request->r.SearchRequest;
+ if (req->scope == LDAP_SEARCH_SCOPE_BASE) {
+ if (req->basedn[0] == '\0') {
+ log = false;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (log) {
+ const char *transport_protection = AUTHZ_TRANSPORT_PROTECTION_NONE;
+ if (call->conn->sockets.active == call->conn->sockets.tls) {
+ transport_protection = AUTHZ_TRANSPORT_PROTECTION_TLS;
+ }
+
+ log_successful_authz_event(call->conn->connection->msg_ctx,
+ call->conn->connection->lp_ctx,
+ call->conn->connection->remote_address,
+ call->conn->connection->local_address,
+ "LDAP",
+ "no bind",
+ transport_protection,
+ call->conn->session_info);
+
+ call->conn->authz_logged = true;
+ }
+ }
+
switch(call->request->type) {
case LDAP_TAG_BindRequest:
return ldapsrv_BindRequest(call);
case LDAP_TAG_SearchRequest:
return ldapsrv_SearchRequest(call);
case LDAP_TAG_ModifyRequest:
- return ldapsrv_ModifyRequest(call);
+ status = ldapsrv_ModifyRequest(call);
+ break;
case LDAP_TAG_AddRequest:
- return ldapsrv_AddRequest(call);
+ status = ldapsrv_AddRequest(call);
+ break;
case LDAP_TAG_DelRequest:
- return ldapsrv_DelRequest(call);
+ status = ldapsrv_DelRequest(call);
+ break;
case LDAP_TAG_ModifyDNRequest:
- return ldapsrv_ModifyDNRequest(call);
+ status = ldapsrv_ModifyDNRequest(call);
+ break;
case LDAP_TAG_CompareRequest:
return ldapsrv_CompareRequest(call);
case LDAP_TAG_AbandonRequest:
return ldapsrv_AbandonRequest(call);
case LDAP_TAG_ExtendedRequest:
- return ldapsrv_ExtendedRequest(call);
+ status = ldapsrv_ExtendedRequest(call);
+ break;
default:
return ldapsrv_unwilling(call, LDAP_PROTOCOL_ERROR);
}
+
+ if (NT_STATUS_IS_OK(status)) {
+ ldapsrv_notification_retry_setup(call->conn->service, true);
+ }
+
+ return status;
}