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