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