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 Flags
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 588446 : 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 0 : struct ncacn_packet r;
53 0 : enum ndr_err_code ndr_err;
54 :
55 588446 : r.rpc_vers = 5;
56 588446 : r.rpc_vers_minor = 0;
57 588446 : r.ptype = ptype;
58 588446 : r.pfc_flags = pfc_flags;
59 588446 : r.drep[0] = DCERPC_DREP_LE;
60 588446 : r.drep[1] = 0;
61 588446 : r.drep[2] = 0;
62 588446 : r.drep[3] = 0;
63 588446 : r.auth_length = auth_length;
64 588446 : r.call_id = call_id;
65 588446 : r.u = *u;
66 :
67 588446 : ndr_err = ndr_push_struct_blob(blob, mem_ctx, &r,
68 : (ndr_push_flags_fn_t)ndr_push_ncacn_packet);
69 588446 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
70 0 : return ndr_map_error2ntstatus(ndr_err);
71 : }
72 :
73 588446 : dcerpc_set_frag_length(blob, blob->length);
74 :
75 :
76 588446 : if (DEBUGLEVEL >= 10) {
77 : /* set frag len for print function */
78 98 : r.frag_length = blob->length;
79 98 : NDR_PRINT_DEBUG(ncacn_packet, &r);
80 : }
81 :
82 588446 : 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 1075 : 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 0 : struct dcerpc_auth r;
108 0 : enum ndr_err_code ndr_err;
109 :
110 1075 : r.auth_type = auth_type;
111 1075 : r.auth_level = auth_level;
112 1075 : r.auth_pad_length = auth_pad_length;
113 1075 : r.auth_reserved = 0;
114 1075 : r.auth_context_id = auth_context_id;
115 1075 : r.credentials = *credentials;
116 :
117 1075 : ndr_err = ndr_push_struct_blob(blob, mem_ctx, &r,
118 : (ndr_push_flags_fn_t)ndr_push_dcerpc_auth);
119 1075 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
120 0 : return ndr_map_error2ntstatus(ndr_err);
121 : }
122 :
123 1075 : if (DEBUGLEVEL >= 10) {
124 0 : NDR_PRINT_DEBUG(dcerpc_auth, &r);
125 : }
126 :
127 1075 : 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 547245 : 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 0 : size_t max_len;
152 0 : size_t mod_len;
153 0 : struct gensec_security *gensec_security;
154 :
155 : /* no auth token cases first */
156 547245 : switch (auth->auth_level) {
157 546879 : case DCERPC_AUTH_LEVEL_NONE:
158 : case DCERPC_AUTH_LEVEL_CONNECT:
159 546879 : max_len = max_xmit_frag - header_len;
160 546879 : *data_to_send = MIN(max_len, data_left);
161 546879 : *pad_len = 0;
162 546879 : *auth_len = 0;
163 546879 : *frag_len = header_len + *data_to_send;
164 546879 : return NT_STATUS_OK;
165 :
166 223 : case DCERPC_AUTH_LEVEL_PRIVACY:
167 223 : break;
168 :
169 89 : case DCERPC_AUTH_LEVEL_INTEGRITY:
170 89 : break;
171 :
172 54 : case DCERPC_AUTH_LEVEL_PACKET:
173 54 : 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 366 : max_len = max_xmit_frag - header_len - DCERPC_AUTH_TRAILER_LENGTH;
183 :
184 : /* Treat the same for all authenticated rpc requests. */
185 366 : switch (auth->auth_type) {
186 366 : case DCERPC_AUTH_TYPE_SPNEGO:
187 : case DCERPC_AUTH_TYPE_NTLMSSP:
188 : case DCERPC_AUTH_TYPE_KRB5:
189 : case DCERPC_AUTH_TYPE_SCHANNEL:
190 366 : gensec_security = auth->auth_ctx;
191 366 : mod_len = (max_len % DCERPC_AUTH_PAD_ALIGNMENT);
192 366 : *auth_len = gensec_sig_size(gensec_security, max_len - mod_len);
193 366 : if (*auth_len == 0) {
194 0 : return NT_STATUS_INTERNAL_ERROR;
195 : }
196 366 : break;
197 0 : default:
198 0 : return NT_STATUS_INVALID_PARAMETER;
199 : }
200 :
201 366 : max_len -= *auth_len;
202 366 : mod_len = (max_len % DCERPC_AUTH_PAD_ALIGNMENT);
203 366 : max_len -= mod_len;
204 :
205 366 : *data_to_send = MIN(max_len, data_left);
206 :
207 366 : *pad_len = DCERPC_AUTH_PAD_LENGTH(*data_to_send);
208 :
209 366 : *frag_len = header_len + *data_to_send + *pad_len
210 366 : + DCERPC_AUTH_TRAILER_LENGTH + *auth_len;
211 :
212 366 : return NT_STATUS_OK;
213 : }
214 :
215 : /*******************************************************************
216 : Create and add the NTLMSSP sign/seal auth data.
217 : ********************************************************************/
218 :
219 366 : static NTSTATUS add_generic_auth_footer(struct gensec_security *gensec_security,
220 : enum dcerpc_AuthLevel auth_level,
221 : DATA_BLOB *rpc_out)
222 : {
223 366 : uint16_t data_and_pad_len = rpc_out->length
224 : - DCERPC_RESPONSE_LENGTH
225 366 : - DCERPC_AUTH_TRAILER_LENGTH;
226 0 : DATA_BLOB auth_blob;
227 0 : NTSTATUS status;
228 :
229 366 : if (!gensec_security) {
230 0 : return NT_STATUS_INVALID_PARAMETER;
231 : }
232 :
233 366 : switch (auth_level) {
234 223 : case DCERPC_AUTH_LEVEL_PRIVACY:
235 : /* Data portion is encrypted. */
236 223 : status = gensec_seal_packet(gensec_security,
237 223 : rpc_out->data,
238 223 : rpc_out->data
239 : + DCERPC_RESPONSE_LENGTH,
240 : data_and_pad_len,
241 223 : rpc_out->data,
242 : rpc_out->length,
243 : &auth_blob);
244 223 : if (!NT_STATUS_IS_OK(status)) {
245 0 : return status;
246 : }
247 223 : break;
248 :
249 143 : case DCERPC_AUTH_LEVEL_INTEGRITY:
250 : case DCERPC_AUTH_LEVEL_PACKET:
251 : /* Data is signed. */
252 143 : status = gensec_sign_packet(gensec_security,
253 143 : rpc_out->data,
254 143 : rpc_out->data
255 143 : + DCERPC_RESPONSE_LENGTH,
256 : data_and_pad_len,
257 143 : rpc_out->data,
258 : rpc_out->length,
259 : &auth_blob);
260 143 : if (!NT_STATUS_IS_OK(status)) {
261 0 : return status;
262 : }
263 143 : 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 366 : if (!data_blob_append(NULL, rpc_out,
274 366 : 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 366 : data_blob_free(&auth_blob);
280 :
281 366 : return NT_STATUS_OK;
282 : }
283 :
284 : /*******************************************************************
285 : Check/unseal the NTLMSSP auth data. (Unseal in place).
286 : ********************************************************************/
287 :
288 366 : 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 366 : if (gensec_security == NULL) {
294 0 : return NT_STATUS_INVALID_PARAMETER;
295 : }
296 :
297 366 : switch (auth_level) {
298 223 : case DCERPC_AUTH_LEVEL_PRIVACY:
299 : /* Data portion is encrypted. */
300 223 : return gensec_unseal_packet(gensec_security,
301 : data->data,
302 : data->length,
303 223 : full_pkt->data,
304 : full_pkt->length,
305 : auth_token);
306 :
307 143 : case DCERPC_AUTH_LEVEL_INTEGRITY:
308 : case DCERPC_AUTH_LEVEL_PACKET:
309 : /* Data is signed. */
310 143 : return gensec_check_packet(gensec_security,
311 143 : data->data,
312 : data->length,
313 143 : 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 366 : NTSTATUS dcerpc_add_auth_footer(struct pipe_auth_data *auth,
332 : size_t pad_len, DATA_BLOB *rpc_out)
333 : {
334 0 : struct gensec_security *gensec_security;
335 366 : const char pad[DCERPC_AUTH_PAD_ALIGNMENT] = { 0, };
336 0 : DATA_BLOB auth_info;
337 0 : DATA_BLOB auth_blob;
338 0 : NTSTATUS status;
339 :
340 366 : if (auth->auth_type == DCERPC_AUTH_TYPE_NONE) {
341 0 : return NT_STATUS_OK;
342 : }
343 :
344 366 : if (pad_len) {
345 190 : SMB_ASSERT(pad_len <= ARRAY_SIZE(pad));
346 :
347 : /* Copy the sign/seal padding data. */
348 190 : 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 366 : auth_blob = data_blob_null;
357 366 : 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 366 : if (!NT_STATUS_IS_OK(status)) {
365 0 : return status;
366 : }
367 :
368 : /* append the header */
369 366 : if (!data_blob_append(NULL, rpc_out,
370 366 : 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 366 : data_blob_free(&auth_info);
376 :
377 : /* Generate any auth sign/seal and add the auth footer. */
378 366 : switch (auth->auth_type) {
379 0 : case DCERPC_AUTH_TYPE_NONE:
380 0 : status = NT_STATUS_OK;
381 0 : break;
382 366 : default:
383 366 : gensec_security = auth->auth_ctx;
384 366 : status = add_generic_auth_footer(gensec_security,
385 : auth->auth_level,
386 : rpc_out);
387 366 : break;
388 : }
389 :
390 366 : 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 547409 : 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 0 : struct gensec_security *gensec_security;
412 0 : NTSTATUS status;
413 0 : struct dcerpc_auth auth_info;
414 0 : uint32_t auth_length;
415 0 : DATA_BLOB full_pkt;
416 0 : DATA_BLOB data;
417 :
418 : /*
419 : * These check should be done in the caller.
420 : */
421 547409 : SMB_ASSERT(raw_pkt->length == pkt->frag_length);
422 547409 : SMB_ASSERT(header_size <= pkt->frag_length);
423 547409 : SMB_ASSERT(pkt_trailer->length < pkt->frag_length);
424 547409 : SMB_ASSERT((pkt_trailer->length + header_size) <= pkt->frag_length);
425 :
426 547409 : switch (auth->auth_level) {
427 223 : case DCERPC_AUTH_LEVEL_PRIVACY:
428 223 : DEBUG(10, ("Requested Privacy.\n"));
429 223 : break;
430 :
431 89 : case DCERPC_AUTH_LEVEL_INTEGRITY:
432 89 : DEBUG(10, ("Requested Integrity.\n"));
433 89 : break;
434 :
435 54 : case DCERPC_AUTH_LEVEL_PACKET:
436 54 : DEBUG(10, ("Requested packet.\n"));
437 54 : break;
438 :
439 136 : case DCERPC_AUTH_LEVEL_CONNECT:
440 136 : if (pkt->auth_length != 0) {
441 0 : break;
442 : }
443 547043 : return NT_STATUS_OK;
444 :
445 546907 : case DCERPC_AUTH_LEVEL_NONE:
446 546907 : 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 546907 : return NT_STATUS_OK;
452 :
453 0 : default:
454 0 : DEBUG(3, ("Unimplemented Auth Level %d\n",
455 : auth->auth_level));
456 0 : return NT_STATUS_INVALID_PARAMETER;
457 : }
458 :
459 366 : if (pkt->auth_length == 0) {
460 0 : return NT_STATUS_INVALID_PARAMETER;
461 : }
462 :
463 366 : status = dcerpc_pull_auth_trailer(pkt, pkt, pkt_trailer,
464 : &auth_info, &auth_length, false);
465 366 : if (!NT_STATUS_IS_OK(status)) {
466 0 : return status;
467 : }
468 :
469 366 : if (auth_info.auth_type != auth->auth_type) {
470 0 : return NT_STATUS_INVALID_PARAMETER;
471 : }
472 :
473 366 : if (auth_info.auth_level != auth->auth_level) {
474 0 : return NT_STATUS_INVALID_PARAMETER;
475 : }
476 :
477 366 : if (auth_info.auth_context_id != auth->auth_context_id) {
478 0 : return NT_STATUS_INVALID_PARAMETER;
479 : }
480 :
481 366 : pkt_trailer->length -= auth_length;
482 366 : data = data_blob_const(raw_pkt->data + header_size,
483 : pkt_trailer->length);
484 366 : full_pkt = data_blob_const(raw_pkt->data, raw_pkt->length);
485 366 : full_pkt.length -= auth_info.credentials.length;
486 :
487 366 : switch (auth->auth_type) {
488 0 : case DCERPC_AUTH_TYPE_NONE:
489 0 : return NT_STATUS_OK;
490 :
491 366 : default:
492 366 : DEBUG(10, ("GENSEC auth\n"));
493 :
494 366 : gensec_security = auth->auth_ctx;
495 366 : status = get_generic_auth_footer(gensec_security,
496 : auth->auth_level,
497 : &data, &full_pkt,
498 : &auth_info.credentials);
499 366 : if (!NT_STATUS_IS_OK(status)) {
500 0 : return status;
501 : }
502 366 : 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 366 : if (auth->auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
510 223 : if (pkt_trailer->length != data.length) {
511 0 : return NT_STATUS_INVALID_PARAMETER;
512 : }
513 223 : memcpy(pkt_trailer->data, data.data, data.length);
514 : }
515 :
516 366 : pkt_trailer->length -= auth_info.auth_pad_length;
517 366 : data_blob_free(&auth_info.credentials);
518 366 : return NT_STATUS_OK;
519 : }
520 :
|