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