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