r24532: rename struct dcerpc_syntax_id into struct ndr_syntax_id
[nivanova/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 "libcli/composite/composite.h"
26 #include "auth/gensec/gensec.h"
27 #include "librpc/rpc/dcerpc.h"
28
29 /*
30   return the rpc syntax and transfer syntax given the pipe uuid and version
31 */
32 static NTSTATUS dcerpc_init_syntaxes(const struct dcerpc_interface_table *table,
33                               struct ndr_syntax_id *syntax,
34                               struct ndr_syntax_id *transfer_syntax)
35 {
36         syntax->uuid = table->syntax_id.uuid;
37         syntax->if_version = table->syntax_id.if_version;
38
39         *transfer_syntax = ndr_transfer_syntax;
40
41         return NT_STATUS_OK;
42 }
43
44
45 /*
46   Send request to do a non-authenticated dcerpc bind
47 */
48 struct composite_context *dcerpc_bind_auth_none_send(TALLOC_CTX *mem_ctx,
49                                                      struct dcerpc_pipe *p,
50                                                      const struct dcerpc_interface_table *table)
51 {
52         struct ndr_syntax_id syntax;
53         struct ndr_syntax_id transfer_syntax;
54
55         struct composite_context *c;
56
57         c = composite_create(mem_ctx, p->conn->event_ctx);
58         if (c == NULL) return NULL;
59
60         c->status = dcerpc_init_syntaxes(table,
61                                          &syntax, &transfer_syntax);
62         if (!NT_STATUS_IS_OK(c->status)) {
63                 DEBUG(2,("Invalid uuid string in "
64                          "dcerpc_bind_auth_none_send\n"));
65                 composite_error(c, c->status);
66                 return c;
67         }
68
69         /* c was only allocated as a container for a possible error */
70         talloc_free(c);
71
72         return dcerpc_bind_send(p, mem_ctx, &syntax, &transfer_syntax);
73 }
74
75
76 /*
77   Receive result of a non-authenticated dcerpc bind
78 */
79 NTSTATUS dcerpc_bind_auth_none_recv(struct composite_context *ctx)
80 {
81         return dcerpc_bind_recv(ctx);
82 }
83
84
85 /*
86   Perform sync non-authenticated dcerpc bind
87 */
88 NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
89                                const struct dcerpc_interface_table *table)
90 {
91         struct composite_context *ctx;
92
93         ctx = dcerpc_bind_auth_none_send(p, p, table);
94         return dcerpc_bind_auth_none_recv(ctx);
95 }
96
97
98 struct bind_auth_state {
99         struct dcerpc_pipe *pipe;
100         DATA_BLOB credentials;
101         BOOL more_processing;   /* Is there anything more to do after the
102                                  * first bind itself received? */
103 };
104
105 static void bind_auth_recv_alter(struct composite_context *creq);
106
107 static void bind_auth_next_step(struct composite_context *c)
108 {
109         struct bind_auth_state *state;
110         struct dcerpc_security *sec;
111         struct composite_context *creq;
112         BOOL more_processing = False;
113
114         state = talloc_get_type(c->private_data, struct bind_auth_state);
115         sec = &state->pipe->conn->security_state;
116
117         /* The status value here, from GENSEC is vital to the security
118          * of the system.  Even if the other end accepts, if GENSEC
119          * claims 'MORE_PROCESSING_REQUIRED' then you must keep
120          * feeding it blobs, or else the remote host/attacker might
121          * avoid mutal authentication requirements.
122          *
123          * Likewise, you must not feed GENSEC too much (after the OK),
124          * it doesn't like that either
125          */
126
127         c->status = gensec_update(sec->generic_state, state,
128                                   sec->auth_info->credentials,
129                                   &state->credentials);
130
131         if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
132                 more_processing = True;
133                 c->status = NT_STATUS_OK;
134         }
135
136         if (!composite_is_ok(c)) return;
137
138         if (state->credentials.length == 0) {
139                 composite_done(c);
140                 return;
141         }
142
143         sec->auth_info->credentials = state->credentials;
144
145         if (!more_processing) {
146                 /* NO reply expected, so just send it */
147                 c->status = dcerpc_auth3(state->pipe->conn, state);
148                 if (!composite_is_ok(c)) return;
149
150                 composite_done(c);
151                 return;
152         }
153
154         /* We are demanding a reply, so use a request that will get us one */
155
156         creq = dcerpc_alter_context_send(state->pipe, state,
157                                          &state->pipe->syntax,
158                                          &state->pipe->transfer_syntax);
159         if (composite_nomem(creq, c)) return;
160
161         composite_continue(c, creq, bind_auth_recv_alter, c);
162 }
163
164
165 static void bind_auth_recv_alter(struct composite_context *creq)
166 {
167         struct composite_context *c = talloc_get_type(creq->async.private_data,
168                                                       struct composite_context);
169
170         c->status = dcerpc_alter_context_recv(creq);
171         if (!composite_is_ok(c)) return;
172
173         bind_auth_next_step(c);
174 }
175
176
177 static void bind_auth_recv_bindreply(struct composite_context *creq)
178 {
179         struct composite_context *c = talloc_get_type(creq->async.private_data,
180                                                       struct composite_context);
181         struct bind_auth_state *state = talloc_get_type(c->private_data,
182                                                         struct bind_auth_state);
183
184         c->status = dcerpc_bind_recv(creq);
185         if (!composite_is_ok(c)) return;
186
187         if (!state->more_processing) {
188                 /* The first gensec_update has not requested a second run, so
189                  * we're done here. */
190                 composite_done(c);
191                 return;
192         }
193
194         bind_auth_next_step(c);
195 }
196
197
198 /**
199    Bind to a DCE/RPC pipe, send async request
200    @param mem_ctx TALLOC_CTX for the allocation of the composite_context
201    @param p The dcerpc_pipe to bind (must already be connected)
202    @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
203    @param credentials The credentials of the account to connect with 
204    @param auth_type Select the authentication scheme to use
205    @param auth_level Chooses between unprotected (connect), signed or sealed
206    @param service The service (used by Kerberos to select the service principal to contact)
207    @retval A composite context describing the partial state of the bind
208 */
209
210 struct composite_context *dcerpc_bind_auth_send(TALLOC_CTX *mem_ctx,
211                                                 struct dcerpc_pipe *p,
212                                                 const struct dcerpc_interface_table *table,
213                                                 struct cli_credentials *credentials,
214                                                 uint8_t auth_type, uint8_t auth_level,
215                                                 const char *service)
216 {
217         struct composite_context *c, *creq;
218         struct bind_auth_state *state;
219         struct dcerpc_security *sec;
220
221         struct ndr_syntax_id syntax, transfer_syntax;
222
223         /* composite context allocation and setup */
224         c = composite_create(mem_ctx, p->conn->event_ctx);
225         if (c == NULL) return NULL;
226
227         state = talloc(c, struct bind_auth_state);
228         if (composite_nomem(state, c)) return c;
229         c->private_data = state;
230
231         state->pipe = p;
232
233         c->status = dcerpc_init_syntaxes(table,
234                                          &syntax,
235                                          &transfer_syntax);
236         if (!composite_is_ok(c)) return c;
237
238         sec = &p->conn->security_state;
239
240         c->status = gensec_client_start(p, &sec->generic_state,
241                                         p->conn->event_ctx);
242         if (!NT_STATUS_IS_OK(c->status)) {
243                 DEBUG(1, ("Failed to start GENSEC client mode: %s\n",
244                           nt_errstr(c->status)));
245                 composite_error(c, c->status);
246                 return c;
247         }
248
249         c->status = gensec_set_credentials(sec->generic_state, credentials);
250         if (!NT_STATUS_IS_OK(c->status)) {
251                 DEBUG(1, ("Failed to set GENSEC client credentails: %s\n",
252                           nt_errstr(c->status)));
253                 composite_error(c, c->status);
254                 return c;
255         }
256
257         c->status = gensec_set_target_hostname(sec->generic_state,
258                                                p->conn->transport.target_hostname(p->conn));
259         if (!NT_STATUS_IS_OK(c->status)) {
260                 DEBUG(1, ("Failed to set GENSEC target hostname: %s\n", 
261                           nt_errstr(c->status)));
262                 composite_error(c, c->status);
263                 return c;
264         }
265
266         if (service != NULL) {
267                 c->status = gensec_set_target_service(sec->generic_state,
268                                                       service);
269                 if (!NT_STATUS_IS_OK(c->status)) {
270                         DEBUG(1, ("Failed to set GENSEC target service: %s\n",
271                                   nt_errstr(c->status)));
272                         composite_error(c, c->status);
273                         return c;
274                 }
275         }
276
277         c->status = gensec_start_mech_by_authtype(sec->generic_state,
278                                                   auth_type, auth_level);
279         if (!NT_STATUS_IS_OK(c->status)) {
280                 DEBUG(1, ("Failed to start GENSEC client mechanism %s: %s\n",
281                           gensec_get_name_by_authtype(auth_type),
282                           nt_errstr(c->status)));
283                 composite_error(c, c->status);
284                 return c;
285         }
286
287         sec->auth_info = talloc(p, struct dcerpc_auth);
288         if (composite_nomem(sec->auth_info, c)) return c;
289
290         sec->auth_info->auth_type = auth_type;
291         sec->auth_info->auth_level = auth_level,
292         sec->auth_info->auth_pad_length = 0;
293         sec->auth_info->auth_reserved = 0;
294         sec->auth_info->auth_context_id = random();
295         sec->auth_info->credentials = data_blob(NULL, 0);
296
297         /* The status value here, from GENSEC is vital to the security
298          * of the system.  Even if the other end accepts, if GENSEC
299          * claims 'MORE_PROCESSING_REQUIRED' then you must keep
300          * feeding it blobs, or else the remote host/attacker might
301          * avoid mutal authentication requirements.
302          *
303          * Likewise, you must not feed GENSEC too much (after the OK),
304          * it doesn't like that either
305          */
306
307         c->status = gensec_update(sec->generic_state, state,
308                                   sec->auth_info->credentials,
309                                   &state->credentials);
310         if (!NT_STATUS_IS_OK(c->status) &&
311             !NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
312                 composite_error(c, c->status);
313                 return c;
314         }
315
316         state->more_processing = NT_STATUS_EQUAL(c->status,
317                                                  NT_STATUS_MORE_PROCESSING_REQUIRED);
318
319         if (state->credentials.length == 0) {
320                 composite_done(c);
321                 return c;
322         }
323
324         sec->auth_info->credentials = state->credentials;
325
326         /* The first request always is a dcerpc_bind. The subsequent ones
327          * depend on gensec results */
328         creq = dcerpc_bind_send(p, state, &syntax, &transfer_syntax);
329         if (composite_nomem(creq, c)) return c;
330
331         composite_continue(c, creq, bind_auth_recv_bindreply, c);
332         return c;
333 }
334
335
336 /**
337    Bind to a DCE/RPC pipe, receive result
338    @param creq A composite context describing state of async call
339    @retval NTSTATUS code
340 */
341
342 NTSTATUS dcerpc_bind_auth_recv(struct composite_context *creq)
343 {
344         NTSTATUS result = composite_wait(creq);
345         struct bind_auth_state *state = talloc_get_type(creq->private_data,
346                                                         struct bind_auth_state);
347
348         if (NT_STATUS_IS_OK(result)) {
349                 /*
350                   after a successful authenticated bind the session
351                   key reverts to the generic session key
352                 */
353                 state->pipe->conn->security_state.session_key = dcerpc_generic_session_key;
354         }
355         
356         talloc_free(creq);
357         return result;
358 }
359
360
361 /**
362    Perform a GENSEC authenticated bind to a DCE/RPC pipe, sync
363    @param p The dcerpc_pipe to bind (must already be connected)
364    @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
365    @param credentials The credentials of the account to connect with 
366    @param auth_type Select the authentication scheme to use
367    @param auth_level Chooses between unprotected (connect), signed or sealed
368    @param service The service (used by Kerberos to select the service principal to contact)
369    @retval NTSTATUS status code
370 */
371
372 NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p,
373                           const struct dcerpc_interface_table *table,
374                           struct cli_credentials *credentials,
375                           uint8_t auth_type, uint8_t auth_level,
376                           const char *service)
377 {
378         struct composite_context *creq;
379         creq = dcerpc_bind_auth_send(p, p, table, credentials,
380                                      auth_type, auth_level, service);
381         return dcerpc_bind_auth_recv(creq);
382 }