r25920: ndr: change NTSTAUS into enum ndr_err_code (samba4 callers)
[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 NDR_RETURN(call) do { \
28         enum ndr_err_code _ndr_err; \
29         _ndr_err = call; \
30         if (!NDR_ERR_CODE_IS_SUCCESS(_ndr_err)) { \
31                 return ndr_map_error2ntstatus(_ndr_err); \
32         } \
33 } while (0)
34
35 #define RAP_GOTO(call) do { \
36         result = call; \
37         if (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) {\
38                 goto buffer_overflow; \
39         } \
40         if (!NT_STATUS_IS_OK(result)) { \
41                 goto done; \
42         } \
43 } while (0)
44
45 #define NDR_GOTO(call) do { \
46         enum ndr_err_code _ndr_err; \
47         _ndr_err = call; \
48         if (!NDR_ERR_CODE_IS_SUCCESS(_ndr_err)) { \
49                 RAP_GOTO(ndr_map_error2ntstatus(_ndr_err)); \
50         } \
51 } while (0)
52
53
54 #define NERR_Success 0
55 #define NERR_badpass 86
56 #define NERR_notsupported 50
57
58 struct rap_string_heap {
59         TALLOC_CTX *mem_ctx;
60         int offset;
61         int num_strings;
62         const char **strings;
63 };
64
65 struct rap_heap_save {
66         int offset, num_strings;
67 };
68
69 static void rap_heap_save(struct rap_string_heap *heap,
70                           struct rap_heap_save *save)
71 {
72         save->offset = heap->offset;
73         save->num_strings = heap->num_strings;
74 }
75
76 static void rap_heap_restore(struct rap_string_heap *heap,
77                              struct rap_heap_save *save)
78 {
79         heap->offset = save->offset;
80         heap->num_strings = save->num_strings;
81 }
82
83 struct rap_call {
84         TALLOC_CTX *mem_ctx;
85         uint16_t callno;
86         const char *paramdesc;
87         const char *datadesc;
88
89         uint16_t status;
90         uint16_t convert;
91
92         uint16_t rcv_paramlen, rcv_datalen;
93
94         struct ndr_push *ndr_push_param;
95         struct ndr_push *ndr_push_data;
96         struct rap_string_heap *heap;
97
98         struct ndr_pull *ndr_pull_param;
99         struct ndr_pull *ndr_pull_data;
100 };
101
102 #define RAPNDR_FLAGS (LIBNDR_FLAG_NOALIGN|LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM);
103
104 static struct rap_call *new_rap_srv_call(TALLOC_CTX *mem_ctx,
105                                          struct smb_trans2 *trans)
106 {
107         struct rap_call *call;
108
109         call = talloc(mem_ctx, struct rap_call);
110
111         if (call == NULL)
112                 return NULL;
113
114         ZERO_STRUCTP(call);
115
116         call->mem_ctx = mem_ctx;
117
118         call->ndr_pull_param = ndr_pull_init_blob(&trans->in.params, mem_ctx);
119         call->ndr_pull_param->flags = RAPNDR_FLAGS;
120
121         call->ndr_pull_data = ndr_pull_init_blob(&trans->in.data, mem_ctx);
122         call->ndr_pull_data->flags = RAPNDR_FLAGS;
123
124         call->heap = talloc(mem_ctx, struct rap_string_heap);
125
126         if (call->heap == NULL)
127                 return NULL;
128
129         ZERO_STRUCTP(call->heap);
130
131         call->heap->mem_ctx = mem_ctx;
132
133         return call;
134 }
135
136 static NTSTATUS rap_srv_pull_word(struct rap_call *call, uint16_t *result)
137 {
138         enum ndr_err_code ndr_err;
139
140         if (*call->paramdesc++ != 'W')
141                 return NT_STATUS_INVALID_PARAMETER;
142
143         ndr_err = ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, result);
144         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
145                 return ndr_map_error2ntstatus(ndr_err);
146         }
147
148         return NT_STATUS_OK;
149 }
150
151 static NTSTATUS rap_srv_pull_dword(struct rap_call *call, uint32_t *result)
152 {
153         enum ndr_err_code ndr_err;
154
155         if (*call->paramdesc++ != 'D')
156                 return NT_STATUS_INVALID_PARAMETER;
157
158         ndr_err = ndr_pull_uint32(call->ndr_pull_param, NDR_SCALARS, result);
159         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
160                 return ndr_map_error2ntstatus(ndr_err);
161         }
162
163         return NT_STATUS_OK;
164 }
165
166 static NTSTATUS rap_srv_pull_string(struct rap_call *call, const char **result)
167 {
168         enum ndr_err_code ndr_err;
169         char paramdesc = *call->paramdesc++;
170
171         if (paramdesc == 'O') {
172                 *result = NULL;
173                 return NT_STATUS_OK;
174         }
175
176         if (paramdesc != 'z')
177                 return NT_STATUS_INVALID_PARAMETER;
178
179         ndr_err = ndr_pull_string(call->ndr_pull_param, NDR_SCALARS, result);
180         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
181                 return ndr_map_error2ntstatus(ndr_err);
182         }
183
184         return NT_STATUS_OK;
185 }
186
187 static NTSTATUS rap_srv_pull_bufsize(struct rap_call *call, uint16_t *bufsize)
188 {
189         enum ndr_err_code ndr_err;
190
191         if ( (*call->paramdesc++ != 'r') || (*call->paramdesc++ != 'L') )
192                 return NT_STATUS_INVALID_PARAMETER;
193
194         ndr_err = ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, bufsize);
195         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
196                 return ndr_map_error2ntstatus(ndr_err);
197         }
198
199         call->heap->offset = *bufsize;
200
201         return NT_STATUS_OK;
202 }
203
204 static NTSTATUS rap_srv_pull_expect_multiple(struct rap_call *call)
205 {
206         if ( (*call->paramdesc++ != 'e') || (*call->paramdesc++ != 'h') )
207                 return NT_STATUS_INVALID_PARAMETER;
208
209         return NT_STATUS_OK;
210 }
211
212 static NTSTATUS rap_push_string(struct ndr_push *data_push,
213                                 struct rap_string_heap *heap,
214                                 const char *str)
215 {
216         size_t space;
217
218         if (str == NULL)
219                 str = "";
220
221         space = strlen(str)+1;
222
223         if (heap->offset < space)
224                 return NT_STATUS_BUFFER_TOO_SMALL;
225
226         heap->offset -= space;
227
228         NDR_RETURN(ndr_push_uint16(data_push, NDR_SCALARS, heap->offset));
229         NDR_RETURN(ndr_push_uint16(data_push, NDR_SCALARS, 0));
230
231         heap->strings = talloc_realloc(heap->mem_ctx,
232                                          heap->strings,
233                                          const char *,
234                                          heap->num_strings + 1);
235
236         if (heap->strings == NULL)
237                 return NT_STATUS_NO_MEMORY;
238
239         heap->strings[heap->num_strings] = str;
240         heap->num_strings += 1;
241
242         return NT_STATUS_OK;
243 }
244
245 static NTSTATUS _rap_netshareenum(struct rap_call *call)
246 {
247         struct rap_NetShareEnum r;
248         NTSTATUS result;
249
250         RAP_GOTO(rap_srv_pull_word(call, &r.in.level));
251         RAP_GOTO(rap_srv_pull_bufsize(call, &r.in.bufsize));
252         RAP_GOTO(rap_srv_pull_expect_multiple(call));
253
254         switch(r.in.level) {
255         case 0:
256                 if (strcmp(call->datadesc, "B13") != 0)
257                         return NT_STATUS_INVALID_PARAMETER;
258                 break;
259         case 1:
260                 if (strcmp(call->datadesc, "B13BWz") != 0)
261                         return NT_STATUS_INVALID_PARAMETER;
262                 break;
263         default:
264                 return NT_STATUS_INVALID_PARAMETER;
265                 break;
266         }
267
268         result = rap_netshareenum(call, &r);
269
270         if (!NT_STATUS_IS_OK(result))
271                 return result;
272
273         for (r.out.count = 0; r.out.count < r.out.available; r.out.count++) {
274
275                 int i = r.out.count;
276                 struct ndr_push_save data_save;
277                 struct rap_heap_save heap_save;
278
279                 ndr_push_save(call->ndr_push_data, &data_save);
280                 rap_heap_save(call->heap, &heap_save);
281
282                 switch(r.in.level) {
283                 case 0:
284                         NDR_GOTO(ndr_push_bytes(call->ndr_push_data,
285                                               (const uint8_t *)r.out.info[i].info0.name,
286                                               sizeof(r.out.info[i].info0.name)));
287                         break;
288                 case 1:
289                         NDR_GOTO(ndr_push_bytes(call->ndr_push_data,
290                                               (const uint8_t *)r.out.info[i].info1.name,
291                                               sizeof(r.out.info[i].info1.name)));
292                         NDR_GOTO(ndr_push_uint8(call->ndr_push_data,
293                                               NDR_SCALARS, r.out.info[i].info1.pad));
294                         NDR_GOTO(ndr_push_uint16(call->ndr_push_data,
295                                                NDR_SCALARS, r.out.info[i].info1.type));
296
297                         RAP_GOTO(rap_push_string(call->ndr_push_data,
298                                                call->heap,
299                                                r.out.info[i].info1.comment));
300
301                         break;
302                 }
303
304                 if (call->ndr_push_data->offset > call->heap->offset) {
305
306         buffer_overflow:
307
308                         ndr_push_restore(call->ndr_push_data, &data_save);
309                         rap_heap_restore(call->heap, &heap_save);
310                         break;
311                 }
312         }
313
314         call->status = r.out.status;
315
316         NDR_RETURN(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.count));
317         NDR_RETURN(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.available));
318
319         result = NT_STATUS_OK;
320
321  done:
322         return result;
323 }
324
325 static NTSTATUS _rap_netserverenum2(struct rap_call *call)
326 {
327         struct rap_NetServerEnum2 r;
328         NTSTATUS result;
329
330         RAP_GOTO(rap_srv_pull_word(call, &r.in.level));
331         RAP_GOTO(rap_srv_pull_bufsize(call, &r.in.bufsize));
332         RAP_GOTO(rap_srv_pull_expect_multiple(call));
333         RAP_GOTO(rap_srv_pull_dword(call, &r.in.servertype));
334         RAP_GOTO(rap_srv_pull_string(call, &r.in.domain));
335
336         switch(r.in.level) {
337         case 0:
338                 if (strcmp(call->datadesc, "B16") != 0)
339                         return NT_STATUS_INVALID_PARAMETER;
340                 break;
341         case 1:
342                 if (strcmp(call->datadesc, "B16BBDz") != 0)
343                         return NT_STATUS_INVALID_PARAMETER;
344                 break;
345         default:
346                 return NT_STATUS_INVALID_PARAMETER;
347                 break;
348         }
349
350         result = rap_netserverenum2(call, &r);
351
352         if (!NT_STATUS_IS_OK(result))
353                 return result;
354
355         for (r.out.count = 0; r.out.count < r.out.available; r.out.count++) {
356
357                 int i = r.out.count;
358                 struct ndr_push_save data_save;
359                 struct rap_heap_save heap_save;
360
361                 ndr_push_save(call->ndr_push_data, &data_save);
362                 rap_heap_save(call->heap, &heap_save);
363
364                 switch(r.in.level) {
365                 case 0:
366                         NDR_GOTO(ndr_push_bytes(call->ndr_push_data,
367                                               (const uint8_t *)r.out.info[i].info0.name,
368                                               sizeof(r.out.info[i].info0.name)));
369                         break;
370                 case 1:
371                         NDR_GOTO(ndr_push_bytes(call->ndr_push_data,
372                                               (const uint8_t *)r.out.info[i].info1.name,
373                                               sizeof(r.out.info[i].info1.name)));
374                         NDR_GOTO(ndr_push_uint8(call->ndr_push_data,
375                                               NDR_SCALARS, r.out.info[i].info1.version_major));
376                         NDR_GOTO(ndr_push_uint8(call->ndr_push_data,
377                                               NDR_SCALARS, r.out.info[i].info1.version_minor));
378                         NDR_GOTO(ndr_push_uint32(call->ndr_push_data,
379                                                NDR_SCALARS, r.out.info[i].info1.servertype));
380
381                         RAP_GOTO(rap_push_string(call->ndr_push_data,
382                                                call->heap,
383                                                r.out.info[i].info1.comment));
384
385                         break;
386                 }
387
388                 if (call->ndr_push_data->offset > call->heap->offset) {
389
390         buffer_overflow:
391
392                         ndr_push_restore(call->ndr_push_data, &data_save);
393                         rap_heap_restore(call->heap, &heap_save);
394                         break;
395                 }
396         }
397
398         call->status = r.out.status;
399
400         NDR_RETURN(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.count));
401         NDR_RETURN(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.available));
402
403         result = NT_STATUS_OK;
404
405  done:
406         return result;
407 }
408
409 static NTSTATUS api_Unsupported(struct rap_call *call)
410 {
411         call->status = NERR_notsupported;
412         call->convert = 0;
413         return NT_STATUS_OK;
414 }
415
416 static const struct
417 {
418         const char *name;
419         int id;
420         NTSTATUS (*fn)(struct rap_call *call);
421 } api_commands[] = {
422         {"NetShareEnum", RAP_WshareEnum, _rap_netshareenum },
423         {"NetServerEnum2", RAP_NetServerEnum2, _rap_netserverenum2 },
424         {NULL, -1, api_Unsupported}
425 };
426
427 NTSTATUS ipc_rap_call(TALLOC_CTX *mem_ctx, struct smb_trans2 *trans)
428 {
429         int i;
430         NTSTATUS result;
431         struct rap_call *call;
432         DATA_BLOB result_param, result_data;
433         struct ndr_push *final_param;
434         struct ndr_push *final_data;
435
436         call = new_rap_srv_call(mem_ctx, trans);
437
438         if (call == NULL)
439                 return NT_STATUS_NO_MEMORY;
440
441         NDR_RETURN(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &call->callno));
442         NDR_RETURN(ndr_pull_string(call->ndr_pull_param, NDR_SCALARS,
443                                   &call->paramdesc));
444         NDR_RETURN(ndr_pull_string(call->ndr_pull_param, NDR_SCALARS,
445                                   &call->datadesc));
446
447         call->ndr_push_param = ndr_push_init_ctx(call);
448         call->ndr_push_data = ndr_push_init_ctx(call);
449
450         if ((call->ndr_push_param == NULL) || (call->ndr_push_data == NULL))
451                 return NT_STATUS_NO_MEMORY;
452
453         call->ndr_push_param->flags = RAPNDR_FLAGS;
454         call->ndr_push_data->flags = RAPNDR_FLAGS;
455
456         result = NT_STATUS_INVALID_SYSTEM_SERVICE;
457
458         for (i=0; api_commands[i].name != NULL; i++) {
459                 if (api_commands[i].id == call->callno) {
460                         DEBUG(5, ("Running RAP call %s\n",
461                                   api_commands[i].name));
462                         result = api_commands[i].fn(call);
463                         break;
464                 }
465         }
466
467         if (!NT_STATUS_IS_OK(result))
468                 return result;
469
470         result_param = ndr_push_blob(call->ndr_push_param);
471         result_data = ndr_push_blob(call->ndr_push_data);
472
473         final_param = ndr_push_init_ctx(call);
474         final_data = ndr_push_init_ctx(call);
475
476         if ((final_param == NULL) || (final_data == NULL))
477                 return NT_STATUS_NO_MEMORY;
478
479         final_param->flags = RAPNDR_FLAGS;
480         final_data->flags = RAPNDR_FLAGS;
481
482         NDR_RETURN(ndr_push_uint16(final_param, NDR_SCALARS, call->status));
483         NDR_RETURN(ndr_push_uint16(final_param,
484                                   NDR_SCALARS, call->heap->offset - result_data.length));
485         NDR_RETURN(ndr_push_bytes(final_param, result_param.data,
486                                  result_param.length));
487
488         NDR_RETURN(ndr_push_bytes(final_data, result_data.data,
489                                  result_data.length));
490
491         for (i=call->heap->num_strings-1; i>=0; i--)
492                 NDR_RETURN(ndr_push_string(final_data, NDR_SCALARS,
493                                           call->heap->strings[i]));
494
495         trans->out.setup_count = 0;
496         trans->out.setup = NULL;
497         trans->out.params = ndr_push_blob(final_param);
498         trans->out.data = ndr_push_blob(final_data);
499
500         return result;
501 }