certmanager/lib

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