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 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 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 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 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 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 * Windows 95+ and Windows NT4.0 both report the version as 4.0. 354 * Windows 2000+ reports the version as 5.x. 355 */ 356 int 357 srvsvc_net_server_getinfo(char *server, char *domain, 358 srvsvc_server_info_t *svinfo) 359 { 360 mlsvc_handle_t handle; 361 struct mslm_NetServerGetInfo arg; 362 struct mslm_SERVER_INFO_101 *sv101; 363 int len, opnum, rc; 364 char user[SMB_USERNAME_MAXLEN]; 365 366 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); 367 368 if (srvsvc_open(server, domain, user, &handle) != 0) 369 return (-1); 370 371 opnum = SRVSVC_OPNUM_NetServerGetInfo; 372 bzero(&arg, sizeof (arg)); 373 374 len = strlen(server) + 4; 375 arg.servername = ndr_rpc_malloc(&handle, len); 376 if (arg.servername == NULL) 377 return (-1); 378 379 (void) snprintf((char *)arg.servername, len, "\\\\%s", server); 380 arg.level = 101; 381 382 rc = ndr_rpc_call(&handle, opnum, &arg); 383 if ((rc != 0) || (arg.status != 0)) { 384 srvsvc_close(&handle); 385 return (-1); 386 } 387 388 sv101 = arg.result.bufptr.bufptr101; 389 390 bzero(svinfo, sizeof (srvsvc_server_info_t)); 391 svinfo->sv_platform_id = sv101->sv101_platform_id; 392 svinfo->sv_version_major = sv101->sv101_version_major; 393 svinfo->sv_version_minor = sv101->sv101_version_minor; 394 svinfo->sv_type = sv101->sv101_type; 395 if (sv101->sv101_name) 396 svinfo->sv_name = strdup((char *)sv101->sv101_name); 397 if (sv101->sv101_comment) 398 svinfo->sv_comment = strdup((char *)sv101->sv101_comment); 399 400 if (svinfo->sv_type & SV_TYPE_WFW) 401 svinfo->sv_os = NATIVE_OS_WIN95; 402 if (svinfo->sv_type & SV_TYPE_WINDOWS) 403 svinfo->sv_os = NATIVE_OS_WIN95; 404 if ((svinfo->sv_type & SV_TYPE_NT) || 405 (svinfo->sv_type & SV_TYPE_SERVER_NT)) 406 svinfo->sv_os = NATIVE_OS_WINNT; 407 if (svinfo->sv_version_major > 4) 408 svinfo->sv_os = NATIVE_OS_WIN2000; 409 410 srvsvc_close(&handle); 411 return (0); 412 } 413 414 /* 415 * Compare the time here with the remote time on the server 416 * and report clock skew. 417 */ 418 void 419 srvsvc_timecheck(char *server, char *domain) 420 { 421 char hostname[MAXHOSTNAMELEN]; 422 struct timeval dc_tv; 423 struct tm dc_tm; 424 struct tm *tm; 425 time_t tnow; 426 time_t tdiff; 427 int priority; 428 429 if (srvsvc_net_remote_tod(server, domain, &dc_tv, &dc_tm) < 0) { 430 syslog(LOG_DEBUG, "srvsvc_net_remote_tod failed"); 431 return; 432 } 433 434 tnow = time(NULL); 435 436 if (tnow > dc_tv.tv_sec) 437 tdiff = (tnow - dc_tv.tv_sec) / SECSPERMIN; 438 else 439 tdiff = (dc_tv.tv_sec - tnow) / SECSPERMIN; 440 441 if (tdiff != 0) { 442 (void) strlcpy(hostname, "localhost", MAXHOSTNAMELEN); 443 (void) gethostname(hostname, MAXHOSTNAMELEN); 444 445 priority = (tdiff > 2) ? LOG_NOTICE : LOG_DEBUG; 446 syslog(priority, "DC [%s] clock skew detected: %u minutes", 447 server, tdiff); 448 449 tm = gmtime(&dc_tv.tv_sec); 450 syslog(priority, "%-8s UTC: %s", server, asctime(tm)); 451 tm = gmtime(&tnow); 452 syslog(priority, "%-8s UTC: %s", hostname, asctime(tm)); 453 } 454 } 455 456 /* 457 * Synchronize the local system clock with the domain controller. 458 */ 459 void 460 srvsvc_timesync(void) 461 { 462 smb_domainex_t di; 463 struct timeval tv; 464 struct tm tm; 465 time_t tsecs; 466 467 if (!smb_domain_getinfo(&di)) 468 return; 469 470 if (srvsvc_net_remote_tod(di.d_dci.dc_name, di.d_primary.di_nbname, 471 &tv, &tm) != 0) 472 return; 473 474 if (settimeofday(&tv, 0)) 475 smb_tracef("unable to set system time"); 476 477 tsecs = time(0); 478 (void) localtime_r(&tsecs, &tm); 479 smb_tracef("SrvsvcTimeSync %s", ctime((time_t *)&tv.tv_sec)); 480 } 481 482 /* 483 * NetRemoteTOD to get the current GMT time from a Windows NT server. 484 */ 485 int 486 srvsvc_gettime(unsigned long *t) 487 { 488 smb_domainex_t di; 489 struct timeval tv; 490 struct tm tm; 491 492 if (!smb_domain_getinfo(&di)) 493 return (-1); 494 495 if (srvsvc_net_remote_tod(di.d_dci.dc_name, di.d_primary.di_nbname, 496 &tv, &tm) != 0) 497 return (-1); 498 499 *t = tv.tv_sec; 500 return (0); 501 } 502 503 /* 504 * This is a client side routine for NetRemoteTOD, which gets the time 505 * and date from a remote system. The time information is returned in 506 * the timeval and tm. 507 * 508 * typedef struct _TIME_OF_DAY_INFO { 509 * DWORD tod_elapsedt; // seconds since 00:00:00 January 1 1970 GMT 510 * DWORD tod_msecs; // arbitrary milliseconds (since reset) 511 * DWORD tod_hours; // current hour [0-23] 512 * DWORD tod_mins; // current minute [0-59] 513 * DWORD tod_secs; // current second [0-59] 514 * DWORD tod_hunds; // current hundredth (0.01) second [0-99] 515 * LONG tod_timezone; // time zone of the server 516 * DWORD tod_tinterval; // clock tick time interval 517 * DWORD tod_day; // day of the month [1-31] 518 * DWORD tod_month; // month of the year [1-12] 519 * DWORD tod_year; // current year 520 * DWORD tod_weekday; // day of the week since sunday [0-6] 521 * } TIME_OF_DAY_INFO; 522 * 523 * The time zone of the server is calculated in minutes from Greenwich 524 * Mean Time (GMT). For time zones west of Greenwich, the value is 525 * positive; for time zones east of Greenwich, the value is negative. 526 * A value of -1 indicates that the time zone is undefined. 527 * 528 * The clock tick value represents a resolution of one ten-thousandth 529 * (0.0001) second. 530 */ 531 int 532 srvsvc_net_remote_tod(char *server, char *domain, struct timeval *tv, 533 struct tm *tm) 534 { 535 struct mslm_NetRemoteTOD arg; 536 struct mslm_TIME_OF_DAY_INFO *tod; 537 mlsvc_handle_t handle; 538 int rc; 539 int opnum; 540 int len; 541 char user[SMB_USERNAME_MAXLEN]; 542 543 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); 544 545 rc = srvsvc_open(server, domain, user, &handle); 546 if (rc != 0) 547 return (-1); 548 549 opnum = SRVSVC_OPNUM_NetRemoteTOD; 550 bzero(&arg, sizeof (struct mslm_NetRemoteTOD)); 551 552 len = strlen(server) + 4; 553 arg.servername = ndr_rpc_malloc(&handle, len); 554 if (arg.servername == NULL) { 555 srvsvc_close(&handle); 556 return (-1); 557 } 558 559 (void) snprintf((char *)arg.servername, len, "\\\\%s", server); 560 561 rc = ndr_rpc_call(&handle, opnum, &arg); 562 if ((rc != 0) || (arg.status != 0)) { 563 srvsvc_close(&handle); 564 return (-1); 565 } 566 567 /* 568 * We're assigning milliseconds to microseconds 569 * here but the value's not really relevant. 570 */ 571 tod = arg.bufptr; 572 573 if (tv) { 574 tv->tv_sec = tod->tod_elapsedt; 575 tv->tv_usec = tod->tod_msecs; 576 } 577 578 if (tm) { 579 tm->tm_sec = tod->tod_secs; 580 tm->tm_min = tod->tod_mins; 581 tm->tm_hour = tod->tod_hours; 582 tm->tm_mday = tod->tod_day; 583 tm->tm_mon = tod->tod_month - 1; 584 tm->tm_year = tod->tod_year - 1900; 585 tm->tm_wday = tod->tod_weekday; 586 } 587 588 srvsvc_close(&handle); 589 return (0); 590 } 591 592 void 593 srvsvc_net_test(char *server, char *domain, char *netname) 594 { 595 smb_domainex_t di; 596 srvsvc_server_info_t svinfo; 597 598 (void) smb_tracef("%s %s %s", server, domain, netname); 599 600 if (smb_domain_getinfo(&di)) { 601 server = di.d_dci.dc_name; 602 domain = di.d_primary.di_nbname; 603 } 604 605 if (srvsvc_net_server_getinfo(server, domain, &svinfo) == 0) { 606 smb_tracef("NetServerGetInfo: %s %s (%d.%d) id=%d type=0x%08x", 607 svinfo.sv_name ? svinfo.sv_name : "NULL", 608 svinfo.sv_comment ? svinfo.sv_comment : "NULL", 609 svinfo.sv_version_major, svinfo.sv_version_minor, 610 svinfo.sv_platform_id, svinfo.sv_type); 611 612 free(svinfo.sv_name); 613 free(svinfo.sv_comment); 614 } 615 616 (void) srvsvc_net_share_get_info(server, domain, netname); 617 #if 0 618 /* 619 * The NetSessionEnum server-side definition was updated. 620 * Disabled until the client-side has been updated. 621 */ 622 (void) srvsvc_net_session_enum(server, domain, netname); 623 #endif 624 (void) srvsvc_net_connect_enum(server, domain, netname, 0); 625 (void) srvsvc_net_connect_enum(server, domain, netname, 1); 626 }