Merge commit '42a3762d0138d6dd0c0f96964be98d0b21ab6bef'
[unleashed.git] / usr / src / cmd / smbsrv / smbd / smbd_pipesvc.c
blob2bff06d252abca9c29dbd4d3d0eb9c699fd1ff97
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.
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.
13 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
17 * This is the named pipe service for smbd.
20 #include <sys/types.h>
21 #include <sys/stat.h>
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>
34 #include <smbsrv/libsmb.h>
35 #include <smbsrv/libmlsvc.h>
36 #include <smbsrv/smb_xdr.h>
37 #include "smbd.h"
39 struct pipe_listener {
40 const char *name;
41 int max_allowed;
42 int max_seen;
43 int current;
44 pthread_t tid;
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);
52 mutex_t pipesvc_mutex = DEFAULTMUTEX;
53 int pipesvc_workers_max = 500;
54 int pipesvc_workers_cur = 0;
56 uint16_t pipe_max_msgsize = SMB_PIPE_MAX_MSGSIZE;
59 * Allow more opens on SRVSVC because that's used by many clients
60 * to get the share list, etc.
62 #define SRVSVC_MAX_OPENS 200
63 #define DEF_MAX_OPENS 50
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 },
81 static ndr_pipe_t *
82 np_new(struct pipe_listener *pl, int fid)
84 ndr_pipe_t *np;
85 size_t len;
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.
92 len = sizeof (*np) + sizeof (smb_netuserinfo_t);
93 np = malloc(len);
94 if (np == NULL)
95 return (NULL);
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;
107 return (np);
110 static void
111 np_free(ndr_pipe_t *np)
113 (void) close(np->np_fid);
114 free(np);
118 * Create the smbd opipe door service.
119 * Returns the door descriptor on success. Otherwise returns -1.
122 smbd_pipesvc_start(void)
124 pthread_t tid;
125 pthread_attr_t tattr;
126 struct pipe_listener *pl;
127 int i, rc;
129 if (mlsvc_init() != 0) {
130 smbd_report("msrpc initialization failed");
131 return (-1);
134 (void) pthread_attr_init(&tattr);
135 (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
137 for (i = 0; i < NLISTENERS; i++) {
138 pl = &pipe_listeners[i];
139 pl->max_seen = 0;
141 if (strcasecmp(pl->name, "spoolss") == 0 &&
142 smb_config_getbool(SMB_CI_PRINT_ENABLE) == B_FALSE)
143 continue;
145 rc = pthread_create(&tid, &tattr, pipesvc_listener, pl);
146 if (rc != 0)
147 break;
148 pipe_listeners[i].tid = tid;
151 if (rc != 0) {
152 smbd_report("pipesvc pthread_create, %d", rc);
155 (void) pthread_attr_destroy(&tattr);
157 return (rc);
160 void
161 smbd_pipesvc_stop(void)
163 int i;
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;
172 (void) mutex_unlock(&pipesvc_mutex);
175 static void *
176 pipesvc_listener(void *varg)
178 struct sockaddr_un sa;
179 int err, listen_fd, newfd;
180 socklen_t snlen;
181 struct pipe_listener *pl = varg;
182 ndr_pipe_t *np;
183 pthread_t tid;
184 int rc;
186 listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
187 if (listen_fd < 0) {
188 smbd_report("pipesvc_listener, so_create: %d", errno);
189 return (NULL);
192 bzero(&sa, sizeof (sa));
193 sa.sun_family = AF_UNIX;
194 (void) snprintf(sa.sun_path, sizeof (sa.sun_path),
195 "%s/%s", SMB_PIPE_DIR, pl->name);
197 /* Bind it to a listening name. */
198 (void) unlink(sa.sun_path);
199 if (bind(listen_fd, (struct sockaddr *)&sa, sizeof (sa)) < 0) {
200 smbd_report("pipesvc_listener, so_bind: %d", errno);
201 (void) close(listen_fd);
202 return (NULL);
205 if (listen(listen_fd, SOMAXCONN) < 0) {
206 smbd_report("pipesvc_listener, listen: %d", errno);
207 (void) close(listen_fd);
208 return (NULL);
211 for (;;) {
213 snlen = sizeof (sa);
214 newfd = accept(listen_fd, (struct sockaddr *)&sa, &snlen);
215 if (newfd < 0) {
216 err = errno;
217 switch (err) {
218 case ECONNABORTED:
219 continue;
220 case EINTR:
221 /* normal termination */
222 goto out;
223 default:
224 smbd_report("pipesvc_listener, "
225 "accept failed: %d", errno);
227 smbd_report("pipesvc_listener, accept: %d", err);
228 break;
231 np = np_new(pl, newfd);
232 if (np == NULL) {
233 smbd_report("pipesvc_listener, alloc1 failed");
234 (void) close(newfd);
235 continue;
238 rc = pthread_create(&tid, NULL, pipesvc_worker, np);
239 if (rc != 0) {
240 smbd_report("pipesvc_listener, pthread_create: %d",
241 errno);
242 np_free(np);
243 continue;
245 (void) pthread_detach(tid);
247 /* Note: np_free in pipesvc_worker */
248 np = NULL;
251 out:
252 (void) close(listen_fd);
253 pl->tid = 0;
254 return (NULL);
257 static void *
258 pipesvc_worker(void *varg)
260 XDR xdrs;
261 smb_pipehdr_t phdr;
262 ndr_pipe_t *np = varg;
263 struct pipe_listener *pl = np->np_listener;
264 void *buf = NULL;
265 uint32_t status;
266 ssize_t rc;
268 (void) mutex_lock(&pipesvc_mutex);
269 if (pipesvc_workers_cur >= pipesvc_workers_max ||
270 pl->current >= pl->max_allowed) {
271 (void) mutex_unlock(&pipesvc_mutex);
272 status = NT_STATUS_PIPE_NOT_AVAILABLE;
273 (void) send(np->np_fid, &status, sizeof (status), 0);
274 goto out_free_np;
276 pipesvc_workers_cur++;
277 pl->current++;
278 if (pl->max_seen < pl->current)
279 pl->max_seen = pl->current;
280 (void) mutex_unlock(&pipesvc_mutex);
283 * The smbsrv kmod sends us one initial message containing an
284 * XDR encoded smb_netuserinfo_t that we read and decode here,
285 * all unbeknownst to libmlrpc.
287 * Might be nice to enhance getpeerucred() so it can give us
288 * all the info smb_netuserinfo_t carries, and then use that,
289 * which would allow using a more generic RPC service.
291 rc = pipe_recv(np, &phdr, sizeof (phdr));
292 if (rc != 0) {
293 smbd_report("pipesvc_worker, recv1: %d", rc);
294 goto out_decr;
296 if (phdr.ph_magic != SMB_PIPE_HDR_MAGIC ||
297 phdr.ph_uilen > 8192) {
298 smbd_report("pipesvc_worker, bad hdr");
299 goto out_decr;
301 buf = malloc(phdr.ph_uilen);
302 if (buf == NULL) {
303 smbd_report("pipesvc_worker, alloc1 failed");
304 goto out_decr;
306 rc = pipe_recv(np, buf, phdr.ph_uilen);
307 if (rc != 0) {
308 smbd_report("pipesvc_worker, recv2: %d", rc);
309 goto out_decr;
312 xdrmem_create(&xdrs, buf, phdr.ph_uilen, XDR_DECODE);
313 if (!smb_netuserinfo_xdr(&xdrs, np->np_user)) {
314 smbd_report("pipesvc_worker, bad uinfo");
315 goto out_free_buf;
319 * Later, could disallow opens of some pipes by
320 * anonymous users, etc. For now, reply "OK".
322 status = 0;
323 rc = pipe_send(np, &status, sizeof (status));
324 if (rc != 0) {
325 smbd_report("pipesvc_worker, send1: %d", rc);
326 goto out_free_buf;
330 * Run the RPC service loop worker, which
331 * returns when it sees the pipe close.
333 ndr_pipe_worker(np);
335 xdrs.x_op = XDR_FREE;
336 (void) smb_netuserinfo_xdr(&xdrs, np->np_user);
338 out_free_buf:
339 free(buf);
340 xdr_destroy(&xdrs);
342 out_decr:
343 (void) mutex_lock(&pipesvc_mutex);
344 pipesvc_workers_cur--;
345 pl->current--;
346 (void) mutex_unlock(&pipesvc_mutex);
348 out_free_np:
349 /* Cleanup what came in by varg. */
350 (void) shutdown(np->np_fid, SHUT_RDWR);
351 np_free(np);
352 return (NULL);
356 * These are the transport get/put callback functions provided
357 * via the ndr_pipe_t object to the libmlrpc`ndr_pipe_worker.
358 * These are called only with known PDU sizes and should
359 * loop as needed to transfer the entire message.
361 static int
362 pipe_recv(ndr_pipe_t *np, void *buf, size_t len)
364 int x;
366 while (len > 0) {
367 x = recv(np->np_fid, buf, len, 0);
368 if (x < 0)
369 return (errno);
370 if (x == 0)
371 return (EIO);
372 buf = (char *)buf + x;
373 len -= x;
376 return (0);
379 static int
380 pipe_send(ndr_pipe_t *np, void *buf, size_t len)
382 int x;
384 while (len > 0) {
385 x = send(np->np_fid, buf, len, 0);
386 if (x < 0)
387 return (errno);
388 if (x == 0)
389 return (EIO);
390 buf = (char *)buf + x;
391 len -= x;
394 return (0);