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