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