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