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 2010 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 <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 if (status == 0) 411 s = "success"; 412 else if (NT_SC_IS_ERROR(status)) 413 s = "error"; 414 else if (NT_SC_IS_WARNING(status)) 415 s = "warning"; 416 else if (NT_SC_IS_INFO(status)) 417 s = "info"; 418 419 if (handle) { 420 svc = handle->clnt->binding->service; 421 name = svc->name; 422 } 423 424 smb_tracef("%s[0x%02x]: %s: %s (0x%08x)", 425 name, opnum, s, xlate_nt_status(status), status); 426 } 427 428 /* 429 * The following functions provide the client callback interface. 430 * If the caller hasn't provided a heap, create one here. 431 */ 432 static int 433 ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa) 434 { 435 ndr_stream_t *recv_nds = &mxa->recv_nds; 436 ndr_stream_t *send_nds = &mxa->send_nds; 437 ndr_heap_t *heap = clnt->heap; 438 int rc; 439 440 if (heap == NULL) { 441 if ((heap = ndr_heap_create()) == NULL) 442 return (-1); 443 444 clnt->heap = heap; 445 } 446 447 mxa->heap = heap; 448 449 rc = nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap); 450 if (rc == 0) 451 rc = nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT, 452 NDR_MODE_RETURN_RECV, heap); 453 454 if (rc != 0) { 455 nds_destruct(&mxa->recv_nds); 456 nds_destruct(&mxa->send_nds); 457 ndr_heap_destroy(mxa->heap); 458 mxa->heap = NULL; 459 clnt->heap = NULL; 460 return (-1); 461 } 462 463 if (clnt->nonull) 464 NDS_SETF(send_nds, NDS_F_NONULL); 465 466 return (0); 467 } 468 469 /* 470 * This is the entry pointy for an RPC client call exchange with 471 * a server, which will result in an smbrdr SmbTransact request. 472 * 473 * SmbTransact should return the number of bytes received, which 474 * we record as the PDU size, or a negative error code. 475 */ 476 static int 477 ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa) 478 { 479 ndr_stream_t *recv_nds = &mxa->recv_nds; 480 ndr_stream_t *send_nds = &mxa->send_nds; 481 int nbytes; 482 483 nbytes = smbrdr_transact(clnt->fid, 484 (char *)send_nds->pdu_base_offset, send_nds->pdu_size, 485 (char *)recv_nds->pdu_base_offset, recv_nds->pdu_max_size); 486 487 if (nbytes < 0) { 488 recv_nds->pdu_size = 0; 489 return (-1); 490 } 491 492 recv_nds->pdu_size = nbytes; 493 return (nbytes); 494 } 495 496 /* 497 * This entry point will be invoked if the xa-exchange response contained 498 * only the first fragment of a multi-fragment response. The RPC client 499 * code will then make repeated xa-read requests to obtain the remaining 500 * fragments, which will result in smbrdr SmbReadX requests. 501 * 502 * SmbReadX should return the number of bytes received, in which case we 503 * expand the PDU size to include the received data, or a negative error 504 * code. 505 */ 506 static int 507 ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa) 508 { 509 ndr_stream_t *nds = &mxa->recv_nds; 510 int len; 511 int nbytes; 512 513 if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0) 514 return (-1); 515 516 nbytes = smbrdr_readx(clnt->fid, 517 (char *)nds->pdu_base_offset + nds->pdu_size, len); 518 519 if (nbytes < 0) 520 return (-1); 521 522 nds->pdu_size += nbytes; 523 524 if (nds->pdu_size > nds->pdu_max_size) { 525 nds->pdu_size = nds->pdu_max_size; 526 return (-1); 527 } 528 529 return (nbytes); 530 } 531 532 /* 533 * Preserve the heap so that the client application has access to data 534 * returned from the server after an RPC call. 535 */ 536 static void 537 ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa) 538 { 539 assert(clnt->heap == mxa->heap); 540 541 clnt->heap_preserved = B_TRUE; 542 mxa->heap = NULL; 543 } 544 545 /* 546 * Dispose of the transaction streams. If the heap has not been 547 * preserved, we can destroy it here. 548 */ 549 static void 550 ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa) 551 { 552 nds_destruct(&mxa->recv_nds); 553 nds_destruct(&mxa->send_nds); 554 555 if (!clnt->heap_preserved) { 556 ndr_heap_destroy(mxa->heap); 557 mxa->heap = NULL; 558 clnt->heap = NULL; 559 } 560 } 561 562 /* 563 * Dispose of a preserved heap. 564 */ 565 static void 566 ndr_xa_release(ndr_client_t *clnt) 567 { 568 if (clnt->heap_preserved) { 569 ndr_heap_destroy(clnt->heap); 570 clnt->heap = NULL; 571 clnt->heap_preserved = B_FALSE; 572 } 573 } 574 575 /* 576 * Lookup platform, type and version information about a server. 577 * If the cache doesn't already contain the data, contact the server and 578 * cache the response before returning the server info to the caller. 579 * 580 * We don't provide the name or comment for now, which avoids the need 581 * to deal with unnecessary memory management. 582 */ 583 static int 584 ndr_svinfo_lookup(char *server, char *domain, srvsvc_server_info_t *svinfo) 585 { 586 static boolean_t timechecked = B_FALSE; 587 ndr_svinfo_t *svi; 588 589 (void) mutex_lock(&ndr_svlist.svl_mtx); 590 if (!ndr_svlist.svl_init) 591 return (-1); 592 593 svi = list_head(&ndr_svlist.svl_list); 594 while (svi != NULL) { 595 if (ndr_svinfo_expired(svi)) { 596 svi = list_head(&ndr_svlist.svl_list); 597 continue; 598 } 599 600 if (ndr_svinfo_match(server, domain, svi)) { 601 bcopy(&svi->svi_svinfo, svinfo, 602 sizeof (srvsvc_server_info_t)); 603 svinfo->sv_name = NULL; 604 svinfo->sv_comment = NULL; 605 (void) mutex_unlock(&ndr_svlist.svl_mtx); 606 return (0); 607 } 608 609 svi = list_next(&ndr_svlist.svl_list, svi); 610 } 611 612 if ((svi = malloc(sizeof (ndr_svinfo_t))) == NULL) { 613 (void) mutex_unlock(&ndr_svlist.svl_mtx); 614 return (-1); 615 } 616 617 if (srvsvc_net_server_getinfo(server, domain, &svi->svi_svinfo) < 0) { 618 (void) mutex_unlock(&ndr_svlist.svl_mtx); 619 free(svi); 620 return (-1); 621 } 622 623 (void) time(&svi->svi_tcached); 624 (void) strlcpy(svi->svi_server, server, MAXNAMELEN); 625 (void) strlcpy(svi->svi_domain, domain, MAXNAMELEN); 626 list_insert_tail(&ndr_svlist.svl_list, svi); 627 bcopy(&svi->svi_svinfo, svinfo, sizeof (srvsvc_server_info_t)); 628 svinfo->sv_name = NULL; 629 svinfo->sv_comment = NULL; 630 631 if (!timechecked) { 632 timechecked = B_TRUE; 633 ndr_srvsvc_timecheck(server, domain); 634 } 635 636 (void) mutex_unlock(&ndr_svlist.svl_mtx); 637 return (0); 638 } 639 640 static boolean_t 641 ndr_svinfo_match(const char *server, const char *domain, 642 const ndr_svinfo_t *svi) 643 { 644 if ((smb_strcasecmp(server, svi->svi_server, 0) == 0) && 645 (smb_strcasecmp(domain, svi->svi_domain, 0) == 0)) { 646 return (B_TRUE); 647 } 648 649 return (B_FALSE); 650 } 651 652 /* 653 * If the server info in the cache has expired, discard it and return true. 654 * Otherwise return false. 655 * 656 * This is a private function to support ndr_svinfo_lookup() that assumes 657 * the list mutex is held. 658 */ 659 static boolean_t 660 ndr_svinfo_expired(ndr_svinfo_t *svi) 661 { 662 time_t tnow; 663 664 (void) time(&tnow); 665 666 if (difftime(tnow, svi->svi_tcached) > NDR_SVINFO_TIMEOUT) { 667 list_remove(&ndr_svlist.svl_list, svi); 668 free(svi->svi_svinfo.sv_name); 669 free(svi->svi_svinfo.sv_comment); 670 free(svi); 671 return (B_TRUE); 672 } 673 674 return (B_FALSE); 675 } 676 677 /* 678 * Compare the time here with the remote time on the server 679 * and report clock skew. 680 */ 681 void 682 ndr_srvsvc_timecheck(char *server, char *domain) 683 { 684 char hostname[MAXHOSTNAMELEN]; 685 struct timeval dc_tv; 686 struct tm dc_tm; 687 struct tm *tm; 688 time_t tnow; 689 time_t tdiff; 690 int priority; 691 692 if (srvsvc_net_remote_tod(server, domain, &dc_tv, &dc_tm) < 0) { 693 syslog(LOG_DEBUG, "srvsvc_net_remote_tod failed"); 694 return; 695 } 696 697 tnow = time(NULL); 698 699 if (tnow > dc_tv.tv_sec) 700 tdiff = (tnow - dc_tv.tv_sec) / SECSPERMIN; 701 else 702 tdiff = (dc_tv.tv_sec - tnow) / SECSPERMIN; 703 704 if (tdiff != 0) { 705 (void) strlcpy(hostname, "localhost", MAXHOSTNAMELEN); 706 (void) gethostname(hostname, MAXHOSTNAMELEN); 707 708 priority = (tdiff > 2) ? LOG_NOTICE : LOG_DEBUG; 709 syslog(priority, "DC [%s] clock skew detected: %u minutes", 710 server, tdiff); 711 712 tm = gmtime(&dc_tv.tv_sec); 713 syslog(priority, "%-8s UTC: %s", server, asctime(tm)); 714 tm = gmtime(&tnow); 715 syslog(priority, "%-8s UTC: %s", hostname, asctime(tm)); 716 } 717 } 718