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 *
np_new(struct pipe_listener * pl,int fid)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
np_free(ndr_pipe_t * np)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
smbd_pipesvc_start(void)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
smbd_pipesvc_stop(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 *
pipesvc_listener(void * varg)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 *
pipesvc_worker(void * varg)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
pipe_recv(ndr_pipe_t * np,void * buf,size_t len)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
pipe_send(ndr_pipe_t * np,void * buf,size_t len)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