xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_pipesvc.c (revision 2a6e99a0f1f7d22c0396e8b2ce9b9babbd1056cf)
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  */
15 
16 /*
17  * This is the named pipe service for smbd.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 
23 #include <stdio.h>
24 #include <strings.h>
25 #include <stdlib.h>
26 #include <synch.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <door.h>
30 #include <errno.h>
31 #include <pthread.h>
32 #include <signal.h>
33 
34 #include <smbsrv/libsmb.h>
35 #include <smbsrv/libmlsvc.h>
36 #include <smbsrv/smb_xdr.h>
37 #include "smbd.h"
38 
39 struct pipe_listener {
40 	const char *name;
41 	int max_allowed;
42 	int max_seen;
43 	int current;
44 	pthread_t tid;
45 };
46 
47 static void *pipesvc_listener(void *);
48 static void *pipesvc_worker(void *);
49 static int pipe_send(ndr_pipe_t *, void *, size_t);
50 static int pipe_recv(ndr_pipe_t *, void *, size_t);
51 
52 mutex_t  pipesvc_mutex = DEFAULTMUTEX;
53 int pipesvc_workers_max = 500;
54 int pipesvc_workers_cur = 0;
55 
56 uint16_t pipe_max_msgsize = SMB_PIPE_MAX_MSGSIZE;
57 
58 /*
59  * Allow more opens on SRVSVC because that's used by many clients
60  * to get the share list, etc.
61  */
62 #define	SRVSVC_MAX_OPENS	200
63 #define	DEF_MAX_OPENS		50
64 
65 #define	NLISTENERS	11
66 static struct pipe_listener
67 pipe_listeners[NLISTENERS] = {
68 	{ "eventlog",	DEF_MAX_OPENS, 0, 0 },
69 	{ "lsarpc",	DEF_MAX_OPENS, 0, 0 },
70 	{ "lsass",	DEF_MAX_OPENS, 0, 0 },
71 	{ "netdfs",	DEF_MAX_OPENS, 0, 0 },
72 	{ "netlogon",	DEF_MAX_OPENS, 0, 0 },
73 	{ "samr",	DEF_MAX_OPENS, 0, 0 },
74 	{ "spoolss",	DEF_MAX_OPENS, 0, 0 },
75 	{ "srvsvc",	SRVSVC_MAX_OPENS, 0, 0 },
76 	{ "svcctl",	DEF_MAX_OPENS, 0, 0 },
77 	{ "winreg",	DEF_MAX_OPENS, 0, 0 },
78 	{ "wkssvc",	DEF_MAX_OPENS, 0, 0 },
79 };
80 
81 static ndr_pipe_t *
82 np_new(struct pipe_listener *pl, int fid)
83 {
84 	ndr_pipe_t *np;
85 	size_t len;
86 
87 	/*
88 	 * Allocating ndr_pipe_t + smb_netuserinfo_t as one.
89 	 * We could just make that part of ndr_pipe_t, but
90 	 * that struct is opaque to libmlrpc.
91 	 */
92 	len = sizeof (*np) + sizeof (smb_netuserinfo_t);
93 	np = malloc(len);
94 	if (np == NULL)
95 		return (NULL);
96 
97 	bzero(np, len);
98 	np->np_listener = pl;
99 	np->np_endpoint = pl->name;
100 	np->np_user = (void*)(np + 1);
101 	np->np_send = pipe_send;
102 	np->np_recv = pipe_recv;
103 	np->np_fid = fid;
104 	np->np_max_xmit_frag = pipe_max_msgsize;
105 	np->np_max_recv_frag = pipe_max_msgsize;
106 
107 	return (np);
108 }
109 
110 static void
111 np_free(ndr_pipe_t *np)
112 {
113 	(void) close(np->np_fid);
114 	free(np);
115 }
116 
117 /*
118  * Create the smbd opipe door service.
119  * Returns the door descriptor on success.  Otherwise returns -1.
120  */
121 int
122 smbd_pipesvc_start(void)
123 {
124 	pthread_t tid;
125 	pthread_attr_t tattr;
126 	struct pipe_listener *pl;
127 	int i, rc;
128 
129 	if (mlsvc_init() != 0) {
130 		smbd_report("msrpc initialization failed");
131 		return (-1);
132 	}
133 
134 	(void) pthread_attr_init(&tattr);
135 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
136 
137 	for (i = 0; i < NLISTENERS; i++) {
138 		pl = &pipe_listeners[i];
139 		pl->max_seen = 0;
140 
141 		if (strcasecmp(pl->name, "spoolss") == 0 &&
142 		    smb_config_getbool(SMB_CI_PRINT_ENABLE) == B_FALSE)
143 			continue;
144 
145 		rc = pthread_create(&tid, &tattr, pipesvc_listener, pl);
146 		if (rc != 0)
147 			break;
148 		pipe_listeners[i].tid = tid;
149 	}
150 
151 	if (rc != 0) {
152 		smbd_report("pipesvc pthread_create, %d", rc);
153 	}
154 
155 	(void) pthread_attr_destroy(&tattr);
156 
157 	return (rc);
158 }
159 
160 void
161 smbd_pipesvc_stop(void)
162 {
163 	int i;
164 
165 	(void) mutex_lock(&pipesvc_mutex);
166 	for (i = 0; i < NLISTENERS; i++) {
167 		if (pipe_listeners[i].tid == 0)
168 			continue;
169 		(void) pthread_kill(pipe_listeners[i].tid, SIGTERM);
170 		pipe_listeners[i].tid = 0;
171 	}
172 	(void) mutex_unlock(&pipesvc_mutex);
173 }
174 
175 static void *
176 pipesvc_listener(void *varg)
177 {
178 	struct sockaddr_un sa;
179 	int err, listen_fd, newfd, snlen;
180 	struct pipe_listener *pl = varg;
181 	ndr_pipe_t *np;
182 	pthread_t tid;
183 	int rc;
184 
185 	listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
186 	if (listen_fd < 0) {
187 		smbd_report("pipesvc_listener, so_create: %d", errno);
188 		return (NULL);
189 	}
190 
191 	bzero(&sa, sizeof (sa));
192 	sa.sun_family = AF_UNIX;
193 	(void) snprintf(sa.sun_path, sizeof (sa.sun_path),
194 	    "%s/%s", SMB_PIPE_DIR, pl->name);
195 
196 	/* Bind it to a listening name. */
197 	(void) unlink(sa.sun_path);
198 	if (bind(listen_fd, (struct sockaddr *)&sa, sizeof (sa)) < 0) {
199 		smbd_report("pipesvc_listener, so_bind: %d", errno);
200 		(void) close(listen_fd);
201 		return (NULL);
202 	}
203 
204 	if (listen(listen_fd, SOMAXCONN) < 0) {
205 		smbd_report("pipesvc_listener, listen: %d", errno);
206 		(void) close(listen_fd);
207 		return (NULL);
208 	}
209 
210 	for (;;) {
211 
212 		snlen = sizeof (sa);
213 		newfd = accept(listen_fd, (struct sockaddr *)&sa, &snlen);
214 		if (newfd < 0) {
215 			err = errno;
216 			switch (err) {
217 			case ECONNABORTED:
218 				continue;
219 			case EINTR:
220 				/* normal termination */
221 				goto out;
222 			default:
223 				smbd_report("pipesvc_listener, "
224 				    "accept failed: %d", errno);
225 			}
226 			smbd_report("pipesvc_listener, accept: %d", err);
227 			break;
228 		}
229 
230 		np = np_new(pl, newfd);
231 		if (np == NULL) {
232 			smbd_report("pipesvc_listener, alloc1 failed");
233 			(void) close(newfd);
234 			continue;
235 		}
236 
237 		rc = pthread_create(&tid, NULL, pipesvc_worker, np);
238 		if (rc != 0) {
239 			smbd_report("pipesvc_listener, pthread_create: %d",
240 			    errno);
241 			np_free(np);
242 			continue;
243 		}
244 		(void) pthread_detach(tid);
245 
246 		/* Note: np_free in pipesvc_worker */
247 		np = NULL;
248 	}
249 
250 out:
251 	(void) close(listen_fd);
252 	pl->tid = 0;
253 	return (NULL);
254 }
255 
256 static void *
257 pipesvc_worker(void *varg)
258 {
259 	XDR xdrs;
260 	smb_pipehdr_t phdr;
261 	ndr_pipe_t *np = varg;
262 	struct pipe_listener *pl = np->np_listener;
263 	void *buf = NULL;
264 	uint32_t status;
265 	ssize_t rc;
266 
267 	(void) mutex_lock(&pipesvc_mutex);
268 	if (pipesvc_workers_cur >= pipesvc_workers_max ||
269 	    pl->current >= pl->max_allowed) {
270 		(void) mutex_unlock(&pipesvc_mutex);
271 		status = NT_STATUS_PIPE_NOT_AVAILABLE;
272 		(void) send(np->np_fid, &status, sizeof (status), 0);
273 		goto out_free_np;
274 	}
275 	pipesvc_workers_cur++;
276 	pl->current++;
277 	if (pl->max_seen < pl->current)
278 		pl->max_seen = pl->current;
279 	(void) mutex_unlock(&pipesvc_mutex);
280 
281 	/*
282 	 * The smbsrv kmod sends us one initial message containing an
283 	 * XDR encoded smb_netuserinfo_t that we read and decode here,
284 	 * all unbeknownst to libmlrpc.
285 	 *
286 	 * Might be nice to enhance getpeerucred() so it can give us
287 	 * all the info smb_netuserinfo_t carries, and then use that,
288 	 * which would allow using a more generic RPC service.
289 	 */
290 	rc = pipe_recv(np, &phdr, sizeof (phdr));
291 	if (rc != 0) {
292 		smbd_report("pipesvc_worker, recv1: %d", rc);
293 		goto out_decr;
294 	}
295 	if (phdr.ph_magic != SMB_PIPE_HDR_MAGIC ||
296 	    phdr.ph_uilen > 8192) {
297 		smbd_report("pipesvc_worker, bad hdr");
298 		goto out_decr;
299 	}
300 	buf = malloc(phdr.ph_uilen);
301 	if (buf == NULL) {
302 		smbd_report("pipesvc_worker, alloc1 failed");
303 		goto out_decr;
304 	}
305 	rc = pipe_recv(np, buf, phdr.ph_uilen);
306 	if (rc != 0) {
307 		smbd_report("pipesvc_worker, recv2: %d", rc);
308 		goto out_decr;
309 	}
310 
311 	xdrmem_create(&xdrs, buf, phdr.ph_uilen, XDR_DECODE);
312 	if (!smb_netuserinfo_xdr(&xdrs, np->np_user)) {
313 		smbd_report("pipesvc_worker, bad uinfo");
314 		goto out_free_buf;
315 	}
316 
317 	/*
318 	 * Later, could disallow opens of some pipes by
319 	 * anonymous users, etc.  For now, reply "OK".
320 	 */
321 	status = 0;
322 	rc = pipe_send(np, &status, sizeof (status));
323 	if (rc != 0) {
324 		smbd_report("pipesvc_worker, send1: %d", rc);
325 		goto out_free_buf;
326 	}
327 
328 	/*
329 	 * Run the RPC service loop worker, which
330 	 * returns when it sees the pipe close.
331 	 */
332 	ndr_pipe_worker(np);
333 
334 	xdrs.x_op = XDR_FREE;
335 	(void) smb_netuserinfo_xdr(&xdrs, np->np_user);
336 
337 out_free_buf:
338 	free(buf);
339 	xdr_destroy(&xdrs);
340 
341 out_decr:
342 	(void) mutex_lock(&pipesvc_mutex);
343 	pipesvc_workers_cur--;
344 	pl->current--;
345 	(void) mutex_unlock(&pipesvc_mutex);
346 
347 out_free_np:
348 	/* Cleanup what came in by varg. */
349 	(void) shutdown(np->np_fid, SHUT_RDWR);
350 	np_free(np);
351 	return (NULL);
352 }
353 
354 /*
355  * These are the transport get/put callback functions provided
356  * via the ndr_pipe_t object to the libmlrpc`ndr_pipe_worker.
357  * These are called only with known PDU sizes and should
358  * loop as needed to transfer the entire message.
359  */
360 static int
361 pipe_recv(ndr_pipe_t *np, void *buf, size_t len)
362 {
363 	int x;
364 
365 	while (len > 0) {
366 		x = recv(np->np_fid, buf, len, 0);
367 		if (x < 0)
368 			return (errno);
369 		if (x == 0)
370 			return (EIO);
371 		buf = (char *)buf + x;
372 		len -= x;
373 	}
374 
375 	return (0);
376 }
377 
378 static int
379 pipe_send(ndr_pipe_t *np, void *buf, size_t len)
380 {
381 	int x;
382 
383 	while (len > 0) {
384 		x = send(np->np_fid, buf, len, 0);
385 		if (x < 0)
386 			return (errno);
387 		if (x == 0)
388 			return (EIO);
389 		buf = (char *)buf + x;
390 		len -= x;
391 	}
392 
393 	return (0);
394 }
395