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