r7313: Prefix a few functions with ncacn_ rather then dcerpc_ because they are
[kai/samba.git] / source4 / rpc_server / dcesrv_auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    server side dcerpc authentication code
5
6    Copyright (C) Andrew Tridgell 2003
7    Copyright (C) Stefan (metze) Metzmacher 2004
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "rpc_server/dcerpc_server.h"
26
27 /*
28   parse any auth information from a dcerpc bind request
29   return False if we can't handle the auth request for some 
30   reason (in which case we send a bind_nak)
31 */
32 BOOL dcesrv_auth_bind(struct dcesrv_call_state *call)
33 {
34         struct ncacn_packet *pkt = &call->pkt;
35         struct dcesrv_connection *dce_conn = call->conn;
36         struct dcesrv_auth *auth = &dce_conn->auth_state;
37         NTSTATUS status;
38
39         if (pkt->u.bind.auth_info.length == 0) {
40                 dce_conn->auth_state.auth_info = NULL;
41                 return True;
42         }
43
44         dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth);
45         if (!dce_conn->auth_state.auth_info) {
46                 return False;
47         }
48
49         status = ndr_pull_struct_blob(&pkt->u.bind.auth_info,
50                                       call,
51                                       dce_conn->auth_state.auth_info,
52                                       (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
53         if (!NT_STATUS_IS_OK(status)) {
54                 return False;
55         }
56
57         status = gensec_server_start(dce_conn, &auth->gensec_security);
58         if (!NT_STATUS_IS_OK(status)) {
59                 DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
60                 return False;
61         }
62
63         status = gensec_start_mech_by_authtype(auth->gensec_security, auth->auth_info->auth_type, 
64                                                auth->auth_info->auth_level);
65
66         if (!NT_STATUS_IS_OK(status)) {
67                 DEBUG(1, ("Failed to start GENSEC mech-specific server code (%d): %s\n", 
68                           (int)auth->auth_info->auth_type,
69                           nt_errstr(status)));
70                 return False;
71         }
72
73         return True;
74 }
75
76 /*
77   add any auth information needed in a bind ack, and process the authentication
78   information found in the bind.
79 */
80 BOOL dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
81 {
82         struct dcesrv_connection *dce_conn = call->conn;
83         NTSTATUS status;
84
85         if (!call->conn->auth_state.gensec_security) {
86                 return True;
87         }
88
89         status = gensec_update(dce_conn->auth_state.gensec_security,
90                                call,
91                                dce_conn->auth_state.auth_info->credentials, 
92                                &dce_conn->auth_state.auth_info->credentials);
93         
94         if (NT_STATUS_IS_OK(status)) {
95                 status = gensec_session_info(dce_conn->auth_state.gensec_security,
96                                              &dce_conn->auth_state.session_info);
97                 if (!NT_STATUS_IS_OK(status)) {
98                         DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
99                         return False;
100                 }
101
102                 /* Now that we are authenticated, go back to the generic session key... */
103                 dce_conn->auth_state.session_key = dcesrv_generic_session_key;
104                 return True;
105         } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
106                 dce_conn->auth_state.auth_info->auth_pad_length = 0;
107                 dce_conn->auth_state.auth_info->auth_reserved = 0;
108                 return True;
109         } else {
110                 DEBUG(2, ("Failed to start dcesrv auth negotiate: %s\n", nt_errstr(status)));
111                 return False;
112         }
113 }
114
115
116 /*
117   process the final stage of a auth request
118 */
119 BOOL dcesrv_auth_auth3(struct dcesrv_call_state *call)
120 {
121         struct ncacn_packet *pkt = &call->pkt;
122         struct dcesrv_connection *dce_conn = call->conn;
123         NTSTATUS status;
124
125         /* We can't work without an existing gensec state, and an new blob to feed it */
126         if (!dce_conn->auth_state.auth_info ||
127             !dce_conn->auth_state.gensec_security ||
128             pkt->u.auth3.auth_info.length == 0) {
129                 return False;
130         }
131
132         status = ndr_pull_struct_blob(&pkt->u.auth3.auth_info,
133                                       call,
134                                       dce_conn->auth_state.auth_info,
135                                       (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
136         if (!NT_STATUS_IS_OK(status)) {
137                 return False;
138         }
139
140         /* Pass the extra data we got from the client down to gensec for processing */
141         status = gensec_update(dce_conn->auth_state.gensec_security,
142                                call,
143                                dce_conn->auth_state.auth_info->credentials, 
144                                &dce_conn->auth_state.auth_info->credentials);
145         if (NT_STATUS_IS_OK(status)) {
146                 status = gensec_session_info(dce_conn->auth_state.gensec_security,
147                                              &dce_conn->auth_state.session_info);
148                 if (!NT_STATUS_IS_OK(status)) {
149                         DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
150                         return False;
151                 }
152                 /* Now that we are authenticated, got back to the generic session key... */
153                 dce_conn->auth_state.session_key = dcesrv_generic_session_key;
154                 return True;
155         } else {
156                 DEBUG(4, ("dcesrv_auth_auth3: failed to authenticate: %s\n", 
157                           nt_errstr(status)));
158                 return False;
159         }
160
161         return True;
162 }
163
164 /*
165   parse any auth information from a dcerpc alter request
166   return False if we can't handle the auth request for some 
167   reason (in which case we send a bind_nak (is this true for here?))
168 */
169 BOOL dcesrv_auth_alter(struct dcesrv_call_state *call)
170 {
171         struct ncacn_packet *pkt = &call->pkt;
172         struct dcesrv_connection *dce_conn = call->conn;
173         NTSTATUS status;
174
175         /* on a pure interface change there is no auth blob */
176         if (pkt->u.alter.auth_info.length == 0) {
177                 return True;
178         }
179
180         /* We can't work without an existing gensec state */
181         if (!dce_conn->auth_state.gensec_security) {
182                 return False;
183         }
184
185         dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth);
186         if (!dce_conn->auth_state.auth_info) {
187                 return False;
188         }
189
190         status = ndr_pull_struct_blob(&pkt->u.alter.auth_info,
191                                       call,
192                                       dce_conn->auth_state.auth_info,
193                                       (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
194         if (!NT_STATUS_IS_OK(status)) {
195                 return False;
196         }
197
198         return True;
199 }
200
201 /*
202   add any auth information needed in a alter ack, and process the authentication
203   information found in the alter.
204 */
205 BOOL dcesrv_auth_alter_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
206 {
207         struct dcesrv_connection *dce_conn = call->conn;
208         NTSTATUS status;
209
210         /* on a pure interface change there is no auth_info structure
211            setup */
212         if (!call->conn->auth_state.auth_info) {
213                 return True;
214         }
215
216         if (!call->conn->auth_state.gensec_security) {
217                 return False;
218         }
219
220         status = gensec_update(dce_conn->auth_state.gensec_security,
221                                call,
222                                dce_conn->auth_state.auth_info->credentials, 
223                                &dce_conn->auth_state.auth_info->credentials);
224
225         if (NT_STATUS_IS_OK(status)) {
226                 status = gensec_session_info(dce_conn->auth_state.gensec_security,
227                                              &dce_conn->auth_state.session_info);
228                 if (!NT_STATUS_IS_OK(status)) {
229                         DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
230                         return False;
231                 }
232
233                 /* Now that we are authenticated, got back to the generic session key... */
234                 dce_conn->auth_state.session_key = dcesrv_generic_session_key;
235                 return True;
236         } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
237                 dce_conn->auth_state.auth_info->auth_pad_length = 0;
238                 dce_conn->auth_state.auth_info->auth_reserved = 0;
239                 return True;
240         } else {
241                 DEBUG(2, ("Failed to finish dcesrv auth alter_ack: %s\n", nt_errstr(status)));
242                 return True;
243         }
244 }
245
246 /*
247   generate a CONNECT level verifier
248 */
249 static NTSTATUS dcesrv_connect_verifier(TALLOC_CTX *mem_ctx, DATA_BLOB *blob)
250 {
251         *blob = data_blob_talloc(mem_ctx, NULL, 16);
252         if (blob->data == NULL) {
253                 return NT_STATUS_NO_MEMORY;
254         }
255         SIVAL(blob->data, 0, 1);
256         memset(blob->data+4, 0, 12);
257         return NT_STATUS_OK;
258 }
259
260 /*
261   generate a CONNECT level verifier
262 */
263 static NTSTATUS dcesrv_check_connect_verifier(DATA_BLOB *blob)
264 {
265         if (blob->length != 16 ||
266             IVAL(blob->data, 0) != 1) {
267                 return NT_STATUS_ACCESS_DENIED;
268         }
269         return NT_STATUS_OK;
270 }
271
272
273 /*
274   check credentials on a request
275 */
276 BOOL dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
277 {
278         struct ncacn_packet *pkt = &call->pkt;
279         struct dcesrv_connection *dce_conn = call->conn;
280         DATA_BLOB auth_blob;
281         struct dcerpc_auth auth;
282         struct ndr_pull *ndr;
283         NTSTATUS status;
284
285         if (!dce_conn->auth_state.auth_info ||
286             !dce_conn->auth_state.gensec_security) {
287                 return True;
288         }
289
290         auth_blob.length = 8 + pkt->auth_length;
291
292         /* check for a valid length */
293         if (pkt->u.request.stub_and_verifier.length < auth_blob.length) {
294                 return False;
295         }
296
297         auth_blob.data = 
298                 pkt->u.request.stub_and_verifier.data + 
299                 pkt->u.request.stub_and_verifier.length - auth_blob.length;
300         pkt->u.request.stub_and_verifier.length -= auth_blob.length;
301
302         /* pull the auth structure */
303         ndr = ndr_pull_init_blob(&auth_blob, call);
304         if (!ndr) {
305                 return False;
306         }
307
308         if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
309                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
310         }
311
312         status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
313         if (!NT_STATUS_IS_OK(status)) {
314                 talloc_free(ndr);
315                 return False;
316         }
317
318         /* check signature or unseal the packet */
319         switch (dce_conn->auth_state.auth_info->auth_level) {
320         case DCERPC_AUTH_LEVEL_PRIVACY:
321                 status = gensec_unseal_packet(dce_conn->auth_state.gensec_security,
322                                               call,
323                                               full_packet->data + DCERPC_REQUEST_LENGTH,
324                                               pkt->u.request.stub_and_verifier.length, 
325                                               full_packet->data,
326                                               full_packet->length-auth.credentials.length,
327                                               &auth.credentials);
328                 memcpy(pkt->u.request.stub_and_verifier.data, 
329                        full_packet->data + DCERPC_REQUEST_LENGTH,
330                        pkt->u.request.stub_and_verifier.length);
331                 break;
332
333         case DCERPC_AUTH_LEVEL_INTEGRITY:
334                 status = gensec_check_packet(dce_conn->auth_state.gensec_security,
335                                              call,
336                                              pkt->u.request.stub_and_verifier.data, 
337                                              pkt->u.request.stub_and_verifier.length,
338                                              full_packet->data,
339                                              full_packet->length-auth.credentials.length,
340                                              &auth.credentials);
341                 break;
342
343         case DCERPC_AUTH_LEVEL_CONNECT:
344                 status = dcesrv_check_connect_verifier(&auth.credentials);
345                 break;
346
347         default:
348                 status = NT_STATUS_INVALID_LEVEL;
349                 break;
350         }
351
352         /* remove the indicated amount of padding */
353         if (pkt->u.request.stub_and_verifier.length < auth.auth_pad_length) {
354                 talloc_free(ndr);
355                 return False;
356         }
357         pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length;
358         talloc_free(ndr);
359
360         return NT_STATUS_IS_OK(status);
361 }
362
363
364 /* 
365    push a signed or sealed dcerpc request packet into a blob
366 */
367 BOOL dcesrv_auth_response(struct dcesrv_call_state *call,
368                           DATA_BLOB *blob, struct ncacn_packet *pkt)
369 {
370         struct dcesrv_connection *dce_conn = call->conn;
371         NTSTATUS status;
372         struct ndr_push *ndr;
373         uint32_t payload_length;
374
375         /* non-signed packets are simple */
376         if (!dce_conn->auth_state.auth_info || !dce_conn->auth_state.gensec_security) {
377                 status = ncacn_push_auth(blob, call, pkt, NULL);
378                 return NT_STATUS_IS_OK(status);
379         }
380
381         ndr = ndr_push_init_ctx(call);
382         if (!ndr) {
383                 return False;
384         }
385
386         if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
387                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
388         }
389
390         status = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
391         if (!NT_STATUS_IS_OK(status)) {
392                 return False;
393         }
394
395         /* pad to 8 byte multiple */
396         dce_conn->auth_state.auth_info->auth_pad_length = NDR_ALIGN(ndr, 8);
397         ndr_push_zero(ndr, dce_conn->auth_state.auth_info->auth_pad_length);
398
399         payload_length = ndr->offset - DCERPC_REQUEST_LENGTH;
400
401         if (dce_conn->auth_state.auth_info->auth_level == DCERPC_AUTH_LEVEL_CONNECT) {
402                 status = dcesrv_connect_verifier(call,
403                                                  &dce_conn->auth_state.auth_info->credentials);
404                 if (!NT_STATUS_IS_OK(status)) {
405                         return False;
406                 }
407         } else {
408                 dce_conn->auth_state.auth_info->credentials
409                         = data_blob_talloc(call, NULL, 
410                                            gensec_sig_size(dce_conn->auth_state.gensec_security));
411         }
412
413         /* add the auth verifier */
414         status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, dce_conn->auth_state.auth_info);
415         if (!NT_STATUS_IS_OK(status)) {
416                 return False;
417         }
418
419         /* extract the whole packet as a blob */
420         *blob = ndr_push_blob(ndr);
421
422         /* fill in the fragment length and auth_length, we can't fill
423            in these earlier as we don't know the signature length (it
424            could be variable length) */
425         dcerpc_set_frag_length(blob, blob->length);
426         dcerpc_set_auth_length(blob, dce_conn->auth_state.auth_info->credentials.length);
427
428         /* sign or seal the packet */
429         switch (dce_conn->auth_state.auth_info->auth_level) {
430         case DCERPC_AUTH_LEVEL_PRIVACY:
431                 status = gensec_seal_packet(dce_conn->auth_state.gensec_security, 
432                                             call,
433                                             ndr->data + DCERPC_REQUEST_LENGTH, 
434                                             payload_length,
435                                             blob->data,
436                                             blob->length - dce_conn->auth_state.auth_info->credentials.length,
437                                             &dce_conn->auth_state.auth_info->credentials);
438                 break;
439
440         case DCERPC_AUTH_LEVEL_INTEGRITY:
441                 status = gensec_sign_packet(dce_conn->auth_state.gensec_security, 
442                                             call,
443                                             ndr->data + DCERPC_REQUEST_LENGTH, 
444                                             payload_length,
445                                             blob->data,
446                                             blob->length - dce_conn->auth_state.auth_info->credentials.length,
447                                             &dce_conn->auth_state.auth_info->credentials);
448
449                 break;
450
451         case DCERPC_AUTH_LEVEL_CONNECT:
452                 break;
453
454         default:
455                 status = NT_STATUS_INVALID_LEVEL;
456                 break;
457         }
458
459         if (!NT_STATUS_IS_OK(status)) {
460                 return False;
461         }       
462
463         memcpy(blob->data + blob->length - dce_conn->auth_state.auth_info->credentials.length, 
464                dce_conn->auth_state.auth_info->credentials.data, dce_conn->auth_state.auth_info->credentials.length);
465         
466         data_blob_free(&dce_conn->auth_state.auth_info->credentials);
467
468         return True;
469 }