📄 pgpdecode.c
字号:
if( IsPGPError( err = pgpFindOptionArgs( optionList,
kPGPOptionType_PassThroughKeys, FALSE,
"%d", &fPassKeys ) ) )
goto error;
if( IsPGPError( err = pgpFindOptionArgs( optionList,
kPGPOptionType_RecursivelyDecode, FALSE,
"%d", &fRecurse ) ) )
goto error;
*recurse = (PGPBoolean)fRecurse;
fPassClear |= fRecurse; /* Recurse needs passthroughclearsigned */
if( fPassClear || fPassKeys ) {
*fifo = pgpFifoCreate( context, &pgpByteFifoDesc );
if (*fifo == NULL) {
err = kPGPError_OutOfMemory;
goto error;
}
}
if( fPassClear ) {
pipeHead->annotate( pipeHead, NULL, PGPANN_PASSTHROUGH_CLEARSIGN,
&boolFlag, 1 );
}
if( fPassKeys ) {
pipeHead->annotate( pipeHead, NULL, PGPANN_PASSTHROUGH_KEYS,
&boolFlag, 1 );
}
return kPGPError_NoErr;
error:
if( IsntNull( *fifo ) ) {
pgpFifoDestroy( &pgpByteFifoDesc, *fifo );
*fifo = NULL;
}
return err;
}
/* Burn a passphrase from the job state structure, if any */
static void
pgpBurnDecodePassphrase ( PGPDecodeJob *s )
{
if( IsntNull( s->passPhrase ) ) {
pgpClearMemory( s->passPhrase, s->passLength );
PGPFreeData( s->passPhrase );
s->passPhrase = NULL;
s->passLength = 0;
}
}
/*********************** Subroutines for callbacks *************************/
/* Close an existing output pipeline and do appropriate cleanup */
static PGPError
pgpCloseOutput(
PGPDecodeJob *s,
PGPPipeline **output
)
{
PGPError err;
if( IsntNull( *output ) ) {
(*output)->sizeAdvise (*output, 0);
if( IsntNull( s->outPipe ) ) {
/* Return buffer sizes to user */
if( IsntNull( s->outBufPtrPtr ) ) {
/* Dynamically allocated buffer - tell user size & position */
if( IsPGPError( err = pgpGetVariableMemOutput( s->outPipe,
s->outBufMaxLength, s->outBufPtrPtr,
s->outBufUsedLength ) ) )
goto error;
} else {
/* Fixed size buffer - tell user actual size used */
pgpAssert( IsntNull( s->outBufPtr ) );
if( IsPGPError( err = pgpGetMemOutput( s->outPipe,
s->outBufMaxLength, s->outBufUsedLength ) ) )
goto error;
}
s->outPipe = NULL;
}
(*output)->teardown (*output);
*output = NULL;
if( s->pfout ) {
pgpFileClose( s->pfout );
s->pfout = NULL;
}
}
return kPGPError_NoErr;
error:
return err;
}
/* Close an existing buffer saving a key and do appropriate cleanup */
static PGPError
pgpCloseKeyOutput(
PGPDecodeJob *s
)
{
PGPOption op; /* Selected option from list */
PGPByte *bufPtr; /* Buffer pointer for key data */
PGPSize bufLength; /* Length of key buffer */
PGPKeySet *keySet; /* Keyset for key data */
PGPKeySet *importKeySet; /* Keyset we are adding to for user */
PGPError err; /* Error code */
PGPContextRef cdkContext;
pgpAssertAddrValid( s, PGPDecodeJob );
cdkContext = s->context;
keySet = NULL;
importKeySet = NULL;
bufPtr = NULL;
/* Get buffer and length where key data is */
pgpAssert( IsntNull( s->outKey ) );
if( IsPGPError( err = pgpGetVariableMemOutput( s->outKey,
(PGPSize)~0, &bufPtr, &bufLength ) ) )
goto error;
/* Close down output portion of pipeline */
s->outKey = NULL;
pgpAssert( s->fPrevOutput );
pgpAssert( IsntNull( s->prevOutput ) );
pgpAssert( IsNull( s->outPipe ) );
if( IsPGPError( err = pgpCloseOutput(s, s->prevOutput ) ) )
goto error;
/* Translate into a keyset holding any imported key(s) */
if( IsPGPError( err = pgpImportKeyBinary( s->context,
bufPtr, bufLength, &keySet ) ) )
goto error;
PGPFreeData( bufPtr );
bufPtr = NULL;
/* See if there is a keyset specified for adding keys to */
if( IsPGPError( err = pgpSearchOptionSingle( s->optionList,
kPGPOptionType_ImportKeysTo, &op ) ) )
goto error;
if( IsOp( op ) ) {
/* Add keys to user-specified keyset */
if( IsPGPError( err = pgpOptionPtr( &op, (void **)&importKeySet ) ) )
goto error;
pgpa(pgpaPGPKeySetValid(importKeySet));
if( !pgpKeySetIsValid(importKeySet) ) {
pgpDebugMsg( "Error: invalid ImportKeysTo keyset" );
err = kPGPError_BadParams;
goto error;
}
if( IsPGPError( err = PGPAddKeys( keySet, importKeySet) ) )
goto error;
if( IsPGPError( err = PGPCommitKeyRingChanges( importKeySet ) ) )
goto error;
PGPFreeKeySet( keySet );
keySet = NULL;
} else {
/* Else see if we should ask user what to do with keys */
PGPUInt32 fKeyEvents;
if( IsPGPError( err = pgpFindOptionArgs( s->optionList,
kPGPOptionType_SendEventIfKeyFound, FALSE,
"%d", &fKeyEvents ) ) )
goto error;
if( fKeyEvents ) {
err = pgpEventKeyFound( s->context, &s->newOptionList,
s->func, s->userValue, keySet );
pgpCleanupOptionList( &s->newOptionList );
if( IsPGPError( err ) )
goto error;
}
/* User has dealt with it if he wants, now we can delete data */
PGPFreeKeySet( keySet );
keySet = NULL;
}
return kPGPError_NoErr;
error:
if( IsntNull( bufPtr ) )
PGPFreeData( bufPtr );
if( IsntNull( keySet ) )
PGPFreeKeySet( keySet );
return err;
}
/*
* Use when we first open output in passthrough mode. We drain the
* contents of the passthrough fifo (which holds header data and such
* which we read before we got the begin annotation).
*/
static void
sDrainPassThroughFifo( PGPDecodeJob *s )
{
PGPByte const * ptr;
PGPSize length;
PGPError err;
pgpAssert( IsntNull( s->prevStarOutput ) );
pgpAssert( IsntNull( s->passThroughFifo ) );
ptr = pgpFifoPeek(&pgpByteFifoDesc, s->passThroughFifo, &length);
while (length != 0) {
s->prevStarOutput->write( s->prevStarOutput, ptr, length, &err );
pgpFifoSeek(&pgpByteFifoDesc, s->passThroughFifo, length);
ptr = pgpFifoPeek(&pgpByteFifoDesc, s->passThroughFifo, &length);
}
}
/*
* Output state diversion. We sometimes want to suspend output to the
* current pipeline temporarily while we deal with a key or non-pgp data.
* We save the current state in the "prev" class of variables, and later
* restore them.
*/
static void
sRestoreOutputState( PGPDecodeJob *s, PGPPipeline **output )
{
/*
* In order for our cleanup in pgpDecodeInternal to work, the output
* tail pointer from the module which calls us must always be the same.
* The following assertion helps test for that. (During pgpDecodeInternal
* we have no access to the output tail pointer, so we will assume that
* the prevOutput value matches what is needed.)
*/
pgpAssert( s->prevOutput == output );
*output = s->prevStarOutput;
s->outPipe = s->prevOutPipe;
s->pfout = s->prevPFout;
s->prevStarOutput = NULL;
s->prevOutPipe = NULL;
s->prevPFout = NULL;
s->fPrevOutput = FALSE;
}
static void
sSaveOutputState( PGPDecodeJob *s, PGPPipeline **output )
{
pgpAssert( s->prevOutput == output );
s->prevStarOutput = *output;
s->prevOutPipe = s->outPipe;
s->prevPFout = s->pfout;
s->fPrevOutput = TRUE;
s->outPipe = NULL;
s->pfout = NULL;
*output = NULL;
}
/***************** Callback functions from Aurora library *******************/
static PGPError pgpDecodeDoCommit(void *arg, PGPInt32 scope);
/*
* The low-level Aurora library calls our analyze and commit functions
* for each recursive level of handling of a given message segment.
* We only want to give one of these to the user. Accordingly we have
* three states as far as getting segment-type information:
*
* - Waiting for segment type info
* - Got segment type info, waiting to give to user
* - Gave segment info to user, waiting for end of segment
*
* We give segment info to the user from the commit call, because that
* is our opportunity to skip a section, which the user is allowed to do
* on the analyze call.
*/
static PGPError
pgpDecodeHandleAnnotation(
void *arg,
PGPPipeline *origin,
PGPInt32 type,
PGPByte const *string,
PGPSize size
)
{
PGPDecodeJob *s; /* Parameters for callbacks */
PGPUInt32 passThroughNonPGP;
PGPError err = kPGPError_NoErr;
s = (PGPDecodeJob *) arg;
(void) origin;
(void) string;
(void) size;
/* Keep track of our nesting status */
PGP_SCOPE_DEPTH_UPDATE(s->scopeLevel, type);
/* Handle passthrough data */
if (type == PGPANN_CLEARDATA) {
/* Passthrough data gets buffered or handed to suspended output */
s->passThrough = TRUE;
if( IsntNull( s->prevStarOutput ) ) {
s->prevStarOutput->write( s->prevStarOutput,
string, size, &err );
} else {
/* We get a few lines of header before the data starts,
* so we buffer that here and will drain it in
* NewOutput.
*/
pgpAssert( IsntNull( s->passThroughFifo ) );
pgpFifoWrite(&pgpByteFifoDesc, s->passThroughFifo,
string, size);
}
} else if( type == PGPANN_LITERAL_TYPE ) {
/* Remember type in case we want to recurse into it */
pgpAssert (size == 1);
s->literalType = *(PGPByte *)string;
} else if( s->analyzeState == kAnalyzeWaiting ) {
/* If looking for the start of a segment, figure out the type */
switch (type) {
case PGPANN_ARMOR_BEGIN:
/* Just get the section offset for later callback */
if( size == sizeof(s->sectOffset) )
s->sectOffset = *(PGPSize *)string;
break;
case PGPANN_PGPKEY_BEGIN:
s->analyzeType = kPGPAnalyze_Key;
s->analyzeState = kAnalyzeGotType;
s->scopeSegment = s->scopeLevel;
break;
case PGPANN_CIPHER_BEGIN:
s->analyzeType = kPGPAnalyze_Encrypted;
s->analyzeState = kAnalyzeGotType;
s->scopeSegment = s->scopeLevel;
break;
case PGPANN_COMPRESSED_BEGIN:
/* Signed may be a detached or a regular sig, need another anno */
/* case PGPANN_SIGNED_BEGIN: */
case PGPANN_SIGNED_SIG:
case PGPANN_SIGNED_SIG2:
s->analyzeType = kPGPAnalyze_Signed;
s->analyzeState = kAnalyzeGotType;
s->scopeSegment = s->scopeLevel;
break;
case PGPANN_SIGNED_SEP:
s->analyzeType = kPGPAnalyze_DetachedSignature;
s->analyzeState = kAnalyzeGotType;
s->scopeSegment = s->scopeLevel;
break;
case PGPANN_CLEARSIG_BEGIN:
/*
* Clearsig doesn't go through binary parser, so we do some
* shortcuts here. Call commit directly. We can't currently
* skip clearsigned messages though. Note commit changes
* analyzeState.
*/
s->analyzeType = kPGPAnalyze_Signed;
s->analyzeState = kAnalyzeGotType;
s->scopeSegment = s->scopeLevel;
if( size == sizeof(s->sectOffset) )
s->sectOffset = *(PGPSize *)string;
err = pgpDecodeDoCommit(arg, type);
if (err == PGPANN_PARSER_EATIT || err == PGPANN_PARSER_RECURSE)
err = kPGPError_NoErr;
break;
case PGPANN_NONPGP_BEGIN:
/*
* Non-PGP data is handled differently. Call commit directly.
* (Skip not supported though.) Note commit changes
* analyzeState.
*/
if( IsPGPError( pgpFindOptionArgs( s->optionList,
kPGPOptionType_PassThroughIfUnrecognized, FALSE,
"%d", &passThroughNonPGP ) ) )
goto error;
if( passThroughNonPGP ) {
s->analyzeType = kPGPAnalyze_Unknown;
s->analyzeState = kAnalyzeGotType;
s->scopeSegment = s->scopeLevel;
if( size == sizeof(s->sectOffset) )
s->sectOffset = *(PGPSize *)string;
err = pgpDecodeDoCommit(arg, type);
if (err == PGPANN_PARSER_EATIT || err == PGPANN_PARSER_RECURSE)
err = kPGPError_NoErr;
}
break;
}
} else if( s->analyzeState == kAnalyzeSegmentEndWait ) {
/*
* Handle end of segment.
* Special treatment for clearsig: it goes CLEARSIG_BEGIN,
* CLEARSIG_END, ARMOR_BEGIN, ARMOR_END. We skip the CLEARSIG_END
* so we terminate the scope at the ARMOR_END.
*/
if( type == PGPANN_PGPKEY_END ) {
/* On close of PGPkey segment, deal with key data */
/* May be nested within an encrypted segment so check here */
if( IsPGPError( err = pgpCloseKeyOutput( s ) ) )
goto error;
pgpAssert( s->fPrevOutput );
sRestoreOutputState( s, s->prevOutput );
}
if( s->scopeLevel < s->scopeSegment && type != PGPANN_CLEARSIG_END) {
/* Close any open output so client can see his data */
if( !s->fixedOutput && IsntNull( s->prevOutput ) ) {
/* If finished with a segment, close it */
if( IsPGPError( err = pgpCloseOutput(s, s->prevOutput )))
goto error;
}
s->passThrough = FALSE; /* End of segment */
err = pgpEventEndLex( s->context, &s->newOptionList,
s->func, s->userValue, s->sectionNumber++ );
pgpCleanupOptionList( &s->newOptionList );
s->analyzeState = kAnalyzeWaiting;
if( IsPGPError( err ) )
goto error;
/* Handle one-section mode */
if( s->sectionNumber == 1 ) {
PGPUInt32 fOnlyOne;
if( IsPGPError( err = pgpFindOptionArgs( s->optionList,
kPGPOptionType_DecodeOnlyOne, FALSE,
"%d", &fOnlyOne ) ) )
goto error;
if( fOnlyOne ) {
err = kPGPError_Interrupted;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -