Line data Source code
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/rpc/dcerpc_util.h"
24 : #include "librpc/gen_ndr/ndr_dcerpc.h"
25 : #include "librpc/crypto/gse.h"
26 : #include "auth/gensec/gensec.h"
27 :
28 : #undef DBGC_CLASS
29 : #define DBGC_CLASS DBGC_RPC_PARSE
30 :
31 : /**
32 : * @brief NDR Encodes a ncacn_packet
33 : *
34 : * @param mem_ctx The memory context the blob will be allocated on
35 : * @param ptype The DCERPC packet type
36 : * @param pfc_flags The DCERPC PFC Falgs
37 : * @param auth_length The length of the trailing auth blob
38 : * @param call_id The call ID
39 : * @param u The payload of the packet
40 : * @param blob [out] The encoded blob if successful
41 : *
42 : * @return an NTSTATUS error code
43 : */
44 6949 : NTSTATUS dcerpc_push_ncacn_packet(TALLOC_CTX *mem_ctx,
45 : enum dcerpc_pkt_type ptype,
46 : uint8_t pfc_flags,
47 : uint16_t auth_length,
48 : uint32_t call_id,
49 : union dcerpc_payload *u,
50 : DATA_BLOB *blob)
51 : {
52 : struct ncacn_packet r;
53 : enum ndr_err_code ndr_err;
54 :
55 6949 : r.rpc_vers = 5;
56 6949 : r.rpc_vers_minor = 0;
57 6949 : r.ptype = ptype;
58 6949 : r.pfc_flags = pfc_flags;
59 6949 : r.drep[0] = DCERPC_DREP_LE;
60 6949 : r.drep[1] = 0;
61 6949 : r.drep[2] = 0;
62 6949 : r.drep[3] = 0;
63 6949 : r.auth_length = auth_length;
64 6949 : r.call_id = call_id;
65 6949 : r.u = *u;
66 :
67 6949 : ndr_err = ndr_push_struct_blob(blob, mem_ctx, &r,
68 : (ndr_push_flags_fn_t)ndr_push_ncacn_packet);
69 6949 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
70 0 : return ndr_map_error2ntstatus(ndr_err);
71 : }
72 :
73 6949 : dcerpc_set_frag_length(blob, blob->length);
74 :
75 :
76 6949 : if (DEBUGLEVEL >= 10) {
77 : /* set frag len for print function */
78 0 : r.frag_length = blob->length;
79 0 : NDR_PRINT_DEBUG(ncacn_packet, &r);
80 : }
81 :
82 6949 : return NT_STATUS_OK;
83 : }
84 :
85 : /**
86 : * @brief NDR Encodes a dcerpc_auth structure
87 : *
88 : * @param mem_ctx The memory context the blob will be allocated on
89 : * @param auth_type The DCERPC Authentication Type
90 : * @param auth_level The DCERPC Authentication Level
91 : * @param auth_pad_length The padding added to the packet this blob will be
92 : * appended to.
93 : * @param auth_context_id The context id
94 : * @param credentials The authentication credentials blob (signature)
95 : * @param blob [out] The encoded blob if successful
96 : *
97 : * @return a NTSTATUS error code
98 : */
99 193 : NTSTATUS dcerpc_push_dcerpc_auth(TALLOC_CTX *mem_ctx,
100 : enum dcerpc_AuthType auth_type,
101 : enum dcerpc_AuthLevel auth_level,
102 : uint8_t auth_pad_length,
103 : uint32_t auth_context_id,
104 : const DATA_BLOB *credentials,
105 : DATA_BLOB *blob)
106 : {
107 : struct dcerpc_auth r;
108 : enum ndr_err_code ndr_err;
109 :
110 193 : r.auth_type = auth_type;
111 193 : r.auth_level = auth_level;
112 193 : r.auth_pad_length = auth_pad_length;
113 193 : r.auth_reserved = 0;
114 193 : r.auth_context_id = auth_context_id;
115 193 : r.credentials = *credentials;
116 :
117 193 : ndr_err = ndr_push_struct_blob(blob, mem_ctx, &r,
118 : (ndr_push_flags_fn_t)ndr_push_dcerpc_auth);
119 193 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
120 0 : return ndr_map_error2ntstatus(ndr_err);
121 : }
122 :
123 193 : if (DEBUGLEVEL >= 10) {
124 0 : NDR_PRINT_DEBUG(dcerpc_auth, &r);
125 : }
126 :
127 193 : return NT_STATUS_OK;
128 : }
129 :
130 : /**
131 : * @brief Calculate how much data we can in a packet, including calculating
132 : * auth token and pad lengths.
133 : *
134 : * @param auth The pipe_auth_data structure for this pipe.
135 : * @param header_len The length of the packet header
136 : * @param data_left The data left in the send buffer
137 : * @param max_xmit_frag The max fragment size.
138 : * @param data_to_send [out] The max data we will send in the pdu
139 : * @param frag_len [out] The total length of the fragment
140 : * @param auth_len [out] The length of the auth trailer
141 : * @param pad_len [out] The padding to be applied
142 : *
143 : * @return A NT Error status code.
144 : */
145 6074 : NTSTATUS dcerpc_guess_sizes(struct pipe_auth_data *auth,
146 : size_t header_len, size_t data_left,
147 : size_t max_xmit_frag,
148 : size_t *data_to_send, size_t *frag_len,
149 : size_t *auth_len, size_t *pad_len)
150 : {
151 : size_t max_len;
152 : size_t mod_len;
153 : struct gensec_security *gensec_security;
154 :
155 : /* no auth token cases first */
156 6074 : switch (auth->auth_level) {
157 5982 : case DCERPC_AUTH_LEVEL_NONE:
158 : case DCERPC_AUTH_LEVEL_CONNECT:
159 5982 : max_len = max_xmit_frag - header_len;
160 5982 : *data_to_send = MIN(max_len, data_left);
161 5982 : *pad_len = 0;
162 5982 : *auth_len = 0;
163 5982 : *frag_len = header_len + *data_to_send;
164 5982 : return NT_STATUS_OK;
165 :
166 82 : case DCERPC_AUTH_LEVEL_PRIVACY:
167 82 : break;
168 :
169 6 : case DCERPC_AUTH_LEVEL_INTEGRITY:
170 6 : break;
171 :
172 4 : case DCERPC_AUTH_LEVEL_PACKET:
173 4 : break;
174 :
175 0 : default:
176 0 : return NT_STATUS_INVALID_PARAMETER;
177 : }
178 :
179 :
180 : /* Sign/seal case, calculate auth and pad lengths */
181 :
182 92 : max_len = max_xmit_frag - header_len - DCERPC_AUTH_TRAILER_LENGTH;
183 :
184 : /* Treat the same for all authenticated rpc requests. */
185 92 : switch (auth->auth_type) {
186 92 : case DCERPC_AUTH_TYPE_SPNEGO:
187 : case DCERPC_AUTH_TYPE_NTLMSSP:
188 : case DCERPC_AUTH_TYPE_KRB5:
189 : case DCERPC_AUTH_TYPE_SCHANNEL:
190 92 : gensec_security = auth->auth_ctx;
191 92 : mod_len = (max_len % DCERPC_AUTH_PAD_ALIGNMENT);
192 92 : *auth_len = gensec_sig_size(gensec_security, max_len - mod_len);
193 92 : if (*auth_len == 0) {
194 0 : return NT_STATUS_INTERNAL_ERROR;
195 : }
196 92 : break;
197 0 : default:
198 0 : return NT_STATUS_INVALID_PARAMETER;
199 : }
200 :
201 92 : max_len -= *auth_len;
202 92 : mod_len = (max_len % DCERPC_AUTH_PAD_ALIGNMENT);
203 92 : max_len -= mod_len;
204 :
205 92 : *data_to_send = MIN(max_len, data_left);
206 :
207 92 : *pad_len = DCERPC_AUTH_PAD_LENGTH(*data_to_send);
208 :
209 153 : *frag_len = header_len + *data_to_send + *pad_len
210 92 : + DCERPC_AUTH_TRAILER_LENGTH + *auth_len;
211 :
212 92 : return NT_STATUS_OK;
213 : }
214 :
215 : /*******************************************************************
216 : Create and add the NTLMSSP sign/seal auth data.
217 : ********************************************************************/
218 :
219 92 : static NTSTATUS add_generic_auth_footer(struct gensec_security *gensec_security,
220 : enum dcerpc_AuthLevel auth_level,
221 : DATA_BLOB *rpc_out)
222 : {
223 153 : uint16_t data_and_pad_len = rpc_out->length
224 : - DCERPC_RESPONSE_LENGTH
225 92 : - DCERPC_AUTH_TRAILER_LENGTH;
226 : DATA_BLOB auth_blob;
227 : NTSTATUS status;
228 :
229 92 : if (!gensec_security) {
230 0 : return NT_STATUS_INVALID_PARAMETER;
231 : }
232 :
233 92 : switch (auth_level) {
234 82 : case DCERPC_AUTH_LEVEL_PRIVACY:
235 : /* Data portion is encrypted. */
236 250 : status = gensec_seal_packet(gensec_security,
237 82 : rpc_out->data,
238 82 : rpc_out->data
239 : + DCERPC_RESPONSE_LENGTH,
240 : data_and_pad_len,
241 82 : rpc_out->data,
242 : rpc_out->length,
243 : &auth_blob);
244 82 : if (!NT_STATUS_IS_OK(status)) {
245 0 : return status;
246 : }
247 82 : break;
248 :
249 10 : case DCERPC_AUTH_LEVEL_INTEGRITY:
250 : case DCERPC_AUTH_LEVEL_PACKET:
251 : /* Data is signed. */
252 20 : status = gensec_sign_packet(gensec_security,
253 10 : rpc_out->data,
254 10 : rpc_out->data
255 10 : + DCERPC_RESPONSE_LENGTH,
256 : data_and_pad_len,
257 10 : rpc_out->data,
258 : rpc_out->length,
259 : &auth_blob);
260 10 : if (!NT_STATUS_IS_OK(status)) {
261 0 : return status;
262 : }
263 10 : break;
264 :
265 0 : default:
266 : /* Can't happen. */
267 0 : smb_panic("bad auth level");
268 : /* Notreached. */
269 : return NT_STATUS_INVALID_PARAMETER;
270 : }
271 :
272 : /* Finally attach the blob. */
273 153 : if (!data_blob_append(NULL, rpc_out,
274 92 : auth_blob.data, auth_blob.length)) {
275 0 : DEBUG(0, ("Failed to add %u bytes auth blob.\n",
276 : (unsigned int)auth_blob.length));
277 0 : return NT_STATUS_NO_MEMORY;
278 : }
279 92 : data_blob_free(&auth_blob);
280 :
281 92 : return NT_STATUS_OK;
282 : }
283 :
284 : /*******************************************************************
285 : Check/unseal the NTLMSSP auth data. (Unseal in place).
286 : ********************************************************************/
287 :
288 92 : static NTSTATUS get_generic_auth_footer(struct gensec_security *gensec_security,
289 : enum dcerpc_AuthLevel auth_level,
290 : DATA_BLOB *data, DATA_BLOB *full_pkt,
291 : DATA_BLOB *auth_token)
292 : {
293 92 : if (gensec_security == NULL) {
294 0 : return NT_STATUS_INVALID_PARAMETER;
295 : }
296 :
297 92 : switch (auth_level) {
298 82 : case DCERPC_AUTH_LEVEL_PRIVACY:
299 : /* Data portion is encrypted. */
300 138 : return gensec_unseal_packet(gensec_security,
301 : data->data,
302 : data->length,
303 82 : full_pkt->data,
304 : full_pkt->length,
305 : auth_token);
306 :
307 10 : case DCERPC_AUTH_LEVEL_INTEGRITY:
308 : case DCERPC_AUTH_LEVEL_PACKET:
309 : /* Data is signed. */
310 20 : return gensec_check_packet(gensec_security,
311 10 : data->data,
312 : data->length,
313 10 : full_pkt->data,
314 : full_pkt->length,
315 : auth_token);
316 :
317 0 : default:
318 0 : return NT_STATUS_INVALID_PARAMETER;
319 : }
320 : }
321 :
322 : /**
323 : * @brief Append an auth footer according to what is the current mechanism
324 : *
325 : * @param auth The pipe_auth_data associated with the connection
326 : * @param pad_len The padding used in the packet
327 : * @param rpc_out Packet blob up to and including the auth header
328 : *
329 : * @return A NTSTATUS error code.
330 : */
331 92 : NTSTATUS dcerpc_add_auth_footer(struct pipe_auth_data *auth,
332 : size_t pad_len, DATA_BLOB *rpc_out)
333 : {
334 : struct gensec_security *gensec_security;
335 92 : const char pad[DCERPC_AUTH_PAD_ALIGNMENT] = { 0, };
336 : DATA_BLOB auth_info;
337 : DATA_BLOB auth_blob;
338 : NTSTATUS status;
339 :
340 92 : if (auth->auth_type == DCERPC_AUTH_TYPE_NONE) {
341 0 : return NT_STATUS_OK;
342 : }
343 :
344 92 : if (pad_len) {
345 36 : SMB_ASSERT(pad_len <= ARRAY_SIZE(pad));
346 :
347 : /* Copy the sign/seal padding data. */
348 36 : if (!data_blob_append(NULL, rpc_out, pad, pad_len)) {
349 0 : return NT_STATUS_NO_MEMORY;
350 : }
351 : }
352 :
353 : /* marshall the dcerpc_auth with an actually empty auth_blob.
354 : * This is needed because the ntmlssp signature includes the
355 : * auth header. We will append the actual blob later. */
356 92 : auth_blob = data_blob_null;
357 92 : status = dcerpc_push_dcerpc_auth(rpc_out->data,
358 : auth->auth_type,
359 : auth->auth_level,
360 : pad_len,
361 : auth->auth_context_id,
362 : &auth_blob,
363 : &auth_info);
364 92 : if (!NT_STATUS_IS_OK(status)) {
365 0 : return status;
366 : }
367 :
368 : /* append the header */
369 153 : if (!data_blob_append(NULL, rpc_out,
370 92 : auth_info.data, auth_info.length)) {
371 0 : DEBUG(0, ("Failed to add %u bytes auth blob.\n",
372 : (unsigned int)auth_info.length));
373 0 : return NT_STATUS_NO_MEMORY;
374 : }
375 92 : data_blob_free(&auth_info);
376 :
377 : /* Generate any auth sign/seal and add the auth footer. */
378 92 : switch (auth->auth_type) {
379 0 : case DCERPC_AUTH_TYPE_NONE:
380 0 : status = NT_STATUS_OK;
381 0 : break;
382 92 : default:
383 92 : gensec_security = auth->auth_ctx;
384 92 : status = add_generic_auth_footer(gensec_security,
385 : auth->auth_level,
386 : rpc_out);
387 92 : break;
388 : }
389 :
390 92 : return status;
391 : }
392 :
393 : /**
394 : * @brief Check authentication for request/response packets
395 : *
396 : * @param auth The auth data for the connection
397 : * @param pkt The actual ncacn_packet
398 : * @param pkt_trailer [in][out] The stub_and_verifier part of the packet,
399 : * the auth_trailer and padding will be removed.
400 : * @param header_size The header size
401 : * @param raw_pkt The whole raw packet data blob
402 : *
403 : * @return A NTSTATUS error code
404 : */
405 6158 : NTSTATUS dcerpc_check_auth(struct pipe_auth_data *auth,
406 : struct ncacn_packet *pkt,
407 : DATA_BLOB *pkt_trailer,
408 : uint8_t header_size,
409 : DATA_BLOB *raw_pkt)
410 : {
411 : struct gensec_security *gensec_security;
412 : NTSTATUS status;
413 : struct dcerpc_auth auth_info;
414 : uint32_t auth_length;
415 : DATA_BLOB full_pkt;
416 : DATA_BLOB data;
417 :
418 : /*
419 : * These check should be done in the caller.
420 : */
421 6158 : SMB_ASSERT(raw_pkt->length == pkt->frag_length);
422 6158 : SMB_ASSERT(header_size <= pkt->frag_length);
423 6158 : SMB_ASSERT(pkt_trailer->length < pkt->frag_length);
424 6158 : SMB_ASSERT((pkt_trailer->length + header_size) <= pkt->frag_length);
425 :
426 6158 : switch (auth->auth_level) {
427 82 : case DCERPC_AUTH_LEVEL_PRIVACY:
428 82 : DEBUG(10, ("Requested Privacy.\n"));
429 82 : break;
430 :
431 6 : case DCERPC_AUTH_LEVEL_INTEGRITY:
432 6 : DEBUG(10, ("Requested Integrity.\n"));
433 6 : break;
434 :
435 4 : case DCERPC_AUTH_LEVEL_PACKET:
436 4 : DEBUG(10, ("Requested packet.\n"));
437 4 : break;
438 :
439 8 : case DCERPC_AUTH_LEVEL_CONNECT:
440 8 : if (pkt->auth_length != 0) {
441 0 : break;
442 : }
443 2330 : return NT_STATUS_OK;
444 :
445 6058 : case DCERPC_AUTH_LEVEL_NONE:
446 6058 : if (pkt->auth_length != 0) {
447 0 : DEBUG(3, ("Got non-zero auth len on non "
448 : "authenticated connection!\n"));
449 0 : return NT_STATUS_INVALID_PARAMETER;
450 : }
451 6058 : return NT_STATUS_OK;
452 :
453 0 : default:
454 0 : DEBUG(3, ("Unimplemented Auth Level %d",
455 : auth->auth_level));
456 0 : return NT_STATUS_INVALID_PARAMETER;
457 : }
458 :
459 92 : if (pkt->auth_length == 0) {
460 0 : return NT_STATUS_INVALID_PARAMETER;
461 : }
462 :
463 92 : status = dcerpc_pull_auth_trailer(pkt, pkt, pkt_trailer,
464 : &auth_info, &auth_length, false);
465 92 : if (!NT_STATUS_IS_OK(status)) {
466 0 : return status;
467 : }
468 :
469 92 : if (auth_info.auth_type != auth->auth_type) {
470 0 : return NT_STATUS_INVALID_PARAMETER;
471 : }
472 :
473 92 : if (auth_info.auth_level != auth->auth_level) {
474 0 : return NT_STATUS_INVALID_PARAMETER;
475 : }
476 :
477 92 : if (auth_info.auth_context_id != auth->auth_context_id) {
478 0 : return NT_STATUS_INVALID_PARAMETER;
479 : }
480 :
481 92 : pkt_trailer->length -= auth_length;
482 92 : data = data_blob_const(raw_pkt->data + header_size,
483 : pkt_trailer->length);
484 92 : full_pkt = data_blob_const(raw_pkt->data, raw_pkt->length);
485 92 : full_pkt.length -= auth_info.credentials.length;
486 :
487 92 : switch (auth->auth_type) {
488 0 : case DCERPC_AUTH_TYPE_NONE:
489 0 : return NT_STATUS_OK;
490 :
491 92 : default:
492 92 : DEBUG(10, ("GENSEC auth\n"));
493 :
494 92 : gensec_security = auth->auth_ctx;
495 92 : status = get_generic_auth_footer(gensec_security,
496 : auth->auth_level,
497 : &data, &full_pkt,
498 : &auth_info.credentials);
499 92 : if (!NT_STATUS_IS_OK(status)) {
500 0 : return status;
501 : }
502 92 : break;
503 : }
504 :
505 : /* TODO: remove later
506 : * this is still needed because in the server code the
507 : * pkt_trailer actually has a copy of the raw data, and they
508 : * are still both used in later calls */
509 92 : if (auth->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
510 82 : if (pkt_trailer->length != data.length) {
511 0 : return NT_STATUS_INVALID_PARAMETER;
512 : }
513 82 : memcpy(pkt_trailer->data, data.data, data.length);
514 : }
515 :
516 92 : pkt_trailer->length -= auth_info.auth_pad_length;
517 92 : data_blob_free(&auth_info.credentials);
518 92 : return NT_STATUS_OK;
519 : }
520 :
|