certmanager/lib

cryptplug.cpp
1 /* -*- Mode: C++ -*-
2 
3  this is a C++-ification of:
4  GPGMEPLUG - an GPGME based cryptography plug-in following
5  the common CRYPTPLUG specification.
6 
7  Copyright (C) 2001 by Klarälvdalens Datakonsult AB
8  Copyright (C) 2002 g10 Code GmbH
9  Copyright (C) 2004 Klarälvdalens Datakonsult AB
10 
11  GPGMEPLUG is free software; you can redistribute it and/or modify
12  it under the terms of GNU General Public License as published by
13  the Free Software Foundation; version 2 of the License.
14 
15  GPGMEPLUG is distributed in the hope that it will be useful,
16  it under the terms of GNU General Public License as published by
17  the Free Software Foundation; version 2 of the License
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program; if not, write to the Free Software
24  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 #include "kleo/oidmap.h"
32 
33 #include <gpgmepp/context.h>
34 #include <gpgmepp/data.h>
35 #include <gpgmepp/importresult.h>
36 
54 #include <tqstring.h>
55 
56 #include <string>
57 #include <vector>
58 #include <algorithm>
59 #include <iostream>
60 #include <memory>
61 
62 #include <stdio.h>
63 #include <string.h>
64 #include <strings.h>
65 #include <assert.h>
66 #include <errno.h>
67 #include <time.h>
68 #include <ctype.h>
69 #include <locale.h>
70 
71 #define __GPGMEPLUG_ERROR_CLEARTEXT_IS_ZERO "Error: Cannot run checkMessageSignature() with cleartext == 0"
72 
73 /* Note: The following specification will result in
74  function encryptAndSignMessage() producing
75  _empty_ mails.
76  This must be changed as soon as our plugin
77  is supporting the encryptAndSignMessage() function. */
78 #ifndef GPGMEPLUG_ENCSIGN_MAKE_MIME_OBJECT
79 #define GPGMEPLUG_ENCSIGN_INCLUDE_CLEARTEXT false
80 #define GPGMEPLUG_ENCSIGN_MAKE_MIME_OBJECT false
81 #define GPGMEPLUG_ENCSIGN_MAKE_MULTI_MIME false
82 #define GPGMEPLUG_ENCSIGN_CTYPE_MAIN ""
83 #define GPGMEPLUG_ENCSIGN_CDISP_MAIN ""
84 #define GPGMEPLUG_ENCSIGN_CTENC_MAIN ""
85 #define GPGMEPLUG_ENCSIGN_CTYPE_VERSION ""
86 #define GPGMEPLUG_ENCSIGN_CDISP_VERSION ""
87 #define GPGMEPLUG_ENCSIGN_CTENC_VERSION ""
88 #define GPGMEPLUG_ENCSIGN_BTEXT_VERSION ""
89 #define GPGMEPLUG_ENCSIGN_CTYPE_CODE ""
90 #define GPGMEPLUG_ENCSIGN_CDISP_CODE ""
91 #define GPGMEPLUG_ENCSIGN_CTENC_CODE ""
92 #define GPGMEPLUG_ENCSIGN_FLAT_PREFIX ""
93 #define GPGMEPLUG_ENCSIGN_FLAT_SEPARATOR ""
94 #define GPGMEPLUG_ENCSIGN_FLAT_POSTFIX ""
95 #endif
96 
97 #include "cryptplug.h"
98 #include <kdebug.h>
99 
100 SMIMECryptPlug::SMIMECryptPlug() : CryptPlug() {
101  GPGMEPLUG_PROTOCOL = GPGME_PROTOCOL_CMS;
102  mProtocol = GpgME::Context::CMS;
103 
104  /* definitions for signing */
105  // 1. opaque signatures (only used for S/MIME)
106  GPGMEPLUG_OPA_SIGN_INCLUDE_CLEARTEXT = false;
107  GPGMEPLUG_OPA_SIGN_MAKE_MIME_OBJECT = true;
108  GPGMEPLUG_OPA_SIGN_MAKE_MULTI_MIME = false;
109  GPGMEPLUG_OPA_SIGN_CTYPE_MAIN = "application/pkcs7-mime; smime-type=signed-data; name=\"smime.p7m\"";
110  GPGMEPLUG_OPA_SIGN_CDISP_MAIN = "attachment; filename=\"smime.p7m\"";
111  GPGMEPLUG_OPA_SIGN_CTENC_MAIN = "base64";
112  GPGMEPLUG_OPA_SIGN_CTYPE_VERSION = "";
113  GPGMEPLUG_OPA_SIGN_CDISP_VERSION = "";
114  GPGMEPLUG_OPA_SIGN_CTENC_VERSION = "";
115  GPGMEPLUG_OPA_SIGN_BTEXT_VERSION = "";
116  GPGMEPLUG_OPA_SIGN_CTYPE_CODE = "";
117  GPGMEPLUG_OPA_SIGN_CDISP_CODE = "";
118  GPGMEPLUG_OPA_SIGN_CTENC_CODE = "";
119  GPGMEPLUG_OPA_SIGN_FLAT_PREFIX = "";
120  GPGMEPLUG_OPA_SIGN_FLAT_SEPARATOR = "";
121  GPGMEPLUG_OPA_SIGN_FLAT_POSTFIX = "";
122  // 2. detached signatures (used for S/MIME and for OpenPGP)
123  GPGMEPLUG_DET_SIGN_INCLUDE_CLEARTEXT = true;
124  GPGMEPLUG_DET_SIGN_MAKE_MIME_OBJECT = true;
125  GPGMEPLUG_DET_SIGN_MAKE_MULTI_MIME = true;
126  GPGMEPLUG_DET_SIGN_CTYPE_MAIN = "multipart/signed; protocol=\"application/pkcs7-signature\"; micalg=sha1";
127  GPGMEPLUG_DET_SIGN_CDISP_MAIN = "";
128  GPGMEPLUG_DET_SIGN_CTENC_MAIN = "";
129  GPGMEPLUG_DET_SIGN_CTYPE_VERSION = "";
130  GPGMEPLUG_DET_SIGN_CDISP_VERSION = "";
131  GPGMEPLUG_DET_SIGN_CTENC_VERSION = "";
132  GPGMEPLUG_DET_SIGN_BTEXT_VERSION = "";
133  GPGMEPLUG_DET_SIGN_CTYPE_CODE = "application/pkcs7-signature; name=\"smime.p7s\"";
134  GPGMEPLUG_DET_SIGN_CDISP_CODE = "attachment; filename=\"smime.p7s\"";
135  GPGMEPLUG_DET_SIGN_CTENC_CODE = "base64";
136  GPGMEPLUG_DET_SIGN_FLAT_PREFIX = "";
137  GPGMEPLUG_DET_SIGN_FLAT_SEPARATOR = "";
138  GPGMEPLUG_DET_SIGN_FLAT_POSTFIX = "";
139  // 3. common definitions for opaque and detached signing
140  __GPGMEPLUG_SIGNATURE_CODE_IS_BINARY = true;
141 
142  /* definitions for encoding */
143  GPGMEPLUG_ENC_INCLUDE_CLEARTEXT = false;
144  GPGMEPLUG_ENC_MAKE_MIME_OBJECT = true;
145  GPGMEPLUG_ENC_MAKE_MULTI_MIME = false;
146  GPGMEPLUG_ENC_CTYPE_MAIN = "application/pkcs7-mime; smime-type=enveloped-data; name=\"smime.p7m\"";
147  GPGMEPLUG_ENC_CDISP_MAIN = "attachment; filename=\"smime.p7m\"";
148  GPGMEPLUG_ENC_CTENC_MAIN = "base64";
149  GPGMEPLUG_ENC_CTYPE_VERSION = "";
150  GPGMEPLUG_ENC_CDISP_VERSION = "";
151  GPGMEPLUG_ENC_CTENC_VERSION = "";
152  GPGMEPLUG_ENC_BTEXT_VERSION = "";
153  GPGMEPLUG_ENC_CTYPE_CODE = "";
154  GPGMEPLUG_ENC_CDISP_CODE = "";
155  GPGMEPLUG_ENC_CTENC_CODE = "";
156  GPGMEPLUG_ENC_FLAT_PREFIX = "";
157  GPGMEPLUG_ENC_FLAT_SEPARATOR = "";
158  GPGMEPLUG_ENC_FLAT_POSTFIX = "";
159  __GPGMEPLUG_ENCRYPTED_CODE_IS_BINARY = true;
160 }
161 
162 OpenPGPCryptPlug::OpenPGPCryptPlug() : CryptPlug() {
163  GPGMEPLUG_PROTOCOL = GPGME_PROTOCOL_OpenPGP;
164  mProtocol = GpgME::Context::OpenPGP;
165 
166  /* definitions for signing */
167  // 1. opaque signatures (only used for S/MIME)
168  GPGMEPLUG_OPA_SIGN_INCLUDE_CLEARTEXT = false;
169  GPGMEPLUG_OPA_SIGN_MAKE_MIME_OBJECT = false;
170  GPGMEPLUG_OPA_SIGN_MAKE_MULTI_MIME = false;
171  GPGMEPLUG_OPA_SIGN_CTYPE_MAIN = "";
172  GPGMEPLUG_OPA_SIGN_CDISP_MAIN = "";
173  GPGMEPLUG_OPA_SIGN_CTENC_MAIN = "";
174  GPGMEPLUG_OPA_SIGN_CTYPE_VERSION = "";
175  GPGMEPLUG_OPA_SIGN_CDISP_VERSION = "";
176  GPGMEPLUG_OPA_SIGN_CTENC_VERSION = "";
177  GPGMEPLUG_OPA_SIGN_BTEXT_VERSION = "";
178  GPGMEPLUG_OPA_SIGN_CTYPE_CODE = "";
179  GPGMEPLUG_OPA_SIGN_CDISP_CODE = "";
180  GPGMEPLUG_OPA_SIGN_CTENC_CODE = "";
181  GPGMEPLUG_OPA_SIGN_FLAT_PREFIX = "";
182  GPGMEPLUG_OPA_SIGN_FLAT_SEPARATOR = "";
183  GPGMEPLUG_OPA_SIGN_FLAT_POSTFIX = "";
184  // 2. detached signatures (used for S/MIME and for OpenPGP)
185  GPGMEPLUG_DET_SIGN_INCLUDE_CLEARTEXT = true;
186  GPGMEPLUG_DET_SIGN_MAKE_MIME_OBJECT = true;
187  GPGMEPLUG_DET_SIGN_MAKE_MULTI_MIME = true;
188  GPGMEPLUG_DET_SIGN_CTYPE_MAIN = "multipart/signed; protocol=\"application/pgp-signature\"; micalg=pgp-sha1";
189  GPGMEPLUG_DET_SIGN_CDISP_MAIN = "";
190  GPGMEPLUG_DET_SIGN_CTENC_MAIN = "";
191  GPGMEPLUG_DET_SIGN_CTYPE_VERSION = "";
192  GPGMEPLUG_DET_SIGN_CDISP_VERSION = "";
193  GPGMEPLUG_DET_SIGN_CTENC_VERSION = "";
194  GPGMEPLUG_DET_SIGN_BTEXT_VERSION = "";
195  GPGMEPLUG_DET_SIGN_CTYPE_CODE = "application/pgp-signature";
196  GPGMEPLUG_DET_SIGN_CDISP_CODE = "";
197  GPGMEPLUG_DET_SIGN_CTENC_CODE = "";
198  GPGMEPLUG_DET_SIGN_FLAT_PREFIX = "";
199  GPGMEPLUG_DET_SIGN_FLAT_SEPARATOR = "";
200  GPGMEPLUG_DET_SIGN_FLAT_POSTFIX = "";
201  // 3. common definitions for opaque and detached signing
202  __GPGMEPLUG_SIGNATURE_CODE_IS_BINARY = false;
203 
204  /* definitions for encoding */
205  GPGMEPLUG_ENC_INCLUDE_CLEARTEXT = false;
206  GPGMEPLUG_ENC_MAKE_MIME_OBJECT = true;
207  GPGMEPLUG_ENC_MAKE_MULTI_MIME = true;
208  GPGMEPLUG_ENC_CTYPE_MAIN = "multipart/encrypted; protocol=\"application/pgp-encrypted\"";
209  GPGMEPLUG_ENC_CDISP_MAIN = "";
210  GPGMEPLUG_ENC_CTENC_MAIN = "";
211  GPGMEPLUG_ENC_CTYPE_VERSION = "application/pgp-encrypted";
212  GPGMEPLUG_ENC_CDISP_VERSION = "attachment";
213  GPGMEPLUG_ENC_CTENC_VERSION = "";
214  GPGMEPLUG_ENC_BTEXT_VERSION = "Version: 1";
215  GPGMEPLUG_ENC_CTYPE_CODE = "application/octet-stream";
216  GPGMEPLUG_ENC_CDISP_CODE = "inline; filename=\"msg.asc\"";
217  GPGMEPLUG_ENC_CTENC_CODE = "";
218  GPGMEPLUG_ENC_FLAT_PREFIX = "";
219  GPGMEPLUG_ENC_FLAT_SEPARATOR = "";
220  GPGMEPLUG_ENC_FLAT_POSTFIX = "";
221  __GPGMEPLUG_ENCRYPTED_CODE_IS_BINARY = false;
222 }
223 
224 #define days_from_seconds(x) ((x)/86400)
225 
226 /* Max number of parts in a DN */
227 #define MAX_GPGME_IDX 20
228 
229 /* some macros to replace ctype ones and avoid locale problems */
230 #define spacep(p) (*(p) == ' ' || *(p) == '\t')
231 #define digitp(p) (*(p) >= '0' && *(p) <= '9')
232 #define hexdigitp(a) (digitp (a) \
233  || (*(a) >= 'A' && *(a) <= 'F') \
234  || (*(a) >= 'a' && *(a) <= 'f'))
235 /* the atoi macros assume that the buffer has only valid digits */
236 #define atoi_1(p) (*(p) - '0' )
237 #define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1))
238 #define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2))
239 #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
240  *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
241 #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
242 
243 static void *
244 xmalloc (size_t n)
245 {
246  void *p = malloc (n);
247  if (!p)
248  {
249  fputs ("\nfatal: out of core\n", stderr);
250  exit (4);
251  }
252  return p;
253 }
254 
255 /* Please: Don't call an allocation function xfoo when it may return NULL. */
256 /* Wrong: #define xstrdup( x ) (x)?strdup(x):0 */
257 /* Right: */
258 static char *
259 xstrdup (const char *string)
260 {
261  char *p = (char*)xmalloc (strlen (string)+1);
262  strcpy (p, string);
263  return p;
264 }
265 
266 
267 CryptPlug::CryptPlug() {
268 }
269 
270 CryptPlug::~CryptPlug() {
271 }
272 
273 bool CryptPlug::initialize() {
274  GpgME::setDefaultLocale( LC_CTYPE, setlocale( LC_CTYPE, 0 ) );
275  GpgME::setDefaultLocale( LC_MESSAGES, setlocale( LC_MESSAGES, 0 ) );
276  return (gpgme_engine_check_version (GPGMEPLUG_PROTOCOL) == GPG_ERR_NO_ERROR);
277 }
278 
279 
280 bool CryptPlug::hasFeature( Feature flag )
281 {
282  /* our own plugins are supposed to support everything */
283  switch ( flag ) {
284  case Feature_SignMessages:
285  case Feature_VerifySignatures:
286  case Feature_EncryptMessages:
287  case Feature_DecryptMessages:
288  case Feature_SendCertificates:
289  case Feature_PinEntrySettings:
290  case Feature_StoreMessagesWithSigs:
291  case Feature_EncryptionCRLs:
292  case Feature_StoreMessagesEncrypted:
293  case Feature_CheckCertificatePath:
294  return true;
295  case Feature_WarnSignCertificateExpiry:
296  case Feature_WarnSignEmailNotInCertificate:
297  case Feature_WarnEncryptCertificateExpiry:
298  case Feature_WarnEncryptEmailNotInCertificate:
299  return GPGMEPLUG_PROTOCOL == GPGME_PROTOCOL_CMS;
300  /* undefined or not yet implemented: */
301  case Feature_CRLDirectoryService:
302  case Feature_CertificateDirectoryService:
303  case Feature_undef:
304  default:
305  return false;
306  }
307 }
308 
309 
310 static
311 void storeNewCharPtr( char** dest, const char* src )
312 {
313  int sLen = strlen( src );
314  *dest = (char*)xmalloc( sLen + 1 );
315  strcpy( *dest, src );
316 }
317 
318 bool CryptPlug::decryptMessage( const char* ciphertext,
319  bool cipherIsBinary,
320  int cipherLen,
321  const char** cleartext,
322  const char* /*certificate*/,
323  int* errId,
324  char** errTxt )
325 {
326  gpgme_ctx_t ctx;
327  gpgme_error_t err;
328  gpgme_data_t gCiphertext, gPlaintext;
329  size_t rCLen = 0;
330  char* rCiph = 0;
331  bool bOk = false;
332 
333  if( !ciphertext )
334  return false;
335 
336  err = gpgme_new (&ctx);
337  gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
338 
339  gpgme_set_armor (ctx, cipherIsBinary ? 0 : 1);
340  /* gpgme_set_textmode (ctx, cipherIsBinary ? 0 : 1); */
341 
342  /*
343  gpgme_data_new_from_mem( &gCiphertext, ciphertext,
344  1+strlen( ciphertext ), 1 ); */
345  gpgme_data_new_from_mem( &gCiphertext,
346  ciphertext,
347  cipherIsBinary
348  ? cipherLen
349  : strlen( ciphertext ),
350  1 );
351 
352  gpgme_data_new( &gPlaintext );
353 
354  err = gpgme_op_decrypt( ctx, gCiphertext, gPlaintext );
355  if( err ) {
356  fprintf( stderr, "\ngpgme_op_decrypt() returned this error code: %i\n\n", err );
357  if( errId )
358  *errId = err;
359  if( errTxt ) {
360  const char* _errTxt = gpgme_strerror( err );
361  *errTxt = (char*)malloc( strlen( _errTxt ) + 1 );
362  if( *errTxt )
363  strcpy(*errTxt, _errTxt );
364  }
365  }
366 
367  gpgme_data_release( gCiphertext );
368 
369  rCiph = gpgme_data_release_and_get_mem( gPlaintext, &rCLen );
370 
371  *cleartext = (char*)malloc( rCLen + 1 );
372  if( *cleartext ) {
373  if( rCLen ) {
374  bOk = true;
375  strncpy((char*)*cleartext, rCiph, rCLen );
376  }
377  ((char*)(*cleartext))[rCLen] = 0;
378  }
379 
380  free( rCiph );
381  gpgme_release( ctx );
382  return bOk;
383 }
384 
385 
386 static char *
387 trim_trailing_spaces( char *string )
388 {
389  char *p, *mark;
390 
391  for( mark = NULL, p = string; *p; p++ ) {
392  if( isspace( *p ) ) {
393  if( !mark )
394  mark = p;
395  }
396  else
397  mark = NULL;
398  }
399  if( mark )
400  *mark = '\0' ;
401 
402  return string ;
403 }
404 
405 /* Parse a DN and return an array-ized one. This is not a validating
406  parser and it does not support any old-stylish syntax; gpgme is
407  expected to return only rfc2253 compatible strings. */
408 static const unsigned char *
409 parse_dn_part (CryptPlug::DnPair *array, const unsigned char *string)
410 {
411  const unsigned char *s, *s1;
412  size_t n;
413  char *p;
414 
415  /* parse attributeType */
416  for (s = string+1; *s && *s != '='; s++)
417  ;
418  if (!*s)
419  return NULL; /* error */
420  n = s - string;
421  if (!n)
422  return NULL; /* empty key */
423  p = (char*)xmalloc (n+1);
424 
425 
426  memcpy (p, string, n);
427  p[n] = 0;
428  trim_trailing_spaces ((char*)p);
429  // map OIDs to their names:
430  for ( unsigned int i = 0 ; i < numOidMaps ; ++i )
431  if ( !strcasecmp ((char*)p, oidmap[i].oid) ) {
432  free( p );
433  p = xstrdup (oidmap[i].name);
434  break;
435  }
436  array->key = p;
437  string = s + 1;
438 
439  if (*string == '#')
440  { /* hexstring */
441  string++;
442  for (s=string; hexdigitp (s); s++)
443  s++;
444  n = s - string;
445  if (!n || (n & 1))
446  return NULL; /* empty or odd number of digits */
447  n /= 2;
448  array->value = p = (char*)xmalloc (n+1);
449 
450 
451  for (s1=string; n; s1 += 2, n--)
452  *p++ = xtoi_2 (s1);
453  *p = 0;
454  }
455  else
456  { /* regular v3 quoted string */
457  for (n=0, s=string; *s; s++)
458  {
459  if (*s == '\\')
460  { /* pair */
461  s++;
462  if (*s == ',' || *s == '=' || *s == '+'
463  || *s == '<' || *s == '>' || *s == '#' || *s == ';'
464  || *s == '\\' || *s == '\"' || *s == ' ')
465  n++;
466  else if (hexdigitp (s) && hexdigitp (s+1))
467  {
468  s++;
469  n++;
470  }
471  else
472  return NULL; /* invalid escape sequence */
473  }
474  else if (*s == '\"')
475  return NULL; /* invalid encoding */
476  else if (*s == ',' || *s == '=' || *s == '+'
477  || *s == '<' || *s == '>' || *s == '#' || *s == ';' )
478  break;
479  else
480  n++;
481  }
482 
483  array->value = p = (char*)xmalloc (n+1);
484 
485 
486  for (s=string; n; s++, n--)
487  {
488  if (*s == '\\')
489  {
490  s++;
491  if (hexdigitp (s))
492  {
493  *p++ = xtoi_2 (s);
494  s++;
495  }
496  else
497  *p++ = *s;
498  }
499  else
500  *p++ = *s;
501  }
502  *p = 0;
503  }
504  return s;
505 }
506 
507 
508 /* Parse a DN and return an array-ized one. This is not a validating
509  parser and it does not support any old-stylish syntax; gpgme is
510  expected to return only rfc2253 compatible strings. */
511 static CryptPlug::DnPair *
512 parse_dn (const unsigned char *string)
513 {
514  struct CryptPlug::DnPair *array;
515  size_t arrayidx, arraysize;
516 
517  if( !string )
518  return NULL;
519 
520  arraysize = 7; /* C,ST,L,O,OU,CN,email */
521  arrayidx = 0;
522  array = (CryptPlug::DnPair*)xmalloc ((arraysize+1) * sizeof *array);
523 
524 
525  while (*string)
526  {
527  while (*string == ' ')
528  string++;
529  if (!*string)
530  break; /* ready */
531  if (arrayidx >= arraysize)
532  { /* mutt lacks a real safe_realoc - so we need to copy */
533  struct CryptPlug::DnPair *a2;
534 
535  arraysize += 5;
536  a2 = (CryptPlug::DnPair*)xmalloc ((arraysize+1) * sizeof *array);
537  for (unsigned int i=0; i < arrayidx; i++)
538  {
539  a2[i].key = array[i].key;
540  a2[i].value = array[i].value;
541  }
542  free (array);
543  array = a2;
544  }
545  array[arrayidx].key = NULL;
546  array[arrayidx].value = NULL;
547  string = parse_dn_part (array+arrayidx, string);
548  arrayidx++;
549  if (!string)
550  goto failure;
551  while (*string == ' ')
552  string++;
553  if (*string && *string != ',' && *string != ';' && *string != '+')
554  goto failure; /* invalid delimiter */
555  if (*string)
556  string++;
557  }
558  array[arrayidx].key = NULL;
559  array[arrayidx].value = NULL;
560  return array;
561 
562  failure:
563  for (unsigned i=0; i < arrayidx; i++)
564  {
565  free (array[i].key);
566  free (array[i].value);
567  }
568  free (array);
569  return NULL;
570 }
571 
572 static void
573 add_dn_part( TQCString& result, struct CryptPlug::DnPair& dnPair )
574 {
575  /* email hack */
576  TQCString mappedPart( dnPair.key );
577  for ( unsigned int i = 0 ; i < numOidMaps ; ++i ){
578  if( !strcasecmp( dnPair.key, oidmap[i].oid ) ) {
579  mappedPart = oidmap[i].name;
580  break;
581  }
582  }
583  result.append( mappedPart );
584  result.append( "=" );
585  result.append( dnPair.value );
586 }
587 
588 static int
589 add_dn_parts( TQCString& result, struct CryptPlug::DnPair* dn, const char* part )
590 {
591  int any = 0;
592 
593  if( dn ) {
594  for(; dn->key; ++dn ) {
595  if( !strcmp( dn->key, part ) ) {
596  if( any )
597  result.append( "," );
598  add_dn_part( result, *dn );
599  any = 1;
600  }
601  }
602  }
603  return any;
604 }
605 
606 static char*
607 reorder_dn( struct CryptPlug::DnPair *dn,
608  char** attrOrder = 0,
609  const char* unknownAttrsHandling = 0 )
610 {
611  struct CryptPlug::DnPair *dnOrg = dn;
612 
613  /* note: The must parts are: CN, L, OU, O, C */
614  const char* defaultpart[] = {
615  "CN", "S", "SN", "GN", "T", "UID",
616  "MAIL", "EMAIL", "MOBILE", "TEL", "FAX", "STREET",
617  "L", "PC", "SP", "ST",
618  "OU",
619  "O",
620  "C",
621  NULL
622  };
623  const char** stdpart = attrOrder ? ((const char**)attrOrder) : defaultpart;
624  int any=0, any2=0, found_X_=0, i;
625  TQCString result;
626  TQCString resultUnknowns;
627 
628  /* find and save the non-standard parts in their original order */
629  if( dn ){
630  for(; dn->key; ++dn ) {
631  for( i = 0; stdpart[i]; ++i ) {
632  if( !strcmp( dn->key, stdpart[i] ) ) {
633  break;
634  }
635  }
636  if( !stdpart[i] ) {
637  if( any2 )
638  resultUnknowns.append( "," );
639  add_dn_part( resultUnknowns, *dn );
640  any2 = 1;
641  }
642  }
643  dn = dnOrg;
644  }
645 
646  /* prepend the unknown attrs if desired */
647  if( unknownAttrsHandling &&
648  !strcmp(unknownAttrsHandling, "PREFIX")
649  && *resultUnknowns ){
650  result.append( resultUnknowns );
651  any = 1;
652  }else{
653  any = 0;
654  }
655 
656  /* add standard parts */
657  for( i = 0; stdpart[i]; ++i ) {
658  dn = dnOrg;
659  if( any ) {
660  result.append( "," );
661  }
662  if( any2 &&
663  !strcmp(stdpart[i], "_X_") &&
664  unknownAttrsHandling &&
665  !strcmp(unknownAttrsHandling, "INFIX") ){
666  if ( !resultUnknowns.isEmpty() ) {
667  result.append( resultUnknowns );
668  any = 1;
669  }
670  found_X_ = 1;
671  }else{
672  any = add_dn_parts( result, dn, stdpart[i] );
673  }
674  }
675 
676  /* append the unknown attrs if desired */
677  if( !unknownAttrsHandling ||
678  !strcmp(unknownAttrsHandling, "POSTFIX") ||
679  ( !strcmp(unknownAttrsHandling, "INFIX") && !found_X_ ) ){
680  if( !resultUnknowns.isEmpty() ) {
681  if( any ){
682  result.append( "," );
683  }
684  result.append( resultUnknowns );
685  }
686  }
687 
688  char* cResult = (char*)xmalloc( (result.length()+1)*sizeof(char) );
689  if( result.isEmpty() )
690  *cResult = 0;
691  else
692  strcpy( cResult, result );
693  return cResult;
694 }
695 
696 GpgME::ImportResult CryptPlug::importCertificateFromMem( const char* data, size_t length )
697 {
698  using namespace GpgME;
699 
700  std::auto_ptr<Context> context( Context::createForProtocol( mProtocol ) );
701  if ( !context.get() )
702  return ImportResult();
703 
704  Data keydata( data, length, false );
705  if ( keydata.isNull() )
706  return ImportResult();
707 
708  return context->importKeys( keydata );
709 }
710 
711 
712 /* == == == == == == == == == == == == == == == == == == == == == == == == ==
713  == ==
714  == Continuation of CryptPlug code ==
715  == ==
716 == == == == == == == == == == == == == == == == == == == == == == == == == */
717 
718 // these are from gpgme-0.4.3:
719 static gpgme_sig_stat_t
720 sig_stat_from_status( gpgme_error_t err )
721 {
722  switch ( gpg_err_code(err) ) {
723  case GPG_ERR_NO_ERROR:
724  return GPGME_SIG_STAT_GOOD;
725  case GPG_ERR_BAD_SIGNATURE:
726  return GPGME_SIG_STAT_BAD;
727  case GPG_ERR_NO_PUBKEY:
728  return GPGME_SIG_STAT_NOKEY;
729  case GPG_ERR_NO_DATA:
730  return GPGME_SIG_STAT_NOSIG;
731  case GPG_ERR_SIG_EXPIRED:
732  return GPGME_SIG_STAT_GOOD_EXP;
733  case GPG_ERR_KEY_EXPIRED:
734  return GPGME_SIG_STAT_GOOD_EXPKEY;
735  default:
736  return GPGME_SIG_STAT_ERROR;
737  }
738 }
739 
740 
741 static gpgme_sig_stat_t
742 intersect_stati( gpgme_signature_t first )
743 {
744  if ( !first )
745  return GPGME_SIG_STAT_NONE;
746  gpgme_sig_stat_t result = sig_stat_from_status( first->status );
747  for ( gpgme_signature_t sig = first->next ; sig ; sig = sig->next )
748  if ( sig_stat_from_status( sig->status ) != result )
749  return GPGME_SIG_STAT_DIFF;
750  return result;
751 }
752 
753 static const char*
754 sig_status_to_string( gpgme_sig_stat_t status )
755 {
756  const char *result;
757 
758  switch (status) {
759  case GPGME_SIG_STAT_NONE:
760  result = "Oops: Signature not verified";
761  break;
762  case GPGME_SIG_STAT_NOSIG:
763  result = "No signature found";
764  break;
765  case GPGME_SIG_STAT_GOOD:
766  result = "Good signature";
767  break;
768  case GPGME_SIG_STAT_BAD:
769  result = "BAD signature";
770  break;
771  case GPGME_SIG_STAT_NOKEY:
772  result = "No public key to verify the signature";
773  break;
774  case GPGME_SIG_STAT_ERROR:
775  result = "Error verifying the signature";
776  break;
777  case GPGME_SIG_STAT_DIFF:
778  result = "Different results for signatures";
779  break;
780  default:
781  result = "Error: Unknown status";
782  break;
783  }
784 
785  return result;
786 }
787 
788 // WARNING: if you fix a bug here, you have to likely fix it in the
789 // gpgme 0.3 version below, too!
790 static
791 void obtain_signature_information( gpgme_ctx_t ctx,
792  gpgme_sig_stat_t & overallStatus,
793  struct CryptPlug::SignatureMetaData* sigmeta,
794  char** attrOrder,
795  const char* unknownAttrsHandling,
796  bool * signatureFound=0 )
797 {
798  gpgme_error_t err;
799  unsigned long sumGPGME;
800  SigStatusFlags sumPlug;
801  struct CryptPlug::DnPair* a;
802  int sig_idx=0;
803 
804  assert( ctx );
805  assert( sigmeta );
806 
807  sigmeta->extended_info = 0;
808  gpgme_verify_result_t result = gpgme_op_verify_result( ctx );
809  if ( !result )
810  return;
811  for ( gpgme_signature_t signature = result->signatures ; signature ; signature = signature->next, ++sig_idx ) {
812  void* alloc_return = realloc( sigmeta->extended_info,
813  sizeof( CryptPlug::SignatureMetaDataExtendedInfo )
814  * ( sig_idx + 1 ) );
815  if ( !alloc_return )
816  break;
817  sigmeta->extended_info = (CryptPlug::SignatureMetaDataExtendedInfo*)alloc_return;
818 
819  /* shorthand notation :) */
820  CryptPlug::SignatureMetaDataExtendedInfo & this_info = sigmeta->extended_info[sig_idx];
821 
822  /* clear the data area */
823  memset( &this_info, 0, sizeof (CryptPlug::SignatureMetaDataExtendedInfo) );
824 
825  /* the creation time */
826  if ( signature->timestamp ) {
827  this_info.creation_time = (tm*)malloc( sizeof( struct tm ) );
828  if ( this_info.creation_time ) {
829  struct tm * ctime_val = localtime( (time_t*)&signature->timestamp );
830  memcpy( this_info.creation_time,
831  ctime_val, sizeof( struct tm ) );
832  }
833  }
834 
835  /* the extended signature verification status */
836  sumGPGME = signature->summary;
837  fprintf( stderr, "gpgmeplug checkMessageSignature status flags: %lX\n", sumGPGME );
838  /* translate GPGME status flags to common CryptPlug status flags */
839  sumPlug = 0;
840 #define convert(X) if ( sumGPGME & GPGME_SIGSUM_##X ) sumPlug |= SigStat_##X
841  convert(VALID);
842  convert(GREEN);
843  convert(RED);
844  convert(KEY_REVOKED);
845  convert(KEY_EXPIRED);
846  convert(SIG_EXPIRED);
847  convert(KEY_MISSING);
848  convert(CRL_MISSING);
849  convert(CRL_TOO_OLD);
850  convert(BAD_POLICY);
851  convert(SYS_ERROR);
852 #undef convert
853  if( sumGPGME && !sumPlug )
854  sumPlug = SigStat_NUMERICAL_CODE | sumGPGME;
855  this_info.sigStatusFlags = sumPlug;
856 
857  /* extract finger print */
858  if ( signature->fpr )
859  storeNewCharPtr( &this_info.fingerprint, signature->fpr );
860 
861  /* validity */
862  this_info.validity = GPGME_VALIDITY_UNKNOWN;
863 
864  /* sig key data */
865  gpgme_key_t key = 0;
866  // PENDING(marc) if this is deprecated, how shall we get at all
867  // the infos below?
868  err = gpgme_get_sig_key (ctx, sig_idx, &key);
869 
870  if ( !err && key ) {
871  const char* attr_string;
872  unsigned long attr_ulong;
873 
874  /* extract key identidy */
875  attr_string = key->subkeys ? key->subkeys->keyid : 0 ;
876  if ( attr_string )
877  storeNewCharPtr( &this_info.keyid, attr_string );
878 
879  /* pubkey algorithm */
880  attr_string = key->subkeys ? gpgme_pubkey_algo_name( key->subkeys->pubkey_algo ) : 0 ;
881  if (attr_string != 0)
882  storeNewCharPtr( &this_info.algo, attr_string );
883  attr_ulong = key->subkeys ? key->subkeys->pubkey_algo : 0 ;
884  this_info.algo_num = attr_ulong;
885 
886  /* extract key validity */
887  attr_ulong = key->uids ? key->uids->validity : 0 ;
888  this_info.validity = attr_ulong;
889 
890  /* extract user id, according to the documentation it's representable
891  * as a number, but it seems that it also has a string representation
892  */
893  attr_string = key->uids ? key->uids->uid : 0 ;
894  if (attr_string != 0) {
895  a = parse_dn( (const unsigned char*)attr_string );
896  this_info.userid = reorder_dn( a, attrOrder, unknownAttrsHandling );
897  }
898 
899  attr_ulong = 0;
900  this_info.userid_num = attr_ulong;
901 
902  /* extract the length */
903  this_info.keylen = key->subkeys ? key->subkeys->length : 0 ;
904 
905  /* extract the creation time of the key */
906  attr_ulong = key->subkeys ? key->subkeys->timestamp : 0 ;
907  this_info.key_created = attr_ulong;
908 
909  /* extract the expiration time of the key */
910  attr_ulong = key->subkeys ? key->subkeys->expires : 0 ;
911  this_info.key_expires = attr_ulong;
912 
913  /* extract user name */
914  attr_string = key->uids ? key->uids->name : 0 ;
915  if (attr_string != 0) {
916  a = parse_dn( (const unsigned char*)attr_string );
917  this_info.name = reorder_dn( a, attrOrder, unknownAttrsHandling );
918  }
919 
920  /* extract email(s) */
921  this_info.emailCount = 0;
922  this_info.emailList = 0;
923  for ( gpgme_user_id_t uid = key->uids ; uid ; uid = uid->next ) {
924  attr_string = uid->email;
925  if ( attr_string && *attr_string) {
926  fprintf( stderr, "gpgmeplug checkMessageSignature found email: %s\n", attr_string );
927  if( !this_info.emailCount )
928  alloc_return = malloc( sizeof( char*) );
929  else
930  alloc_return = realloc( this_info.emailList,
931  sizeof( char*)
932  * (this_info.emailCount + 1) );
933  if( alloc_return ) {
934  this_info.emailList = (char**)alloc_return;
935  storeNewCharPtr( &( this_info.emailList[ this_info.emailCount ] ),
936  attr_string );
937  ++this_info.emailCount;
938  }
939  }
940  }
941  if( !this_info.emailCount )
942  fprintf( stderr, "gpgmeplug checkMessageSignature found NO EMAIL\n" );
943 
944  /* extract the comment */
945  attr_string = key->uids ? key->uids->comment : 0 ;
946  if (attr_string != 0)
947  storeNewCharPtr( &this_info.comment, attr_string );
948  }
949 
950  gpgme_sig_stat_t status = sig_stat_from_status( signature->status );
951  const char* sig_status = sig_status_to_string( status );
952  storeNewCharPtr( &this_info.status_text, sig_status );
953  }
954  sigmeta->extended_info_count = sig_idx;
955  overallStatus = intersect_stati( result->signatures );
956  sigmeta->status_code = overallStatus;
957  storeNewCharPtr( &sigmeta->status, sig_status_to_string( overallStatus ) );
958  if ( signatureFound )
959  *signatureFound = ( overallStatus != GPGME_SIG_STAT_NONE );
960 }
961 
962 bool CryptPlug::checkMessageSignature( char** cleartext,
963  const char* signaturetext,
964  bool signatureIsBinary,
965  int signatureLen,
966  struct CryptPlug::SignatureMetaData* sigmeta,
967  char** attrOrder,
968  const char* unknownAttrsHandling )
969 {
970  gpgme_ctx_t ctx;
971  gpgme_sig_stat_t status = GPGME_SIG_STAT_NONE;
972  gpgme_data_t datapart, sigpart;
973  char* rClear = 0;
974  size_t clearLen;
975  bool isOpaqueSigned;
976 
977  if( !cleartext ) {
978  if( sigmeta )
979  storeNewCharPtr( &sigmeta->status,
980  __GPGMEPLUG_ERROR_CLEARTEXT_IS_ZERO );
981 
982  return false;
983  }
984 
985  isOpaqueSigned = !*cleartext;
986 
987  gpgme_new( &ctx );
988  gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
989  gpgme_set_armor (ctx, signatureIsBinary ? 0 : 1);
990  /* gpgme_set_textmode (ctx, signatureIsBinary ? 0 : 1); */
991 
992  if( isOpaqueSigned )
993  gpgme_data_new( &datapart );
994  else
995  gpgme_data_new_from_mem( &datapart, *cleartext,
996  strlen( *cleartext ), 1 );
997 
998  gpgme_data_new_from_mem( &sigpart,
999  signaturetext,
1000  signatureIsBinary
1001  ? signatureLen
1002  : strlen( signaturetext ),
1003  1 );
1004 
1005  if ( isOpaqueSigned )
1006  gpgme_op_verify( ctx, sigpart, 0, datapart );
1007  else
1008  gpgme_op_verify( ctx, sigpart, datapart, 0 );
1009 
1010  if( isOpaqueSigned ) {
1011  rClear = gpgme_data_release_and_get_mem( datapart, &clearLen );
1012  *cleartext = (char*)malloc( clearLen + 1 );
1013  if( *cleartext ) {
1014  if( clearLen )
1015  strncpy(*cleartext, rClear, clearLen );
1016  (*cleartext)[clearLen] = '\0';
1017  }
1018  free( rClear );
1019  }
1020  else
1021  gpgme_data_release( datapart );
1022 
1023  gpgme_data_release( sigpart );
1024 
1025  obtain_signature_information( ctx, status, sigmeta,
1026  attrOrder, unknownAttrsHandling );
1027 
1028  gpgme_release( ctx );
1029  return ( status == GPGME_SIG_STAT_GOOD );
1030 }
1031 
1032 bool CryptPlug::decryptAndCheckMessage( const char* ciphertext,
1033  bool cipherIsBinary,
1034  int cipherLen,
1035  const char** cleartext,
1036  const char* /*certificate*/,
1037  bool* signatureFound,
1038  struct CryptPlug::SignatureMetaData* sigmeta,
1039  int* errId,
1040  char** errTxt,
1041  char** attrOrder,
1042  const char* unknownAttrsHandling )
1043 {
1044  gpgme_ctx_t ctx;
1045  gpgme_error_t err;
1046  gpgme_decrypt_result_t decryptresult;
1047  gpgme_data_t gCiphertext, gPlaintext;
1048  gpgme_sig_stat_t sigstatus = GPGME_SIG_STAT_NONE;
1049  size_t rCLen = 0;
1050  char* rCiph = 0;
1051  bool bOk = false;
1052 
1053  if( !ciphertext )
1054  return false;
1055 
1056  err = gpgme_new (&ctx);
1057  gpgme_set_protocol (ctx, GPGMEPLUG_PROTOCOL);
1058 
1059  gpgme_set_armor (ctx, cipherIsBinary ? 0 : 1);
1060  /* gpgme_set_textmode (ctx, cipherIsBinary ? 0 : 1); */
1061 
1062  /*
1063  gpgme_data_new_from_mem( &gCiphertext, ciphertext,
1064  1+strlen( ciphertext ), 1 ); */
1065  gpgme_data_new_from_mem( &gCiphertext,
1066  ciphertext,
1067  cipherIsBinary
1068  ? cipherLen
1069  : strlen( ciphertext ),
1070  1 );
1071 
1072  gpgme_data_new( &gPlaintext );
1073 
1074  err = gpgme_op_decrypt_verify( ctx, gCiphertext, gPlaintext );
1075  gpgme_data_release( gCiphertext );
1076 
1077  if( err ) {
1078  fprintf( stderr, "\ngpgme_op_decrypt_verify() returned this error code: %i\n\n", err );
1079  if( errId )
1080  *errId = err;
1081  if( errTxt ) {
1082  const char* _errTxt = gpgme_strerror( err );
1083  *errTxt = (char*)malloc( strlen( _errTxt ) + 1 );
1084  if( *errTxt )
1085  strcpy(*errTxt, _errTxt );
1086  }
1087  gpgme_data_release( gPlaintext );
1088  gpgme_release( ctx );
1089  return bOk;
1090  }
1091  decryptresult = gpgme_op_decrypt_result( ctx );
1092 
1093  bool bWrongKeyUsage = false;
1094 #ifdef HAVE_GPGME_WRONG_KEY_USAGE
1095  if( decryptresult && decryptresult->wrong_key_usage )
1096  bWrongKeyUsage = true;
1097 #endif
1098 
1099  if( bWrongKeyUsage ) {
1100  if( errId )
1101  *errId = CRYPTPLUG_ERR_WRONG_KEY_USAGE; // report the wrong key usage
1102  }
1103 
1104  rCiph = gpgme_data_release_and_get_mem( gPlaintext, &rCLen );
1105 
1106  *cleartext = (char*)malloc( rCLen + 1 );
1107  if( *cleartext ) {
1108  if( rCLen ) {
1109  bOk = true;
1110  strncpy((char*)*cleartext, rCiph, rCLen );
1111  }
1112  ((char*)(*cleartext))[rCLen] = 0;
1113  }
1114  free( rCiph );
1115 
1116  obtain_signature_information( ctx, sigstatus, sigmeta,
1117  attrOrder, unknownAttrsHandling,
1118  signatureFound );
1119 
1120  gpgme_release( ctx );
1121  return bOk;
1122 }
1123 
Common API header for CRYPTPLUG.