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/libsmbrdr.h> 43 #include <smbsrv/libmlsvc.h> 44 #include <smbsrv/smbinfo.h> 45 #include <smbsrv/ntstatus.h> 46 #include <smbsrv/ndl/srvsvc.ndl> 47 48 /* 49 * Information level for NetShareGetInfo. 50 */ 51 DWORD srvsvc_info_level = 1; 52 53 static int srvsvc_net_remote_tod(char *, char *, struct timeval *, struct tm *); 54 55 /* 56 * Bind to the the SRVSVC. 57 * 58 * If username argument is NULL, an anonymous connection will be established. 59 * Otherwise, an authenticated connection will be established. 60 */ 61 static int 62 srvsvc_open(char *server, char *domain, char *username, mlsvc_handle_t *handle) 63 { 64 smb_domain_t di; 65 66 if (server == NULL || domain == NULL) { 67 if (!smb_domain_getinfo(&di)) 68 return (-1); 69 70 server = di.d_dc; 71 domain = di.d_info.di_nbname; 72 } 73 74 if (username == NULL) 75 username = MLSVC_ANON_USER; 76 77 if (ndr_rpc_bind(handle, server, domain, username, "SRVSVC") < 0) 78 return (-1); 79 80 return (0); 81 } 82 83 /* 84 * Unbind the SRVSVC connection. 85 */ 86 static void 87 srvsvc_close(mlsvc_handle_t *handle) 88 { 89 ndr_rpc_unbind(handle); 90 } 91 92 /* 93 * This is a client side routine for NetShareGetInfo. 94 * Levels 0 and 1 work with an anonymous connection but 95 * level 2 requires administrator access. 96 */ 97 int 98 srvsvc_net_share_get_info(char *server, char *domain, char *netname) 99 { 100 struct mlsm_NetShareGetInfo arg; 101 mlsvc_handle_t handle; 102 int rc; 103 int opnum; 104 struct mslm_NetShareInfo_0 *info0; 105 struct mslm_NetShareInfo_1 *info1; 106 struct mslm_NetShareInfo_2 *info2; 107 int len; 108 char *user = NULL; 109 110 if (netname == NULL) 111 return (-1); 112 113 if (srvsvc_info_level == 2) 114 user = smbrdr_ipc_get_user(); 115 116 if (srvsvc_open(server, domain, user, &handle) != 0) 117 return (-1); 118 119 opnum = SRVSVC_OPNUM_NetShareGetInfo; 120 bzero(&arg, sizeof (struct mlsm_NetShareGetInfo)); 121 122 len = strlen(server) + 4; 123 arg.servername = ndr_rpc_malloc(&handle, len); 124 if (arg.servername == NULL) { 125 srvsvc_close(&handle); 126 return (-1); 127 } 128 129 (void) snprintf((char *)arg.servername, len, "\\\\%s", server); 130 arg.netname = (LPTSTR)netname; 131 arg.level = srvsvc_info_level; /* share information level */ 132 133 rc = ndr_rpc_call(&handle, opnum, &arg); 134 if ((rc != 0) || (arg.status != 0)) { 135 srvsvc_close(&handle); 136 return (-1); 137 } 138 139 switch (arg.result.switch_value) { 140 case 0: 141 info0 = arg.result.ru.info0; 142 smb_tracef("srvsvc shi0_netname=%s", info0->shi0_netname); 143 break; 144 145 case 1: 146 info1 = arg.result.ru.info1; 147 smb_tracef("srvsvc shi1_netname=%s", info1->shi1_netname); 148 smb_tracef("srvsvc shi1_type=%u", info1->shi1_type); 149 150 if (info1->shi1_comment) 151 smb_tracef("srvsvc shi1_comment=%s", 152 info1->shi1_comment); 153 break; 154 155 case 2: 156 info2 = arg.result.ru.info2; 157 smb_tracef("srvsvc shi2_netname=%s", info2->shi2_netname); 158 smb_tracef("srvsvc shi2_type=%u", info2->shi2_type); 159 160 if (info2->shi2_comment) 161 smb_tracef("srvsvc shi2_comment=%s", 162 info2->shi2_comment); 163 164 smb_tracef("srvsvc shi2_perms=%d", info2->shi2_permissions); 165 smb_tracef("srvsvc shi2_max_use=%d", info2->shi2_max_uses); 166 smb_tracef("srvsvc shi2_cur_use=%d", info2->shi2_current_uses); 167 168 if (info2->shi2_path) 169 smb_tracef("srvsvc shi2_path=%s", info2->shi2_path); 170 171 if (info2->shi2_passwd) 172 smb_tracef("srvsvc shi2_passwd=%s", info2->shi2_passwd); 173 break; 174 175 default: 176 smb_tracef("srvsvc: unknown level"); 177 break; 178 } 179 180 srvsvc_close(&handle); 181 return (0); 182 } 183 184 /* 185 * This is a client side routine for NetSessionEnum. 186 * NetSessionEnum requires administrator rights. 187 */ 188 int 189 srvsvc_net_session_enum(char *server, char *domain, char *netname) 190 { 191 struct mslm_NetSessionEnum arg; 192 mlsvc_handle_t handle; 193 int rc; 194 int opnum; 195 struct mslm_infonres infonres; 196 struct mslm_SESSION_INFO_1 *nsi1; 197 int len; 198 char *user = smbrdr_ipc_get_user(); 199 200 if (netname == NULL) 201 return (-1); 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 = smbrdr_ipc_get_user(); 264 265 if (netname == NULL) 266 return (-1); 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 int 351 srvsvc_net_server_getinfo(char *server, char *domain, 352 srvsvc_server_info_t *svinfo) 353 { 354 mlsvc_handle_t handle; 355 struct mslm_NetServerGetInfo arg; 356 struct mslm_SERVER_INFO_101 *sv101; 357 int len, opnum, rc; 358 char *user = smbrdr_ipc_get_user(); 359 360 if (srvsvc_open(server, domain, user, &handle) != 0) 361 return (-1); 362 363 opnum = SRVSVC_OPNUM_NetServerGetInfo; 364 bzero(&arg, sizeof (arg)); 365 366 len = strlen(server) + 4; 367 arg.servername = ndr_rpc_malloc(&handle, len); 368 if (arg.servername == NULL) 369 return (-1); 370 371 (void) snprintf((char *)arg.servername, len, "\\\\%s", server); 372 arg.level = 101; 373 374 rc = ndr_rpc_call(&handle, opnum, &arg); 375 if ((rc != 0) || (arg.status != 0)) { 376 srvsvc_close(&handle); 377 return (-1); 378 } 379 380 sv101 = arg.result.bufptr.bufptr101; 381 382 bzero(svinfo, sizeof (srvsvc_server_info_t)); 383 svinfo->sv_platform_id = sv101->sv101_platform_id; 384 svinfo->sv_version_major = sv101->sv101_version_major; 385 svinfo->sv_version_minor = sv101->sv101_version_minor; 386 svinfo->sv_type = sv101->sv101_type; 387 if (sv101->sv101_name) 388 svinfo->sv_name = strdup((char *)sv101->sv101_name); 389 if (sv101->sv101_comment) 390 svinfo->sv_comment = strdup((char *)sv101->sv101_comment); 391 392 srvsvc_close(&handle); 393 return (0); 394 } 395 396 /* 397 * Synchronize the local system clock with the domain controller. 398 */ 399 void 400 srvsvc_timesync(void) 401 { 402 smb_domain_t di; 403 struct timeval tv; 404 struct tm tm; 405 time_t tsecs; 406 407 if (!smb_domain_getinfo(&di)) 408 return; 409 410 if (srvsvc_net_remote_tod(di.d_dc, di.d_info.di_nbname, &tv, &tm) != 0) 411 return; 412 413 if (settimeofday(&tv, 0)) 414 smb_tracef("unable to set system time"); 415 416 tsecs = time(0); 417 (void) localtime_r(&tsecs, &tm); 418 smb_tracef("SrvsvcTimeSync %s", ctime((time_t *)&tv.tv_sec)); 419 } 420 421 /* 422 * NetRemoteTOD to get the current GMT time from a Windows NT server. 423 */ 424 int 425 srvsvc_gettime(unsigned long *t) 426 { 427 smb_domain_t di; 428 struct timeval tv; 429 struct tm tm; 430 431 if (!smb_domain_getinfo(&di)) 432 return (-1); 433 434 if (srvsvc_net_remote_tod(di.d_dc, di.d_info.di_nbname, &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 470 srvsvc_net_remote_tod(char *server, char *domain, struct timeval *tv, 471 struct tm *tm) 472 { 473 char timebuf[64]; 474 struct mslm_NetRemoteTOD arg; 475 struct mslm_TIME_OF_DAY_INFO *tod; 476 mlsvc_handle_t handle; 477 int rc; 478 int opnum; 479 int len; 480 char *user = smbrdr_ipc_get_user(); 481 482 rc = srvsvc_open(server, domain, user, &handle); 483 if (rc != 0) 484 return (-1); 485 486 opnum = SRVSVC_OPNUM_NetRemoteTOD; 487 bzero(&arg, sizeof (struct mslm_NetRemoteTOD)); 488 489 len = strlen(server) + 4; 490 arg.servername = ndr_rpc_malloc(&handle, len); 491 if (arg.servername == NULL) { 492 srvsvc_close(&handle); 493 return (-1); 494 } 495 496 (void) snprintf((char *)arg.servername, len, "\\\\%s", server); 497 498 rc = ndr_rpc_call(&handle, opnum, &arg); 499 if ((rc != 0) || (arg.status != 0)) { 500 srvsvc_close(&handle); 501 return (-1); 502 } 503 504 /* 505 * We're assigning milliseconds to microseconds 506 * here but the value's not really relevant. 507 */ 508 tod = arg.bufptr; 509 510 if (tv) { 511 tv->tv_sec = tod->tod_elapsedt; 512 tv->tv_usec = tod->tod_msecs; 513 smb_tracef("RemoteTime from %s: %s", server, 514 ctime(&tv->tv_sec)); 515 } 516 517 if (tm) { 518 tm->tm_sec = tod->tod_secs; 519 tm->tm_min = tod->tod_mins; 520 tm->tm_hour = tod->tod_hours; 521 tm->tm_mday = tod->tod_day; 522 tm->tm_mon = tod->tod_month - 1; 523 tm->tm_year = tod->tod_year - 1900; 524 tm->tm_wday = tod->tod_weekday; 525 526 (void) strftime(timebuf, sizeof (timebuf), 527 "NetRemoteTOD: %D %T", tm); 528 smb_tracef("NetRemoteTOD from %s: %s", server, timebuf); 529 } 530 531 srvsvc_close(&handle); 532 return (0); 533 } 534 535 void 536 srvsvc_net_test(char *server, char *domain, char *netname) 537 { 538 smb_domain_t di; 539 srvsvc_server_info_t svinfo; 540 541 (void) smb_tracef("%s %s %s", server, domain, netname); 542 543 if (smb_domain_getinfo(&di)) { 544 server = di.d_dc; 545 domain = di.d_info.di_nbname; 546 } 547 548 if (srvsvc_net_server_getinfo(server, domain, &svinfo) == 0) { 549 smb_tracef("NetServerGetInfo: %s %s (%d.%d) id=%d type=0x%08x", 550 svinfo.sv_name ? svinfo.sv_name : "NULL", 551 svinfo.sv_comment ? svinfo.sv_comment : "NULL", 552 svinfo.sv_version_major, svinfo.sv_version_minor, 553 svinfo.sv_platform_id, svinfo.sv_type); 554 555 free(svinfo.sv_name); 556 free(svinfo.sv_comment); 557 } 558 559 (void) srvsvc_net_share_get_info(server, domain, netname); 560 #if 0 561 /* 562 * The NetSessionEnum server-side definition was updated. 563 * Disabled until the client-side has been updated. 564 */ 565 (void) srvsvc_net_session_enum(server, domain, netname); 566 #endif 567 (void) srvsvc_net_connect_enum(server, domain, netname, 0); 568 (void) srvsvc_net_connect_enum(server, domain, netname, 1); 569 } 570