xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_kmod.c (revision e77c795bcbe51aebd7579fe13cbf2a6d56eca47f)
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 boolean_t
67 smb_kmod_isbound(void)
68 {
69 	return ((smbdrv_fd == -1) ? B_FALSE : B_TRUE);
70 }
71 
72 int
73 smb_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 
100 int
101 smb_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 
110 int
111 smb_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 
121 void
122 smb_kmod_stop(void)
123 {
124 	smb_ioc_header_t ioc;
125 
126 	(void) smb_kmod_ioctl(SMB_IOC_STOP, &ioc, sizeof (ioc));
127 }
128 
129 int
130 smb_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 
138 int
139 smb_kmod_tcplisten(int error)
140 {
141 	smb_ioc_listen_t ioc;
142 
143 	ioc.error = error;
144 	return (smb_kmod_ioctl(SMB_IOC_TCP_LISTEN, &ioc.hdr, sizeof (ioc)));
145 }
146 
147 int
148 smb_kmod_nbtlisten(int error)
149 {
150 	smb_ioc_listen_t ioc;
151 
152 	ioc.error = error;
153 	return (smb_kmod_ioctl(SMB_IOC_NBT_LISTEN, &ioc.hdr, sizeof (ioc)));
154 }
155 
156 int
157 smb_kmod_tcpreceive(void)
158 {
159 	smb_ioc_header_t ioc;
160 
161 	return (smb_kmod_ioctl(SMB_IOC_TCP_RECEIVE, &ioc, sizeof (ioc)));
162 }
163 
164 int
165 smb_kmod_nbtreceive(void)
166 {
167 	smb_ioc_header_t ioc;
168 
169 	return (smb_kmod_ioctl(SMB_IOC_NBT_RECEIVE, &ioc, sizeof (ioc)));
170 }
171 
172 int
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
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
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 int
241 smb_kmod_get_open_num(smb_opennum_t *opennum)
242 {
243 	smb_ioc_opennum_t ioc;
244 	int rc;
245 
246 	bzero(&ioc, sizeof (ioc));
247 	ioc.qualtype = opennum->qualtype;
248 	(void) strlcpy(ioc.qualifier, opennum->qualifier, MAXNAMELEN);
249 
250 	rc = smb_kmod_ioctl(SMB_IOC_NUMOPEN, &ioc.hdr, sizeof (ioc));
251 	if (rc == 0) {
252 		opennum->open_users = ioc.open_users;
253 		opennum->open_trees = ioc.open_trees;
254 		opennum->open_files = ioc.open_files;
255 	}
256 
257 	return (rc);
258 }
259 
260 int
261 smb_kmod_get_spool_doc(uint32_t *spool_num, char *username,
262     char *path, smb_inaddr_t *ipaddr)
263 {
264 	smb_ioc_spooldoc_t ioc;
265 	int rc;
266 
267 	bzero(&ioc, sizeof (ioc));
268 	rc = smb_kmod_ioctl(SMB_IOC_SPOOLDOC, &ioc.hdr, sizeof (ioc));
269 	if (rc == 0) {
270 		*spool_num = ioc.spool_num;
271 		(void) strlcpy(username, ioc.username, MAXNAMELEN);
272 		(void) strlcpy(path, ioc.path, MAXPATHLEN);
273 		*ipaddr = ioc.ipaddr;
274 	}
275 	return (rc);
276 }
277 
278 /*
279  * Initialization for an smb_kmod_enum request.  If this call succeeds,
280  * smb_kmod_enum_fini() must be called later to deallocate resources.
281  */
282 smb_netsvc_t *
283 smb_kmod_enum_init(smb_svcenum_t *request)
284 {
285 	smb_netsvc_t		*ns;
286 	smb_svcenum_t		*svcenum;
287 	smb_ioc_svcenum_t	*ioc;
288 	uint32_t		ioclen;
289 
290 	if ((ns = calloc(1, sizeof (smb_netsvc_t))) == NULL)
291 		return (NULL);
292 
293 	ioclen = sizeof (smb_ioc_svcenum_t) + SMB_IOC_DATA_SIZE;
294 	if ((ioc = malloc(ioclen)) == NULL) {
295 		free(ns);
296 		return (NULL);
297 	}
298 
299 	bzero(ioc, ioclen);
300 	svcenum = &ioc->svcenum;
301 	svcenum->se_type   = request->se_type;
302 	svcenum->se_level  = request->se_level;
303 	svcenum->se_bavail = SMB_IOC_DATA_SIZE;
304 	svcenum->se_nlimit = request->se_nlimit;
305 	svcenum->se_nskip = request->se_nskip;
306 	svcenum->se_buflen = SMB_IOC_DATA_SIZE;
307 
308 	list_create(&ns->ns_list, sizeof (smb_netsvcitem_t),
309 	    offsetof(smb_netsvcitem_t, nsi_lnd));
310 
311 	ns->ns_ioc = ioc;
312 	ns->ns_ioclen = ioclen;
313 	return (ns);
314 }
315 
316 /*
317  * Cleanup resources allocated via smb_kmod_enum_init and smb_kmod_enum.
318  */
319 void
320 smb_kmod_enum_fini(smb_netsvc_t *ns)
321 {
322 	list_t			*lst;
323 	smb_netsvcitem_t	*item;
324 	smb_netuserinfo_t	*user;
325 	smb_netconnectinfo_t	*tree;
326 	smb_netfileinfo_t	*ofile;
327 	uint32_t		se_type;
328 
329 	if (ns == NULL)
330 		return;
331 
332 	lst = &ns->ns_list;
333 	se_type = ns->ns_ioc->svcenum.se_type;
334 
335 	while ((item = list_head(lst)) != NULL) {
336 		list_remove(lst, item);
337 
338 		switch (se_type) {
339 		case SMB_SVCENUM_TYPE_USER:
340 			user = &item->nsi_un.nsi_user;
341 			free(user->ui_domain);
342 			free(user->ui_account);
343 			free(user->ui_workstation);
344 			break;
345 		case SMB_SVCENUM_TYPE_TREE:
346 			tree = &item->nsi_un.nsi_tree;
347 			free(tree->ci_username);
348 			free(tree->ci_share);
349 			break;
350 		case SMB_SVCENUM_TYPE_FILE:
351 			ofile = &item->nsi_un.nsi_ofile;
352 			free(ofile->fi_path);
353 			free(ofile->fi_username);
354 			break;
355 		default:
356 			break;
357 		}
358 	}
359 
360 	list_destroy(&ns->ns_list);
361 	free(ns->ns_items);
362 	free(ns->ns_ioc);
363 	free(ns);
364 }
365 
366 /*
367  * Enumerate users, connections or files.
368  */
369 int
370 smb_kmod_enum(smb_netsvc_t *ns)
371 {
372 	smb_ioc_svcenum_t	*ioc;
373 	uint32_t		ioclen;
374 	smb_svcenum_t		*svcenum;
375 	smb_netsvcitem_t	*items;
376 	smb_netuserinfo_t	*user;
377 	smb_netconnectinfo_t	*tree;
378 	smb_netfileinfo_t	*ofile;
379 	uint8_t			*data;
380 	uint32_t		len;
381 	uint32_t		se_type;
382 	uint_t			nbytes;
383 	int			i;
384 	int			rc;
385 
386 	ioc = ns->ns_ioc;
387 	ioclen = ns->ns_ioclen;
388 	rc = smb_kmod_ioctl(SMB_IOC_SVCENUM, &ioc->hdr, ioclen);
389 	if (rc != 0)
390 		return (rc);
391 
392 	svcenum = &ioc->svcenum;
393 	items = calloc(svcenum->se_nitems, sizeof (smb_netsvcitem_t));
394 	if (items == NULL)
395 		return (ENOMEM);
396 
397 	ns->ns_items = items;
398 	se_type = ns->ns_ioc->svcenum.se_type;
399 	data = svcenum->se_buf;
400 	len = svcenum->se_bused;
401 
402 	for (i = 0; i < svcenum->se_nitems; ++i) {
403 		switch (se_type) {
404 		case SMB_SVCENUM_TYPE_USER:
405 			user = &items->nsi_un.nsi_user;
406 			rc = smb_netuserinfo_decode(user, data, len, &nbytes);
407 			break;
408 		case SMB_SVCENUM_TYPE_TREE:
409 			tree = &items->nsi_un.nsi_tree;
410 			rc = smb_netconnectinfo_decode(tree, data, len,
411 			    &nbytes);
412 			break;
413 		case SMB_SVCENUM_TYPE_FILE:
414 			ofile = &items->nsi_un.nsi_ofile;
415 			rc = smb_netfileinfo_decode(ofile, data, len, &nbytes);
416 			break;
417 		default:
418 			rc = -1;
419 			break;
420 		}
421 
422 		if (rc != 0)
423 			return (EINVAL);
424 
425 		list_insert_tail(&ns->ns_list, items);
426 
427 		++items;
428 		data += nbytes;
429 		len -= nbytes;
430 	}
431 
432 	return (0);
433 }
434 
435 /*
436  * A NULL pointer is a wildcard indicator, which we pass on
437  * as an empty string (by virtue of the bzero).
438  */
439 int
440 smb_kmod_session_close(const char *client, const char *username)
441 {
442 	smb_ioc_session_t ioc;
443 	int rc;
444 
445 	bzero(&ioc, sizeof (ioc));
446 
447 	if (client != NULL)
448 		(void) strlcpy(ioc.client, client, MAXNAMELEN);
449 	if (username != NULL)
450 		(void) strlcpy(ioc.username, username, MAXNAMELEN);
451 
452 	rc = smb_kmod_ioctl(SMB_IOC_SESSION_CLOSE, &ioc.hdr, sizeof (ioc));
453 	return (rc);
454 }
455 
456 int
457 smb_kmod_file_close(uint32_t uniqid)
458 {
459 	smb_ioc_fileid_t ioc;
460 	int rc;
461 
462 	bzero(&ioc, sizeof (ioc));
463 	ioc.uniqid = uniqid;
464 
465 	rc = smb_kmod_ioctl(SMB_IOC_FILE_CLOSE, &ioc.hdr, sizeof (ioc));
466 	return (rc);
467 }
468 
469 void
470 smb_kmod_unbind(void)
471 {
472 	if (smbdrv_fd != -1) {
473 		(void) close(smbdrv_fd);
474 		smbdrv_fd = -1;
475 	}
476 }
477 
478 static int
479 smb_kmod_ioctl(int cmd, smb_ioc_header_t *ioc, uint32_t len)
480 {
481 	int rc = EINVAL;
482 
483 	ioc->version = SMB_IOC_VERSION;
484 	ioc->cmd = cmd;
485 	ioc->len = len;
486 	ioc->crc = 0;
487 	ioc->crc = smb_crc_gen((uint8_t *)ioc, sizeof (smb_ioc_header_t));
488 
489 	if (smbdrv_fd != -1) {
490 		if (ioctl(smbdrv_fd, cmd, ioc) < 0)
491 			rc = errno;
492 		else
493 			rc = 0;
494 	}
495 	return (rc);
496 }
497