s4:rpc_server: support DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN by default
[bbaumbach/samba-autobuild/.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 3 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, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "rpc_server/dcerpc_server.h"
25 #include "rpc_server/dcerpc_server_proto.h"
26 #include "rpc_server/common/proto.h"
27 #include "librpc/rpc/dcerpc_proto.h"
28 #include "librpc/gen_ndr/ndr_dcerpc.h"
29 #include "auth/credentials/credentials.h"
30 #include "auth/gensec/gensec.h"
31 #include "auth/auth.h"
32 #include "param/param.h"
33 #include "librpc/rpc/rpc_common.h"
34
35 /*
36   parse any auth information from a dcerpc bind request
37   return false if we can't handle the auth request for some 
38   reason (in which case we send a bind_nak)
39 */
40 bool dcesrv_auth_bind(struct dcesrv_call_state *call)
41 {
42         struct cli_credentials *server_credentials;
43         struct ncacn_packet *pkt = &call->pkt;
44         struct dcesrv_connection *dce_conn = call->conn;
45         struct dcesrv_auth *auth = &dce_conn->auth_state;
46         NTSTATUS status;
47         uint32_t auth_length;
48
49         if (pkt->u.bind.auth_info.length == 0) {
50                 dce_conn->auth_state.auth_info = NULL;
51                 return true;
52         }
53
54         dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth);
55         if (!dce_conn->auth_state.auth_info) {
56                 return false;
57         }
58
59         status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.bind.auth_info,
60                                           dce_conn->auth_state.auth_info,
61                                           &auth_length, false);
62         server_credentials 
63                 = cli_credentials_init(call);
64         if (!server_credentials) {
65                 DEBUG(1, ("Failed to init server credentials\n"));
66                 return false;
67         }
68         
69         cli_credentials_set_conf(server_credentials, call->conn->dce_ctx->lp_ctx);
70         status = cli_credentials_set_machine_account(server_credentials, call->conn->dce_ctx->lp_ctx);
71         if (!NT_STATUS_IS_OK(status)) {
72                 DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(status)));
73                 talloc_free(server_credentials);
74                 server_credentials = NULL;
75         }
76
77         status = samba_server_gensec_start(dce_conn, call->event_ctx, 
78                                            call->msg_ctx,
79                                            call->conn->dce_ctx->lp_ctx,
80                                            server_credentials,
81                                            NULL,
82                                            &auth->gensec_security);
83
84         status = gensec_start_mech_by_authtype(auth->gensec_security, auth->auth_info->auth_type, 
85                                                auth->auth_info->auth_level);
86
87         if (!NT_STATUS_IS_OK(status)) {
88                 DEBUG(3, ("Failed to start GENSEC mechanism for DCERPC server: auth_type=%d, auth_level=%d: %s\n",
89                           (int)auth->auth_info->auth_type,
90                           (int)auth->auth_info->auth_level,
91                           nt_errstr(status)));
92                 return false;
93         }
94
95         return true;
96 }
97
98 /*
99   add any auth information needed in a bind ack, and process the authentication
100   information found in the bind.
101 */
102 NTSTATUS dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
103 {
104         struct dcesrv_connection *dce_conn = call->conn;
105         NTSTATUS status;
106         bool want_header_signing = false;
107
108         if (!call->conn->auth_state.gensec_security) {
109                 return NT_STATUS_OK;
110         }
111
112         if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) {
113                 want_header_signing = true;
114         }
115
116         if (!lpcfg_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","header signing", true)) {
117                 want_header_signing = false;
118         }
119
120         status = gensec_update(dce_conn->auth_state.gensec_security,
121                                call, call->event_ctx,
122                                dce_conn->auth_state.auth_info->credentials, 
123                                &dce_conn->auth_state.auth_info->credentials);
124         
125         if (NT_STATUS_IS_OK(status)) {
126                 status = gensec_session_info(dce_conn->auth_state.gensec_security,
127                                              dce_conn,
128                                              &dce_conn->auth_state.session_info);
129                 if (!NT_STATUS_IS_OK(status)) {
130                         DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
131                         return status;
132                 }
133
134                 if (!gensec_have_feature(dce_conn->auth_state.gensec_security,
135                                          GENSEC_FEATURE_SIGN_PKT_HEADER))
136                 {
137                         want_header_signing = false;
138                 }
139
140                 if (want_header_signing) {
141                         gensec_want_feature(dce_conn->auth_state.gensec_security,
142                                             GENSEC_FEATURE_SIGN_PKT_HEADER);
143                         call->conn->state_flags |= DCESRV_CALL_STATE_FLAG_HEADER_SIGNING;
144                         pkt->pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
145                 }
146
147                 /* Now that we are authenticated, go back to the generic session key... */
148                 dce_conn->auth_state.session_key = dcesrv_generic_session_key;
149                 return NT_STATUS_OK;
150         } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
151                 dce_conn->auth_state.auth_info->auth_pad_length = 0;
152                 dce_conn->auth_state.auth_info->auth_reserved = 0;
153
154                 if (!gensec_have_feature(dce_conn->auth_state.gensec_security,
155                                          GENSEC_FEATURE_SIGN_PKT_HEADER))
156                 {
157                         want_header_signing = false;
158                 }
159
160                 if (want_header_signing) {
161                         gensec_want_feature(dce_conn->auth_state.gensec_security,
162                                             GENSEC_FEATURE_SIGN_PKT_HEADER);
163                         call->conn->state_flags |= DCESRV_CALL_STATE_FLAG_HEADER_SIGNING;
164                         pkt->pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
165                 }
166
167                 return NT_STATUS_OK;
168         } else {
169                 DEBUG(4, ("GENSEC mech rejected the incoming authentication at bind_ack: %s\n",
170                           nt_errstr(status)));
171                 return status;
172         }
173 }
174
175
176 /*
177   process the final stage of a auth request
178 */
179 bool dcesrv_auth_auth3(struct dcesrv_call_state *call)
180 {
181         struct ncacn_packet *pkt = &call->pkt;
182         struct dcesrv_connection *dce_conn = call->conn;
183         NTSTATUS status;
184         uint32_t auth_length;
185
186         /* We can't work without an existing gensec state, and an new blob to feed it */
187         if (!dce_conn->auth_state.auth_info ||
188             !dce_conn->auth_state.gensec_security ||
189             pkt->u.auth3.auth_info.length == 0) {
190                 return false;
191         }
192
193         status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.auth3.auth_info,
194                                           dce_conn->auth_state.auth_info, &auth_length, true);
195         if (!NT_STATUS_IS_OK(status)) {
196                 return false;
197         }
198
199         /* Pass the extra data we got from the client down to gensec for processing */
200         status = gensec_update(dce_conn->auth_state.gensec_security,
201                                call, call->event_ctx,
202                                dce_conn->auth_state.auth_info->credentials, 
203                                &dce_conn->auth_state.auth_info->credentials);
204         if (NT_STATUS_IS_OK(status)) {
205                 status = gensec_session_info(dce_conn->auth_state.gensec_security,
206                                              dce_conn,
207                                              &dce_conn->auth_state.session_info);
208                 if (!NT_STATUS_IS_OK(status)) {
209                         DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
210                         return false;
211                 }
212                 /* Now that we are authenticated, go back to the generic session key... */
213                 dce_conn->auth_state.session_key = dcesrv_generic_session_key;
214                 return true;
215         } else {
216                 DEBUG(4, ("GENSEC mech rejected the incoming authentication at bind_auth3: %s\n",
217                           nt_errstr(status)));
218                 return false;
219         }
220 }
221
222 /*
223   parse any auth information from a dcerpc alter request
224   return false if we can't handle the auth request for some 
225   reason (in which case we send a bind_nak (is this true for here?))
226 */
227 bool dcesrv_auth_alter(struct dcesrv_call_state *call)
228 {
229         struct ncacn_packet *pkt = &call->pkt;
230         struct dcesrv_connection *dce_conn = call->conn;
231         NTSTATUS status;
232         uint32_t auth_length;
233
234         /* on a pure interface change there is no auth blob */
235         if (pkt->u.alter.auth_info.length == 0) {
236                 return true;
237         }
238
239         /* We can't work without an existing gensec state */
240         if (!dce_conn->auth_state.gensec_security) {
241                 return false;
242         }
243
244         dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth);
245         if (!dce_conn->auth_state.auth_info) {
246                 return false;
247         }
248
249         status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.alter.auth_info,
250                                           dce_conn->auth_state.auth_info,
251                                           &auth_length, true);
252         if (!NT_STATUS_IS_OK(status)) {
253                 return false;
254         }
255
256         return true;
257 }
258
259 /*
260   add any auth information needed in a alter ack, and process the authentication
261   information found in the alter.
262 */
263 NTSTATUS dcesrv_auth_alter_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
264 {
265         struct dcesrv_connection *dce_conn = call->conn;
266         NTSTATUS status;
267
268         /* on a pure interface change there is no auth_info structure
269            setup */
270         if (!call->conn->auth_state.auth_info ||
271             dce_conn->auth_state.auth_info->credentials.length == 0) {
272                 return NT_STATUS_OK;
273         }
274
275         if (!call->conn->auth_state.gensec_security) {
276                 return NT_STATUS_INVALID_PARAMETER;
277         }
278
279         status = gensec_update(dce_conn->auth_state.gensec_security,
280                                call, call->event_ctx,
281                                dce_conn->auth_state.auth_info->credentials, 
282                                &dce_conn->auth_state.auth_info->credentials);
283
284         if (NT_STATUS_IS_OK(status)) {
285                 status = gensec_session_info(dce_conn->auth_state.gensec_security,
286                                              dce_conn,
287                                              &dce_conn->auth_state.session_info);
288                 if (!NT_STATUS_IS_OK(status)) {
289                         DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
290                         return status;
291                 }
292
293                 /* Now that we are authenticated, got back to the generic session key... */
294                 dce_conn->auth_state.session_key = dcesrv_generic_session_key;
295                 return NT_STATUS_OK;
296         } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
297                 dce_conn->auth_state.auth_info->auth_pad_length = 0;
298                 dce_conn->auth_state.auth_info->auth_reserved = 0;
299                 return NT_STATUS_OK;
300         }
301
302         DEBUG(4, ("GENSEC mech rejected the incoming authentication at auth alter_ack: %s\n",
303                   nt_errstr(status)));
304         return status;
305 }
306
307 /*
308   check credentials on a request
309 */
310 bool dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
311 {
312         struct ncacn_packet *pkt = &call->pkt;
313         struct dcesrv_connection *dce_conn = call->conn;
314         struct dcerpc_auth auth;
315         NTSTATUS status;
316         uint32_t auth_length;
317         size_t hdr_size = DCERPC_REQUEST_LENGTH;
318
319         if (!dce_conn->auth_state.auth_info ||
320             !dce_conn->auth_state.gensec_security) {
321                 return true;
322         }
323
324         if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
325                 hdr_size += 16;
326         }
327
328         switch (dce_conn->auth_state.auth_info->auth_level) {
329         case DCERPC_AUTH_LEVEL_PRIVACY:
330         case DCERPC_AUTH_LEVEL_INTEGRITY:
331                 break;
332
333         case DCERPC_AUTH_LEVEL_CONNECT:
334                 if (pkt->auth_length != 0) {
335                         break;
336                 }
337                 return true;
338         case DCERPC_AUTH_LEVEL_NONE:
339                 if (pkt->auth_length != 0) {
340                         return false;
341                 }
342                 return true;
343
344         default:
345                 return false;
346         }
347
348         status = dcerpc_pull_auth_trailer(pkt, call,
349                                           &pkt->u.request.stub_and_verifier,
350                                           &auth, &auth_length, false);
351         if (!NT_STATUS_IS_OK(status)) {
352                 return false;
353         }
354
355         pkt->u.request.stub_and_verifier.length -= auth_length;
356
357         /* check signature or unseal the packet */
358         switch (dce_conn->auth_state.auth_info->auth_level) {
359         case DCERPC_AUTH_LEVEL_PRIVACY:
360                 status = gensec_unseal_packet(dce_conn->auth_state.gensec_security,
361                                               full_packet->data + hdr_size,
362                                               pkt->u.request.stub_and_verifier.length, 
363                                               full_packet->data,
364                                               full_packet->length-auth.credentials.length,
365                                               &auth.credentials);
366                 memcpy(pkt->u.request.stub_and_verifier.data, 
367                        full_packet->data + hdr_size,
368                        pkt->u.request.stub_and_verifier.length);
369                 break;
370
371         case DCERPC_AUTH_LEVEL_INTEGRITY:
372                 status = gensec_check_packet(dce_conn->auth_state.gensec_security,
373                                              pkt->u.request.stub_and_verifier.data, 
374                                              pkt->u.request.stub_and_verifier.length,
375                                              full_packet->data,
376                                              full_packet->length-auth.credentials.length,
377                                              &auth.credentials);
378                 break;
379
380         case DCERPC_AUTH_LEVEL_CONNECT:
381                 /* for now we ignore possible signatures here */
382                 status = NT_STATUS_OK;
383                 break;
384
385         default:
386                 status = NT_STATUS_INVALID_LEVEL;
387                 break;
388         }
389
390         /* remove the indicated amount of padding */
391         if (pkt->u.request.stub_and_verifier.length < auth.auth_pad_length) {
392                 return false;
393         }
394         pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length;
395
396         return NT_STATUS_IS_OK(status);
397 }
398
399
400 /* 
401    push a signed or sealed dcerpc request packet into a blob
402 */
403 bool dcesrv_auth_response(struct dcesrv_call_state *call,
404                           DATA_BLOB *blob, size_t sig_size,
405                           struct ncacn_packet *pkt)
406 {
407         struct dcesrv_connection *dce_conn = call->conn;
408         NTSTATUS status;
409         enum ndr_err_code ndr_err;
410         struct ndr_push *ndr;
411         uint32_t payload_length;
412         DATA_BLOB creds2;
413
414         /* non-signed packets are simple */
415         if (sig_size == 0) {
416                 status = ncacn_push_auth(blob, call, pkt, NULL);
417                 return NT_STATUS_IS_OK(status);
418         }
419
420         switch (dce_conn->auth_state.auth_info->auth_level) {
421         case DCERPC_AUTH_LEVEL_PRIVACY:
422         case DCERPC_AUTH_LEVEL_INTEGRITY:
423                 break;
424
425         case DCERPC_AUTH_LEVEL_CONNECT:
426                 /*
427                  * TODO: let the gensec mech decide if it wants to generate a
428                  *       signature that might be needed for schannel...
429                  */
430                 status = ncacn_push_auth(blob, call, pkt, NULL);
431                 return NT_STATUS_IS_OK(status);
432
433         case DCERPC_AUTH_LEVEL_NONE:
434                 status = ncacn_push_auth(blob, call, pkt, NULL);
435                 return NT_STATUS_IS_OK(status);
436
437         default:
438                 return false;
439         }
440
441         ndr = ndr_push_init_ctx(call);
442         if (!ndr) {
443                 return false;
444         }
445
446         if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
447                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
448         }
449
450         ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
451         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
452                 return false;
453         }
454
455         /* pad to 16 byte multiple in the payload portion of the
456            packet. This matches what w2k3 does. Note that we can't use
457            ndr_push_align() as that is relative to the start of the
458            whole packet, whereas w2k8 wants it relative to the start
459            of the stub */
460         dce_conn->auth_state.auth_info->auth_pad_length =
461                 (16 - (pkt->u.response.stub_and_verifier.length & 15)) & 15;
462         ndr_err = ndr_push_zero(ndr,
463                                 dce_conn->auth_state.auth_info->auth_pad_length);
464         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
465                 return false;
466         }
467
468         payload_length = pkt->u.response.stub_and_verifier.length +
469                 dce_conn->auth_state.auth_info->auth_pad_length;
470
471         /* we start without signature, it will appended later */
472         dce_conn->auth_state.auth_info->credentials = data_blob(NULL, 0);
473
474         /* add the auth verifier */
475         ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
476                                        dce_conn->auth_state.auth_info);
477         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
478                 return false;
479         }
480
481         /* extract the whole packet as a blob */
482         *blob = ndr_push_blob(ndr);
483
484         /*
485          * Setup the frag and auth length in the packet buffer.
486          * This is needed if the GENSEC mech does AEAD signing
487          * of the packet headers. The signature itself will be
488          * appended later.
489          */
490         dcerpc_set_frag_length(blob, blob->length + sig_size);
491         dcerpc_set_auth_length(blob, sig_size);
492
493         /* sign or seal the packet */
494         switch (dce_conn->auth_state.auth_info->auth_level) {
495         case DCERPC_AUTH_LEVEL_PRIVACY:
496                 status = gensec_seal_packet(dce_conn->auth_state.gensec_security, 
497                                             call,
498                                             ndr->data + DCERPC_REQUEST_LENGTH, 
499                                             payload_length,
500                                             blob->data,
501                                             blob->length,
502                                             &creds2);
503                 break;
504
505         case DCERPC_AUTH_LEVEL_INTEGRITY:
506                 status = gensec_sign_packet(dce_conn->auth_state.gensec_security, 
507                                             call,
508                                             ndr->data + DCERPC_REQUEST_LENGTH, 
509                                             payload_length,
510                                             blob->data,
511                                             blob->length,
512                                             &creds2);
513                 break;
514
515         default:
516                 status = NT_STATUS_INVALID_LEVEL;
517                 break;
518         }
519
520         if (!NT_STATUS_IS_OK(status)) {
521                 return false;
522         }       
523
524         if (creds2.length != sig_size) {
525                 DEBUG(3,("dcesrv_auth_response: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n",
526                          (unsigned)creds2.length, (uint32_t)sig_size,
527                          (unsigned)dce_conn->auth_state.auth_info->auth_pad_length,
528                          (unsigned)pkt->u.response.stub_and_verifier.length));
529                 dcerpc_set_frag_length(blob, blob->length + creds2.length);
530                 dcerpc_set_auth_length(blob, creds2.length);
531         }
532
533         if (!data_blob_append(call, blob, creds2.data, creds2.length)) {
534                 status = NT_STATUS_NO_MEMORY;
535                 return false;
536         }
537         data_blob_free(&creds2);
538
539         return true;
540 }