s4:libcli/ldap Rename ldap.h to libcli_ldap.h
[garming/samba-autobuild/.git] / source4 / ldap_server / ldap_bind.c
index beaf3da46cb0fdb33d58ca4562499ef61d2a7cb6..1b235f2a1bdacb5f67abd2eb5b59f2efe83df369 100644 (file)
@@ -5,7 +5,7 @@
    
    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 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #include "ldap_server/ldap_server.h"
 #include "auth/auth.h"
-#include "smbd/service_stream.h"
+#include "smbd/service.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
 #include "dsdb/samdb/samdb.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
 
 static NTSTATUS ldapsrv_BindSimple(struct ldapsrv_call *call)
 {
@@ -40,35 +43,38 @@ static NTSTATUS ldapsrv_BindSimple(struct ldapsrv_call *call)
 
        DEBUG(10, ("BindSimple dn: %s\n",req->dn));
 
-       status = crack_dn_to_nt4_name(call, req->dn, &nt4_domain, &nt4_account);
+       status = crack_auto_name_to_nt4_name(call, call->conn->connection->event.ctx, call->conn->lp_ctx, req->dn, &nt4_domain, &nt4_account);
        if (NT_STATUS_IS_OK(status)) {
-               status = authenticate_username_pw(call, nt4_domain, nt4_account, 
-                                                 req->creds.password, &session_info);
+               status = authenticate_username_pw(call,
+                                                 call->conn->connection->event.ctx,
+                                                 call->conn->connection->msg_ctx,
+                                                 call->conn->lp_ctx,
+                                                 nt4_domain, nt4_account, 
+                                                 req->creds.password,
+                                                 &session_info);
        }
 
-       /* When we add authentication here, we also need to handle telling the backends */
-
        reply = ldapsrv_init_reply(call, LDAP_TAG_BindResponse);
        if (!reply) {
                return NT_STATUS_NO_MEMORY;
        }
 
        if (NT_STATUS_IS_OK(status)) {
-               struct ldapsrv_partition *part;
                result = LDAP_SUCCESS;
                errstr = NULL;
 
                talloc_free(call->conn->session_info);
                call->conn->session_info = session_info;
-               for (part = call->conn->partitions; part; part = part->next) {
-                       if (!part->ops->Bind) {
-                               continue;
-                       }
-                       status = part->ops->Bind(part, call->conn);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               result = LDAP_OPERATIONS_ERROR;
-                               errstr = talloc_asprintf(reply, "Simple Bind: Failed to advise partition %s of new credentials: %s", part->base_dn, nt_errstr(status));
-                       }
+               talloc_steal(call->conn, session_info);
+
+               /* don't leak the old LDB */
+               talloc_free(call->conn->ldb);
+
+               status = ldapsrv_backend_Init(call->conn);              
+               
+               if (!NT_STATUS_IS_OK(status)) {
+                       result = LDAP_OPERATIONS_ERROR;
+                       errstr = talloc_asprintf(reply, "Simple Bind: Failed to advise ldb new credentials: %s", nt_errstr(status));
                }
        } else {
                status = auth_nt_status_squash(status);
@@ -82,117 +88,175 @@ static NTSTATUS ldapsrv_BindSimple(struct ldapsrv_call *call)
        resp->response.errormessage = errstr;
        resp->response.dn = NULL;
        resp->response.referral = NULL;
-
-       /* This looks wrong... */
-       resp->SASL.secblob = data_blob(NULL, 0);
+       resp->SASL.secblob = NULL;
 
        ldapsrv_queue_reply(call, reply);
        return NT_STATUS_OK;
 }
 
+struct ldapsrv_sasl_context {
+       struct ldapsrv_connection *conn;
+       struct socket_context *sasl_socket;
+};
+
+static void ldapsrv_set_sasl(void *private_data)
+{
+       struct ldapsrv_sasl_context *ctx = talloc_get_type(private_data, struct ldapsrv_sasl_context);
+       talloc_steal(ctx->conn->connection, ctx->sasl_socket);
+       talloc_unlink(ctx->conn->connection, ctx->conn->connection->socket);
+
+       ctx->conn->sockets.sasl = ctx->sasl_socket;
+       ctx->conn->connection->socket = ctx->sasl_socket;
+       packet_set_socket(ctx->conn->packet, ctx->conn->connection->socket);
+}
+
 static NTSTATUS ldapsrv_BindSASL(struct ldapsrv_call *call)
 {
        struct ldap_BindRequest *req = &call->request->r.BindRequest;
        struct ldapsrv_reply *reply;
        struct ldap_BindResponse *resp;
        struct ldapsrv_connection *conn;
-       int result;
-       const char *errstr;
+       int result = 0;
+       const char *errstr=NULL;
        NTSTATUS status = NT_STATUS_OK;
 
        DEBUG(10, ("BindSASL dn: %s\n",req->dn));
 
-       if (!call->conn->gensec) {
-               struct cli_credentials *server_credentials;
-               call->conn->session_info = NULL;
+       reply = ldapsrv_init_reply(call, LDAP_TAG_BindResponse);
+       if (!reply) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       resp = &reply->msg->r.BindResponse;
+       
+       conn = call->conn;
 
-               status = gensec_server_start(call->conn, &call->conn->gensec,
-                                            call->conn->connection->event.ctx);
+       /* 
+        * TODO: a SASL bind with a different mechanism
+        *       should cancel an inprogress SASL bind.
+        *       (see RFC 4513)
+        */
+
+       if (!conn->gensec) {
+               conn->session_info = NULL;
+
+               status = samba_server_gensec_start(conn,
+                                                  conn->connection->event.ctx,
+                                                  conn->connection->msg_ctx,
+                                                  conn->lp_ctx,
+                                                  conn->server_credentials,
+                                                  "ldap",
+                                                  &conn->gensec);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
-                       return status;
-               }
-               
-               gensec_set_target_service(call->conn->gensec, "ldap");
-
-               server_credentials 
-                       = cli_credentials_init(call);
-               if (!server_credentials) {
-                       DEBUG(1, ("Failed to init server credentials\n"));
-                       return NT_STATUS_NO_MEMORY;
-               }
+                       result = LDAP_OPERATIONS_ERROR;
+                       errstr = talloc_asprintf(reply, "SASL: Failed to start authentication system: %s", 
+                                                nt_errstr(status));
+               } else {
                
-               cli_credentials_set_conf(server_credentials);
-               status = cli_credentials_set_machine_account(server_credentials);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(status)));
-                       talloc_free(server_credentials);
-                       server_credentials = NULL;
+                       gensec_want_feature(conn->gensec, GENSEC_FEATURE_SIGN);
+                       gensec_want_feature(conn->gensec, GENSEC_FEATURE_SEAL);
+                       gensec_want_feature(conn->gensec, GENSEC_FEATURE_ASYNC_REPLIES);
+                       
+                       status = gensec_start_mech_by_sasl_name(conn->gensec, req->creds.SASL.mechanism);
+                       
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(1, ("Failed to start GENSEC SASL[%s] server code: %s\n", 
+                                         req->creds.SASL.mechanism, nt_errstr(status)));
+                               result = LDAP_OPERATIONS_ERROR;
+                               errstr = talloc_asprintf(reply, "SASL:[%s]: Failed to start authentication backend: %s", 
+                                                        req->creds.SASL.mechanism, nt_errstr(status));
+                       }
                }
