5eb9c445ab25d785b20ee0efa7d01c00403dc8c7
[samba.git] / librpc / rpc / dcerpc_pkt_auth.c
1 /*
2    Unix SMB/CIFS implementation.
3    raw dcerpc operations
4
5    Copyright (C) Andrew Tridgell 2003-2005
6    Copyright (C) Jelmer Vernooij 2004-2005
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "replace.h"
23 #include "system/network.h"
24 #include <tevent.h>
25 #include "lib/util/talloc_stack.h"
26 #include "lib/util/debug.h"
27 #include "lib/util/byteorder.h"
28 #include "lib/util/samba_util.h"
29 #include "librpc/rpc/dcerpc.h"
30 #include "librpc/rpc/dcerpc_util.h"
31 #include "librpc/rpc/dcerpc_pkt_auth.h"
32 #include "librpc/gen_ndr/ndr_dcerpc.h"
33 #include "rpc_common.h"
34 #include "lib/util/bitmap.h"
35 #include "auth/gensec/gensec.h"
36 #include "lib/util/mkdir_p.h"
37 #include "lib/crypto/gnutls_helpers.h"
38 #include <gnutls/crypto.h>
39
40 NTSTATUS dcerpc_ncacn_pull_pkt_auth(const struct dcerpc_auth *auth_state,
41                                     struct gensec_security *gensec,
42                                     bool check_pkt_auth_fields,
43                                     TALLOC_CTX *mem_ctx,
44                                     enum dcerpc_pkt_type ptype,
45                                     uint8_t required_flags,
46                                     uint8_t optional_flags,
47                                     uint8_t payload_offset,
48                                     DATA_BLOB *payload_and_verifier,
49                                     DATA_BLOB *raw_packet,
50                                     const struct ncacn_packet *pkt)
51 {
52         NTSTATUS status;
53         struct dcerpc_auth auth;
54         uint32_t auth_length;
55
56         if (auth_state == NULL) {
57                 return NT_STATUS_INTERNAL_ERROR;
58         }
59
60         status = dcerpc_verify_ncacn_packet_header(pkt, ptype,
61                                         payload_and_verifier->length,
62                                         required_flags, optional_flags);
63         if (!NT_STATUS_IS_OK(status)) {
64                 return status;
65         }
66
67         switch (auth_state->auth_level) {
68         case DCERPC_AUTH_LEVEL_PRIVACY:
69         case DCERPC_AUTH_LEVEL_INTEGRITY:
70         case DCERPC_AUTH_LEVEL_PACKET:
71                 break;
72
73         case DCERPC_AUTH_LEVEL_CONNECT:
74                 if (pkt->auth_length != 0) {
75                         break;
76                 }
77                 return NT_STATUS_OK;
78         case DCERPC_AUTH_LEVEL_NONE:
79                 if (pkt->auth_length != 0) {
80                         return NT_STATUS_ACCESS_DENIED;
81                 }
82                 return NT_STATUS_OK;
83
84         default:
85                 return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL;
86         }
87
88         if (pkt->auth_length == 0) {
89                 return NT_STATUS_RPC_PROTOCOL_ERROR;
90         }
91
92         if (gensec == NULL) {
93                 return NT_STATUS_INTERNAL_ERROR;
94         }
95
96         status = dcerpc_pull_auth_trailer(pkt, mem_ctx,
97                                           payload_and_verifier,
98                                           &auth, &auth_length, false);
99         if (!NT_STATUS_IS_OK(status)) {
100                 return status;
101         }
102
103         if (payload_and_verifier->length < auth_length) {
104                 /*
105                  * should be checked in dcerpc_pull_auth_trailer()
106                  */
107                 return NT_STATUS_INTERNAL_ERROR;
108         }
109
110         payload_and_verifier->length -= auth_length;
111
112         if (payload_and_verifier->length < auth.auth_pad_length) {
113                 /*
114                  * should be checked in dcerpc_pull_auth_trailer()
115                  */
116                 return NT_STATUS_INTERNAL_ERROR;
117         }
118
119         if (check_pkt_auth_fields) {
120                 if (auth.auth_type != auth_state->auth_type) {
121                         return NT_STATUS_ACCESS_DENIED;
122                 }
123
124                 if (auth.auth_level != auth_state->auth_level) {
125                         return NT_STATUS_ACCESS_DENIED;
126                 }
127
128                 if (auth.auth_context_id != auth_state->auth_context_id) {
129                         return NT_STATUS_ACCESS_DENIED;
130                 }
131         }
132
133         /* check signature or unseal the packet */
134         switch (auth_state->auth_level) {
135         case DCERPC_AUTH_LEVEL_PRIVACY:
136                 status = gensec_unseal_packet(gensec,
137                                               raw_packet->data + payload_offset,
138                                               payload_and_verifier->length,
139                                               raw_packet->data,
140                                               raw_packet->length -
141                                               auth.credentials.length,
142                                               &auth.credentials);
143                 if (!NT_STATUS_IS_OK(status)) {
144                         return NT_STATUS_RPC_SEC_PKG_ERROR;
145                 }
146                 memcpy(payload_and_verifier->data,
147                        raw_packet->data + payload_offset,
148                        payload_and_verifier->length);
149                 break;
150
151         case DCERPC_AUTH_LEVEL_INTEGRITY:
152         case DCERPC_AUTH_LEVEL_PACKET:
153                 status = gensec_check_packet(gensec,
154                                              payload_and_verifier->data,
155                                              payload_and_verifier->length,
156                                              raw_packet->data,
157                                              raw_packet->length -
158                                              auth.credentials.length,
159                                              &auth.credentials);
160                 if (!NT_STATUS_IS_OK(status)) {
161                         return NT_STATUS_RPC_SEC_PKG_ERROR;
162                 }
163                 break;
164
165         case DCERPC_AUTH_LEVEL_CONNECT:
166                 /* for now we ignore possible signatures here */
167                 break;
168
169         default:
170                 return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL;
171         }
172
173         /*
174          * remove the indicated amount of padding
175          *
176          * A possible overflow is checked above.
177          */
178         payload_and_verifier->length -= auth.auth_pad_length;
179
180         return NT_STATUS_OK;
181 }
182
183 NTSTATUS dcerpc_ncacn_push_pkt_auth(const struct dcerpc_auth *auth_state,
184                                     struct gensec_security *gensec,
185                                     TALLOC_CTX *mem_ctx,
186                                     DATA_BLOB *raw_packet,
187                                     size_t sig_size,
188                                     uint8_t payload_offset,
189                                     const DATA_BLOB *payload,
190                                     const struct ncacn_packet *pkt)
191 {
192         TALLOC_CTX *frame = talloc_stackframe();
193         NTSTATUS status;
194         enum ndr_err_code ndr_err;
195         struct ndr_push *ndr = NULL;
196         uint32_t payload_length;
197         uint32_t whole_length;
198         DATA_BLOB blob = data_blob_null;
199         DATA_BLOB sig = data_blob_null;
200         struct dcerpc_auth _out_auth_info;
201         struct dcerpc_auth *out_auth_info = NULL;
202
203         *raw_packet = data_blob_null;
204
205         if (auth_state == NULL) {
206                 TALLOC_FREE(frame);
207                 return NT_STATUS_INTERNAL_ERROR;
208         }
209
210         switch (auth_state->auth_level) {
211         case DCERPC_AUTH_LEVEL_PRIVACY:
212         case DCERPC_AUTH_LEVEL_INTEGRITY:
213         case DCERPC_AUTH_LEVEL_PACKET:
214                 if (sig_size == 0) {
215                         TALLOC_FREE(frame);
216                         return NT_STATUS_INTERNAL_ERROR;
217                 }
218
219                 if (gensec == NULL) {
220                         TALLOC_FREE(frame);
221                         return NT_STATUS_INTERNAL_ERROR;
222                 }
223
224                 _out_auth_info = (struct dcerpc_auth) {
225                         .auth_type = auth_state->auth_type,
226                         .auth_level = auth_state->auth_level,
227                         .auth_context_id = auth_state->auth_context_id,
228                 };
229                 out_auth_info = &_out_auth_info;
230                 break;
231
232         case DCERPC_AUTH_LEVEL_CONNECT:
233                 /*
234                  * TODO: let the gensec mech decide if it wants to generate a
235                  *       signature that might be needed for schannel...
236                  */
237                 if (sig_size != 0) {
238                         TALLOC_FREE(frame);
239                         return NT_STATUS_INTERNAL_ERROR;
240                 }
241
242                 if (gensec == NULL) {
243                         TALLOC_FREE(frame);
244                         return NT_STATUS_INTERNAL_ERROR;
245                 }
246                 break;
247
248         case DCERPC_AUTH_LEVEL_NONE:
249                 if (sig_size != 0) {
250                         TALLOC_FREE(frame);
251                         return NT_STATUS_INTERNAL_ERROR;
252                 }
253                 break;
254
255         default:
256                 TALLOC_FREE(frame);
257                 return NT_STATUS_INTERNAL_ERROR;
258         }
259
260         ndr = ndr_push_init_ctx(frame);
261         if (ndr == NULL) {
262                 TALLOC_FREE(frame);
263                 return NT_STATUS_NO_MEMORY;
264         }
265
266         ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
267         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
268                 TALLOC_FREE(frame);
269                 return ndr_map_error2ntstatus(ndr_err);
270         }
271
272         if (out_auth_info != NULL) {
273                 /*
274                  * pad to 16 byte multiple in the payload portion of the
275                  * packet. This matches what w2k3 does. Note that we can't use
276                  * ndr_push_align() as that is relative to the start of the
277                  * whole packet, whereas w2k8 wants it relative to the start
278                  * of the stub.
279                  */
280                 out_auth_info->auth_pad_length =
281                         DCERPC_AUTH_PAD_LENGTH(payload->length);
282                 ndr_err = ndr_push_zero(ndr, out_auth_info->auth_pad_length);
283                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
284                         TALLOC_FREE(frame);
285                         return ndr_map_error2ntstatus(ndr_err);
286                 }
287
288                 payload_length = payload->length +
289                         out_auth_info->auth_pad_length;
290
291                 ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
292                                                out_auth_info);
293                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
294                         TALLOC_FREE(frame);
295                         return ndr_map_error2ntstatus(ndr_err);
296                 }
297
298                 whole_length = ndr->offset;
299
300                 ndr_err = ndr_push_zero(ndr, sig_size);
301                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
302                         TALLOC_FREE(frame);
303                         return ndr_map_error2ntstatus(ndr_err);
304                 }
305         } else {
306                 payload_length = payload->length;
307                 whole_length = ndr->offset;
308         }
309
310         /* extract the whole packet as a blob */
311         blob = ndr_push_blob(ndr);
312
313         /*
314          * Setup the frag and auth length in the packet buffer.
315          * This is needed if the GENSEC mech does AEAD signing
316          * of the packet headers. The signature itself will be
317          * appended later.
318          */
319         dcerpc_set_frag_length(&blob, blob.length);
320         dcerpc_set_auth_length(&blob, sig_size);
321
322         /* sign or seal the packet */
323         switch (auth_state->auth_level) {
324         case DCERPC_AUTH_LEVEL_PRIVACY:
325                 status = gensec_seal_packet(gensec,
326                                             frame,
327                                             blob.data + payload_offset,
328                                             payload_length,
329                                             blob.data,
330                                             whole_length,
331                                             &sig);
332                 if (!NT_STATUS_IS_OK(status)) {
333                         TALLOC_FREE(frame);
334                         return status;
335                 }
336                 break;
337
338         case DCERPC_AUTH_LEVEL_INTEGRITY:
339         case DCERPC_AUTH_LEVEL_PACKET:
340                 status = gensec_sign_packet(gensec,
341                                             frame,
342                                             blob.data + payload_offset,
343                                             payload_length,
344                                             blob.data,
345                                             whole_length,
346                                             &sig);
347                 if (!NT_STATUS_IS_OK(status)) {
348                         TALLOC_FREE(frame);
349                         return status;
350                 }
351                 break;
352
353         case DCERPC_AUTH_LEVEL_CONNECT:
354         case DCERPC_AUTH_LEVEL_NONE:
355                 break;
356
357         default:
358                 TALLOC_FREE(frame);
359                 return NT_STATUS_INTERNAL_ERROR;
360         }
361
362         if (sig.length != sig_size) {
363                 TALLOC_FREE(frame);
364                 return NT_STATUS_RPC_SEC_PKG_ERROR;
365         }
366
367         if (sig_size != 0) {
368                 memcpy(blob.data + whole_length, sig.data, sig_size);
369         }
370
371         *raw_packet = blob;
372         talloc_steal(mem_ctx, raw_packet->data);
373         TALLOC_FREE(frame);
374         return NT_STATUS_OK;
375 }
376
377 #ifdef DEVELOPER
378
379 /*
380  * Save valid, well-formed DCE/RPC stubs to use as a seed for
381  * ndr_fuzz_X
382  */
383 void dcerpc_save_ndr_fuzz_seed(TALLOC_CTX *mem_ctx,
384                                DATA_BLOB raw_blob,
385                                const char *dump_dir,
386                                const char *iface_name,
387                                ndr_flags_type flags,
388                                int opnum,
389                                bool ndr64)
390 {
391         char *fname = NULL;
392         const char *sub_dir = NULL;
393         TALLOC_CTX *temp_ctx = talloc_new(mem_ctx);
394         DATA_BLOB blob;
395         int ret, rc;
396         uint8_t digest[20];
397         DATA_BLOB digest_blob;
398         char *digest_hex;
399         uint16_t fuzz_flags = 0;
400
401         /*
402          * We want to save the 'stub' in a per-pipe subdirectory, with
403          * the ndr_fuzz_X header 4 byte header. For the sake of
404          * convenience (this is a developer only function), we mkdir
405          * -p the sub-directories when they are needed.
406          */
407
408         if (dump_dir == NULL) {
409                 return;
410         }
411
412         temp_ctx = talloc_stackframe();
413
414         sub_dir = talloc_asprintf(temp_ctx, "%s/%s",
415                                   dump_dir,
416                                   iface_name);
417         if (sub_dir == NULL) {
418                 talloc_free(temp_ctx);
419                 return;
420         }
421         ret = mkdir_p(sub_dir, 0755);
422         if (ret && errno != EEXIST) {
423                 DBG_ERR("could not create %s\n", sub_dir);
424                 talloc_free(temp_ctx);
425                 return;
426         }
427
428         blob.length = raw_blob.length + 4;
429         blob.data = talloc_array(sub_dir,
430                                  uint8_t,
431                                  blob.length);
432         if (blob.data == NULL) {
433                 DBG_ERR("could not allocate for fuzz seeds! (%s)\n",
434                         iface_name);
435                 talloc_free(temp_ctx);
436                 return;
437         }
438
439         if (ndr64) {
440                 fuzz_flags = 4;
441         }
442         if (flags & NDR_IN) {
443                 fuzz_flags |= 1;
444         } else if (flags & NDR_OUT) {
445                 fuzz_flags |= 2;
446         }
447
448         SSVAL(blob.data, 0, fuzz_flags);
449         SSVAL(blob.data, 2, opnum);
450
451         memcpy(&blob.data[4],
452                raw_blob.data,
453                raw_blob.length);
454
455         /*
456          * This matches how oss-fuzz names the corpus input files, due
457          * to a preference from libFuzzer
458          */
459         rc = gnutls_hash_fast(GNUTLS_DIG_SHA1,
460                               blob.data,
461                               blob.length,
462                               digest);
463         if (rc < 0) {
464                 /*
465                  * This prints a better error message, eg if SHA1 is
466                  * disabled
467                  */
468                 NTSTATUS status = gnutls_error_to_ntstatus(rc,
469                                                   NT_STATUS_HASH_NOT_SUPPORTED);
470                 DBG_ERR("Failed to generate SHA1 to save fuzz seed: %s\n",
471                         nt_errstr(status));
472                 talloc_free(temp_ctx);
473                 return;
474         }
475
476         digest_blob.data = digest;
477         digest_blob.length = sizeof(digest);
478         digest_hex = data_blob_hex_string_lower(temp_ctx, &digest_blob);
479
480         fname = talloc_asprintf(temp_ctx, "%s/%s",
481                                 sub_dir,
482                                 digest_hex);
483         if (fname == NULL) {
484                 talloc_free(temp_ctx);
485                 return;
486         }
487
488         /*
489          * If this fails, it is most likely because that file already
490          * exists.  This is fine, it means we already have this
491          * sample
492          */
493         file_save(fname,
494                   blob.data,
495                   blob.length);
496
497         talloc_free(temp_ctx);
498 }
499
500 #endif /*if DEVELOPER, enveloping _dcesrv_save_ndr_fuzz_seed() */