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