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
smb_kmod_bind(void)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
smb_kmod_isbound(void)67 smb_kmod_isbound(void)
68 {
69 return ((smbdrv_fd == -1) ? B_FALSE : B_TRUE);
70 }
71
72 int
smb_kmod_setcfg(smb_kmod_cfg_t * cfg)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
smb_kmod_setgmtoff(int32_t gmtoff)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
smb_kmod_start(int opipe,int lmshr,int udoor)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
smb_kmod_stop(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
smb_kmod_event_notify(uint32_t txid)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
smb_kmod_share(nvlist_t * shrlist)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
smb_kmod_unshare(nvlist_t * shrlist)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
smb_kmod_shareinfo(char * shrname,boolean_t * shortnames)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
smb_kmod_get_open_num(smb_opennum_t * opennum)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
smb_kmod_get_spool_doc(uint32_t * spool_num,char * username,char * path,smb_inaddr_t * ipaddr)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 *
smb_kmod_enum_init(smb_svcenum_t * request)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
smb_kmod_enum_fini(smb_netsvc_t * ns)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
smb_kmod_enum(smb_netsvc_t * ns)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
smb_kmod_session_close(const char * client,const char * username)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
smb_kmod_file_close(uint32_t uniqid)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
smb_kmod_unbind(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
smb_kmod_ioctl(int cmd,smb_ioc_header_t * ioc,uint32_t len)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