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