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