2 Unix SMB/CIFS implementation.
4 server side dcerpc core code
6 Copyright (C) Andrew Tridgell 2003-2005
7 Copyright (C) Stefan (metze) Metzmacher 2004-2005
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 "librpc/gen_ndr/ndr_dcerpc.h"
25 #include "auth/auth.h"
26 #include "auth/gensec/gensec.h"
27 #include "lib/util/dlinklist.h"
28 #include "rpc_server/dcerpc_server.h"
29 #include "rpc_server/dcerpc_server_proto.h"
30 #include "librpc/rpc/dcerpc_proto.h"
31 #include "lib/events/events.h"
32 #include "smbd/service_task.h"
33 #include "smbd/service_stream.h"
34 #include "smbd/service.h"
35 #include "system/filesys.h"
36 #include "libcli/security/security.h"
37 #include "param/param.h"
39 #define SAMBA_ACCOC_GROUP 0x12345678
41 extern const struct dcesrv_interface dcesrv_mgmt_interface
;
44 see if two endpoints match
46 static bool endpoints_match(const struct dcerpc_binding
*ep1
,
47 const struct dcerpc_binding
*ep2
)
49 if (ep1
->transport
!= ep2
->transport
) {
53 if (!ep1
->endpoint
|| !ep2
->endpoint
) {
54 return ep1
->endpoint
== ep2
->endpoint
;
57 if (strcasecmp(ep1
->endpoint
, ep2
->endpoint
) != 0)
64 find an endpoint in the dcesrv_context
66 static struct dcesrv_endpoint
*find_endpoint(struct dcesrv_context
*dce_ctx
,
67 const struct dcerpc_binding
*ep_description
)
69 struct dcesrv_endpoint
*ep
;
70 for (ep
=dce_ctx
->endpoint_list
; ep
; ep
=ep
->next
) {
71 if (endpoints_match(ep
->ep_description
, ep_description
)) {
79 find a registered context_id from a bind or alter_context
81 static struct dcesrv_connection_context
*dcesrv_find_context(struct dcesrv_connection
*conn
,
84 struct dcesrv_connection_context
*c
;
85 for (c
=conn
->contexts
;c
;c
=c
->next
) {
86 if (c
->context_id
== context_id
) return c
;
92 see if a uuid and if_version match to an interface
94 static bool interface_match(const struct dcesrv_interface
*if1
,
95 const struct dcesrv_interface
*if2
)
97 return (if1
->syntax_id
.if_version
== if2
->syntax_id
.if_version
&&
98 GUID_equal(&if1
->syntax_id
.uuid
, &if2
->syntax_id
.uuid
));
102 find the interface operations on an endpoint
104 static const struct dcesrv_interface
*find_interface(const struct dcesrv_endpoint
*endpoint
,
105 const struct dcesrv_interface
*iface
)
107 struct dcesrv_if_list
*ifl
;
108 for (ifl
=endpoint
->interface_list
; ifl
; ifl
=ifl
->next
) {
109 if (interface_match(&(ifl
->iface
), iface
)) {
110 return &(ifl
->iface
);
117 see if a uuid and if_version match to an interface
119 static bool interface_match_by_uuid(const struct dcesrv_interface
*iface
,
120 const struct GUID
*uuid
, uint32_t if_version
)
122 return (iface
->syntax_id
.if_version
== if_version
&&
123 GUID_equal(&iface
->syntax_id
.uuid
, uuid
));
127 find the interface operations on an endpoint by uuid
129 static const struct dcesrv_interface
*find_interface_by_uuid(const struct dcesrv_endpoint
*endpoint
,
130 const struct GUID
*uuid
, uint32_t if_version
)
132 struct dcesrv_if_list
*ifl
;
133 for (ifl
=endpoint
->interface_list
; ifl
; ifl
=ifl
->next
) {
134 if (interface_match_by_uuid(&(ifl
->iface
), uuid
, if_version
)) {
135 return &(ifl
->iface
);
142 find the earlier parts of a fragmented call awaiting reassembily
144 static struct dcesrv_call_state
*dcesrv_find_fragmented_call(struct dcesrv_connection
*dce_conn
, uint16_t call_id
)
146 struct dcesrv_call_state
*c
;
147 for (c
=dce_conn
->incoming_fragmented_call_list
;c
;c
=c
->next
) {
148 if (c
->pkt
.call_id
== call_id
) {
156 register an interface on an endpoint
158 _PUBLIC_ NTSTATUS
dcesrv_interface_register(struct dcesrv_context
*dce_ctx
,
160 const struct dcesrv_interface
*iface
,
161 const struct security_descriptor
*sd
)
163 struct dcesrv_endpoint
*ep
;
164 struct dcesrv_if_list
*ifl
;
165 struct dcerpc_binding
*binding
;
169 status
= dcerpc_parse_binding(dce_ctx
, ep_name
, &binding
);
171 if (NT_STATUS_IS_ERR(status
)) {
172 DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name
));
176 /* check if this endpoint exists
178 if ((ep
=find_endpoint(dce_ctx
, binding
))==NULL
) {
179 ep
= talloc(dce_ctx
, struct dcesrv_endpoint
);
181 return NT_STATUS_NO_MEMORY
;
184 ep
->ep_description
= talloc_reference(ep
, binding
);
187 /* add mgmt interface */
188 ifl
= talloc(dce_ctx
, struct dcesrv_if_list
);
190 return NT_STATUS_NO_MEMORY
;
193 memcpy(&(ifl
->iface
), &dcesrv_mgmt_interface
,
194 sizeof(struct dcesrv_interface
));
196 DLIST_ADD(ep
->interface_list
, ifl
);
199 /* see if the interface is already registered on te endpoint */
200 if (find_interface(ep
, iface
)!=NULL
) {
201 DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
202 iface
->name
, ep_name
));
203 return NT_STATUS_OBJECT_NAME_COLLISION
;
206 /* talloc a new interface list element */
207 ifl
= talloc(dce_ctx
, struct dcesrv_if_list
);
209 return NT_STATUS_NO_MEMORY
;
212 /* copy the given interface struct to the one on the endpoints interface list */
213 memcpy(&(ifl
->iface
),iface
, sizeof(struct dcesrv_interface
));
215 /* if we have a security descriptor given,
216 * we should see if we can set it up on the endpoint
219 /* if there's currently no security descriptor given on the endpoint
222 if (ep
->sd
== NULL
) {
223 ep
->sd
= security_descriptor_copy(dce_ctx
, sd
);
226 /* if now there's no security descriptor given on the endpoint
227 * something goes wrong, either we failed to copy the security descriptor
228 * or there was already one on the endpoint
230 if (ep
->sd
!= NULL
) {
231 DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
232 " on endpoint '%s'\n",
233 iface
->name
, ep_name
));
234 if (add_ep
) free(ep
);
236 return NT_STATUS_OBJECT_NAME_COLLISION
;
240 /* finally add the interface on the endpoint */
241 DLIST_ADD(ep
->interface_list
, ifl
);
243 /* if it's a new endpoint add it to the dcesrv_context */
245 DLIST_ADD(dce_ctx
->endpoint_list
, ep
);
248 DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
249 iface
->name
, ep_name
));
254 static NTSTATUS
dcesrv_inherited_session_key(struct dcesrv_connection
*p
,
255 DATA_BLOB
*session_key
)
257 if (p
->auth_state
.session_info
->session_key
.length
) {
258 *session_key
= p
->auth_state
.session_info
->session_key
;
261 return NT_STATUS_NO_USER_SESSION_KEY
;
264 NTSTATUS
dcesrv_generic_session_key(struct dcesrv_connection
*p
,
265 DATA_BLOB
*session_key
)
267 /* this took quite a few CPU cycles to find ... */
268 session_key
->data
= discard_const_p(uint8_t, "SystemLibraryDTC");
269 session_key
->length
= 16;
274 fetch the user session key - may be default (above) or the SMB session key
276 The key is always truncated to 16 bytes
278 _PUBLIC_ NTSTATUS
dcesrv_fetch_session_key(struct dcesrv_connection
*p
,
279 DATA_BLOB
*session_key
)
281 NTSTATUS status
= p
->auth_state
.session_key(p
, session_key
);
282 if (!NT_STATUS_IS_OK(status
)) {
286 session_key
->length
= MIN(session_key
->length
, 16);
293 destroy a link to an endpoint
295 static int dcesrv_endpoint_destructor(struct dcesrv_connection
*p
)
297 while (p
->contexts
) {
298 struct dcesrv_connection_context
*c
= p
->contexts
;
300 DLIST_REMOVE(p
->contexts
, c
);
303 c
->iface
->unbind(c
, c
->iface
);
312 connect to a dcerpc endpoint
314 _PUBLIC_ NTSTATUS
dcesrv_endpoint_connect(struct dcesrv_context
*dce_ctx
,
316 const struct dcesrv_endpoint
*ep
,
317 struct auth_session_info
*session_info
,
318 struct event_context
*event_ctx
,
319 struct messaging_context
*msg_ctx
,
320 struct server_id server_id
,
321 uint32_t state_flags
,
322 struct dcesrv_connection
**_p
)
324 struct dcesrv_connection
*p
;
327 return NT_STATUS_ACCESS_DENIED
;
330 p
= talloc(mem_ctx
, struct dcesrv_connection
);
331 NT_STATUS_HAVE_NO_MEMORY(p
);
333 if (!talloc_reference(p
, session_info
)) {
335 return NT_STATUS_NO_MEMORY
;
338 p
->dce_ctx
= dce_ctx
;
342 p
->incoming_fragmented_call_list
= NULL
;
343 p
->pending_call_list
= NULL
;
344 p
->cli_max_recv_frag
= 0;
345 p
->partial_input
= data_blob(NULL
, 0);
346 p
->auth_state
.auth_info
= NULL
;
347 p
->auth_state
.gensec_security
= NULL
;
348 p
->auth_state
.session_info
= session_info
;
349 p
->auth_state
.session_key
= dcesrv_generic_session_key
;
350 p
->event_ctx
= event_ctx
;
351 p
->msg_ctx
= msg_ctx
;
352 p
->server_id
= server_id
;
353 p
->processing
= false;
354 p
->state_flags
= state_flags
;
355 ZERO_STRUCT(p
->transport
);
357 talloc_set_destructor(p
, dcesrv_endpoint_destructor
);
364 search and connect to a dcerpc endpoint
366 _PUBLIC_ NTSTATUS
dcesrv_endpoint_search_connect(struct dcesrv_context
*dce_ctx
,
368 const struct dcerpc_binding
*ep_description
,
369 struct auth_session_info
*session_info
,
370 struct event_context
*event_ctx
,
371 struct messaging_context
*msg_ctx
,
372 struct server_id server_id
,
373 uint32_t state_flags
,
374 struct dcesrv_connection
**dce_conn_p
)
377 const struct dcesrv_endpoint
*ep
;
379 /* make sure this endpoint exists */
380 ep
= find_endpoint(dce_ctx
, ep_description
);
382 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
385 status
= dcesrv_endpoint_connect(dce_ctx
, mem_ctx
, ep
, session_info
,
386 event_ctx
, msg_ctx
, server_id
,
387 state_flags
, dce_conn_p
);
388 NT_STATUS_NOT_OK_RETURN(status
);
390 (*dce_conn_p
)->auth_state
.session_key
= dcesrv_inherited_session_key
;
392 /* TODO: check security descriptor of the endpoint here
393 * if it's a smb named pipe
394 * if it's failed free dce_conn_p
401 static void dcesrv_init_hdr(struct ncacn_packet
*pkt
, bool bigendian
)
404 pkt
->rpc_vers_minor
= 0;
408 pkt
->drep
[0] = DCERPC_DREP_LE
;
416 move a call from an existing linked list to the specified list. This
417 prevents bugs where we forget to remove the call from a previous
420 static void dcesrv_call_set_list(struct dcesrv_call_state
*call
,
421 enum dcesrv_call_list list
)
423 switch (call
->list
) {
424 case DCESRV_LIST_NONE
:
426 case DCESRV_LIST_CALL_LIST
:
427 DLIST_REMOVE(call
->conn
->call_list
, call
);
429 case DCESRV_LIST_FRAGMENTED_CALL_LIST
:
430 DLIST_REMOVE(call
->conn
->incoming_fragmented_call_list
, call
);
432 case DCESRV_LIST_PENDING_CALL_LIST
:
433 DLIST_REMOVE(call
->conn
->pending_call_list
, call
);
438 case DCESRV_LIST_NONE
:
440 case DCESRV_LIST_CALL_LIST
:
441 DLIST_ADD_END(call
->conn
->call_list
, call
, struct dcesrv_call_state
*);
443 case DCESRV_LIST_FRAGMENTED_CALL_LIST
:
444 DLIST_ADD_END(call
->conn
->incoming_fragmented_call_list
, call
, struct dcesrv_call_state
*);
446 case DCESRV_LIST_PENDING_CALL_LIST
:
447 DLIST_ADD_END(call
->conn
->pending_call_list
, call
, struct dcesrv_call_state
*);
453 return a dcerpc fault
455 static NTSTATUS
dcesrv_fault(struct dcesrv_call_state
*call
, uint32_t fault_code
)
457 struct ncacn_packet pkt
;
458 struct data_blob_list_item
*rep
;
462 /* setup a bind_ack */
463 dcesrv_init_hdr(&pkt
, lp_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
465 pkt
.call_id
= call
->pkt
.call_id
;
466 pkt
.ptype
= DCERPC_PKT_FAULT
;
467 pkt
.pfc_flags
= DCERPC_PFC_FLAG_FIRST
| DCERPC_PFC_FLAG_LAST
;
468 pkt
.u
.fault
.alloc_hint
= 0;
469 pkt
.u
.fault
.context_id
= 0;
470 pkt
.u
.fault
.cancel_count
= 0;
471 pkt
.u
.fault
.status
= fault_code
;
474 pkt
.u
.fault
._pad
= data_blob_const(zeros
, sizeof(zeros
));
476 rep
= talloc(call
, struct data_blob_list_item
);
478 return NT_STATUS_NO_MEMORY
;
481 status
= ncacn_push_auth(&rep
->blob
, call
, lp_iconv_convenience(call
->conn
->dce_ctx
->lp_ctx
), &pkt
, NULL
);
482 if (!NT_STATUS_IS_OK(status
)) {
486 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
488 DLIST_ADD_END(call
->replies
, rep
, struct data_blob_list_item
*);
489 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
496 return a dcerpc bind_nak
498 static NTSTATUS
dcesrv_bind_nak(struct dcesrv_call_state
*call
, uint32_t reason
)
500 struct ncacn_packet pkt
;
501 struct data_blob_list_item
*rep
;
504 /* setup a bind_nak */
505 dcesrv_init_hdr(&pkt
, lp_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
507 pkt
.call_id
= call
->pkt
.call_id
;
508 pkt
.ptype
= DCERPC_PKT_BIND_NAK
;
509 pkt
.pfc_flags
= DCERPC_PFC_FLAG_FIRST
| DCERPC_PFC_FLAG_LAST
;
510 pkt
.u
.bind_nak
.reject_reason
= reason
;
511 if (pkt
.u
.bind_nak
.reject_reason
== DECRPC_BIND_PROTOCOL_VERSION_NOT_SUPPORTED
) {
512 pkt
.u
.bind_nak
.versions
.v
.num_versions
= 0;
515 rep
= talloc(call
, struct data_blob_list_item
);
517 return NT_STATUS_NO_MEMORY
;
520 status
= ncacn_push_auth(&rep
->blob
, call
, lp_iconv_convenience(call
->conn
->dce_ctx
->lp_ctx
), &pkt
, NULL
);
521 if (!NT_STATUS_IS_OK(status
)) {
525 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
527 DLIST_ADD_END(call
->replies
, rep
, struct data_blob_list_item
*);
528 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
535 handle a bind request
537 static NTSTATUS
dcesrv_bind(struct dcesrv_call_state
*call
)
539 uint32_t if_version
, transfer_syntax_version
;
540 struct GUID uuid
, *transfer_syntax_uuid
;
541 struct ncacn_packet pkt
;
542 struct data_blob_list_item
*rep
;
544 uint32_t result
=0, reason
=0;
546 const struct dcesrv_interface
*iface
;
547 uint32_t extra_flags
= 0;
550 * Association groups allow policy handles to be shared across
551 * multiple client connections. We don't implement this yet.
553 * So we just allow 0 if the client wants to create a new
556 * And we allow the 0x12345678 value, we give away as
557 * assoc_group_id back to the clients
559 if (call
->pkt
.u
.bind
.assoc_group_id
!= 0 &&
560 call
->pkt
.u
.bind
.assoc_group_id
!= SAMBA_ACCOC_GROUP
) {
561 return dcesrv_bind_nak(call
, 0);
564 if (call
->pkt
.u
.bind
.num_contexts
< 1 ||
565 call
->pkt
.u
.bind
.ctx_list
[0].num_transfer_syntaxes
< 1) {
566 return dcesrv_bind_nak(call
, 0);
569 context_id
= call
->pkt
.u
.bind
.ctx_list
[0].context_id
;
571 /* you can't bind twice on one context */
572 if (dcesrv_find_context(call
->conn
, context_id
) != NULL
) {
573 return dcesrv_bind_nak(call
, 0);
576 if_version
= call
->pkt
.u
.bind
.ctx_list
[0].abstract_syntax
.if_version
;
577 uuid
= call
->pkt
.u
.bind
.ctx_list
[0].abstract_syntax
.uuid
;
579 transfer_syntax_version
= call
->pkt
.u
.bind
.ctx_list
[0].transfer_syntaxes
[0].if_version
;
580 transfer_syntax_uuid
= &call
->pkt
.u
.bind
.ctx_list
[0].transfer_syntaxes
[0].uuid
;
581 if (!GUID_equal(&ndr_transfer_syntax
.uuid
, transfer_syntax_uuid
) != 0 ||
582 ndr_transfer_syntax
.if_version
!= transfer_syntax_version
) {
583 char *uuid_str
= GUID_string(call
, transfer_syntax_uuid
);
584 /* we only do NDR encoded dcerpc */
585 DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str
));
586 talloc_free(uuid_str
);
587 return dcesrv_bind_nak(call
, 0);
590 iface
= find_interface_by_uuid(call
->conn
->endpoint
, &uuid
, if_version
);
592 char *uuid_str
= GUID_string(call
, &uuid
);
593 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str
, if_version
));
594 talloc_free(uuid_str
);
596 /* we don't know about that interface */
597 result
= DCERPC_BIND_PROVIDER_REJECT
;
598 reason
= DCERPC_BIND_REASON_ASYNTAX
;
602 /* add this context to the list of available context_ids */
603 struct dcesrv_connection_context
*context
= talloc(call
->conn
,
604 struct dcesrv_connection_context
);
605 if (context
== NULL
) {
606 return dcesrv_bind_nak(call
, 0);
608 context
->conn
= call
->conn
;
609 context
->iface
= iface
;
610 context
->context_id
= context_id
;
611 context
->private = NULL
;
612 context
->handles
= NULL
;
613 DLIST_ADD(call
->conn
->contexts
, context
);
614 call
->context
= context
;
617 if (call
->conn
->cli_max_recv_frag
== 0) {
618 call
->conn
->cli_max_recv_frag
= call
->pkt
.u
.bind
.max_recv_frag
;
621 if ((call
->pkt
.pfc_flags
& DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN
) &&
622 lp_parm_bool(call
->conn
->dce_ctx
->lp_ctx
, NULL
, "dcesrv","header signing", false)) {
623 call
->conn
->state_flags
|= DCESRV_CALL_STATE_FLAG_HEADER_SIGNING
;
624 extra_flags
|= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN
;
627 /* handle any authentication that is being requested */
628 if (!dcesrv_auth_bind(call
)) {
629 return dcesrv_bind_nak(call
, DCERPC_BIND_REASON_INVALID_AUTH_TYPE
);
632 /* setup a bind_ack */
633 dcesrv_init_hdr(&pkt
, lp_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
635 pkt
.call_id
= call
->pkt
.call_id
;
636 pkt
.ptype
= DCERPC_PKT_BIND_ACK
;
637 pkt
.pfc_flags
= DCERPC_PFC_FLAG_FIRST
| DCERPC_PFC_FLAG_LAST
| extra_flags
;
638 pkt
.u
.bind_ack
.max_xmit_frag
= 0x2000;
639 pkt
.u
.bind_ack
.max_recv_frag
= 0x2000;
640 /* we need to send a non zero assoc_group_id here to make longhorn happy, it also matches samba3 */
641 pkt
.u
.bind_ack
.assoc_group_id
= SAMBA_ACCOC_GROUP
;
643 /* FIXME: Use pipe name as specified by endpoint instead of interface name */
644 pkt
.u
.bind_ack
.secondary_address
= talloc_asprintf(call
, "\\PIPE\\%s", iface
->name
);
646 pkt
.u
.bind_ack
.secondary_address
= "";
648 pkt
.u
.bind_ack
.num_results
= 1;
649 pkt
.u
.bind_ack
.ctx_list
= talloc(call
, struct dcerpc_ack_ctx
);
650 if (!pkt
.u
.bind_ack
.ctx_list
) {
651 return NT_STATUS_NO_MEMORY
;
653 pkt
.u
.bind_ack
.ctx_list
[0].result
= result
;
654 pkt
.u
.bind_ack
.ctx_list
[0].reason
= reason
;
655 pkt
.u
.bind_ack
.ctx_list
[0].syntax
= ndr_transfer_syntax
;
656 pkt
.u
.bind_ack
.auth_info
= data_blob(NULL
, 0);
658 status
= dcesrv_auth_bind_ack(call
, &pkt
);
659 if (!NT_STATUS_IS_OK(status
)) {
660 return dcesrv_bind_nak(call
, 0);
664 status
= iface
->bind(call
, iface
);
665 if (!NT_STATUS_IS_OK(status
)) {
666 char *uuid_str
= GUID_string(call
, &uuid
);
667 DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
668 uuid_str
, if_version
, nt_errstr(status
)));
669 talloc_free(uuid_str
);
670 return dcesrv_bind_nak(call
, 0);
674 rep
= talloc(call
, struct data_blob_list_item
);
676 return NT_STATUS_NO_MEMORY
;
679 status
= ncacn_push_auth(&rep
->blob
, call
, lp_iconv_convenience(call
->conn
->dce_ctx
->lp_ctx
), &pkt
, call
->conn
->auth_state
.auth_info
);
680 if (!NT_STATUS_IS_OK(status
)) {
684 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
686 DLIST_ADD_END(call
->replies
, rep
, struct data_blob_list_item
*);
687 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
694 handle a auth3 request
696 static NTSTATUS
dcesrv_auth3(struct dcesrv_call_state
*call
)
698 /* handle the auth3 in the auth code */
699 if (!dcesrv_auth_auth3(call
)) {
700 return dcesrv_fault(call
, DCERPC_FAULT_OTHER
);
705 /* we don't send a reply to a auth3 request, except by a
712 handle a bind request
714 static NTSTATUS
dcesrv_alter_new_context(struct dcesrv_call_state
*call
, uint32_t context_id
)
716 uint32_t if_version
, transfer_syntax_version
;
717 struct dcesrv_connection_context
*context
;
718 const struct dcesrv_interface
*iface
;
719 struct GUID uuid
, *transfer_syntax_uuid
;
722 if_version
= call
->pkt
.u
.alter
.ctx_list
[0].abstract_syntax
.if_version
;
723 uuid
= call
->pkt
.u
.alter
.ctx_list
[0].abstract_syntax
.uuid
;
725 transfer_syntax_version
= call
->pkt
.u
.alter
.ctx_list
[0].transfer_syntaxes
[0].if_version
;
726 transfer_syntax_uuid
= &call
->pkt
.u
.alter
.ctx_list
[0].transfer_syntaxes
[0].uuid
;
727 if (!GUID_equal(transfer_syntax_uuid
, &ndr_transfer_syntax
.uuid
) ||
728 ndr_transfer_syntax
.if_version
!= transfer_syntax_version
) {
729 /* we only do NDR encoded dcerpc */
730 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED
;
733 iface
= find_interface_by_uuid(call
->conn
->endpoint
, &uuid
, if_version
);
735 char *uuid_str
= GUID_string(call
, &uuid
);
736 DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str
, if_version
));
737 talloc_free(uuid_str
);
738 return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED
;
741 /* add this context to the list of available context_ids */
742 context
= talloc(call
->conn
, struct dcesrv_connection_context
);
743 if (context
== NULL
) {
744 return NT_STATUS_NO_MEMORY
;
746 context
->conn
= call
->conn
;
747 context
->iface
= iface
;
748 context
->context_id
= context_id
;
749 context
->private = NULL
;
750 context
->handles
= NULL
;
751 DLIST_ADD(call
->conn
->contexts
, context
);
752 call
->context
= context
;
755 status
= iface
->bind(call
, iface
);
756 if (!NT_STATUS_IS_OK(status
)) {
766 handle a alter context request
768 static NTSTATUS
dcesrv_alter(struct dcesrv_call_state
*call
)
770 struct ncacn_packet pkt
;
771 struct data_blob_list_item
*rep
;
773 uint32_t result
=0, reason
=0;
776 /* handle any authentication that is being requested */
777 if (!dcesrv_auth_alter(call
)) {
778 /* TODO: work out the right reject code */
779 result
= DCERPC_BIND_PROVIDER_REJECT
;
780 reason
= DCERPC_BIND_REASON_ASYNTAX
;
783 context_id
= call
->pkt
.u
.alter
.ctx_list
[0].context_id
;
785 /* see if they are asking for a new interface */
787 dcesrv_find_context(call
->conn
, context_id
) == NULL
) {
788 status
= dcesrv_alter_new_context(call
, context_id
);
789 if (!NT_STATUS_IS_OK(status
)) {
790 result
= DCERPC_BIND_PROVIDER_REJECT
;
791 reason
= DCERPC_BIND_REASON_ASYNTAX
;
795 /* setup a alter_resp */
796 dcesrv_init_hdr(&pkt
, lp_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
798 pkt
.call_id
= call
->pkt
.call_id
;
799 pkt
.ptype
= DCERPC_PKT_ALTER_RESP
;
800 pkt
.pfc_flags
= DCERPC_PFC_FLAG_FIRST
| DCERPC_PFC_FLAG_LAST
;
801 pkt
.u
.alter_resp
.max_xmit_frag
= 0x2000;
802 pkt
.u
.alter_resp
.max_recv_frag
= 0x2000;
803 pkt
.u
.alter_resp
.assoc_group_id
= call
->pkt
.u
.alter
.assoc_group_id
;
804 pkt
.u
.alter_resp
.num_results
= 1;
805 pkt
.u
.alter_resp
.ctx_list
= talloc_array(call
, struct dcerpc_ack_ctx
, 1);
806 if (!pkt
.u
.alter_resp
.ctx_list
) {
807 return NT_STATUS_NO_MEMORY
;
809 pkt
.u
.alter_resp
.ctx_list
[0].result
= result
;
810 pkt
.u
.alter_resp
.ctx_list
[0].reason
= reason
;
811 pkt
.u
.alter_resp
.ctx_list
[0].syntax
= ndr_transfer_syntax
;
812 pkt
.u
.alter_resp
.auth_info
= data_blob(NULL
, 0);
813 pkt
.u
.alter_resp
.secondary_address
= "";
815 status
= dcesrv_auth_alter_ack(call
, &pkt
);
816 if (!NT_STATUS_IS_OK(status
)) {
817 if (NT_STATUS_EQUAL(status
, NT_STATUS_ACCESS_DENIED
)
818 || NT_STATUS_EQUAL(status
, NT_STATUS_LOGON_FAILURE
)
819 || NT_STATUS_EQUAL(status
, NT_STATUS_NO_SUCH_USER
)
820 || NT_STATUS_EQUAL(status
, NT_STATUS_WRONG_PASSWORD
)) {
821 return dcesrv_fault(call
, DCERPC_FAULT_ACCESS_DENIED
);
823 return dcesrv_fault(call
, 0);
826 rep
= talloc(call
, struct data_blob_list_item
);
828 return NT_STATUS_NO_MEMORY
;
831 status
= ncacn_push_auth(&rep
->blob
, call
, lp_iconv_convenience(call
->conn
->dce_ctx
->lp_ctx
), &pkt
, call
->conn
->auth_state
.auth_info
);
832 if (!NT_STATUS_IS_OK(status
)) {
836 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
838 DLIST_ADD_END(call
->replies
, rep
, struct data_blob_list_item
*);
839 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
845 handle a dcerpc request packet
847 static NTSTATUS
dcesrv_request(struct dcesrv_call_state
*call
)
849 struct ndr_pull
*pull
;
851 struct dcesrv_connection_context
*context
;
853 /* if authenticated, and the mech we use can't do async replies, don't use them... */
854 if (call
->conn
->auth_state
.gensec_security
&&
855 !gensec_have_feature(call
->conn
->auth_state
.gensec_security
, GENSEC_FEATURE_ASYNC_REPLIES
)) {
856 call
->state_flags
&= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC
;
859 context
= dcesrv_find_context(call
->conn
, call
->pkt
.u
.request
.context_id
);
860 if (context
== NULL
) {
861 return dcesrv_fault(call
, DCERPC_FAULT_UNK_IF
);
864 pull
= ndr_pull_init_blob(&call
->pkt
.u
.request
.stub_and_verifier
, call
,
865 lp_iconv_convenience(call
->conn
->dce_ctx
->lp_ctx
));
866 NT_STATUS_HAVE_NO_MEMORY(pull
);
868 pull
->flags
|= LIBNDR_FLAG_REF_ALLOC
;
870 call
->context
= context
;
871 call
->ndr_pull
= pull
;
873 if (call
->pkt
.pfc_flags
& DCERPC_PFC_FLAG_OBJECT_UUID
) {
874 pull
->flags
|= LIBNDR_FLAG_OBJECT_PRESENT
;
877 if (!(call
->pkt
.drep
[0] & DCERPC_DREP_LE
)) {
878 pull
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
881 /* unravel the NDR for the packet */
882 status
= context
->iface
->ndr_pull(call
, call
, pull
, &call
->r
);
883 if (!NT_STATUS_IS_OK(status
)) {
884 return dcesrv_fault(call
, call
->fault_code
);
887 if (pull
->offset
!= pull
->data_size
) {
888 DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
889 pull
->data_size
- pull
->offset
));
890 dump_data(10, pull
->data
+pull
->offset
, pull
->data_size
- pull
->offset
);
893 /* call the dispatch function */
894 status
= context
->iface
->dispatch(call
, call
, call
->r
);
895 if (!NT_STATUS_IS_OK(status
)) {
896 DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
897 context
->iface
->name
,
898 call
->pkt
.u
.request
.opnum
,
899 dcerpc_errstr(pull
, call
->fault_code
)));
900 return dcesrv_fault(call
, call
->fault_code
);
903 /* add the call to the pending list */
904 dcesrv_call_set_list(call
, DCESRV_LIST_PENDING_CALL_LIST
);
906 if (call
->state_flags
& DCESRV_CALL_STATE_FLAG_ASYNC
) {
910 return dcesrv_reply(call
);
913 _PUBLIC_ NTSTATUS
dcesrv_reply(struct dcesrv_call_state
*call
)
915 struct ndr_push
*push
;
918 uint32_t total_length
, chunk_size
;
919 struct dcesrv_connection_context
*context
= call
->context
;
922 /* call the reply function */
923 status
= context
->iface
->reply(call
, call
, call
->r
);
924 if (!NT_STATUS_IS_OK(status
)) {
925 return dcesrv_fault(call
, call
->fault_code
);
928 /* form the reply NDR */
929 push
= ndr_push_init_ctx(call
, lp_iconv_convenience(call
->conn
->dce_ctx
->lp_ctx
));
930 NT_STATUS_HAVE_NO_MEMORY(push
);
932 /* carry over the pointer count to the reply in case we are
933 using full pointer. See NDR specification for full
935 push
->ptr_count
= call
->ndr_pull
->ptr_count
;
937 if (lp_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
)) {
938 push
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
941 status
= context
->iface
->ndr_push(call
, call
, push
, call
->r
);
942 if (!NT_STATUS_IS_OK(status
)) {
943 return dcesrv_fault(call
, call
->fault_code
);
946 stub
= ndr_push_blob(push
);
948 total_length
= stub
.length
;
950 /* we can write a full max_recv_frag size, minus the dcerpc
951 request header size */
952 chunk_size
= call
->conn
->cli_max_recv_frag
;
953 chunk_size
-= DCERPC_REQUEST_LENGTH
;
954 if (call
->conn
->auth_state
.auth_info
&&
955 call
->conn
->auth_state
.gensec_security
) {
956 sig_size
= gensec_sig_size(call
->conn
->auth_state
.gensec_security
,
957 call
->conn
->cli_max_recv_frag
);
959 chunk_size
-= DCERPC_AUTH_TRAILER_LENGTH
;
960 chunk_size
-= sig_size
;
963 chunk_size
-= (chunk_size
% 16);
967 struct data_blob_list_item
*rep
;
968 struct ncacn_packet pkt
;
970 rep
= talloc(call
, struct data_blob_list_item
);
971 NT_STATUS_HAVE_NO_MEMORY(rep
);
973 length
= MIN(chunk_size
, stub
.length
);
975 /* form the dcerpc response packet */
976 dcesrv_init_hdr(&pkt
, lp_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
978 pkt
.call_id
= call
->pkt
.call_id
;
979 pkt
.ptype
= DCERPC_PKT_RESPONSE
;
981 if (stub
.length
== total_length
) {
982 pkt
.pfc_flags
|= DCERPC_PFC_FLAG_FIRST
;
984 if (length
== stub
.length
) {
985 pkt
.pfc_flags
|= DCERPC_PFC_FLAG_LAST
;
987 pkt
.u
.response
.alloc_hint
= stub
.length
;
988 pkt
.u
.response
.context_id
= call
->pkt
.u
.request
.context_id
;
989 pkt
.u
.response
.cancel_count
= 0;
990 pkt
.u
.response
.stub_and_verifier
.data
= stub
.data
;
991 pkt
.u
.response
.stub_and_verifier
.length
= length
;
993 if (!dcesrv_auth_response(call
, &rep
->blob
, sig_size
, &pkt
)) {
994 return dcesrv_fault(call
, DCERPC_FAULT_OTHER
);
997 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
999 DLIST_ADD_END(call
->replies
, rep
, struct data_blob_list_item
*);
1001 stub
.data
+= length
;
1002 stub
.length
-= length
;
1003 } while (stub
.length
!= 0);
1005 /* move the call from the pending to the finished calls list */
1006 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
1008 if (call
->conn
->call_list
&& call
->conn
->call_list
->replies
) {
1009 if (call
->conn
->transport
.report_output_data
) {
1010 call
->conn
->transport
.report_output_data(call
->conn
);
1014 return NT_STATUS_OK
;
1017 _PUBLIC_
struct socket_address
*dcesrv_connection_get_my_addr(struct dcesrv_connection
*conn
, TALLOC_CTX
*mem_ctx
)
1019 if (!conn
->transport
.get_my_addr
) {
1023 return conn
->transport
.get_my_addr(conn
, mem_ctx
);
1026 _PUBLIC_
struct socket_address
*dcesrv_connection_get_peer_addr(struct dcesrv_connection
*conn
, TALLOC_CTX
*mem_ctx
)
1028 if (!conn
->transport
.get_peer_addr
) {
1032 return conn
->transport
.get_peer_addr(conn
, mem_ctx
);
1036 work out if we have a full packet yet
1038 static bool dce_full_packet(const DATA_BLOB
*data
)
1040 if (data
->length
< DCERPC_FRAG_LEN_OFFSET
+2) {
1043 if (dcerpc_get_frag_length(data
) > data
->length
) {
1050 we might have consumed only part of our input - advance past that part
1052 static void dce_partial_advance(struct dcesrv_connection
*dce_conn
, uint32_t offset
)
1056 if (dce_conn
->partial_input
.length
== offset
) {
1057 data_blob_free(&dce_conn
->partial_input
);
1061 blob
= dce_conn
->partial_input
;
1062 dce_conn
->partial_input
= data_blob(blob
.data
+ offset
,
1063 blob
.length
- offset
);
1064 data_blob_free(&blob
);
1068 remove the call from the right list when freed
1070 static int dcesrv_call_dequeue(struct dcesrv_call_state
*call
)
1072 dcesrv_call_set_list(call
, DCESRV_LIST_NONE
);
1077 process some input to a dcerpc endpoint server.
1079 NTSTATUS
dcesrv_input_process(struct dcesrv_connection
*dce_conn
)
1081 struct ndr_pull
*ndr
;
1082 enum ndr_err_code ndr_err
;
1084 struct dcesrv_call_state
*call
;
1087 call
= talloc_zero(dce_conn
, struct dcesrv_call_state
);
1089 talloc_free(dce_conn
->partial_input
.data
);
1090 return NT_STATUS_NO_MEMORY
;
1092 call
->conn
= dce_conn
;
1093 call
->event_ctx
= dce_conn
->event_ctx
;
1094 call
->msg_ctx
= dce_conn
->msg_ctx
;
1095 call
->state_flags
= call
->conn
->state_flags
;
1096 call
->time
= timeval_current();
1097 call
->list
= DCESRV_LIST_NONE
;
1099 talloc_set_destructor(call
, dcesrv_call_dequeue
);
1101 blob
= dce_conn
->partial_input
;
1102 blob
.length
= dcerpc_get_frag_length(&blob
);
1104 ndr
= ndr_pull_init_blob(&blob
, call
, lp_iconv_convenience(call
->conn
->dce_ctx
->lp_ctx
));
1106 talloc_free(dce_conn
->partial_input
.data
);
1108 return NT_STATUS_NO_MEMORY
;
1111 if (!(CVAL(blob
.data
, DCERPC_DREP_OFFSET
) & DCERPC_DREP_LE
)) {
1112 ndr
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
1115 ndr_err
= ndr_pull_ncacn_packet(ndr
, NDR_SCALARS
|NDR_BUFFERS
, &call
->pkt
);
1116 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1117 talloc_free(dce_conn
->partial_input
.data
);
1119 return ndr_map_error2ntstatus(ndr_err
);
1122 /* we have to check the signing here, before combining the
1124 if (call
->pkt
.ptype
== DCERPC_PKT_REQUEST
&&
1125 !dcesrv_auth_request(call
, &blob
)) {
1126 dce_partial_advance(dce_conn
, blob
.length
);
1127 return dcesrv_fault(call
, DCERPC_FAULT_ACCESS_DENIED
);
1130 dce_partial_advance(dce_conn
, blob
.length
);
1132 /* see if this is a continued packet */
1133 if (call
->pkt
.ptype
== DCERPC_PKT_REQUEST
&&
1134 !(call
->pkt
.pfc_flags
& DCERPC_PFC_FLAG_FIRST
)) {
1135 struct dcesrv_call_state
*call2
= call
;
1136 uint32_t alloc_size
;
1138 /* we only allow fragmented requests, no other packet types */
1139 if (call
->pkt
.ptype
!= DCERPC_PKT_REQUEST
) {
1140 return dcesrv_fault(call2
, DCERPC_FAULT_OTHER
);
1143 /* this is a continuation of an existing call - find the call then
1144 tack it on the end */
1145 call
= dcesrv_find_fragmented_call(dce_conn
, call2
->pkt
.call_id
);
1147 return dcesrv_fault(call2
, DCERPC_FAULT_OTHER
);
1150 if (call
->pkt
.ptype
!= call2
->pkt
.ptype
) {
1151 /* trying to play silly buggers are we? */
1152 return dcesrv_fault(call2
, DCERPC_FAULT_OTHER
);
1155 alloc_size
= call
->pkt
.u
.request
.stub_and_verifier
.length
+
1156 call2
->pkt
.u
.request
.stub_and_verifier
.length
;
1157 if (call
->pkt
.u
.request
.alloc_hint
> alloc_size
) {
1158 alloc_size
= call
->pkt
.u
.request
.alloc_hint
;
1161 call
->pkt
.u
.request
.stub_and_verifier
.data
=
1162 talloc_realloc(call
,
1163 call
->pkt
.u
.request
.stub_and_verifier
.data
,
1164 uint8_t, alloc_size
);
1165 if (!call
->pkt
.u
.request
.stub_and_verifier
.data
) {
1166 return dcesrv_fault(call2
, DCERPC_FAULT_OTHER
);
1168 memcpy(call
->pkt
.u
.request
.stub_and_verifier
.data
+
1169 call
->pkt
.u
.request
.stub_and_verifier
.length
,
1170 call2
->pkt
.u
.request
.stub_and_verifier
.data
,
1171 call2
->pkt
.u
.request
.stub_and_verifier
.length
);
1172 call
->pkt
.u
.request
.stub_and_verifier
.length
+=
1173 call2
->pkt
.u
.request
.stub_and_verifier
.length
;
1175 call
->pkt
.pfc_flags
|= (call2
->pkt
.pfc_flags
& DCERPC_PFC_FLAG_LAST
);
1180 /* this may not be the last pdu in the chain - if its isn't then
1181 just put it on the incoming_fragmented_call_list and wait for the rest */
1182 if (call
->pkt
.ptype
== DCERPC_PKT_REQUEST
&&
1183 !(call
->pkt
.pfc_flags
& DCERPC_PFC_FLAG_LAST
)) {
1184 dcesrv_call_set_list(call
, DCESRV_LIST_FRAGMENTED_CALL_LIST
);
1185 return NT_STATUS_OK
;
1188 /* This removes any fragments we may have had stashed away */
1189 dcesrv_call_set_list(call
, DCESRV_LIST_NONE
);
1191 switch (call
->pkt
.ptype
) {
1192 case DCERPC_PKT_BIND
:
1193 status
= dcesrv_bind(call
);
1195 case DCERPC_PKT_AUTH3
:
1196 status
= dcesrv_auth3(call
);
1198 case DCERPC_PKT_ALTER
:
1199 status
= dcesrv_alter(call
);
1201 case DCERPC_PKT_REQUEST
:
1202 status
= dcesrv_request(call
);
1205 status
= NT_STATUS_INVALID_PARAMETER
;
1209 /* if we are going to be sending a reply then add
1210 it to the list of pending calls. We add it to the end to keep the call
1211 list in the order we will answer */
1212 if (!NT_STATUS_IS_OK(status
)) {
1221 provide some input to a dcerpc endpoint server. This passes data
1222 from a dcerpc client into the server
1224 _PUBLIC_ NTSTATUS
dcesrv_input(struct dcesrv_connection
*dce_conn
, const DATA_BLOB
*data
)
1228 dce_conn
->partial_input
.data
= talloc_realloc(dce_conn
,
1229 dce_conn
->partial_input
.data
,
1231 dce_conn
->partial_input
.length
+ data
->length
);
1232 if (!dce_conn
->partial_input
.data
) {
1233 return NT_STATUS_NO_MEMORY
;
1235 memcpy(dce_conn
->partial_input
.data
+ dce_conn
->partial_input
.length
,
1236 data
->data
, data
->length
);
1237 dce_conn
->partial_input
.length
+= data
->length
;
1239 while (dce_full_packet(&dce_conn
->partial_input
)) {
1240 status
= dcesrv_input_process(dce_conn
);
1241 if (!NT_STATUS_IS_OK(status
)) {
1246 return NT_STATUS_OK
;
1250 retrieve some output from a dcerpc server
1251 The caller supplies a function that will be called to do the
1254 The first argument to write_fn() will be 'private', the second will
1255 be a pointer to a buffer containing the data to be sent and the 3rd
1256 will be a pointer to a size_t variable that will be set to the
1257 number of bytes that are consumed from the output.
1259 from the current fragment
1261 _PUBLIC_ NTSTATUS
dcesrv_output(struct dcesrv_connection
*dce_conn
,
1263 NTSTATUS (*write_fn
)(void *private_data
, DATA_BLOB
*output
, size_t *nwritten
))
1266 struct dcesrv_call_state
*call
;
1267 struct data_blob_list_item
*rep
;
1270 call
= dce_conn
->call_list
;
1271 if (!call
|| !call
->replies
) {
1272 if (dce_conn
->pending_call_list
) {
1273 /* TODO: we need to say act async here
1274 * as we know we have pending requests
1275 * which will be finished at a time
1277 return NT_STATUS_FOOBAR
;
1279 return NT_STATUS_FOOBAR
;
1281 rep
= call
->replies
;
1283 status
= write_fn(private_data
, &rep
->blob
, &nwritten
);
1284 NT_STATUS_IS_ERR_RETURN(status
);
1286 rep
->blob
.length
-= nwritten
;
1287 rep
->blob
.data
+= nwritten
;
1289 if (rep
->blob
.length
== 0) {
1290 /* we're done with this section of the call */
1291 DLIST_REMOVE(call
->replies
, rep
);
1294 if (call
->replies
== NULL
) {
1295 /* we're done with the whole call */
1296 dcesrv_call_set_list(call
, DCESRV_LIST_NONE
);
1303 _PUBLIC_ NTSTATUS
dcesrv_init_context(TALLOC_CTX
*mem_ctx
,
1304 struct loadparm_context
*lp_ctx
,
1305 const char **endpoint_servers
, struct dcesrv_context
**_dce_ctx
)
1308 struct dcesrv_context
*dce_ctx
;
1311 if (!endpoint_servers
) {
1312 DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
1313 return NT_STATUS_INTERNAL_ERROR
;
1316 dce_ctx
= talloc(mem_ctx
, struct dcesrv_context
);
1317 NT_STATUS_HAVE_NO_MEMORY(dce_ctx
);
1318 dce_ctx
->endpoint_list
= NULL
;
1319 dce_ctx
->lp_ctx
= lp_ctx
;
1321 for (i
=0;endpoint_servers
[i
];i
++) {
1322 const struct dcesrv_endpoint_server
*ep_server
;
1324 ep_server
= dcesrv_ep_server_byname(endpoint_servers
[i
]);
1326 DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers
[i
]));
1327 return NT_STATUS_INTERNAL_ERROR
;
1330 status
= ep_server
->init_server(dce_ctx
, ep_server
);
1331 if (!NT_STATUS_IS_OK(status
)) {
1332 DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers
[i
],
1333 nt_errstr(status
)));
1338 *_dce_ctx
= dce_ctx
;
1339 return NT_STATUS_OK
;
1342 /* the list of currently registered DCERPC endpoint servers.
1344 static struct ep_server
{
1345 struct dcesrv_endpoint_server
*ep_server
;
1346 } *ep_servers
= NULL
;
1347 static int num_ep_servers
;
1350 register a DCERPC endpoint server.
1352 The 'name' can be later used by other backends to find the operations
1353 structure for this backend.
1355 The 'type' is used to specify whether this is for a disk, printer or IPC$ share
1357 _PUBLIC_ NTSTATUS
dcerpc_register_ep_server(const void *_ep_server
)
1359 const struct dcesrv_endpoint_server
*ep_server
= _ep_server
;
1361 if (dcesrv_ep_server_byname(ep_server
->name
) != NULL
) {
1362 /* its already registered! */
1363 DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
1365 return NT_STATUS_OBJECT_NAME_COLLISION
;
1368 ep_servers
= realloc_p(ep_servers
, struct ep_server
, num_ep_servers
+1);
1370 smb_panic("out of memory in dcerpc_register");
1373 ep_servers
[num_ep_servers
].ep_server
= smb_xmemdup(ep_server
, sizeof(*ep_server
));
1374 ep_servers
[num_ep_servers
].ep_server
->name
= smb_xstrdup(ep_server
->name
);
1378 DEBUG(3,("DCERPC endpoint server '%s' registered\n",
1381 return NT_STATUS_OK
;
1385 return the operations structure for a named backend of the specified type
1387 const struct dcesrv_endpoint_server
*dcesrv_ep_server_byname(const char *name
)
1391 for (i
=0;i
<num_ep_servers
;i
++) {
1392 if (strcmp(ep_servers
[i
].ep_server
->name
, name
) == 0) {
1393 return ep_servers
[i
].ep_server
;
1401 return the DCERPC module version, and the size of some critical types
1402 This can be used by endpoint server modules to either detect compilation errors, or provide
1403 multiple implementations for different smbd compilation options in one module
1405 const struct dcesrv_critical_sizes
*dcerpc_module_version(void)
1407 static const struct dcesrv_critical_sizes critical_sizes
= {
1408 DCERPC_MODULE_VERSION
,
1409 sizeof(struct dcesrv_context
),
1410 sizeof(struct dcesrv_endpoint
),
1411 sizeof(struct dcesrv_endpoint_server
),
1412 sizeof(struct dcesrv_interface
),
1413 sizeof(struct dcesrv_if_list
),
1414 sizeof(struct dcesrv_connection
),
1415 sizeof(struct dcesrv_call_state
),
1416 sizeof(struct dcesrv_auth
),
1417 sizeof(struct dcesrv_handle
)
1420 return &critical_sizes
;
1424 initialise the dcerpc server context for ncacn_np based services
1426 _PUBLIC_ NTSTATUS
dcesrv_init_ipc_context(TALLOC_CTX
*mem_ctx
, struct loadparm_context
*lp_ctx
,
1427 struct dcesrv_context
**_dce_ctx
)
1430 struct dcesrv_context
*dce_ctx
;
1432 status
= dcesrv_init_context(mem_ctx
, lp_ctx
, lp_dcerpc_endpoint_servers(lp_ctx
), &dce_ctx
);
1433 NT_STATUS_NOT_OK_RETURN(status
);
1435 *_dce_ctx
= dce_ctx
;
1436 return NT_STATUS_OK
;