xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_pipesvc.c (revision 201ceb75ab95f9bf1f42ea1dc9ab363b43ba47cf)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
14  * Copyright 2022-2023 RackTop Systems, Inc.
15  */
16 
17 /*
18  * This is the named pipe service for smbd.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 
24 #include <stdio.h>
25 #include <strings.h>
26 #include <stdlib.h>
27 #include <synch.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <door.h>
31 #include <errno.h>
32 #include <pthread.h>
33 #include <signal.h>
34 #include <ucred.h>
35 #include <priv.h>
36 
37 #include <smbsrv/libsmb.h>
38 #include <smbsrv/libmlsvc.h>
39 #include <smbsrv/smb_xdr.h>
40 #include "smbd.h"
41 
42 struct pipe_listener {
43 	const char *name;
44 	int max_allowed;
45 	int max_seen;
46 	int current;
47 	pthread_t tid;
48 };
49 
50 static void *pipesvc_listener(void *);
51 static void *pipesvc_worker(void *);
52 static int pipe_send(ndr_pipe_t *, void *, size_t);
53 static int pipe_recv(ndr_pipe_t *, void *, size_t);
54 
55 mutex_t  pipesvc_mutex = DEFAULTMUTEX;
56 int pipesvc_workers_max = 500;
57 int pipesvc_workers_cur = 0;
58 
59 uint16_t pipe_max_msgsize = SMB_PIPE_MAX_MSGSIZE;
60 
61 /*
62  * Allow more opens on SRVSVC because that's used by many clients
63  * to get the share list, etc.
64  */
65 #define	SRVSVC_MAX_OPENS	200
66 #define	DEF_MAX_OPENS		50
67 
68 #define	NLISTENERS	11
69 static struct pipe_listener
70 pipe_listeners[NLISTENERS] = {
71 	{ "eventlog",	DEF_MAX_OPENS, 0, 0 },
72 	{ "lsarpc",	DEF_MAX_OPENS, 0, 0 },
73 	{ "lsass",	DEF_MAX_OPENS, 0, 0 },
74 	{ "netdfs",	DEF_MAX_OPENS, 0, 0 },
75 	{ "netlogon",	DEF_MAX_OPENS, 0, 0 },
76 	{ "samr",	DEF_MAX_OPENS, 0, 0 },
77 	{ "spoolss",	DEF_MAX_OPENS, 0, 0 },
78 	{ "srvsvc",	SRVSVC_MAX_OPENS, 0, 0 },
79 	{ "svcctl",	DEF_MAX_OPENS, 0, 0 },
80 	{ "winreg",	DEF_MAX_OPENS, 0, 0 },
81 	{ "wkssvc",	DEF_MAX_OPENS, 0, 0 },
82 };
83 
84 static ndr_pipe_t *
85 np_new(struct pipe_listener *pl, int fid)
86 {
87 	ndr_pipe_t *np;
88 	size_t len;
89 
90 	/*
91 	 * Allocating ndr_pipe_t + smb_netuserinfo_t as one.
92 	 * We could just make that part of ndr_pipe_t, but
93 	 * that struct is opaque to libmlrpc.
94 	 */
95 	len = sizeof (*np) + sizeof (smb_netuserinfo_t);
96 	np = malloc(len);
97 	if (np == NULL)
98 		return (NULL);
99 
100 	bzero(np, len);
101 	np->np_listener = pl;
102 	np->np_endpoint = pl->name;
103 	np->np_user = (void*)(np + 1);
104 	np->np_send = pipe_send;
105 	np->np_recv = pipe_recv;
106 	np->np_fid = fid;
107 	np->np_max_xmit_frag = pipe_max_msgsize;
108 	np->np_max_recv_frag = pipe_max_msgsize;
109 
110 	return (np);
111 }
112 
113 static void
114 np_free(ndr_pipe_t *np)
115 {
116 	(void) close(np->np_fid);
117 	free(np);
118 }
119 
120 /*
121  * Create the smbd opipe door service.
122  * Returns the door descriptor on success.  Otherwise returns -1.
123  */
124 int
125 smbd_pipesvc_start(void)
126 {
127 	pthread_t tid;
128 	pthread_attr_t tattr;
129 	struct pipe_listener *pl;
130 	int i, rc;
131 
132 	if (mlsvc_init() != 0) {
133 		smbd_report("msrpc initialization failed");
134 		return (-1);
135 	}
136 
137 	(void) pthread_attr_init(&tattr);
138 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
139 
140 	for (i = 0; i < NLISTENERS; i++) {
141 		pl = &pipe_listeners[i];
142 		pl->max_seen = 0;
143 
144 		if (strcasecmp(pl->name, "spoolss") == 0 &&
145 		    smb_config_getbool(SMB_CI_PRINT_ENABLE) == B_FALSE)
146 			continue;
147 
148 		rc = pthread_create(&tid, &tattr, pipesvc_listener, pl);
149 		if (rc != 0)
150 			break;
151 		pipe_listeners[i].tid = tid;
152 	}
153 
154 	if (rc != 0) {
155 		smbd_report("pipesvc pthread_create, %d", rc);
156 	}
157 
158 	(void) pthread_attr_destroy(&tattr);
159 
160 	return (rc);
161 }
162 
163 void
164 smbd_pipesvc_stop(void)
165 {
166 	int i;
167 
168 	(void) mutex_lock(&pipesvc_mutex);
169 	for (i = 0; i < NLISTENERS; i++) {
170 		if (pipe_listeners[i].tid == 0)
171 			continue;
172 		(void) pthread_kill(pipe_listeners[i].tid, SIGTERM);
173 		pipe_listeners[i].tid = 0;
174 	}
175 	(void) mutex_unlock(&pipesvc_mutex);
176 }
177 
178 static void *
179 pipesvc_listener(void *varg)
180 {
181 	struct sockaddr_un sa;
182 	int err, listen_fd, newfd, snlen;
183 	struct pipe_listener *pl = varg;
184 	ndr_pipe_t *np;
185 	pthread_t tid;
186 	int rc;
187 
188 	listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
189 	if (listen_fd < 0) {
190 		smbd_report("pipesvc_listener, so_create: %d", errno);
191 		return (NULL);
192 	}
193 
194 	bzero(&sa, sizeof (sa));
195 	sa.sun_family = AF_UNIX;
196 	(void) snprintf(sa.sun_path, sizeof (sa.sun_path),
197 	    "%s/%s", SMB_PIPE_DIR, pl->name);
198 
199 	/* Bind it to a listening name. */
200 	(void) unlink(sa.sun_path);
201 	if (bind(listen_fd, (struct sockaddr *)&sa, sizeof (sa)) < 0) {
202 		smbd_report("pipesvc_listener, so_bind: %d", errno);
203 		(void) close(listen_fd);
204 		return (NULL);
205 	}
206 
207 	if (listen(listen_fd, SOMAXCONN) < 0) {
208 		smbd_report("pipesvc_listener, listen: %d", errno);
209 		(void) close(listen_fd);
210 		return (NULL);
211 	}
212 
213 	for (;;) {
214 
215 		snlen = sizeof (sa);
216 		newfd = accept(listen_fd, (struct sockaddr *)&sa, &snlen);
217 		if (newfd < 0) {
218 			err = errno;
219 			switch (err) {
220 			case ECONNABORTED:
221 				continue;
222 			case EINTR:
223 				/* normal termination */
224 				goto out;
225 			default:
226 				smbd_report("pipesvc_listener, "
227 				    "accept failed: %d", errno);
228 			}
229 			smbd_report("pipesvc_listener, accept: %d", err);
230 			break;
231 		}
232 
233 		np = np_new(pl, newfd);
234 		if (np == NULL) {
235 			smbd_report("pipesvc_listener, alloc1 failed");
236 			(void) close(newfd);
237 			smbd_nomem();
238 		}
239 
240 		rc = pthread_create(&tid, NULL, pipesvc_worker, np);
241 		if (rc != 0) {
242 			smbd_report("pipesvc_listener, pthread_create: %d",
243 			    errno);
244 			np_free(np);
245 			smbd_nomem();
246 		}
247 		(void) pthread_detach(tid);
248 
249 		/* Note: np_free in pipesvc_worker */
250 		np = NULL;
251 	}
252 
253 out:
254 	(void) close(listen_fd);
255 	pl->tid = 0;
256 	return (NULL);
257 }
258 
259 #ifndef FKSMBD
260 /*
261  * Decide whether we should trust the (in-band) user information
262  * that the client sends us over the named pipe.  The (in-kernel)
263  * SMB service calls this with the credential of the logged-on
264  * SMB user.  The privileges are normally:
265  *	  effective: basic,file_dac_search
266  *	inheritable: basic
267  *	  permitted: basic,file_dac_search,sys_smb
268  *	      limit: all
269  * This tests the permitted set for the presence of PRIV_SYS_SMB,
270  * which should only be granted to the SMB server.
271  */
272 static boolean_t
273 pipe_has_priv(ndr_pipe_t *np)
274 {
275 	ucred_t *uc = NULL;
276 	const priv_set_t *ps = NULL;
277 	boolean_t ret = B_FALSE;
278 	pid_t  clpid;
279 
280 	if (getpeerucred(np->np_fid, &uc) != 0) {
281 		smbd_report("pipesvc: getpeerucred err %d", errno);
282 		return (B_FALSE);
283 	}
284 	clpid = ucred_getpid(uc);
285 	ps = ucred_getprivset(uc, PRIV_PERMITTED);
286 	if (ps == NULL) {
287 		smbd_report("pipesvc: ucred_getprivset failed");
288 		goto out;
289 	}
290 
291 	/*
292 	 * Require sys_smb priv.
293 	 */
294 	if (priv_ismember(ps, PRIV_SYS_SMB)) {
295 		ret = B_TRUE;
296 		goto out;
297 	}
298 
299 	if (smbd.s_debug) {
300 		smbd_report("pipesvc: non-privileged client "
301 		    "PID = %d UID = %d",
302 		    (int)clpid, ucred_getruid(uc));
303 	}
304 
305 out:
306 	/* ps is free'd with the ucred */
307 	if (uc != NULL)
308 		ucred_free(uc);
309 
310 	return (ret);
311 }
312 #endif
313 
314 static void *
315 pipesvc_worker(void *varg)
316 {
317 	XDR xdrs;
318 	smb_pipehdr_t phdr;
319 	ndr_pipe_t *np = varg;
320 	struct pipe_listener *pl = np->np_listener;
321 	void *buf = NULL;
322 	uint32_t status;
323 	ssize_t rc;
324 
325 	(void) mutex_lock(&pipesvc_mutex);
326 	if (pipesvc_workers_cur >= pipesvc_workers_max ||
327 	    pl->current >= pl->max_allowed) {
328 		(void) mutex_unlock(&pipesvc_mutex);
329 		status = NT_STATUS_PIPE_NOT_AVAILABLE;
330 		(void) send(np->np_fid, &status, sizeof (status), 0);
331 		goto out_free_np;
332 	}
333 	pipesvc_workers_cur++;
334 	pl->current++;
335 	if (pl->max_seen < pl->current)
336 		pl->max_seen = pl->current;
337 	(void) mutex_unlock(&pipesvc_mutex);
338 
339 	/*
340 	 * The smbsrv kmod sends us one initial message containing an
341 	 * XDR encoded smb_netuserinfo_t that we read and decode here,
342 	 * all unbeknownst to libmlrpc.
343 	 *
344 	 * Might be nice to enhance getpeerucred() so it can give us
345 	 * all the info smb_netuserinfo_t carries, and then use that,
346 	 * which would allow using a more generic RPC service.
347 	 */
348 	rc = pipe_recv(np, &phdr, sizeof (phdr));
349 	if (rc != 0) {
350 		smbd_report("pipesvc_worker, recv1: %d", rc);
351 		goto out_decr;
352 	}
353 	if (phdr.ph_magic != SMB_PIPE_HDR_MAGIC ||
354 	    phdr.ph_uilen > 8192) {
355 		smbd_report("pipesvc_worker, bad hdr");
356 		goto out_decr;
357 	}
358 	buf = malloc(phdr.ph_uilen);
359 	if (buf == NULL) {
360 		smbd_report("pipesvc_worker, alloc1 failed");
361 		goto out_decr;
362 	}
363 	rc = pipe_recv(np, buf, phdr.ph_uilen);
364 	if (rc != 0) {
365 		smbd_report("pipesvc_worker, recv2: %d", rc);
366 		goto out_decr;
367 	}
368 
369 	xdrmem_create(&xdrs, buf, phdr.ph_uilen, XDR_DECODE);
370 	if (!smb_netuserinfo_xdr(&xdrs, np->np_user)) {
371 		smbd_report("pipesvc_worker, bad uinfo");
372 		goto out_free_buf;
373 	}
374 
375 	/*
376 	 * Don't trust the netuserinfo unless the client side
377 	 * has the necessary privileges
378 	 */
379 #ifndef FKSMBD
380 	if (!pipe_has_priv(np)) {
381 		np->np_user->ui_flags = SMB_ATF_ANON;
382 	}
383 #endif
384 
385 	/*
386 	 * Later, could disallow opens of some pipes by
387 	 * anonymous users, etc.  For now, reply "OK".
388 	 */
389 	status = 0;
390 	rc = pipe_send(np, &status, sizeof (status));
391 	if (rc != 0) {
392 		smbd_report("pipesvc_worker, send1: %d", rc);
393 		goto out_free_buf;
394 	}
395 
396 	/*
397 	 * Run the RPC service loop worker, which
398 	 * returns when it sees the pipe close.
399 	 */
400 	ndr_pipe_worker(np);
401 
402 	xdrs.x_op = XDR_FREE;
403 	(void) smb_netuserinfo_xdr(&xdrs, np->np_user);
404 
405 out_free_buf:
406 	free(buf);
407 	xdr_destroy(&xdrs);
408 
409 out_decr:
410 	(void) mutex_lock(&pipesvc_mutex);
411 	pipesvc_workers_cur--;
412 	pl->current--;
413 	(void) mutex_unlock(&pipesvc_mutex);
414 
415 out_free_np:
416 	/* Cleanup what came in by varg. */
417 	(void) shutdown(np->np_fid, SHUT_RDWR);
418 	np_free(np);
419 	return (NULL);
420 }
421 
422 /*
423  * These are the transport get/put callback functions provided
424  * via the ndr_pipe_t object to the libmlrpc`ndr_pipe_worker.
425  * These are called only with known PDU sizes and should
426  * loop as needed to transfer the entire message.
427  */
428 static int
429 pipe_recv(ndr_pipe_t *np, void *buf, size_t len)
430 {
431 	int x;
432 
433 	while (len > 0) {
434 		x = recv(np->np_fid, buf, len, 0);
435 		if (x < 0)
436 			return (errno);
437 		if (x == 0)
438 			return (EIO);
439 		buf = (char *)buf + x;
440 		len -= x;
441 	}
442 
443 	return (0);
444 }
445 
446 static int
447 pipe_send(ndr_pipe_t *np, void *buf, size_t len)
448 {
449 	int x;
450 
451 	while (len > 0) {
452 		x = send(np->np_fid, buf, len, 0);
453 		if (x < 0)
454 			return (errno);
455 		if (x == 0)
456 			return (EIO);
457 		buf = (char *)buf + x;
458 		len -= x;
459 	}
460 
461 	return (0);
462 }
463