static gpgme_error_t build_fd_data_map (engine_spawn_t esp) { struct datalist_s *a; size_t datac; int fds[2]; for (datac = 0, a = esp->arglist; a; a = a->next) if (a->data) datac++; free_fd_data_map (esp->fd_data_map); esp->fd_data_map = calloc (datac + 1, sizeof *esp->fd_data_map); if (!esp->fd_data_map) return gpg_error_from_syserror (); for (datac = 0, a = esp->arglist; a; a = a->next) { assert (a->data); if (_gpgme_io_pipe (fds, a->inbound ? 1 : 0) == -1) { free (esp->fd_data_map); esp->fd_data_map = NULL; return gpg_error_from_syserror (); } if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, esp) || _gpgme_io_set_close_notify (fds[1], close_notify_handler, esp)) { /* FIXME: Need error cleanup. */ return gpg_error (GPG_ERR_GENERAL); } esp->fd_data_map[datac].inbound = a->inbound; if (a->inbound) { esp->fd_data_map[datac].fd = fds[0]; esp->fd_data_map[datac].peer_fd = fds[1]; } else { esp->fd_data_map[datac].fd = fds[1]; esp->fd_data_map[datac].peer_fd = fds[0]; } esp->fd_data_map[datac].data = a->data; esp->fd_data_map[datac].dup_to = a->dup_to; datac++; } return 0; }
static gpgme_error_t start (engine_gpgsm_t gpgsm, const char *command) { gpgme_error_t err; assuan_fd_t afdlist[5]; int fdlist[5]; int nfds; int i; /* We need to know the fd used by assuan for reads. We do this by using the assumption that the first returned fd from assuan_get_active_fds() is always this one. */ nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */, afdlist, DIM (afdlist)); if (nfds < 1) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ /* For now... */ for (i = 0; i < nfds; i++) fdlist[i] = (int) afdlist[i]; /* We "duplicate" the file descriptor, so we can close it here (we can't close fdlist[0], as that is closed by libassuan, and closing it here might cause libassuan to close some unrelated FD later). Alternatively, we could special case status_fd and register/unregister it manually as needed, but this increases code duplication and is more complicated as we can not use the close notifications etc. A third alternative would be to let Assuan know that we closed the FD, but that complicates the Assuan interface. */ gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]); if (gpgsm->status_cb.fd < 0) return gpg_error_from_syserror (); if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd, close_notify_handler, gpgsm)) { _gpgme_io_close (gpgsm->status_cb.fd); gpgsm->status_cb.fd = -1; return gpg_error (GPG_ERR_GENERAL); } err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler); if (!err && gpgsm->input_cb.fd != -1) err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler); if (!err && gpgsm->output_cb.fd != -1) err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler); if (!err && gpgsm->message_cb.fd != -1) err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler); if (!err) err = assuan_write_line (gpgsm->assuan_ctx, command); if (!err) gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL); return err; }
static gpgme_error_t start(engine_gpgsm_t gpgsm, const char *command) { gpgme_error_t err; int fdlist[5]; int nfds; /* We need to know the fd used by assuan for reads. We do this by using the assumption that the first returned fd from assuan_get_active_fds() is always this one. */ nfds = assuan_get_active_fds(gpgsm->assuan_ctx, 0 /* read fds */, fdlist, DIM(fdlist)); if(nfds < 1) return gpg_error(GPG_ERR_GENERAL); /* FIXME */ /* We duplicate the file descriptor, so we can close it without disturbing assuan. Alternatively, we could special case status_fd and register/unregister it manually as needed, but this increases code duplication and is more complicated as we can not use the close notifications etc. */ gpgsm->status_cb.fd = dup(fdlist[0]); if(gpgsm->status_cb.fd < 0) return gpg_error_from_syserror(); if(_gpgme_io_set_close_notify(gpgsm->status_cb.fd, close_notify_handler, gpgsm)) { close(gpgsm->status_cb.fd); gpgsm->status_cb.fd = -1; return gpg_error(GPG_ERR_GENERAL); } err = add_io_cb(gpgsm, &gpgsm->status_cb, status_handler); if(!err && gpgsm->input_cb.fd != -1) err = add_io_cb(gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler); if(!err && gpgsm->output_cb.fd != -1) err = add_io_cb(gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler); if(!err && gpgsm->message_cb.fd != -1) err = add_io_cb(gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler); if(!err) err = map_assuan_error(assuan_write_line(gpgsm->assuan_ctx, command)); if(!err) (*gpgsm->io_cbs.event)(gpgsm->io_cbs.event_priv, GPGME_EVENT_START, NULL); return err; }
static gpgme_error_t gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt) { gpg_error_t err = 0; char line[COMMANDLINELEN]; char *which; iocb_data_t *iocb_data; int dir; switch (fd_type) { case INPUT_FD: which = "INPUT"; iocb_data = &gpgsm->input_cb; break; case OUTPUT_FD: which = "OUTPUT"; iocb_data = &gpgsm->output_cb; break; case MESSAGE_FD: which = "MESSAGE"; iocb_data = &gpgsm->message_cb; break; default: return gpg_error (GPG_ERR_INV_VALUE); } dir = iocb_data->dir; #if USE_DESCRIPTOR_PASSING /* We try to short-cut the communication by giving GPGSM direct access to the file descriptor, rather than using a pipe. */ iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data); if (iocb_data->server_fd < 0) { int fds[2]; if (_gpgme_io_pipe (fds, dir) < 0) return gpg_error_from_syserror (); iocb_data->fd = dir ? fds[0] : fds[1]; iocb_data->server_fd = dir ? fds[1] : fds[0]; if (_gpgme_io_set_close_notify (iocb_data->fd, close_notify_handler, gpgsm)) { err = gpg_error (GPG_ERR_GENERAL); goto leave_set_fd; } } err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd); if (err) goto leave_set_fd; _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; if (opt) snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt); else snprintf (line, COMMANDLINELEN, "%s FD", which); #else if (opt) snprintf (line, COMMANDLINELEN, "%s FD=%s %s", which, iocb_data->server_fd_str, opt); else snprintf (line, COMMANDLINELEN, "%s FD=%s", which, iocb_data->server_fd_str); #endif err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL); #if USE_DESCRIPTOR_PASSING leave_set_fd: if (err) { _gpgme_io_close (iocb_data->fd); iocb_data->fd = -1; if (iocb_data->server_fd != -1) { _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; } } #endif return err; }
static gpgme_error_t gpgsm_new (void **engine, const char *file_name, const char *home_dir) { gpgme_error_t err = 0; engine_gpgsm_t gpgsm; const char *argv[5]; int argc; #if !USE_DESCRIPTOR_PASSING int fds[2]; int child_fds[4]; #endif char *dft_display = NULL; char dft_ttyname[64]; char *dft_ttytype = NULL; char *optstr; gpgsm = calloc (1, sizeof *gpgsm); if (!gpgsm) return gpg_error_from_syserror (); gpgsm->status_cb.fd = -1; gpgsm->status_cb.dir = 1; gpgsm->status_cb.tag = 0; gpgsm->status_cb.data = gpgsm; gpgsm->input_cb.fd = -1; gpgsm->input_cb.dir = 0; gpgsm->input_cb.tag = 0; gpgsm->input_cb.server_fd = -1; *gpgsm->input_cb.server_fd_str = 0; gpgsm->output_cb.fd = -1; gpgsm->output_cb.dir = 1; gpgsm->output_cb.tag = 0; gpgsm->output_cb.server_fd = -1; *gpgsm->output_cb.server_fd_str = 0; gpgsm->message_cb.fd = -1; gpgsm->message_cb.dir = 0; gpgsm->message_cb.tag = 0; gpgsm->message_cb.server_fd = -1; *gpgsm->message_cb.server_fd_str = 0; gpgsm->status.fnc = 0; gpgsm->colon.fnc = 0; gpgsm->colon.attic.line = 0; gpgsm->colon.attic.linesize = 0; gpgsm->colon.attic.linelen = 0; gpgsm->colon.any = 0; gpgsm->inline_data = NULL; gpgsm->io_cbs.add = NULL; gpgsm->io_cbs.add_priv = NULL; gpgsm->io_cbs.remove = NULL; gpgsm->io_cbs.event = NULL; gpgsm->io_cbs.event_priv = NULL; #if !USE_DESCRIPTOR_PASSING if (_gpgme_io_pipe (fds, 0) < 0) { err = gpg_error_from_syserror (); goto leave; } gpgsm->input_cb.fd = fds[1]; gpgsm->input_cb.server_fd = fds[0]; if (_gpgme_io_pipe (fds, 1) < 0) { err = gpg_error_from_syserror (); goto leave; } gpgsm->output_cb.fd = fds[0]; gpgsm->output_cb.server_fd = fds[1]; if (_gpgme_io_pipe (fds, 0) < 0) { err = gpg_error_from_syserror (); goto leave; } gpgsm->message_cb.fd = fds[1]; gpgsm->message_cb.server_fd = fds[0]; child_fds[0] = gpgsm->input_cb.server_fd; child_fds[1] = gpgsm->output_cb.server_fd; child_fds[2] = gpgsm->message_cb.server_fd; child_fds[3] = -1; #endif argc = 0; argv[argc++] = "gpgsm"; if (home_dir) { argv[argc++] = "--homedir"; argv[argc++] = home_dir; } argv[argc++] = "--server"; argv[argc++] = NULL; err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME, &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb, NULL); if (err) goto leave; assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks); #if USE_DESCRIPTOR_PASSING err = assuan_pipe_connect (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (), argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING); #else { assuan_fd_t achild_fds[4]; int i; /* For now... */ for (i = 0; i < 4; i++) achild_fds[i] = (assuan_fd_t) child_fds[i]; err = assuan_pipe_connect (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (), argv, achild_fds, NULL, NULL, 0); /* For now... */ for (i = 0; i < 4; i++) child_fds[i] = (int) achild_fds[i]; } /* On Windows, handles are inserted in the spawned process with DuplicateHandle, and child_fds contains the server-local names for the inserted handles when assuan_pipe_connect returns. */ if (!err) { /* Note: We don't use _gpgme_io_fd2str here. On W32 the returned handles are real W32 system handles, not whatever GPGME uses internally (which may be a system handle, a C library handle or a GLib/Qt channel. Confusing, yes, but remember these are server-local names, so they are not part of GPGME at all. */ snprintf (gpgsm->input_cb.server_fd_str, sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]); snprintf (gpgsm->output_cb.server_fd_str, sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]); snprintf (gpgsm->message_cb.server_fd_str, sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]); } #endif if (err) goto leave; err = _gpgme_getenv ("DISPLAY", &dft_display); if (err) goto leave; if (dft_display) { if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0) { free (dft_display); err = gpg_error_from_syserror (); goto leave; } free (dft_display); err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; } if (isatty (1)) { int rc; rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); if (rc) { err = gpg_error_from_errno (rc); goto leave; } else { if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0) { err = gpg_error_from_syserror (); goto leave; } err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; err = _gpgme_getenv ("TERM", &dft_ttytype); if (err) goto leave; if (dft_ttytype) { if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0) { free (dft_ttytype); err = gpg_error_from_syserror (); goto leave; } free (dft_ttytype); err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; } } } /* Ask gpgsm to enable the audit log support. */ if (!err) { err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This is an optional feature of gpgsm. */ } #ifdef HAVE_W32_SYSTEM /* Under Windows we need to use AllowSetForegroundWindow. Tell gpgsm to tell us when it needs it. */ if (!err) { err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This is a new feature of gpgsm. */ } #endif /*HAVE_W32_SYSTEM*/ #if !USE_DESCRIPTOR_PASSING if (!err && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd, close_notify_handler, gpgsm) || _gpgme_io_set_close_notify (gpgsm->output_cb.fd, close_notify_handler, gpgsm) || _gpgme_io_set_close_notify (gpgsm->message_cb.fd, close_notify_handler, gpgsm))) { err = gpg_error (GPG_ERR_GENERAL); goto leave; } #endif leave: /* Close the server ends of the pipes (because of this, we must use the stored server_fd_str in the function start). Our ends are closed in gpgsm_release(). */ #if !USE_DESCRIPTOR_PASSING if (gpgsm->input_cb.server_fd != -1) _gpgme_io_close (gpgsm->input_cb.server_fd); if (gpgsm->output_cb.server_fd != -1) _gpgme_io_close (gpgsm->output_cb.server_fd); if (gpgsm->message_cb.server_fd != -1) _gpgme_io_close (gpgsm->message_cb.server_fd); #endif if (err) gpgsm_release (gpgsm); else *engine = gpgsm; return err; }
static gpgme_error_t gpgsm_new(void **engine, const char *file_name, const char *home_dir) { gpgme_error_t err = 0; engine_gpgsm_t gpgsm; const char *argv[5]; int argc; #if !USE_DESCRIPTOR_PASSING int fds[2]; int child_fds[4]; #endif char *dft_display = NULL; char dft_ttyname[64]; char *dft_ttytype = NULL; char *optstr; gpgsm = calloc(1, sizeof * gpgsm); if(!gpgsm) return gpg_error_from_errno(errno); gpgsm->status_cb.fd = -1; gpgsm->status_cb.dir = 1; gpgsm->status_cb.tag = 0; gpgsm->status_cb.data = gpgsm; gpgsm->input_cb.fd = -1; gpgsm->input_cb.dir = 0; gpgsm->input_cb.tag = 0; gpgsm->input_cb.server_fd = -1; gpgsm->output_cb.fd = -1; gpgsm->output_cb.dir = 1; gpgsm->output_cb.tag = 0; gpgsm->output_cb.server_fd = -1; gpgsm->message_cb.fd = -1; gpgsm->message_cb.dir = 0; gpgsm->message_cb.tag = 0; gpgsm->message_cb.server_fd = -1; gpgsm->status.fnc = 0; gpgsm->colon.fnc = 0; gpgsm->colon.attic.line = 0; gpgsm->colon.attic.linesize = 0; gpgsm->colon.attic.linelen = 0; gpgsm->colon.any = 0; gpgsm->io_cbs.add = NULL; gpgsm->io_cbs.add_priv = NULL; gpgsm->io_cbs.remove = NULL; gpgsm->io_cbs.event = NULL; gpgsm->io_cbs.event_priv = NULL; #if !USE_DESCRIPTOR_PASSING if(_gpgme_io_pipe(fds, 0) < 0) { err = gpg_error_from_errno(errno); goto leave; } gpgsm->input_cb.fd = fds[1]; gpgsm->input_cb.server_fd = fds[0]; if(_gpgme_io_pipe(fds, 1) < 0) { err = gpg_error_from_errno(errno); goto leave; } gpgsm->output_cb.fd = fds[0]; gpgsm->output_cb.server_fd = fds[1]; if(_gpgme_io_pipe(fds, 0) < 0) { err = gpg_error_from_errno(errno); goto leave; } gpgsm->message_cb.fd = fds[1]; gpgsm->message_cb.server_fd = fds[0]; child_fds[0] = gpgsm->input_cb.server_fd; child_fds[1] = gpgsm->output_cb.server_fd; child_fds[2] = gpgsm->message_cb.server_fd; child_fds[3] = -1; #endif argc = 0; argv[argc++] = "gpgsm"; if(home_dir) { argv[argc++] = "--homedir"; argv[argc++] = home_dir; } argv[argc++] = "--server"; argv[argc++] = NULL; #if USE_DESCRIPTOR_PASSING err = assuan_pipe_connect_ext (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path(), argv, NULL, NULL, NULL, 1); #else err = assuan_pipe_connect (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path(), argv, child_fds); #endif if(err) goto leave; err = _gpgme_getenv("DISPLAY", &dft_display); if(err) goto leave; if(dft_display) { if(asprintf(&optstr, "OPTION display=%s", dft_display) < 0) { free(dft_display); err = gpg_error_from_errno(errno); goto leave; } free(dft_display); err = assuan_transact(gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free(optstr); if(err) { err = map_assuan_error(err); goto leave; } } if(isatty(1)) { if(ttyname_r(1, dft_ttyname, sizeof(dft_ttyname))) { err = gpg_error_from_errno(errno); goto leave; } else { if(asprintf(&optstr, "OPTION ttyname=%s", dft_ttyname) < 0) { err = gpg_error_from_errno(errno); goto leave; } err = assuan_transact(gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free(optstr); if(err) { err = map_assuan_error(err); goto leave; } err = _gpgme_getenv("TERM", &dft_ttytype); if(err) goto leave; if(dft_ttytype) { if(asprintf(&optstr, "OPTION ttytype=%s", dft_ttytype) < 0) { free(dft_ttytype); err = gpg_error_from_errno(errno); goto leave; } free(dft_ttytype); err = assuan_transact(gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free(optstr); if(err) { err = map_assuan_error(err); goto leave; } } } } #if !USE_DESCRIPTOR_PASSING if(!err && (_gpgme_io_set_close_notify(gpgsm->input_cb.fd, close_notify_handler, gpgsm) || _gpgme_io_set_close_notify(gpgsm->output_cb.fd, close_notify_handler, gpgsm) || _gpgme_io_set_close_notify(gpgsm->message_cb.fd, close_notify_handler, gpgsm))) { err = gpg_error(GPG_ERR_GENERAL); goto leave; } #endif leave: /* Close the server ends of the pipes. Our ends are closed in gpgsm_release(). */ #if !USE_DESCRIPTOR_PASSING if(gpgsm->input_cb.server_fd != -1) _gpgme_io_close(gpgsm->input_cb.server_fd); if(gpgsm->output_cb.server_fd != -1) _gpgme_io_close(gpgsm->output_cb.server_fd); if(gpgsm->message_cb.server_fd != -1) _gpgme_io_close(gpgsm->message_cb.server_fd); #endif if(err) gpgsm_release(gpgsm); else *engine = gpgsm; return err; }