Line data Source code
1 : /* certreqgen-ui.c - Simple user interface for certreqgen.c
2 : * Copyright (C) 2007, 2010, 2011 Free Software Foundation, Inc.
3 : *
4 : * This file is part of GnuPG.
5 : *
6 : * GnuPG is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * GnuPG is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, see <https://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include <config.h>
21 : #include <stdio.h>
22 : #include <stdlib.h>
23 : #include <string.h>
24 : #include <errno.h>
25 : #include <unistd.h>
26 : #include <time.h>
27 : #include <assert.h>
28 :
29 : #include "gpgsm.h"
30 : #include <gcrypt.h>
31 :
32 : #include "i18n.h"
33 : #include "ttyio.h"
34 : #include "membuf.h"
35 :
36 :
37 : /* Prompt for lines and append them to MB. */
38 : static void
39 0 : ask_mb_lines (membuf_t *mb, const char *prefix)
40 : {
41 0 : char *answer = NULL;
42 :
43 : do
44 : {
45 0 : xfree (answer);
46 0 : answer = tty_get ("> ");
47 0 : tty_kill_prompt ();
48 0 : trim_spaces (answer);
49 0 : if (*answer)
50 : {
51 0 : put_membuf_str (mb, prefix);
52 0 : put_membuf_str (mb, answer);
53 0 : put_membuf (mb, "\n", 1);
54 : }
55 : }
56 0 : while (*answer);
57 0 : xfree (answer);
58 0 : }
59 :
60 : /* Helper to store stuff in a membuf. */
61 : void
62 0 : store_key_value_lf (membuf_t *mb, const char *key, const char *value)
63 : {
64 0 : put_membuf_str (mb, key);
65 0 : put_membuf_str (mb, value);
66 0 : put_membuf (mb, "\n", 1);
67 0 : }
68 :
69 : /* Helper tp store a membuf create by mb_ask_lines into MB. Returns
70 : -1 on error. */
71 : int
72 0 : store_mb_lines (membuf_t *mb, membuf_t *lines)
73 : {
74 : char *p;
75 :
76 0 : if (get_membuf_len (lines))
77 : {
78 0 : put_membuf (lines, "", 1);
79 0 : p = get_membuf (lines, NULL);
80 0 : if (!p)
81 0 : return -1;
82 0 : put_membuf_str (mb, p);
83 0 : xfree (p);
84 : }
85 0 : return 0;
86 : }
87 :
88 :
89 : /* Chech whether we have a key for the key with HEXGRIP. Returns NULL
90 : if not or a string describing the type of the key (RSA, ELG, DSA,
91 : etc..). */
92 : static const char *
93 0 : check_keygrip (ctrl_t ctrl, const char *hexgrip)
94 : {
95 : gpg_error_t err;
96 : ksba_sexp_t public;
97 : size_t publiclen;
98 : const char *algostr;
99 :
100 0 : if (hexgrip[0] == '&')
101 0 : hexgrip++;
102 :
103 0 : err = gpgsm_agent_readkey (ctrl, 0, hexgrip, &public);
104 0 : if (err)
105 0 : return NULL;
106 0 : publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL);
107 :
108 0 : get_pk_algo_from_canon_sexp (public, publiclen, &algostr);
109 0 : xfree (public);
110 :
111 0 : if (!algostr)
112 0 : return NULL;
113 0 : else if (!strcmp (algostr, "rsa"))
114 0 : return "RSA";
115 0 : else if (!strcmp (algostr, "dsa"))
116 0 : return "DSA";
117 0 : else if (!strcmp (algostr, "elg"))
118 0 : return "ELG";
119 0 : else if (!strcmp (algostr, "ecdsa"))
120 0 : return "ECDSA";
121 : else
122 0 : return NULL;
123 : }
124 :
125 :
126 : /* This function is used to create a certificate request from the
127 : command line. In the past the similar gpgsm-gencert.sh script has
128 : been used for it; however that scripts requires a full Unix shell
129 : and thus is not suitable for the Windows port. So here is the
130 : re-implementation. */
131 : void
132 0 : gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream)
133 : {
134 : gpg_error_t err;
135 : char *answer;
136 : int selection;
137 0 : estream_t fp = NULL;
138 : int method;
139 0 : char *keytype_buffer = NULL;
140 : const char *keytype;
141 0 : char *keygrip = NULL;
142 : unsigned int nbits;
143 0 : int minbits = 1024;
144 0 : int maxbits = 4096;
145 0 : int defbits = 2048;
146 : const char *keyusage;
147 : char *subject_name;
148 : membuf_t mb_email, mb_dns, mb_uri, mb_result;
149 0 : char *result = NULL;
150 : const char *s, *s2;
151 : int selfsigned;
152 :
153 0 : answer = NULL;
154 0 : init_membuf (&mb_email, 100);
155 0 : init_membuf (&mb_dns, 100);
156 0 : init_membuf (&mb_uri, 100);
157 0 : init_membuf (&mb_result, 512);
158 :
159 : again:
160 : /* Get the type of the key. */
161 0 : tty_printf (_("Please select what kind of key you want:\n"));
162 0 : tty_printf (_(" (%d) RSA\n"), 1 );
163 0 : tty_printf (_(" (%d) Existing key\n"), 2 );
164 0 : tty_printf (_(" (%d) Existing key from card\n"), 3 );
165 :
166 : do
167 : {
168 0 : xfree (answer);
169 0 : answer = tty_get (_("Your selection? "));
170 0 : tty_kill_prompt ();
171 0 : selection = *answer? atoi (answer): 1;
172 : }
173 0 : while (!(selection >= 1 && selection <= 3));
174 0 : method = selection;
175 :
176 : /* Get size of the key. */
177 0 : if (method == 1)
178 : {
179 0 : keytype = "RSA";
180 : for (;;)
181 : {
182 0 : xfree (answer);
183 0 : answer = tty_getf (_("What keysize do you want? (%u) "), defbits);
184 0 : tty_kill_prompt ();
185 0 : trim_spaces (answer);
186 0 : nbits = *answer? atoi (answer): defbits;
187 0 : if (nbits < minbits || nbits > maxbits)
188 0 : tty_printf(_("%s keysizes must be in the range %u-%u\n"),
189 : "RSA", minbits, maxbits);
190 : else
191 : break; /* Okay. */
192 0 : }
193 0 : tty_printf (_("Requested keysize is %u bits\n"), nbits);
194 : /* We round it up so that it better matches the word size. */
195 0 : if (( nbits % 64))
196 : {
197 0 : nbits = ((nbits + 63) / 64) * 64;
198 0 : tty_printf (_("rounded up to %u bits\n"), nbits);
199 : }
200 : }
201 0 : else if (method == 2)
202 : {
203 : for (;;)
204 : {
205 0 : xfree (answer);
206 0 : answer = tty_get (_("Enter the keygrip: "));
207 0 : tty_kill_prompt ();
208 0 : trim_spaces (answer);
209 :
210 0 : if (!*answer)
211 0 : goto again;
212 0 : else if (strlen (answer) != 40 &&
213 0 : !(answer[0] == '&' && strlen (answer+1) == 40))
214 0 : tty_printf (_("Not a valid keygrip (expecting 40 hex digits)\n"));
215 0 : else if (!(keytype = check_keygrip (ctrl, answer)) )
216 0 : tty_printf (_("No key with this keygrip\n"));
217 : else
218 0 : break; /* Okay. */
219 0 : }
220 0 : xfree (keygrip);
221 0 : keygrip = answer;
222 0 : answer = NULL;
223 0 : nbits = 1024; /* A dummy value is sufficient. */
224 : }
225 : else /* method == 3 */
226 : {
227 : char *serialno;
228 : strlist_t keypairlist, sl;
229 : int count;
230 :
231 0 : err = gpgsm_agent_scd_serialno (ctrl, &serialno);
232 0 : if (err)
233 : {
234 0 : tty_printf (_("error reading the card: %s\n"), gpg_strerror (err));
235 0 : goto again;
236 : }
237 0 : tty_printf (_("Serial number of the card: %s\n"), serialno);
238 0 : xfree (serialno);
239 :
240 0 : err = gpgsm_agent_scd_keypairinfo (ctrl, &keypairlist);
241 0 : if (err)
242 : {
243 0 : tty_printf (_("error reading the card: %s\n"), gpg_strerror (err));
244 0 : goto again;
245 : }
246 :
247 : do
248 : {
249 0 : tty_printf (_("Available keys:\n"));
250 0 : for (count=1,sl=keypairlist; sl; sl = sl->next, count++)
251 0 : tty_printf (" (%d) %s\n", count, sl->d);
252 0 : xfree (answer);
253 0 : answer = tty_get (_("Your selection? "));
254 0 : tty_kill_prompt ();
255 0 : trim_spaces (answer);
256 0 : selection = atoi (answer);
257 : }
258 0 : while (!(selection > 0 && selection < count));
259 :
260 0 : for (count=1,sl=keypairlist; sl; sl = sl->next, count++)
261 0 : if (count == selection)
262 0 : break;
263 :
264 0 : s = sl->d;
265 0 : while (*s && !spacep (s))
266 0 : s++;
267 0 : while (spacep (s))
268 0 : s++;
269 :
270 0 : xfree (keygrip);
271 0 : keygrip = NULL;
272 0 : xfree (keytype_buffer);
273 0 : keytype_buffer = xasprintf ("card:%s", s);
274 0 : free_strlist (keypairlist);
275 0 : keytype = keytype_buffer;
276 0 : nbits = 1024; /* A dummy value is sufficient. */
277 : }
278 :
279 : /* Ask for the key usage. */
280 0 : tty_printf (_("Possible actions for a %s key:\n"), "RSA");
281 0 : tty_printf (_(" (%d) sign, encrypt\n"), 1 );
282 0 : tty_printf (_(" (%d) sign\n"), 2 );
283 0 : tty_printf (_(" (%d) encrypt\n"), 3 );
284 : do
285 : {
286 0 : xfree (answer);
287 0 : answer = tty_get (_("Your selection? "));
288 0 : tty_kill_prompt ();
289 0 : trim_spaces (answer);
290 0 : selection = *answer? atoi (answer): 1;
291 0 : switch (selection)
292 : {
293 0 : case 1: keyusage = "sign, encrypt"; break;
294 0 : case 2: keyusage = "sign"; break;
295 0 : case 3: keyusage = "encrypt"; break;
296 0 : default: keyusage = NULL; break;
297 : }
298 : }
299 0 : while (!keyusage);
300 :
301 : /* Get the subject name. */
302 : do
303 : {
304 : size_t erroff, errlen;
305 :
306 0 : xfree (answer);
307 0 : answer = tty_get (_("Enter the X.509 subject name: "));
308 0 : tty_kill_prompt ();
309 0 : trim_spaces (answer);
310 0 : if (!*answer)
311 0 : tty_printf (_("No subject name given\n"));
312 0 : else if ( (err = ksba_dn_teststr (answer, 0, &erroff, &errlen)) )
313 : {
314 0 : if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME)
315 0 : tty_printf (_("Invalid subject name label '%.*s'\n"),
316 : (int)errlen, answer+erroff);
317 : else
318 : {
319 : /* TRANSLATORS: The 22 in the second string is the
320 : length of the first string up to the "%s". Please
321 : adjust it do the length of your translation. The
322 : second string is merely passed to atoi so you can
323 : drop everything after the number. */
324 0 : tty_printf (_("Invalid subject name '%s'\n"), answer);
325 0 : tty_printf ("%*s^\n",
326 0 : atoi (_("22 translator: see "
327 : "certreg-ui.c:gpgsm_gencertreq_tty"))
328 0 : + (int)erroff, "");
329 : }
330 0 : *answer = 0;
331 : }
332 : }
333 0 : while (!*answer);
334 0 : subject_name = answer;
335 0 : answer = NULL;
336 :
337 : /* Get the email addresses. */
338 0 : tty_printf (_("Enter email addresses"));
339 0 : tty_printf (_(" (end with an empty line):\n"));
340 0 : ask_mb_lines (&mb_email, "Name-Email: ");
341 :
342 : /* DNS names. */
343 0 : tty_printf (_("Enter DNS names"));
344 0 : tty_printf (_(" (optional; end with an empty line):\n"));
345 0 : ask_mb_lines (&mb_dns, "Name-DNS: ");
346 :
347 : /* URIs. */
348 0 : tty_printf (_("Enter URIs"));
349 0 : tty_printf (_(" (optional; end with an empty line):\n"));
350 0 : ask_mb_lines (&mb_uri, "Name-URI: ");
351 :
352 :
353 : /* Want a self-signed certificate? */
354 0 : selfsigned = tty_get_answer_is_yes
355 0 : (_("Create self-signed certificate? (y/N) "));
356 :
357 :
358 : /* Put it all together. */
359 0 : store_key_value_lf (&mb_result, "Key-Type: ", keytype);
360 : {
361 : char numbuf[30];
362 0 : snprintf (numbuf, sizeof numbuf, "%u", nbits);
363 0 : store_key_value_lf (&mb_result, "Key-Length: ", numbuf);
364 : }
365 0 : if (keygrip)
366 0 : store_key_value_lf (&mb_result, "Key-Grip: ", keygrip);
367 0 : store_key_value_lf (&mb_result, "Key-Usage: ", keyusage);
368 0 : if (selfsigned)
369 0 : store_key_value_lf (&mb_result, "Serial: ", "random");
370 0 : store_key_value_lf (&mb_result, "Name-DN: ", subject_name);
371 0 : if (store_mb_lines (&mb_result, &mb_email))
372 0 : goto mem_error;
373 0 : if (store_mb_lines (&mb_result, &mb_dns))
374 0 : goto mem_error;
375 0 : if (store_mb_lines (&mb_result, &mb_uri))
376 0 : goto mem_error;
377 0 : put_membuf (&mb_result, "", 1);
378 0 : result = get_membuf (&mb_result, NULL);
379 0 : if (!result)
380 0 : goto mem_error;
381 :
382 0 : tty_printf (_("These parameters are used:\n"));
383 0 : for (s=result; (s2 = strchr (s, '\n')); s = s2+1)
384 0 : tty_printf (" %.*s\n", (int)(s2-s), s);
385 0 : tty_printf ("\n");
386 :
387 0 : if (!tty_get_answer_is_yes ("Proceed with creation? (y/N) "))
388 0 : goto leave;
389 :
390 : /* Now create a parameter file and generate the key. */
391 0 : fp = es_fopenmem (0, "w+");
392 0 : if (!fp)
393 : {
394 0 : log_error (_("error creating temporary file: %s\n"), strerror (errno));
395 0 : goto leave;
396 : }
397 0 : es_fputs (result, fp);
398 0 : es_rewind (fp);
399 0 : if (selfsigned)
400 0 : tty_printf ("%s", _("Now creating self-signed certificate. "));
401 : else
402 0 : tty_printf ("%s", _("Now creating certificate request. "));
403 0 : tty_printf ("%s", _("This may take a while ...\n"));
404 :
405 : {
406 0 : int save_pem = ctrl->create_pem;
407 0 : ctrl->create_pem = 1; /* Force creation of PEM. */
408 0 : err = gpgsm_genkey (ctrl, fp, output_stream);
409 0 : ctrl->create_pem = save_pem;
410 : }
411 0 : if (!err)
412 : {
413 0 : if (selfsigned)
414 0 : tty_printf (_("Ready.\n"));
415 : else
416 0 : tty_printf
417 0 : (_("Ready. You should now send this request to your CA.\n"));
418 : }
419 :
420 :
421 0 : goto leave;
422 : mem_error:
423 0 : log_error (_("resource problem: out of core\n"));
424 : leave:
425 0 : es_fclose (fp);
426 0 : xfree (answer);
427 0 : xfree (subject_name);
428 0 : xfree (keytype_buffer);
429 0 : xfree (keygrip);
430 0 : xfree (get_membuf (&mb_email, NULL));
431 0 : xfree (get_membuf (&mb_dns, NULL));
432 0 : xfree (get_membuf (&mb_uri, NULL));
433 0 : xfree (get_membuf (&mb_result, NULL));
434 0 : xfree (result);
435 0 : }
|