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