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