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