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