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
ndr_rpc_bind(mlsvc_handle_t * handle,char * server,char * domain,char * username,const char * service)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
ndr_rpc_unbind(mlsvc_handle_t * handle)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
ndr_rpc_call(mlsvc_handle_t * handle,int opnum,void * params)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
ndr_rpc_set_nonull(mlsvc_handle_t * handle)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 *
ndr_rpc_server_info(mlsvc_handle_t * handle)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
ndr_rpc_server_os(mlsvc_handle_t * handle)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
ndr_rpc_get_ssnkey(mlsvc_handle_t * handle,unsigned char * ssn_key,size_t len)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 *
ndr_rpc_malloc(mlsvc_handle_t * handle,size_t size)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 *
ndr_rpc_get_heap(mlsvc_handle_t * handle)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
ndr_rpc_release(mlsvc_handle_t * handle)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
ndr_is_null_handle(mlsvc_handle_t * handle)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
ndr_is_bind_handle(mlsvc_handle_t * handle)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
ndr_inherit_handle(mlsvc_handle_t * child,mlsvc_handle_t * parent)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
ndr_rpc_status(mlsvc_handle_t * handle,int opnum,DWORD status)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
ndr_xa_init(ndr_client_t * clnt,ndr_xa_t * mxa)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
ndr_xa_exchange(ndr_client_t * clnt,ndr_xa_t * mxa)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
ndr_xa_read(ndr_client_t * clnt,ndr_xa_t * mxa)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
ndr_xa_preserve(ndr_client_t * clnt,ndr_xa_t * mxa)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
ndr_xa_destruct(ndr_client_t * clnt,ndr_xa_t * mxa)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
ndr_xa_release(ndr_client_t * clnt)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
ndr_srvsvc_timecheck(char * server,char * domain)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