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 }