00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #ifdef HAVE_CONFIG_H
00020 #include <config.h>
00021 #endif
00022
00023 #include "kpgpbase.h"
00024 #include "kpgp.h"
00025
00026 #include <string.h>
00027 #include <assert.h>
00028
00029 #include <qregexp.h>
00030 #include <qdatetime.h>
00031
00032 #include <klocale.h>
00033 #include <kprocess.h>
00034 #include <kdebug.h>
00035
00036
00037 namespace Kpgp {
00038
00039 Base5::Base5()
00040 : Base()
00041 {
00042 }
00043
00044
00045 Base5::~Base5()
00046 {
00047 }
00048
00049
00050 int
00051 Base5::encrypt( Block& block, const KeyIDList& recipients )
00052 {
00053 return encsign( block, recipients, 0 );
00054 }
00055
00056
00057 int
00058 Base5::clearsign( Block& block, const char *passphrase )
00059 {
00060 return encsign( block, KeyIDList(), passphrase );
00061 }
00062
00063
00064 int
00065 Base5::encsign( Block& block, const KeyIDList& recipients,
00066 const char *passphrase )
00067 {
00068 QCString cmd;
00069 int exitStatus = 0;
00070 int index;
00071
00072
00073
00074 bool signonly = false;
00075
00076 if(!recipients.isEmpty() && passphrase != 0)
00077 cmd = "pgpe +batchmode -afts ";
00078 else if(!recipients.isEmpty())
00079 cmd = "pgpe +batchmode -aft ";
00080 else if(passphrase != 0)
00081 {
00082 cmd = "pgps +batchmode -abft ";
00083 signonly = true;
00084 }
00085 else
00086 {
00087 errMsg = i18n("Neither recipients nor passphrase specified.");
00088 return OK;
00089 }
00090
00091 if(passphrase != 0)
00092 cmd += addUserId();
00093
00094 if(!recipients.isEmpty())
00095 {
00096 if(Module::getKpgp()->encryptToSelf())
00097 {
00098 cmd += " -r 0x";
00099 cmd += Module::getKpgp()->user();
00100 }
00101
00102 for( KeyIDList::ConstIterator it = recipients.begin();
00103 it != recipients.end(); ++it ) {
00104 cmd += " -r 0x";
00105 cmd += (*it);
00106 }
00107 }
00108
00109 clear();
00110 input = block.text();
00111
00112 if (signonly)
00113 {
00114 input.append("\n");
00115 input.replace(QRegExp("[ \t]+\n"), "\n");
00116 }
00117
00118
00119 exitStatus = run(cmd.data(), passphrase);
00120 block.setError( error );
00121
00122 if(exitStatus != 0)
00123 status = ERROR;
00124
00125
00126 if(error.find("Cannot unlock private key") != -1)
00127 {
00128 errMsg = i18n("The passphrase you entered is invalid.");
00129 status |= ERROR;
00130 status |= BADPHRASE;
00131 }
00132
00133
00134 QCString aStr;
00135 index = -1;
00136 while((index = error.find("WARNING: The above key",index+1)) != -1)
00137 {
00138 int index2 = error.find("But you previously",index);
00139 int index3 = error.find("WARNING: The above key",index+1);
00140 if(index2 == -1 || (index2 > index3 && index3 != -1))
00141 {
00142
00143
00144 index2 = error.find('\n',index);
00145 index3 = error.find('\n',index2+1);
00146 aStr += error.mid(index2+1, index3-index2-1);
00147 aStr += ", ";
00148 }
00149 }
00150 if(!aStr.isEmpty())
00151 {
00152 aStr.truncate(aStr.length()-2);
00153 if(error.find("No valid keys found") != -1)
00154 errMsg = i18n("The key(s) you want to encrypt your message "
00155 "to are not trusted. No encryption done.");
00156 else
00157 errMsg = i18n("The following key(s) are not trusted:\n%1\n"
00158 "Their owner(s) will not be able to decrypt the message.")
00159 .arg(aStr);
00160 status |= ERROR;
00161 status |= BADKEYS;
00162 }
00163
00164 if((index = error.find("No encryption keys found for")) != -1)
00165 {
00166 index = error.find(':',index);
00167 int index2 = error.find('\n',index);
00168
00169 errMsg = i18n("Missing encryption key(s) for:\n%1")
00170 .arg(error.mid(index,index2-index));
00171
00172
00173 status |= ERROR;
00174 status |= MISSINGKEY;
00175 }
00176
00177 if(signonly) {
00178
00179 if (input[0] == '-')
00180 input = "- " + input;
00181 for ( int idx = 0 ; (idx = input.find("\n-", idx)) >= 0 ; idx += 4 )
00182 input.replace(idx, 2, "\n- -");
00183
00184 output = "-----BEGIN PGP SIGNED MESSAGE-----\n\n" + input + "\n" + output;
00185 }
00186
00187 block.setProcessedText( output );
00188 block.setStatus( status );
00189 return status;
00190 }
00191
00192
00193 int
00194 Base5::decrypt( Block& block, const char *passphrase )
00195 {
00196 int exitStatus = 0;
00197
00198 clear();
00199 input = block.text();
00200 exitStatus = run("pgpv -f +batchmode=1", passphrase);
00201 if( !output.isEmpty() )
00202 block.setProcessedText( output );
00203 block.setError( error );
00204
00205 if(exitStatus == -1) {
00206 errMsg = i18n("Error running PGP");
00207 status = RUN_ERR;
00208 block.setStatus( status );
00209 return status;
00210 }
00211
00212
00213 int index;
00214
00215 index = error.find("Cannot decrypt message");
00216 if(index != -1)
00217 {
00218
00219 status |= ENCRYPTED;
00220
00221
00222
00223 if(error.find("Need a pass phrase") != -1)
00224 {
00225 if(passphrase != 0)
00226 {
00227 errMsg = i18n("Bad passphrase; could not decrypt.");
00228 kdDebug(5100) << "Base: passphrase is bad" << endl;
00229 status |= BADPHRASE;
00230 status |= ERROR;
00231 }
00232 }
00233 else
00234 {
00235
00236 status |= NO_SEC_KEY;
00237 status |= ERROR;
00238 errMsg = i18n("You do not have the secret key needed to decrypt this message.");
00239 kdDebug(5100) << "Base: no secret key for this message" << endl;
00240 }
00241
00242 #if 0
00243
00244
00245 index = error.find("can only be decrypted by:");
00246 if(index != -1)
00247 {
00248 index = error.find('\n',index);
00249 int end = error.find("\n\n",index);
00250
00251 mRecipients.clear();
00252 int index2;
00253 while( (index2 = error.find('\n',index+1)) <= end )
00254 {
00255 QCString item = error.mid(index+1,index2-index-1);
00256 item.stripWhiteSpace();
00257 mRecipients.append(item);
00258 index = index2;
00259 }
00260 }
00261 #endif
00262 }
00263 index = error.find("Good signature");
00264 if(index != -1)
00265 {
00266
00267 status |= SIGNED;
00268 status |= GOODSIG;
00269
00270
00271 index = error.find("Key ID ", index) + 7;
00272 block.setSignatureKeyId( error.mid(index, 8) );
00273
00274
00275 index = error.find('"',index) + 1;
00276 int index2 = error.find('"', index);
00277 block.setSignatureUserId( error.mid(index, index2-index) );
00278
00280 block.setSignatureDate( "" );
00281 }
00282 index = error.find("BAD signature");
00283 if(index != -1)
00284 {
00285
00286 status |= SIGNED;
00287 status |= ERROR;
00288
00289
00290 index = error.find("Key ID ", index) + 7;
00291 block.setSignatureKeyId( error.mid(index, 8) );
00292
00293
00294 index = error.find('"',index) + 1;
00295 int index2 = error.find('"', index);
00296 block.setSignatureUserId( error.mid(index, index2-index) );
00297
00299 block.setSignatureDate( "" );
00300 }
00301 index = error.find("Signature by unknown key");
00302 if(index != -1)
00303 {
00304 index = error.find("keyid: 0x",index) + 9;
00305 block.setSignatureKeyId( error.mid(index, 8) );
00306 block.setSignatureUserId( QString::null );
00307
00308 status |= SIGNED;
00309 status |= GOODSIG;
00310
00312 block.setSignatureDate( "" );
00313 }
00314
00315
00316 block.setStatus( status );
00317 return status;
00318 }
00319
00320
00321 Key*
00322 Base5::readPublicKey( const KeyID& keyId, const bool readTrust, Key* key )
00323 {
00324 int exitStatus = 0;
00325
00326 status = 0;
00327 exitStatus = run( "pgpk -ll 0x" + keyId, 0, true );
00328
00329 if(exitStatus != 0) {
00330 status = ERROR;
00331 return 0;
00332 }
00333
00334 key = parseSingleKey( output, key );
00335
00336 if( key == 0 )
00337 {
00338 return 0;
00339 }
00340
00341 if( readTrust )
00342 {
00343 exitStatus = run( "pgpk -c 0x" + keyId, 0, true );
00344
00345 if(exitStatus != 0) {
00346 status = ERROR;
00347 return 0;
00348 }
00349
00350 parseTrustDataForKey( key, output );
00351 }
00352
00353 return key;
00354 }
00355
00356
00357 KeyList
00358 Base5::publicKeys( const QStringList & patterns )
00359 {
00360 int exitStatus = 0;
00361
00362 QCString cmd = "pgpk -ll";
00363 for ( QStringList::ConstIterator it = patterns.begin();
00364 it != patterns.end(); ++it ) {
00365 cmd += " ";
00366 cmd += KProcess::quote( *it ).local8Bit();
00367 }
00368 status = 0;
00369 exitStatus = run( cmd, 0, true );
00370
00371 if(exitStatus != 0) {
00372 status = ERROR;
00373 return KeyList();
00374 }
00375
00376
00377 KeyList keys = parseKeyList( output, false );
00378
00379
00380 keys.sort();
00381
00382 return keys;
00383 }
00384
00385
00386 KeyList
00387 Base5::secretKeys( const QStringList & patterns )
00388 {
00389 int exitStatus = 0;
00390
00391 status = 0;
00392 QCString cmd = "pgpk -ll";
00393 for ( QStringList::ConstIterator it = patterns.begin();
00394 it != patterns.end(); ++it ) {
00395 cmd += " ";
00396 cmd += KProcess::quote( *it ).local8Bit();
00397 }
00398 status = 0;
00399 exitStatus = run( cmd, 0, true );
00400
00401 if(exitStatus != 0) {
00402 status = ERROR;
00403 return KeyList();
00404 }
00405
00406
00407 KeyList keys = parseKeyList( output, true );
00408
00409
00410 keys.sort();
00411
00412 return keys;
00413 }
00414
00415
00416 QCString Base5::getAsciiPublicKey(const KeyID& keyID)
00417 {
00418 int exitStatus = 0;
00419
00420 if (keyID.isEmpty())
00421 return QCString();
00422
00423 status = 0;
00424 exitStatus = run( "pgpk -xa 0x" + keyID, 0, true );
00425
00426 if(exitStatus != 0) {
00427 status = ERROR;
00428 return QCString();
00429 }
00430
00431 return output;
00432 }
00433
00434
00435 int
00436 Base5::signKey(const KeyID& keyID, const char *passphrase)
00437 {
00438 QCString cmd;
00439 int exitStatus = 0;
00440
00441 if(passphrase == 0) return false;
00442
00443 cmd = "pgpk -s -f +batchmode=1 0x";
00444 cmd += keyID;
00445 cmd += addUserId();
00446
00447 status = 0;
00448 exitStatus = run(cmd.data(), passphrase);
00449
00450 if (exitStatus != 0)
00451 status = ERROR;
00452
00453 return status;
00454 }
00455
00456
00457
00458 Key*
00459 Base5::parseKeyData( const QCString& output, int& offset, Key* key )
00460
00461
00462
00463
00464
00465 {
00466 if( ( strncmp( output.data() + offset, "pub", 3 ) != 0 ) &&
00467 ( strncmp( output.data() + offset, "sec", 3 ) != 0 ) )
00468 {
00469 kdDebug(5100) << "Unknown key type or corrupt key data.\n";
00470 return 0;
00471 }
00472
00473 if( key == 0 )
00474 key = new Key();
00475 else
00476 key->clear();
00477
00478 Subkey *subkey = 0;
00479 bool primaryKey = true;
00480
00481 while( true )
00482 {
00483 int eol;
00484
00485
00486 eol = output.find( '\n', offset );
00487 if( ( eol == -1 ) || ( eol == offset ) )
00488 break;
00489
00490
00491
00492 if( !strncmp( output.data() + offset, "pub", 3 ) ||
00493 !strncmp( output.data() + offset, "sec", 3 ) ||
00494 !strncmp( output.data() + offset, "sub", 3 ) )
00495 {
00496
00497 int pos, pos2;
00498
00499 subkey = new Subkey( "", false );
00500 key->addSubkey( subkey );
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511 switch( output[offset+3] )
00512 {
00513 case ' ':
00514 break;
00515 case '@':
00516 subkey->setDisabled( true );
00517 key->setDisabled( true );
00518 break;
00519 default:
00520
00521 ;
00522 }
00523
00524
00525 pos = offset + 4;
00526 while( output[pos] == ' ' )
00527 pos++;
00528 pos2 = output.find( ' ', pos );
00529 subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
00530
00531
00532
00533 pos = pos2 + 1;
00534 while( output[pos] == ' ' )
00535 pos++;
00536 pos += 2;
00537 pos2 = output.find( ' ', pos );
00538 subkey->setKeyID( output.mid( pos, pos2-pos ) );
00539
00540
00541
00542 pos = pos2 + 1;
00543 while( output[pos] == ' ' )
00544 pos++;
00545 pos2 = output.find( ' ', pos );
00546 int year = output.mid( pos, 4 ).toInt();
00547 int month = output.mid( pos+5, 2 ).toInt();
00548 int day = output.mid( pos+8, 2 ).toInt();
00549 QDateTime dt( QDate( year, month, day ), QTime( 00, 00 ) );
00550 QDateTime epoch( QDate( 1970, 01, 01 ), QTime( 00, 00 ) );
00551
00552
00553
00554
00555 subkey->setCreationDate( epoch.secsTo( dt ) );
00556
00557
00558
00559 if( primaryKey || !key->revoked() )
00560 {
00561 pos = pos2 + 1;
00562 while( output[pos] == ' ' )
00563 pos++;
00564 pos2 = output.find( ' ', pos );
00565 if( output[pos] == '-' )
00566 {
00567 subkey->setExpirationDate( -1 );
00568 }
00569 else if( !strncmp( output.data() + pos, "*REVOKED*", 9 ) )
00570 {
00571 subkey->setRevoked( true );
00572 key->setRevoked( true );
00573 }
00574 else
00575 {
00576 int year = output.mid( pos, 4 ).toInt();
00577 int month = output.mid( pos+5, 2 ).toInt();
00578 int day = output.mid( pos+8, 2 ).toInt();
00579 QDateTime dt( QDate( year, month, day ), QTime( 00, 00 ) );
00580 subkey->setCreationDate( epoch.secsTo( dt ) );
00581
00582 if( QDateTime::currentDateTime() >= dt )
00583 {
00584 subkey->setExpired( true );
00585 key->setExpired( true );
00586 }
00587 }
00588 }
00589 else if( key->revoked() )
00590 subkey->setRevoked( true );
00591
00592
00593 bool sign = false;
00594 bool encr = false;
00595 pos = pos2 + 1;
00596 while( output[pos] == ' ' )
00597 pos++;
00598 pos2 = output.find( ' ', pos );
00599 if( !strncmp( output.data() + pos, "RSA", 3 ) )
00600 {
00601 sign = true;
00602 encr = true;
00603 }
00604 else if( !strncmp( output.data() + pos, "DSS", 3 ) )
00605 sign = true;
00606 else if( !strncmp( output.data() + pos, "Diffie-Hellman", 14 ) )
00607 encr = true;
00608 else
00609 kdDebug(5100)<<"Unknown key algorithm\n";
00610
00611
00612 subkey->setCanEncrypt( encr );
00613 subkey->setCanSign( sign );
00614 subkey->setCanCertify( sign );
00615
00616 if( primaryKey )
00617 {
00618
00619 bool canSign = false;
00620 bool canEncr = false;
00621 pos = pos2 + 1;
00622 while( output[pos] == ' ' )
00623 pos++;
00624 pos2 = eol;
00625 if( !strncmp( output.data() + pos, "Sign & Encrypt", 14 ) )
00626 {
00627 canSign = true;
00628 canEncr = true;
00629 }
00630 else if( !strncmp( output.data() + pos, "Sign only", 9 ) )
00631 canSign = true;
00632 else if( !strncmp( output.data() + pos, "Encrypt only", 12 ) )
00633 canEncr = true;
00634 else
00635 kdDebug(5100)<<"Unknown key capability\n";
00636
00637
00638 if( !key->expired() && !key->revoked() )
00639 {
00640 key->setCanEncrypt( canEncr );
00641 key->setCanSign( canSign );
00642 key->setCanCertify( canSign );
00643 }
00644
00645 primaryKey = false;
00646 }
00647 }
00648 else if( !strncmp( output.data() + offset, "f16", 3 ) ||
00649 !strncmp( output.data() + offset, "f20", 3 ) )
00650 {
00651
00652
00653
00654
00655
00656 int pos = output.find( '=', offset+3 ) + 2;
00657 QCString fingerprint = output.mid( pos, eol-pos );
00658
00659 for ( int idx = 0 ; (idx = fingerprint.find(' ', idx)) >= 0 ; )
00660 fingerprint.replace( idx, 1, "" );
00661 assert( subkey != 0 );
00662 subkey->setFingerprint( fingerprint );
00663
00664 }
00665 else if( !strncmp( output.data() + offset, "uid", 3 ) )
00666 {
00667 int pos = offset+5;
00668 QCString uid = output.mid( pos, eol-pos );
00669 key->addUserID( uid );
00670
00671
00672
00673
00674
00675
00676
00677 }
00678 else if ( !strncmp( output.data() + offset, "sig", 3 ) ||
00679 !strncmp( output.data() + offset, "SIG", 3 ) ||
00680 !strncmp( output.data() + offset, "ret", 3 ) )
00681 {
00682
00683
00684 }
00685
00686 offset = eol + 1;
00687 }
00688
00689 return key;
00690 }
00691
00692
00693 Key*
00694 Base5::parseSingleKey( const QCString& output, Key* key )
00695 {
00696 int offset;
00697
00698
00699 if( !strncmp( output.data(), "Type Bits", 9 ) )
00700 offset = 0;
00701 else
00702 {
00703 offset = output.find( "\nType Bits" ) + 1;
00704 if( offset == 0 )
00705 return 0;
00706 }
00707
00708
00709 offset = output.find( '\n', offset ) + 1;
00710 if( offset == -1 )
00711 return 0;
00712
00713 key = parseKeyData( output, offset, key );
00714
00715
00716
00717 return key;
00718 }
00719
00720
00721 KeyList
00722 Base5::parseKeyList( const QCString& output, bool onlySecretKeys )
00723 {
00724 KeyList keys;
00725 Key *key = 0;
00726 int offset;
00727
00728
00729 if( !strncmp( output.data(), "Type Bits", 9 ) )
00730 offset = 0;
00731 else
00732 {
00733 offset = output.find( "\nType Bits" ) + 1;
00734 if( offset == 0 )
00735 return keys;
00736 }
00737
00738
00739 offset = output.find( '\n', offset ) + 1;
00740 if( offset == -1 )
00741 return keys;
00742
00743 do
00744 {
00745 key = parseKeyData( output, offset );
00746 if( key != 0 )
00747 {
00748
00749 if( !onlySecretKeys || !key->secret() )
00750 keys.append( key );
00751
00752 offset++;
00753 }
00754 }
00755 while( key != 0 );
00756
00757
00758
00759 return keys;
00760 }
00761
00762
00763 void
00764 Base5::parseTrustDataForKey( Key* key, const QCString& str )
00765 {
00766 if( ( key == 0 ) || str.isEmpty() )
00767 return;
00768
00769 QCString keyID = "0x" + key->primaryKeyID();
00770 UserIDList userIDs = key->userIDs();
00771
00772
00773 int offset = str.find( "\n\n KeyID" ) + 9;
00774 if( offset == -1 + 9 )
00775 return;
00776
00777 offset = str.find( '\n', offset ) + 1;
00778 if( offset == -1 + 1 )
00779 return;
00780
00781 bool ultimateTrust = false;
00782 if( !strncmp( str.data() + offset+13, "ultimate", 8 ) )
00783 ultimateTrust = true;
00784
00785 while( true )
00786 {
00787
00788 int eol;
00789
00790
00791 if( ( eol = str.find( '\n', offset ) ) == -1 )
00792 break;
00793
00794 if( str[offset+23] != ' ' )
00795 {
00796
00797
00798 Validity validity = KPGP_VALIDITY_UNKNOWN;
00799 if( !strncmp( str.data() + offset+23, "complete", 8 ) )
00800 if( ultimateTrust )
00801 validity = KPGP_VALIDITY_ULTIMATE;
00802 else
00803 validity = KPGP_VALIDITY_FULL;
00804 else if( !strncmp( str.data() + offset+23, "marginal", 8 ) )
00805 validity = KPGP_VALIDITY_MARGINAL;
00806 else if( !strncmp( str.data() + offset+23, "invalid", 7 ) )
00807 validity = KPGP_VALIDITY_UNDEFINED;
00808
00809
00810 int pos = offset + 33;
00811 QString uid = str.mid( pos, eol-pos );
00812
00813
00814 for( UserIDListIterator it( userIDs ); it.current(); ++it )
00815 if( (*it)->text() == uid )
00816 {
00817 kdDebug(5100)<<"Setting the validity of "<<uid<<" to "<<validity<<endl;
00818 (*it)->setValidity( validity );
00819 break;
00820 }
00821 }
00822
00823 offset = eol + 1;
00824 }
00825 }
00826
00827
00828 }