Line data Source code
1 : /* assuan-pipe-connect.c - Establish a pipe connection (client)
2 : Copyright (C) 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010,
3 : 2011 Free Software Foundation, Inc.
4 :
5 : This file is part of Assuan.
6 :
7 : Assuan is free software; you can redistribute it and/or modify it
8 : under the terms of the GNU Lesser General Public License as
9 : published by the Free Software Foundation; either version 2.1 of
10 : the License, or (at your option) any later version.
11 :
12 : Assuan is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : Lesser General Public License for more details.
16 :
17 : You should have received a copy of the GNU Lesser General Public
18 : License along with this program; if not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #ifdef HAVE_CONFIG_H
22 : #include <config.h>
23 : #endif
24 :
25 : #include <stdlib.h>
26 : #include <stdio.h>
27 : #include <string.h>
28 : /* On Windows systems signal.h is not needed and even not supported on
29 : WindowsCE. */
30 : #ifndef HAVE_DOSISH_SYSTEM
31 : # include <signal.h>
32 : #endif
33 : #ifdef HAVE_UNISTD_H
34 : # include <unistd.h>
35 : #endif
36 : #include <errno.h>
37 : #ifdef HAVE_FCNTL_H
38 : #include <fcntl.h>
39 : #endif
40 : #ifdef HAVE_SYS_TYPES_H
41 : # include <sys/types.h>
42 : #endif
43 : #ifndef HAVE_W32_SYSTEM
44 : # include <sys/wait.h>
45 : #else
46 : # ifdef HAVE_WINSOCK2_H
47 : # include <winsock2.h>
48 : # endif
49 : # include <windows.h>
50 : #endif
51 :
52 : #include "assuan-defs.h"
53 : #include "debug.h"
54 :
55 : /* Hacks for Slowaris. */
56 : #ifndef PF_LOCAL
57 : # ifdef PF_UNIX
58 : # define PF_LOCAL PF_UNIX
59 : # else
60 : # define PF_LOCAL AF_UNIX
61 : # endif
62 : #endif
63 : #ifndef AF_LOCAL
64 : # define AF_LOCAL AF_UNIX
65 : #endif
66 :
67 :
68 : #ifdef _POSIX_OPEN_MAX
69 : #define MAX_OPEN_FDS _POSIX_OPEN_MAX
70 : #else
71 : #define MAX_OPEN_FDS 20
72 : #endif
73 :
74 :
75 : /* This should be called to make sure that SIGPIPE gets ignored. */
76 : static void
77 2 : fix_signals (void)
78 : {
79 : #ifndef HAVE_DOSISH_SYSTEM /* No SIGPIPE for these systems. */
80 : static int fixed_signals;
81 :
82 2 : if (!fixed_signals)
83 : {
84 : struct sigaction act;
85 :
86 2 : sigaction (SIGPIPE, NULL, &act);
87 2 : if (act.sa_handler == SIG_DFL)
88 : {
89 2 : act.sa_handler = SIG_IGN;
90 2 : sigemptyset (&act.sa_mask);
91 2 : act.sa_flags = 0;
92 2 : sigaction (SIGPIPE, &act, NULL);
93 : }
94 2 : fixed_signals = 1;
95 : /* FIXME: This is not MT safe */
96 : }
97 : #endif /*HAVE_DOSISH_SYSTEM*/
98 2 : }
99 :
100 :
101 : /* Helper for pipe_connect. */
102 : static gpg_error_t
103 2 : initial_handshake (assuan_context_t ctx)
104 : {
105 : assuan_response_t response;
106 : int off;
107 : gpg_error_t err;
108 :
109 2 : err = _assuan_read_from_server (ctx, &response, &off, 0);
110 2 : if (err)
111 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "initial_handshake", ctx,
112 : "can't connect server: %s", gpg_strerror (err));
113 2 : else if (response != ASSUAN_RESPONSE_OK)
114 : {
115 0 : TRACE1 (ctx, ASSUAN_LOG_SYSIO, "initial_handshake", ctx,
116 : "can't connect server: `%s'", ctx->inbound.line);
117 0 : err = _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED);
118 : }
119 :
120 2 : return err;
121 : }
122 :
123 :
124 : struct at_pipe_fork
125 : {
126 : void (*user_atfork) (void *opaque, int reserved);
127 : void *user_atforkvalue;
128 : pid_t parent_pid;
129 : };
130 :
131 :
132 : static void
133 1 : at_pipe_fork_cb (void *opaque, int reserved)
134 : {
135 1 : struct at_pipe_fork *atp = opaque;
136 :
137 1 : if (atp->user_atfork)
138 0 : atp->user_atfork (atp->user_atforkvalue, reserved);
139 :
140 : #ifndef HAVE_W32_SYSTEM
141 : {
142 : char mypidstr[50];
143 :
144 : /* We store our parents pid in the environment so that the execed
145 : assuan server is able to read the actual pid of the client.
146 : The server can't use getppid because it might have been double
147 : forked before the assuan server has been initialized. */
148 1 : sprintf (mypidstr, "%lu", (unsigned long) atp->parent_pid);
149 1 : setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
150 :
151 : /* Make sure that we never pass a connection fd variable when
152 : using a simple pipe. */
153 1 : unsetenv ("_assuan_connection_fd");
154 : }
155 : #endif
156 1 : }
157 :
158 :
159 : static gpg_error_t
160 1 : pipe_connect (assuan_context_t ctx,
161 : const char *name, const char **argv,
162 : assuan_fd_t *fd_child_list,
163 : void (*atfork) (void *opaque, int reserved),
164 : void *atforkvalue, unsigned int flags)
165 : {
166 : gpg_error_t rc;
167 : assuan_fd_t rp[2];
168 : assuan_fd_t wp[2];
169 : pid_t pid;
170 : int res;
171 : struct at_pipe_fork atp;
172 : unsigned int spawn_flags;
173 :
174 1 : atp.user_atfork = atfork;
175 1 : atp.user_atforkvalue = atforkvalue;
176 1 : atp.parent_pid = getpid ();
177 :
178 1 : if (!ctx || !name || !argv || !argv[0])
179 0 : return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
180 :
181 1 : if (! ctx->flags.no_fixsignals)
182 1 : fix_signals ();
183 :
184 1 : if (_assuan_pipe (ctx, rp, 1) < 0)
185 0 : return _assuan_error (ctx, gpg_err_code_from_syserror ());
186 :
187 1 : if (_assuan_pipe (ctx, wp, 0) < 0)
188 : {
189 0 : _assuan_close (ctx, rp[0]);
190 0 : _assuan_close_inheritable (ctx, rp[1]);
191 0 : return _assuan_error (ctx, gpg_err_code_from_syserror ());
192 : }
193 :
194 1 : spawn_flags = 0;
195 1 : if (flags & ASSUAN_PIPE_CONNECT_DETACHED)
196 0 : spawn_flags |= ASSUAN_SPAWN_DETACHED;
197 :
198 : /* FIXME: Use atfork handler that closes child fds on Unix. */
199 1 : res = _assuan_spawn (ctx, &pid, name, argv, wp[0], rp[1],
200 : fd_child_list, at_pipe_fork_cb, &atp, spawn_flags);
201 1 : if (res < 0)
202 : {
203 0 : rc = gpg_err_code_from_syserror ();
204 0 : _assuan_close (ctx, rp[0]);
205 0 : _assuan_close_inheritable (ctx, rp[1]);
206 0 : _assuan_close_inheritable (ctx, wp[0]);
207 0 : _assuan_close (ctx, wp[1]);
208 0 : return _assuan_error (ctx, rc);
209 : }
210 :
211 : /* Close the stdin/stdout child fds in the parent. */
212 1 : _assuan_close_inheritable (ctx, rp[1]);
213 1 : _assuan_close_inheritable (ctx, wp[0]);
214 :
215 1 : ctx->engine.release = _assuan_client_release;
216 1 : ctx->engine.readfnc = _assuan_simple_read;
217 1 : ctx->engine.writefnc = _assuan_simple_write;
218 1 : ctx->engine.sendfd = NULL;
219 1 : ctx->engine.receivefd = NULL;
220 1 : ctx->finish_handler = _assuan_client_finish;
221 1 : ctx->max_accepts = 1;
222 1 : ctx->accept_handler = NULL;
223 1 : ctx->inbound.fd = rp[0]; /* Our inbound is read end of read pipe. */
224 1 : ctx->outbound.fd = wp[1]; /* Our outbound is write end of write pipe. */
225 1 : ctx->pid = pid;
226 :
227 1 : rc = initial_handshake (ctx);
228 1 : if (rc)
229 0 : _assuan_reset (ctx);
230 1 : return rc;
231 : }
232 :
233 :
234 : /* FIXME: For socketpair_connect, use spawn function and add atfork
235 : handler to do the right thing. Instead of stdin and stdout, we
236 : extend the fd_child_list by fds[1]. */
237 :
238 : #ifndef HAVE_W32_SYSTEM
239 : struct at_socketpair_fork
240 : {
241 : assuan_fd_t peer_fd;
242 : void (*user_atfork) (void *opaque, int reserved);
243 : void *user_atforkvalue;
244 : pid_t parent_pid;
245 : };
246 :
247 :
248 : static void
249 1 : at_socketpair_fork_cb (void *opaque, int reserved)
250 : {
251 1 : struct at_socketpair_fork *atp = opaque;
252 :
253 1 : if (atp->user_atfork)
254 0 : atp->user_atfork (atp->user_atforkvalue, reserved);
255 :
256 : #ifndef HAVE_W32_SYSTEM
257 : {
258 : char mypidstr[50];
259 :
260 : /* We store our parents pid in the environment so that the execed
261 : assuan server is able to read the actual pid of the client.
262 : The server can't use getppid because it might have been double
263 : forked before the assuan server has been initialized. */
264 1 : sprintf (mypidstr, "%lu", (unsigned long) atp->parent_pid);
265 1 : setenv ("_assuan_pipe_connect_pid", mypidstr, 1);
266 :
267 : /* Now set the environment variable used to convey the
268 : connection's file descriptor. */
269 1 : sprintf (mypidstr, "%d", atp->peer_fd);
270 1 : if (setenv ("_assuan_connection_fd", mypidstr, 1))
271 0 : _exit (4);
272 : }
273 : #endif
274 1 : }
275 :
276 :
277 : /* This function is similar to pipe_connect but uses a socketpair and
278 : sets the I/O up to use sendmsg/recvmsg. */
279 : static gpg_error_t
280 1 : socketpair_connect (assuan_context_t ctx,
281 : const char *name, const char **argv,
282 : assuan_fd_t *fd_child_list,
283 : void (*atfork) (void *opaque, int reserved),
284 : void *atforkvalue)
285 : {
286 : gpg_error_t err;
287 : int idx;
288 : int fds[2];
289 : char mypidstr[50];
290 : pid_t pid;
291 1 : int *child_fds = NULL;
292 1 : int child_fds_cnt = 0;
293 : struct at_socketpair_fork atp;
294 : int rc;
295 :
296 1 : TRACE_BEG3 (ctx, ASSUAN_LOG_CTX, "socketpair_connect", ctx,
297 : "name=%s,atfork=%p,atforkvalue=%p", name ? name : "(null)",
298 : atfork, atforkvalue);
299 :
300 1 : atp.user_atfork = atfork;
301 1 : atp.user_atforkvalue = atforkvalue;
302 1 : atp.parent_pid = getpid ();
303 :
304 1 : if (!ctx
305 1 : || (name && (!argv || !argv[0]))
306 1 : || (!name && !argv))
307 0 : return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
308 :
309 1 : if (! ctx->flags.no_fixsignals)
310 1 : fix_signals ();
311 :
312 1 : sprintf (mypidstr, "%lu", (unsigned long)getpid ());
313 :
314 1 : if (fd_child_list)
315 3 : while (fd_child_list[child_fds_cnt] != ASSUAN_INVALID_FD)
316 1 : child_fds_cnt++;
317 1 : child_fds = _assuan_malloc (ctx, (child_fds_cnt + 2) * sizeof (int));
318 1 : if (! child_fds)
319 0 : return TRACE_ERR (gpg_err_code_from_syserror ());
320 1 : child_fds[1] = ASSUAN_INVALID_FD;
321 1 : if (fd_child_list)
322 1 : memcpy (&child_fds[1], fd_child_list, (child_fds_cnt + 1) * sizeof (int));
323 :
324 1 : if (_assuan_socketpair (ctx, AF_LOCAL, SOCK_STREAM, 0, fds))
325 : {
326 0 : TRACE_LOG1 ("socketpair failed: %s", strerror (errno));
327 0 : _assuan_free (ctx, child_fds);
328 0 : return TRACE_ERR (GPG_ERR_ASS_GENERAL);
329 : }
330 1 : atp.peer_fd = fds[1];
331 1 : child_fds[0] = fds[1];
332 :
333 1 : rc = _assuan_spawn (ctx, &pid, name, argv, ASSUAN_INVALID_FD,
334 : ASSUAN_INVALID_FD, child_fds, at_socketpair_fork_cb,
335 : &atp, 0);
336 2 : if (rc < 0)
337 : {
338 0 : err = gpg_err_code_from_syserror ();
339 0 : _assuan_close (ctx, fds[0]);
340 0 : _assuan_close (ctx, fds[1]);
341 0 : _assuan_free (ctx, child_fds);
342 0 : return TRACE_ERR (err);
343 : }
344 :
345 : /* For W32, the user needs to know the server-local names of the
346 : inherited handles. Return them here. Note that the translation
347 : of the peer socketpair fd (fd_child_list[0]) must be done by the
348 : wrapper program based on the environment variable
349 : _assuan_connection_fd. */
350 2 : if (fd_child_list)
351 : {
352 4 : for (idx = 0; fd_child_list[idx] != -1; idx++)
353 : /* We add 1 to skip over the socketpair end. */
354 2 : fd_child_list[idx] = child_fds[idx + 1];
355 : }
356 :
357 : /* If this is the server child process, exit early. */
358 2 : if (! name && (*argv)[0] == 's')
359 : {
360 1 : _assuan_free (ctx, child_fds);
361 1 : _assuan_close (ctx, fds[0]);
362 1 : return 0;
363 : }
364 :
365 1 : _assuan_close (ctx, fds[1]);
366 :
367 1 : ctx->engine.release = _assuan_client_release;
368 1 : ctx->finish_handler = _assuan_client_finish;
369 1 : ctx->max_accepts = 1;
370 1 : ctx->inbound.fd = fds[0];
371 1 : ctx->outbound.fd = fds[0];
372 1 : _assuan_init_uds_io (ctx);
373 :
374 1 : err = initial_handshake (ctx);
375 1 : if (err)
376 0 : _assuan_reset (ctx);
377 1 : return err;
378 : }
379 : #endif /*!HAVE_W32_SYSTEM*/
380 :
381 :
382 : /* Connect to a server over a full-duplex socket (i.e. created by
383 : socketpair), creating the assuan context and returning it in CTX.
384 : The server filename is NAME, the argument vector in ARGV.
385 : FD_CHILD_LIST is a -1 terminated list of file descriptors not to
386 : close in the child. ATFORK is called in the child right after the
387 : fork; ATFORKVALUE is passed as the first argument and 0 is passed
388 : as the second argument. The ATFORK function should only act if the
389 : second value is 0.
390 :
391 : FLAGS is a bit vector and controls how the function acts:
392 : Bit 0: If cleared a simple pipe based server is expected and the
393 : function behaves similar to `assuan_pipe_connect'.
394 :
395 : If set a server based on full-duplex pipes is expected. Such
396 : pipes are usually created using the `socketpair' function.
397 : It also enables features only available with such servers.
398 :
399 : Bit 7: If set and there is a need to start the server it will be
400 : started as a background process. This flag is useful under
401 : W32 systems, so that no new console is created and pops up a
402 : console window when starting the server
403 :
404 :
405 : If NAME is NULL, no exec is done but the same process is continued.
406 : However all file descriptors are closed and some special
407 : environment variables are set. To let the caller detect whether the
408 : child or the parent continues, the child returns "client" or
409 : "server" in *ARGV (but it is sufficient to check only the first
410 : character). This feature is only available on POSIX platforms. */
411 : gpg_error_t
412 2 : assuan_pipe_connect (assuan_context_t ctx,
413 : const char *name, const char *argv[],
414 : assuan_fd_t *fd_child_list,
415 : void (*atfork) (void *opaque, int reserved),
416 : void *atforkvalue, unsigned int flags)
417 : {
418 2 : TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_pipe_connect", ctx,
419 : "name=%s, flags=0x%x", name ? name : "(null)", flags);
420 :
421 2 : if (flags & ASSUAN_PIPE_CONNECT_FDPASSING)
422 : {
423 : #ifdef HAVE_W32_SYSTEM
424 : return _assuan_error (ctx, GPG_ERR_NOT_IMPLEMENTED);
425 : #else
426 1 : return socketpair_connect (ctx, name, argv, fd_child_list,
427 : atfork, atforkvalue);
428 : #endif
429 : }
430 : else
431 1 : return pipe_connect (ctx, name, argv, fd_child_list, atfork, atforkvalue,
432 : flags);
433 : }
434 :
|