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