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