s3-dcerpc: Make dcerpc_check_auth() common code
[kai/samba.git] / source3 / librpc / rpc / dcerpc_helpers.c
1 /*
2  *  DCERPC Helper routines
3  *  Günther Deschner <gd@samba.org> 2010.
4  *  Simo Sorce <idra@samba.org> 2010.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20
21 #include "includes.h"
22 #include "librpc/rpc/dcerpc.h"
23 #include "librpc/gen_ndr/ndr_dcerpc.h"
24 #include "librpc/gen_ndr/ndr_schannel.h"
25 #include "../libcli/auth/schannel.h"
26 #include "../libcli/auth/spnego.h"
27 #include "../libcli/auth/ntlmssp.h"
28 #include "ntlmssp_wrap.h"
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_RPC_PARSE
32
33 /**
34 * @brief NDR Encodes a ncacn_packet
35 *
36 * @param mem_ctx        The memory context the blob will be allocated on
37 * @param ptype          The DCERPC packet type
38 * @param pfc_flags      The DCERPC PFC Falgs
39 * @param auth_length    The length of the trailing auth blob
40 * @param call_id        The call ID
41 * @param u              The payload of the packet
42 * @param blob [out]     The encoded blob if successful
43 *
44 * @return an NTSTATUS error code
45 */
46 NTSTATUS dcerpc_push_ncacn_packet(TALLOC_CTX *mem_ctx,
47                                   enum dcerpc_pkt_type ptype,
48                                   uint8_t pfc_flags,
49                                   uint16_t auth_length,
50                                   uint32_t call_id,
51                                   union dcerpc_payload *u,
52                                   DATA_BLOB *blob)
53 {
54         struct ncacn_packet r;
55         enum ndr_err_code ndr_err;
56
57         r.rpc_vers              = 5;
58         r.rpc_vers_minor        = 0;
59         r.ptype                 = ptype;
60         r.pfc_flags             = pfc_flags;
61         r.drep[0]               = DCERPC_DREP_LE;
62         r.drep[1]               = 0;
63         r.drep[2]               = 0;
64         r.drep[3]               = 0;
65         r.auth_length           = auth_length;
66         r.call_id               = call_id;
67         r.u                     = *u;
68
69         ndr_err = ndr_push_struct_blob(blob, mem_ctx, &r,
70                 (ndr_push_flags_fn_t)ndr_push_ncacn_packet);
71         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
72                 return ndr_map_error2ntstatus(ndr_err);
73         }
74
75         dcerpc_set_frag_length(blob, blob->length);
76
77
78         if (DEBUGLEVEL >= 10) {
79                 /* set frag len for print function */
80                 r.frag_length = blob->length;
81                 NDR_PRINT_DEBUG(ncacn_packet, &r);
82         }
83
84         return NT_STATUS_OK;
85 }
86
87 /**
88 * @brief Decodes a ncacn_packet
89 *
90 * @param mem_ctx        The memory context on which to allocate the packet
91 *                       elements
92 * @param blob           The blob of data to decode
93 * @param r              An empty ncacn_packet, must not be NULL
94 *
95 * @return a NTSTATUS error code
96 */
97 NTSTATUS dcerpc_pull_ncacn_packet(TALLOC_CTX *mem_ctx,
98                                   const DATA_BLOB *blob,
99                                   struct ncacn_packet *r,
100                                   bool bigendian)
101 {
102         enum ndr_err_code ndr_err;
103         struct ndr_pull *ndr;
104
105         ndr = ndr_pull_init_blob(blob, mem_ctx);
106         if (!ndr) {
107                 return NT_STATUS_NO_MEMORY;
108         }
109         if (bigendian) {
110                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
111         }
112
113         ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, r);
114
115         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
116                 talloc_free(ndr);
117                 return ndr_map_error2ntstatus(ndr_err);
118         }
119         talloc_free(ndr);
120
121         if (DEBUGLEVEL >= 10) {
122                 NDR_PRINT_DEBUG(ncacn_packet, r);
123         }
124
125         return NT_STATUS_OK;
126 }
127
128 /**
129 * @brief NDR Encodes a NL_AUTH_MESSAGE
130 *
131 * @param mem_ctx        The memory context the blob will be allocated on
132 * @param r              The NL_AUTH_MESSAGE to encode
133 * @param blob [out]     The encoded blob if successful
134 *
135 * @return a NTSTATUS error code
136 */
137 NTSTATUS dcerpc_push_schannel_bind(TALLOC_CTX *mem_ctx,
138                                    struct NL_AUTH_MESSAGE *r,
139                                    DATA_BLOB *blob)
140 {
141         enum ndr_err_code ndr_err;
142
143         ndr_err = ndr_push_struct_blob(blob, mem_ctx, r,
144                 (ndr_push_flags_fn_t)ndr_push_NL_AUTH_MESSAGE);
145         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
146                 return ndr_map_error2ntstatus(ndr_err);
147         }
148
149         if (DEBUGLEVEL >= 10) {
150                 NDR_PRINT_DEBUG(NL_AUTH_MESSAGE, r);
151         }
152
153         return NT_STATUS_OK;
154 }
155
156 /**
157 * @brief NDR Encodes a dcerpc_auth structure
158 *
159 * @param mem_ctx          The memory context the blob will be allocated on
160 * @param auth_type        The DCERPC Authentication Type
161 * @param auth_level       The DCERPC Authentication Level
162 * @param auth_pad_length  The padding added to the packet this blob will be
163 *                          appended to.
164 * @param auth_context_id  The context id
165 * @param credentials      The authentication credentials blob (signature)
166 * @param blob [out]       The encoded blob if successful
167 *
168 * @return a NTSTATUS error code
169 */
170 NTSTATUS dcerpc_push_dcerpc_auth(TALLOC_CTX *mem_ctx,
171                                  enum dcerpc_AuthType auth_type,
172                                  enum dcerpc_AuthLevel auth_level,
173                                  uint8_t auth_pad_length,
174                                  uint32_t auth_context_id,
175                                  const DATA_BLOB *credentials,
176                                  DATA_BLOB *blob)
177 {
178         struct dcerpc_auth r;
179         enum ndr_err_code ndr_err;
180
181         r.auth_type             = auth_type;
182         r.auth_level            = auth_level;
183         r.auth_pad_length       = auth_pad_length;
184         r.auth_reserved         = 0;
185         r.auth_context_id       = auth_context_id;
186         r.credentials           = *credentials;
187
188         ndr_err = ndr_push_struct_blob(blob, mem_ctx, &r,
189                 (ndr_push_flags_fn_t)ndr_push_dcerpc_auth);
190         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
191                 return ndr_map_error2ntstatus(ndr_err);
192         }
193
194         if (DEBUGLEVEL >= 10) {
195                 NDR_PRINT_DEBUG(dcerpc_auth, &r);
196         }
197
198         return NT_STATUS_OK;
199 }
200
201 /**
202 * @brief Decodes a dcerpc_auth blob
203 *
204 * @param mem_ctx        The memory context on which to allocate the packet
205 *                       elements
206 * @param blob           The blob of data to decode
207 * @param r              An empty dcerpc_auth structure, must not be NULL
208 *
209 * @return a NTSTATUS error code
210 */
211 NTSTATUS dcerpc_pull_dcerpc_auth(TALLOC_CTX *mem_ctx,
212                                  const DATA_BLOB *blob,
213                                  struct dcerpc_auth *r,
214                                  bool bigendian)
215 {
216         enum ndr_err_code ndr_err;
217         struct ndr_pull *ndr;
218
219         ndr = ndr_pull_init_blob(blob, mem_ctx);
220         if (!ndr) {
221                 return NT_STATUS_NO_MEMORY;
222         }
223         if (bigendian) {
224                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
225         }
226
227         ndr_err = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, r);
228
229         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
230                 talloc_free(ndr);
231                 return ndr_map_error2ntstatus(ndr_err);
232         }
233         talloc_free(ndr);
234
235         if (DEBUGLEVEL >= 10) {
236                 NDR_PRINT_DEBUG(dcerpc_auth, r);
237         }
238
239         return NT_STATUS_OK;
240 }
241
242 /*******************************************************************
243  Create and add the NTLMSSP sign/seal auth data.
244  ********************************************************************/
245
246 static NTSTATUS add_ntlmssp_auth_footer(struct auth_ntlmssp_state *auth_state,
247                                         enum dcerpc_AuthLevel auth_level,
248                                         DATA_BLOB *rpc_out)
249 {
250         uint16_t data_and_pad_len = rpc_out->length
251                                         - DCERPC_RESPONSE_LENGTH
252                                         - DCERPC_AUTH_TRAILER_LENGTH;
253         DATA_BLOB auth_blob;
254         NTSTATUS status;
255
256         if (!auth_state) {
257                 return NT_STATUS_INVALID_PARAMETER;
258         }
259
260         switch (auth_level) {
261         case DCERPC_AUTH_LEVEL_PRIVACY:
262                 /* Data portion is encrypted. */
263                 status = auth_ntlmssp_seal_packet(auth_state,
264                                              rpc_out->data,
265                                              rpc_out->data
266                                                 + DCERPC_RESPONSE_LENGTH,
267                                              data_and_pad_len,
268                                              rpc_out->data,
269                                              rpc_out->length,
270                                              &auth_blob);
271                 if (!NT_STATUS_IS_OK(status)) {
272                         return status;
273                 }
274                 break;
275
276         case DCERPC_AUTH_LEVEL_INTEGRITY:
277                 /* Data is signed. */
278                 status = auth_ntlmssp_sign_packet(auth_state,
279                                              rpc_out->data,
280                                              rpc_out->data
281                                                 + DCERPC_RESPONSE_LENGTH,
282                                              data_and_pad_len,
283                                              rpc_out->data,
284                                              rpc_out->length,
285                                              &auth_blob);
286                 if (!NT_STATUS_IS_OK(status)) {
287                         return status;
288                 }
289                 break;
290
291         default:
292                 /* Can't happen. */
293                 smb_panic("bad auth level");
294                 /* Notreached. */
295                 return NT_STATUS_INVALID_PARAMETER;
296         }
297
298         /* Finally attach the blob. */
299         if (!data_blob_append(NULL, rpc_out,
300                                 auth_blob.data, auth_blob.length)) {
301                 DEBUG(0, ("Failed to add %u bytes auth blob.\n",
302                           (unsigned int)auth_blob.length));
303                 return NT_STATUS_NO_MEMORY;
304         }
305         data_blob_free(&auth_blob);
306
307         return NT_STATUS_OK;
308 }
309
310 /*******************************************************************
311  Create and add the schannel sign/seal auth data.
312  ********************************************************************/
313
314 static NTSTATUS add_schannel_auth_footer(struct schannel_state *sas,
315                                         enum dcerpc_AuthLevel auth_level,
316                                         DATA_BLOB *rpc_out)
317 {
318         uint8_t *data_p = rpc_out->data + DCERPC_RESPONSE_LENGTH;
319         size_t data_and_pad_len = rpc_out->length
320                                         - DCERPC_RESPONSE_LENGTH
321                                         - DCERPC_AUTH_TRAILER_LENGTH;
322         DATA_BLOB auth_blob;
323         NTSTATUS status;
324
325         if (!sas) {
326                 return NT_STATUS_INVALID_PARAMETER;
327         }
328
329         DEBUG(10,("add_schannel_auth_footer: SCHANNEL seq_num=%d\n",
330                         sas->seq_num));
331
332         switch (auth_level) {
333         case DCERPC_AUTH_LEVEL_PRIVACY:
334                 status = netsec_outgoing_packet(sas,
335                                                 rpc_out->data,
336                                                 true,
337                                                 data_p,
338                                                 data_and_pad_len,
339                                                 &auth_blob);
340                 break;
341         case DCERPC_AUTH_LEVEL_INTEGRITY:
342                 status = netsec_outgoing_packet(sas,
343                                                 rpc_out->data,
344                                                 false,
345                                                 data_p,
346                                                 data_and_pad_len,
347                                                 &auth_blob);
348                 break;
349         default:
350                 status = NT_STATUS_INTERNAL_ERROR;
351                 break;
352         }
353
354         if (!NT_STATUS_IS_OK(status)) {
355                 DEBUG(1,("add_schannel_auth_footer: failed to process packet: %s\n",
356                         nt_errstr(status)));
357                 return status;
358         }
359
360         if (DEBUGLEVEL >= 10) {
361                 dump_NL_AUTH_SIGNATURE(talloc_tos(), &auth_blob);
362         }
363
364         /* Finally attach the blob. */
365         if (!data_blob_append(NULL, rpc_out,
366                                 auth_blob.data, auth_blob.length)) {
367                 return NT_STATUS_NO_MEMORY;
368         }
369         data_blob_free(&auth_blob);
370
371         return NT_STATUS_OK;
372 }
373
374 /**
375 * @brief   Append an auth footer according to what is the current mechanism
376 *
377 * @param auth           The pipe_auth_data associated with the connection
378 * @param pad_len        The padding used in the packet
379 * @param rpc_out        Packet blob up to and including the auth header
380 *
381 * @return A NTSTATUS error code.
382 */
383 NTSTATUS dcerpc_add_auth_footer(struct pipe_auth_data *auth,
384                                 size_t pad_len, DATA_BLOB *rpc_out)
385 {
386         enum dcerpc_AuthType auth_type;
387         char pad[CLIENT_NDR_PADDING_SIZE] = { 0, };
388         DATA_BLOB auth_info;
389         DATA_BLOB auth_blob;
390         NTSTATUS status;
391
392         if (auth->auth_type == PIPE_AUTH_TYPE_NONE) {
393                 return NT_STATUS_OK;
394         }
395
396         if (pad_len) {
397                 /* Copy the sign/seal padding data. */
398                 if (!data_blob_append(NULL, rpc_out, pad, pad_len)) {
399                         return NT_STATUS_NO_MEMORY;
400                 }
401         }
402
403         auth_type = map_pipe_auth_type_to_rpc_auth_type(auth->auth_type);
404
405         /* marshall the dcerpc_auth with an actually empty auth_blob.
406          * This is needed because the ntmlssp signature includes the
407          * auth header. We will append the actual blob later. */
408         auth_blob = data_blob_null;
409         status = dcerpc_push_dcerpc_auth(rpc_out->data,
410                                          auth_type,
411                                          auth->auth_level,
412                                          pad_len,
413                                          1 /* context id. */,
414                                          &auth_blob,
415                                          &auth_info);
416         if (!NT_STATUS_IS_OK(status)) {
417                 return status;
418         }
419
420         /* append the header */
421         if (!data_blob_append(NULL, rpc_out,
422                                 auth_info.data, auth_info.length)) {
423                 DEBUG(0, ("Failed to add %u bytes auth blob.\n",
424                           (unsigned int)auth_info.length));
425                 return NT_STATUS_NO_MEMORY;
426         }
427         data_blob_free(&auth_info);
428
429         /* Generate any auth sign/seal and add the auth footer. */
430         switch (auth->auth_type) {
431         case PIPE_AUTH_TYPE_NONE:
432                 status = NT_STATUS_OK;
433                 break;
434         case PIPE_AUTH_TYPE_NTLMSSP:
435         case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP:
436                 status = add_ntlmssp_auth_footer(auth->a_u.auth_ntlmssp_state,
437                                                  auth->auth_level,
438                                                  rpc_out);
439                 break;
440         case PIPE_AUTH_TYPE_SCHANNEL:
441                 status = add_schannel_auth_footer(auth->a_u.schannel_auth,
442                                                   auth->auth_level,
443                                                   rpc_out);
444                 break;
445         default:
446                 status = NT_STATUS_INVALID_PARAMETER;
447                 break;
448         }
449
450         return status;
451 }
452
453 /**
454 * @brief Check authentication for request/response packets
455 *
456 * @param auth           The auth data for the connection
457 * @param pkt            The actual ncacn_packet
458 * @param pkt_trailer    The stub_and_verifier part of the packet
459 * @param header_size    The header size
460 * @param raw_pkt        The whole raw packet data blob
461 * @param pad_len        [out] The padding length used in the packet
462 *
463 * @return A NTSTATUS error code
464 */
465 NTSTATUS dcerpc_check_auth(struct pipe_auth_data *auth,
466                            struct ncacn_packet *pkt,
467                            DATA_BLOB *pkt_trailer,
468                            size_t header_size,
469                            DATA_BLOB *raw_pkt,
470                            size_t *pad_len)
471 {
472         NTSTATUS status;
473         struct dcerpc_auth auth_info;
474         uint32_t auth_length;
475         DATA_BLOB full_pkt;
476         DATA_BLOB data;
477
478         switch (auth->auth_level) {
479         case DCERPC_AUTH_LEVEL_PRIVACY:
480                 DEBUG(10, ("Requested Privacy.\n"));
481                 break;
482
483         case DCERPC_AUTH_LEVEL_INTEGRITY:
484                 DEBUG(10, ("Requested Integrity.\n"));
485                 break;
486
487         case DCERPC_AUTH_LEVEL_CONNECT:
488                 if (pkt->auth_length != 0) {
489                         break;
490                 }
491                 *pad_len = 0;
492                 return NT_STATUS_OK;
493
494         case DCERPC_AUTH_LEVEL_NONE:
495                 if (pkt->auth_length != 0) {
496                         DEBUG(3, ("Got non-zero auth len on non "
497                                   "authenticated connection!\n"));
498                         return NT_STATUS_INVALID_PARAMETER;
499                 }
500                 *pad_len = 0;
501                 return NT_STATUS_OK;
502
503         default:
504                 DEBUG(3, ("Unimplemented Auth Level %d",
505                           auth->auth_level));
506                 return NT_STATUS_INVALID_PARAMETER;
507         }
508
509         /* Paranioa checks for auth_length. */
510         if (pkt->auth_length > pkt->frag_length) {
511                 return NT_STATUS_INFO_LENGTH_MISMATCH;
512         }
513         if ((pkt->auth_length
514              + DCERPC_AUTH_TRAILER_LENGTH < pkt->auth_length) ||
515             (pkt->auth_length
516              + DCERPC_AUTH_TRAILER_LENGTH < DCERPC_AUTH_TRAILER_LENGTH)) {
517                 /* Integer wrap attempt. */
518                 return NT_STATUS_INFO_LENGTH_MISMATCH;
519         }
520
521         status = dcerpc_pull_auth_trailer(pkt, pkt, pkt_trailer,
522                                           &auth_info, &auth_length, false);
523         if (!NT_STATUS_IS_OK(status)) {
524                 return status;
525         }
526
527         data = data_blob_const(raw_pkt->data + header_size,
528                                 pkt_trailer->length - auth_length);
529         full_pkt = data_blob_const(raw_pkt->data,
530                                 raw_pkt->length - auth_info.credentials.length);
531
532         switch (auth->auth_type) {
533         case PIPE_AUTH_TYPE_NONE:
534                 return NT_STATUS_OK;
535
536         case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP:
537         case PIPE_AUTH_TYPE_NTLMSSP:
538
539                 DEBUG(10, ("NTLMSSP auth\n"));
540
541                 if (!auth->a_u.auth_ntlmssp_state) {
542                         DEBUG(0, ("Invalid auth level, "
543                                   "failed to process packet auth.\n"));
544                         return NT_STATUS_INVALID_PARAMETER;
545                 }
546
547                 switch (auth->auth_level) {
548                 case DCERPC_AUTH_LEVEL_PRIVACY:
549                         status = auth_ntlmssp_unseal_packet(
550                                         auth->a_u.auth_ntlmssp_state,
551                                         data.data, data.length,
552                                         full_pkt.data, full_pkt.length,
553                                         &auth_info.credentials);
554                         if (!NT_STATUS_IS_OK(status)) {
555                                 return status;
556                         }
557                         memcpy(pkt_trailer->data, data.data, data.length);
558                         break;
559
560                 case DCERPC_AUTH_LEVEL_INTEGRITY:
561                         status = auth_ntlmssp_check_packet(
562                                         auth->a_u.auth_ntlmssp_state,
563                                         data.data, data.length,
564                                         full_pkt.data, full_pkt.length,
565                                         &auth_info.credentials);
566                         if (!NT_STATUS_IS_OK(status)) {
567                                 return status;
568                         }
569                         break;
570
571                 default:
572                         DEBUG(0, ("Invalid auth level, "
573                                   "failed to process packet auth.\n"));
574                         return NT_STATUS_INVALID_PARAMETER;
575                 }
576                 break;
577
578         case PIPE_AUTH_TYPE_SCHANNEL:
579
580                 DEBUG(10, ("SCHANNEL auth\n"));
581
582                 switch (auth->auth_level) {
583                 case DCERPC_AUTH_LEVEL_PRIVACY:
584                         status = netsec_incoming_packet(
585                                         auth->a_u.schannel_auth,
586                                         pkt, true,
587                                         data.data, data.length,
588                                         &auth_info.credentials);
589                         if (!NT_STATUS_IS_OK(status)) {
590                                 return status;
591                         }
592                         memcpy(pkt_trailer->data, data.data, data.length);
593                         break;
594
595                 case DCERPC_AUTH_LEVEL_INTEGRITY:
596                         status = netsec_incoming_packet(
597                                         auth->a_u.schannel_auth,
598                                         pkt, false,
599                                         data.data, data.length,
600                                         &auth_info.credentials);
601                         if (!NT_STATUS_IS_OK(status)) {
602                                 return status;
603                         }
604                         break;
605
606                 default:
607                         DEBUG(0, ("Invalid auth level, "
608                                   "failed to process packet auth.\n"));
609                         return NT_STATUS_INVALID_PARAMETER;
610                 }
611                 break;
612
613         default:
614                 DEBUG(0, ("process_request_pdu: "
615                           "unknown auth type %u set.\n",
616                           (unsigned int)auth->auth_type));
617                 return NT_STATUS_INVALID_PARAMETER;
618         }
619
620         *pad_len = auth_info.auth_pad_length;
621         data_blob_free(&auth_info.credentials);
622         return NT_STATUS_OK;
623 }
624