smbd: Remove struct open_symlink_err
[Samba.git] / librpc / rpc / dcesrv_reply.c
blob6d6051681766bad1202d56fd804f9e86e78c2c44
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 "librpc/rpc/dcesrv_core.h"
25 #include "librpc/rpc/dcesrv_core_proto.h"
26 #include "librpc/rpc/dcerpc_util.h"
27 #include "auth/gensec/gensec.h"
28 #include "lib/util/dlinklist.h"
29 #include "param/param.h"
32 move a call from an existing linked list to the specified list. This
33 prevents bugs where we forget to remove the call from a previous
34 list when moving it.
36 static void dcesrv_call_set_list(struct dcesrv_call_state *call,
37 enum dcesrv_call_list list)
39 switch (call->list) {
40 case DCESRV_LIST_NONE:
41 break;
42 case DCESRV_LIST_CALL_LIST:
43 DLIST_REMOVE(call->conn->call_list, call);
44 break;
45 case DCESRV_LIST_FRAGMENTED_CALL_LIST:
46 DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
47 break;
48 case DCESRV_LIST_PENDING_CALL_LIST:
49 DLIST_REMOVE(call->conn->pending_call_list, call);
50 break;
52 call->list = list;
53 switch (list) {
54 case DCESRV_LIST_NONE:
55 break;
56 case DCESRV_LIST_CALL_LIST:
57 DLIST_ADD_END(call->conn->call_list, call);
58 break;
59 case DCESRV_LIST_FRAGMENTED_CALL_LIST:
60 DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call);
61 break;
62 case DCESRV_LIST_PENDING_CALL_LIST:
63 DLIST_ADD_END(call->conn->pending_call_list, call);
64 break;
69 void dcesrv_init_hdr(struct ncacn_packet *pkt, bool bigendian)
71 pkt->rpc_vers = 5;
72 pkt->rpc_vers_minor = 0;
73 if (bigendian) {
74 pkt->drep[0] = 0;
75 } else {
76 pkt->drep[0] = DCERPC_DREP_LE;
78 pkt->drep[1] = 0;
79 pkt->drep[2] = 0;
80 pkt->drep[3] = 0;
85 return a dcerpc fault
87 NTSTATUS dcesrv_fault_with_flags(struct dcesrv_call_state *call,
88 uint32_t fault_code,
89 uint8_t extra_flags)
91 struct ncacn_packet pkt;
92 struct data_blob_list_item *rep;
93 NTSTATUS status;
95 if (call->conn->terminate != NULL) {
97 * If we're already disconnecting
98 * we should just drop a possible
99 * response
101 talloc_free(call);
102 return NT_STATUS_OK;
105 /* setup a fault */
106 dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
107 pkt.auth_length = 0;
108 pkt.call_id = call->pkt.call_id;
109 pkt.ptype = DCERPC_PKT_FAULT;
110 pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
111 pkt.u.fault.alloc_hint = 24;
112 if (call->context != NULL) {
113 pkt.u.fault.context_id = call->context->context_id;
114 } else {
115 pkt.u.fault.context_id = 0;
117 pkt.u.fault.cancel_count = 0;
118 pkt.u.fault.flags = 0;
119 pkt.u.fault.status = fault_code;
120 pkt.u.fault.reserved = 0;
121 pkt.u.fault.error_and_verifier = data_blob_null;
123 rep = talloc_zero(call, struct data_blob_list_item);
124 if (!rep) {
125 return NT_STATUS_NO_MEMORY;
128 status = dcerpc_ncacn_push_auth(&rep->blob, call, &pkt, NULL);
129 if (!NT_STATUS_IS_OK(status)) {
130 return status;
133 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
135 DLIST_ADD_END(call->replies, rep);
136 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
138 if (call->conn->call_list && call->conn->call_list->replies) {
139 if (call->conn->transport.report_output_data) {
140 call->conn->transport.report_output_data(call->conn);
144 return NT_STATUS_OK;
147 NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
149 return dcesrv_fault_with_flags(call, fault_code, 0);
152 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
154 struct ndr_push *push;
155 NTSTATUS status;
156 DATA_BLOB stub;
157 uint32_t total_length, chunk_size;
158 struct dcesrv_connection_context *context = call->context;
159 struct dcesrv_auth *auth = call->auth_state;
160 size_t sig_size = 0;
163 * call the reply function,
164 * it's mostly for debug messages
165 * and dcesrv_fault() also checks for
166 * (call->conn->terminate != NULL) internally.
168 status = context->iface->reply(call, call, call->r);
169 if (!NT_STATUS_IS_OK(status)) {
170 return dcesrv_fault(call, call->fault_code);
173 if (call->conn->terminate != NULL) {
175 * If we're already disconnecting
176 * we should just drop a possible
177 * response
179 talloc_free(call);
180 return NT_STATUS_OK;
183 /* form the reply NDR */
184 push = ndr_push_init_ctx(call);
185 NT_STATUS_HAVE_NO_MEMORY(push);
187 /* carry over the pointer count to the reply in case we are
188 using full pointer. See NDR specification for full
189 pointers */
190 push->ptr_count = call->ndr_pull->ptr_count;
192 if (lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
193 push->flags |= LIBNDR_FLAG_BIGENDIAN;
196 if (context->ndr64) {
197 push->flags |= LIBNDR_FLAG_NDR64;
200 status = context->iface->ndr_push(call, call, push, call->r);
201 if (!NT_STATUS_IS_OK(status)) {
202 return dcesrv_fault(call, call->fault_code);
205 stub = ndr_push_blob(push);
207 dcesrv_save_ndr_fuzz_seed(stub,
208 call,
209 NDR_OUT);
211 total_length = stub.length;
213 /* we can write a full max_recv_frag size, minus the dcerpc
214 request header size */
215 chunk_size = call->conn->max_xmit_frag;
216 chunk_size -= DCERPC_REQUEST_LENGTH;
217 if (auth->auth_finished && auth->gensec_security != NULL) {
218 size_t max_payload = chunk_size;
220 max_payload -= DCERPC_AUTH_TRAILER_LENGTH;
221 max_payload -= (max_payload % DCERPC_AUTH_PAD_ALIGNMENT);
223 sig_size = gensec_sig_size(auth->gensec_security,
224 max_payload);
225 if (sig_size) {
226 chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
227 chunk_size -= sig_size;
230 chunk_size -= (chunk_size % DCERPC_AUTH_PAD_ALIGNMENT);
232 do {
233 uint32_t length;
234 struct data_blob_list_item *rep;
235 struct ncacn_packet pkt;
236 bool ok;
238 rep = talloc_zero(call, struct data_blob_list_item);
239 NT_STATUS_HAVE_NO_MEMORY(rep);
241 length = MIN(chunk_size, stub.length);
243 /* form the dcerpc response packet */
244 dcesrv_init_hdr(&pkt,
245 lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
246 pkt.auth_length = 0;
247 pkt.call_id = call->pkt.call_id;
248 pkt.ptype = DCERPC_PKT_RESPONSE;
249 pkt.pfc_flags = 0;
250 if (stub.length == total_length) {
251 pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
253 if (length == stub.length) {
254 pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
256 pkt.u.response.alloc_hint = stub.length;
258 * bug for bug, feature for feature...
260 * Windows truncates the context_id with & 0xFF,
261 * so we do.
263 pkt.u.response.context_id = context->context_id & 0xFF;
264 pkt.u.response.cancel_count = 0;
265 pkt.u.response.stub_and_verifier.data = stub.data;
266 pkt.u.response.stub_and_verifier.length = length;
268 ok = dcesrv_auth_pkt_push(call, &rep->blob, sig_size,
269 DCERPC_RESPONSE_LENGTH,
270 &pkt.u.response.stub_and_verifier,
271 &pkt);
272 if (!ok) {
273 return dcesrv_fault(call, DCERPC_FAULT_OTHER);
276 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
278 DLIST_ADD_END(call->replies, rep);
280 stub.data += length;
281 stub.length -= length;
282 } while (stub.length != 0);
284 /* move the call from the pending to the finished calls list */
285 dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
287 if (call->conn->call_list && call->conn->call_list->replies) {
288 if (call->conn->transport.report_output_data) {
289 call->conn->transport.report_output_data(call->conn);
293 return NT_STATUS_OK;
296 _PUBLIC_ void _dcesrv_async_reply(struct dcesrv_call_state *call,
297 const char *func,
298 const char *location)
300 struct dcesrv_connection *conn = call->conn;
301 NTSTATUS status;
303 status = dcesrv_reply(call);
304 if (!NT_STATUS_IS_OK(status)) {
305 D_ERR("%s: %s: dcesrv_async_reply() failed - %s\n",
306 func, location, nt_errstr(status));
307 dcesrv_terminate_connection(conn, nt_errstr(status));