s4:rpc_server: return the context_id of a RESPONSE in the same way as windows
[Samba.git] / source4 / rpc_server / common / reply.c
blobef2906e07242ad8f2b2bddb8577206adc91bb468
1 /*
2 Unix SMB/CIFS implementation.
4 server side dcerpc common code
6 Copyright (C) Andrew Tridgell 2003-2010
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/>.
23 #include "includes.h"
24 #include "auth/auth.h"
25 #include "auth/gensec/gensec.h"
26 #include "../lib/util/dlinklist.h"
27 #include "rpc_server/dcerpc_server.h"
28 #include "rpc_server/dcerpc_server_proto.h"
29 #include "rpc_server/common/proto.h"
30 #include "librpc/rpc/dcerpc_proto.h"
31 #include "system/filesys.h"
32 #include "libcli/security/security.h"
33 #include "param/param.h"
34 #include "../lib/tsocket/tsocket.h"
35 #include "../libcli/named_pipe_auth/npa_tstream.h"
36 #include "smbd/service_stream.h"
37 #include "../lib/tsocket/tsocket.h"
38 #include "lib/socket/socket.h"
39 #include "smbd/process_model.h"
40 #include "lib/messaging/irpc.h"
41 #include "librpc/rpc/rpc_common.h"
45 move a call from an existing linked list to the specified list. This
46 prevents bugs where we forget to remove the call from a previous
47 list when moving it.
49 static void dcesrv_call_set_list(struct dcesrv_call_state *call,
50 enum dcesrv_call_list list)
52 switch (call->list) {
53 case DCESRV_LIST_NONE:
54 break;
55 case DCESRV_LIST_CALL_LIST:
56 DLIST_REMOVE(call->conn->call_list, call);
57 break;
58 case DCESRV_LIST_FRAGMENTED_CALL_LIST:
59 DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
60 break;
61 case DCESRV_LIST_PENDING_CALL_LIST:
62 DLIST_REMOVE(call->conn->pending_call_list, call);
63 break;
65 call->list = list;
66 switch (list) {
67 case DCESRV_LIST_NONE:
68 break;
69 case DCESRV_LIST_CALL_LIST:
70 DLIST_ADD_END(call->conn->call_list, call);
71 break;
72 case DCESRV_LIST_FRAGMENTED_CALL_LIST:
73 DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call);
74 break;
75 case DCESRV_LIST_PENDING_CALL_LIST:
76 DLIST_ADD_END(call->conn->pending_call_list, call);
77 break;
82 void dcesrv_init_hdr(struct ncacn_packet *pkt, bool bigendian)
84 pkt->rpc_vers = 5;
85 pkt->rpc_vers_minor = 0;
86 if (bigendian) {
87 pkt->drep[0] = 0;
88 } else {
89 pkt->drep[0] = DCERPC_DREP_LE;
91 pkt->drep[1] = 0;
92 pkt->drep[2] = 0;
93 pkt->drep[3] = 0;
98 return a dcerpc fault
100 NTSTATUS dcesrv_fault_with_flags(struct dcesrv_call_state *call,
101 uint32_t fault_code,
102 uint8_t extra_flags)
104 struct ncacn_packet pkt;
105 struct data_blob_list_item *rep;
106 NTSTATUS status;
108 /* setup a fault */
109 dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
110 pkt.auth_length = 0;
111 pkt.call_id = call->pkt.call_id;
112 pkt.ptype = DCERPC_PKT_FAULT;
113 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
114 pkt.u.fault.alloc_hint = 24;
115 if (call->context != NULL) {
116 pkt.u.fault.context_id = call->context->context_id;
117 } else {
118 pkt.u.fault.context_id = 0;
120 pkt.u.fault.cancel_count = 0;
121 pkt.u.fault.flags = 0;
122 pkt.u.fault.status = fault_code;
123 pkt.u.fault.reserved = 0;
124 pkt.u.fault.error_and_verifier = data_blob_null;
126 rep = talloc_zero(call, struct data_blob_list_item);
127 if (!rep) {
128 return NT_STATUS_NO_MEMORY;
131 status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
132 if (!NT_STATUS_IS_OK(status)) {
133 return status;
136 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
138 DLIST_ADD_END(call->replies, rep);
139 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
141 if (call->conn->call_list && call->conn->call_list->replies) {
142 if (call->conn->transport.report_output_data) {
143 call->conn->transport.report_output_data(call->conn);
147 return NT_STATUS_OK;
150 NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
152 return dcesrv_fault_with_flags(call, fault_code, 0);
155 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
157 struct ndr_push *push;
158 NTSTATUS status;
159 DATA_BLOB stub;
160 uint32_t total_length, chunk_size;
161 struct dcesrv_connection_context *context = call->context;
162 size_t sig_size = 0;
164 /* call the reply function */
165 status = context->iface->reply(call, call, call->r);
166 if (!NT_STATUS_IS_OK(status)) {
167 return dcesrv_fault(call, call->fault_code);
170 /* form the reply NDR */
171 push = ndr_push_init_ctx(call);
172 NT_STATUS_HAVE_NO_MEMORY(push);
174 /* carry over the pointer count to the reply in case we are
175 using full pointer. See NDR specification for full
176 pointers */
177 push->ptr_count = call->ndr_pull->ptr_count;
179 if (lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
180 push->flags |= LIBNDR_FLAG_BIGENDIAN;
183 status = context->iface->ndr_push(call, call, push, call->r);
184 if (!NT_STATUS_IS_OK(status)) {
185 return dcesrv_fault(call, call->fault_code);
188 stub = ndr_push_blob(push);
190 total_length = stub.length;
192 /* we can write a full max_recv_frag size, minus the dcerpc
193 request header size */
194 chunk_size = call->conn->max_xmit_frag;
195 chunk_size -= DCERPC_REQUEST_LENGTH;
196 if (call->conn->auth_state.auth_finished &&
197 call->conn->auth_state.gensec_security) {
198 size_t max_payload = chunk_size;
200 max_payload -= DCERPC_AUTH_TRAILER_LENGTH;
201 max_payload -= (max_payload % DCERPC_AUTH_PAD_ALIGNMENT);
203 sig_size = gensec_sig_size(call->conn->auth_state.gensec_security,
204 max_payload);
205 if (sig_size) {
206 chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
207 chunk_size -= sig_size;
210 chunk_size -= (chunk_size % DCERPC_AUTH_PAD_ALIGNMENT);
212 do {
213 uint32_t length;
214 struct data_blob_list_item *rep;
215 struct ncacn_packet pkt;
216 bool ok;
218 rep = talloc_zero(call, struct data_blob_list_item);
219 NT_STATUS_HAVE_NO_MEMORY(rep);
221 length = MIN(chunk_size, stub.length);
223 /* form the dcerpc response packet */
224 dcesrv_init_hdr(&pkt,
225 lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
226 pkt.auth_length = 0;
227 pkt.call_id = call->pkt.call_id;
228 pkt.ptype = DCERPC_PKT_RESPONSE;
229 pkt.pfc_flags = 0;
230 if (stub.length == total_length) {
231 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
233 if (length == stub.length) {
234 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
236 pkt.u.response.alloc_hint = stub.length;
238 * bug for bug, feature for feature...
240 * Windows truncates the context_id with & 0xFF,
241 * so we do.
243 pkt.u.response.context_id = context->context_id & 0xFF;
244 pkt.u.response.cancel_count = 0;
245 pkt.u.response.stub_and_verifier.data = stub.data;
246 pkt.u.response.stub_and_verifier.length = length;
248 ok = dcesrv_auth_pkt_push(call, &rep->blob, sig_size,
249 DCERPC_RESPONSE_LENGTH,
250 &pkt.u.response.stub_and_verifier,
251 &pkt);
252 if (!ok) {
253 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
256 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
258 DLIST_ADD_END(call->replies, rep);
260 stub.data += length;
261 stub.length -= length;
262 } while (stub.length != 0);
264 /* move the call from the pending to the finished calls list */
265 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
267 if (call->conn->call_list && call->conn->call_list->replies) {
268 if (call->conn->transport.report_output_data) {
269 call->conn->transport.report_output_data(call->conn);
273 return NT_STATUS_OK;
276 NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *c,
277 DATA_BLOB *session_key)
279 enum dcerpc_transport_t transport =
280 dcerpc_binding_get_transport(c->endpoint->ep_description);
282 if (transport != NCALRPC && transport != NCACN_UNIX_STREAM) {
283 return NT_STATUS_NO_USER_SESSION_KEY;
286 return dcerpc_generic_session_key(NULL, session_key);