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 /* 351 * Synchronize the local system clock with the domain controller. 352 */ 353 void 354 srvsvc_timesync(void) 355 { 356 smb_domain_t di; 357 struct timeval tv; 358 struct tm tm; 359 time_t tsecs; 360 361 if (!smb_domain_getinfo(&di)) 362 return; 363 364 if (srvsvc_net_remote_tod(di.d_dc, di.d_info.di_nbname, &tv, &tm) != 0) 365 return; 366 367 if (settimeofday(&tv, 0)) 368 smb_tracef("unable to set system time"); 369 370 tsecs = time(0); 371 (void) localtime_r(&tsecs, &tm); 372 smb_tracef("SrvsvcTimeSync %s", ctime((time_t *)&tv.tv_sec)); 373 } 374 375 /* 376 * NetRemoteTOD to get the current GMT time from a Windows NT server. 377 */ 378 int 379 srvsvc_gettime(unsigned long *t) 380 { 381 smb_domain_t di; 382 struct timeval tv; 383 struct tm tm; 384 385 if (!smb_domain_getinfo(&di)) 386 return (-1); 387 388 if (srvsvc_net_remote_tod(di.d_dc, di.d_info.di_nbname, &tv, &tm) != 0) 389 return (-1); 390 391 *t = tv.tv_sec; 392 return (0); 393 } 394 395 /* 396 * This is a client side routine for NetRemoteTOD, which gets the time 397 * and date from a remote system. The time information is returned in 398 * the timeval and tm. 399 * 400 * typedef struct _TIME_OF_DAY_INFO { 401 * DWORD tod_elapsedt; // seconds since 00:00:00 January 1 1970 GMT 402 * DWORD tod_msecs; // arbitrary milliseconds (since reset) 403 * DWORD tod_hours; // current hour [0-23] 404 * DWORD tod_mins; // current minute [0-59] 405 * DWORD tod_secs; // current second [0-59] 406 * DWORD tod_hunds; // current hundredth (0.01) second [0-99] 407 * LONG tod_timezone; // time zone of the server 408 * DWORD tod_tinterval; // clock tick time interval 409 * DWORD tod_day; // day of the month [1-31] 410 * DWORD tod_month; // month of the year [1-12] 411 * DWORD tod_year; // current year 412 * DWORD tod_weekday; // day of the week since sunday [0-6] 413 * } TIME_OF_DAY_INFO; 414 * 415 * The time zone of the server is calculated in minutes from Greenwich 416 * Mean Time (GMT). For time zones west of Greenwich, the value is 417 * positive; for time zones east of Greenwich, the value is negative. 418 * A value of -1 indicates that the time zone is undefined. 419 * 420 * The clock tick value represents a resolution of one ten-thousandth 421 * (0.0001) second. 422 */ 423 int 424 srvsvc_net_remote_tod(char *server, char *domain, struct timeval *tv, 425 struct tm *tm) 426 { 427 char timebuf[64]; 428 struct mslm_NetRemoteTOD arg; 429 struct mslm_TIME_OF_DAY_INFO *tod; 430 mlsvc_handle_t handle; 431 int rc; 432 int opnum; 433 int len; 434 char *user = smbrdr_ipc_get_user(); 435 436 rc = srvsvc_open(server, domain, user, &handle); 437 if (rc != 0) 438 return (-1); 439 440 opnum = SRVSVC_OPNUM_NetRemoteTOD; 441 bzero(&arg, sizeof (struct mslm_NetRemoteTOD)); 442 443 len = strlen(server) + 4; 444 arg.servername = ndr_rpc_malloc(&handle, len); 445 if (arg.servername == NULL) { 446 srvsvc_close(&handle); 447 return (-1); 448 } 449 450 (void) snprintf((char *)arg.servername, len, "\\\\%s", server); 451 452 rc = ndr_rpc_call(&handle, opnum, &arg); 453 if ((rc != 0) || (arg.status != 0)) { 454 srvsvc_close(&handle); 455 return (-1); 456 } 457 458 /* 459 * We're assigning milliseconds to microseconds 460 * here but the value's not really relevant. 461 */ 462 tod = arg.bufptr; 463 464 if (tv) { 465 tv->tv_sec = tod->tod_elapsedt; 466 tv->tv_usec = tod->tod_msecs; 467 smb_tracef("RemoteTime from %s: %s", server, 468 ctime(&tv->tv_sec)); 469 } 470 471 if (tm) { 472 tm->tm_sec = tod->tod_secs; 473 tm->tm_min = tod->tod_mins; 474 tm->tm_hour = tod->tod_hours; 475 tm->tm_mday = tod->tod_day; 476 tm->tm_mon = tod->tod_month - 1; 477 tm->tm_year = tod->tod_year - 1900; 478 tm->tm_wday = tod->tod_weekday; 479 480 (void) strftime(timebuf, sizeof (timebuf), 481 "NetRemoteTOD: %D %T", tm); 482 smb_tracef("NetRemoteTOD from %s: %s", server, timebuf); 483 } 484 485 srvsvc_close(&handle); 486 return (0); 487 } 488 489 void 490 srvsvc_net_test(char *server, char *domain, char *netname) 491 { 492 smb_domain_t di; 493 494 (void) smb_tracef("%s %s %s", server, domain, netname); 495 496 if (smb_domain_getinfo(&di)) { 497 server = di.d_dc; 498 domain = di.d_info.di_nbname; 499 } 500 501 (void) srvsvc_net_share_get_info(server, domain, netname); 502 #if 0 503 /* 504 * The NetSessionEnum server-side definition was updated. 505 * Disabled until the client-side has been updated. 506 */ 507 (void) srvsvc_net_session_enum(server, domain, netname); 508 #endif 509 (void) srvsvc_net_connect_enum(server, domain, netname, 0); 510 (void) srvsvc_net_connect_enum(server, domain, netname, 1); 511 } 512