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