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