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