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