-               
-               gensec_set_credentials(call->conn->gensec, server_credentials);
+       }
 
-               gensec_want_feature(call->conn->gensec, GENSEC_FEATURE_SIGN);
-               gensec_want_feature(call->conn->gensec, GENSEC_FEATURE_SEAL);
-               gensec_want_feature(call->conn->gensec, GENSEC_FEATURE_ASYNC_REPLIES);
+       if (NT_STATUS_IS_OK(status)) {
+               DATA_BLOB input = data_blob(NULL, 0);
+               DATA_BLOB output = data_blob(NULL, 0);
 
-               status = gensec_start_mech_by_sasl_name(call->conn->gensec, req->creds.SASL.mechanism);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(1, ("Failed to start GENSEC SASL[%s] server code: %s\n", 
-                               req->creds.SASL.mechanism, nt_errstr(status)));
+               if (req->creds.SASL.secblob) {
+                       input = *req->creds.SASL.secblob;
                }
-       }
 
-       reply = ldapsrv_init_reply(call, LDAP_TAG_BindResponse);
-       if (!reply) {
-               return NT_STATUS_NO_MEMORY;
-       }
-       resp = &reply->msg->r.BindResponse;
-       
-       conn = call->conn;
+               status = gensec_update(conn->gensec, reply,
+                                      input, &output);
 
