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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Server Service (srvsvc) client side RPC library interface. The
30  * srvsvc interface allows a client to query a server for information
31  * on shares, sessions, connections and files on the server. Some
32  * functions are available via anonymous IPC while others require
33  * administrator privilege. Also, some functions return NT status
34  * values while others return Win32 errors codes.
35  */
36 
37 #include <sys/errno.h>
38 #include <stdio.h>
39 #include <time.h>
40 #include <strings.h>
41 #include <time.h>
42 
43 #include <smbsrv/libsmb.h>
44 #include <smbsrv/libsmbrdr.h>
45 #include <smbsrv/smbinfo.h>
46 #include <smbsrv/ntstatus.h>
47 #include <smbsrv/ndl/srvsvc.ndl>
48 #include <smbsrv/mlsvc_util.h>
49 
50 /*
51  * Information level for NetShareGetInfo.
52  */
53 DWORD srvsvc_info_level = 1;
54 
55 static int srvsvc_net_remote_tod(char *, char *, struct timeval *, struct tm *);
56 
57 /*
58  * Ensure that an appropriate session and logon exists for the srvsvc
59  * client calls. Open and bind the RPC interface.
60  *
61  * On success 0 is returned. Otherwise a -ve error code.
62  */
63 int
64 srvsvc_open(int ipc_mode, char *server, char *domain, char *username,
65     char *password, mlsvc_handle_t *handle, mlrpc_heapref_t *heapref)
66 {
67 	smb_ntdomain_t *di;
68 	int fid;
69 	int rc;
70 
71 	if ((di = smb_getdomaininfo(0)) == NULL)
72 		return (-1);
73 
74 	if (server == NULL || domain == NULL) {
75 		server = di->server;
76 		domain = di->domain;
77 	}
78 
79 	switch (ipc_mode) {
80 	case MLSVC_IPC_USER:
81 		/*
82 		 * Use the supplied credentials.
83 		 */
84 		rc = mlsvc_user_logon(server, domain, username, password);
85 		break;
86 
87 	case MLSVC_IPC_ADMIN:
88 		/*
89 		 * Use the resource domain administrator credentials.
90 		 */
91 		server = di->server;
92 		domain = di->domain;
93 		username = smbrdr_ipc_get_user();
94 
95 		rc = mlsvc_admin_logon(server, domain);
96 		break;
97 
98 	case MLSVC_IPC_ANON:
99 	default:
100 		rc = mlsvc_anonymous_logon(server, domain, &username);
101 		break;
102 	}
103 
104 	if (rc != 0)
105 		return (-1);
106 
107 	fid = mlsvc_open_pipe(server, domain, username, "\\srvsvc");
108 	if (fid < 0)
109 		return (-1);
110 
111 	if ((rc = mlsvc_rpc_bind(handle, fid, "SRVSVC")) < 0) {
112 		(void) mlsvc_close_pipe(fid);
113 		return (rc);
114 	}
115 
116 	rc = mlsvc_rpc_init(heapref);
117 	return (rc);
118 }
119 
120 /*
121  * Close the srvsvc pipe and free the associated context. This function
122  * should only be called if the open was successful.
123  */
124 void
125 srvsvc_close(mlsvc_handle_t *handle, mlrpc_heapref_t *heapref)
126 {
127 	mlsvc_rpc_free(handle->context, heapref);
128 	(void) mlsvc_close_pipe(handle->context->fid);
129 	free(handle->context);
130 }
131 
132 /*
133  * This is a client side routine for NetShareGetInfo.
134  * Levels 0 and 1 work with an anonymous connection but
135  * level 2 requires administrator access.
136  */
137 int
138 srvsvc_net_share_get_info(char *server, char *domain, char *netname)
139 {
140 	struct mlsm_NetShareGetInfo arg;
141 	mlsvc_handle_t handle;
142 	mlrpc_heapref_t heap;
143 	int rc;
144 	int opnum;
145 	struct mslm_NetShareGetInfo0 *info0;
146 	struct mslm_NetShareGetInfo1 *info1;
147 	struct mslm_NetShareGetInfo2 *info2;
148 	int ipc_mode;
149 	int len;
150 
151 	if (netname == NULL)
152 		return (-1);
153 
154 	if (srvsvc_info_level == 2)
155 		ipc_mode = MLSVC_IPC_ADMIN;
156 	else
157 		ipc_mode = MLSVC_IPC_ANON;
158 
159 	rc = srvsvc_open(ipc_mode, server, domain, 0, 0, &handle, &heap);
160 	if (rc != 0)
161 		return (-1);
162 
163 	opnum = SRVSVC_OPNUM_NetShareGetInfo;
164 	bzero(&arg, sizeof (struct mlsm_NetShareGetInfo));
165 
166 	len = strlen(server) + 4;
167 	arg.servername = mlrpc_heap_malloc(heap.heap, len);
168 	if (arg.servername == NULL) {
169 		srvsvc_close(&handle, &heap);
170 		return (-1);
171 	}
172 
173 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
174 	arg.netname = (LPTSTR)netname;
175 	arg.level = srvsvc_info_level; /* share information level */
176 
177 	rc = mlsvc_rpc_call(handle.context, opnum, &arg, &heap);
178 	if ((rc != 0) || (arg.status != 0)) {
179 		srvsvc_close(&handle, &heap);
180 		return (-1);
181 	}
182 
183 	switch (arg.result.switch_value) {
184 	case 0:
185 		info0 = arg.result.ru.info0;
186 		smb_tracef("srvsvc shi0_netname=%s", info0->shi0_netname);
187 		break;
188 
189 	case 1:
190 		info1 = arg.result.ru.info1;
191 		smb_tracef("srvsvc shi1_netname=%s", info1->shi1_netname);
192 		smb_tracef("srvsvc shi1_type=%u", info1->shi1_type);
193 
194 		if (info1->shi1_comment)
195 			smb_tracef("srvsvc shi1_comment=%s",
196 			    info1->shi1_comment);
197 		break;
198 
199 	case 2:
200 		info2 = arg.result.ru.info2;
201 		smb_tracef("srvsvc shi2_netname=%s", info2->shi2_netname);
202 		smb_tracef("srvsvc shi2_type=%u", info2->shi2_type);
203 
204 		if (info2->shi2_comment)
205 			smb_tracef("srvsvc shi2_comment=%s",
206 			    info2->shi2_comment);
207 
208 		smb_tracef("srvsvc shi2_perms=%d", info2->shi2_permissions);
209 		smb_tracef("srvsvc shi2_max_use=%d", info2->shi2_max_uses);
210 		smb_tracef("srvsvc shi2_cur_use=%d", info2->shi2_current_uses);
211 
212 		if (info2->shi2_path)
213 			smb_tracef("srvsvc shi2_path=%s", info2->shi2_path);
214 
215 		if (info2->shi2_passwd)
216 			smb_tracef("srvsvc shi2_passwd=%s", info2->shi2_passwd);
217 		break;
218 
219 	default:
220 		smb_tracef("srvsvc: unknown level");
221 		break;
222 	}
223 
224 	srvsvc_close(&handle, &heap);
225 	return (0);
226 }
227 
228 /*
229  * This is a client side routine for NetSessionEnum.
230  * NetSessionEnum requires administrator rights.
231  */
232 int
233 srvsvc_net_session_enum(char *server, char *domain, char *netname)
234 {
235 	struct mslm_NetSessionEnum arg;
236 	mlsvc_handle_t handle;
237 	mlrpc_heapref_t heap;
238 	int rc;
239 	int opnum;
240 	struct mslm_infonres infonres;
241 	struct mslm_SESSION_INFO_1 *nsi1;
242 	int len;
243 
244 	if (netname == NULL)
245 		return (-1);
246 
247 	rc = srvsvc_open(MLSVC_IPC_ADMIN, server, domain, 0, 0, &handle, &heap);
248 	if (rc != 0)
249 		return (-1);
250 
251 	opnum = SRVSVC_OPNUM_NetSessionEnum;
252 	bzero(&arg, sizeof (struct mslm_NetSessionEnum));
253 
254 	len = strlen(server) + 4;
255 	arg.servername = mlrpc_heap_malloc(heap.heap, len);
256 	if (arg.servername == NULL) {
257 		srvsvc_close(&handle, &heap);
258 		return (-1);
259 	}
260 
261 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
262 	infonres.entriesread = 0;
263 	infonres.entries = 0;
264 	arg.level = 1;
265 	arg.result.level = 1;
266 	arg.result.bufptr.p = &infonres;
267 	arg.resume_handle = 0;
268 	arg.pref_max_len = 0xFFFFFFFF;
269 
270 	rc = mlsvc_rpc_call(handle.context, opnum, &arg, &heap);
271 	if ((rc != 0) || (arg.status != 0)) {
272 		srvsvc_close(&handle, &heap);
273 		return (-1);
274 	}
275 
276 	/* Only the first session info is dereferenced. */
277 	nsi1 = ((struct mslm_infonres *)arg.result.bufptr.p)->entries;
278 
279 	smb_tracef("srvsvc switch_value=%d", arg.level);
280 	smb_tracef("srvsvc sesi1_cname=%s", nsi1->sesi1_cname);
281 	smb_tracef("srvsvc sesi1_uname=%s", nsi1->sesi1_uname);
282 	smb_tracef("srvsvc sesi1_nopens=%u", nsi1->sesi1_nopens);
283 	smb_tracef("srvsvc sesi1_time=%u", nsi1->sesi1_time);
284 	smb_tracef("srvsvc sesi1_itime=%u", nsi1->sesi1_itime);
285 	smb_tracef("srvsvc sesi1_uflags=%u", nsi1->sesi1_uflags);
286 
287 	srvsvc_close(&handle, &heap);
288 	return (0);
289 }
290 
291 /*
292  * This is a client side routine for NetConnectEnum.
293  * NetConnectEnum requires administrator rights.
294  * Level 0 and level 1 requests are supported.
295  */
296 int
297 srvsvc_net_connect_enum(char *server, char *domain, char *netname, int level)
298 {
299 	struct mslm_NetConnectEnum arg;
300 	mlsvc_handle_t handle;
301 	mlrpc_heapref_t heap;
302 	int rc;
303 	int opnum;
304 	struct mslm_NetConnectInfo1 info1;
305 	struct mslm_NetConnectInfo0 info0;
306 	struct mslm_NetConnectInfoBuf1 *cib1;
307 	int len;
308 
309 	if (netname == NULL)
310 		return (-1);
311 
312 	rc = srvsvc_open(MLSVC_IPC_ADMIN, server, domain, 0, 0, &handle, &heap);
313 	if (rc != 0)
314 		return (-1);
315 
316 	opnum = SRVSVC_OPNUM_NetConnectEnum;
317 	bzero(&arg, sizeof (struct mslm_NetConnectEnum));
318 
319 	len = strlen(server) + 4;
320 	arg.servername = mlrpc_heap_malloc(heap.heap, len);
321 	if (arg.servername == NULL) {
322 		srvsvc_close(&handle, &heap);
323 		return (-1);
324 	}
325 
326 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
327 	arg.qualifier = (LPTSTR)netname;
328 
329 	switch (level) {
330 	case 0:
331 		arg.info.level = 0;
332 		arg.info.switch_value = 0;
333 		arg.info.ru.info0 = &info0;
334 		info0.entries_read = 0;
335 		info0.ci0 = 0;
336 		break;
337 	case 1:
338 		arg.info.level = 1;
339 		arg.info.switch_value = 1;
340 		arg.info.ru.info1 = &info1;
341 		info1.entries_read = 0;
342 		info1.ci1 = 0;
343 		break;
344 	default:
345 		srvsvc_close(&handle, &heap);
346 		return (-1);
347 	}
348 
349 	arg.resume_handle = 0;
350 	arg.pref_max_len = 0xFFFFFFFF;
351 
352 	rc = mlsvc_rpc_call(handle.context, opnum, &arg, &heap);
353 	if ((rc != 0) || (arg.status != 0)) {
354 		srvsvc_close(&handle, &heap);
355 		return (-1);
356 	}
357 
358 	smb_tracef("srvsvc switch_value=%d", arg.info.switch_value);
359 
360 	switch (level) {
361 	case 0:
362 		if (arg.info.ru.info0 && arg.info.ru.info0->ci0) {
363 			smb_tracef("srvsvc coni0_id=%x",
364 			    arg.info.ru.info0->ci0->coni0_id);
365 		}
366 		break;
367 	case 1:
368 		if (arg.info.ru.info1 && arg.info.ru.info1->ci1) {
369 			cib1 = arg.info.ru.info1->ci1;
370 
371 			smb_tracef("srvsvc coni_uname=%s",
372 			    cib1->coni1_username ?
373 			    (char *)cib1->coni1_username : "(null)");
374 			smb_tracef("srvsvc coni1_netname=%s",
375 			    cib1->coni1_netname ?
376 			    (char *)cib1->coni1_netname : "(null)");
377 			smb_tracef("srvsvc coni1_nopens=%u",
378 			    cib1->coni1_num_opens);
379 			smb_tracef("srvsvc coni1_time=%u", cib1->coni1_time);
380 			smb_tracef("srvsvc coni1_num_users=%u",
381 			    cib1->coni1_num_users);
382 		}
383 		break;
384 
385 	default:
386 		smb_tracef("srvsvc: unknown level");
387 		break;
388 	}
389 
390 	srvsvc_close(&handle, &heap);
391 	return (0);
392 }
393 
394 /*
395  * Synchronize the local system clock with the domain controller.
396  */
397 void
398 srvsvc_timesync(void)
399 {
400 	smb_ntdomain_t *di;
401 	struct timeval tv;
402 	struct tm tm;
403 	time_t tsecs;
404 
405 	if ((di = smb_getdomaininfo(0)) == NULL)
406 		return;
407 
408 	if (srvsvc_net_remote_tod(di->server, di->domain, &tv, &tm) != 0)
409 		return;
410 
411 	if (settimeofday(&tv, 0))
412 		smb_tracef("unable to set system time");
413 
414 	tsecs = time(0);
415 	(void) localtime_r(&tsecs, &tm);
416 	smb_tracef("SrvsvcTimeSync %s", ctime((time_t *)&tv.tv_sec));
417 }
418 
419 /*
420  * NetRemoteTOD to get the current GMT time from a Windows NT server.
421  */
422 int
423 srvsvc_gettime(unsigned long *t)
424 {
425 	smb_ntdomain_t *di;
426 	struct timeval tv;
427 	struct tm tm;
428 
429 	if ((di = smb_getdomaininfo(0)) == NULL)
430 		return (-1);
431 
432 	if (srvsvc_net_remote_tod(di->server, di->domain, &tv, &tm) != 0)
433 		return (-1);
434 
435 	*t = tv.tv_sec;
436 	return (0);
437 }
438 
439 /*
440  * This is a client side routine for NetRemoteTOD, which gets the time
441  * and date from a remote system. The time information is returned in
442  * the timeval and tm.
443  *
444  * typedef struct _TIME_OF_DAY_INFO {
445  *	DWORD tod_elapsedt;  // seconds since 00:00:00 January 1 1970 GMT
446  *	DWORD tod_msecs;     // arbitrary milliseconds (since reset)
447  *	DWORD tod_hours;     // current hour [0-23]
448  *	DWORD tod_mins;      // current minute [0-59]
449  *	DWORD tod_secs;      // current second [0-59]
450  *	DWORD tod_hunds;     // current hundredth (0.01) second [0-99]
451  *	LONG tod_timezone;   // time zone of the server
452  *	DWORD tod_tinterval; // clock tick time interval
453  *	DWORD tod_day;       // day of the month [1-31]
454  *	DWORD tod_month;     // month of the year [1-12]
455  *	DWORD tod_year;      // current year
456  *	DWORD tod_weekday;   // day of the week since sunday [0-6]
457  * } TIME_OF_DAY_INFO;
458  *
459  * The time zone of the server is calculated in minutes from Greenwich
460  * Mean Time (GMT). For time zones west of Greenwich, the value is
461  * positive; for time zones east of Greenwich, the value is negative.
462  * A value of -1 indicates that the time zone is undefined.
463  *
464  * The clock tick value represents a resolution of one ten-thousandth
465  * (0.0001) second.
466  */
467 int
468 srvsvc_net_remote_tod(char *server, char *domain, struct timeval *tv,
469     struct tm *tm)
470 {
471 	char timebuf[64];
472 	struct mslm_NetRemoteTOD arg;
473 	struct mslm_TIME_OF_DAY_INFO *tod;
474 	mlsvc_handle_t handle;
475 	mlrpc_heapref_t heap;
476 	int rc;
477 	int opnum;
478 	int len;
479 
480 	rc = srvsvc_open(MLSVC_IPC_ANON, server, domain, 0, 0, &handle, &heap);
481 	if (rc != 0)
482 		return (-1);
483 
484 	opnum = SRVSVC_OPNUM_NetRemoteTOD;
485 	bzero(&arg, sizeof (struct mslm_NetRemoteTOD));
486 
487 	len = strlen(server) + 4;
488 	arg.servername = mlrpc_heap_malloc(heap.heap, len);
489 	if (arg.servername == NULL) {
490 		srvsvc_close(&handle, &heap);
491 		return (-1);
492 	}
493 
494 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
495 
496 	rc = mlsvc_rpc_call(handle.context, opnum, &arg, &heap);
497 	if ((rc != 0) || (arg.status != 0)) {
498 		srvsvc_close(&handle, &heap);
499 		return (-1);
500 	}
501 
502 	/*
503 	 * We're assigning milliseconds to microseconds
504 	 * here but the value's not really relevant.
505 	 */
506 	tod = arg.bufptr;
507 
508 	if (tv) {
509 		tv->tv_sec = tod->tod_elapsedt;
510 		tv->tv_usec = tod->tod_msecs;
511 		smb_tracef("RemoteTime: %s", ctime(&tv->tv_sec));
512 	}
513 
514 	if (tm) {
515 		tm->tm_sec = tod->tod_secs;
516 		tm->tm_min = tod->tod_mins;
517 		tm->tm_hour = tod->tod_hours;
518 		tm->tm_mday = tod->tod_day;
519 		tm->tm_mon = tod->tod_month - 1;
520 		tm->tm_year = tod->tod_year - 1900;
521 		tm->tm_wday = tod->tod_weekday;
522 
523 		(void) strftime(timebuf, sizeof (timebuf),
524 		    "NetRemoteTOD: %D %T", tm);
525 		smb_tracef("NetRemoteTOD: %s", timebuf);
526 	}
527 
528 	srvsvc_close(&handle, &heap);
529 	return (0);
530 }
531 
532 void
533 srvsvc_net_test(char *server, char *domain, char *netname)
534 {
535 	smb_ntdomain_t *di;
536 
537 	(void) smb_tracef("%s %s %s", server, domain, netname);
538 
539 	if ((di = smb_getdomaininfo(0)) != NULL) {
540 		server = di->server;
541 		domain = di->domain;
542 	}
543 
544 	(void) srvsvc_net_share_get_info(server, domain, netname);
545 #if 0
546 	/*
547 	 * The NetSessionEnum server-side definition was updated.
548 	 * Disabled until the client-side has been updated.
549 	 */
550 	(void) srvsvc_net_session_enum(server, domain, netname);
551 #endif
552 	(void) srvsvc_net_connect_enum(server, domain, netname, 0);
553 	(void) srvsvc_net_connect_enum(server, domain, netname, 1);
554 }
555