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