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