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
99SMIMECryptPlug::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
161OpenPGPCryptPlug::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
242static void *
243xmalloc (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: */
257static char *
258xstrdup (const char *string)
259{
260 char *p = (char*)xmalloc (strlen (string)+1);
261 strcpy (p, string);
262 return p;
263}
264
265
266CryptPlug::CryptPlug() {
267}
268
269CryptPlug::~CryptPlug() {
270}
271
272bool 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
279bool 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
309static
310void storeNewCharPtr( char** dest, const char* src )
311{
312 int sLen = strlen( src );
313 *dest = (char*)xmalloc( sLen + 1 );
314 strcpy( *dest, src );
315}
316
317bool 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
385static char *
386trim_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. */
407static const unsigned char *
408parse_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. */
510static CryptPlug::DnPair *
511parse_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
571static void
572add_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
587static int
588add_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
605static char*
606reorder_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
695GpgME::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:
718static gpgme_sig_stat_t
719sig_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
740static gpgme_sig_stat_t
741intersect_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
752static const char*
753sig_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!
789static
790void 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
961bool 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
1031bool 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.