summaryrefslogtreecommitdiffstats
path: root/sm/qualified.c
blob: b43371756310c556c9747a0a0d205da4e9fc2d00 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/* qualified.c - Routines related to qualified signatures
 * Copyright (C) 2005, 2007 Free Software Foundation, Inc.
 *
 * This file is part of GnuPG.
 *
 * GnuPG is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * GnuPG is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
 */

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include <errno.h>

#include "gpgsm.h"
#include "../common/i18n.h"
#include <ksba.h>


/* We open the file only once and keep the open file pointer as well
   as the name of the file here.  Note that, a listname not equal to
   NULL indicates that this module has been initialized and if the
   LISTFP is also NULL, no list of qualified signatures exists. */
static char *listname;
static estream_t listfp;


/* Read the trustlist and return entry by entry.  KEY must point to a
   buffer of at least 41 characters. COUNTRY shall be a buffer of at
   least 3 characters to receive the country code of that qualified
   signature (i.e. "de" for German and "be" for Belgium).

   Reading a valid entry returns 0, EOF is indicated by GPG_ERR_EOF
   and any other error condition is indicated by the appropriate error
   code. */
static gpg_error_t
read_list (char *key, char *country, int *lnr)
{
  gpg_error_t err;
  int c, i, j;
  char *p, line[256];

  *key = 0;
  *country = 0;

  if (!listname)
    {
      listname = make_filename (gnupg_datadir (), "qualified.txt", NULL);
      listfp = es_fopen (listname, "r");
      if (!listfp && errno != ENOENT)
        {
          err = gpg_error_from_syserror ();
          log_error (_("can't open '%s': %s\n"), listname, gpg_strerror (err));
          return err;
        }
    }

  if (!listfp)
    return gpg_error (GPG_ERR_EOF);

  do
    {
      if (!es_fgets (line, DIM(line)-1, listfp) )
        {
          if (es_feof (listfp))
            return gpg_error (GPG_ERR_EOF);
          return gpg_error_from_syserror ();
        }

      if (!*line || line[strlen(line)-1] != '\n')
        {
          /* Eat until end of line. */
          while ((c = es_getc (listfp)) != EOF && c != '\n')
            ;
          return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
                                 : GPG_ERR_INCOMPLETE_LINE);
        }
      ++*lnr;

      /* Allow for empty lines and spaces */
      for (p=line; spacep (p); p++)
        ;
    }
  while (!*p || *p == '\n' || *p == '#');

  for (i=j=0; (p[i] == ':' || hexdigitp (p+i)) && j < 40; i++)
    if ( p[i] != ':' )
      key[j++] = p[i] >= 'a'? (p[i] & 0xdf): p[i];
  key[j] = 0;
  if (j != 40 || !(spacep (p+i) || p[i] == '\n'))
    {
      log_error (_("invalid formatted fingerprint in '%s', line %d\n"),
                 listname, *lnr);
      return gpg_error (GPG_ERR_BAD_DATA);
    }
  assert (p[i]);
  i++;
  while (spacep (p+i))
    i++;
  if ( p[i] >= 'a' && p[i] <= 'z'
       && p[i+1] >= 'a' && p[i+1] <= 'z'
       && (spacep (p+i+2) || p[i+2] == '\n'))
    {
      country[0] = p[i];
      country[1] = p[i+1];
      country[2] = 0;
    }
  else
    {
      log_error (_("invalid country code in '%s', line %d\n"), listname, *lnr);
      return gpg_error (GPG_ERR_BAD_DATA);
    }

  return 0;
}




/* Check whether the certificate CERT is included in the list of
   qualified certificates.  This list is similar to the "trustlist.txt"
   as maintained by gpg-agent and includes fingerprints of root
   certificates to be used for qualified (legally binding like
   handwritten) signatures.  We keep this list system wide and not
   per user because it is not a decision of the user.

   Returns: 0 if the certificate is included.  GPG_ERR_NOT_FOUND if it
   is not in the list or any other error (e.g. if no list of
   qualified signatures is available.  If COUNTRY has not been passed
   as NULL a string witha maximum length of 2 will be copied into it;
   thus the caller needs to provide a buffer of length 3. */
