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