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