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