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 2017 Nexenta Systems, Inc. All rights reserved.
24 * Copyright 2017 Joyent, Inc.
25 * Copyright 2022 RackTop Systems, Inc.
26 */
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/ioccom.h>
31 #include <sys/param.h>
32 #include <stddef.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <errno.h>
40
41 #include <smbsrv/smb_xdr.h>
42 #include <smbsrv/smbinfo.h>
43 #include <smbsrv/smb_ioctl.h>
44 #include <smbsrv/libsmb.h>
45
46 #define SMBDRV_DEVICE_PATH "/dev/smbsrv"
47
48 int smb_kmod_ioctl(int, smb_ioc_header_t *, uint32_t);
49
50
51 int smbdrv_fd = -1;
52
53 /*
54 * Open the smbsrv driver.
55 * The smb daemon (smbd) opens the "control" device (/dev/smbsrv)
56 * and other consumers open the library device (/dev/smbsrv1)
57 */
58 int
smb_kmod_bind(boolean_t smbd)59 smb_kmod_bind(boolean_t smbd)
60 {
61 int fd;
62
63 if (smbdrv_fd != -1)
64 (void) close(smbdrv_fd);
65
66 if (smbd) {
67 fd = open(SMBDRV_DEVICE_PATH, 0);
68 } else {
69 fd = open(SMBDRV_DEVICE_PATH "1", 0);
70 }
71 if (fd < 0)
72 return (errno);
73
74 smbdrv_fd = fd;
75
76 return (0);
77 }
78
79 boolean_t
smb_kmod_isbound(void)80 smb_kmod_isbound(void)
81 {
82 return ((smbdrv_fd == -1) ? B_FALSE : B_TRUE);
83 }
84
85 /* See also: smbsrv smb_server_store_cfg */
86 int
smb_kmod_setcfg(smb_kmod_cfg_t * cfg)87 smb_kmod_setcfg(smb_kmod_cfg_t *cfg)
88 {
89 smb_ioc_cfg_t ioc = {0};
90
91 ioc.maxworkers = cfg->skc_maxworkers;
92 ioc.maxconnections = cfg->skc_maxconnections;
93 ioc.keepalive = cfg->skc_keepalive;
94 ioc.restrict_anon = cfg->skc_restrict_anon;
95 ioc.signing_enable = cfg->skc_signing_enable;
96 ioc.signing_required = cfg->skc_signing_required;
97 ioc.oplock_enable = cfg->skc_oplock_enable;
98 ioc.sync_enable = cfg->skc_sync_enable;
99 ioc.secmode = cfg->skc_secmode;
100 ioc.netbios_enable = cfg->skc_netbios_enable;
101 ioc.ipv6_enable = cfg->skc_ipv6_enable;
102 ioc.print_enable = cfg->skc_print_enable;
103 ioc.traverse_mounts = cfg->skc_traverse_mounts;
104 ioc.short_names = cfg->skc_short_names;
105
106 ioc.max_protocol = cfg->skc_max_protocol;
107 ioc.min_protocol = cfg->skc_min_protocol;
108 ioc.exec_flags = cfg->skc_execflags;
109 ioc.negtok_len = cfg->skc_negtok_len;
110 ioc.max_opens = cfg->skc_max_opens;
111
112 ioc.version = cfg->skc_version;
113 ioc.initial_credits = cfg->skc_initial_credits;
114 ioc.maximum_credits = cfg->skc_maximum_credits;
115 ioc.encrypt = cfg->skc_encrypt;
116 ioc.encrypt_ciphers = cfg->skc_encrypt_ciphers;
117
118 (void) memcpy(ioc.machine_uuid, cfg->skc_machine_uuid, sizeof (uuid_t));
119 (void) memcpy(ioc.negtok, cfg->skc_negtok, sizeof (ioc.negtok));
120 (void) memcpy(ioc.native_os, cfg->skc_native_os,
121 sizeof (ioc.native_os));
122 (void) memcpy(ioc.native_lm, cfg->skc_native_lm,
123 sizeof (ioc.native_lm));
124
125 (void) strlcpy(ioc.nbdomain, cfg->skc_nbdomain, sizeof (ioc.nbdomain));
126 (void) strlcpy(ioc.fqdn, cfg->skc_fqdn, sizeof (ioc.fqdn));
127 (void) strlcpy(ioc.hostname, cfg->skc_hostname, sizeof (ioc.hostname));
128 (void) strlcpy(ioc.system_comment, cfg->skc_system_comment,
129 sizeof (ioc.system_comment));
130
131 return (smb_kmod_ioctl(SMB_IOC_CONFIG, &ioc.hdr, sizeof (ioc)));
132 }
133
134 int
smb_kmod_setgmtoff(int32_t gmtoff)135 smb_kmod_setgmtoff(int32_t gmtoff)
136 {
137 smb_ioc_gmt_t ioc;
138
139 ioc.offset = gmtoff;
140 return (smb_kmod_ioctl(SMB_IOC_GMTOFF, &ioc.hdr,
141 sizeof (ioc)));
142 }
143
144 int
smb_kmod_start(int opipe,int lmshr,int udoor)145 smb_kmod_start(int opipe, int lmshr, int udoor)
146 {
147 smb_ioc_start_t ioc;
148
149 ioc.opipe = opipe;
150 ioc.lmshrd = lmshr;
151 ioc.udoor = udoor;
152 return (smb_kmod_ioctl(SMB_IOC_START, &ioc.hdr, sizeof (ioc)));
153 }
154
155 void
smb_kmod_stop(void)156 smb_kmod_stop(void)
157 {
158 smb_ioc_header_t ioc;
159
160 (void) smb_kmod_ioctl(SMB_IOC_STOP, &ioc, sizeof (ioc));
161 }
162
163 int
smb_kmod_event_notify(uint32_t txid)164 smb_kmod_event_notify(uint32_t txid)
165 {
166 smb_ioc_event_t ioc;
167
168 ioc.txid = txid;
169 return (smb_kmod_ioctl(SMB_IOC_EVENT, &ioc.hdr, sizeof (ioc)));
170 }
171
172 int
smb_kmod_share(nvlist_t * shrlist)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
smb_kmod_unshare(nvlist_t * shrlist)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
smb_kmod_shareinfo(char * shrname,boolean_t * shortnames)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 /*
241 * Does the client have any access in this share?
242 */
243 int
smb_kmod_shareaccess(smb_netuserinfo_t * ui,smb_share_t * si)244 smb_kmod_shareaccess(smb_netuserinfo_t *ui, smb_share_t *si)
245 {
246 smb_ioc_shareaccess_t ioc;
247 int rc;
248
249 bzero(&ioc, sizeof (ioc));
250 ioc.session_id = ui->ui_session_id;
251 ioc.user_id = ui->ui_user_id;
252 (void) strlcpy(ioc.shrname, si->shr_name, MAXNAMELEN);
253
254 rc = smb_kmod_ioctl(SMB_IOC_SHAREACCESS, &ioc.hdr, sizeof (ioc));
255
256 return (rc);
257 }
258
259 int
smb_kmod_get_open_num(smb_opennum_t * opennum)260 smb_kmod_get_open_num(smb_opennum_t *opennum)
261 {
262 smb_ioc_opennum_t ioc;
263 int rc;
264
265 bzero(&ioc, sizeof (ioc));
266 ioc.qualtype = opennum->qualtype;
267 (void) strlcpy(ioc.qualifier, opennum->qualifier, MAXNAMELEN);
268
269 rc = smb_kmod_ioctl(SMB_IOC_NUMOPEN, &ioc.hdr, sizeof (ioc));
270 if (rc == 0) {
271 opennum->open_users = ioc.open_users;
272 opennum->open_trees = ioc.open_trees;
273 opennum->open_files = ioc.open_files;
274 }
275
276 return (rc);
277 }
278
279 int
smb_kmod_get_spool_doc(uint32_t * spool_num,char * username,char * path,smb_inaddr_t * ipaddr)280 smb_kmod_get_spool_doc(uint32_t *spool_num, char *username,
281 char *path, smb_inaddr_t *ipaddr)
282 {
283 smb_ioc_spooldoc_t ioc;
284 int rc;
285
286 bzero(&ioc, sizeof (ioc));
287 rc = smb_kmod_ioctl(SMB_IOC_SPOOLDOC, &ioc.hdr, sizeof (ioc));
288 if (rc == 0) {
289 *spool_num = ioc.spool_num;
290 (void) strlcpy(username, ioc.username, MAXNAMELEN);
291 (void) strlcpy(path, ioc.path, MAXPATHLEN);
292 *ipaddr = ioc.ipaddr;
293 }
294 return (rc);
295 }
296
297 /*
298 * Initialization for an smb_kmod_enum request. If this call succeeds,
299 * smb_kmod_enum_fini() must be called later to deallocate resources.
300 */
301 smb_netsvc_t *
smb_kmod_enum_init(smb_svcenum_t * request)302 smb_kmod_enum_init(smb_svcenum_t *request)
303 {
304 smb_netsvc_t *ns;
305 smb_svcenum_t *svcenum;
306 smb_ioc_svcenum_t *ioc;
307 uint32_t ioclen;
308
309 if ((ns = calloc(1, sizeof (smb_netsvc_t))) == NULL)
310 return (NULL);
311
312 ioclen = sizeof (smb_ioc_svcenum_t) + SMB_IOC_DATA_SIZE;
313 if ((ioc = malloc(ioclen)) == NULL) {
314 free(ns);
315 return (NULL);
316 }
317
318 bzero(ioc, ioclen);
319 svcenum = &ioc->svcenum;
320 svcenum->se_type = request->se_type;
321 svcenum->se_level = request->se_level;
322 svcenum->se_bavail = SMB_IOC_DATA_SIZE;
323 svcenum->se_nlimit = request->se_nlimit;
324 svcenum->se_nskip = request->se_nskip;
325 svcenum->se_buflen = SMB_IOC_DATA_SIZE;
326
327 list_create(&ns->ns_list, sizeof (smb_netsvcitem_t),
328 offsetof(smb_netsvcitem_t, nsi_lnd));
329
330 ns->ns_ioc = ioc;
331 ns->ns_ioclen = ioclen;
332 return (ns);
333 }
334
335 /*
336 * Cleanup resources allocated via smb_kmod_enum_init and smb_kmod_enum.
337 */
338 void
smb_kmod_enum_fini(smb_netsvc_t * ns)339 smb_kmod_enum_fini(smb_netsvc_t *ns)
340 {
341 list_t *lst;
342 smb_netsvcitem_t *item;
343 smb_netuserinfo_t *user;
344 smb_netconnectinfo_t *tree;
345 smb_netfileinfo_t *ofile;
346 uint32_t se_type;
347
348 if (ns == NULL)
349 return;
350
351 lst = &ns->ns_list;
352 se_type = ns->ns_ioc->svcenum.se_type;
353
354 while ((item = list_head(lst)) != NULL) {
355 list_remove(lst, item);
356
357 switch (se_type) {
358 case SMB_SVCENUM_TYPE_USER:
359 user = &item->nsi_un.nsi_user;
360 free(user->ui_domain);
361 free(user->ui_account);
362 free(user->ui_workstation);
363 break;
364 case SMB_SVCENUM_TYPE_TREE:
365 tree = &item->nsi_un.nsi_tree;
366 free(tree->ci_username);
367 free(tree->ci_share);
368 break;
369 case SMB_SVCENUM_TYPE_FILE:
370 ofile = &item->nsi_un.nsi_ofile;
371 free(ofile->fi_path);
372 free(ofile->fi_username);
373 break;
374 default:
375 break;
376 }
377 }
378
379 list_destroy(&ns->ns_list);
380 free(ns->ns_items);
381 free(ns->ns_ioc);
382 free(ns);
383 }
384
385 /*
386 * Enumerate users, connections or files.
387 */
388 int
smb_kmod_enum(smb_netsvc_t * ns)389 smb_kmod_enum(smb_netsvc_t *ns)
390 {
391 smb_ioc_svcenum_t *ioc;
392 uint32_t ioclen;
393 smb_svcenum_t *svcenum;
394 smb_netsvcitem_t *items;
395 smb_netuserinfo_t *user;
396 smb_netconnectinfo_t *tree;
397 smb_netfileinfo_t *ofile;
398 uint8_t *data;
399 uint32_t len;
400 uint32_t se_type;
401 uint_t nbytes;
402 int i;
403 int rc;
404
405 ioc = ns->ns_ioc;
406 ioclen = ns->ns_ioclen;
407 rc = smb_kmod_ioctl(SMB_IOC_SVCENUM, &ioc->hdr, ioclen);
408 if (rc != 0)
409 return (rc);
410
411 svcenum = &ioc->svcenum;
412 items = calloc(svcenum->se_nitems, sizeof (smb_netsvcitem_t));
413 if (items == NULL)
414 return (ENOMEM);
415
416 ns->ns_items = items;
417 se_type = ns->ns_ioc->svcenum.se_type;
418 data = svcenum->se_buf;
419 len = svcenum->se_bused;
420
421 for (i = 0; i < svcenum->se_nitems; ++i) {
422 switch (se_type) {
423 case SMB_SVCENUM_TYPE_USER:
424 user = &items->nsi_un.nsi_user;
425 rc = smb_netuserinfo_decode(user, data, len, &nbytes);
426 break;
427 case SMB_SVCENUM_TYPE_TREE:
428 tree = &items->nsi_un.nsi_tree;
429 rc = smb_netconnectinfo_decode(tree, data, len,
430 &nbytes);
431 break;
432 case SMB_SVCENUM_TYPE_FILE:
433 ofile = &items->nsi_un.nsi_ofile;
434 rc = smb_netfileinfo_decode(ofile, data, len, &nbytes);
435 break;
436 default:
437 rc = -1;
438 break;
439 }
440
441 if (rc != 0)
442 return (EINVAL);
443
444 list_insert_tail(&ns->ns_list, items);
445
446 ++items;
447 data += nbytes;
448 len -= nbytes;
449 }
450
451 return (0);
452 }
453
454 /*
455 * A NULL pointer is a wildcard indicator, which we pass on
456 * as an empty string (by virtue of the bzero).
457 */
458 int
smb_kmod_session_close(const char * client,const char * username)459 smb_kmod_session_close(const char *client, const char *username)
460 {
461 smb_ioc_session_t ioc;
462 int rc;
463
464 bzero(&ioc, sizeof (ioc));
465
466 if (client != NULL)
467 (void) strlcpy(ioc.client, client, MAXNAMELEN);
468 if (username != NULL)
469 (void) strlcpy(ioc.username, username, MAXNAMELEN);
470
471 rc = smb_kmod_ioctl(SMB_IOC_SESSION_CLOSE, &ioc.hdr, sizeof (ioc));
472 return (rc);
473 }
474
475 int
smb_kmod_file_close(uint32_t uniqid)476 smb_kmod_file_close(uint32_t uniqid)
477 {
478 smb_ioc_fileid_t ioc;
479 int rc;
480
481 bzero(&ioc, sizeof (ioc));
482 ioc.uniqid = uniqid;
483
484 rc = smb_kmod_ioctl(SMB_IOC_FILE_CLOSE, &ioc.hdr, sizeof (ioc));
485 return (rc);
486 }
487
488 void
smb_kmod_unbind(void)489 smb_kmod_unbind(void)
490 {
491 if (smbdrv_fd != -1) {
492 (void) close(smbdrv_fd);
493 smbdrv_fd = -1;
494 }
495 }
496
497 /*
498 * Note: The user-space smbd-d provides it own version of this function
499 * which directly calls the "kernel" module code (in user space).
500 */
501 int
smb_kmod_ioctl(int cmd,smb_ioc_header_t * ioc,uint32_t len)502 smb_kmod_ioctl(int cmd, smb_ioc_header_t *ioc, uint32_t len)
503 {
504 int rc = EINVAL;
505
506 ioc->version = SMB_IOC_VERSION;
507 ioc->cmd = cmd;
508 ioc->len = len;
509 ioc->crc = 0;
510 ioc->crc = smb_crc_gen((uint8_t *)ioc, sizeof (smb_ioc_header_t));
511
512 if (smbdrv_fd != -1) {
513 if (ioctl(smbdrv_fd, cmd, ioc) < 0)
514 rc = errno;
515 else
516 rc = 0;
517 }
518 return (rc);
519 }
520