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