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  * Compare the time here with the remote time on the server
 354  * and report clock skew.
 355  */
 356 void
 357 srvsvc_timecheck(char *server, char *domain)
 358 {
 359         char                    hostname[MAXHOSTNAMELEN];
 360         struct timeval          dc_tv;
 361         struct tm               dc_tm;
 362         struct tm               *tm;
 363         time_t                  tnow;
 364         time_t                  tdiff;
 365         int                     priority;
 366 
 367         if (srvsvc_net_remote_tod(server, domain, &dc_tv, &dc_tm) < 0) {
 368                 syslog(LOG_DEBUG, "srvsvc_net_remote_tod failed");
 369                 return;
 370         }
 371 
 372         tnow = time(NULL);
 373 
 374         if (tnow > dc_tv.tv_sec)
 375                 tdiff = (tnow - dc_tv.tv_sec) / SECSPERMIN;
 376         else
 377                 tdiff = (dc_tv.tv_sec - tnow) / SECSPERMIN;
 378 
 379         if (tdiff != 0) {
 380                 (void) strlcpy(hostname, "localhost", MAXHOSTNAMELEN);
 381                 (void) gethostname(hostname, MAXHOSTNAMELEN);
 382 
 383                 priority = (tdiff > 2) ? LOG_NOTICE : LOG_DEBUG;
 384                 syslog(priority, "DC [%s] clock skew detected: %u minutes",
 385                     server, tdiff);
 386 
 387                 tm = gmtime(&dc_tv.tv_sec);
 388                 syslog(priority, "%-8s  UTC: %s", server, asctime(tm));
 389                 tm = gmtime(&tnow);
 390                 syslog(priority, "%-8s  UTC: %s", hostname, asctime(tm));
 391         }
 392 }
 393 
 394 /*
 395  * Synchronize the local system clock with the domain controller.
 396  */
 397 void
 398 srvsvc_timesync(void)
 399 {
 400         smb_domainex_t di;
 401         struct timeval tv;
 402         struct tm tm;
 403         time_t tsecs;
 404 
 405         if (!smb_domain_getinfo(&di))
 406                 return;
 407 
 408         if (srvsvc_net_remote_tod(di.d_dci.dc_name, di.d_primary.di_nbname,
 409             &tv, &tm) != 0)
 410                 return;
 411 
 412         if (settimeofday(&tv, 0))
 413                 smb_tracef("unable to set system time");
 414 
 415         tsecs = time(0);
 416         (void) localtime_r(&tsecs, &tm);
 417         smb_tracef("SrvsvcTimeSync %s", ctime((time_t *)&tv.tv_sec));
 418 }
 419 
 420 /*
 421  * NetRemoteTOD to get the current GMT time from a Windows NT server.
 422  */
 423 int
 424 srvsvc_gettime(unsigned long *t)
 425 {
 426         smb_domainex_t di;
 427         struct timeval tv;
 428         struct tm tm;
 429 
 430         if (!smb_domain_getinfo(&di))
 431                 return (-1);
 432 
 433         if (srvsvc_net_remote_tod(di.d_dci.dc_name, di.d_primary.di_nbname,
 434             &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         struct mslm_NetRemoteTOD        arg;
 474         struct mslm_TIME_OF_DAY_INFO    *tod;
 475         mlsvc_handle_t                  handle;
 476         int                             rc;
 477         int                             opnum;
 478         int                             len;
 479         char                            user[SMB_USERNAME_MAXLEN];
 480 
 481         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 482 
 483         rc = srvsvc_open(server, domain, user, &handle);
 484         if (rc != 0)
 485                 return (-1);
 486 
 487         opnum = SRVSVC_OPNUM_NetRemoteTOD;
 488         bzero(&arg, sizeof (struct mslm_NetRemoteTOD));
 489 
 490         len = strlen(server) + 4;
 491         arg.servername = ndr_rpc_malloc(&handle, len);
 492         if (arg.servername == NULL) {
 493                 srvsvc_close(&handle);
 494                 return (-1);
 495         }
 496 
 497         (void) snprintf((char *)arg.servername, len, "\\\\%s", server);
 498 
 499         rc = ndr_rpc_call(&handle, opnum, &arg);
 500         if ((rc != 0) || (arg.status != 0)) {
 501                 srvsvc_close(&handle);
 502                 return (-1);
 503         }
 504 
 505         /*
 506          * We're assigning milliseconds to microseconds
 507          * here but the value's not really relevant.
 508          */
 509         tod = arg.bufptr;
 510 
 511         if (tv) {
 512                 tv->tv_sec = tod->tod_elapsedt;
 513                 tv->tv_usec = tod->tod_msecs;
 514         }
 515 
 516         if (tm) {
 517                 tm->tm_sec = tod->tod_secs;
 518                 tm->tm_min = tod->tod_mins;
 519                 tm->tm_hour = tod->tod_hours;
 520                 tm->tm_mday = tod->tod_day;
 521                 tm->tm_mon = tod->tod_month - 1;
 522                 tm->tm_year = tod->tod_year - 1900;
 523                 tm->tm_wday = tod->tod_weekday;
 524         }
 525 
 526         srvsvc_close(&handle);
 527         return (0);
 528 }