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
srvsvc_open(char * server,char * domain,char * username,mlsvc_handle_t * handle)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
srvsvc_close(mlsvc_handle_t * handle)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
srvsvc_net_share_get_info(char * server,char * domain,char * netname)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
srvsvc_net_session_enum(char * server,char * domain,char * netname)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
srvsvc_net_connect_enum(char * server,char * domain,char * netname,int level)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
srvsvc_net_server_getinfo(char * server,char * domain,srvsvc_server_info_t * svinfo)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
srvsvc_timesync(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
srvsvc_gettime(unsigned long * t)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
srvsvc_net_remote_tod(char * server,char * domain,struct timeval * tv,struct tm * tm)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
srvsvc_net_test(char * server,char * domain,char * netname)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