-       if (NT_STATUS_IS_OK(status)) {
-               status = gensec_update(call->conn->gensec, reply,
-                                      req->creds.SASL.secblob, &resp->SASL.secblob);
+               /* Windows 2000 mmc doesn't like secblob == NULL and reports a decoding error */
+               resp->SASL.secblob = talloc(reply, DATA_BLOB);
+               NT_STATUS_HAVE_NO_MEMORY(resp->SASL.secblob);
+               *resp->SASL.secblob = output;
+       } else {
+               resp->SASL.secblob = NULL;
        }
 
        if (NT_STATUS_EQUAL(NT_STATUS_MORE_PROCESSING_REQUIRED, status)) {
                result = LDAP_SASL_BIND_IN_PROGRESS;
                errstr = NULL;
        } else if (NT_STATUS_IS_OK(status)) {
-               struct ldapsrv_partition *part;
-               struct auth_session_info *old_session_info;
+               struct auth_session_info *old_session_info=NULL;
+               struct ldapsrv_sasl_context *ctx;
 
                result = LDAP_SUCCESS;
                errstr = NULL;
-               if (gensec_have_feature(call->conn->gensec, GENSEC_FEATURE_SEAL) ||
-                   gensec_have_feature(call->conn->gensec, GENSEC_FEATURE_SIGN)) {
-                       call->conn->enable_wrap = True;
-               }
-               old_session_info = call->conn->session_info;
-               call->conn->session_info = NULL;
-               status = gensec_session_info(call->conn->gensec, &call->conn->session_info);
-               if (!NT_STATUS_IS_OK(status)) {
-                       call->conn->session_info = old_session_info;
+
+               ctx = talloc(call, struct ldapsrv_sasl_context); 
+
+               if (!ctx) {
+                       status = NT_STATUS_NO_MEMORY;
+               } else {
+                       ctx->conn = conn;
+                       status = gensec_socket_init(conn->gensec, 
+                                                   conn->connection,
+                                                   conn->connection->socket,
+                                                   conn->connection->event.ctx, 
+                                                   stream_io_handler_callback,
+                                                   conn->connection,
+                                                   &ctx->sasl_socket);
+               } 
+
+               if (!ctx || !NT_STATUS_IS_OK(status)) {
+                       conn->session_info = old_session_info;
                        result = LDAP_OPERATIONS_ERROR;
-                       errstr = talloc_asprintf(reply, "SASL:[%s]: Failed to get session info: %s", req->creds.SASL.mechanism, nt_errstr(status));
+                       errstr = talloc_asprintf(reply, 
+                                                "SASL:[%s]: Failed to setup SASL socket: %s", 
+                                                req->creds.SASL.mechanism, nt_errstr(status));
                } else {
-                       talloc_free(old_session_info);
-                       for (part = call->conn->partitions; part; part = part->next) {
-                               if (!part->ops->Bind) {
-                                       continue;
-                               }
-                               status = part->ops->Bind(part, conn);
+
+                       call->send_callback = ldapsrv_set_sasl;
+                       call->send_private = ctx;
+               
+                       old_session_info = conn->session_info;
+                       conn->session_info = NULL;
+                       status = gensec_session_info(conn->gensec, &conn->session_info);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               conn->session_info = old_session_info;
+                               result = LDAP_OPERATIONS_ERROR;
+                               errstr = talloc_asprintf(reply, 
+                                                        "SASL:[%s]: Failed to get session info: %s", 
+                                                        req->creds.SASL.mechanism, nt_errstr(status));
+                       } else {
+                               talloc_free(old_session_info);
+                               talloc_steal(conn, conn->session_info);
+                               
+                               /* don't leak the old LDB */
+                               talloc_free(conn->ldb);
+                               
+                               status = ldapsrv_backend_Init(conn);            
+                               
                                if (!NT_STATUS_IS_OK(status)) {
                                        result = LDAP_OPERATIONS_ERROR;
-                                       errstr = talloc_asprintf(reply, "SASL:[%s]: Failed to advise partition %s of new credentials: %s", req->creds.SASL.mechanism, part->base_dn, nt_errstr(status));
+                                       errstr = talloc_asprintf(reply, 
+                                                                "SASL:[%s]: Failed to advise samdb of new credentials: %s", 
+                                                                req->creds.SASL.mechanism, 
+                                                                nt_errstr(status));
                                }
                        }
                }
        } else {
                status = auth_nt_status_squash(status);
-               result = LDAP_INVALID_CREDENTIALS;
-               errstr = talloc_asprintf(reply, "SASL:[%s]: %s", req->creds.SASL.mechanism, nt_errstr(status));
+               if (result == 0) {
+                       result = LDAP_INVALID_CREDENTIALS;
+                       errstr = talloc_asprintf(reply, "SASL:[%s]: %s", req->creds.SASL.mechanism, nt_errstr(status));
+               }
        }
 
        resp->response.resultcode = result;
@@ -210,6 +274,14 @@ NTSTATUS ldapsrv_BindRequest(struct ldapsrv_call *call)
        struct ldapsrv_reply *reply;
        struct ldap_BindResponse *resp;
 
+       /* 
+        * TODO: we should fail the bind request
+        *       if there're any pending requests.
+        *
+        *       also a simple bind should cancel an
+        *       inprogress SASL bind.
+        *       (see RFC 4513)
+        */
        switch (req->mechanism) {
                case LDAP_AUTH_MECH_SIMPLE:
                        return ldapsrv_BindSimple(call);
@@ -227,7 +299,7 @@ NTSTATUS ldapsrv_BindRequest(struct ldapsrv_call *call)
        resp->response.dn = NULL;
        resp->response.errormessage = talloc_asprintf(reply, "Bad AuthenticationChoice [%d]", req->mechanism);
        resp->response.referral = NULL;
-       resp->SASL.secblob = data_blob(NULL, 0);
+       resp->SASL.secblob = NULL;
 
        ldapsrv_queue_reply(call, reply);
        return NT_STATUS_OK;