s4:librpc/rpc: convert dcerpc_bind_send/recv to tevent_req
[sfrench/samba-autobuild/.git] / source4 / librpc / rpc / dcerpc_auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Generic Authentication Interface
5
6    Copyright (C) Andrew Tridgell 2003
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
8    Copyright (C) Stefan Metzmacher 2004
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include <tevent.h>
26 #include "libcli/composite/composite.h"
27 #include "auth/gensec/gensec.h"
28 #include "librpc/rpc/dcerpc.h"
29 #include "librpc/rpc/dcerpc_proto.h"
30 #include "param/param.h"
31
32 /*
33   return the rpc syntax and transfer syntax given the pipe uuid and version
34 */
35 static NTSTATUS dcerpc_init_syntaxes(const struct ndr_interface_table *table,
36                                      uint32_t pipe_flags,
37                                      struct ndr_syntax_id *syntax,
38                                      struct ndr_syntax_id *transfer_syntax)
39 {
40         syntax->uuid = table->syntax_id.uuid;
41         syntax->if_version = table->syntax_id.if_version;
42
43         if (pipe_flags & DCERPC_NDR64) {
44                 *transfer_syntax = ndr64_transfer_syntax;
45         } else {
46                 *transfer_syntax = ndr_transfer_syntax;
47         }
48
49         return NT_STATUS_OK;
50 }
51
52
53 /*
54   Send request to do a non-authenticated dcerpc bind
55 */
56 static void dcerpc_bind_auth_none_done(struct tevent_req *subreq);
57
58 struct composite_context *dcerpc_bind_auth_none_send(TALLOC_CTX *mem_ctx,
59                                                      struct dcerpc_pipe *p,
60                                                      const struct ndr_interface_table *table)
61 {
62         struct ndr_syntax_id syntax;
63         struct ndr_syntax_id transfer_syntax;
64
65         struct composite_context *c;
66         struct tevent_req *subreq;
67
68         c = composite_create(mem_ctx, p->conn->event_ctx);
69         if (c == NULL) return NULL;
70
71         c->status = dcerpc_init_syntaxes(table, p->conn->flags,
72                                          &syntax, &transfer_syntax);
73         if (!NT_STATUS_IS_OK(c->status)) {
74                 DEBUG(2,("Invalid uuid string in "
75                          "dcerpc_bind_auth_none_send\n"));
76                 composite_error(c, c->status);
77                 return c;
78         }
79
80         subreq = dcerpc_bind_send(mem_ctx, p->conn->event_ctx, p,
81                                   &syntax, &transfer_syntax);
82         if (composite_nomem(subreq, c)) return c;
83         tevent_req_set_callback(subreq, dcerpc_bind_auth_none_done, c);
84
85         return c;
86 }
87
88 static void dcerpc_bind_auth_none_done(struct tevent_req *subreq)
89 {
90         struct composite_context *ctx =
91                 tevent_req_callback_data(subreq,
92                 struct composite_context);
93
94         ctx->status = dcerpc_bind_recv(subreq);
95         TALLOC_FREE(subreq);
96         if (!composite_is_ok(ctx)) return;
97
98         composite_done(ctx);
99 }
100
101 /*
102   Receive result of a non-authenticated dcerpc bind
103 */
104 NTSTATUS dcerpc_bind_auth_none_recv(struct composite_context *ctx)
105 {
106         NTSTATUS result = composite_wait(ctx);
107         TALLOC_FREE(ctx);
108         return result;
109 }
110
111
112 /*
113   Perform sync non-authenticated dcerpc bind
114 */
115 _PUBLIC_ NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
116                                const struct ndr_interface_table *table)
117 {
118         struct composite_context *ctx;
119
120         ctx = dcerpc_bind_auth_none_send(p, p, table);
121         return dcerpc_bind_auth_none_recv(ctx);
122 }
123
124
125 struct bind_auth_state {
126         struct dcerpc_pipe *pipe;
127         DATA_BLOB credentials;
128         bool more_processing;   /* Is there anything more to do after the
129                                  * first bind itself received? */
130 };
131
132 static void bind_auth_recv_alter(struct composite_context *creq);
133
134 static void bind_auth_next_step(struct composite_context *c)
135 {
136         struct bind_auth_state *state;
137         struct dcecli_security *sec;
138         struct composite_context *creq;
139         bool more_processing = false;
140
141         state = talloc_get_type(c->private_data, struct bind_auth_state);
142         sec = &state->pipe->conn->security_state;
143
144         /* The status value here, from GENSEC is vital to the security
145          * of the system.  Even if the other end accepts, if GENSEC
146          * claims 'MORE_PROCESSING_REQUIRED' then you must keep
147          * feeding it blobs, or else the remote host/attacker might
148          * avoid mutal authentication requirements.
149          *
150          * Likewise, you must not feed GENSEC too much (after the OK),
151          * it doesn't like that either
152          */
153
154         c->status = gensec_update(sec->generic_state, state,
155                                   state->pipe->conn->event_ctx,
156                                   sec->auth_info->credentials,
157                                   &state->credentials);
158         data_blob_free(&sec->auth_info->credentials);
159
160         if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
161                 more_processing = true;
162                 c->status = NT_STATUS_OK;
163         }
164
165         if (!composite_is_ok(c)) return;
166
167         if (state->pipe->conn->flags & DCERPC_HEADER_SIGNING) {
168                 gensec_want_feature(sec->generic_state, GENSEC_FEATURE_SIGN_PKT_HEADER);
169         }
170
171         if (state->credentials.length == 0) {
172                 composite_done(c);
173                 return;
174         }
175
176         sec->auth_info->credentials = state->credentials;
177
178         if (!more_processing) {
179                 /* NO reply expected, so just send it */
180                 c->status = dcerpc_auth3(state->pipe, state);
181                 data_blob_free(&state->credentials);
182                 sec->auth_info->credentials = data_blob(NULL, 0);
183                 if (!composite_is_ok(c)) return;
184
185                 composite_done(c);
186                 return;
187         }
188
189         /* We are demanding a reply, so use a request that will get us one */
190
191         creq = dcerpc_alter_context_send(state->pipe, state,
192                                          &state->pipe->syntax,
193                                          &state->pipe->transfer_syntax);
194         data_blob_free(&state->credentials);
195         sec->auth_info->credentials = data_blob(NULL, 0);
196         if (composite_nomem(creq, c)) return;
197
198         composite_continue(c, creq, bind_auth_recv_alter, c);
199 }
200
201
202 static void bind_auth_recv_alter(struct composite_context *creq)
203 {
204         struct composite_context *c = talloc_get_type(creq->async.private_data,
205                                                       struct composite_context);
206
207         c->status = dcerpc_alter_context_recv(creq);
208         if (!composite_is_ok(c)) return;
209
210         bind_auth_next_step(c);
211 }
212
213
214 static void bind_auth_recv_bindreply(struct tevent_req *subreq)
215 {
216         struct composite_context *c =
217                 tevent_req_callback_data(subreq,
218                 struct composite_context);
219         struct bind_auth_state *state = talloc_get_type(c->private_data,
220                                                         struct bind_auth_state);
221
222         c->status = dcerpc_bind_recv(subreq);
223         TALLOC_FREE(subreq);
224         if (!composite_is_ok(c)) return;
225
226         if (!state->more_processing) {
227                 /* The first gensec_update has not requested a second run, so
228                  * we're done here. */
229                 composite_done(c);
230                 return;
231         }
232
233         bind_auth_next_step(c);
234 }
235
236
237 /**
238    Bind to a DCE/RPC pipe, send async request
239    @param mem_ctx TALLOC_CTX for the allocation of the composite_context
240    @param p The dcerpc_pipe to bind (must already be connected)
241    @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
242    @param credentials The credentials of the account to connect with 
243    @param auth_type Select the authentication scheme to use
244    @param auth_level Chooses between unprotected (connect), signed or sealed
245    @param service The service (used by Kerberos to select the service principal to contact)
246    @retval A composite context describing the partial state of the bind
247 */
248
249 struct composite_context *dcerpc_bind_auth_send(TALLOC_CTX *mem_ctx,
250                                                 struct dcerpc_pipe *p,
251                                                 const struct ndr_interface_table *table,
252                                                 struct cli_credentials *credentials,
253                                                 struct gensec_settings *gensec_settings,
254                                                 uint8_t auth_type, uint8_t auth_level,
255                                                 const char *service)
256 {
257         struct composite_context *c;
258         struct bind_auth_state *state;
259         struct dcecli_security *sec;
260         struct tevent_req *subreq;
261
262         struct ndr_syntax_id syntax, transfer_syntax;
263
264         /* composite context allocation and setup */
265         c = composite_create(mem_ctx, p->conn->event_ctx);
266         if (c == NULL) return NULL;
267
268         state = talloc(c, struct bind_auth_state);
269         if (composite_nomem(state, c)) return c;
270         c->private_data = state;
271
272         state->pipe = p;
273
274         c->status = dcerpc_init_syntaxes(table, p->conn->flags,
275                                          &syntax,
276                                          &transfer_syntax);
277         if (!composite_is_ok(c)) return c;
278
279         sec = &p->conn->security_state;
280
281         c->status = gensec_client_start(p, &sec->generic_state,
282                                         gensec_settings);
283         if (!NT_STATUS_IS_OK(c->status)) {
284                 DEBUG(1, ("Failed to start GENSEC client mode: %s\n",
285                           nt_errstr(c->status)));
286                 composite_error(c, c->status);
287                 return c;
288         }
289
290         c->status = gensec_set_credentials(sec->generic_state, credentials);
291         if (!NT_STATUS_IS_OK(c->status)) {
292                 DEBUG(1, ("Failed to set GENSEC client credentials: %s\n",
293                           nt_errstr(c->status)));
294                 composite_error(c, c->status);
295                 return c;
296         }
297
298         c->status = gensec_set_target_hostname(sec->generic_state,
299                                                p->conn->transport.target_hostname(p->conn));
300         if (!NT_STATUS_IS_OK(c->status)) {
301                 DEBUG(1, ("Failed to set GENSEC target hostname: %s\n", 
302                           nt_errstr(c->status)));
303                 composite_error(c, c->status);
304                 return c;
305         }
306
307         if (service != NULL) {
308                 c->status = gensec_set_target_service(sec->generic_state,
309                                                       service);
310                 if (!NT_STATUS_IS_OK(c->status)) {
311                         DEBUG(1, ("Failed to set GENSEC target service: %s\n",
312                                   nt_errstr(c->status)));
313                         composite_error(c, c->status);
314                         return c;
315                 }
316         }
317
318         if (p->binding && p->binding->target_principal) {
319                 c->status = gensec_set_target_principal(sec->generic_state,
320                                                         p->binding->target_principal);
321                 if (!NT_STATUS_IS_OK(c->status)) {
322                         DEBUG(1, ("Failed to set GENSEC target principal to %s: %s\n",
323                                   p->binding->target_principal, nt_errstr(c->status)));
324                         composite_error(c, c->status);
325                         return c;
326                 }
327         }
328
329         c->status = gensec_start_mech_by_authtype(sec->generic_state,
330                                                   auth_type, auth_level);
331         if (!NT_STATUS_IS_OK(c->status)) {
332                 DEBUG(1, ("Failed to start GENSEC client mechanism %s: %s\n",
333                           gensec_get_name_by_authtype(sec->generic_state, auth_type),
334                           nt_errstr(c->status)));
335                 composite_error(c, c->status);
336                 return c;
337         }
338
339         sec->auth_info = talloc(p, struct dcerpc_auth);
340         if (composite_nomem(sec->auth_info, c)) return c;
341
342         sec->auth_info->auth_type = auth_type;
343         sec->auth_info->auth_level = auth_level,
344         sec->auth_info->auth_pad_length = 0;
345         sec->auth_info->auth_reserved = 0;
346         sec->auth_info->auth_context_id = random();
347         sec->auth_info->credentials = data_blob(NULL, 0);
348
349         /* The status value here, from GENSEC is vital to the security
350          * of the system.  Even if the other end accepts, if GENSEC
351          * claims 'MORE_PROCESSING_REQUIRED' then you must keep
352          * feeding it blobs, or else the remote host/attacker might
353          * avoid mutal authentication requirements.
354          *
355          * Likewise, you must not feed GENSEC too much (after the OK),
356          * it doesn't like that either
357          */
358
359         c->status = gensec_update(sec->generic_state, state,
360                                   p->conn->event_ctx,
361                                   sec->auth_info->credentials,
362                                   &state->credentials);
363         if (!NT_STATUS_IS_OK(c->status) &&
364             !NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
365                 composite_error(c, c->status);
366                 return c;
367         }
368
369         state->more_processing = NT_STATUS_EQUAL(c->status,
370                                                  NT_STATUS_MORE_PROCESSING_REQUIRED);
371
372         if (state->credentials.length == 0) {
373                 composite_done(c);
374                 return c;
375         }
376
377         sec->auth_info->credentials = state->credentials;
378
379         /* The first request always is a dcerpc_bind. The subsequent ones
380          * depend on gensec results */
381         subreq = dcerpc_bind_send(state, p->conn->event_ctx, p,
382                                   &syntax, &transfer_syntax);
383         data_blob_free(&state->credentials);
384         sec->auth_info->credentials = data_blob(NULL, 0);
385         if (composite_nomem(subreq, c)) return c;
386         tevent_req_set_callback(subreq, bind_auth_recv_bindreply, c);
387
388         return c;
389 }
390
391
392 /**
393    Bind to a DCE/RPC pipe, receive result
394    @param creq A composite context describing state of async call
395    @retval NTSTATUS code
396 */
397
398 NTSTATUS dcerpc_bind_auth_recv(struct composite_context *creq)
399 {
400         NTSTATUS result = composite_wait(creq);
401         struct bind_auth_state *state = talloc_get_type(creq->private_data,
402                                                         struct bind_auth_state);
403
404         if (NT_STATUS_IS_OK(result)) {
405                 /*
406                   after a successful authenticated bind the session
407                   key reverts to the generic session key
408                 */
409                 state->pipe->conn->security_state.session_key = dcerpc_generic_session_key;
410         }
411         
412         talloc_free(creq);
413         return result;
414 }
415
416
417 /**
418    Perform a GENSEC authenticated bind to a DCE/RPC pipe, sync
419    @param p The dcerpc_pipe to bind (must already be connected)
420    @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
421    @param credentials The credentials of the account to connect with 
422    @param auth_type Select the authentication scheme to use
423    @param auth_level Chooses between unprotected (connect), signed or sealed
424    @param service The service (used by Kerberos to select the service principal to contact)
425    @retval NTSTATUS status code
426 */
427
428 _PUBLIC_ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p,
429                           const struct ndr_interface_table *table,
430                           struct cli_credentials *credentials,
431                           struct gensec_settings *gensec_settings,
432                           uint8_t auth_type, uint8_t auth_level,
433                           const char *service)
434 {
435         struct composite_context *creq;
436         creq = dcerpc_bind_auth_send(p, p, table, credentials, gensec_settings,
437                                      auth_type, auth_level, service);
438         return dcerpc_bind_auth_recv(creq);
439 }