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