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