/* Read the prefix of the keyblob and do some basic parsing. On success returns an open estream file at R_FP and the length of the header at R_HEADERLEN. */ static gpg_error_t read_keyblob_prefix (const char *filename, estream_t *r_fp, size_t *r_headerlen) { gpg_error_t err; estream_t fp; unsigned char packet[32]; *r_fp = NULL; fp = es_fopen (filename, "rb"); if (!fp) { err = gpg_error_from_syserror (); log_error ("error reading '%s': %s\n", filename, gpg_strerror (err)); return err; } /* Read the header. It is defined as 32 bytes thus we read it in one go. */ if (es_fread (packet, 32, 1, fp) != 1) { err = gpg_error_from_syserror (); log_error ("error reading the header of '%s': %s\n", filename, gpg_strerror (err)); es_fclose (fp); return err; } err = parse_header (filename, packet, 32, r_headerlen); if (err) es_fclose (fp); else *r_fp = fp; return err; }
static gpg_error_t armor_data (char **r_string, const void *data, size_t datalen) { gpg_error_t err; struct b64state b64state; estream_t fp; long length; char *buffer; size_t nread; *r_string = NULL; fp = es_fopenmem (0, "rw"); if (!fp) return gpg_error_from_syserror (); if ((err=b64enc_start_es (&b64state, fp, "PGP PUBLIC KEY BLOCK")) || (err=b64enc_write (&b64state, data, datalen)) || (err = b64enc_finish (&b64state))) { es_fclose (fp); return err; } /* FIXME: To avoid the extra buffer allocation estream should provide a function to snatch the internal allocated memory from such a memory stream. */ length = es_ftell (fp); if (length < 0) { err = gpg_error_from_syserror (); es_fclose (fp); return err; } buffer = xtrymalloc (length+1); if (!buffer) { err = gpg_error_from_syserror (); es_fclose (fp); return err; } es_rewind (fp); if (es_read (fp, buffer, length, &nread)) { err = gpg_error_from_syserror (); es_fclose (fp); return err; } buffer[nread] = 0; es_fclose (fp); *r_string = buffer; return 0; }
/* Write an S-expression formatted key to our key storage. With FORCE passed as true an existing key with the given GRIP will get overwritten. */ int agent_write_private_key (const unsigned char *grip, const void *buffer, size_t length, int force) { char *fname; estream_t fp; char hexgrip[40+4+1]; bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); /* FIXME: Write to a temp file first so that write failures during key updates won't lead to a key loss. */ if (!force && !access (fname, F_OK)) { log_error ("secret key file '%s' already exists\n", fname); xfree (fname); return gpg_error (GPG_ERR_EEXIST); } fp = es_fopen (fname, force? "wb,mode=-rw" : "wbx,mode=-rw"); if (!fp) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr)); xfree (fname); return tmperr; } if (es_fwrite (buffer, length, 1, fp) != 1) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("error writing '%s': %s\n", fname, gpg_strerror (tmperr)); es_fclose (fp); gnupg_remove (fname); xfree (fname); return tmperr; } if (es_fclose (fp)) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("error closing '%s': %s\n", fname, gpg_strerror (tmperr)); gnupg_remove (fname); xfree (fname); return tmperr; } bump_key_eventcounter (); xfree (fname); return 0; }
/* Retrieve keys from URL and write the result to the provided output stream OUTFP. */ gpg_error_t ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp) { gpg_error_t err = 0; estream_t infp; parsed_uri_t parsed_uri; /* The broken down URI. */ if (!url) return gpg_error (GPG_ERR_INV_URI); err = http_parse_uri (&parsed_uri, url, 1); if (err) return err; if (parsed_uri->is_http) { err = ks_http_fetch (ctrl, url, &infp); if (!err) { err = copy_stream (infp, outfp); es_fclose (infp); } } else if (!parsed_uri->opaque) { err = gpg_error (GPG_ERR_INV_URI); } else if (!strcmp (parsed_uri->scheme, "finger")) { err = ks_finger_fetch (ctrl, parsed_uri, &infp); if (!err) { err = copy_stream (infp, outfp); es_fclose (infp); } } else if (!strcmp (parsed_uri->scheme, "kdns")) { err = ks_kdns_fetch (ctrl, parsed_uri, &infp); if (!err) { err = copy_stream (infp, outfp); es_fclose (infp); } } else err = gpg_error (GPG_ERR_INV_URI); http_release_parsed_uri (parsed_uri); return err; }
/* Set the status FD. */ void wks_set_status_fd (int fd) { static int last_fd = -1; if (fd != -1 && last_fd == fd) return; if (statusfp && statusfp != es_stdout && statusfp != es_stderr) es_fclose (statusfp); statusfp = NULL; if (fd == -1) return; if (fd == 1) statusfp = es_stdout; else if (fd == 2) statusfp = es_stderr; else statusfp = es_fdopen (fd, "w"); if (!statusfp) { log_fatal ("can't open fd %d for status output: %s\n", fd, gpg_strerror (gpg_error_from_syserror ())); } last_fd = fd; }
/* Get the requested keys (matching PATTERNS) using all configured keyservers and write the result to the provided output stream. */ gpg_error_t ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp) { gpg_error_t err = 0; gpg_error_t first_err = 0; int any_server = 0; int any_data = 0; strlist_t sl; uri_item_t uri; estream_t infp; if (!patterns) return gpg_error (GPG_ERR_NO_USER_ID); /* FIXME: We only take care of the first keyserver. To fully support multiple keyservers we need to track the result for each pattern and use the next keyserver if one key was not found. The keyservers might not all be fully synced thus it is not clear whether the first keyserver has the freshest copy of the key. Need to think about a better strategy. */ for (uri = ctrl->keyservers; !err && uri; uri = uri->next) { if (uri->parsed_uri->is_http) { any_server = 1; for (sl = patterns; !err && sl; sl = sl->next) { err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp); if (err) { /* It is possible that a server does not carry a key, thus we only save the error and continue with the next pattern. FIXME: It is an open question how to return such an error condition to the caller. */ first_err = err; err = 0; } else { err = copy_stream (infp, outfp); /* Reading from the keyserver should never fail, thus return this error. */ if (!err) any_data = 1; es_fclose (infp); infp = NULL; } } } if (any_data) break; /* Stop loop after a keyserver returned something. */ } if (!any_server) err = gpg_error (GPG_ERR_NO_KEYSERVER); else if (!err && first_err && !any_data) err = first_err; return err; }
void set_status_fd (int fd) { static int last_fd = -1; if (fd != -1 && last_fd == fd) return; if (statusfp && statusfp != es_stdout && statusfp != es_stderr ) es_fclose (statusfp); statusfp = NULL; if (fd == -1) return; if (fd == 1) statusfp = es_stdout; else if (fd == 2) statusfp = es_stderr; else statusfp = es_fdopen (fd, "w"); if (!statusfp) { log_fatal ("can't open fd %d for status output: %s\n", fd, strerror (errno)); } last_fd = fd; gcry_set_progress_handler (progress_cb, NULL); }
static gpg_error_t cmd_verify (assuan_context_t ctx, char *line) { int rc; ctrl_t ctrl = assuan_get_pointer (ctx); int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); int out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); estream_t out_fp = NULL; (void)line; if (fd == -1) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); if (out_fd != -1) { out_fp = es_fdopen_nc (out_fd, "w"); if (!out_fp) return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); } rc = start_audit_session (ctrl); if (!rc) rc = gpgsm_verify (assuan_get_pointer (ctx), fd, ctrl->server_local->message_fd, out_fp); es_fclose (out_fp); /* Close and reset the fd. */ close_message_fd (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); return rc; }
static gpg_error_t cmd_decrypt (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int inp_fd, out_fd; estream_t out_fp; int rc; (void)line; inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); if (inp_fd == -1) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); if (out_fd == -1) return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); out_fp = es_fdopen_nc (out_fd, "w"); if (!out_fp) return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); rc = start_audit_session (ctrl); if (!rc) rc = gpgsm_decrypt (ctrl, inp_fd, out_fp); es_fclose (out_fp); /* Close and reset the fds. */ close_message_fd (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); return rc; }
static gpg_error_t cmd_sign (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int inp_fd, out_fd; estream_t out_fp; int detached; int rc; inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); if (inp_fd == -1) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); if (out_fd == -1) return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); detached = has_option (line, "--detached"); out_fp = es_fdopen_nc (out_fd, "w"); if (!out_fp) return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed"); rc = start_audit_session (ctrl); if (!rc) rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist, inp_fd, detached, out_fp); es_fclose (out_fp); /* close and reset the fd */ close_message_fd (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); return rc; }
/* Format NAME which is expected to be in rfc2253 format into a better human readable format. Caller must free the returned string. NULL is returned in case of an error. With TRANSLATE set to true the name will be translated to the native encoding. Note that NAME is internally always UTF-8 encoded. */ char * gpgsm_format_name2 (const char *name, int translate) { estream_t fp; struct format_name_cookie cookie; es_cookie_io_functions_t io = { NULL }; memset (&cookie, 0, sizeof cookie); io.func_write = format_name_writer; fp = es_fopencookie (&cookie, "w", io); if (!fp) { int save_errno = errno; log_error ("error creating memory stream: %s\n", strerror (save_errno)); gpg_err_set_errno (save_errno); return NULL; } gpgsm_es_print_name2 (fp, name, translate); es_fclose (fp); if (cookie.error || !cookie.buffer) { xfree (cookie.buffer); gpg_err_set_errno (cookie.error); return NULL; } return cookie.buffer; }
/* Read the keyblob at FILENAME. The caller should have acquired a lockfile and checked that the file exists. */ static gpg_error_t read_keyblob (const char *filename, void **r_enckeyblob, size_t *r_enckeybloblen) { gpg_error_t err; estream_t fp = NULL; size_t headerlen = 0; size_t msglen; void *msg = NULL; *r_enckeyblob = NULL; *r_enckeybloblen = 0; err = read_keyblob_prefix (filename, &fp, &headerlen); if (err) goto leave; if (opt.verbose) log_info ("header length of '%s' is %zu\n", filename, headerlen); /* Read everything including the padding. We should eventually do a regular OpenPGP parsing to detect the padding packet and pass only the actual used OpenPGP data to the engine. This is in particular required when supporting CMS which will be encapsulated in an OpenPGP packet. */ assert (headerlen >= 32); msglen = headerlen - 32; if (!msglen) { err = gpg_error (GPG_ERR_NO_DATA); goto leave; } msg = xtrymalloc (msglen); if (!msglen) { err = gpg_error_from_syserror (); goto leave; } if (es_fread (msg, msglen, 1, fp) != 1) { err = gpg_error_from_syserror (); log_error ("error reading keyblob of '%s': %s\n", filename, gpg_strerror (err)); goto leave; } *r_enckeyblob = msg; msg = NULL; *r_enckeybloblen = msglen; leave: xfree (msg); es_fclose (fp); return err; }
/* VERIFY This does a verify operation on the message send to the input-FD. The result is written out using status lines. If an output FD was given, the signed text will be written to that. If the signature is a detached one, the server will inquire about the signed material and the client must provide it. */ static gpg_error_t cmd_verify (assuan_context_t ctx, char *line) { int rc; #ifdef HAVE_W32_SYSTEM (void)ctx; (void)line; rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); #else ctrl_t ctrl = assuan_get_pointer (ctx); gnupg_fd_t fd = assuan_get_input_fd (ctx); gnupg_fd_t out_fd = assuan_get_output_fd (ctx); estream_t out_fp = NULL; /* FIXME: Revamp this code it is nearly to 3 years old and was only intended as a quick test. */ (void)line; if (fd == GNUPG_INVALID_FD) return gpg_error (GPG_ERR_ASS_NO_INPUT); if (out_fd != GNUPG_INVALID_FD) { es_syshd_t syshd; #ifdef HAVE_W32_SYSTEM syshd.type = ES_SYSHD_HANDLE; syshd.u.handle = out_fd; #else syshd.type = ES_SYSHD_FD; syshd.u.fd = out_fd; #endif out_fp = es_sysopen_nc (&syshd, "w"); if (!out_fp) return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); } log_debug ("WARNING: The server mode is WORK " "IN PROGRESS and not ready for use\n"); rc = gpg_verify (ctrl, fd, ctrl->server_local->message_fd, out_fp); es_fclose (out_fp); close_message_fd (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); #endif if (rc) log_error ("command '%s' failed: %s\n", "VERIFY", gpg_strerror (rc)); return rc; }
/* Send the key in {DATA,DATALEN} to the keyserver identified by URI. */ gpg_error_t ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen) { gpg_error_t err; char *hostport = NULL; char *request = NULL; estream_t fp = NULL; struct put_post_parm_s parm; char *armored = NULL; parm.datastring = NULL; err = armor_data (&armored, data, datalen); if (err) goto leave; parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS); if (!parm.datastring) { err = gpg_error_from_syserror (); goto leave; } xfree (armored); armored = NULL; /* Build the request string. */ hostport = make_host_part (uri->scheme, uri->host, uri->port); if (!hostport) { err = gpg_error_from_syserror (); goto leave; } request = strconcat (hostport, "/pks/add", NULL); if (!request) { err = gpg_error_from_syserror (); goto leave; } /* Send the request. */ err = send_request (ctrl, request, hostport, put_post_cb, &parm, &fp); if (err) goto leave; leave: es_fclose (fp); xfree (parm.datastring); xfree (armored); xfree (request); xfree (hostport); return err; }
/* Ask the dirmngr for the policy flags and return them as an estream * memory stream. If no policy flags are set, NULL is stored at * R_BUFFER. */ gpg_error_t wkd_get_policy_flags (const char *addrspec, estream_t *r_buffer) { gpg_error_t err; assuan_context_t ctx; struct wkd_get_parm_s parm; char *line = NULL; char *buffer = NULL; memset (&parm, 0, sizeof parm); *r_buffer = NULL; err = connect_dirmngr (&ctx); if (err) return err; line = es_bsprintf ("WKD_GET --policy-flags -- %s", addrspec); if (!line) { err = gpg_error_from_syserror (); goto leave; } if (strlen (line) + 2 >= ASSUAN_LINELENGTH) { err = gpg_error (GPG_ERR_TOO_LARGE); goto leave; } parm.memfp = es_fopenmem (0, "rwb"); if (!parm.memfp) { err = gpg_error_from_syserror (); goto leave; } err = assuan_transact (ctx, line, wkd_get_data_cb, &parm, NULL, NULL, wkd_get_status_cb, &parm); if (err) goto leave; es_rewind (parm.memfp); *r_buffer = parm.memfp; parm.memfp = 0; leave: es_free (buffer); es_fclose (parm.memfp); xfree (line); assuan_release (ctx); return err; }
static gpg_error_t cmd_genkey (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int inp_fd, out_fd; estream_t in_stream, out_stream; int rc; (void)line; inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); if (inp_fd == -1) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); if (out_fd == -1) return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); in_stream = es_fdopen_nc (inp_fd, "r"); if (!in_stream) return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen failed"); out_stream = es_fdopen_nc (out_fd, "w"); if (!out_stream) { es_fclose (in_stream); return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); } rc = gpgsm_genkey (ctrl, in_stream, out_stream); es_fclose (out_stream); es_fclose (in_stream); /* close and reset the fds */ assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); return rc; }
/* Test whether the container with name FILENAME is a suitable G13 container. This function may even be called on a mounted container. */ gpg_error_t g13_is_container (ctrl_t ctrl, const char *filename) { gpg_error_t err; estream_t fp = NULL; size_t dummy; (void)ctrl; /* Read just the prefix of the header. */ err = read_keyblob_prefix (filename, &fp, &dummy); if (!err) es_fclose (fp); return err; }
static gpg_error_t cmd_encrypt (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); certlist_t cl; int inp_fd, out_fd; estream_t out_fp; int rc; (void)line; inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); if (inp_fd == -1) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); if (out_fd == -1) return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); out_fp = es_fdopen_nc (out_fd, "w"); if (!out_fp) return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); /* Now add all encrypt-to marked recipients from the default list. */ rc = 0; if (!opt.no_encrypt_to && !ctrl->server_local->no_encrypt_to) { for (cl=ctrl->server_local->default_recplist; !rc && cl; cl = cl->next) if (cl->is_encrypt_to) rc = gpgsm_add_cert_to_certlist (ctrl, cl->cert, &ctrl->server_local->recplist, 1); } if (!rc) rc = ctrl->audit? 0 : start_audit_session (ctrl); if (!rc) rc = gpgsm_encrypt (assuan_get_pointer (ctx), ctrl->server_local->recplist, inp_fd, out_fp); es_fclose (out_fp); gpgsm_release_certlist (ctrl->server_local->recplist); ctrl->server_local->recplist = NULL; /* Close and reset the fd */ close_message_fd (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); return rc; }
static gpg_error_t cmd_getauditlog (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int out_fd; estream_t out_stream; int opt_data, opt_html; int rc; opt_data = has_option (line, "--data"); opt_html = has_option (line, "--html"); line = skip_options (line); if (!ctrl->audit) return gpg_error (GPG_ERR_NO_DATA); if (opt_data) { out_stream = es_fopencookie (ctx, "w", data_line_cookie_functions); if (!out_stream) return set_error (GPG_ERR_ASS_GENERAL, "error setting up a data stream"); } else { out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); if (out_fd == -1) return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); out_stream = es_fdopen_nc (out_fd, "w"); if (!out_stream) { return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed"); } } audit_print_result (ctrl->audit, out_stream, opt_html); rc = 0; es_fclose (out_stream); /* Close and reset the fd. */ if (!opt_data) assuan_close_output_fd (ctx); return rc; }
/* This thread feeds data to the given stream. */ static THREAD_RET_TYPE producer_thread (void *argaddr) { struct thread_arg *arg = argaddr; int i = 0; (void)arg; while (!arg->stop_me && i++ < 3) { show ("thread '%s' about to write\n", arg->name); es_fprintf (arg->stream, "This is '%s' count=%d\n", arg->name, i); es_fflush (arg->stream); } es_fclose (arg->stream); return THREAD_RET_VALUE; }
int keyserver_fetch (ctrl_t ctrl, strlist_t urilist) { gpg_error_t err; strlist_t sl; estream_t datastream; unsigned int options = opt.keyserver_options.import_options; /* Switch on fast-import, since fetch can handle more than one import and we don't want each set to rebuild the trustdb. Instead we do it once at the end. */ opt.keyserver_options.import_options |= IMPORT_FAST; for (sl=urilist; sl; sl=sl->next) { if (!opt.quiet) log_info (_("requesting key from '%s'\n"), sl->d); err = gpg_dirmngr_ks_fetch (ctrl, sl->d, &datastream); if (!err) { void *stats_handle; stats_handle = import_new_stats_handle(); import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL, opt.keyserver_options.import_options); import_print_stats (stats_handle); import_release_stats_handle (stats_handle); } else log_info (_("WARNING: unable to fetch URI %s: %s\n"), sl->d, gpg_strerror (err)); es_fclose (datastream); } opt.keyserver_options.import_options = options; /* If the original options didn't have fast import, and the trustdb is dirty, rebuild. */ if (!(opt.keyserver_options.import_options&IMPORT_FAST)) trustdb_check_or_update (); return 0; }
/* The public release function. */ void runner_release (runner_t runner) { gpg_error_t err; if (!runner) return; if (!--runner->refcount) return; err = mountinfo_del_mount (NULL, NULL, runner->identifier); if (err) log_error ("failed to remove mount with rid %u from mtab: %s\n", runner->identifier, gpg_strerror (err)); es_fclose (runner->status_fp); if (runner->in_fd != -1) close (runner->in_fd); if (runner->out_fd != -1) close (runner->out_fd); /* Fixme: close the process. */ /* Tell the engine to release its data. */ if (runner->handler_cleanup) runner->handler_cleanup (runner->handler_data); if (runner->pid != (pid_t)(-1)) { /* The process has not been cleaned up - do it now. */ gnupg_kill_process (runner->pid); /* (Actually we should use the program name and not the arbitrary NAME of the runner object. However it does not matter because that information is only used for diagnostics.) */ gnupg_wait_process (runner->name, runner->pid, 1, NULL); gnupg_release_process (runner->pid); } xfree (runner->name); xfree (runner); }
/* This thread eats data from the given stream. */ static THREAD_RET_TYPE consumer_thread (void *argaddr) { struct thread_arg *arg = argaddr; char buf[15]; (void)arg; while (!arg->stop_me) { show ("thread '%s' ready to read\n", arg->name); if (!es_fgets (buf, sizeof buf, arg->stream)) { show ("Thread '%s' received EOF or error\n", arg->name); break; } show ("Thread '%s' got: '%s'\n", arg->name, buf); } es_fclose (arg->stream); return THREAD_RET_VALUE; }
/* Helper to write mail to the output(s). */ gpg_error_t wks_send_mime (mime_maker_t mime) { gpg_error_t err; estream_t mail; /* Without any option we take a short path. */ if (!opt.use_sendmail && !opt.output) { es_set_binary (es_stdout); return mime_maker_make (mime, es_stdout); } mail = es_fopenmem (0, "w+b"); if (!mail) { err = gpg_error_from_syserror (); return err; } err = mime_maker_make (mime, mail); if (!err && opt.output) { es_rewind (mail); err = send_mail_to_file (mail, opt.output); } if (!err && opt.use_sendmail) { es_rewind (mail); err = send_mail (mail); } es_fclose (mail); return err; }
/* VERIFY This does a verify operation on the message send to the input-FD. The result is written out using status lines. If an output FD was given, the signed text will be written to that. If the signature is a detached one, the server will inquire about the signed material and the client must provide it. */ static gpg_error_t cmd_verify (assuan_context_t ctx, char *line) { int rc; ctrl_t ctrl = assuan_get_pointer (ctx); gnupg_fd_t fd = assuan_get_input_fd (ctx); gnupg_fd_t out_fd = assuan_get_output_fd (ctx); estream_t out_fp = NULL; /* FIXME: Revamp this code it is nearly to 3 years old and was only intended as a quick test. */ (void)line; if (fd == GNUPG_INVALID_FD) return gpg_error (GPG_ERR_ASS_NO_INPUT); if (out_fd != GNUPG_INVALID_FD) { out_fp = es_fdopen_nc (out_fd, "w"); if (!out_fp) return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); } log_debug ("WARNING: The server mode is WORK " "iN PROGRESS and not ready for use\n"); rc = gpg_verify (ctrl, fd, ctrl->server_local->message_fd, out_fp); es_fclose (out_fp); close_message_fd (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); if (rc) log_error ("command '%s' failed: %s\n", "VERIFY", gpg_strerror (rc)); return rc; }
/* Search all configured keyservers for keys matching PATTERNS and write the result to the provided output stream. */ gpg_error_t ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp) { gpg_error_t err = 0; int any_server = 0; uri_item_t uri; estream_t infp; if (!patterns) return gpg_error (GPG_ERR_NO_USER_ID); /* FIXME: We only take care of the first pattern. To fully support multiple patterns we might either want to run several queries in parallel and merge them. We also need to decide what to do with errors - it might not be the best idea to ignore an error from one server and silently continue with another server. For now we stop at the first error. */ for (uri = ctrl->keyservers; !err && uri; uri = uri->next) { if (uri->parsed_uri->is_http) { any_server = 1; err = ks_hkp_search (ctrl, uri->parsed_uri, patterns->d, &infp); if (!err) { err = copy_stream (infp, outfp); es_fclose (infp); break; } } } if (!any_server) err = gpg_error (GPG_ERR_NO_KEYSERVER); return err; }
/* The thread spawned by runner_spawn. */ static void * runner_thread (void *arg) { runner_t runner = arg; gpg_error_t err = 0; log_debug ("starting runner thread\n"); /* If a status_fp is available, the thread's main task is to read from that stream and invoke the backend's handler function. This is done on a line by line base and the line length is limited to a reasonable value (about 1000 characters). Other work will continue either due to an EOF of the stream or by demand of the engine. */ if (runner->status_fp) { int c, cont_line; unsigned int pos; char buffer[1024]; estream_t fp = runner->status_fp; pos = 0; cont_line = 0; while (!err && !runner->cancel_flag && (c=es_getc (fp)) != EOF) { buffer[pos++] = c; if (pos >= sizeof buffer - 5 || c == '\n') { buffer[pos - (c == '\n')] = 0; if (opt.verbose) log_info ("%s%s: %s\n", runner->name, cont_line? "(cont)":"", buffer); /* We handle only complete lines and ignore any stuff we possibly had to truncate. That is - at least for the encfs engine - not an issue because our changes to the tool make sure that only relatively short prompt lines are of interest. */ if (!cont_line && runner->handler) err = runner->handler (runner->handler_data, runner, buffer); pos = 0; cont_line = (c != '\n'); } } if (!err && runner->cancel_flag) log_debug ("runner thread noticed cancel flag\n"); else log_debug ("runner thread saw EOF\n"); if (pos) { buffer[pos] = 0; if (opt.verbose) log_info ("%s%s: %s\n", runner->name, cont_line? "(cont)":"", buffer); if (!cont_line && !err && runner->handler) err = runner->handler (runner->handler_data, runner, buffer); } if (!err && es_ferror (fp)) { err = gpg_error_from_syserror (); log_error ("error reading from %s: %s\n", runner->name, gpg_strerror (err)); } runner->status_fp = NULL; es_fclose (fp); log_debug ("runner thread closed status fp\n"); } /* Now wait for the process to finish. */ if (!err && runner->pid != (pid_t)(-1)) { int exitcode; log_debug ("runner thread waiting ...\n"); err = gnupg_wait_process (runner->name, runner->pid, 1, &exitcode); gnupg_release_process (runner->pid); runner->pid = (pid_t)(-1); if (err) log_error ("running '%s' failed (exitcode=%d): %s\n", runner->name, exitcode, gpg_strerror (err)); log_debug ("runner thread waiting finished\n"); } /* Get rid of the runner object (note: it is refcounted). */ log_debug ("runner thread releasing runner ...\n"); { runner_t r, rprev; for (r = running_threads, rprev = NULL; r; rprev = r, r = r->next_running) if (r == runner) { if (!rprev) running_threads = r->next_running; else rprev->next_running = r->next_running; r->next_running = NULL; break; } } runner_release (runner); log_debug ("runner thread runner released\n"); return NULL; }
/* Fork and exec the PGMNAME, see exechelp.h for details. */ gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[], int *except, void (*preexec)(void), unsigned int flags, estream_t *r_infp, estream_t *r_outfp, estream_t *r_errfp, pid_t *pid) { gpg_error_t err; int inpipe[2] = {-1, -1}; int outpipe[2] = {-1, -1}; int errpipe[2] = {-1, -1}; estream_t infp = NULL; estream_t outfp = NULL; estream_t errfp = NULL; int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK); if (r_infp) *r_infp = NULL; if (r_outfp) *r_outfp = NULL; if (r_errfp) *r_errfp = NULL; *pid = (pid_t)(-1); /* Always required. */ if (r_infp) { err = create_pipe_and_estream (inpipe, &infp, 1, nonblock); if (err) return err; } if (r_outfp) { err = create_pipe_and_estream (outpipe, &outfp, 0, nonblock); if (err) { if (infp) es_fclose (infp); else if (inpipe[1] != -1) close (inpipe[1]); if (inpipe[0] != -1) close (inpipe[0]); return err; } } if (r_errfp) { err = create_pipe_and_estream (errpipe, &errfp, 0, nonblock); if (err) { if (infp) es_fclose (infp); else if (inpipe[1] != -1) close (inpipe[1]); if (inpipe[0] != -1) close (inpipe[0]); if (outfp) es_fclose (outfp); else if (outpipe[0] != -1) close (outpipe[0]); if (outpipe[1] != -1) close (outpipe[1]); return err; } } *pid = fork (); if (*pid == (pid_t)(-1)) { err = my_error_from_syserror (); log_error (_("error forking process: %s\n"), gpg_strerror (err)); if (infp) es_fclose (infp); else if (inpipe[1] != -1) close (inpipe[1]); if (inpipe[0] != -1) close (inpipe[0]); if (outfp) es_fclose (outfp); else if (outpipe[0] != -1) close (outpipe[0]); if (outpipe[1] != -1) close (outpipe[1]); if (errfp) es_fclose (errfp); else if (errpipe[0] != -1) close (errpipe[0]); if (errpipe[1] != -1) close (errpipe[1]); return err; } if (!*pid) { /* This is the child. */ gcry_control (GCRYCTL_TERM_SECMEM); es_fclose (outfp); es_fclose (errfp); do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1], except, preexec); /*NOTREACHED*/ } /* This is the parent. */ if (inpipe[0] != -1) close (inpipe[0]); if (outpipe[1] != -1) close (outpipe[1]); if (errpipe[1] != -1) close (errpipe[1]); if (r_infp) *r_infp = infp; if (r_outfp) *r_outfp = outfp; if (r_errfp) *r_errfp = errfp; return 0; }
/* Fork and exec the PGMNAME, see exechelp.h for details. */ gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[], gpg_err_source_t errsource, void (*preexec)(void), unsigned int flags, estream_t infp, estream_t *r_outfp, estream_t *r_errfp, pid_t *pid) { gpg_error_t err; SECURITY_ATTRIBUTES sec_attr; PROCESS_INFORMATION pi = { NULL, /* Returns process handle. */ 0, /* Returns primary thread handle. */ 0, /* Returns pid. */ 0 /* Returns tid. */ }; STARTUPINFO si; int cr_flags; char *cmdline; HANDLE inhandle = INVALID_HANDLE_VALUE; HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; estream_t outfp = NULL; estream_t errfp = NULL; HANDLE nullhd[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; int i; es_syshd_t syshd; if (r_outfp) *r_outfp = NULL; if (r_errfp) *r_errfp = NULL; *pid = (pid_t)(-1); /* Always required. */ if (infp) { es_fflush (infp); es_rewind (infp); es_syshd (infp, &syshd); switch (syshd.type) { case ES_SYSHD_FD: inhandle = (HANDLE)_get_osfhandle (syshd.u.fd); break; case ES_SYSHD_SOCK: inhandle = (HANDLE)_get_osfhandle (syshd.u.sock); break; case ES_SYSHD_HANDLE: inhandle = syshd.u.handle; break; default: inhandle = INVALID_HANDLE_VALUE; break; } if (inhandle == INVALID_HANDLE_VALUE) return gpg_err_make (errsource, GPG_ERR_INV_VALUE); /* FIXME: In case we can't get a system handle (e.g. due to es_fopencookie we should create a piper and a feeder thread. */ } if (r_outfp) { if (create_inheritable_pipe (outpipe, 1)) { err = gpg_err_make (errsource, GPG_ERR_GENERAL); log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); return err; } syshd.type = ES_SYSHD_HANDLE; syshd.u.handle = outpipe[0]; outfp = es_sysopen (&syshd, "r"); if (!outfp) { err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); log_error (_("error creating a stream for a pipe: %s\n"), gpg_strerror (err)); CloseHandle (outpipe[0]); CloseHandle (outpipe[1]); outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE; return err; } } if (r_errfp) { if (create_inheritable_pipe (errpipe, 1)) { err = gpg_err_make (errsource, GPG_ERR_GENERAL); log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); return err; } syshd.type = ES_SYSHD_HANDLE; syshd.u.handle = errpipe[0]; errfp = es_sysopen (&syshd, "r"); if (!errfp) { err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); log_error (_("error creating a stream for a pipe: %s\n"), gpg_strerror (err)); CloseHandle (errpipe[0]); CloseHandle (errpipe[1]); errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE; if (outfp) es_fclose (outfp); else if (outpipe[0] != INVALID_HANDLE_VALUE) CloseHandle (outpipe[0]); if (outpipe[1] != INVALID_HANDLE_VALUE) CloseHandle (outpipe[1]); return err; } } /* Prepare security attributes. */ memset (&sec_attr, 0, sizeof sec_attr ); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; /* Build the command line. */ err = build_w32_commandline (pgmname, argv, &cmdline); if (err) return err; if (inhandle != INVALID_HANDLE_VALUE) nullhd[0] = w32_open_null (0); if (outpipe[1] != INVALID_HANDLE_VALUE) nullhd[1] = w32_open_null (0); if (errpipe[1] != INVALID_HANDLE_VALUE) nullhd[2] = w32_open_null (0); /* Start the process. Note that we can't run the PREEXEC function because this might change our own environment. */ (void)preexec; memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; si.hStdInput = inhandle == INVALID_HANDLE_VALUE? nullhd[0] : inhandle; si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1]; si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1]; cr_flags = (CREATE_DEFAULT_ERROR_MODE | ((flags & 128)? DETACHED_PROCESS : 0) | GetPriorityClass (GetCurrentProcess ()) | CREATE_SUSPENDED); /* log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline); */ if (!CreateProcess (pgmname, /* Program to start. */ cmdline, /* Command line arguments. */ &sec_attr, /* Process security attributes. */ &sec_attr, /* Thread security attributes. */ TRUE, /* Inherit handles. */ cr_flags, /* Creation flags. */ NULL, /* Environment. */ NULL, /* Use current drive/directory. */ &si, /* Startup information. */ &pi /* Returns process information. */ )) { log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); xfree (cmdline); if (outfp) es_fclose (outfp); else if (outpipe[0] != INVALID_HANDLE_VALUE) CloseHandle (outpipe[0]); if (outpipe[1] != INVALID_HANDLE_VALUE) CloseHandle (outpipe[1]); if (errfp) es_fclose (errfp); else if (errpipe[0] != INVALID_HANDLE_VALUE) CloseHandle (errpipe[0]); if (errpipe[1] != INVALID_HANDLE_VALUE) CloseHandle (errpipe[1]); return gpg_err_make (errsource, GPG_ERR_GENERAL); } xfree (cmdline); cmdline = NULL; /* Close the inherited handles to /dev/null. */ for (i=0; i < DIM (nullhd); i++) if (nullhd[i] != INVALID_HANDLE_VALUE) CloseHandle (nullhd[i]); /* Close the inherited ends of the pipes. */ if (outpipe[1] != INVALID_HANDLE_VALUE) CloseHandle (outpipe[1]); if (errpipe[1] != INVALID_HANDLE_VALUE) CloseHandle (errpipe[1]); /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ /* " dwProcessID=%d dwThreadId=%d\n", */ /* pi.hProcess, pi.hThread, */ /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ /* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */ /* Fixme: For unknown reasons AllowSetForegroundWindow returns an invalid argument error if we pass it the correct processID. As a workaround we use -1 (ASFW_ANY). */ if ( (flags & 64) ) gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/); /* Process has been created suspended; resume it now. */ ResumeThread (pi.hThread); CloseHandle (pi.hThread); if (r_outfp) *r_outfp = outfp; if (r_errfp) *r_errfp = errfp; *pid = handle_to_pid (pi.hProcess); return 0; }
/* Fork and exec the PGMNAME, see exechelp.h for details. */ gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[], gpg_err_source_t errsource, void (*preexec)(void), unsigned int flags, estream_t infp, estream_t *r_outfp, estream_t *r_errfp, pid_t *pid) { gpg_error_t err; int infd = -1; int outpipe[2] = {-1, -1}; int errpipe[2] = {-1, -1}; estream_t outfp = NULL; estream_t errfp = NULL; (void)flags; /* Currently not used. */ if (r_outfp) *r_outfp = NULL; if (r_errfp) *r_errfp = NULL; *pid = (pid_t)(-1); /* Always required. */ if (infp) { es_fflush (infp); es_rewind (infp); infd = es_fileno (infp); if (infd == -1) return gpg_err_make (errsource, GPG_ERR_INV_VALUE); } if (r_outfp) { err = create_pipe_and_estream (outpipe, &outfp, errsource); if (err) return err; } if (r_errfp) { err = create_pipe_and_estream (errpipe, &errfp, errsource); if (err) { if (outfp) es_fclose (outfp); else if (outpipe[0] != -1) close (outpipe[0]); if (outpipe[1] != -1) close (outpipe[1]); return err; } } *pid = fork (); if (*pid == (pid_t)(-1)) { err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); log_error (_("error forking process: %s\n"), gpg_strerror (err)); if (outfp) es_fclose (outfp); else if (outpipe[0] != -1) close (outpipe[0]); if (outpipe[1] != -1) close (outpipe[1]); if (errfp) es_fclose (errfp); else if (errpipe[0] != -1) close (errpipe[0]); if (errpipe[1] != -1) close (errpipe[1]); return err; } if (!*pid) { /* This is the child. */ gcry_control (GCRYCTL_TERM_SECMEM); es_fclose (outfp); es_fclose (errfp); do_exec (pgmname, argv, infd, outpipe[1], errpipe[1], preexec); /*NOTREACHED*/ } /* This is the parent. */ if (outpipe[1] != -1) close (outpipe[1]); if (errpipe[1] != -1) close (errpipe[1]); if (r_outfp) *r_outfp = outfp; if (r_errfp) *r_errfp = errfp; return 0; }