A whole bunch of changes to allow building for Windows.
[gnupg.git] / sm / qualified.c
1 /* qualified.c - Routines related to qualified signatures
2  * Copyright (C) 2005, 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 2 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, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <assert.h>
28 #include <errno.h>
29 #ifdef HAVE_LOCALE_H
30 #include <locale.h>
31 #endif
32 #ifdef HAVE_LANGINFO_CODESET
33 #include <langinfo.h>
34 #endif
35
36 #include "gpgsm.h"
37 #include "i18n.h"
38 #include <ksba.h>
39
40
41 /* We open the file only once and keep the open file pointer as well
42    as the name of the file here.  Note that, a listname not equal to
43    NULL indicates that this module has been intialized and if the
44    LISTFP is also NULL, no list of qualified signatures exists. */
45 static char *listname;
46 static FILE *listfp;
47
48
49 /* Read the trustlist and return entry by entry.  KEY must point to a
50    buffer of at least 41 characters. COUNTRY shall be a buffer of at
51    least 3 characters to receive the country code of that qualified
52    signature (i.e. "de" for German and "be" for Belgium).
53
54    Reading a valid entry returns 0, EOF is indicated by GPG_ERR_EOF
55    and any other error condition is indicated by the appropriate error
56    code. */
57 static gpg_error_t
58 read_list (char *key, char *country, int *lnr)
59 {
60   gpg_error_t err;
61   int c, i, j;
62   char *p, line[256];
63
64   *key = 0;
65   *country = 0;
66
67   if (!listname)
68     {
69       listname = make_filename (gnupg_datadir (), "qualified.txt", NULL);
70       listfp = fopen (listname, "r");
71       if (!listfp && errno != ENOENT)
72         {
73           err = gpg_error_from_syserror ();
74           log_error (_("can't open `%s': %s\n"), listname, gpg_strerror (err));
75           return err;
76         }
77     }
78
79   if (!listfp)
80     return gpg_error (GPG_ERR_EOF);
81
82   do
83     {
84       if (!fgets (line, DIM(line)-1, listfp) )
85         {
86           if (feof (listfp))
87             return gpg_error (GPG_ERR_EOF);
88           return gpg_error_from_syserror ();
89         }
90
91       if (!*line || line[strlen(line)-1] != '\n')
92         {
93           /* Eat until end of line. */
94           while ( (c=getc (listfp)) != EOF && c != '\n')
95             ;
96           return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
97                                  : GPG_ERR_INCOMPLETE_LINE);
98         }
99       ++*lnr;
100       
101       /* Allow for empty lines and spaces */
102       for (p=line; spacep (p); p++)
103         ;
104     }
105   while (!*p || *p == '\n' || *p == '#');
106   
107   for (i=j=0; (p[i] == ':' || hexdigitp (p+i)) && j < 40; i++)
108     if ( p[i] != ':' )
109       key[j++] = p[i] >= 'a'? (p[i] & 0xdf): p[i];
110   key[j] = 0;
111   if (j != 40 || !(spacep (p+i) || p[i] == '\n'))
112     {
113       log_error (_("invalid formatted fingerprint in `%s', line %d\n"),
114                  listname, *lnr);
115       return gpg_error (GPG_ERR_BAD_DATA);
116     }
117   assert (p[i]);
118   i++;
119   while (spacep (p+i))
120     i++;
121   if ( p[i] >= 'a' && p[i] <= 'z' 
122        && p[i+1] >= 'a' && p[i+1] <= 'z' 
123        && (spacep (p+i+2) || p[i+2] == '\n'))
124     {
125       country[0] = p[i];
126       country[1] = p[i+1];
127       country[2] = 0;
128     }
129   else
130     {
131       log_error (_("invalid country code in `%s', line %d\n"), listname, *lnr);
132       return gpg_error (GPG_ERR_BAD_DATA);
133     }
134
135   return 0;
136 }
137
138
139
140
141 /* Check whether the certificate CERT is included in the list of
142    qualified certificates.  This list is similar to the "trustlist.txt"
143    as maintained by gpg-agent and includes fingerprints of root
144    certificates to be used for qualified (legally binding like
145    handwritten) signatures.  We keep this list system wide and not
146    per user because it is not a decision of the user. 
147
148    Returns: 0 if the certificate is included.  GPG_ERR_NOT_FOUND if it
149    is not in the list or any other error (e.g. if no list of
150    qualified signatures is available.  If COUNTRY has not been passed
151    as NULL a string witha maximum length of 2 will be copied into it;
152    thus the caller needs to provide a buffer of length 3. */
153 gpg_error_t
154 gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert, char *country)
155 {
156   gpg_error_t err;
157   char *fpr;
158   char key[41];
159   char mycountry[3];
160   int lnr = 0;
161
162   if (country)
163     *country = 0;
164
165   fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
166   if (!fpr)
167     return gpg_error (GPG_ERR_GENERAL);
168
169   if (listfp)
170     rewind (listfp);
171   while (!(err = read_list (key, mycountry, &lnr)))
172     {
173       if (!strcmp (key, fpr))
174         break;
175     }
176   if (gpg_err_code (err) == GPG_ERR_EOF)
177     err = gpg_error (GPG_ERR_NOT_FOUND);
178
179   if (!err && country)
180     strcpy (country, mycountry);
181
182   xfree (fpr);
183   return err;
184 }
185
186
187 /* We know that CERT is a qualified certificate.  Ask the user for
188    consent to actually create a signature using this certificate.
189    Returns: 0 for yes, GPG_ERR_CANCEL for no or any otehr error
190    code. */
191 gpg_error_t
192 gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert)
193 {
194   gpg_error_t err;
195   char *name, *subject, *buffer, *p;
196   const char *s;
197   char *orig_codeset = NULL;
198
199   name = ksba_cert_get_subject (cert, 0);
200   if (!name)
201     return gpg_error (GPG_ERR_GENERAL);
202   subject = gpgsm_format_name2 (name, 0);
203   ksba_free (name); name = NULL;
204
205 #ifdef ENABLE_NLS
206   /* The Assuan agent protocol requires us to transmit utf-8 strings */
207   orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
208 #ifdef HAVE_LANGINFO_CODESET
209   if (!orig_codeset)
210     orig_codeset = nl_langinfo (CODESET);
211 #endif
212   if (orig_codeset)
213     { /* We only switch when we are able to restore the codeset later.
214          Note that bind_textdomain_codeset does only return on memory
215          errors but not if a codeset is not available.  Thus we don't
216          bother printing a diagnostic here. */
217       orig_codeset = xstrdup (orig_codeset);
218       if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8"))
219         {
220           xfree (orig_codeset);
221           orig_codeset = NULL; 
222         }
223     }
224 #endif
225
226   if (asprintf (&name,
227                 _("You are about to create a signature using your "
228                   "certificate:\n"
229                   "\"%s\"\n"
230                   "This will create a qualified signature by law "
231                   "equated to a handwritten signature.\n\n%s%s"
232                   "Are you really sure that you want to do this?"),
233                 subject? subject:"?",
234                 opt.qualsig_approval? 
235                 "":
236                 _("Note, that this software is not officially approved "
237                   "to create or verify such signatures.\n"),
238                 opt.qualsig_approval? "":"\n"
239                 ) < 0 )
240     err = gpg_error_from_syserror ();
241   else
242     err = 0;
243
244 #ifdef ENABLE_NLS
245   if (orig_codeset)
246     bind_textdomain_codeset (PACKAGE_GT, orig_codeset);
247 #endif
248   xfree (orig_codeset);
249   xfree (subject);
250
251   if (err)
252     return err;
253
254   buffer = p = xtrymalloc (strlen (name) * 3 + 1);
255   if (!buffer)
256     {
257       err = gpg_error_from_syserror ();
258       free (name);
259       return err;
260     }
261   for (s=name; *s; s++)
262     {
263       if (*s < ' ' || *s == '+')
264         {
265           sprintf (p, "%%%02X", *(unsigned char *)s);
266           p += 3;
267         }
268       else if (*s == ' ')
269         *p++ = '+';
270       else
271         *p++ = *s;
272     }
273   *p = 0;
274   free (name); 
275
276
277   err = gpgsm_agent_get_confirmation (ctrl, buffer);
278
279   xfree (buffer);
280   return err;
281 }
282
283
284 /* Popup a prompt to inform the user that the signature created is not
285    a qualified one.  This is of course only done if we know that we
286    have been approved. */
287 gpg_error_t
288 gpgsm_not_qualified_warning (ctrl_t ctrl, ksba_cert_t cert)
289 {
290   gpg_error_t err;
291   char *name, *subject, *buffer, *p;
292   const char *s;
293 #ifdef ENABLE_NLS
294   char *orig_codeset = NULL;
295 #endif
296
297   if (!opt.qualsig_approval)
298     return 0;
299
300   name = ksba_cert_get_subject (cert, 0);
301   if (!name)
302     return gpg_error (GPG_ERR_GENERAL);
303   subject = gpgsm_format_name2 (name, 0);
304   ksba_free (name); name = NULL;
305
306
307 #ifdef ENABLE_NLS
308   /* The Assuan agent protocol requires us to transmit utf-8 strings */
309   orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
310 #ifdef HAVE_LANGINFO_CODESET
311   if (!orig_codeset)
312     orig_codeset = nl_langinfo (CODESET);
313 #endif
314   if (orig_codeset)
315     { /* We only switch when we are able to restore the codeset later.
316          Note that bind_textdomain_codeset does only return on memory
317          errors but not if a codeset is not available.  Thus we don't
318          bother printing a diagnostic here. */
319       orig_codeset = xstrdup (orig_codeset);
320       if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8"))
321         {
322           xfree (orig_codeset);
323           orig_codeset = NULL; 
324         }
325     }
326 #endif
327
328   if (asprintf (&name,
329                 _("You are about to create a signature using your "
330                   "certificate:\n"
331                   "\"%s\"\n"
332                   "Note, that this certificate will NOT create a "
333                   "qualified signature!"),
334                 subject? subject:"?") < 0 )
335     err = gpg_error_from_syserror ();
336   else
337     err = 0;
338
339 #ifdef ENABLE_NLS
340   if (orig_codeset)
341     {
342       bind_textdomain_codeset (PACKAGE_GT, orig_codeset);
343       xfree (orig_codeset);
344     }
345 #endif
346   xfree (subject);
347
348   if (err)
349     return err;
350
351   buffer = p = xtrymalloc (strlen (name) * 3 + 1);
352   if (!buffer)
353     {
354       err = gpg_error_from_syserror ();
355       free (name);
356       return err;
357     }
358   for (s=name; *s; s++)
359     {
360       if (*s < ' ' || *s == '+')
361         {
362           sprintf (p, "%%%02X", *(unsigned char *)s);
363           p += 3;
364         }
365       else if (*s == ' ')
366         *p++ = '+';
367       else
368         *p++ = *s;
369     }
370   *p = 0;
371   free (name); 
372
373
374   err = gpgsm_agent_get_confirmation (ctrl, buffer);
375
376   xfree (buffer);
377   return err;
378 }