diff options
Diffstat (limited to 'sm')
-rw-r--r-- | sm/ChangeLog | 12 | ||||
-rw-r--r-- | sm/Makefile.am | 1 | ||||
-rw-r--r-- | sm/call-agent.c | 157 | ||||
-rw-r--r-- | sm/certreqgen-ui.c | 317 | ||||
-rw-r--r-- | sm/certreqgen.c | 14 | ||||
-rw-r--r-- | sm/gpgsm.c | 11 | ||||
-rw-r--r-- | sm/gpgsm.h | 7 | ||||
-rw-r--r-- | sm/server.c | 2 |
8 files changed, 362 insertions, 159 deletions
diff --git a/sm/ChangeLog b/sm/ChangeLog index 896b5c368..737a57d0c 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,15 @@ +2007-06-21 Werner Koch <[email protected]> + + * certreqgen-ui.c: New. + * gpgsm.c (main): Let --gen-key call it. + * certreqgen.c (gpgsm_genkey): Add optional IN_STREAM arg and + adjusted caller. + + * gpgsm.h (ctrl_t): Remove. It is now declared in ../common/util.h. + + * call-agent.c (start_agent): Factored almost all code out to + ../common/asshelp.c. + 2007-06-20 Werner Koch <[email protected]> * call-agent.c (start_agent) [W32]: Start the agent on the fly. diff --git a/sm/Makefile.am b/sm/Makefile.am index 7e5c154ac..e56064812 100644 --- a/sm/Makefile.am +++ b/sm/Makefile.am @@ -50,6 +50,7 @@ gpgsm_SOURCES = \ export.c \ delete.c \ certreqgen.c \ + certreqgen-ui.c \ qualified.c diff --git a/sm/call-agent.c b/sm/call-agent.c index b30fe60df..9483785cc 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -38,11 +38,10 @@ #include "asshelp.h" #include "keydb.h" /* fixme: Move this to import.c */ #include "membuf.h" -#include "exechelp.h" static assuan_context_t agent_ctx = NULL; -static int force_pipe_server = 0; + struct cipher_parm_s { @@ -72,161 +71,25 @@ struct learn_parm_s static int start_agent (ctrl_t ctrl) { - int rc = 0; - char *infostr, *p; - assuan_context_t ctx; - if (agent_ctx) return 0; /* fixme: We need a context for each thread or serialize the access to the agent (which is suitable given that the agent is not MT. */ - infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO"); - if (!infostr || !*infostr) - { - char *sockname; - - /* First check whether we can connect at the standard - socket. */ - sockname = make_filename (opt.homedir, "S.gpg-agent", NULL); - rc = assuan_socket_connect (&ctx, sockname, 0); - - if (rc) - { - /* With no success start a new server. */ - if (opt.verbose) - log_info (_("no running gpg-agent - starting one\n")); - - gpgsm_status (ctrl, STATUS_PROGRESS, "starting_agent ? 0 0"); - - if (fflush (NULL)) - { - gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); - log_error ("error flushing pending output: %s\n", - strerror (errno)); - xfree (sockname); - return tmperr; - } - - if (!opt.agent_program || !*opt.agent_program) - opt.agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT); - -#ifdef HAVE_W32_SYSTEM - { - /* Under Windows we start the server in daemon mode. This - is because the default is to use the standard socket - and thus there is no need for the GPG_AGENT_INFO - envvar. This is possible as we don't have a real unix - domain socket but use a plain file and thus there is no - need to care about non-local file systems. */ - const char *argv[3]; - - /* The --no-reuse-standard option makes sure that we don't - start a second instance of a agent in case another - process has started one in the meantime. */ - argv[0] = "--daemon"; - argv[1] = "--no-reuse-standard-socket"; - argv[2] = NULL; - - rc = gnupg_spawn_process_detached (opt.agent_program, argv, NULL); - if (rc) - log_debug ("failed to start agent `%s': %s\n", - opt.agent_program, gpg_strerror (rc)); - else - { - /* Give the agent some time to prepare itself. */ - gnupg_sleep (3); - /* Now try again to connect the agent. */ - rc = assuan_socket_connect (&ctx, sockname, 0); - } - } -#else /*!HAVE_W32_SYSTEM*/ - { - const char *pgmname; - const char *argv[3]; - int no_close_list[3]; - int i; - - if ( !(pgmname = strrchr (opt.agent_program, '/'))) - pgmname = opt.agent_program; - else - pgmname++; - - argv[0] = pgmname; - argv[1] = "--server"; - argv[2] = NULL; - - i=0; - if (log_get_fd () != -1) - no_close_list[i++] = log_get_fd (); - no_close_list[i++] = fileno (stderr); - no_close_list[i] = -1; - - /* Connect to the agent and perform initial handshaking. */ - rc = assuan_pipe_connect (&ctx, opt.agent_program, argv, - no_close_list); - } -#endif /*!HAVE_W32_SYSTEM*/ - } - xfree (sockname); - } - else - { - int prot; - int pid; - - infostr = xstrdup (infostr); - if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr) - { - log_error (_("malformed GPG_AGENT_INFO environment variable\n")); - xfree (infostr); - force_pipe_server = 1; - return start_agent (ctrl); - } - *p++ = 0; - pid = atoi (p); - while (*p && *p != PATHSEP_C) - p++; - prot = *p? atoi (p+1) : 0; - if (prot != 1) - { - log_error (_("gpg-agent protocol version %d is not supported\n"), - prot); - xfree (infostr); - force_pipe_server = 1; - return start_agent (ctrl); - } - - rc = assuan_socket_connect (&ctx, infostr, pid); - xfree (infostr); - if (gpg_err_code (rc) == GPG_ERR_ASS_CONNECT_FAILED) - { - log_info (_("can't connect to the agent - trying fall back\n")); - force_pipe_server = 1; - return start_agent (ctrl); - } - } - if (rc) - { - log_error ("can't connect to the agent: %s\n", gpg_strerror (rc)); - return gpg_error (GPG_ERR_NO_AGENT); - } - agent_ctx = ctx; - - if (DBG_ASSUAN) - log_debug ("connection to agent established\n"); + return start_new_gpg_agent (&agent_ctx, + GPG_ERR_SOURCE_DEFAULT, + opt.homedir, + opt.agent_program, + opt.display, opt.ttyname, opt.ttytype, + opt.lc_ctype, opt.lc_messages, + opt.verbose, DBG_ASSUAN, + gpgsm_status2, ctrl); - rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); - if (rc) - return rc; - - return send_pinentry_environment (agent_ctx, GPG_ERR_SOURCE_DEFAULT, - opt.display, opt.ttyname, opt.ttytype, - opt.lc_ctype, opt.lc_messages); } + static int membuf_data_cb (void *opaque, const void *buffer, size_t length) { diff --git a/sm/certreqgen-ui.c b/sm/certreqgen-ui.c new file mode 100644 index 000000000..738999ff8 --- /dev/null +++ b/sm/certreqgen-ui.c @@ -0,0 +1,317 @@ +/* certreqgen-ui.c - Simple user interface for certreqgen.c + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <assert.h> + +#include "gpgsm.h" +#include <gcrypt.h> + +#include "i18n.h" +#include "ttyio.h" +#include "membuf.h" + + +/* Prompt for lines and append them to MB. */ +static void +ask_mb_lines (membuf_t *mb, const char *prefix) +{ + char *answer = NULL; + + do + { + xfree (answer); + answer = tty_get ("> "); + tty_kill_prompt (); + trim_spaces (answer); + if (*answer) + { + put_membuf_str (mb, prefix); + put_membuf_str (mb, answer); + put_membuf (mb, "\n", 1); + } + } + while (*answer); + xfree (answer); +} + +/* Helper to store stuff in a membuf. */ +void +store_key_value_lf (membuf_t *mb, const char *key, const char *value) +{ + put_membuf_str (mb, key); + put_membuf_str (mb, value); + put_membuf (mb, "\n", 1); +} + +/* Helper tp store a membuf create by mb_ask_lines into MB. Returns + -1 on error. */ +int +store_mb_lines (membuf_t *mb, membuf_t *lines) +{ + char *p; + + if (get_membuf_len (lines)) + { + put_membuf (lines, "", 1); + p = get_membuf (lines, NULL); + if (!p) + return -1; + put_membuf_str (mb, p); + xfree (p); + } + return 0; +} + + +/* This function is used to create a certificate request from the + command line. In the past the similar gpgsm-gencert.sh script has + been used for it; however that scripts requires a full Unix shell + and thus is not suitable for the Windows port. So here is the + re-implementation. */ +void +gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp) +{ + gpg_error_t err; + char *answer; + int selection; + FILE *fp = NULL; + int method; + char *keytype; + char *keygrip = NULL; + unsigned int nbits; + int minbits = 1024; + int maxbits = 4096; + int defbits = 2048; + const char *keyusage; + char *subject_name; + membuf_t mb_email, mb_dns, mb_uri, mb_result; + char *result = NULL; + int i; + const char *s, *s2; + + init_membuf (&mb_email, 100); + init_membuf (&mb_dns, 100); + init_membuf (&mb_uri, 100); + init_membuf (&mb_result, 512); + + /* Get the type of the key. */ + tty_printf (_("Please select what kind of key you want:\n")); + tty_printf (_(" (%d) RSA\n"), 1 ); + tty_printf (_(" (%d) Existing key\n"), 2 ); + tty_printf (_(" (%d) Existing key from card\n"), 3 ); + + do + { + answer = tty_get (_("Your selection? ")); + tty_kill_prompt (); + selection = *answer? atoi (answer): 1; + xfree (answer); + } + while (!(selection >= 1 && selection <= 3)); + method = selection; + + /* Get size of the key. */ + if (method == 1) + { + keytype = xstrdup ("RSA"); + for (;;) + { + answer = tty_getf (_("What keysize do you want? (%u) "), defbits); + tty_kill_prompt (); + nbits = *answer? atoi (answer): defbits; + xfree (answer); + if (nbits < minbits || nbits > maxbits) + tty_printf(_("%s keysizes must be in the range %u-%u\n"), + "RSA", minbits, maxbits); + else + break; /* Okay. */ + } + tty_printf (_("Requested keysize is %u bits\n"), nbits); + /* We round it up so that it better matches the word size. */ + if (( nbits % 64)) + { + nbits = ((nbits + 63) / 64) * 64; + tty_printf (_("rounded up to %u bits\n"), nbits); + } + } + else if (method == 2) + { + tty_printf ("Not yet supported; " + "use the gpgsm-gencert.sh script instead\n"); + keytype = xstrdup ("RSA"); + nbits = defbits; /* We need a dummy value. */ + } + else /* method == 3 */ + { + tty_printf ("Not yet supported; " + "use the gpgsm-gencert.sh script instead\n"); + keytype = xstrdup ("card:foobar"); + nbits = defbits; /* We need a dummy value. */ + } + + /* Ask for the key usage. */ + tty_printf (_("Possible actions for a %s key:\n"), "RSA"); + tty_printf (_(" (%d) sign, encrypt\n"), 1 ); + tty_printf (_(" (%d) sign\n"), 2 ); + tty_printf (_(" (%d) encrypt\n"), 3 ); + do + { + answer = tty_get (_("Your selection? ")); + tty_kill_prompt (); + selection = *answer? atoi (answer): 1; + xfree (answer); + switch (selection) + { + case 1: keyusage = "sign, encrypt"; break; + case 2: keyusage = "sign"; break; + case 3: keyusage = "encrypt"; break; + default: keyusage = NULL; break; + } + } + while (!keyusage); + + /* Get the subject name. */ + answer = NULL; + do + { + size_t erroff, errlen; + + xfree (answer); + answer = tty_get (_("Enter the X.509 subject name: ")); + tty_kill_prompt (); + trim_spaces (answer); + if (!*answer) + tty_printf (_("No subject name given\n")); + else if ( (err = ksba_dn_teststr (answer, 0, &erroff, &errlen)) ) + { + if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME) + tty_printf (_("Invalid subject name label `%.*s'\n"), + (int)errlen, answer+erroff); + else + { + /* TRANSLATORS: The 22 in the second string is the + length of the first string up to the "%s". Please + adjust it do the length of your translation. The + second string is merely passed to atoi so you can + drop everything after the number. */ + tty_printf (_("Invalid subject name `%s'\n"), answer); + tty_printf ("%*s^\n", + atoi (_("22 translator: see " + "certreg-ui.c:gpgsm_gencertreq_tty")) + + (int)erroff, ""); + } + *answer = 0; + } + } + while (!*answer); + subject_name = answer; + answer = NULL; + + /* Get the email addresses. */ + tty_printf (_("Enter email addresses")); + tty_printf (_(" (end with an empty line):\n")); + ask_mb_lines (&mb_email, "Name-Email: "); + + /* DNS names. */ + tty_printf (_("Enter DNS names")); + tty_printf (_(" (optional; end with an empty line):\n")); + ask_mb_lines (&mb_email, "Name-DNS: "); + + /* URIs. */ + tty_printf (_("Enter URIs")); + tty_printf (_(" (optional; end with an empty line):\n")); + ask_mb_lines (&mb_email, "Name-URI: "); + + + /* Put it all together. */ + store_key_value_lf (&mb_result, "Key-Type: ", keytype); + { + char numbuf[30]; + snprintf (numbuf, sizeof numbuf, "%u", nbits); + store_key_value_lf (&mb_result, "Key-Length: ", numbuf); + } + store_key_value_lf (&mb_result, "Key-Usage: ", keyusage); + store_key_value_lf (&mb_result, "Name-DN: ", subject_name); + if (keygrip) + store_key_value_lf (&mb_result, "Key-Grip: ", keygrip); + if (store_mb_lines (&mb_result, &mb_email)) + goto mem_error; + if (store_mb_lines (&mb_result, &mb_dns)) + goto mem_error; + if (store_mb_lines (&mb_result, &mb_uri)) + goto mem_error; + put_membuf (&mb_result, "", 1); + result = get_membuf (&mb_result, NULL); + if (!result) + goto mem_error; + + tty_printf (_("Parameters to be used for the certificate request:\n")); + for (s=result; (s2 = strchr (s, '\n')); s = s2+1, i++) + tty_printf (" %.*s\n", (int)(s2-s), s); + tty_printf ("\n"); + + + if (!tty_get_answer_is_yes ("Really create request? (y/N) ")) + goto leave; + + /* Now create a parameter file and generate the key. */ + fp = tmpfile (); + if (!fp) + { + log_error (_("error creating temporary file: %s\n"), strerror (errno)); + goto leave; + } + fputs (result, fp); + rewind (fp); + tty_printf (_("Now creating certificate request. " + "This may take a while ...\n")); + { + int save_pem = ctrl->create_pem; + ctrl->create_pem = 1; /* Force creation of PEM. */ + err = gpgsm_genkey (ctrl, -1, fp, output_fp); + ctrl->create_pem = save_pem; + } + if (!err) + tty_printf (_("Ready. You should now send this request to your CA.\n")); + + + goto leave; + mem_error: + log_error (_("resource problem: out or core\n")); + leave: + if (fp) + fclose (fp); + xfree (keytype); + xfree (subject_name); + xfree (keygrip); + xfree (get_membuf (&mb_email, NULL)); + xfree (get_membuf (&mb_dns, NULL)); + xfree (get_membuf (&mb_uri, NULL)); + xfree (get_membuf (&mb_result, NULL)); + xfree (result); +} diff --git a/sm/certreqgen.c b/sm/certreqgen.c index 043e218c4..4ffd8363e 100644 --- a/sm/certreqgen.c +++ b/sm/certreqgen.c @@ -831,17 +831,20 @@ create_request (ctrl_t ctrl, -/* Create a new key by reading the parameters from in_fd. Multiple - keys may be created */ +/* Create a new key by reading the parameters from in_fd or in_stream. + Multiple keys may be created */ int -gpgsm_genkey (ctrl_t ctrl, int in_fd, FILE *out_fp) +gpgsm_genkey (ctrl_t ctrl, int in_fd, FILE *in_stream, FILE *out_fp) { int rc; FILE *in_fp; Base64Context b64writer = NULL; ksba_writer_t writer; - in_fp = fdopen (dup (in_fd), "rb"); + if (in_stream) + in_fp = in_stream; + else + in_fp = fdopen (dup (in_fd), "rb"); if (!in_fp) { gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); @@ -877,7 +880,8 @@ gpgsm_genkey (ctrl_t ctrl, int in_fd, FILE *out_fp) leave: gpgsm_destroy_writer (b64writer); - fclose (in_fp); + if (!in_stream) + fclose (in_fp); return rc; } diff --git a/sm/gpgsm.c b/sm/gpgsm.c index b6a3e69c2..ee6cf08cd 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -1590,12 +1590,15 @@ main ( int argc, char **argv) case aKeygen: /* Generate a key; well kind of. */ - log_error - (_("key generation is not available from the commandline\n")); - log_info (_("please use the script \"%s\" to generate a new key\n"), - "gpgsm-gencert.sh"); + { + FILE *fp = open_fwrite (opt.outfile?opt.outfile:"-"); + gpgsm_gencertreq_tty (&ctrl, fp); + if (fp != stdout) + fclose (fp); + } break; + case aImport: gpgsm_import_files (&ctrl, argc, argv, open_read); break; diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 727b1cf98..62b106bfc 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -167,7 +167,6 @@ struct server_control_s signer) */ int use_ocsp; /* Set to true if OCSP should be used. */ }; -typedef struct server_control_s *ctrl_t; /* Data structure used in base64.c. */ @@ -317,7 +316,11 @@ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int in_fd, FILE *out_fp); int gpgsm_decrypt (ctrl_t ctrl, int in_fd, FILE *out_fp); /*-- certreqgen.c --*/ -int gpgsm_genkey (ctrl_t ctrl, int in_fd, FILE *out_fp); +int gpgsm_genkey (ctrl_t ctrl, int in_fd, FILE *in_stream, FILE *out_fp); + +/*-- certreqgen-ui.c --*/ +void gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *out_fp); + /*-- qualified.c --*/ gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert, diff --git a/sm/server.c b/sm/server.c index 278f4ecbf..27749799c 100644 --- a/sm/server.c +++ b/sm/server.c @@ -865,7 +865,7 @@ cmd_genkey (assuan_context_t ctx, char *line) out_fp = fdopen ( dup(out_fd), "w"); if (!out_fp) return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed"); - rc = gpgsm_genkey (ctrl, inp_fd, out_fp); + rc = gpgsm_genkey (ctrl, inp_fd, NULL, out_fp); fclose (out_fp); /* close and reset the fds */ |