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