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 }