waf: added top level build rules
[samba.git] / librpc / rpc / dcerpc_util.c
1 /*
2    Unix SMB/CIFS implementation.
3    raw dcerpc operations
4
5    Copyright (C) Andrew Tridgell 2003-2005
6    Copyright (C) Jelmer Vernooij 2004-2005
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "system/network.h"
24 #include <tevent.h>
25 #include "lib/tsocket/tsocket.h"
26 #include "lib/util/tevent_ntstatus.h"
27 #include "librpc/rpc/dcerpc.h"
28 #include "librpc/gen_ndr/ndr_dcerpc.h"
29
30 /* we need to be able to get/set the fragment length without doing a full
31    decode */
32 void dcerpc_set_frag_length(DATA_BLOB *blob, uint16_t v)
33 {
34         if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
35                 SSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
36         } else {
37                 RSSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
38         }
39 }
40
41 uint16_t dcerpc_get_frag_length(const DATA_BLOB *blob)
42 {
43         if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
44                 return SVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
45         } else {
46                 return RSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
47         }
48 }
49
50 void dcerpc_set_auth_length(DATA_BLOB *blob, uint16_t v)
51 {
52         if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
53                 SSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
54         } else {
55                 RSSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
56         }
57 }
58
59 uint8_t dcerpc_get_endian_flag(DATA_BLOB *blob)
60 {
61         return blob->data[DCERPC_DREP_OFFSET];
62 }
63
64
65 /**
66 * @brief        Pull a dcerpc_auth structure, taking account of any auth
67 *               padding in the blob. For request/response packets we pass
68 *               the whole data blob, so auth_data_only must be set to false
69 *               as the blob contains data+pad+auth and no just pad+auth.
70 *
71 * @param pkt            - The ncacn_packet strcuture
72 * @param mem_ctx        - The mem_ctx used to allocate dcerpc_auth elements
73 * @param pkt_trailer    - The packet trailer data, usually the trailing
74 *                         auth_info blob, but in the request/response case
75 *                         this is the stub_and_verifier blob.
76 * @param auth           - A preallocated dcerpc_auth *empty* structure
77 * @param auth_length    - The length of the auth trail, sum of auth header
78 *                         lenght and pkt->auth_length
79 * @param auth_data_only - Whether the pkt_trailer includes only the auth_blob
80 *                         (+ padding) or also other data.
81 *
82 * @return               - A NTSTATUS error code.
83 */
84 NTSTATUS dcerpc_pull_auth_trailer(struct ncacn_packet *pkt,
85                                   TALLOC_CTX *mem_ctx,
86                                   DATA_BLOB *pkt_trailer,
87                                   struct dcerpc_auth *auth,
88                                   uint32_t *auth_length,
89                                   bool auth_data_only)
90 {
91         struct ndr_pull *ndr;
92         enum ndr_err_code ndr_err;
93         uint32_t data_and_pad;
94
95         data_and_pad = pkt_trailer->length
96                         - (DCERPC_AUTH_TRAILER_LENGTH + pkt->auth_length);
97
98         /* paranoia check for pad size. This would be caught anyway by
99            the ndr_pull_advance() a few lines down, but it scared
100            Jeremy enough for him to call me, so we might as well check
101            it now, just to prevent someone posting a bogus YouTube
102            video in the future.
103         */
104         if (data_and_pad > pkt_trailer->length) {
105                 return NT_STATUS_INFO_LENGTH_MISMATCH;
106         }
107
108         *auth_length = pkt_trailer->length - data_and_pad;
109
110         ndr = ndr_pull_init_blob(pkt_trailer, mem_ctx);
111         if (!ndr) {
112                 return NT_STATUS_NO_MEMORY;
113         }
114
115         if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
116                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
117         }
118
119         ndr_err = ndr_pull_advance(ndr, data_and_pad);
120         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
121                 talloc_free(ndr);
122                 return ndr_map_error2ntstatus(ndr_err);
123         }
124
125         ndr_err = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, auth);
126         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
127                 talloc_free(ndr);
128                 return ndr_map_error2ntstatus(ndr_err);
129         }
130
131         if (auth_data_only && data_and_pad != auth->auth_pad_length) {
132                 DEBUG(1, (__location__ ": WARNING: pad length mismatch. "
133                           "Calculated %u  got %u\n",
134                           (unsigned)data_and_pad,
135                           (unsigned)auth->auth_pad_length));
136         }
137
138         DEBUG(6,(__location__ ": auth_pad_length %u\n",
139                  (unsigned)auth->auth_pad_length));
140
141         talloc_steal(mem_ctx, auth->credentials.data);
142         talloc_free(ndr);
143
144         return NT_STATUS_OK;
145 }
146
147 struct dcerpc_read_ncacn_packet_state {
148 #if 0
149         struct {
150         } caller;
151 #endif
152         DATA_BLOB buffer;
153         struct ncacn_packet *pkt;
154 };
155
156 static int dcerpc_read_ncacn_packet_next_vector(struct tstream_context *stream,
157                                                 void *private_data,
158                                                 TALLOC_CTX *mem_ctx,
159                                                 struct iovec **_vector,
160                                                 size_t *_count);
161 static void dcerpc_read_ncacn_packet_done(struct tevent_req *subreq);
162
163 struct tevent_req *dcerpc_read_ncacn_packet_send(TALLOC_CTX *mem_ctx,
164                                                  struct tevent_context *ev,
165                                                  struct tstream_context *stream)
166 {
167         struct tevent_req *req;
168         struct dcerpc_read_ncacn_packet_state *state;
169         struct tevent_req *subreq;
170
171         req = tevent_req_create(mem_ctx, &state,
172                                 struct dcerpc_read_ncacn_packet_state);
173         if (req == NULL) {
174                 return NULL;
175         }
176
177         state->buffer = data_blob_const(NULL, 0);
178         state->pkt = talloc(state, struct ncacn_packet);
179         if (tevent_req_nomem(state->pkt, req)) {
180                 goto post;
181         }
182
183         subreq = tstream_readv_pdu_send(state, ev,
184                                         stream,
185                                         dcerpc_read_ncacn_packet_next_vector,
186                                         state);
187         if (tevent_req_nomem(subreq, req)) {
188                 goto post;
189         }
190         tevent_req_set_callback(subreq, dcerpc_read_ncacn_packet_done, req);
191
192         return req;
193  post:
194         tevent_req_post(req, ev);
195         return req;
196 }
197
198 static int dcerpc_read_ncacn_packet_next_vector(struct tstream_context *stream,
199                                                 void *private_data,
200                                                 TALLOC_CTX *mem_ctx,
201                                                 struct iovec **_vector,
202                                                 size_t *_count)
203 {
204         struct dcerpc_read_ncacn_packet_state *state =
205                 talloc_get_type_abort(private_data,
206                 struct dcerpc_read_ncacn_packet_state);
207         struct iovec *vector;
208         off_t ofs = 0;
209
210         if (state->buffer.length == 0) {
211                 /* first get enough to read the fragment length */
212                 ofs = 0;
213                 state->buffer.length = DCERPC_FRAG_LEN_OFFSET + 2;
214                 state->buffer.data = talloc_array(state, uint8_t,
215                                                   state->buffer.length);
216                 if (!state->buffer.data) {
217                         return -1;
218                 }
219         } else if (state->buffer.length == (DCERPC_FRAG_LEN_OFFSET + 2)) {
220                 /* now read the fragment length and allocate the full buffer */
221                 size_t frag_len = dcerpc_get_frag_length(&state->buffer);
222
223                 ofs = state->buffer.length;
224
225                 state->buffer.data = talloc_realloc(state,
226                                                     state->buffer.data,
227                                                     uint8_t, frag_len);
228                 if (!state->buffer.data) {
229                         return -1;
230                 }
231                 state->buffer.length = frag_len;
232         } else {
233                 /* if we reach this we have a full fragment */
234                 *_vector = NULL;
235                 *_count = 0;
236                 return 0;
237         }
238
239         /* now create the vector that we want to be filled */
240         vector = talloc_array(mem_ctx, struct iovec, 1);
241         if (!vector) {
242                 return -1;
243         }
244
245         vector[0].iov_base = (void *) (state->buffer.data + ofs);
246         vector[0].iov_len = state->buffer.length - ofs;
247
248         *_vector = vector;
249         *_count = 1;
250         return 0;
251 }
252
253 static void dcerpc_read_ncacn_packet_done(struct tevent_req *subreq)
254 {
255         struct tevent_req *req = tevent_req_callback_data(subreq,
256                                  struct tevent_req);
257         struct dcerpc_read_ncacn_packet_state *state = tevent_req_data(req,
258                                         struct dcerpc_read_ncacn_packet_state);
259         int ret;
260         int sys_errno;
261         struct ndr_pull *ndr;
262         enum ndr_err_code ndr_err;
263         NTSTATUS status;
264
265         ret = tstream_readv_pdu_recv(subreq, &sys_errno);
266         TALLOC_FREE(subreq);
267         if (ret == -1) {
268                 status = map_nt_error_from_unix(sys_errno);
269                 tevent_req_nterror(req, status);
270                 return;
271         }
272
273         ndr = ndr_pull_init_blob(&state->buffer, state->pkt);
274         if (tevent_req_nomem(ndr, req)) {
275                 return;
276         }
277
278         if (!(CVAL(ndr->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
279                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
280         }
281
282         if (CVAL(ndr->data, DCERPC_PFC_OFFSET) & DCERPC_PFC_FLAG_OBJECT_UUID) {
283                 ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
284         }
285
286         ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, state->pkt);
287         TALLOC_FREE(ndr);
288         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
289                 status = ndr_map_error2ntstatus(ndr_err);
290                 tevent_req_nterror(req, status);
291                 return;
292         }
293
294         tevent_req_done(req);
295 }
296
297 NTSTATUS dcerpc_read_ncacn_packet_recv(struct tevent_req *req,
298                                        TALLOC_CTX *mem_ctx,
299                                        struct ncacn_packet **pkt,
300                                        DATA_BLOB *buffer)
301 {
302         struct dcerpc_read_ncacn_packet_state *state = tevent_req_data(req,
303                                         struct dcerpc_read_ncacn_packet_state);
304         NTSTATUS status;
305
306         if (tevent_req_is_nterror(req, &status)) {
307                 tevent_req_received(req);
308                 return status;
309         }
310
311         *pkt = talloc_move(mem_ctx, &state->pkt);
312         if (buffer) {
313                 buffer->data = talloc_move(mem_ctx, &state->buffer.data);
314                 buffer->length = state->buffer.length;
315         }
316
317         tevent_req_received(req);
318         return NT_STATUS_OK;
319 }