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/errno.h> 31 #include <strings.h> 32 #include <assert.h> 33 #include <smbsrv/libsmb.h> 34 #include <smbsrv/libsmbrdr.h> 35 #include <smbsrv/libmlrpc.h> 36 #include <smbsrv/libmlsvc.h> 37 38 static int ndr_xa_init(ndr_client_t *, ndr_xa_t *); 39 static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *); 40 static int ndr_xa_read(ndr_client_t *, ndr_xa_t *); 41 static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *); 42 static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *); 43 static void ndr_xa_release(ndr_client_t *); 44 45 /* 46 * This call must be made to initialize an RPC client structure and bind 47 * to the remote service before any RPCs can be exchanged with that service. 48 * 49 * The mlsvc_handle_t is a wrapper that is used to associate an RPC handle 50 * with the client context for an instance of the interface. The handle 51 * is zeroed to ensure that it doesn't look like a valid handle - 52 * handle content is provided by the remove service. 53 * 54 * The client points to this top-level handle so that we know when to 55 * unbind and teardown the connection. As each handle is initialized it 56 * will inherit a reference to the client context. 57 */ 58 int 59 ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, 60 char *username, const char *service) 61 { 62 ndr_client_t *clnt; 63 ndr_service_t *svc; 64 smbrdr_session_info_t si; 65 int fid; 66 int rc; 67 68 if (handle == NULL || server == NULL || 69 domain == NULL || username == NULL) 70 return (-1); 71 72 if ((svc = ndr_svc_lookup_name(service)) == NULL) 73 return (-1); 74 75 if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) 76 return (-1); 77 78 fid = smbrdr_open_pipe(server, domain, username, svc->endpoint); 79 if (fid < 0) { 80 free(clnt); 81 return (-1); 82 } 83 84 bzero(clnt, sizeof (ndr_client_t)); 85 clnt->handle = &handle->handle; 86 clnt->fid = fid; 87 88 ndr_svc_binding_pool_init(&clnt->binding_list, 89 clnt->binding_pool, NDR_N_BINDING_POOL); 90 91 clnt->xa_init = ndr_xa_init; 92 clnt->xa_exchange = ndr_xa_exchange; 93 clnt->xa_read = ndr_xa_read; 94 clnt->xa_preserve = ndr_xa_preserve; 95 clnt->xa_destruct = ndr_xa_destruct; 96 clnt->xa_release = ndr_xa_release; 97 98 (void) smbrdr_session_info(fid, &si); 99 bzero(&handle->handle, sizeof (ndr_hdid_t)); 100 handle->clnt = clnt; 101 handle->remote_os = si.si_server_os; 102 103 if (ndr_rpc_get_heap(handle) == NULL) { 104 free(clnt); 105 return (-1); 106 } 107 108 rc = ndr_clnt_bind(clnt, service, &clnt->binding); 109 if (NDR_DRC_IS_FAULT(rc)) { 110 (void) smbrdr_close_pipe(fid); 111 ndr_heap_destroy(clnt->heap); 112 free(clnt); 113 handle->clnt = NULL; 114 return (-1); 115 } 116 117 return (0); 118 } 119 120 /* 121 * Unbind and close the pipe to an RPC service. 122 * 123 * If the heap has been preserved we need to go through an xa release. 124 * The heap is preserved during an RPC call because that's where data 125 * returned from the server is stored. 126 * 127 * Otherwise we destroy the heap directly. 128 */ 129 void 130 ndr_rpc_unbind(mlsvc_handle_t *handle) 131 { 132 ndr_client_t *clnt = handle->clnt; 133 134 if (clnt->heap_preserved) 135 ndr_clnt_free_heap(clnt); 136 else 137 ndr_heap_destroy(clnt->heap); 138 139 (void) smbrdr_close_pipe(clnt->fid); 140 free(handle->clnt); 141 bzero(handle, sizeof (mlsvc_handle_t)); 142 } 143 144 /* 145 * Call the RPC function identified by opnum. The remote service is 146 * identified by the handle, which should have been initialized by 147 * ndr_rpc_bind. 148 * 149 * If the RPC call is successful (returns 0), the caller must call 150 * ndr_rpc_release to release the heap. Otherwise, we release the 151 * heap here. 152 */ 153 int 154 ndr_rpc_call(mlsvc_handle_t *handle, int opnum, void *params) 155 { 156 ndr_client_t *clnt = handle->clnt; 157 int rc; 158 159 if (ndr_rpc_get_heap(handle) == NULL) 160 return (-1); 161 162 rc = ndr_clnt_call(clnt->binding, opnum, params); 163 164 if (NDR_DRC_IS_FAULT(rc)) { 165 ndr_rpc_release(handle); 166 return (-1); 167 } 168 169 return (0); 170 } 171 172 /* 173 * Set information about the remote RPC server in the handle. 174 */ 175 void 176 ndr_rpc_server_setinfo(mlsvc_handle_t *handle, 177 const srvsvc_server_info_t *svinfo) 178 { 179 bcopy(svinfo, &handle->svinfo, sizeof (srvsvc_server_info_t)); 180 handle->svinfo.sv_name = NULL; 181 handle->svinfo.sv_comment = NULL; 182 183 if (svinfo->sv_version_major > 4) 184 handle->remote_os = NATIVE_OS_WIN2000; 185 else 186 handle->remote_os = NATIVE_OS_WINNT; 187 188 smb_tracef("NdrRpcServerSetInfo: %s (version %d.%d)", 189 svinfo->sv_name ? svinfo->sv_name : "<unknown>", 190 svinfo->sv_version_major, svinfo->sv_version_minor); 191 } 192 193 /* 194 * Get information about the remote RPC server from the handle. 195 */ 196 void 197 ndr_rpc_server_getinfo(mlsvc_handle_t *handle, srvsvc_server_info_t *svinfo) 198 { 199 bcopy(&handle->svinfo, svinfo, sizeof (srvsvc_server_info_t)); 200 } 201 202 /* 203 * Returns the Native-OS of the RPC server. 204 */ 205 int 206 ndr_rpc_server_os(mlsvc_handle_t *handle) 207 { 208 return (handle->remote_os); 209 } 210 211 /* 212 * Get the session key from a bound RPC client handle. 213 * 214 * The key returned is the 16-byte "user session key" 215 * established by the underlying authentication protocol 216 * (either Kerberos or NTLM). This key is needed for 217 * SAM RPC calls such as SamrSetInformationUser, etc. 218 * See [MS-SAMR] sections: 2.2.3.3, 2.2.7.21, 2.2.7.25. 219 * 220 * Returns zero (success) or an errno. 221 */ 222 int 223 ndr_rpc_get_ssnkey(mlsvc_handle_t *handle, 224 unsigned char *ssn_key, size_t len) 225 { 226 ndr_client_t *clnt = handle->clnt; 227 int rc; 228 229 if (clnt == NULL) 230 return (EINVAL); 231 232 rc = smbrdr_get_ssnkey(clnt->fid, ssn_key, len); 233 return (rc); 234 } 235 236 void * 237 ndr_rpc_malloc(mlsvc_handle_t *handle, size_t size) 238 { 239 ndr_heap_t *heap; 240 241 if ((heap = ndr_rpc_get_heap(handle)) == NULL) 242 return (NULL); 243 244 return (ndr_heap_malloc(heap, size)); 245 } 246 247 ndr_heap_t * 248 ndr_rpc_get_heap(mlsvc_handle_t *handle) 249 { 250 ndr_client_t *clnt = handle->clnt; 251 252 if (clnt->heap == NULL) 253 clnt->heap = ndr_heap_create(); 254 255 return (clnt->heap); 256 } 257 258 /* 259 * Must be called by RPC clients to free the heap after a successful RPC 260 * call, i.e. ndr_rpc_call returned 0. The caller should take a copy 261 * of any data returned by the RPC prior to calling this function because 262 * returned data is in the heap. 263 */ 264 void 265 ndr_rpc_release(mlsvc_handle_t *handle) 266 { 267 ndr_client_t *clnt = handle->clnt; 268 269 if (clnt->heap_preserved) 270 ndr_clnt_free_heap(clnt); 271 else 272 ndr_heap_destroy(clnt->heap); 273 274 clnt->heap = NULL; 275 } 276 277 /* 278 * Returns true if the handle is null. 279 * Otherwise returns false. 280 */ 281 boolean_t 282 ndr_is_null_handle(mlsvc_handle_t *handle) 283 { 284 static ndr_hdid_t zero_handle; 285 286 if (handle == NULL || handle->clnt == NULL) 287 return (B_TRUE); 288 289 if (!memcmp(&handle->handle, &zero_handle, sizeof (ndr_hdid_t))) 290 return (B_TRUE); 291 292 return (B_FALSE); 293 } 294 295 /* 296 * Returns true if the handle is the top level bind handle. 297 * Otherwise returns false. 298 */ 299 boolean_t 300 ndr_is_bind_handle(mlsvc_handle_t *handle) 301 { 302 return (handle->clnt->handle == &handle->handle); 303 } 304 305 /* 306 * Pass the client reference from parent to child. 307 */ 308 void 309 ndr_inherit_handle(mlsvc_handle_t *child, mlsvc_handle_t *parent) 310 { 311 child->clnt = parent->clnt; 312 child->remote_os = parent->remote_os; 313 } 314 315 void 316 ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status) 317 { 318 ndr_service_t *svc; 319 char *name = "NDR RPC"; 320 char *s = "unknown"; 321 322 if (status == 0) 323 s = "success"; 324 else if (NT_SC_IS_ERROR(status)) 325 s = "error"; 326 else if (NT_SC_IS_WARNING(status)) 327 s = "warning"; 328 else if (NT_SC_IS_INFO(status)) 329 s = "info"; 330 331 if (handle) { 332 svc = handle->clnt->binding->service; 333 name = svc->name; 334 } 335 336 smb_tracef("%s[0x%02x]: %s: %s (0x%08x)", 337 name, opnum, s, xlate_nt_status(status), status); 338 } 339 340 /* 341 * The following functions provide the client callback interface. 342 * If the caller hasn't provided a heap, create one here. 343 */ 344 static int 345 ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa) 346 { 347 ndr_stream_t *recv_nds = &mxa->recv_nds; 348 ndr_stream_t *send_nds = &mxa->send_nds; 349 ndr_heap_t *heap = clnt->heap; 350 351 if (heap == NULL) { 352 if ((heap = ndr_heap_create()) == NULL) 353 return (-1); 354 355 clnt->heap = heap; 356 } 357 358 mxa->heap = heap; 359 360 nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap); 361 nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT, 362 NDR_MODE_RETURN_RECV, heap); 363 return (0); 364 } 365 366 /* 367 * This is the entry pointy for an RPC client call exchange with 368 * a server, which will result in an smbrdr SmbTransact request. 369 * 370 * SmbTransact should return the number of bytes received, which 371 * we record as the PDU size, or a negative error code. 372 */ 373 static int 374 ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa) 375 { 376 ndr_stream_t *recv_nds = &mxa->recv_nds; 377 ndr_stream_t *send_nds = &mxa->send_nds; 378 int nbytes; 379 380 nbytes = smbrdr_transact(clnt->fid, 381 (char *)send_nds->pdu_base_offset, send_nds->pdu_size, 382 (char *)recv_nds->pdu_base_offset, recv_nds->pdu_max_size); 383 384 if (nbytes < 0) { 385 recv_nds->pdu_size = 0; 386 return (-1); 387 } 388 389 recv_nds->pdu_size = nbytes; 390 return (nbytes); 391 } 392 393 /* 394 * This entry point will be invoked if the xa-exchange response contained 395 * only the first fragment of a multi-fragment response. The RPC client 396 * code will then make repeated xa-read requests to obtain the remaining 397 * fragments, which will result in smbrdr SmbReadX requests. 398 * 399 * SmbReadX should return the number of bytes received, in which case we 400 * expand the PDU size to include the received data, or a negative error 401 * code. 402 */ 403 static int 404 ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa) 405 { 406 ndr_stream_t *nds = &mxa->recv_nds; 407 int len; 408 int nbytes; 409 410 if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0) 411 return (-1); 412 413 nbytes = smbrdr_readx(clnt->fid, 414 (char *)nds->pdu_base_offset + nds->pdu_size, len); 415 416 if (nbytes < 0) 417 return (-1); 418 419 nds->pdu_size += nbytes; 420 421 if (nds->pdu_size > nds->pdu_max_size) { 422 nds->pdu_size = nds->pdu_max_size; 423 return (-1); 424 } 425 426 return (nbytes); 427 } 428 429 /* 430 * Preserve the heap so that the client application has access to data 431 * returned from the server after an RPC call. 432 */ 433 static void 434 ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa) 435 { 436 assert(clnt->heap == mxa->heap); 437 438 clnt->heap_preserved = B_TRUE; 439 mxa->heap = NULL; 440 } 441 442 /* 443 * Dispose of the transaction streams. If the heap has not been 444 * preserved, we can destroy it here. 445 */ 446 static void 447 ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa) 448 { 449 nds_destruct(&mxa->recv_nds); 450 nds_destruct(&mxa->send_nds); 451 452 if (!clnt->heap_preserved) { 453 ndr_heap_destroy(mxa->heap); 454 mxa->heap = NULL; 455 clnt->heap = NULL; 456 } 457 } 458 459 /* 460 * Dispose of a preserved heap. 461 */ 462 static void 463 ndr_xa_release(ndr_client_t *clnt) 464 { 465 if (clnt->heap_preserved) { 466 ndr_heap_destroy(clnt->heap); 467 clnt->heap = NULL; 468 clnt->heap_preserved = B_FALSE; 469 } 470 } 471