605a172a706b1ef560db21f434e6fffd69892004
[bbaumbach/samba-autobuild/.git] / source4 / ntvfs / ipc / ipc_rap.c
1 /* 
2    Unix SMB/CIFS implementation.
3    RAP handlers
4
5    Copyright (C) Volker Lendecke 2004
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "libcli/raw/interfaces.h"
23 #include "libcli/rap/rap.h"
24 #include "ntvfs/ipc/proto.h"
25 #include "librpc/ndr/libndr.h"
26
27 #define NERR_Success 0
28 #define NERR_badpass 86
29 #define NERR_notsupported 50
30
31 struct rap_string_heap {
32         TALLOC_CTX *mem_ctx;
33         int offset;
34         int num_strings;
35         const char **strings;
36 };
37
38 struct rap_heap_save {
39         int offset, num_strings;
40 };
41
42 static void rap_heap_save(struct rap_string_heap *heap,
43                           struct rap_heap_save *save)
44 {
45         save->offset = heap->offset;
46         save->num_strings = heap->num_strings;
47 }
48
49 static void rap_heap_restore(struct rap_string_heap *heap,
50                              struct rap_heap_save *save)
51 {
52         heap->offset = save->offset;
53         heap->num_strings = save->num_strings;
54 }
55
56 struct rap_call {
57         TALLOC_CTX *mem_ctx;
58         uint16_t callno;
59         const char *paramdesc;
60         const char *datadesc;
61
62         uint16_t status;
63         uint16_t convert;
64
65         uint16_t rcv_paramlen, rcv_datalen;
66
67         struct ndr_push *ndr_push_param;
68         struct ndr_push *ndr_push_data;
69         struct rap_string_heap *heap;
70
71         struct ndr_pull *ndr_pull_param;
72         struct ndr_pull *ndr_pull_data;
73 };
74
75 #define RAPNDR_FLAGS (LIBNDR_FLAG_NOALIGN|LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM);
76
77 static struct rap_call *new_rap_srv_call(TALLOC_CTX *mem_ctx,
78                                          struct smb_trans2 *trans)
79 {
80         struct rap_call *call;
81
82         call = talloc(mem_ctx, struct rap_call);
83
84         if (call == NULL)
85                 return NULL;
86
87         ZERO_STRUCTP(call);
88
89         call->mem_ctx = mem_ctx;
90
91         call->ndr_pull_param = ndr_pull_init_blob(&trans->in.params, mem_ctx);
92         call->ndr_pull_param->flags = RAPNDR_FLAGS;
93
94         call->ndr_pull_data = ndr_pull_init_blob(&trans->in.data, mem_ctx);
95         call->ndr_pull_data->flags = RAPNDR_FLAGS;
96
97         call->heap = talloc(mem_ctx, struct rap_string_heap);
98
99         if (call->heap == NULL)
100                 return NULL;
101
102         ZERO_STRUCTP(call->heap);
103
104         call->heap->mem_ctx = mem_ctx;
105
106         return call;
107 }
108
109 static NTSTATUS rap_srv_pull_word(struct rap_call *call, uint16_t *result)
110 {
111         if (*call->paramdesc++ != 'W')
112                 return NT_STATUS_INVALID_PARAMETER;
113
114         return ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, result);
115 }
116
117 static NTSTATUS rap_srv_pull_dword(struct rap_call *call, uint32_t *result)
118 {
119         if (*call->paramdesc++ != 'D')
120                 return NT_STATUS_INVALID_PARAMETER;
121
122         return ndr_pull_uint32(call->ndr_pull_param, NDR_SCALARS, result);
123 }
124
125 static NTSTATUS rap_srv_pull_string(struct rap_call *call, const char **result)
126 {
127         char paramdesc = *call->paramdesc++;
128
129         if (paramdesc == 'O') {
130                 *result = NULL;
131                 return NT_STATUS_OK;
132         }
133
134         if (paramdesc != 'z')
135                 return NT_STATUS_INVALID_PARAMETER;
136
137         return ndr_pull_string(call->ndr_pull_param, NDR_SCALARS, result);
138 }
139
140 static NTSTATUS rap_srv_pull_bufsize(struct rap_call *call, uint16_t *bufsize)
141 {
142         NTSTATUS result;
143
144         if ( (*call->paramdesc++ != 'r') || (*call->paramdesc++ != 'L') )
145                 return NT_STATUS_INVALID_PARAMETER;
146
147         result = ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, bufsize);
148
149         if (!NT_STATUS_IS_OK(result))
150                 return result;
151
152         call->heap->offset = *bufsize;
153
154         return NT_STATUS_OK;
155 }
156
157 static NTSTATUS rap_srv_pull_expect_multiple(struct rap_call *call)
158 {
159         if ( (*call->paramdesc++ != 'e') || (*call->paramdesc++ != 'h') )
160                 return NT_STATUS_INVALID_PARAMETER;
161
162         return NT_STATUS_OK;
163 }
164
165 static NTSTATUS rap_push_string(struct ndr_push *data_push,
166                                 struct rap_string_heap *heap,
167                                 const char *str)
168 {
169         size_t space;
170
171         if (str == NULL)
172                 str = "";
173
174         space = strlen(str)+1;
175
176         if (heap->offset < space)
177                 return NT_STATUS_BUFFER_TOO_SMALL;
178
179         heap->offset -= space;
180
181         NDR_CHECK(ndr_push_uint16(data_push, NDR_SCALARS, heap->offset));
182         NDR_CHECK(ndr_push_uint16(data_push, NDR_SCALARS, 0));
183
184         heap->strings = talloc_realloc(heap->mem_ctx,
185                                          heap->strings,
186                                          const char *,
187                                          heap->num_strings + 1);
188
189         if (heap->strings == NULL)
190                 return NT_STATUS_NO_MEMORY;
191
192         heap->strings[heap->num_strings] = str;
193         heap->num_strings += 1;
194
195         return NT_STATUS_OK;
196 }
197
198 #define NDR_OK(call) do { result = call; \
199                              if (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) \
200                                 goto buffer_overflow; \
201                              if (!NT_STATUS_IS_OK(result)) \
202                                 goto done; \
203                         } while (0)
204
205 static NTSTATUS _rap_netshareenum(struct rap_call *call)
206 {
207         struct rap_NetShareEnum r;
208         NTSTATUS result;
209
210         NDR_OK(rap_srv_pull_word(call, &r.in.level));
211         NDR_OK(rap_srv_pull_bufsize(call, &r.in.bufsize));
212         NDR_OK(rap_srv_pull_expect_multiple(call));
213
214         switch(r.in.level) {
215         case 0:
216                 if (strcmp(call->datadesc, "B13") != 0)
217                         return NT_STATUS_INVALID_PARAMETER;
218                 break;
219         case 1:
220                 if (strcmp(call->datadesc, "B13BWz") != 0)
221                         return NT_STATUS_INVALID_PARAMETER;
222                 break;
223         default:
224                 return NT_STATUS_INVALID_PARAMETER;
225                 break;
226         }
227
228         result = rap_netshareenum(call, &r);
229
230         if (!NT_STATUS_IS_OK(result))
231                 return result;
232
233         for (r.out.count = 0; r.out.count < r.out.available; r.out.count++) {
234
235                 int i = r.out.count;
236                 struct ndr_push_save data_save;
237                 struct rap_heap_save heap_save;
238
239                 ndr_push_save(call->ndr_push_data, &data_save);
240                 rap_heap_save(call->heap, &heap_save);
241
242                 switch(r.in.level) {
243                 case 0:
244                         NDR_OK(ndr_push_bytes(call->ndr_push_data,
245                                               (const uint8_t *)r.out.info[i].info0.name,
246                                               sizeof(r.out.info[i].info0.name)));
247                         break;
248                 case 1:
249                         NDR_OK(ndr_push_bytes(call->ndr_push_data,
250                                               (const uint8_t *)r.out.info[i].info1.name,
251                                               sizeof(r.out.info[i].info1.name)));
252                         NDR_OK(ndr_push_uint8(call->ndr_push_data,
253                                               NDR_SCALARS, r.out.info[i].info1.pad));
254                         NDR_OK(ndr_push_uint16(call->ndr_push_data,
255                                                NDR_SCALARS, r.out.info[i].info1.type));
256
257                         NDR_OK(rap_push_string(call->ndr_push_data,
258                                                call->heap,
259                                                r.out.info[i].info1.comment));
260
261                         break;
262                 }
263
264                 if (call->ndr_push_data->offset > call->heap->offset) {
265
266         buffer_overflow:
267
268                         ndr_push_restore(call->ndr_push_data, &data_save);
269                         rap_heap_restore(call->heap, &heap_save);
270                         break;
271                 }
272         }
273
274         call->status = r.out.status;
275
276         NDR_CHECK(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.count));
277         NDR_CHECK(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.available));
278
279         result = NT_STATUS_OK;
280
281  done:
282         return result;
283 }
284
285 static NTSTATUS _rap_netserverenum2(struct rap_call *call)
286 {
287         struct rap_NetServerEnum2 r;
288         NTSTATUS result;
289
290         NDR_OK(rap_srv_pull_word(call, &r.in.level));
291         NDR_OK(rap_srv_pull_bufsize(call, &r.in.bufsize));
292         NDR_OK(rap_srv_pull_expect_multiple(call));
293         NDR_OK(rap_srv_pull_dword(call, &r.in.servertype));
294         NDR_OK(rap_srv_pull_string(call, &r.in.domain));
295
296         switch(r.in.level) {
297         case 0:
298                 if (strcmp(call->datadesc, "B16") != 0)
299                         return NT_STATUS_INVALID_PARAMETER;
300                 break;
301         case 1:
302                 if (strcmp(call->datadesc, "B16BBDz") != 0)
303                         return NT_STATUS_INVALID_PARAMETER;
304                 break;
305         default:
306                 return NT_STATUS_INVALID_PARAMETER;
307                 break;
308         }
309
310         result = rap_netserverenum2(call, &r);
311
312         if (!NT_STATUS_IS_OK(result))
313                 return result;
314
315         for (r.out.count = 0; r.out.count < r.out.available; r.out.count++) {
316
317                 int i = r.out.count;
318                 struct ndr_push_save data_save;
319                 struct rap_heap_save heap_save;
320
321                 ndr_push_save(call->ndr_push_data, &data_save);
322                 rap_heap_save(call->heap, &heap_save);
323
324                 switch(r.in.level) {
325                 case 0:
326                         NDR_OK(ndr_push_bytes(call->ndr_push_data,
327                                               (const uint8_t *)r.out.info[i].info0.name,
328                                               sizeof(r.out.info[i].info0.name)));
329                         break;
330                 case 1:
331                         NDR_OK(ndr_push_bytes(call->ndr_push_data,
332                                               (const uint8_t *)r.out.info[i].info1.name,
333                                               sizeof(r.out.info[i].info1.name)));
334                         NDR_OK(ndr_push_uint8(call->ndr_push_data,
335                                               NDR_SCALARS, r.out.info[i].info1.version_major));
336                         NDR_OK(ndr_push_uint8(call->ndr_push_data,
337                                               NDR_SCALARS, r.out.info[i].info1.version_minor));
338                         NDR_OK(ndr_push_uint32(call->ndr_push_data,
339                                                NDR_SCALARS, r.out.info[i].info1.servertype));
340
341                         NDR_OK(rap_push_string(call->ndr_push_data,
342                                                call->heap,
343                                                r.out.info[i].info1.comment));
344
345                         break;
346                 }
347
348                 if (call->ndr_push_data->offset > call->heap->offset) {
349
350         buffer_overflow:
351
352                         ndr_push_restore(call->ndr_push_data, &data_save);
353                         rap_heap_restore(call->heap, &heap_save);
354                         break;
355                 }
356         }
357
358         call->status = r.out.status;
359
360         NDR_CHECK(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.count));
361         NDR_CHECK(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.available));
362
363         result = NT_STATUS_OK;
364
365  done:
366         return result;
367 }
368
369 static NTSTATUS api_Unsupported(struct rap_call *call)
370 {
371         call->status = NERR_notsupported;
372         call->convert = 0;
373         return NT_STATUS_OK;
374 }
375
376 static const struct
377 {
378         const char *name;
379         int id;
380         NTSTATUS (*fn)(struct rap_call *call);
381 } api_commands[] = {
382         {"NetShareEnum", RAP_WshareEnum, _rap_netshareenum },
383         {"NetServerEnum2", RAP_NetServerEnum2, _rap_netserverenum2 },
384         {NULL, -1, api_Unsupported}
385 };
386
387 NTSTATUS ipc_rap_call(TALLOC_CTX *mem_ctx, struct smb_trans2 *trans)
388 {
389         int i;
390         NTSTATUS result;
391         struct rap_call *call;
392         DATA_BLOB result_param, result_data;
393         struct ndr_push *final_param;
394         struct ndr_push *final_data;
395
396         call = new_rap_srv_call(mem_ctx, trans);
397
398         if (call == NULL)
399                 return NT_STATUS_NO_MEMORY;
400
401         NDR_CHECK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &call->callno));
402         NDR_CHECK(ndr_pull_string(call->ndr_pull_param, NDR_SCALARS,
403                                   &call->paramdesc));
404         NDR_CHECK(ndr_pull_string(call->ndr_pull_param, NDR_SCALARS,
405                                   &call->datadesc));
406
407         call->ndr_push_param = ndr_push_init_ctx(call);
408         call->ndr_push_data = ndr_push_init_ctx(call);
409
410         if ((call->ndr_push_param == NULL) || (call->ndr_push_data == NULL))
411                 return NT_STATUS_NO_MEMORY;
412
413         call->ndr_push_param->flags = RAPNDR_FLAGS;
414         call->ndr_push_data->flags = RAPNDR_FLAGS;
415
416         result = NT_STATUS_INVALID_SYSTEM_SERVICE;
417
418         for (i=0; api_commands[i].name != NULL; i++) {
419                 if (api_commands[i].id == call->callno) {
420                         DEBUG(5, ("Running RAP call %s\n",
421                                   api_commands[i].name));
422                         result = api_commands[i].fn(call);
423                         break;
424                 }
425         }
426
427         if (!NT_STATUS_IS_OK(result))
428                 return result;
429
430         result_param = ndr_push_blob(call->ndr_push_param);
431         result_data = ndr_push_blob(call->ndr_push_data);
432
433         final_param = ndr_push_init_ctx(call);
434         final_data = ndr_push_init_ctx(call);
435
436         if ((final_param == NULL) || (final_data == NULL))
437                 return NT_STATUS_NO_MEMORY;
438
439         final_param->flags = RAPNDR_FLAGS;
440         final_data->flags = RAPNDR_FLAGS;
441
442         NDR_CHECK(ndr_push_uint16(final_param, NDR_SCALARS, call->status));
443         NDR_CHECK(ndr_push_uint16(final_param,
444                                   NDR_SCALARS, call->heap->offset - result_data.length));
445         NDR_CHECK(ndr_push_bytes(final_param, result_param.data,
446                                  result_param.length));
447
448         NDR_CHECK(ndr_push_bytes(final_data, result_data.data,
449                                  result_data.length));
450
451         for (i=call->heap->num_strings-1; i>=0; i--)
452                 NDR_CHECK(ndr_push_string(final_data, NDR_SCALARS,
453                                           call->heap->strings[i]));
454
455         trans->out.setup_count = 0;
456         trans->out.setup = NULL;
457         trans->out.params = ndr_push_blob(final_param);
458         trans->out.data = ndr_push_blob(final_data);
459
460         return result;
461 }