/* certreqgen-ui.c - Simple user interface for certreqgen.c * Copyright (C) 2007, 2010, 2011 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 3 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, see . */ #include #include #include #include #include #include #include #include #include "gpgsm.h" #include #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; } /* Chech whether we have a key for the key with HEXGRIP. Returns NULL if not or a string describing the type of the key (RSA, ELG, DSA, etc..). */ static const char * check_keygrip (ctrl_t ctrl, const char *hexgrip) { gpg_error_t err; ksba_sexp_t public; size_t publiclen; int algo; if (hexgrip[0] == '&') hexgrip++; err = gpgsm_agent_readkey (ctrl, 0, hexgrip, &public); if (err) return NULL; publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL); get_pk_algo_from_canon_sexp (public, publiclen, &algo); xfree (public); switch (algo) { case GCRY_PK_RSA: return "RSA"; case GCRY_PK_DSA: return "DSA"; case GCRY_PK_ELG: return "ELG"; case GCRY_PK_ECDSA: return "ECDSA"; default: return NULL; } } /* 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, estream_t output_stream) { gpg_error_t err; char *answer; int selection; estream_t fp = NULL; int method; char *keytype_buffer = NULL; const 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; int selfsigned; answer = NULL; init_membuf (&mb_email, 100); init_membuf (&mb_dns, 100); init_membuf (&mb_uri, 100); init_membuf (&mb_result, 512); again: /* 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 { xfree (answer); answer = tty_get (_("Your selection? ")); tty_kill_prompt (); selection = *answer? atoi (answer): 1; } while (!(selection >= 1 && selection <= 3)); method = selection; /* Get size of the key. */ if (method == 1) { keytype = "RSA"; for (;;) { xfree (answer); answer = tty_getf (_("What keysize do you want? (%u) "), defbits); tty_kill_prompt (); trim_spaces (answer); nbits = *answer? atoi (answer): defbits; 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) { for (;;) { xfree (answer); answer = tty_get (_("Enter the keygrip: ")); tty_kill_prompt (); trim_spaces (answer); if (!*answer) goto again; else if (strlen (answer) != 40 && !(answer[0] == '&' && strlen (answer+1) == 40)) tty_printf (_("Not a valid keygrip (expecting 40 hex digits)\n")); else if (!(keytype = check_keygrip (ctrl, answer)) ) tty_printf (_("No key with this keygrip\n")); else break; /* Okay. */ } xfree (keygrip); keygrip = answer; answer = NULL; nbits = 1024; /* A dummy value is sufficient. */ } else /* method == 3 */ { char *serialno; strlist_t keypairlist, sl; int count; err = gpgsm_agent_scd_serialno (ctrl, &serialno); if (err) { tty_printf (_("error reading the card: %s\n"), gpg_strerror (err)); goto again; } tty_printf (_("Serial number of the card: %s\n"), serialno); xfree (serialno); err = gpgsm_agent_scd_keypairinfo (ctrl, &keypairlist); if (err) { tty_printf (_("error reading the card: %s\n"), gpg_strerror (err)); goto again; } do { tty_printf (_("Available keys:\n")); for (count=1,sl=keypairlist; sl; sl = sl->next, count++) tty_printf (" (%d) %s\n", count, sl->d); xfree (answer); answer = tty_get (_("Your selection? ")); tty_kill_prompt (); trim_spaces (answer); selection = atoi (answer); } while (!(selection > 0 && selection < count)); for (count=1,sl=keypairlist; sl; sl = sl->next, count++) if (count == selection) break; s = sl->d; while (*s && !spacep (s)) s++; while (spacep (s)) s++; xfree (keygrip); keygrip = NULL; xfree (keytype_buffer); keytype_buffer = xasprintf ("card:%s", s); free_strlist (keypairlist); keytype = keytype_buffer; nbits = 1024; /* A dummy value is sufficient. */ } /* 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 { xfree (answer); answer = tty_get (_("Your selection? ")); tty_kill_prompt (); trim_spaces (answer); selection = *answer? atoi (answer): 1; 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. */ 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_dns, "Name-DNS: "); /* URIs. */ tty_printf (_("Enter URIs")); tty_printf (_(" (optional; end with an empty line):\n")); ask_mb_lines (&mb_uri, "Name-URI: "); /* Want a self-signed certificate? */ selfsigned = tty_get_answer_is_yes (_("Create self-signed certificate? (y/N) ")); /* 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); } if (keygrip) store_key_value_lf (&mb_result, "Key-Grip: ", keygrip); store_key_value_lf (&mb_result, "Key-Usage: ", keyusage); if (selfsigned) store_key_value_lf (&mb_result, "Serial: ", "random"); store_key_value_lf (&mb_result, "Name-DN: ", subject_name); 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 (_("These parameters are used:\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 ("Proceed with creation? (y/N) ")) goto leave; /* Now create a parameter file and generate the key. */ fp = es_fopenmem (0, "w+"); if (!fp) { log_error (_("error creating temporary file: %s\n"), strerror (errno)); goto leave; } es_fputs (result, fp); es_rewind (fp); if (selfsigned) tty_printf ("%s", _("Now creating self-signed certificate. ")); else tty_printf ("%s", _("Now creating certificate request. ")); tty_printf ("%s", _("This may take a while ...\n")); { int save_pem = ctrl->create_pem; ctrl->create_pem = 1; /* Force creation of PEM. */ err = gpgsm_genkey (ctrl, fp, output_stream); ctrl->create_pem = save_pem; } if (!err) { if (selfsigned) tty_printf (_("Ready.\n")); else tty_printf (_("Ready. You should now send this request to your CA.\n")); } goto leave; mem_error: log_error (_("resource problem: out of core\n")); leave: es_fclose (fp); xfree (answer); xfree (subject_name); xfree (keytype_buffer); 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); }