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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  24  * Copyright 2017 Joyent, Inc.
  25  */
  26 
  27 #include <sys/types.h>
  28 #include <sys/stat.h>
  29 #include <sys/ioccom.h>
  30 #include <sys/param.h>
  31 #include <stddef.h>
  32 #include <stdio.h>
  33 #include <string.h>
  34 #include <strings.h>
  35 #include <stdlib.h>
  36 #include <unistd.h>
  37 #include <fcntl.h>
  38 #include <errno.h>
  39 
  40 #include <smbsrv/smb_xdr.h>
  41 #include <smbsrv/smbinfo.h>
  42 #include <smbsrv/smb_ioctl.h>
  43 #include <smbsrv/libsmb.h>
  44 
  45 #define SMBDRV_DEVICE_PATH              "/dev/smbsrv"
  46 
  47 int smb_kmod_ioctl(int, smb_ioc_header_t *, uint32_t);
  48 
  49 
  50 int     smbdrv_fd = -1;
  51 
  52 int
  53 smb_kmod_bind(void)
  54 {
  55         if (smbdrv_fd != -1)
  56                 (void) close(smbdrv_fd);
  57 
  58         if ((smbdrv_fd = open(SMBDRV_DEVICE_PATH, 0)) < 0) {
  59                 smbdrv_fd = -1;
  60                 return (errno);
  61         }
  62 
  63         return (0);
  64 }
  65 
  66 boolean_t
  67 smb_kmod_isbound(void)
  68 {
  69         return ((smbdrv_fd == -1) ? B_FALSE : B_TRUE);
  70 }
  71 
  72 /* See also: smbsrv smb_server_store_cfg */
  73 int
  74 smb_kmod_setcfg(smb_kmod_cfg_t *cfg)
  75 {
  76         smb_ioc_cfg_t ioc;
  77 
  78         ioc.maxworkers = cfg->skc_maxworkers;
  79         ioc.maxconnections = cfg->skc_maxconnections;
  80         ioc.keepalive = cfg->skc_keepalive;
  81         ioc.restrict_anon = cfg->skc_restrict_anon;
  82         ioc.signing_enable = cfg->skc_signing_enable;
  83         ioc.signing_required = cfg->skc_signing_required;
  84         ioc.oplock_enable = cfg->skc_oplock_enable;
  85         ioc.sync_enable = cfg->skc_sync_enable;
  86         ioc.secmode = cfg->skc_secmode;
  87         ioc.netbios_enable = cfg->skc_netbios_enable;
  88         ioc.ipv6_enable = cfg->skc_ipv6_enable;
  89         ioc.print_enable = cfg->skc_print_enable;
  90         ioc.traverse_mounts = cfg->skc_traverse_mounts;
  91         ioc.max_protocol = cfg->skc_max_protocol;
  92         ioc.min_protocol = cfg->skc_min_protocol;
  93         ioc.exec_flags = cfg->skc_execflags;
  94         ioc.negtok_len = cfg->skc_negtok_len;
  95         ioc.version = cfg->skc_version;
  96         ioc.initial_credits = cfg->skc_initial_credits;
  97         ioc.maximum_credits = cfg->skc_maximum_credits;
  98         ioc.encrypt = cfg->skc_encrypt;
  99 
 100         (void) memcpy(ioc.machine_uuid, cfg->skc_machine_uuid, sizeof (uuid_t));
 101         (void) memcpy(ioc.negtok, cfg->skc_negtok, sizeof (ioc.negtok));
 102         (void) memcpy(ioc.native_os, cfg->skc_native_os,
 103             sizeof (ioc.native_os));
 104         (void) memcpy(ioc.native_lm, cfg->skc_native_lm,
 105             sizeof (ioc.native_lm));
 106 
 107         (void) strlcpy(ioc.nbdomain, cfg->skc_nbdomain, sizeof (ioc.nbdomain));
 108         (void) strlcpy(ioc.fqdn, cfg->skc_fqdn, sizeof (ioc.fqdn));
 109         (void) strlcpy(ioc.hostname, cfg->skc_hostname, sizeof (ioc.hostname));
 110         (void) strlcpy(ioc.system_comment, cfg->skc_system_comment,
 111             sizeof (ioc.system_comment));
 112 
 113         return (smb_kmod_ioctl(SMB_IOC_CONFIG, &ioc.hdr, sizeof (ioc)));
 114 }
 115 
 116 int
 117 smb_kmod_setgmtoff(int32_t gmtoff)
 118 {
 119         smb_ioc_gmt_t ioc;
 120 
 121         ioc.offset = gmtoff;
 122         return (smb_kmod_ioctl(SMB_IOC_GMTOFF, &ioc.hdr,
 123             sizeof (ioc)));
 124 }
 125 
 126 int
 127 smb_kmod_start(int opipe, int lmshr, int udoor)
 128 {
 129         smb_ioc_start_t ioc;
 130 
 131         ioc.opipe = opipe;
 132         ioc.lmshrd = lmshr;
 133         ioc.udoor = udoor;
 134         return (smb_kmod_ioctl(SMB_IOC_START, &ioc.hdr, sizeof (ioc)));
 135 }
 136 
 137 void
 138 smb_kmod_stop(void)
 139 {
 140         smb_ioc_header_t ioc;
 141 
 142         (void) smb_kmod_ioctl(SMB_IOC_STOP, &ioc, sizeof (ioc));
 143 }
 144 
 145 int
 146 smb_kmod_event_notify(uint32_t txid)
 147 {
 148         smb_ioc_event_t ioc;
 149 
 150         ioc.txid = txid;
 151         return (smb_kmod_ioctl(SMB_IOC_EVENT, &ioc.hdr, sizeof (ioc)));
 152 }
 153 
 154 int
 155 smb_kmod_share(nvlist_t *shrlist)
 156 {
 157         smb_ioc_share_t *ioc;
 158         uint32_t ioclen;
 159         char *shrbuf = NULL;
 160         size_t bufsz;
 161         int rc = ENOMEM;
 162 
 163         if ((rc = nvlist_pack(shrlist, &shrbuf, &bufsz, NV_ENCODE_XDR, 0)) != 0)
 164                 return (rc);
 165 
 166         ioclen = sizeof (smb_ioc_share_t) + bufsz;
 167 
 168         if ((ioc = malloc(ioclen)) != NULL) {
 169                 ioc->shrlen = bufsz;
 170                 bcopy(shrbuf, ioc->shr, bufsz);
 171                 rc = smb_kmod_ioctl(SMB_IOC_SHARE, &ioc->hdr, ioclen);
 172                 free(ioc);
 173         }
 174 
 175         free(shrbuf);
 176         return (rc);
 177 }
 178 
 179 int
 180 smb_kmod_unshare(nvlist_t *shrlist)
 181 {
 182         smb_ioc_share_t *ioc;
 183         uint32_t ioclen;
 184         char *shrbuf = NULL;
 185         size_t bufsz;
 186         int rc = ENOMEM;
 187 
 188         if ((rc = nvlist_pack(shrlist, &shrbuf, &bufsz, NV_ENCODE_XDR, 0)) != 0)
 189                 return (rc);
 190 
 191         ioclen = sizeof (smb_ioc_share_t) + bufsz;
 192 
 193         if ((ioc = malloc(ioclen)) != NULL) {
 194                 ioc->shrlen = bufsz;
 195                 bcopy(shrbuf, ioc->shr, bufsz);
 196                 rc = smb_kmod_ioctl(SMB_IOC_UNSHARE, &ioc->hdr, ioclen);
 197                 free(ioc);
 198         }
 199 
 200         free(shrbuf);
 201         return (rc);
 202 }
 203 
 204 int
 205 smb_kmod_shareinfo(char *shrname, boolean_t *shortnames)
 206 {
 207         smb_ioc_shareinfo_t ioc;
 208         int rc;
 209 
 210         bzero(&ioc, sizeof (ioc));
 211         (void) strlcpy(ioc.shrname, shrname, MAXNAMELEN);
 212 
 213         rc = smb_kmod_ioctl(SMB_IOC_SHAREINFO, &ioc.hdr, sizeof (ioc));
 214         if (rc == 0)
 215                 *shortnames = ioc.shortnames;
 216         else
 217                 *shortnames = B_TRUE;
 218 
 219         return (rc);
 220 }
 221 
 222 int
 223 smb_kmod_get_open_num(smb_opennum_t *opennum)
 224 {
 225         smb_ioc_opennum_t ioc;
 226         int rc;
 227 
 228         bzero(&ioc, sizeof (ioc));
 229         ioc.qualtype = opennum->qualtype;
 230         (void) strlcpy(ioc.qualifier, opennum->qualifier, MAXNAMELEN);
 231 
 232         rc = smb_kmod_ioctl(SMB_IOC_NUMOPEN, &ioc.hdr, sizeof (ioc));
 233         if (rc == 0) {
 234                 opennum->open_users = ioc.open_users;
 235                 opennum->open_trees = ioc.open_trees;
 236                 opennum->open_files = ioc.open_files;
 237         }
 238 
 239         return (rc);
 240 }
 241 
 242 int
 243 smb_kmod_get_spool_doc(uint32_t *spool_num, char *username,
 244     char *path, smb_inaddr_t *ipaddr)
 245 {
 246         smb_ioc_spooldoc_t ioc;
 247         int rc;
 248 
 249         bzero(&ioc, sizeof (ioc));
 250         rc = smb_kmod_ioctl(SMB_IOC_SPOOLDOC, &ioc.hdr, sizeof (ioc));
 251         if (rc == 0) {
 252                 *spool_num = ioc.spool_num;
 253                 (void) strlcpy(username, ioc.username, MAXNAMELEN);
 254                 (void) strlcpy(path, ioc.path, MAXPATHLEN);
 255                 *ipaddr = ioc.ipaddr;
 256         }
 257         return (rc);
 258 }
 259 
 260 /*
 261  * Initialization for an smb_kmod_enum request.  If this call succeeds,
 262  * smb_kmod_enum_fini() must be called later to deallocate resources.
 263  */
 264 smb_netsvc_t *
 265 smb_kmod_enum_init(smb_svcenum_t *request)
 266 {
 267         smb_netsvc_t            *ns;
 268         smb_svcenum_t           *svcenum;
 269         smb_ioc_svcenum_t       *ioc;
 270         uint32_t                ioclen;
 271 
 272         if ((ns = calloc(1, sizeof (smb_netsvc_t))) == NULL)
 273                 return (NULL);
 274 
 275         ioclen = sizeof (smb_ioc_svcenum_t) + SMB_IOC_DATA_SIZE;
 276         if ((ioc = malloc(ioclen)) == NULL) {
 277                 free(ns);
 278                 return (NULL);
 279         }
 280 
 281         bzero(ioc, ioclen);
 282         svcenum = &ioc->svcenum;
 283         svcenum->se_type   = request->se_type;
 284         svcenum->se_level  = request->se_level;
 285         svcenum->se_bavail = SMB_IOC_DATA_SIZE;
 286         svcenum->se_nlimit = request->se_nlimit;
 287         svcenum->se_nskip = request->se_nskip;
 288         svcenum->se_buflen = SMB_IOC_DATA_SIZE;
 289 
 290         list_create(&ns->ns_list, sizeof (smb_netsvcitem_t),
 291             offsetof(smb_netsvcitem_t, nsi_lnd));
 292 
 293         ns->ns_ioc = ioc;
 294         ns->ns_ioclen = ioclen;
 295         return (ns);
 296 }
 297 
 298 /*
 299  * Cleanup resources allocated via smb_kmod_enum_init and smb_kmod_enum.
 300  */
 301 void
 302 smb_kmod_enum_fini(smb_netsvc_t *ns)
 303 {
 304         list_t                  *lst;
 305         smb_netsvcitem_t        *item;
 306         smb_netuserinfo_t       *user;
 307         smb_netconnectinfo_t    *tree;
 308         smb_netfileinfo_t       *ofile;
 309         uint32_t                se_type;
 310 
 311         if (ns == NULL)
 312                 return;
 313 
 314         lst = &ns->ns_list;
 315         se_type = ns->ns_ioc->svcenum.se_type;
 316 
 317         while ((item = list_head(lst)) != NULL) {
 318                 list_remove(lst, item);
 319 
 320                 switch (se_type) {
 321                 case SMB_SVCENUM_TYPE_USER:
 322                         user = &item->nsi_un.nsi_user;
 323                         free(user->ui_domain);
 324                         free(user->ui_account);
 325                         free(user->ui_workstation);
 326                         break;
 327                 case SMB_SVCENUM_TYPE_TREE:
 328                         tree = &item->nsi_un.nsi_tree;
 329                         free(tree->ci_username);
 330                         free(tree->ci_share);
 331                         break;
 332                 case SMB_SVCENUM_TYPE_FILE:
 333                         ofile = &item->nsi_un.nsi_ofile;
 334                         free(ofile->fi_path);
 335                         free(ofile->fi_username);
 336                         break;
 337                 default:
 338                         break;
 339                 }
 340         }
 341 
 342         list_destroy(&ns->ns_list);
 343         free(ns->ns_items);
 344         free(ns->ns_ioc);
 345         free(ns);
 346 }
 347 
 348 /*
 349  * Enumerate users, connections or files.
 350  */
 351 int
 352 smb_kmod_enum(smb_netsvc_t *ns)
 353 {
 354         smb_ioc_svcenum_t       *ioc;
 355         uint32_t                ioclen;
 356         smb_svcenum_t           *svcenum;
 357         smb_netsvcitem_t        *items;
 358         smb_netuserinfo_t       *user;
 359         smb_netconnectinfo_t    *tree;
 360         smb_netfileinfo_t       *ofile;
 361         uint8_t                 *data;
 362         uint32_t                len;
 363         uint32_t                se_type;
 364         uint_t                  nbytes;
 365         int                     i;
 366         int                     rc;
 367 
 368         ioc = ns->ns_ioc;
 369         ioclen = ns->ns_ioclen;
 370         rc = smb_kmod_ioctl(SMB_IOC_SVCENUM, &ioc->hdr, ioclen);
 371         if (rc != 0)
 372                 return (rc);
 373 
 374         svcenum = &ioc->svcenum;
 375         items = calloc(svcenum->se_nitems, sizeof (smb_netsvcitem_t));
 376         if (items == NULL)
 377                 return (ENOMEM);
 378 
 379         ns->ns_items = items;
 380         se_type = ns->ns_ioc->svcenum.se_type;
 381         data = svcenum->se_buf;
 382         len = svcenum->se_bused;
 383 
 384         for (i = 0; i < svcenum->se_nitems; ++i) {
 385                 switch (se_type) {
 386                 case SMB_SVCENUM_TYPE_USER:
 387                         user = &items->nsi_un.nsi_user;
 388                         rc = smb_netuserinfo_decode(user, data, len, &nbytes);
 389                         break;
 390                 case SMB_SVCENUM_TYPE_TREE:
 391                         tree = &items->nsi_un.nsi_tree;
 392                         rc = smb_netconnectinfo_decode(tree, data, len,
 393                             &nbytes);
 394                         break;
 395                 case SMB_SVCENUM_TYPE_FILE:
 396                         ofile = &items->nsi_un.nsi_ofile;
 397                         rc = smb_netfileinfo_decode(ofile, data, len, &nbytes);
 398                         break;
 399                 default:
 400                         rc = -1;
 401                         break;
 402                 }
 403 
 404                 if (rc != 0)
 405                         return (EINVAL);
 406 
 407                 list_insert_tail(&ns->ns_list, items);
 408 
 409                 ++items;
 410                 data += nbytes;
 411                 len -= nbytes;
 412         }
 413 
 414         return (0);
 415 }
 416 
 417 /*
 418  * A NULL pointer is a wildcard indicator, which we pass on
 419  * as an empty string (by virtue of the bzero).
 420  */
 421 int
 422 smb_kmod_session_close(const char *client, const char *username)
 423 {
 424         smb_ioc_session_t ioc;
 425         int rc;
 426 
 427         bzero(&ioc, sizeof (ioc));
 428 
 429         if (client != NULL)
 430                 (void) strlcpy(ioc.client, client, MAXNAMELEN);
 431         if (username != NULL)
 432                 (void) strlcpy(ioc.username, username, MAXNAMELEN);
 433 
 434         rc = smb_kmod_ioctl(SMB_IOC_SESSION_CLOSE, &ioc.hdr, sizeof (ioc));
 435         return (rc);
 436 }
 437 
 438 int
 439 smb_kmod_file_close(uint32_t uniqid)
 440 {
 441         smb_ioc_fileid_t ioc;
 442         int rc;
 443 
 444         bzero(&ioc, sizeof (ioc));
 445         ioc.uniqid = uniqid;
 446 
 447         rc = smb_kmod_ioctl(SMB_IOC_FILE_CLOSE, &ioc.hdr, sizeof (ioc));
 448         return (rc);
 449 }
 450 
 451 void
 452 smb_kmod_unbind(void)
 453 {
 454         if (smbdrv_fd != -1) {
 455                 (void) close(smbdrv_fd);
 456                 smbdrv_fd = -1;
 457         }
 458 }
 459 
 460 /*
 461  * Note: The user-space smbd-d provides it own version of this function
 462  * which directly calls the "kernel" module code (in user space).
 463  */
 464 int
 465 smb_kmod_ioctl(int cmd, smb_ioc_header_t *ioc, uint32_t len)
 466 {
 467         int rc = EINVAL;
 468 
 469         ioc->version = SMB_IOC_VERSION;
 470         ioc->cmd = cmd;
 471         ioc->len = len;
 472         ioc->crc = 0;
 473         ioc->crc = smb_crc_gen((uint8_t *)ioc, sizeof (smb_ioc_header_t));
 474 
 475         if (smbdrv_fd != -1) {
 476                 if (ioctl(smbdrv_fd, cmd, ioc) < 0)
 477                         rc = errno;
 478                 else
 479                         rc = 0;
 480         }
 481         return (rc);
 482 }