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 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 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 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 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 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 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 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 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 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 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 }