gpg_error_t
gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert, char *country)
{
  gpg_error_t err;
  char *fpr;
  char key[41];
  char mycountry[3];
  int lnr = 0;

  (void)ctrl;

  if (country)
    *country = 0;

  fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
  if (!fpr)
    return gpg_error (GPG_ERR_GENERAL);

  if (listfp)
    {
      /* W32ce has no rewind, thus we use the equivalent code.  */
      es_fseek (listfp, 0, SEEK_SET);
      es_clearerr (listfp);
    }
  while (!(err = read_list (key, mycountry, &lnr)))
    {
      if (!strcmp (key, fpr))
        break;
    }
  if (gpg_err_code (err) == GPG_ERR_EOF)
    err = gpg_error (GPG_ERR_NOT_FOUND);

  if (!err && country)
    strcpy (country, mycountry);

  xfree (fpr);
  return err;
}


/* We know that CERT is a qualified certificate.  Ask the user for
   consent to actually create a signature using this certificate.
   Returns: 0 for yes, GPG_ERR_CANCEL for no or any other error
   code. */
gpg_error_t
gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert)
{
  gpg_error_t err;
  char *name, *subject, *buffer, *p;
  const char *s;
  char *orig_codeset = NULL;

  name = ksba_cert_get_subject (cert, 0);
  if (!name)
    return gpg_error (GPG_ERR_GENERAL);
  subject = gpgsm_format_name2 (name, 0);
  ksba_free (name); name = NULL;

  orig_codeset = i18n_switchto_utf8 ();

  if (asprintf (&name,
                _("You are about to create a signature using your "
                  "certificate:\n"
                  "\"%s\"\n"
                  "This will create a qualified signature by law "
                  "equated to a handwritten signature.\n\n%s%s"
                  "Are you really sure that you want to do this?"),
                subject? subject:"?",
                opt.qualsig_approval?
                "":
                _("Note, that this software is not officially approved "
                  "to create or verify such signatures.\n"),
                opt.qualsig_approval? "":"\n"
                ) < 0 )
    err = gpg_error_from_syserror ();
  else
    err = 0;

  i18n_switchback (orig_codeset);
  xfree (subject);

  if (err)
    return err;

  buffer = p = xtrymalloc (strlen (name) * 3 + 1);
  if (!buffer)
    {
      err = gpg_error_from_syserror ();
      free (name);
      return err;
    }
  for (s=name; *s; s++)
    {
      if (*s < ' ' || *s == '+')
        {
          sprintf (p, "%%%02X", *(unsigned char *)s);
          p += 3;
        }
      else if (*s == ' ')
        *p++ = '+';
      else
        *p++ = *s;
    }
  *p = 0;
  free (name);


  err = gpgsm_agent_get_confirmation (ctrl, buffer);

  xfree (buffer);
  return err;
}


/* Popup a prompt to inform the user that the signature created is not
   a qualified one.  This is of course only done if we know that we
   have been approved. */
gpg_error_t
gpgsm_not_qualified_warning (ctrl_t ctrl, ksba_cert_t cert)
{
  gpg_error_t err;
  char *name, *subject, *buffer, *p;
  const char *s;
  char *orig_codeset;

  if (!opt.qualsig_approval)
    return 0;

  name = ksba_cert_get_subject (cert, 0);
  if (!name)
    return gpg_error (GPG_ERR_GENERAL);
  subject = gpgsm_format_name2 (name, 0);
  ksba_free (name); name = NULL;

  orig_codeset = i18n_switchto_utf8 ();

  if (asprintf (&name,
                _("You are about to create a signature using your "
                  "certificate:\n"
                  "\"%s\"\n"
                  "Note, that this certificate will NOT create a "
                  "qualified signature!"),
                subject? subject:"?") < 0 )
    err = gpg_error_from_syserror ();
  else
    err = 0;

  i18n_switchback (orig_codeset);
  xfree (subject);

  if (err)
    return err;

  buffer = p = xtrymalloc (strlen (name) * 3 + 1);
  if (!buffer)
    {
      err = gpg_error_from_syserror ();
      free (name);
      return err;
    }
  for (s=name; *s; s++)
    {
      if (*s < ' ' || *s == '+')
        {
          sprintf (p, "%%%02X", *(unsigned char *)s);
          p += 3;
        }
      else if (*s == ' ')
        *p++ = '+';
      else
        *p++ = *s;
    }
  *p = 0;
  free (name);


  err = gpgsm_agent_get_confirmation (ctrl, buffer);

  xfree (buffer);
  return err;
}