78fd43caac8ccfe5a3483d6ed2c9006b82313427
[metze/samba/wip.git] / source4 / ldap_server / ldap_extended.c
1 /* 
2    Unix SMB/CIFS implementation.
3    LDAP server
4    Copyright (C) Stefan Metzmacher 2004
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "ldap_server/ldap_server.h"
22 #include "../lib/util/dlinklist.h"
23 #include "lib/tls/tls.h"
24 #include "smbd/service_stream.h"
25
26 struct ldapsrv_starttls_context {
27         struct ldapsrv_connection *conn;
28         struct socket_context *tls_socket;
29 };
30
31 static void ldapsrv_start_tls(void *private_data)
32 {
33         struct ldapsrv_starttls_context *ctx = talloc_get_type(private_data, struct ldapsrv_starttls_context);
34         talloc_steal(ctx->conn->connection, ctx->tls_socket);
35
36         ctx->conn->sockets.tls = ctx->tls_socket;
37         ctx->conn->connection->socket = ctx->tls_socket;
38         packet_set_socket(ctx->conn->packet, ctx->conn->connection->socket);
39         packet_set_unreliable_select(ctx->conn->packet);
40 }
41
42 static NTSTATUS ldapsrv_StartTLS(struct ldapsrv_call *call,
43                                  struct ldapsrv_reply *reply,
44                                  const char **errstr)
45 {
46         struct ldapsrv_starttls_context *ctx;
47
48         (*errstr) = NULL;
49
50         /*
51          * TODO: give LDAP_OPERATIONS_ERROR also when
52          *       there're pending requests or there's
53          *       a SASL bind in progress
54          *       (see rfc4513 section 3.1.1)
55          */
56         if (call->conn->sockets.tls) {
57                 (*errstr) = talloc_asprintf(reply, "START-TLS: TLS is already enabled on this LDAP session");
58                 return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
59         }
60
61         ctx = talloc(call, struct ldapsrv_starttls_context);
62         NT_STATUS_HAVE_NO_MEMORY(ctx);
63
64         ctx->conn = call->conn;
65         ctx->tls_socket = tls_init_server(call->conn->service->tls_params,
66                                           call->conn->connection->socket,
67                                           call->conn->connection->event.fde, 
68                                           NULL);
69         if (!ctx->tls_socket) {
70                 (*errstr) = talloc_asprintf(reply, "START-TLS: Failed to setup TLS socket");
71                 return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
72         }
73
74         call->send_callback = ldapsrv_start_tls;
75         call->send_private  = ctx;
76
77         reply->msg->r.ExtendedResponse.response.resultcode = LDAP_SUCCESS;
78         reply->msg->r.ExtendedResponse.response.errormessage = NULL;
79
80         ldapsrv_queue_reply(call, reply);
81         return NT_STATUS_OK;
82 }
83
84 struct ldapsrv_extended_operation {
85         const char *oid;
86         NTSTATUS (*fn)(struct ldapsrv_call *call, struct ldapsrv_reply *reply, const char **errorstr);
87 };
88
89 static struct ldapsrv_extended_operation extended_ops[] = {
90         {
91                 .oid    = LDB_EXTENDED_START_TLS_OID,
92                 .fn     = ldapsrv_StartTLS,
93         },{
94                 .oid    = NULL,
95                 .fn     = NULL,
96         }
97 };
98
99 NTSTATUS ldapsrv_ExtendedRequest(struct ldapsrv_call *call)
100 {
101         struct ldap_ExtendedRequest *req = &call->request->r.ExtendedRequest;
102         struct ldapsrv_reply *reply;
103         int result = LDAP_PROTOCOL_ERROR;
104         const char *error_str = NULL;
105         NTSTATUS status = NT_STATUS_OK;
106         unsigned int i;
107
108         DEBUG(10, ("Extended\n"));
109
110         reply = ldapsrv_init_reply(call, LDAP_TAG_ExtendedResponse);
111         NT_STATUS_HAVE_NO_MEMORY(reply);
112  
113         ZERO_STRUCT(reply->msg->r);
114         reply->msg->r.ExtendedResponse.oid = talloc_steal(reply, req->oid);
115         reply->msg->r.ExtendedResponse.response.resultcode = LDAP_PROTOCOL_ERROR;
116         reply->msg->r.ExtendedResponse.response.errormessage = NULL;
117  
118         for (i=0; extended_ops[i].oid; i++) {
119                 if (strcmp(extended_ops[i].oid,req->oid) != 0) continue;
120  
121                 /* 
122                  * if the backend function returns an error we
123                  * need to send the reply otherwise the reply is already
124                  * send and we need to return directly
125                  */
126                 status = extended_ops[i].fn(call, reply, &error_str);
127                 NT_STATUS_IS_OK_RETURN(status);
128  
129                 if (NT_STATUS_IS_LDAP(status)) {
130                         result = NT_STATUS_LDAP_CODE(status);
131                 } else {
132                         result = LDAP_OPERATIONS_ERROR;
133                         error_str = talloc_asprintf(reply, "Extended Operation(%s) failed: %s",
134                                                     req->oid, nt_errstr(status));
135                 }
136         }
137         /* if we haven't found the oid, then status is still NT_STATUS_OK */
138         if (NT_STATUS_IS_OK(status)) {
139                 error_str = talloc_asprintf(reply, "Extended Operation(%s) not supported",
140                                             req->oid);
141         }
142  
143         reply->msg->r.ExtendedResponse.response.resultcode = result;
144         reply->msg->r.ExtendedResponse.response.errormessage = error_str;
145  
146         ldapsrv_queue_reply(call, reply);
147         return NT_STATUS_OK;
148 }