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