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