Support the SETQUALITYBAR command of recent pinentries.
[gnupg.git] / agent / genkey.c
1 /* genkey.c - Generate a keypair
2  *      Copyright (C) 2002, 2003, 2004, 2007 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 <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <assert.h>
27
28 #include "agent.h"
29 #include "i18n.h"
30 #include "exechelp.h"
31 #include "sysutils.h"
32
33 static int
34 store_key (gcry_sexp_t private, const char *passphrase, int force)
35 {
36   int rc;
37   unsigned char *buf;
38   size_t len;
39   unsigned char grip[20];
40   
41   if ( !gcry_pk_get_keygrip (private, grip) )
42     {
43       log_error ("can't calculate keygrip\n");
44       return gpg_error (GPG_ERR_GENERAL);
45     }
46
47   len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, NULL, 0);
48   assert (len);
49   buf = gcry_malloc_secure (len);
50   if (!buf)
51       return out_of_core ();
52   len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len);
53   assert (len);
54
55   if (passphrase)
56     {
57       unsigned char *p;
58
59       rc = agent_protect (buf, passphrase, &p, &len);
60       if (rc)
61         {
62           xfree (buf);
63           return rc;
64         }
65       xfree (buf);
66       buf = p;
67     }
68
69   rc = agent_write_private_key (grip, buf, len, force);
70   xfree (buf);
71   return rc;
72 }
73
74
75 /* Count the number of non-alpha characters in S.  Control characters
76    and non-ascii characters are not considered.  */
77 static size_t
78 nonalpha_count (const char *s)
79 {
80   size_t n;
81
82   for (n=0; *s; s++)
83     if (isascii (*s) && ( isdigit (*s) || ispunct (*s) ))
84       n++;
85
86   return n;
87 }
88
89
90 /* Check PW against a list of pattern.  Return 0 if PW does not match
91    these pattern.  */
92 static int
93 check_passphrase_pattern (ctrl_t ctrl, const char *pw)
94 {
95   gpg_error_t err = 0;
96   const char *pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CHECK_PATTERN);
97   FILE *infp;
98   const char *argv[10];
99   pid_t pid;
100   int result, i;
101
102   infp = gnupg_tmpfile ();
103   if (!infp)
104     {
105       err = gpg_error_from_syserror ();
106       log_error (_("error creating temporary file: %s\n"), strerror (errno));
107       return 1; /* Error - assume password should not be used.  */
108     }
109
110   if (fwrite (pw, strlen (pw), 1, infp) != 1)
111     {
112       err = gpg_error_from_syserror ();
113       log_error (_("error writing to temporary file: %s\n"),
114                  strerror (errno));
115       fclose (infp);
116       return 1; /* Error - assume password should not be used.  */
117     }
118   rewind (infp);
119
120   i = 0;
121   argv[i++] = "--null";
122   argv[i++] = "--",
123   argv[i++] = opt.check_passphrase_pattern,
124   argv[i] = NULL;
125   assert (i < sizeof argv);
126
127   if (gnupg_spawn_process_fd (pgmname, argv, fileno (infp), -1, -1, &pid))
128     result = 1; /* Execute error - assume password should no be used.  */
129   else if (gnupg_wait_process (pgmname, pid, NULL))
130     result = 1; /* Helper returned an error - probably a match.  */
131   else
132     result = 0; /* Success; i.e. no match.  */
133
134   /* Overwrite our temporary file. */
135   rewind (infp);
136   for (i=((strlen (pw)+99)/100)*100; i > 0; i--)
137     putc ('\xff', infp);
138   fflush (infp);
139   fclose (infp);
140   return result;
141 }
142
143
144 static int 
145 take_this_one_anyway2 (ctrl_t ctrl, const char *desc, const char *anyway_btn)
146 {
147   gpg_error_t err;
148
149   if (opt.enforce_passphrase_constraints)
150     {
151       err = agent_show_message (ctrl, desc, _("Enter new passphrase"));
152       if (!err)
153         err = gpg_error (GPG_ERR_CANCELED);
154     }
155   else
156     err = agent_get_confirmation (ctrl, desc,
157                                   anyway_btn, _("Enter new passphrase"));
158   return err;
159 }
160
161
162 static int 
163 take_this_one_anyway (ctrl_t ctrl, const char *desc)
164 {
165   return take_this_one_anyway2 (ctrl, desc, _("Take this one anyway"));
166 }
167
168
169 /* Check whether the passphrase PW is suitable. Returns 0 if the
170    passphrase is suitable and true if it is not and the user should be
171    asked to provide a different one.  If SILENT is set, no message are
172    displayed.  */
173 int
174 check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent)
175 {
176   gpg_error_t err;
177   unsigned int minlen = opt.min_passphrase_len;
178   unsigned int minnonalpha = opt.min_passphrase_nonalpha;
179
180   if (!pw)
181     pw = "";
182
183   if (utf8_charcount (pw) < minlen ) 
184     {
185       char *desc;
186       
187       if (silent)
188         return gpg_error (GPG_ERR_INV_PASSPHRASE);
189
190       desc = xtryasprintf 
191         ( ngettext ("Warning:  You have entered a passphrase that%%0A"
192                     "is obviously not secure.  A passphrase should%%0A"
193                     "be at least %u character long.", 
194                     "Warning:  You have entered a passphrase that%%0A"
195                     "is obviously not secure.  A passphrase should%%0A"
196                     "be at least %u characters long.", minlen), minlen );
197       if (!desc)
198         return gpg_error_from_syserror ();
199       err = take_this_one_anyway (ctrl, desc);
200       xfree (desc);
201       if (err)
202         return err;
203     }
204
205   if (nonalpha_count (pw) < minnonalpha ) 
206     {
207       char *desc;
208
209       if (silent)
210         return gpg_error (GPG_ERR_INV_PASSPHRASE);
211
212       desc = xtryasprintf 
213         ( ngettext ("Warning:  You have entered a passphrase that%%0A"
214                     "is obviously not secure.  A passphrase should%%0A"
215                     "contain at least %u digit or special character.", 
216                     "Warning:  You have entered a passphrase that%%0A"
217                     "is obviously not secure.  A passphrase should%%0A"
218                     "contain at least %u digits or special characters.",
219                     minnonalpha), minnonalpha );
220       if (!desc)
221         return gpg_error_from_syserror ();
222       err = take_this_one_anyway (ctrl, desc);
223       xfree (desc);
224       if (err)
225         return err;
226     }
227
228   /* If configured check the passphrase against a list of know words
229      and pattern.  The actual test is done by an external program.
230      The warning message is generic to give the user no hint on how to
231      circumvent this list.  */
232   if (*pw && opt.check_passphrase_pattern &&
233       check_passphrase_pattern (ctrl, pw))
234     {
235       const char *desc =
236         /* */     _("Warning:  You have entered a passphrase that%0A"
237                     "is obviously not secure.  A passphrase may not%0A"
238                     "be a known term or match certain pattern.");
239
240       if (silent)
241         return gpg_error (GPG_ERR_INV_PASSPHRASE);
242
243       err = take_this_one_anyway (ctrl, desc);
244       if (err)
245         return err;
246     }
247
248   /* The final check is to warn about an empty passphrase. */
249   if (!*pw)
250     {
251       const char *desc = (opt.enforce_passphrase_constraints?
252                           _("You have not entered a passphrase!%0A"
253                             "An empty passphrase is not allowed.") :
254                           _("You have not entered a passphrase - "
255                             "this is in general a bad idea!%0A"
256                             "Please confirm that you do not want to "
257                             "have any protection on your key."));
258       
259       if (silent)
260         return gpg_error (GPG_ERR_INV_PASSPHRASE);
261
262       err = take_this_one_anyway2 (ctrl, desc,
263                                    _("Yes, protection is not needed"));
264       if (err)
265         return err;
266     }
267
268   return 0;
269 }
270
271
272 /* Callback function to compare the first entered PIN with the one
273    currently being entered. */
274 static int
275 reenter_compare_cb (struct pin_entry_info_s *pi)
276 {
277   const char *pin1 = pi->check_cb_arg;
278
279   if (!strcmp (pin1, pi->pin))
280     return 0; /* okay */
281   return -1;
282 }
283
284
285
286 /* Generate a new keypair according to the parameters given in
287    KEYPARAM */
288 int
289 agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
290               membuf_t *outbuf) 
291 {
292   gcry_sexp_t s_keyparam, s_key, s_private, s_public;
293   struct pin_entry_info_s *pi, *pi2;
294   int rc;
295   size_t len;
296   char *buf;
297
298   rc = gcry_sexp_sscan (&s_keyparam, NULL, keyparam, keyparamlen);
299   if (rc)
300     {
301       log_error ("failed to convert keyparam: %s\n", gpg_strerror (rc));
302       return gpg_error (GPG_ERR_INV_DATA);
303     }
304
305   /* Get the passphrase now, cause key generation may take a while. */
306   {
307     const char *text1 = _("Please enter the passphrase to%0A"
308                                "to protect your new key");
309     const char *text2 = _("Please re-enter this passphrase");
310     const char *initial_errtext = NULL;
311
312     pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
313     pi2 = pi + (sizeof *pi + 100);
314     pi->max_length = 100;
315     pi->max_tries = 3;
316     pi->with_qualitybar = 1;
317     pi2->max_length = 100;
318     pi2->max_tries = 3;
319     pi2->check_cb = reenter_compare_cb;
320     pi2->check_cb_arg = pi->pin;
321
322   next_try:
323     rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
324     initial_errtext = NULL;
325     if (!rc)
326       {
327         if (check_passphrase_constraints (ctrl, pi->pin, 0))
328           {
329             pi->failed_tries = 0;
330             pi2->failed_tries = 0;
331             goto next_try;
332           }
333         if (pi->pin && *pi->pin)
334           {
335             rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
336             if (rc == -1)
337               { /* The re-entered one did not match and the user did not
338                    hit cancel. */
339                 initial_errtext = _("does not match - try again");
340                 goto next_try;
341               }
342           }
343       }
344     if (rc)
345       {
346         xfree (pi);
347         return rc;
348       }
349         
350     if (!*pi->pin)
351       {
352         xfree (pi);
353         pi = NULL; /* User does not want a passphrase. */
354       }
355   }
356
357   rc = gcry_pk_genkey (&s_key, s_keyparam );
358   gcry_sexp_release (s_keyparam);
359   if (rc)
360     {
361       log_error ("key generation failed: %s\n", gpg_strerror (rc));
362       xfree (pi);
363       return rc;
364     }
365
366   /* break out the parts */
367   s_private = gcry_sexp_find_token (s_key, "private-key", 0);
368   if (!s_private)
369     {
370       log_error ("key generation failed: invalid return value\n");
371       gcry_sexp_release (s_key);
372       xfree (pi);
373       return gpg_error (GPG_ERR_INV_DATA);
374     }
375   s_public = gcry_sexp_find_token (s_key, "public-key", 0);
376   if (!s_public)
377     {
378       log_error ("key generation failed: invalid return value\n");
379       gcry_sexp_release (s_private);
380       gcry_sexp_release (s_key);
381       xfree (pi);
382       return gpg_error (GPG_ERR_INV_DATA);
383     }
384   gcry_sexp_release (s_key); s_key = NULL;
385   
386   /* store the secret key */
387   if (DBG_CRYPTO)
388     log_debug ("storing private key\n");
389   rc = store_key (s_private, pi? pi->pin:NULL, 0);
390   xfree (pi); pi = NULL;
391   gcry_sexp_release (s_private);
392   if (rc)
393     {
394       gcry_sexp_release (s_public);
395       return rc;
396     }
397
398   /* return the public key */
399   if (DBG_CRYPTO)
400     log_debug ("returning public key\n");
401   len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, NULL, 0);
402   assert (len);
403   buf = xtrymalloc (len);
404   if (!buf)
405     {
406       gpg_error_t tmperr = out_of_core ();
407       gcry_sexp_release (s_private);
408       gcry_sexp_release (s_public);
409       return tmperr;
410     }
411   len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, buf, len);
412   assert (len);
413   put_membuf (outbuf, buf, len);
414   gcry_sexp_release (s_public);
415   xfree (buf);
416
417   return 0;
418 }
419
420
421 \f
422 /* Apply a new passpahrse to the key S_SKEY and store it. */
423 int
424 agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey) 
425 {
426   struct pin_entry_info_s *pi, *pi2;
427   int rc;
428
429   {
430     const char *text1 = _("Please enter the new passphrase");
431     const char *text2 = _("Please re-enter this passphrase");
432     const char *initial_errtext = NULL;
433
434     pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
435     pi2 = pi + (sizeof *pi + 100);
436     pi->max_length = 100;
437     pi->max_tries = 3;
438     pi->with_qualitybar = 1;
439     pi2->max_length = 100;
440     pi2->max_tries = 3;
441     pi2->check_cb = reenter_compare_cb;
442     pi2->check_cb_arg = pi->pin;
443
444   next_try:
445     rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
446     initial_errtext = NULL;
447     if (!rc)
448       {
449         if (check_passphrase_constraints (ctrl, pi->pin, 0))
450           {
451             pi->failed_tries = 0;
452             pi2->failed_tries = 0;
453             goto next_try;
454           }
455         /* Unless the passphrase is empty, ask to confirm it.  */
456         if (pi->pin && *pi->pin)
457           {
458             rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
459             if (rc == -1)
460               { /* The re-entered one did not match and the user did not
461                    hit cancel. */
462                 initial_errtext = _("does not match - try again");
463                 goto next_try;
464               }
465           }
466       }
467     if (rc)
468       {
469         xfree (pi);
470         return rc;
471       }
472
473     if (!*pi->pin)
474       {
475         xfree (pi);
476         pi = NULL; /* User does not want a passphrase. */
477       }
478   }
479
480   rc = store_key (s_skey, pi? pi->pin:NULL, 1);
481   xfree (pi);
482   return 0;
483 }