xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_pipesvc.c (revision dd72704bd9e794056c558153663c739e2012d721)
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 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 			continue;
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 			continue;
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 static boolean_t
261 pipe_has_priv(ndr_pipe_t *np)
262 {
263 	ucred_t *uc = NULL;
264 	const priv_set_t *ps = NULL;
265 	boolean_t ret = B_FALSE;
266 	pid_t  clpid;
267 
268 	if (getpeerucred(np->np_fid, &uc) != 0) {
269 		smbd_report("pipesvc: getpeerucred err %d", errno);
270 		return (B_FALSE);
271 	}
272 	clpid = ucred_getpid(uc);
273 	if (clpid == 0) {
274 		/* in-kernel caller: OK */
275 		ret = B_TRUE;
276 		goto out;
277 	}
278 
279 	ps = ucred_getprivset(uc, PRIV_EFFECTIVE);
280 	if (ps == NULL) {
281 		smbd_report("pipesvc: ucred_getprivset failed");
282 		goto out;
283 	}
284 
285 	/*
286 	 * Otherwise require sys_smb priv.
287 	 */
288 	if (priv_ismember(ps, PRIV_SYS_SMB)) {
289 		ret = B_TRUE;
290 		goto out;
291 	}
292 
293 	if (smbd.s_debug) {
294 		smbd_report("pipesvc: non-privileged client "
295 		    "PID = %d UID = %d",
296 		    (int)clpid, ucred_getruid(uc));
297 	}
298 
299 out:
300 	/* ps is free'd with the ucred */
301 	if (uc != NULL)
302 		ucred_free(uc);
303 
304 	return (ret);
305 }
306 #endif
307 
308 static void *
309 pipesvc_worker(void *varg)
310 {
311 	XDR xdrs;
312 	smb_pipehdr_t phdr;
313 	ndr_pipe_t *np = varg;
314 	struct pipe_listener *pl = np->np_listener;
315 	void *buf = NULL;
316 	uint32_t status;
317 	ssize_t rc;
318 
319 	(void) mutex_lock(&pipesvc_mutex);
320 	if (pipesvc_workers_cur >= pipesvc_workers_max ||
321 	    pl->current >= pl->max_allowed) {
322 		(void) mutex_unlock(&pipesvc_mutex);
323 		status = NT_STATUS_PIPE_NOT_AVAILABLE;
324 		(void) send(np->np_fid, &status, sizeof (status), 0);
325 		goto out_free_np;
326 	}
327 	pipesvc_workers_cur++;
328 	pl->current++;
329 	if (pl->max_seen < pl->current)
330 		pl->max_seen = pl->current;
331 	(void) mutex_unlock(&pipesvc_mutex);
332 
333 	/*
334 	 * The smbsrv kmod sends us one initial message containing an
335 	 * XDR encoded smb_netuserinfo_t that we read and decode here,
336 	 * all unbeknownst to libmlrpc.
337 	 *
338 	 * Might be nice to enhance getpeerucred() so it can give us
339 	 * all the info smb_netuserinfo_t carries, and then use that,
340 	 * which would allow using a more generic RPC service.
341 	 */
342 	rc = pipe_recv(np, &phdr, sizeof (phdr));
343 	if (rc != 0) {
344 		smbd_report("pipesvc_worker, recv1: %d", rc);
345 		goto out_decr;
346 	}
347 	if (phdr.ph_magic != SMB_PIPE_HDR_MAGIC ||
348 	    phdr.ph_uilen > 8192) {
349 		smbd_report("pipesvc_worker, bad hdr");
350 		goto out_decr;
351 	}
352 	buf = malloc(phdr.ph_uilen);
353 	if (buf == NULL) {
354 		smbd_report("pipesvc_worker, alloc1 failed");
355 		goto out_decr;
356 	}
357 	rc = pipe_recv(np, buf, phdr.ph_uilen);
358 	if (rc != 0) {
359 		smbd_report("pipesvc_worker, recv2: %d", rc);
360 		goto out_decr;
361 	}
362 
363 	xdrmem_create(&xdrs, buf, phdr.ph_uilen, XDR_DECODE);
364 	if (!smb_netuserinfo_xdr(&xdrs, np->np_user)) {
365 		smbd_report("pipesvc_worker, bad uinfo");
366 		goto out_free_buf;
367 	}
368 
369 	/*
370 	 * Don't trust the netuserinfo unless the client side
371 	 * has the necessary privileges
372 	 */
373 #ifndef FKSMBD
374 	if (!pipe_has_priv(np)) {
375 		np->np_user->ui_flags = SMB_ATF_ANON;
376 	}
377 #endif
378 
379 	/*
380 	 * Later, could disallow opens of some pipes by
381 	 * anonymous users, etc.  For now, reply "OK".
382 	 */
383 	status = 0;
384 	rc = pipe_send(np, &status, sizeof (status));
385 	if (rc != 0) {
386 		smbd_report("pipesvc_worker, send1: %d", rc);
387 		goto out_free_buf;
388 	}
389 
390 	/*
391 	 * Run the RPC service loop worker, which
392 	 * returns when it sees the pipe close.
393 	 */
394 	ndr_pipe_worker(np);
395 
396 	xdrs.x_op = XDR_FREE;
397 	(void) smb_netuserinfo_xdr(&xdrs, np->np_user);
398 
399 out_free_buf:
400 	free(buf);
401 	xdr_destroy(&xdrs);
402 
403 out_decr:
404 	(void) mutex_lock(&pipesvc_mutex);
405 	pipesvc_workers_cur--;
406 	pl->current--;
407 	(void) mutex_unlock(&pipesvc_mutex);
408 
409 out_free_np:
410 	/* Cleanup what came in by varg. */
411 	(void) shutdown(np->np_fid, SHUT_RDWR);
412 	np_free(np);
413 	return (NULL);
414 }
415 
416 /*
417  * These are the transport get/put callback functions provided
418  * via the ndr_pipe_t object to the libmlrpc`ndr_pipe_worker.
419  * These are called only with known PDU sizes and should
420  * loop as needed to transfer the entire message.
421  */
422 static int
423 pipe_recv(ndr_pipe_t *np, void *buf, size_t len)
424 {
425 	int x;
426 
427 	while (len > 0) {
428 		x = recv(np->np_fid, buf, len, 0);
429 		if (x < 0)
430 			return (errno);
431 		if (x == 0)
432 			return (EIO);
433 		buf = (char *)buf + x;
434 		len -= x;
435 	}
436 
437 	return (0);
438 }
439 
440 static int
441 pipe_send(ndr_pipe_t *np, void *buf, size_t len)
442 {
443 	int x;
444 
445 	while (len > 0) {
446 		x = send(np->np_fid, buf, len, 0);
447 		if (x < 0)
448 			return (errno);
449 		if (x == 0)
450 			return (EIO);
451 		buf = (char *)buf + x;
452 		len -= x;
453 	}
454 
455 	return (0);
456 }
457