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