1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Client NDR RPC interface. 28 */ 29 30 #include <sys/types.h> 31 #include <sys/errno.h> 32 #include <time.h> 33 #include <strings.h> 34 #include <assert.h> 35 #include <thread.h> 36 #include <synch.h> 37 #include <smbsrv/libsmb.h> 38 #include <smbsrv/libsmbrdr.h> 39 #include <smbsrv/libmlrpc.h> 40 #include <smbsrv/libmlsvc.h> 41 #include <smbsrv/ndl/srvsvc.ndl> 42 43 /* 44 * Server info cache entry expiration in seconds. 45 */ 46 #define NDR_SVINFO_TIMEOUT 1800 47 48 typedef struct ndr_svinfo { 49 list_node_t svi_lnd; 50 time_t svi_tcached; 51 char svi_server[MAXNAMELEN]; 52 char svi_domain[MAXNAMELEN]; 53 srvsvc_server_info_t svi_svinfo; 54 } ndr_svinfo_t; 55 56 typedef struct ndr_svlist { 57 list_t svl_list; 58 mutex_t svl_mtx; 59 boolean_t svl_init; 60 } ndr_svlist_t; 61 62 static ndr_svlist_t ndr_svlist; 63 64 static int ndr_xa_init(ndr_client_t *, ndr_xa_t *); 65 static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *); 66 static int ndr_xa_read(ndr_client_t *, ndr_xa_t *); 67 static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *); 68 static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *); 69 static void ndr_xa_release(ndr_client_t *); 70 71 static int ndr_svinfo_lookup(char *, char *, srvsvc_server_info_t *); 72 static boolean_t ndr_svinfo_match(const char *, const char *, const 73 ndr_svinfo_t *); 74 static boolean_t ndr_svinfo_expired(ndr_svinfo_t *); 75 76 /* 77 * Initialize the RPC client interface: create the server info cache. 78 */ 79 void 80 ndr_rpc_init(void) 81 { 82 (void) mutex_lock(&ndr_svlist.svl_mtx); 83 84 if (!ndr_svlist.svl_init) { 85 list_create(&ndr_svlist.svl_list, sizeof (ndr_svinfo_t), 86 offsetof(ndr_svinfo_t, svi_lnd)); 87 ndr_svlist.svl_init = B_TRUE; 88 } 89 90 (void) mutex_unlock(&ndr_svlist.svl_mtx); 91 } 92 93 /* 94 * Terminate the RPC client interface: flush and destroy the server info 95 * cache. 96 */ 97 void 98 ndr_rpc_fini(void) 99 { 100 ndr_svinfo_t *svi; 101 102 (void) mutex_lock(&ndr_svlist.svl_mtx); 103 104 if (ndr_svlist.svl_init) { 105 while ((svi = list_head(&ndr_svlist.svl_list)) != NULL) { 106 list_remove(&ndr_svlist.svl_list, svi); 107 free(svi->svi_svinfo.sv_name); 108 free(svi->svi_svinfo.sv_comment); 109 free(svi); 110 } 111 112 list_destroy(&ndr_svlist.svl_list); 113 ndr_svlist.svl_init = B_FALSE; 114 } 115 116 (void) mutex_unlock(&ndr_svlist.svl_mtx); 117 } 118 119 /* 120 * This call must be made to initialize an RPC client structure and bind 121 * to the remote service before any RPCs can be exchanged with that service. 122 * 123 * The mlsvc_handle_t is a wrapper that is used to associate an RPC handle 124 * with the client context for an instance of the interface. The handle 125 * is zeroed to ensure that it doesn't look like a valid handle - 126 * handle content is provided by the remove service. 127 * 128 * The client points to this top-level handle so that we know when to 129 * unbind and teardown the connection. As each handle is initialized it 130 * will inherit a reference to the client context. 131 */ 132 int 133 ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, 134 char *username, const char *service) 135 { 136 ndr_client_t *clnt; 137 ndr_service_t *svc; 138 srvsvc_server_info_t svinfo; 139 int fid; 140 int rc; 141 142 if (handle == NULL || server == NULL || 143 domain == NULL || username == NULL) 144 return (-1); 145 146 if ((svc = ndr_svc_lookup_name(service)) == NULL) 147 return (-1); 148 149 /* 150 * Set the default based on the assumption that most 151 * servers will be Windows 2000 or later. 152 * Don't lookup the svinfo if this is a SRVSVC request 153 * because the SRVSVC is used to get the server info. 154 * None of the SRVSVC calls depend on the server info. 155 */ 156 bzero(&svinfo, sizeof (srvsvc_server_info_t)); 157 svinfo.sv_platform_id = SV_PLATFORM_ID_NT; 158 svinfo.sv_version_major = 5; 159 svinfo.sv_version_minor = 0; 160 svinfo.sv_type = SV_TYPE_DEFAULT; 161 svinfo.sv_os = NATIVE_OS_WIN2000; 162 163 if (strcasecmp(service, "SRVSVC") != 0) 164 (void) ndr_svinfo_lookup(server, domain, &svinfo); 165 166 if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) 167 return (-1); 168 169 fid = smbrdr_open_pipe(server, domain, username, svc->endpoint); 170 if (fid < 0) { 171 free(clnt); 172 return (-1); 173 } 174 175 bzero(clnt, sizeof (ndr_client_t)); 176 clnt->handle = &handle->handle; 177 clnt->fid = fid; 178 179 ndr_svc_binding_pool_init(&clnt->binding_list, 180 clnt->binding_pool, NDR_N_BINDING_POOL); 181 182 clnt->xa_init = ndr_xa_init; 183 clnt->xa_exchange = ndr_xa_exchange; 184 clnt->xa_read = ndr_xa_read; 185 clnt->xa_preserve = ndr_xa_preserve; 186 clnt->xa_destruct = ndr_xa_destruct; 187 clnt->xa_release = ndr_xa_release; 188 189 bzero(&handle->handle, sizeof (ndr_hdid_t)); 190 handle->clnt = clnt; 191 bcopy(&svinfo, &handle->svinfo, sizeof (srvsvc_server_info_t)); 192 193 if (ndr_rpc_get_heap(handle) == NULL) { 194 free(clnt); 195 return (-1); 196 } 197 198 rc = ndr_clnt_bind(clnt, service, &clnt->binding); 199 if (NDR_DRC_IS_FAULT(rc)) { 200 (void) smbrdr_close_pipe(fid); 201 ndr_heap_destroy(clnt->heap); 202 free(clnt); 203 handle->clnt = NULL; 204 return (-1); 205 } 206 207 return (0); 208 } 209 210 /* 211 * Unbind and close the pipe to an RPC service. 212 * 213 * If the heap has been preserved we need to go through an xa release. 214 * The heap is preserved during an RPC call because that's where data 215 * returned from the server is stored. 216 * 217 * Otherwise we destroy the heap directly. 218 */ 219 void 220 ndr_rpc_unbind(mlsvc_handle_t *handle) 221 { 222 ndr_client_t *clnt = handle->clnt; 223 224 if (clnt->heap_preserved) 225 ndr_clnt_free_heap(clnt); 226 else 227 ndr_heap_destroy(clnt->heap); 228 229 (void) smbrdr_close_pipe(clnt->fid); 230 free(handle->clnt); 231 bzero(handle, sizeof (mlsvc_handle_t)); 232 } 233 234 /* 235 * Call the RPC function identified by opnum. The remote service is 236 * identified by the handle, which should have been initialized by 237 * ndr_rpc_bind. 238 * 239 * If the RPC call is successful (returns 0), the caller must call 240 * ndr_rpc_release to release the heap. Otherwise, we release the 241 * heap here. 242 */ 243 int 244 ndr_rpc_call(mlsvc_handle_t *handle, int opnum, void *params) 245 { 246 ndr_client_t *clnt = handle->clnt; 247 int rc; 248 249 if (ndr_rpc_get_heap(handle) == NULL) 250 return (-1); 251 252 rc = ndr_clnt_call(clnt->binding, opnum, params); 253 254 /* 255 * Always clear the nonull flag to ensure 256 * it is not applied to subsequent calls. 257 */ 258 clnt->nonull = B_FALSE; 259 260 if (NDR_DRC_IS_FAULT(rc)) { 261 ndr_rpc_release(handle); 262 return (-1); 263 } 264 265 return (0); 266 } 267 268 /* 269 * Outgoing strings should not be null terminated. 270 */ 271 void 272 ndr_rpc_set_nonull(mlsvc_handle_t *handle) 273 { 274 handle->clnt->nonull = B_TRUE; 275 } 276 277 /* 278 * Return a reference to the server info. 279 */ 280 const srvsvc_server_info_t * 281 ndr_rpc_server_info(mlsvc_handle_t *handle) 282 { 283 return (&handle->svinfo); 284 } 285 286 /* 287 * Return the RPC server OS level. 288 */ 289 uint32_t 290 ndr_rpc_server_os(mlsvc_handle_t *handle) 291 { 292 return (handle->svinfo.sv_os); 293 } 294 295 /* 296 * Get the session key from a bound RPC client handle. 297 * 298 * The key returned is the 16-byte "user session key" 299 * established by the underlying authentication protocol 300 * (either Kerberos or NTLM). This key is needed for 301 * SAM RPC calls such as SamrSetInformationUser, etc. 302 * See [MS-SAMR] sections: 2.2.3.3, 2.2.7.21, 2.2.7.25. 303 * 304 * Returns zero (success) or an errno. 305 */ 306 int 307 ndr_rpc_get_ssnkey(mlsvc_handle_t *handle, 308 unsigned char *ssn_key, size_t len) 309 { 310 ndr_client_t *clnt = handle->clnt; 311 int rc; 312 313 if (clnt == NULL) 314 return (EINVAL); 315 316 rc = smbrdr_get_ssnkey(clnt->fid, ssn_key, len); 317 return (rc); 318 } 319 320 void * 321 ndr_rpc_malloc(mlsvc_handle_t *handle, size_t size) 322 { 323 ndr_heap_t *heap; 324 325 if ((heap = ndr_rpc_get_heap(handle)) == NULL) 326 return (NULL); 327 328 return (ndr_heap_malloc(heap, size)); 329 } 330 331 ndr_heap_t * 332 ndr_rpc_get_heap(mlsvc_handle_t *handle) 333 { 334 ndr_client_t *clnt = handle->clnt; 335 336 if (clnt->heap == NULL) 337 clnt->heap = ndr_heap_create(); 338 339 return (clnt->heap); 340 } 341 342 /* 343 * Must be called by RPC clients to free the heap after a successful RPC 344 * call, i.e. ndr_rpc_call returned 0. The caller should take a copy 345 * of any data returned by the RPC prior to calling this function because 346 * returned data is in the heap. 347 */ 348 void 349 ndr_rpc_release(mlsvc_handle_t *handle) 350 { 351 ndr_client_t *clnt = handle->clnt; 352 353 if (clnt->heap_preserved) 354 ndr_clnt_free_heap(clnt); 355 else 356 ndr_heap_destroy(clnt->heap); 357 358 clnt->heap = NULL; 359 } 360 361 /* 362 * Returns true if the handle is null. 363 * Otherwise returns false. 364 */ 365 boolean_t 366 ndr_is_null_handle(mlsvc_handle_t *handle) 367 { 368 static ndr_hdid_t zero_handle; 369 370 if (handle == NULL || handle->clnt == NULL) 371 return (B_TRUE); 372 373 if (!memcmp(&handle->handle, &zero_handle, sizeof (ndr_hdid_t))) 374 return (B_TRUE); 375 376 return (B_FALSE); 377 } 378 379 /* 380 * Returns true if the handle is the top level bind handle. 381 * Otherwise returns false. 382 */ 383 boolean_t 384 ndr_is_bind_handle(mlsvc_handle_t *handle) 385 { 386 return (handle->clnt->handle == &handle->handle); 387 } 388 389 /* 390 * Pass the client reference from parent to child. 391 */ 392 void 393 ndr_inherit_handle(mlsvc_handle_t *child, mlsvc_handle_t *parent) 394 { 395 child->clnt = parent->clnt; 396 bcopy(&parent->svinfo, &child->svinfo, sizeof (srvsvc_server_info_t)); 397 } 398 399 void 400 ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status) 401 { 402 ndr_service_t *svc; 403 char *name = "NDR RPC"; 404 char *s = "unknown"; 405 406 if (status == 0) 407 s = "success"; 408 else if (NT_SC_IS_ERROR(status)) 409 s = "error"; 410 else if (NT_SC_IS_WARNING(status)) 411 s = "warning"; 412 else if (NT_SC_IS_INFO(status)) 413 s = "info"; 414 415 if (handle) { 416 svc = handle->clnt->binding->service; 417 name = svc->name; 418 } 419 420 smb_tracef("%s[0x%02x]: %s: %s (0x%08x)", 421 name, opnum, s, xlate_nt_status(status), status); 422 } 423 424 /* 425 * The following functions provide the client callback interface. 426 * If the caller hasn't provided a heap, create one here. 427 */ 428 static int 429 ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa) 430 { 431 ndr_stream_t *recv_nds = &mxa->recv_nds; 432 ndr_stream_t *send_nds = &mxa->send_nds; 433 ndr_heap_t *heap = clnt->heap; 434 int rc; 435 436 if (heap == NULL) { 437 if ((heap = ndr_heap_create()) == NULL) 438 return (-1); 439 440 clnt->heap = heap; 441 } 442 443 mxa->heap = heap; 444 445 rc = nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap); 446 if (rc == 0) 447 rc = nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT, 448 NDR_MODE_RETURN_RECV, heap); 449 450 if (rc != 0) { 451 nds_destruct(&mxa->recv_nds); 452 nds_destruct(&mxa->send_nds); 453 ndr_heap_destroy(mxa->heap); 454 mxa->heap = NULL; 455 clnt->heap = NULL; 456 return (-1); 457 } 458 459 if (clnt->nonull) 460 NDS_SETF(send_nds, NDS_F_NONULL); 461 462 return (0); 463 } 464 465 /* 466 * This is the entry pointy for an RPC client call exchange with 467 * a server, which will result in an smbrdr SmbTransact request. 468 * 469 * SmbTransact should return the number of bytes received, which 470 * we record as the PDU size, or a negative error code. 471 */ 472 static int 473 ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa) 474 { 475 ndr_stream_t *recv_nds = &mxa->recv_nds; 476 ndr_stream_t *send_nds = &mxa->send_nds; 477 int nbytes; 478 479 nbytes = smbrdr_transact(clnt->fid, 480 (char *)send_nds->pdu_base_offset, send_nds->pdu_size, 481 (char *)recv_nds->pdu_base_offset, recv_nds->pdu_max_size); 482 483 if (nbytes < 0) { 484 recv_nds->pdu_size = 0; 485 return (-1); 486 } 487 488 recv_nds->pdu_size = nbytes; 489 return (nbytes); 490 } 491 492 /* 493 * This entry point will be invoked if the xa-exchange response contained 494 * only the first fragment of a multi-fragment response. The RPC client 495 * code will then make repeated xa-read requests to obtain the remaining 496 * fragments, which will result in smbrdr SmbReadX requests. 497 * 498 * SmbReadX should return the number of bytes received, in which case we 499 * expand the PDU size to include the received data, or a negative error 500 * code. 501 */ 502 static int 503 ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa) 504 { 505 ndr_stream_t *nds = &mxa->recv_nds; 506 int len; 507 int nbytes; 508 509 if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0) 510 return (-1); 511 512 nbytes = smbrdr_readx(clnt->fid, 513 (char *)nds->pdu_base_offset + nds->pdu_size, len); 514 515 if (nbytes < 0) 516 return (-1); 517 518 nds->pdu_size += nbytes; 519 520 if (nds->pdu_size > nds->pdu_max_size) { 521 nds->pdu_size = nds->pdu_max_size; 522 return (-1); 523 } 524 525 return (nbytes); 526 } 527 528 /* 529 * Preserve the heap so that the client application has access to data 530 * returned from the server after an RPC call. 531 */ 532 static void 533 ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa) 534 { 535 assert(clnt->heap == mxa->heap); 536 537 clnt->heap_preserved = B_TRUE; 538 mxa->heap = NULL; 539 } 540 541 /* 542 * Dispose of the transaction streams. If the heap has not been 543 * preserved, we can destroy it here. 544 */ 545 static void 546 ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa) 547 { 548 nds_destruct(&mxa->recv_nds); 549 nds_destruct(&mxa->send_nds); 550 551 if (!clnt->heap_preserved) { 552 ndr_heap_destroy(mxa->heap); 553 mxa->heap = NULL; 554 clnt->heap = NULL; 555 } 556 } 557 558 /* 559 * Dispose of a preserved heap. 560 */ 561 static void 562 ndr_xa_release(ndr_client_t *clnt) 563 { 564 if (clnt->heap_preserved) { 565 ndr_heap_destroy(clnt->heap); 566 clnt->heap = NULL; 567 clnt->heap_preserved = B_FALSE; 568 } 569 } 570 571 /* 572 * Lookup platform, type and version information about a server. 573 * If the cache doesn't already contain the data, contact the server and 574 * cache the response before returning the server info to the caller. 575 * 576 * We don't provide the name or comment for now, which avoids the need 577 * to deal with unnecessary memory management. 578 */ 579 static int 580 ndr_svinfo_lookup(char *server, char *domain, srvsvc_server_info_t *svinfo) 581 { 582 ndr_svinfo_t *svi; 583 584 (void) mutex_lock(&ndr_svlist.svl_mtx); 585 assert(ndr_svlist.svl_init == B_TRUE); 586 587 svi = list_head(&ndr_svlist.svl_list); 588 while (svi != NULL) { 589 if (ndr_svinfo_expired(svi)) { 590 svi = list_head(&ndr_svlist.svl_list); 591 continue; 592 } 593 594 if (ndr_svinfo_match(server, domain, svi)) { 595 bcopy(&svi->svi_svinfo, svinfo, 596 sizeof (srvsvc_server_info_t)); 597 svinfo->sv_name = NULL; 598 svinfo->sv_comment = NULL; 599 (void) mutex_unlock(&ndr_svlist.svl_mtx); 600 return (0); 601 } 602 603 svi = list_next(&ndr_svlist.svl_list, svi); 604 } 605 606 if ((svi = malloc(sizeof (ndr_svinfo_t))) == NULL) { 607 (void) mutex_unlock(&ndr_svlist.svl_mtx); 608 return (-1); 609 } 610 611 if (srvsvc_net_server_getinfo(server, domain, &svi->svi_svinfo) < 0) { 612 (void) mutex_unlock(&ndr_svlist.svl_mtx); 613 free(svi); 614 return (-1); 615 } 616 617 (void) time(&svi->svi_tcached); 618 (void) strlcpy(svi->svi_server, server, MAXNAMELEN); 619 (void) strlcpy(svi->svi_domain, domain, MAXNAMELEN); 620 list_insert_tail(&ndr_svlist.svl_list, svi); 621 bcopy(&svi->svi_svinfo, svinfo, sizeof (srvsvc_server_info_t)); 622 svinfo->sv_name = NULL; 623 svinfo->sv_comment = NULL; 624 (void) mutex_unlock(&ndr_svlist.svl_mtx); 625 return (0); 626 } 627 628 static boolean_t 629 ndr_svinfo_match(const char *server, const char *domain, 630 const ndr_svinfo_t *svi) 631 { 632 if ((smb_strcasecmp(server, svi->svi_server, 0) == 0) && 633 (smb_strcasecmp(domain, svi->svi_domain, 0) == 0)) { 634 return (B_TRUE); 635 } 636 637 return (B_FALSE); 638 } 639 640 /* 641 * If the server info in the cache has expired, discard it and return true. 642 * Otherwise return false. 643 * 644 * This is a private function to support ndr_svinfo_lookup() that assumes 645 * the list mutex is held. 646 */ 647 static boolean_t 648 ndr_svinfo_expired(ndr_svinfo_t *svi) 649 { 650 time_t tnow; 651 652 (void) time(&tnow); 653 654 if (difftime(tnow, svi->svi_tcached) > NDR_SVINFO_TIMEOUT) { 655 list_remove(&ndr_svlist.svl_list, svi); 656 free(svi->svi_svinfo.sv_name); 657 free(svi->svi_svinfo.sv_comment); 658 free(svi); 659 return (B_TRUE); 660 } 661 662 return (B_FALSE); 663 } 664