2 Unix SMB/CIFS implementation.
4 server side dcerpc authentication code
6 Copyright (C) Andrew Tridgell 2003
7 Copyright (C) Stefan (metze) Metzmacher 2004
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.
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.
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/>.
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"
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)
40 bool dcesrv_auth_bind(struct dcesrv_call_state
*call
)
42 struct cli_credentials
*server_credentials
= NULL
;
43 struct ncacn_packet
*pkt
= &call
->pkt
;
44 struct dcesrv_connection
*dce_conn
= call
->conn
;
45 struct dcesrv_auth
*auth
= &dce_conn
->auth_state
;
48 if (pkt
->auth_length
== 0) {
49 auth
->auth_type
= DCERPC_AUTH_TYPE_NONE
;
50 auth
->auth_level
= DCERPC_AUTH_LEVEL_NONE
;
51 auth
->auth_context_id
= 0;
55 status
= dcerpc_pull_auth_trailer(pkt
, call
, &pkt
->u
.bind
.auth_info
,
58 if (!NT_STATUS_IS_OK(status
)) {
60 * Setting DCERPC_AUTH_LEVEL_NONE,
61 * gives the caller the reject_reason
64 * Note: DCERPC_AUTH_LEVEL_NONE == 1
66 auth
->auth_type
= DCERPC_AUTH_TYPE_NONE
;
67 auth
->auth_level
= DCERPC_AUTH_LEVEL_NONE
;
68 auth
->auth_context_id
=
69 DCERPC_BIND_NAK_REASON_PROTOCOL_VERSION_NOT_SUPPORTED
;
73 switch (call
->in_auth_info
.auth_level
) {
74 case DCERPC_AUTH_LEVEL_CONNECT
:
75 case DCERPC_AUTH_LEVEL_CALL
:
76 case DCERPC_AUTH_LEVEL_PACKET
:
77 case DCERPC_AUTH_LEVEL_INTEGRITY
:
78 case DCERPC_AUTH_LEVEL_PRIVACY
:
80 * We evaluate auth_type only if auth_level was valid
85 * Setting DCERPC_AUTH_LEVEL_NONE,
86 * gives the caller the reject_reason
89 * Note: DCERPC_AUTH_LEVEL_NONE == 1
91 auth
->auth_type
= DCERPC_AUTH_TYPE_NONE
;
92 auth
->auth_level
= DCERPC_AUTH_LEVEL_NONE
;
93 auth
->auth_context_id
= DCERPC_BIND_NAK_REASON_NOT_SPECIFIED
;
97 auth
->auth_type
= call
->in_auth_info
.auth_type
;
98 auth
->auth_level
= call
->in_auth_info
.auth_level
;
99 auth
->auth_context_id
= call
->in_auth_info
.auth_context_id
;
102 = cli_credentials_init(call
);
103 if (!server_credentials
) {
104 DEBUG(1, ("Failed to init server credentials\n"));
108 cli_credentials_set_conf(server_credentials
, call
->conn
->dce_ctx
->lp_ctx
);
109 status
= cli_credentials_set_machine_account(server_credentials
, call
->conn
->dce_ctx
->lp_ctx
);
110 if (!NT_STATUS_IS_OK(status
)) {
111 DEBUG(1, ("Failed to obtain server credentials: %s\n",
116 status
= samba_server_gensec_start(dce_conn
, call
->event_ctx
,
118 call
->conn
->dce_ctx
->lp_ctx
,
121 &auth
->gensec_security
);
122 if (!NT_STATUS_IS_OK(status
)) {
123 DEBUG(1, ("Failed to call samba_server_gensec_start %s\n",
128 if (call
->conn
->remote_address
!= NULL
) {
129 status
= gensec_set_remote_address(auth
->gensec_security
,
130 call
->conn
->remote_address
);
131 if (!NT_STATUS_IS_OK(status
)) {
132 DEBUG(1, ("Failed to call gensec_set_remote_address() %s\n",
138 status
= gensec_start_mech_by_authtype(auth
->gensec_security
, auth
->auth_type
,
140 if (!NT_STATUS_IS_OK(status
)) {
141 const char *backend_name
=
142 gensec_get_name_by_authtype(auth
->gensec_security
,
145 DEBUG(3, ("Failed to start GENSEC mechanism for DCERPC server: "
146 "auth_type=%d (%s), auth_level=%d: %s\n",
147 (int)auth
->auth_type
, backend_name
,
148 (int)auth
->auth_level
,
152 * Setting DCERPC_AUTH_LEVEL_NONE,
153 * gives the caller the reject_reason
154 * as auth_context_id.
156 * Note: DCERPC_AUTH_LEVEL_NONE == 1
158 auth
->auth_type
= DCERPC_AUTH_TYPE_NONE
;
159 auth
->auth_level
= DCERPC_AUTH_LEVEL_NONE
;
160 if (backend_name
!= NULL
) {
161 auth
->auth_context_id
=
162 DCERPC_BIND_NAK_REASON_INVALID_CHECKSUM
;
164 auth
->auth_context_id
=
165 DCERPC_BIND_NAK_REASON_INVALID_AUTH_TYPE
;
174 add any auth information needed in a bind ack, and process the authentication
175 information found in the bind.
177 NTSTATUS
dcesrv_auth_bind_ack(struct dcesrv_call_state
*call
, struct ncacn_packet
*pkt
)
179 struct dcesrv_connection
*dce_conn
= call
->conn
;
181 bool want_header_signing
= false;
183 dce_conn
->allow_alter
= true;
184 dce_conn
->allow_auth3
= true;
186 if (call
->pkt
.auth_length
== 0) {
187 dce_conn
->auth_state
.auth_finished
= true;
188 dce_conn
->allow_request
= true;
192 /* We can't work without an existing gensec state */
193 if (!call
->conn
->auth_state
.gensec_security
) {
194 return NT_STATUS_INTERNAL_ERROR
;
197 if (call
->pkt
.pfc_flags
& DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN
) {
198 dce_conn
->auth_state
.client_hdr_signing
= true;
199 want_header_signing
= true;
202 if (!lpcfg_parm_bool(call
->conn
->dce_ctx
->lp_ctx
, NULL
, "dcesrv","header signing", true)) {
203 want_header_signing
= false;
206 call
->_out_auth_info
= (struct dcerpc_auth
) {
207 .auth_type
= dce_conn
->auth_state
.auth_type
,
208 .auth_level
= dce_conn
->auth_state
.auth_level
,
209 .auth_context_id
= dce_conn
->auth_state
.auth_context_id
,
211 call
->out_auth_info
= &call
->_out_auth_info
;
213 status
= gensec_update_ev(dce_conn
->auth_state
.gensec_security
,
214 call
, call
->event_ctx
,
215 call
->in_auth_info
.credentials
,
216 &call
->out_auth_info
->credentials
);
218 if (NT_STATUS_IS_OK(status
)) {
219 status
= gensec_session_info(dce_conn
->auth_state
.gensec_security
,
221 &dce_conn
->auth_state
.session_info
);
222 if (!NT_STATUS_IS_OK(status
)) {
223 DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status
)));
226 dce_conn
->auth_state
.auth_finished
= true;
227 dce_conn
->allow_request
= true;
229 if (!gensec_have_feature(dce_conn
->auth_state
.gensec_security
,
230 GENSEC_FEATURE_SIGN_PKT_HEADER
))
232 want_header_signing
= false;
235 if (want_header_signing
) {
236 gensec_want_feature(dce_conn
->auth_state
.gensec_security
,
237 GENSEC_FEATURE_SIGN_PKT_HEADER
);
238 dce_conn
->auth_state
.hdr_signing
= true;
239 pkt
->pfc_flags
|= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN
;
242 /* Now that we are authenticated, go back to the generic session key... */
243 dce_conn
->auth_state
.session_key
= dcesrv_generic_session_key
;
245 } else if (NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
246 if (!gensec_have_feature(dce_conn
->auth_state
.gensec_security
,
247 GENSEC_FEATURE_SIGN_PKT_HEADER
))
249 want_header_signing
= false;
252 if (want_header_signing
) {
253 gensec_want_feature(dce_conn
->auth_state
.gensec_security
,
254 GENSEC_FEATURE_SIGN_PKT_HEADER
);
255 dce_conn
->auth_state
.hdr_signing
= true;
256 pkt
->pfc_flags
|= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN
;
261 DEBUG(4, ("GENSEC mech rejected the incoming authentication at bind_ack: %s\n",
269 process the final stage of a auth request
271 bool dcesrv_auth_auth3(struct dcesrv_call_state
*call
)
273 struct ncacn_packet
*pkt
= &call
->pkt
;
274 struct dcesrv_connection
*dce_conn
= call
->conn
;
277 if (pkt
->auth_length
== 0) {
281 if (dce_conn
->auth_state
.auth_finished
) {
285 /* We can't work without an existing gensec state */
286 if (!dce_conn
->auth_state
.gensec_security
) {
290 status
= dcerpc_pull_auth_trailer(pkt
, call
, &pkt
->u
.auth3
.auth_info
,
291 &call
->in_auth_info
, NULL
, true);
292 if (!NT_STATUS_IS_OK(status
)) {
294 * Windows returns DCERPC_NCA_S_FAULT_REMOTE_NO_MEMORY
295 * instead of DCERPC_NCA_S_PROTO_ERROR.
297 call
->fault_code
= DCERPC_NCA_S_FAULT_REMOTE_NO_MEMORY
;
301 if (call
->in_auth_info
.auth_type
!= dce_conn
->auth_state
.auth_type
) {
305 if (call
->in_auth_info
.auth_level
!= dce_conn
->auth_state
.auth_level
) {
309 if (call
->in_auth_info
.auth_context_id
!= dce_conn
->auth_state
.auth_context_id
) {
313 call
->_out_auth_info
= (struct dcerpc_auth
) {
314 .auth_type
= dce_conn
->auth_state
.auth_type
,
315 .auth_level
= dce_conn
->auth_state
.auth_level
,
316 .auth_context_id
= dce_conn
->auth_state
.auth_context_id
,
318 call
->out_auth_info
= &call
->_out_auth_info
;
320 /* Pass the extra data we got from the client down to gensec for processing */
321 status
= gensec_update_ev(dce_conn
->auth_state
.gensec_security
,
322 call
, call
->event_ctx
,
323 call
->in_auth_info
.credentials
,
324 &call
->out_auth_info
->credentials
);
325 if (NT_STATUS_IS_OK(status
)) {
326 status
= gensec_session_info(dce_conn
->auth_state
.gensec_security
,
328 &dce_conn
->auth_state
.session_info
);
329 if (!NT_STATUS_IS_OK(status
)) {
330 DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status
)));
333 dce_conn
->auth_state
.auth_finished
= true;
334 dce_conn
->allow_request
= true;
336 /* Now that we are authenticated, go back to the generic session key... */
337 dce_conn
->auth_state
.session_key
= dcesrv_generic_session_key
;
339 if (call
->out_auth_info
->credentials
.length
!= 0) {
341 DEBUG(4, ("GENSEC produced output token (len=%u) at bind_auth3\n",
342 (unsigned)call
->out_auth_info
->credentials
.length
));
347 DEBUG(4, ("GENSEC mech rejected the incoming authentication at bind_auth3: %s\n",
354 parse any auth information from a dcerpc alter request
355 return false if we can't handle the auth request for some
356 reason (in which case we send a bind_nak (is this true for here?))
358 bool dcesrv_auth_alter(struct dcesrv_call_state
*call
)
360 struct ncacn_packet
*pkt
= &call
->pkt
;
361 struct dcesrv_connection
*dce_conn
= call
->conn
;
364 /* on a pure interface change there is no auth blob */
365 if (pkt
->auth_length
== 0) {
366 if (!dce_conn
->auth_state
.auth_finished
) {
372 if (dce_conn
->auth_state
.auth_finished
) {
373 call
->fault_code
= DCERPC_FAULT_ACCESS_DENIED
;
377 /* We can't work without an existing gensec state */
378 if (!dce_conn
->auth_state
.gensec_security
) {
382 status
= dcerpc_pull_auth_trailer(pkt
, call
, &pkt
->u
.alter
.auth_info
,
383 &call
->in_auth_info
, NULL
, true);
384 if (!NT_STATUS_IS_OK(status
)) {
385 call
->fault_code
= DCERPC_NCA_S_PROTO_ERROR
;
389 if (call
->in_auth_info
.auth_type
== DCERPC_AUTH_TYPE_NONE
) {
390 call
->fault_code
= DCERPC_FAULT_ACCESS_DENIED
;
394 if (call
->in_auth_info
.auth_type
!= dce_conn
->auth_state
.auth_type
) {
398 if (call
->in_auth_info
.auth_level
!= dce_conn
->auth_state
.auth_level
) {
402 if (call
->in_auth_info
.auth_context_id
!= dce_conn
->auth_state
.auth_context_id
) {
410 add any auth information needed in a alter ack, and process the authentication
411 information found in the alter.
413 NTSTATUS
dcesrv_auth_alter_ack(struct dcesrv_call_state
*call
, struct ncacn_packet
*pkt
)
415 struct dcesrv_connection
*dce_conn
= call
->conn
;
418 /* on a pure interface change there is no auth_info structure
420 if (call
->pkt
.auth_length
== 0) {
424 if (!call
->conn
->auth_state
.gensec_security
) {
425 return NT_STATUS_INTERNAL_ERROR
;
428 call
->_out_auth_info
= (struct dcerpc_auth
) {
429 .auth_type
= dce_conn
->auth_state
.auth_type
,
430 .auth_level
= dce_conn
->auth_state
.auth_level
,
431 .auth_context_id
= dce_conn
->auth_state
.auth_context_id
,
433 call
->out_auth_info
= &call
->_out_auth_info
;
435 status
= gensec_update_ev(dce_conn
->auth_state
.gensec_security
,
436 call
, call
->event_ctx
,
437 call
->in_auth_info
.credentials
,
438 &call
->out_auth_info
->credentials
);
440 if (NT_STATUS_IS_OK(status
)) {
441 status
= gensec_session_info(dce_conn
->auth_state
.gensec_security
,
443 &dce_conn
->auth_state
.session_info
);
444 if (!NT_STATUS_IS_OK(status
)) {
445 DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status
)));
448 dce_conn
->auth_state
.auth_finished
= true;
449 dce_conn
->allow_request
= true;
451 /* Now that we are authenticated, got back to the generic session key... */
452 dce_conn
->auth_state
.session_key
= dcesrv_generic_session_key
;
454 } else if (NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
458 DEBUG(4, ("GENSEC mech rejected the incoming authentication at auth alter_ack: %s\n",
464 check credentials on a request
466 bool dcesrv_auth_request(struct dcesrv_call_state
*call
, DATA_BLOB
*full_packet
)
468 struct ncacn_packet
*pkt
= &call
->pkt
;
469 struct dcesrv_connection
*dce_conn
= call
->conn
;
470 const struct dcerpc_auth tmp_auth
= {
471 .auth_type
= dce_conn
->auth_state
.auth_type
,
472 .auth_level
= dce_conn
->auth_state
.auth_level
,
473 .auth_context_id
= dce_conn
->auth_state
.auth_context_id
,
476 uint8_t payload_offset
= DCERPC_REQUEST_LENGTH
;
478 if (!dce_conn
->allow_request
) {
479 call
->fault_code
= DCERPC_NCA_S_PROTO_ERROR
;
483 if (dce_conn
->auth_state
.auth_invalid
) {
487 if (pkt
->pfc_flags
& DCERPC_PFC_FLAG_OBJECT_UUID
) {
488 payload_offset
+= 16;
491 status
= dcerpc_ncacn_pull_pkt_auth(&tmp_auth
,
492 dce_conn
->auth_state
.gensec_security
,
495 0, /* required_flags */
496 DCERPC_PFC_FLAG_FIRST
|
497 DCERPC_PFC_FLAG_LAST
|
498 DCERPC_PFC_FLAG_PENDING_CANCEL
|
499 0x08 | /* this is not defined, but should be ignored */
500 DCERPC_PFC_FLAG_CONC_MPX
|
501 DCERPC_PFC_FLAG_DID_NOT_EXECUTE
|
502 DCERPC_PFC_FLAG_MAYBE
|
503 DCERPC_PFC_FLAG_OBJECT_UUID
,
505 &pkt
->u
.request
.stub_and_verifier
,
508 if (NT_STATUS_EQUAL(status
, NT_STATUS_RPC_PROTOCOL_ERROR
)) {
509 call
->fault_code
= DCERPC_NCA_S_PROTO_ERROR
;
512 if (NT_STATUS_EQUAL(status
, NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL
)) {
513 call
->fault_code
= DCERPC_NCA_S_UNSUPPORTED_AUTHN_LEVEL
;
516 if (NT_STATUS_EQUAL(status
, NT_STATUS_RPC_SEC_PKG_ERROR
)) {
517 call
->fault_code
= DCERPC_FAULT_SEC_PKG_ERROR
;
520 if (NT_STATUS_EQUAL(status
, NT_STATUS_ACCESS_DENIED
)) {
521 call
->fault_code
= DCERPC_FAULT_ACCESS_DENIED
;
524 if (!NT_STATUS_IS_OK(status
)) {
533 push a signed or sealed dcerpc request packet into a blob
535 bool dcesrv_auth_response(struct dcesrv_call_state
*call
,
536 DATA_BLOB
*blob
, size_t sig_size
,
537 struct ncacn_packet
*pkt
)
539 struct dcesrv_connection
*dce_conn
= call
->conn
;
541 enum ndr_err_code ndr_err
;
542 struct ndr_push
*ndr
;
543 uint32_t payload_length
;
546 switch (dce_conn
->auth_state
.auth_level
) {
547 case DCERPC_AUTH_LEVEL_PRIVACY
:
548 case DCERPC_AUTH_LEVEL_INTEGRITY
:
549 case DCERPC_AUTH_LEVEL_PACKET
:
556 case DCERPC_AUTH_LEVEL_CONNECT
:
558 * TODO: let the gensec mech decide if it wants to generate a
559 * signature that might be needed for schannel...
561 status
= ncacn_push_auth(blob
, call
, pkt
, NULL
);
562 return NT_STATUS_IS_OK(status
);
564 case DCERPC_AUTH_LEVEL_NONE
:
565 status
= ncacn_push_auth(blob
, call
, pkt
, NULL
);
566 return NT_STATUS_IS_OK(status
);
572 if (!dce_conn
->auth_state
.gensec_security
) {
576 ndr
= ndr_push_init_ctx(call
);
581 if (!(pkt
->drep
[0] & DCERPC_DREP_LE
)) {
582 ndr
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
585 ndr_err
= ndr_push_ncacn_packet(ndr
, NDR_SCALARS
|NDR_BUFFERS
, pkt
);
586 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
590 call
->_out_auth_info
= (struct dcerpc_auth
) {
591 .auth_type
= dce_conn
->auth_state
.auth_type
,
592 .auth_level
= dce_conn
->auth_state
.auth_level
,
593 .auth_context_id
= dce_conn
->auth_state
.auth_context_id
,
595 call
->out_auth_info
= &call
->_out_auth_info
;
597 /* pad to 16 byte multiple in the payload portion of the
598 packet. This matches what w2k3 does. Note that we can't use
599 ndr_push_align() as that is relative to the start of the
600 whole packet, whereas w2k8 wants it relative to the start
602 call
->out_auth_info
->auth_pad_length
=
603 DCERPC_AUTH_PAD_LENGTH(pkt
->u
.response
.stub_and_verifier
.length
);
604 ndr_err
= ndr_push_zero(ndr
, call
->out_auth_info
->auth_pad_length
);
605 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
609 payload_length
= pkt
->u
.response
.stub_and_verifier
.length
+
610 call
->out_auth_info
->auth_pad_length
;
612 /* add the auth verifier */
613 ndr_err
= ndr_push_dcerpc_auth(ndr
, NDR_SCALARS
|NDR_BUFFERS
,
614 call
->out_auth_info
);
615 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
619 /* extract the whole packet as a blob */
620 *blob
= ndr_push_blob(ndr
);
623 * Setup the frag and auth length in the packet buffer.
624 * This is needed if the GENSEC mech does AEAD signing
625 * of the packet headers. The signature itself will be
628 dcerpc_set_frag_length(blob
, blob
->length
+ sig_size
);
629 dcerpc_set_auth_length(blob
, sig_size
);
631 /* sign or seal the packet */
632 switch (dce_conn
->auth_state
.auth_level
) {
633 case DCERPC_AUTH_LEVEL_PRIVACY
:
634 status
= gensec_seal_packet(dce_conn
->auth_state
.gensec_security
,
636 ndr
->data
+ DCERPC_REQUEST_LENGTH
,
643 case DCERPC_AUTH_LEVEL_INTEGRITY
:
644 case DCERPC_AUTH_LEVEL_PACKET
:
645 status
= gensec_sign_packet(dce_conn
->auth_state
.gensec_security
,
647 ndr
->data
+ DCERPC_REQUEST_LENGTH
,
655 status
= NT_STATUS_INVALID_LEVEL
;
659 if (!NT_STATUS_IS_OK(status
)) {
663 if (creds2
.length
!= sig_size
) {
664 DEBUG(3,("dcesrv_auth_response: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n",
665 (unsigned)creds2
.length
, (uint32_t)sig_size
,
666 (unsigned)call
->out_auth_info
->auth_pad_length
,
667 (unsigned)pkt
->u
.response
.stub_and_verifier
.length
));
668 dcerpc_set_frag_length(blob
, blob
->length
+ creds2
.length
);
669 dcerpc_set_auth_length(blob
, creds2
.length
);
672 if (!data_blob_append(call
, blob
, creds2
.data
, creds2
.length
)) {
673 status
= NT_STATUS_NO_MEMORY
;
676 data_blob_free(&creds2
);