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 } 485