00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "kpgpbase.h"
00020 #include "kpgp.h"
00021
00022 #include <klocale.h>
00023 #include <kshell.h>
00024 #include <kdebug.h>
00025
00026 #include <QTextCodec>
00027 #include <QByteArray>
00028
00029 #include <string.h>
00030
00031 namespace Kpgp {
00032
00033 BaseG::BaseG()
00034 : Base()
00035 {
00036
00037 runGpg( "--version", 0 );
00038 int eol = output.indexOf( '\n' );
00039 if( eol > 0 ) {
00040 int pos = output.lastIndexOf( ' ', eol - 1 );
00041 if( pos != -1 ) {
00042 mVersion = output.mid( pos + 1, eol - pos - 1 );
00043 kDebug( 5326 ) <<"found GnuPG" << mVersion;
00044 }
00045 }
00046 }
00047
00048
00049 BaseG::~BaseG()
00050 {
00051 }
00052
00053
00054 int
00055 BaseG::encrypt( Block& block, const KeyIDList& recipients )
00056 {
00057 return encsign( block, recipients, 0 );
00058 }
00059
00060
00061 int
00062 BaseG::clearsign( Block& block, const char *passphrase )
00063 {
00064 return encsign( block, KeyIDList(), passphrase );
00065 }
00066
00067
00068 int
00069 BaseG::encsign( Block& block, const KeyIDList& recipients,
00070 const char *passphrase )
00071 {
00072 QByteArray cmd;
00073 int exitStatus = 0;
00074
00075 if(!recipients.isEmpty() && passphrase != 0)
00076 cmd = "--batch --armor --sign --encrypt --textmode";
00077 else if(!recipients.isEmpty())
00078 cmd = "--batch --armor --encrypt --textmode";
00079 else if(passphrase != 0)
00080 cmd = "--batch --escape-from --clearsign";
00081 else
00082 {
00083 kDebug( 5326 ) <<"kpgpbase: Neither recipients nor passphrase specified.";
00084 return OK;
00085 }
00086
00087 if(passphrase != 0)
00088 cmd += addUserId();
00089
00090 if(!recipients.isEmpty())
00091 {
00092 cmd += " --set-filename stdin";
00093
00094 QByteArray pgpUser = Module::getKpgp()->user();
00095 if(Module::getKpgp()->encryptToSelf() && !pgpUser.isEmpty()) {
00096 cmd += " -r 0x";
00097 cmd += pgpUser;
00098 }
00099
00100 for( KeyIDList::ConstIterator it = recipients.begin();
00101 it != recipients.end(); ++it ) {
00102 cmd += " -r 0x";
00103 cmd += (*it);
00104 }
00105 }
00106
00107 clear();
00108 input = block.text();
00109 exitStatus = runGpg(cmd.data(), passphrase);
00110 if( !output.isEmpty() )
00111 block.setProcessedText( output );
00112 block.setError( error );
00113
00114 if( exitStatus != 0 )
00115 {
00116
00117 errMsg = i18n( "Unknown error." );
00118 status = ERROR;
00119 }
00120
00121 #if 0
00122
00123
00124
00125 if(!recipients.isEmpty())
00126 {
00127 int index = 0;
00128 bool bad = false;
00129 unsigned int num = 0;
00130 QByteArray badkeys = "";
00131
00132
00133
00134
00135
00136
00137
00138 while((index = error.indexOf("skipped: ",index) ) != -1 )
00139 {
00140 bad = true;
00141 index = error.indexOf('\'',index);
00142 int index2 = error.indexOf('\'',index+1);
00143 badkeys += error.mid(index, index2-index+1) + ", ";
00144 num++;
00145 }
00146 if(bad)
00147 {
00148 badkeys.trimmed();
00149 if(num == recipients.count())
00150 errMsg = i18n("Could not find public keys matching the userid(s)\n"
00151 "%1;\n"
00152 "the message is not encrypted.",
00153 badkeys.data() );
00154 else
00155 errMsg = i18n("Could not find public keys matching the userid(s)\n"
00156 "%1;\n"
00157 "these persons will not be able to read the message.",
00158 badkeys.data() );
00159 status |= MISSINGKEY;
00160 status |= ERROR;
00161 }
00162 }
00163 #endif
00164 if( passphrase != 0 )
00165 {
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178 if( error.contains("bad passphrase") )
00179 {
00180 errMsg = i18n("Signing failed because the passphrase is wrong.");
00181 status |= BADPHRASE;
00182 status |= ERR_SIGNING;
00183 status |= ERROR;
00184 }
00185 else if( error.contains("unusable secret key") )
00186 {
00187 errMsg = i18n("Signing failed because your secret key is unusable.");
00188 status |= ERR_SIGNING;
00189 status |= ERROR;
00190 }
00191 else if( !( status & ERROR ) )
00192 {
00193
00194 status |= SIGNED;
00195 }
00196 }
00197
00198
00199 block.setStatus( status );
00200 return status;
00201 }
00202
00203
00204 int
00205 BaseG::decrypt( Block& block, const char *passphrase )
00206 {
00207 int index, index2;
00208 int exitStatus = 0;
00209
00210 clear();
00211 input = block.text();
00212 exitStatus = runGpg("--batch --decrypt", passphrase);
00213 if( !output.isEmpty() && ( !error.contains( "gpg: quoted printable" ) ) )
00214 block.setProcessedText( output );
00215 block.setError( error );
00216
00217 if(exitStatus == -1) {
00218 errMsg = i18n("Error running gpg");
00219 status = ERROR;
00220 block.setStatus( status );
00221 return status;
00222 }
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244 if( error.contains( "gpg: encrypted with" ) )
00245 {
00246
00247 status |= ENCRYPTED;
00248 if( error.contains( "\ngpg: decryption failed" ) )
00249 {
00250 if( ( index = error.indexOf( "bad passphrase" ) ) != -1 )
00251 {
00252 if( passphrase != 0 )
00253 {
00254 errMsg = i18n( "Bad passphrase; could not decrypt." );
00255 kDebug( 5326 ) <<"Base: passphrase is bad";
00256 status |= BADPHRASE;
00257 status |= ERROR;
00258 }
00259 else
00260 {
00261
00262 index2 = error.lastIndexOf('"', index) - 1;
00263 index = error.lastIndexOf(" \"", index2) + 7;
00264
00265
00266 block.setRequiredUserId( QString::fromUtf8( error.mid( index, index2 - index + 1 ) ) );
00267 kDebug( 5326 ) <<"Base: key needed is \"" << block.requiredUserId() <<"\"!";
00268 }
00269 }
00270 else if( error.contains( "secret key not available" ) )
00271 {
00272
00273 status |= NO_SEC_KEY;
00274 status |= ERROR;
00275 errMsg = i18n("You do not have the secret key needed to decrypt this message.");
00276 kDebug( 5326 ) <<"Base: no secret key for this message";
00277 }
00278 }
00279
00280 #if 0
00281
00282
00283 index = error.indexOf("can only be read by:");
00284 if(index != -1)
00285 {
00286 index = error.indexOf('\n',index);
00287 int end = error.indexOf("\n\n",index);
00288
00289 mRecipients.clear();
00290 while( (index2 = error.indexOf('\n',index+1)) <= end )
00291 {
00292 QByteArray item = error.mid(index+1,index2-index-1);
00293 item.trimmed();
00294 mRecipients.append(item);
00295 index = index2;
00296 }
00297 }
00298 #endif
00299 }
00300
00301
00302
00303
00304 if( ( index = error.indexOf("Signature made") ) != -1 )
00305 {
00306
00307 status |= SIGNED;
00308
00309
00310 index2 = error.indexOf("using", index+15);
00311 block.setSignatureDate( error.mid(index+15, index2-(index+15)-1) );
00312 kDebug( 5326 ) <<"Message was signed on '" << block.signatureDate() <<"'";
00313 index2 = error.indexOf("key ID ", index2) + 7;
00314 block.setSignatureKeyId( error.mid(index2,8) );
00315 kDebug( 5326 ) <<"Message was signed with key '" << block.signatureKeyId() <<"'";
00316
00317 index = error.indexOf('\n', index2)+1;
00318
00319 if ((error.indexOf("Key matching expected", index) != -1 )
00320 || (error.indexOf("Can't check signature", index) != -1 ))
00321 {
00322 status |= UNKNOWN_SIG;
00323 status |= GOODSIG;
00324 block.setSignatureUserId( QString() );
00325 }
00326 else if( error.indexOf("Good signature", index) != -1 )
00327 {
00328 status |= GOODSIG;
00329
00330 index = error.indexOf('"',index);
00331 index2 = error.indexOf('\n',index+1);
00332 index2 = error.lastIndexOf('"', index2-1);
00333 block.setSignatureUserId( error.mid( index+1, index2-index-1 ) );
00334 }
00335 else if( error.indexOf("BAD signature", index) != -1 )
00336 {
00337
00338 status |= ERROR;
00339
00340 index = error.indexOf('"',index);
00341 index2 = error.indexOf('\n',index+1);
00342 index2 = error.lastIndexOf('"', index2-1);
00343 block.setSignatureUserId( error.mid( index+1, index2-index-1 ) );
00344 }
00345 else if( error.indexOf("Can't find the right public key", index) != -1 )
00346 {
00347
00348
00349
00350 status |= UNKNOWN_SIG;
00351 status |= GOODSIG;
00352 block.setSignatureUserId( i18n("??? (file ~/.gnupg/pubring.gpg not found)") );
00353 }
00354 else
00355 {
00356 status |= ERROR;
00357 block.setSignatureUserId( QString() );
00358 }
00359 }
00360
00361 block.setStatus( status );
00362 return status;
00363 }
00364
00365
00366 Key*
00367 BaseG::readPublicKey( const KeyID& keyID,
00368 const bool readTrust ,
00369 Key* key )
00370 {
00371 int exitStatus = 0;
00372
00373 status = 0;
00374 if( readTrust )
00375 exitStatus = runGpg( "--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode 0x" + keyID, 0, true );
00376 else
00377 exitStatus = runGpg( "--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode --no-expensive-trust-checks 0x" + keyID, 0, true );
00378
00379 if(exitStatus != 0) {
00380 status = ERROR;
00381 return 0;
00382 }
00383
00384 int offset;
00385
00386 if( !strncmp( output.data(), "pub:", 4 ) )
00387 offset = 0;
00388 else {
00389 offset = output.indexOf( "\npub:" );
00390 if( offset == -1 )
00391 return 0;
00392 else
00393 offset++;
00394 }
00395
00396 key = parseKeyData( output, offset, key );
00397
00398 return key;
00399 }
00400
00401
00402 KeyList
00403 BaseG::publicKeys( const QStringList & patterns )
00404 {
00405 int exitStatus = 0;
00406
00407
00408
00409 QByteArray cmd = "--batch --list-public-keys --with-fingerprint --with-colons "
00410 "--fixed-list-mode --no-expensive-trust-checks";
00411 for ( QStringList::ConstIterator it = patterns.begin();
00412 it != patterns.end(); ++it ) {
00413 cmd += ' ';
00414 cmd += KShell::quoteArg( *it ).toLocal8Bit();
00415 }
00416 status = 0;
00417 exitStatus = runGpg( cmd, 0, true );
00418
00419 if(exitStatus != 0) {
00420 status = ERROR;
00421 return KeyList();
00422 }
00423
00424
00425 KeyList publicKeys = parseKeyList(output, false);
00426
00427
00428 publicKeys.sort();
00429
00430 return publicKeys;
00431 }
00432
00433
00434 KeyList
00435 BaseG::secretKeys( const QStringList & patterns )
00436 {
00437 int exitStatus = 0;
00438
00439
00440
00441 QByteArray cmd = "--batch --list-secret-keys --with-fingerprint --with-colons "
00442 "--fixed-list-mode";
00443 for ( QStringList::ConstIterator it = patterns.begin();
00444 it != patterns.end(); ++it ) {
00445 cmd += ' ';
00446 cmd += KShell::quoteArg( *it ).toLocal8Bit();
00447 }
00448 status = 0;
00449 exitStatus = runGpg( cmd, 0, true );
00450
00451 if(exitStatus != 0) {
00452 status = ERROR;
00453 return KeyList();
00454 }
00455
00456
00457 KeyList secretKeys = parseKeyList(output, true);
00458
00459
00460 secretKeys.sort();
00461
00462 return secretKeys;
00463 }
00464
00465
00466 int
00467 BaseG::signKey(const KeyID& keyID, const char *passphrase)
00468 {
00469 QByteArray cmd;
00470 int exitStatus = 0;
00471
00472 cmd = "--batch";
00473 cmd += addUserId();
00474 cmd += " --sign-key 0x";
00475 cmd += keyID;
00476
00477 status = 0;
00478 exitStatus = runGpg(cmd.data(), passphrase);
00479
00480 if (exitStatus != 0)
00481 status = ERROR;
00482
00483 return status;
00484 }
00485
00486
00487 QByteArray
00488 BaseG::getAsciiPublicKey(const KeyID& keyID)
00489 {
00490 int exitStatus = 0;
00491
00492 if (keyID.isEmpty())
00493 return QByteArray();
00494
00495 status = 0;
00496 exitStatus = runGpg("--batch --armor --export 0x" + keyID, 0, true);
00497
00498 if(exitStatus != 0) {
00499 status = ERROR;
00500 return QByteArray();
00501 }
00502
00503 return output;
00504 }
00505
00506
00507 Key*
00508 BaseG::parseKeyData( const QByteArray& output, int& offset, Key* key )
00509
00510
00511
00512
00513
00514
00515
00516 {
00517 int index = offset;
00518
00519 if( ( strncmp( output.data() + offset, "pub:", 4 ) != 0 )
00520 && ( strncmp( output.data() + offset, "sec:", 4 ) != 0 ) ) {
00521 return 0;
00522 }
00523
00524 if( key == 0 )
00525 key = new Key();
00526 else
00527 key->clear();
00528
00529 QByteArray keyID;
00530 bool firstKey = true;
00531
00532 while( true )
00533 {
00534 int eol;
00535
00536 if( ( eol = output.indexOf( '\n', index ) ) == -1 )
00537 break;
00538
00539 bool bIsPublicKey = false;
00540 if( ( bIsPublicKey = !strncmp( output.data() + index, "pub:", 4 ) )
00541 || !strncmp( output.data() + index, "sec:", 4 ) )
00542 {
00543
00544
00545
00546 if( !firstKey )
00547 break;
00548 firstKey = false;
00549
00550 key->setSecret( !bIsPublicKey );
00551
00552 Subkey *subkey = new Subkey( QByteArray(), !bIsPublicKey );
00553
00554 int pos = index + 4;
00555 int pos2 = output.indexOf( ':', pos );
00556 for( int field = 2; field <= 12; field++ )
00557 {
00558 switch( field )
00559 {
00560 case 2:
00561 if( pos2 > pos )
00562 {
00563 switch( output[pos] )
00564 {
00565 case 'o':
00566 break;
00567 case 'i':
00568 subkey->setInvalid( true );
00569 key->setInvalid( true );
00570 break;
00571 case 'd':
00572 subkey->setDisabled( true );
00573 key->setDisabled( true );
00574 break;
00575 case 'r':
00576 subkey->setRevoked( true );
00577 key->setRevoked( true );
00578 break;
00579 case 'e':
00580 subkey->setExpired( true );
00581 key->setExpired( true );
00582 break;
00583 case '-':
00584 case 'q':
00585 case 'n':
00586 case 'm':
00587 case 'f':
00588 case 'u':
00589
00590
00591 break;
00592 default:
00593 kDebug( 5326 ) <<"Unknown trust value";
00594 }
00595 }
00596 break;
00597 case 3:
00598 if( pos2 > pos )
00599 subkey->setKeyLength( output.mid( pos, pos2-pos ).toUInt() );
00600 break;
00601 case 4:
00602 if( pos2 > pos )
00603 subkey->setKeyAlgorithm( output.mid( pos, pos2-pos ).toUInt() );
00604 break;
00605 case 5:
00606 keyID = output.mid( pos, pos2-pos );
00607 subkey->setKeyID( keyID );
00608 break;
00609 case 6:
00610 if( pos2 > pos )
00611 subkey->setCreationDate( QString(output.mid( pos, pos2-pos )).toLong() );
00612 break;
00613 case 7:
00614 if( pos2 > pos )
00615 subkey->setExpirationDate( QString(output.mid( pos, pos2-pos )).toLong() );
00616 else
00617 subkey->setExpirationDate( -1 );
00618 break;
00619 case 8:
00620 case 9:
00621 case 10:
00622 case 11:
00623 break;
00624 case 12:
00625 for( int i=pos; i<pos2; i++ )
00626 switch( output[i] )
00627 {
00628 case 'e':
00629 subkey->setCanEncrypt( true );
00630 break;
00631 case 's':
00632 subkey->setCanSign( true );
00633 break;
00634 case 'c':
00635 subkey->setCanCertify( true );
00636 break;
00637 case 'E':
00638 key->setCanEncrypt( true );
00639 break;
00640 case 'S':
00641 key->setCanSign( true );
00642 break;
00643 case 'C':
00644 key->setCanCertify( true );
00645 break;
00646 default:
00647 kDebug( 5326 ) <<"Unknown key capability";
00648 }
00649 break;
00650 }
00651 pos = pos2 + 1;
00652 pos2 = output.indexOf( ':', pos );
00653 }
00654 key->addSubkey( subkey );
00655 }
00656 else if( !strncmp( output.data() + index, "uid:", 4 ) )
00657 {
00658
00659
00660 UserID *userID = new UserID( "" );
00661
00662 int pos = index + 4;
00663 int pos2 = output.indexOf( ':', pos );
00664 for( int field=2; field <= 10; field++ )
00665 {
00666 switch( field )
00667 {
00668 case 2:
00669 if( pos2 > pos )
00670 {
00671 switch( output[pos] )
00672 {
00673 case 'i':
00674 userID->setInvalid( true );
00675 break;
00676 case 'r':
00677 userID->setRevoked( true );
00678 break;
00679 case '-':
00680 case 'q':
00681 userID->setValidity( KPGP_VALIDITY_UNDEFINED );
00682 break;
00683 case 'n':
00684 userID->setValidity( KPGP_VALIDITY_NEVER );
00685 break;
00686 case 'm':
00687 userID->setValidity( KPGP_VALIDITY_MARGINAL );
00688 break;
00689 case 'f':
00690 userID->setValidity( KPGP_VALIDITY_FULL );
00691 break;
00692 case 'u':
00693 userID->setValidity( KPGP_VALIDITY_ULTIMATE );
00694 break;
00695 default:
00696 kDebug( 5326 ) <<"Unknown trust value";
00697 }
00698 }
00699 break;
00700 case 3:
00701 case 4:
00702 case 5:
00703 case 6:
00704 case 7:
00705 case 8:
00706 case 9:
00707 break;
00708 case 10:
00709 QByteArray uid = output.mid( pos, pos2-pos );
00710
00711
00712
00713 for ( int idx = 0 ; (idx = uid.indexOf( "\\x", idx ) != -1) ; ++idx ) {
00714 char str[2] = "x";
00715 str[0] = (char) QString( uid.mid( idx + 2, 2 ) ).toShort( 0, 16 );
00716 uid.replace( idx, 4, str );
00717 }
00718 QString uidString = QString::fromUtf8( uid.data() );
00719
00720 bool isUtf8 = true;
00721 for ( int i = 0; i + 1 < uidString.length(); ++i ) {
00722 if ( uidString[i].unicode() == 0xdbff &&
00723 uidString[i+1].row() == 0xde ) {
00724
00725 isUtf8 = false;
00726 break;
00727 }
00728 }
00729 if( !isUtf8 ) {
00730
00731
00732 kDebug( 5326 ) <<"User Id '" << uid
00733 << "' doesn't seem to be utf-8 encoded.";
00734
00735
00736
00737 int nonAsciiCount = 0, asciiCount = 0;
00738
00739
00740
00741 for( signed char* ch = (signed char*)uid.data();
00742 *ch && ( *ch != '(' ) && ( *ch != '<' );
00743 ++ch ) {
00744 if( ( ( *ch >= 'A' ) && ( *ch <= 'Z' ) )
00745 || ( ( *ch >= 'a' ) && ( *ch <= 'z' ) ) )
00746 ++asciiCount;
00747 else if( *ch < 0 )
00748 ++nonAsciiCount;
00749 }
00750 kDebug( 5326 ) <<"ascii-nonAscii ratio :" << asciiCount
00751 << ":" << nonAsciiCount;
00752 if( nonAsciiCount > asciiCount ) {
00753
00754 kDebug( 5326 ) <<"Assume koi8-r encoding.";
00755 QTextCodec *codec = QTextCodec::codecForName("KOI8-R");
00756 uidString = codec->toUnicode( uid.data() );
00757
00758
00759
00760
00761
00762
00763
00764 if( ( uidString.length() >= 2 )
00765 && ( uidString[0].toLower() == uidString[0] )
00766 && ( uidString[1].toUpper() == uidString[1] ) ) {
00767
00768
00769 kDebug( 5326 ) <<"No, it doesn't seem to be koi8-r."
00770 "Use CP 1251 instead.";
00771 QTextCodec *codec = QTextCodec::codecForName("CP1251");
00772 uidString = codec->toUnicode( uid.data() );
00773 }
00774 }
00775 else {
00776
00777 kDebug( 5326 ) <<"Assume latin1 encoding.";
00778 uidString = QString::fromLatin1( uid.data() );
00779 }
00780 }
00781 userID->setText( uidString );
00782 break;
00783 }
00784 pos = pos2 + 1;
00785 pos2 = output.indexOf( ':', pos );
00786 }
00787
00788
00789 key->addUserID( userID );
00790 }
00791 else if( !strncmp( output.data() + index, "fpr:", 4 ) )
00792 {
00793
00794
00795 if (key == 0)
00796 break;
00797
00798
00799 int pos = index + 4;
00800 for( int i = 0; i < 8; i++ )
00801 pos = output.indexOf( ':', pos ) + 1;
00802 int pos2 = output.indexOf( ':', pos );
00803
00804 key->setFingerprint( keyID, output.mid( pos, pos2-pos ) );
00805 }
00806 index = eol + 1;
00807 }
00808
00809
00810
00811 offset = index;
00812
00813 return key;
00814 }
00815
00816
00817 KeyList
00818 BaseG::parseKeyList( const QByteArray& output, bool secretKeys )
00819 {
00820 KeyList keys;
00821 Key *key = 0;
00822 int offset;
00823
00824
00825 if( !strncmp( output.data(), "pub:", 4 )
00826 || !strncmp( output.data(), "sec:", 4 ) )
00827 offset = 0;
00828 else {
00829 if( secretKeys )
00830 offset = output.indexOf( "\nsec:" );
00831 else
00832 offset = output.indexOf( "\npub:" );
00833 if( offset == -1 )
00834 return keys;
00835 else
00836 offset++;
00837 }
00838
00839 do {
00840 key = parseKeyData( output, offset );
00841 if( key != 0 )
00842 keys.append( key );
00843 }
00844 while( key != 0 );
00845
00846
00847
00848 return keys;
00849 }
00850
00851
00852 }