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