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 * Copyright 2012 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 /* 28 * Client NDR RPC interface. 29 */ 30 31 #include <sys/types.h> 32 #include <sys/errno.h> 33 #include <sys/fcntl.h> 34 #include <sys/tzfile.h> 35 #include <time.h> 36 #include <strings.h> 37 #include <assert.h> 38 #include <errno.h> 39 #include <thread.h> 40 #include <unistd.h> 41 #include <syslog.h> 42 #include <synch.h> 43 44 #include <netsmb/smbfs_api.h> 45 #include <smbsrv/libsmb.h> 46 #include <smbsrv/libmlrpc.h> 47 #include <smbsrv/libmlsvc.h> 48 #include <smbsrv/ndl/srvsvc.ndl> 49 #include <libsmbrdr.h> 50 #include <mlsvc.h> 51 52 static int ndr_xa_init(ndr_client_t *, ndr_xa_t *); 53 static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *); 54 static int ndr_xa_read(ndr_client_t *, ndr_xa_t *); 55 static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *); 56 static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *); 57 static void ndr_xa_release(ndr_client_t *); 58 59 60 /* 61 * This call must be made to initialize an RPC client structure and bind 62 * to the remote service before any RPCs can be exchanged with that service. 63 * 64 * The mlsvc_handle_t is a wrapper that is used to associate an RPC handle 65 * with the client context for an instance of the interface. The handle 66 * is zeroed to ensure that it doesn't look like a valid handle - 67 * handle content is provided by the remove service. 68 * 69 * The client points to this top-level handle so that we know when to 70 * unbind and teardown the connection. As each handle is initialized it 71 * will inherit a reference to the client context. 72 */ 73 int 74 ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, 75 char *username, const char *service) 76 { 77 struct smb_ctx *ctx = NULL; 78 ndr_client_t *clnt = NULL; 79 ndr_service_t *svc; 80 srvsvc_server_info_t svinfo; 81 int fd = -1; 82 int rc; 83 84 if (handle == NULL || server == NULL || 85 domain == NULL || username == NULL) 86 return (-1); 87 88 if ((svc = ndr_svc_lookup_name(service)) == NULL) 89 return (-1); 90 91 /* 92 * Set the default based on the assumption that most 93 * servers will be Windows 2000 or later. This used to 94 * try to get the actual server version, but that RPC 95 * is not necessarily allowed anymore, so don't bother. 96 */ 97 bzero(&svinfo, sizeof (srvsvc_server_info_t)); 98 svinfo.sv_platform_id = SV_PLATFORM_ID_NT; 99 svinfo.sv_version_major = 5; 100 svinfo.sv_version_minor = 0; 101 svinfo.sv_type = SV_TYPE_DEFAULT; 102 svinfo.sv_os = NATIVE_OS_WIN2000; 103 104 /* 105 * Some callers pass this when they want a NULL session. 106 * Todo: have callers pass an empty string for that. 107 */ 108 if (strcmp(username, MLSVC_ANON_USER) == 0) 109 username = ""; 110 111 /* 112 * Setup smbfs library handle, authenticate, connect to 113 * the IPC$ share. This will reuse an existing connection 114 * if the driver already has one for this combination of 115 * server, user, domain. 116 */ 117 if ((rc = smbrdr_ctx_new(&ctx, server, domain, username)) != 0) { 118 syslog(LOG_ERR, "ndr_rpc_bind: smbrdr_ctx_new" 119 "(Srv=%s Dom=%s User=%s), %s (0x%x)", 120 server, domain, username, 121 xlate_nt_status(rc), rc); 122 goto errout; 123 } 124 125 /* 126 * Open the named pipe. 127 */ 128 fd = smb_fh_open(ctx, svc->endpoint, O_RDWR); 129 if (fd < 0) { 130 syslog(LOG_DEBUG, "ndr_rpc_bind: " 131 "smb_fh_open, err=%d", errno); 132 goto errout; 133 } 134 135 /* 136 * Setup the RPC client handle. 137 */ 138 if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) 139 goto errout; 140 bzero(clnt, sizeof (ndr_client_t)); 141 142 clnt->handle = &handle->handle; 143 clnt->xa_init = ndr_xa_init; 144 clnt->xa_exchange = ndr_xa_exchange; 145 clnt->xa_read = ndr_xa_read; 146 clnt->xa_preserve = ndr_xa_preserve; 147 clnt->xa_destruct = ndr_xa_destruct; 148 clnt->xa_release = ndr_xa_release; 149 clnt->xa_private = ctx; 150 clnt->xa_fd = fd; 151 152 ndr_svc_binding_pool_init(&clnt->binding_list, 153 clnt->binding_pool, NDR_N_BINDING_POOL); 154 155 if ((clnt->heap = ndr_heap_create()) == NULL) 156 goto errout; 157 158 /* 159 * Fill in the caller's handle. 160 */ 161 bzero(&handle->handle, sizeof (ndr_hdid_t)); 162 handle->clnt = clnt; 163 bcopy(&svinfo, &handle->svinfo, sizeof (srvsvc_server_info_t)); 164 165 /* 166 * Do the OtW RPC bind. 167 */ 168 rc = ndr_clnt_bind(clnt, service, &clnt->binding); 169 if (NDR_DRC_IS_FAULT(rc)) { 170 syslog(LOG_DEBUG, "ndr_rpc_bind: " 171 "ndr_clnt_bind, rc=0x%x", rc); 172 goto errout; 173 } 174 175 /* Success! */ 176 return (0); 177 178 errout: 179 handle->clnt = NULL; 180 if (clnt != NULL) { 181 ndr_heap_destroy(clnt->heap); 182 free(clnt); 183 } 184 if (ctx != NULL) { 185 if (fd != -1) 186 (void) smb_fh_close(fd); 187 smbrdr_ctx_free(ctx); 188 } 189 190 return (-1); 191 } 192 193 /* 194 * Unbind and close the pipe to an RPC service. 195 * 196 * If the heap has been preserved we need to go through an xa release. 197 * The heap is preserved during an RPC call because that's where data 198 * returned from the server is stored. 199 * 200 * Otherwise we destroy the heap directly. 201 */ 202 void 203 ndr_rpc_unbind(mlsvc_handle_t *handle) 204 { 205 ndr_client_t *clnt = handle->clnt; 206 struct smb_ctx *ctx = clnt->xa_private; 207 208 if (clnt->heap_preserved) 209 ndr_clnt_free_heap(clnt); 210 else 211 ndr_heap_destroy(clnt->heap); 212 213 (void) smb_fh_close(clnt->xa_fd); 214 smbrdr_ctx_free(ctx); 215 free(clnt); 216 bzero(handle, sizeof (mlsvc_handle_t)); 217 } 218 219 /* 220 * Call the RPC function identified by opnum. The remote service is 221 * identified by the handle, which should have been initialized by 222 * ndr_rpc_bind. 223 * 224 * If the RPC call is successful (returns 0), the caller must call 225 * ndr_rpc_release to release the heap. Otherwise, we release the 226 * heap here. 227 */ 228 int 229 ndr_rpc_call(mlsvc_handle_t *handle, int opnum, void *params) 230 { 231 ndr_client_t *clnt = handle->clnt; 232 int rc; 233 234 if (ndr_rpc_get_heap(handle) == NULL) 235 return (-1); 236 237 rc = ndr_clnt_call(clnt->binding, opnum, params); 238 239 /* 240 * Always clear the nonull flag to ensure 241 * it is not applied to subsequent calls. 242 */ 243 clnt->nonull = B_FALSE; 244 245 if (NDR_DRC_IS_FAULT(rc)) { 246 ndr_rpc_release(handle); 247 return (-1); 248 } 249 250 return (0); 251 } 252 253 /* 254 * Outgoing strings should not be null terminated. 255 */ 256 void 257 ndr_rpc_set_nonull(mlsvc_handle_t *handle) 258 { 259 handle->clnt->nonull = B_TRUE; 260 } 261 262 /* 263 * Return a reference to the server info. 264 */ 265 const srvsvc_server_info_t * 266 ndr_rpc_server_info(mlsvc_handle_t *handle) 267 { 268 return (&handle->svinfo); 269 } 270 271 /* 272 * Return the RPC server OS level. 273 */ 274 uint32_t 275 ndr_rpc_server_os(mlsvc_handle_t *handle) 276 { 277 return (handle->svinfo.sv_os); 278 } 279 280 /* 281 * Get the session key from a bound RPC client handle. 282 * 283 * The key returned is the 16-byte "user session key" 284 * established by the underlying authentication protocol 285 * (either Kerberos or NTLM). This key is needed for 286 * SAM RPC calls such as SamrSetInformationUser, etc. 287 * See [MS-SAMR] sections: 2.2.3.3, 2.2.7.21, 2.2.7.25. 288 * 289 * Returns zero (success) or an errno. 290 */ 291 int 292 ndr_rpc_get_ssnkey(mlsvc_handle_t *handle, 293 unsigned char *ssn_key, size_t len) 294 { 295 ndr_client_t *clnt = handle->clnt; 296 int rc; 297 298 if (clnt == NULL) 299 return (EINVAL); 300 301 rc = smb_fh_getssnkey(clnt->xa_fd, ssn_key, len); 302 return (rc); 303 } 304 305 void * 306 ndr_rpc_malloc(mlsvc_handle_t *handle, size_t size) 307 { 308 ndr_heap_t *heap; 309 310 if ((heap = ndr_rpc_get_heap(handle)) == NULL) 311 return (NULL); 312 313 return (ndr_heap_malloc(heap, size)); 314 } 315 316 ndr_heap_t * 317 ndr_rpc_get_heap(mlsvc_handle_t *handle) 318 { 319 ndr_client_t *clnt = handle->clnt; 320 321 if (clnt->heap == NULL) 322 clnt->heap = ndr_heap_create(); 323 324 return (clnt->heap); 325 } 326 327 /* 328 * Must be called by RPC clients to free the heap after a successful RPC 329 * call, i.e. ndr_rpc_call returned 0. The caller should take a copy 330 * of any data returned by the RPC prior to calling this function because 331 * returned data is in the heap. 332 */ 333 void 334 ndr_rpc_release(mlsvc_handle_t *handle) 335 { 336 ndr_client_t *clnt = handle->clnt; 337 338 if (clnt->heap_preserved) 339 ndr_clnt_free_heap(clnt); 340 else 341 ndr_heap_destroy(clnt->heap); 342 343 clnt->heap = NULL; 344 } 345 346 /* 347 * Returns true if the handle is null. 348 * Otherwise returns false. 349 */ 350 boolean_t 351 ndr_is_null_handle(mlsvc_handle_t *handle) 352 { 353 static ndr_hdid_t zero_handle; 354 355 if (handle == NULL || handle->clnt == NULL) 356 return (B_TRUE); 357 358 if (!memcmp(&handle->handle, &zero_handle, sizeof (ndr_hdid_t))) 359 return (B_TRUE); 360 361 return (B_FALSE); 362 } 363 364 /* 365 * Returns true if the handle is the top level bind handle. 366 * Otherwise returns false. 367 */ 368 boolean_t 369 ndr_is_bind_handle(mlsvc_handle_t *handle) 370 { 371 return (handle->clnt->handle == &handle->handle); 372 } 373 374 /* 375 * Pass the client reference from parent to child. 376 */ 377 void 378 ndr_inherit_handle(mlsvc_handle_t *child, mlsvc_handle_t *parent) 379 { 380 child->clnt = parent->clnt; 381 bcopy(&parent->svinfo, &child->svinfo, sizeof (srvsvc_server_info_t)); 382 } 383 384 void 385 ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status) 386 { 387 ndr_service_t *svc; 388 char *name = "NDR RPC"; 389 char *s = "unknown"; 390 391 switch (NT_SC_SEVERITY(status)) { 392 case NT_STATUS_SEVERITY_SUCCESS: 393 s = "success"; 394 break; 395 case NT_STATUS_SEVERITY_INFORMATIONAL: 396 s = "info"; 397 break; 398 case NT_STATUS_SEVERITY_WARNING: 399 s = "warning"; 400 break; 401 case NT_STATUS_SEVERITY_ERROR: 402 s = "error"; 403 break; 404 } 405 406 if (handle) { 407 svc = handle->clnt->binding->service; 408 name = svc->name; 409 } 410 411 smb_tracef("%s[0x%02x]: %s: %s (0x%08x)", 412 name, opnum, s, xlate_nt_status(status), status); 413 } 414 415 /* 416 * The following functions provide the client callback interface. 417 * If the caller hasn't provided a heap, create one here. 418 */ 419 static int 420 ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa) 421 { 422 ndr_stream_t *recv_nds = &mxa->recv_nds; 423 ndr_stream_t *send_nds = &mxa->send_nds; 424 ndr_heap_t *heap = clnt->heap; 425 int rc; 426 427 if (heap == NULL) { 428 if ((heap = ndr_heap_create()) == NULL) 429 return (-1); 430 431 clnt->heap = heap; 432 } 433 434 mxa->heap = heap; 435 436 rc = nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap); 437 if (rc == 0) 438 rc = nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT, 439 NDR_MODE_RETURN_RECV, heap); 440 441 if (rc != 0) { 442 nds_destruct(&mxa->recv_nds); 443 nds_destruct(&mxa->send_nds); 444 ndr_heap_destroy(mxa->heap); 445 mxa->heap = NULL; 446 clnt->heap = NULL; 447 return (-1); 448 } 449 450 if (clnt->nonull) 451 NDS_SETF(send_nds, NDS_F_NONULL); 452 453 return (0); 454 } 455 456 /* 457 * This is the entry pointy for an RPC client call exchange with 458 * a server, which will result in an smbrdr SmbTransact request. 459 * 460 * SmbTransact should return the number of bytes received, which 461 * we record as the PDU size, or a negative error code. 462 */ 463 static int 464 ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa) 465 { 466 ndr_stream_t *recv_nds = &mxa->recv_nds; 467 ndr_stream_t *send_nds = &mxa->send_nds; 468 int err, more, nbytes; 469 470 nbytes = recv_nds->pdu_max_size; 471 err = smb_fh_xactnp(clnt->xa_fd, 472 send_nds->pdu_size, (char *)send_nds->pdu_base_offset, 473 &nbytes, (char *)recv_nds->pdu_base_offset, &more); 474 if (err) { 475 recv_nds->pdu_size = 0; 476 return (-1); 477 } 478 479 recv_nds->pdu_size = nbytes; 480 return (0); 481 } 482 483 /* 484 * This entry point will be invoked if the xa-exchange response contained 485 * only the first fragment of a multi-fragment response. The RPC client 486 * code will then make repeated xa-read requests to obtain the remaining 487 * fragments, which will result in smbrdr SmbReadX requests. 488 * 489 * SmbReadX should return the number of bytes received, in which case we 490 * expand the PDU size to include the received data, or a negative error 491 * code. 492 */ 493 static int 494 ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa) 495 { 496 ndr_stream_t *nds = &mxa->recv_nds; 497 int len; 498 int nbytes; 499 500 if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0) 501 return (-1); 502 503 nbytes = smb_fh_read(clnt->xa_fd, 0, len, 504 (char *)nds->pdu_base_offset + nds->pdu_size); 505 506 if (nbytes < 0) 507 return (-1); 508 509 nds->pdu_size += nbytes; 510 511 if (nds->pdu_size > nds->pdu_max_size) { 512 nds->pdu_size = nds->pdu_max_size; 513 return (-1); 514 } 515 516 return (nbytes); 517 } 518 519 /* 520 * Preserve the heap so that the client application has access to data 521 * returned from the server after an RPC call. 522 */ 523 static void 524 ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa) 525 { 526 assert(clnt->heap == mxa->heap); 527 528 clnt->heap_preserved = B_TRUE; 529 mxa->heap = NULL; 530 } 531 532 /* 533 * Dispose of the transaction streams. If the heap has not been 534 * preserved, we can destroy it here. 535 */ 536 static void 537 ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa) 538 { 539 nds_destruct(&mxa->recv_nds); 540 nds_destruct(&mxa->send_nds); 541 542 if (!clnt->heap_preserved) { 543 ndr_heap_destroy(mxa->heap); 544 mxa->heap = NULL; 545 clnt->heap = NULL; 546 } 547 } 548 549 /* 550 * Dispose of a preserved heap. 551 */ 552 static void 553 ndr_xa_release(ndr_client_t *clnt) 554 { 555 if (clnt->heap_preserved) { 556 ndr_heap_destroy(clnt->heap); 557 clnt->heap = NULL; 558 clnt->heap_preserved = B_FALSE; 559 } 560 } 561 562 563 /* 564 * Compare the time here with the remote time on the server 565 * and report clock skew. 566 */ 567 void 568 ndr_srvsvc_timecheck(char *server, char *domain) 569 { 570 char hostname[MAXHOSTNAMELEN]; 571 struct timeval dc_tv; 572 struct tm dc_tm; 573 struct tm *tm; 574 time_t tnow; 575 time_t tdiff; 576 int priority; 577 578 if (srvsvc_net_remote_tod(server, domain, &dc_tv, &dc_tm) < 0) { 579 syslog(LOG_DEBUG, "srvsvc_net_remote_tod failed"); 580 return; 581 } 582 583 tnow = time(NULL); 584 585 if (tnow > dc_tv.tv_sec) 586 tdiff = (tnow - dc_tv.tv_sec) / SECSPERMIN; 587 else 588 tdiff = (dc_tv.tv_sec - tnow) / SECSPERMIN; 589 590 if (tdiff != 0) { 591 (void) strlcpy(hostname, "localhost", MAXHOSTNAMELEN); 592 (void) gethostname(hostname, MAXHOSTNAMELEN); 593 594 priority = (tdiff > 2) ? LOG_NOTICE : LOG_DEBUG; 595 syslog(priority, "DC [%s] clock skew detected: %u minutes", 596 server, tdiff); 597 598 tm = gmtime(&dc_tv.tv_sec); 599 syslog(priority, "%-8s UTC: %s", server, asctime(tm)); 600 tm = gmtime(&tnow); 601 syslog(priority, "%-8s UTC: %s", hostname, asctime(tm)); 602 } 603 } 604