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