📄 pgprngread.c
字号:
ringFreeTree(pool, down);
}
}
ringFreeObject(pool, obj);
}
/*
* Free up a newly created dummy key.
* Unlink it from the pool and free it and all descendents.
*/
static void
ringFreeDummyKey(RingPool *pool, union RingObject *key)
{
union RingObject **objp;
pgpAssert(OBJISKEY(key));
/* Find head of list this object is on */
if (OBJISTOP(key))
objp = &pool->keys;
else
objp = &key->g.up->g.down;
while (*objp != key) {
pgpAssert(*objp);
objp = &(*objp)->g.next;
}
*objp = key->g.next;
ringFreeTree(pool, key);
}
/*
* Return 0 if the packet in the pktbuf is the same as the packet in
* the given file at the given offset, and the file packet is of type
* pkttype. Returns 1 if they differ, and -1 (and sets the ring's error
* status) if there is an error, including an unexpected packet byte.
* Compare at most max bytes.
*
* This does NOT examine any more of the object than its filepos
* chain; in particular, it does NOT examine the object's type.
* Thus, it is possible to have a key object and use it to
* fetch a secret-key packet.
*
* Special case: returns pktbuf[0] if pktbuf[0] is 2 or 3 and the file's
* packet begins with 5-pktbuf[0]. This is used by the key difference
* code to detect the version byte bug. The other things can ignore it,
* and just treat all positive return values as "different".
*/
static int
ringPacketDiffers(RingFile *file, union RingObject const *obj,
int pkttype, PGPUInt32 max)
{
RingPool *pool = file->set.pool;
FilePos const *pos;
PGPByte *p;
PGPFile *f;
PGPSize len, len1;
int i;
PGPByte c;
int magic;
pos = ringFilePos(obj, file);
/* Memory file, special case for comparison */
if (pgpVirtMaskIsEqual (&file->set.mask, &pool->memringmask)) {
len = pos->fpos;
if (len > max)
len = max;
if (max > pool->pktbuflen)
max = pool->pktbuflen;
if (len != max)
return 1; /* Different */
if (!len)
return 0;
/* Check first character specially */
p = (PGPByte *)pos->ptr.buf;
magic = 0;
if (p[0] != ((PGPByte *)pool->pktbuf)[0]) {
if ((p[0] ^ ((PGPByte *)pool->pktbuf)[0]) != 1
|| (p[0] & ((PGPByte *)pool->pktbuf)[0]) != 2)
{
return 1; /* First char different */
}
magic = ((PGPByte *)pool->pktbuf)[0]; /* First char magic */
}
return memcmp(p+1, pool->pktbuf+1, (size_t)len-1) ? 1 : magic;
}
/* Usual case - external file */
f = file->f;
pgpAssert(f);
i = pgpFileSeek(f, pos->fpos, SEEK_SET);
if (i != 0) {
ringErr(file, pos->fpos, kPGPError_FileOpFailed);
return kPGPError_FileOpFailed;
}
i = pktByteGet(f, &len, &len1, (PGPSize *)NULL);
if (i < 0) {
ringErr(file, pos->fpos, (PGPError)i);
return i;
}
if (PKTBYTE_TYPE(i) != pkttype) {
ringErr(file, pos->fpos, kPGPError_BadPacket);
return kPGPError_BadPacket;
}
if (len > max)
len = max;
if (max > pool->pktbuflen)
max = pool->pktbuflen;
if (len != max)
return 1; /* Different */
if (!len)
return 0;
/* Check first character specially */
if (pgpFileRead(&c, 1, f) != 1) {
i = pgpFileError(f) ? kPGPError_ReadFailed : kPGPError_EOF;
ringErr(file, pos->fpos, (PGPError)i);
return i;
}
i = c & 255;
magic = 0; /* First char the same */
if (i != ((PGPByte *)pool->pktbuf)[0]) {
if ((i ^ ((PGPByte *)pool->pktbuf)[0]) != 1
|| (i & ((PGPByte *)pool->pktbuf)[0]) != 2)
return 1;
magic = ((PGPByte *)pool->pktbuf)[0]; /* First char magic */
}
i = fileUnequalBuf(f, pool->pktbuf+1, len-1, len1-1);
if (i < 0)
ringErr(file, pos->fpos, (PGPError)i);
return i ? i : magic;
}
/*
* Return 1 if the key in the ring's pktbuf differs from the
* key in its other homes, 0 if it is the same, and <0 if there
* is some sort of error.
* Does *not* alter file1's read position unless there is an error.
* (I.e. if it needs to seek file1, it saves and restores the file
* position. It doesn't bother for other files. Use the MEMRING file
* if you don't need this feature.)
*
* SPECIAL CASE:
* Returns >1 if the version byte bug was detected. This is the case
* wherein version 2.6 would write out an edited secret key with a
* version byte of 3 even if it was originally 2.
* This function returns the current pktbuf's version byte (2 or 3)
* if the keys are identical except that the version byte differs
* from something read previously.
* The caller must decide what sort of trouble to log.
*/
int
keysdiffer(RingFile *file1, union RingObject const *key, int pktbyte)
{
RingPool *pool = file1->set.pool;
RingFile *file2;
size_t savelen, publen;
PGPVirtMask secmask;
PGPUInt32 max;
int i, type;
long retpos=0;
pgpVirtMaskInit (pool, &secmask);
pgpAssert(OBJISKEY(key));
pgpAssert(file1->f ||
pgpVirtMaskIsEqual(&file1->set.mask, &pool->memringmask));
/*
* If this is a secret key, find the prefix which is a public key,
* and limit it to that if reasonable.
*/
savelen = pool->pktbuflen;
if ((PKTBYTE_TYPE(pktbyte) == PKTBYTE_SECKEY ||
PKTBYTE_TYPE(pktbyte) == PKTBYTE_SECSUBKEY)
&& !(key->g.flags & KEYF_ERROR))
{
publen = ringKeyParsePublicPrefix((PGPByte const *)pool->pktbuf,
pool->pktbuflen);
if (publen)
pool->pktbuflen = publen;
}
/* Find a file containing the key - try for public first */
ringKeySecMask(pool, key, &secmask);
file2 = ringBestFile(pool, key, &secmask);
if (file2) {
max = (PGPUInt32)-1;
type = OBJISTOPKEY(key) ? PKTBYTE_PUBKEY : PKTBYTE_PUBSUBKEY;
} else {
file2 = ringBestFile(pool, key, 0);
pgpAssert(file2);
max = (PGPUInt32)pool->pktbuflen;
type = OBJISTOPKEY(key) ? PKTBYTE_SECKEY : PKTBYTE_SECSUBKEY;
}
if (file2 == file1 && file1->f && (retpos=pgpFileTell(file1->f)) < 0) {
ringErr(file1, pgpFileTell(file1->f), kPGPError_FileOpFailed);
pgpVirtMaskCleanup (pool, &secmask);
return kPGPError_FileOpFailed;
}
/* Note that we don't need to seek file2 here, ringPacketDiffers does */
i = ringPacketDiffers(file2, key, type, max);
/*
* If we compared against a secret key, and encountered the version
* bug, and the version bug has already been noted, ignore the
* difference.
*/
if (i == PGPVERSION_2
&& type == PKTBYTE_SECKEY
&& key->g.flags & SECF_VERSION_BUG)
i = 0;
if (file2 == file1 && file1->f &&
pgpFileSeek(file1->f, retpos, SEEK_SET) != 0) {
ringErr(file1, pgpFileTell(file1->f), kPGPError_FileOpFailed);
pgpVirtMaskCleanup (pool, &secmask);
return kPGPError_FileOpFailed;
}
/* Restore pktbuflen, may have been changed above */
pool->pktbuflen = savelen;
pgpVirtMaskCleanup (pool, &secmask);
return i;
}
/*
* Return 1 if the secret in the ring's pktbuf differs from the
* key in its other homes, 0 if it is the same, and <0 if there
* is some sort of error.
* Does *not* alter file1's read position unless there is an error.
*
* SPECIAL CASE:
* Returns >1 if the version byte bug was detected. This is the case
* wherein version 2.6 would write out an edited secret key with a
* version byte of 3 even if it was originally 2.
* This function returns the current pktbuf's version byte (2 or 3)
* if the keys are identical except that the version byte differs
* from something read previously.
* The caller must decide what sort of trouble to log.
*/
/*
* Return 1 if the signature in the ring's pktbuf differs from the
* sig in its various homes, 0 if it is the same, and <0 if there
* is some sort of error.
* Does *not* alter file1's read position unless there is an error.
* (I.e. if it needs to seek file1, it saves and restores the file
* position. It doesn't bother for other files. Use the MEMRING file
* if you don't need this feature.)
*/
static int
secsdiffer(RingFile *file1, union RingObject const *sec)
{
RingPool *pool = file1->set.pool;
RingFile *file2;
long retpos=0;
PGPByte pktbyte;
int i;
pgpAssert(OBJISSEC(sec));
/* Find a matching signature */
file2 = ringBestFile(pool, sec, 0);
pgpAssert (file2);
pgpAssert (file2->f ||
pgpVirtMaskIsEqual (&file2->set.mask, &pool->memringmask));
if (file2 == file1 && file1->f && (retpos=pgpFileTell(file1->f)) < 0) {
ringErr(file1, pgpFileTell(file1->f), kPGPError_FileOpFailed);
return kPGPError_FileOpFailed;
}
pktbyte = OBJISTOPKEY(sec->g.up) ? PKTBYTE_SECKEY :
PKTBYTE_SECSUBKEY;
/* Note that we don't need to seek file2 here, ringPacketDiffers does */
i = ringPacketDiffers(file2, sec, pktbyte, (PGPUInt32)-1);
if (i < 0)
return i;
if (file2 == file1 && file1->f &&
pgpFileSeek(file1->f, retpos, SEEK_SET) != 0) {
ringErr(file1, sec->g.pos.fpos, kPGPError_FileOpFailed);
return kPGPError_FileOpFailed;
}
return i;
}
static union RingObject *
ringFindSec(RingFile *file, union RingObject *parent)
{
RingPool *pool = file->set.pool;
union RingObject *sec, **secp;
int i;
/* Properties of the secret */
PGPUInt32 hash;
hash = ringHashBuf((PGPByte const *)pool->pktbuf, pool->pktbuflen);
/* Search for matching sigs */
for (secp=&parent->g.down; (sec=*secp) != NULL; secp=&sec->g.next) {
if (OBJISSEC(sec)
&& sec->c.hash == hash
&& (i = secsdiffer(file, sec)) <= 0)
return i<0 ? NULL : sec;
}
/* Not found - allocate a new secret */
sec = ringNewSec(pool);
if (sec) {
/*
* Make secret object the first thing. This is assumed by
* ringFindSig which tries to keep sigs together just past
* the sec obj. Especially important with subkeys where it's
* hard to tell which sigs go with which keys as we read.
*/
sec->g.next = parent->g.down;
parent->g.down = sec;
sec->g.up = parent;
sec->c.hash = hash;
}
return sec;
}
/*
* Return 1 if the name in the ring's pktbuf differs from the
* name in its various homes, 0 if it is the same, and <0 if there
* is some sort of error.
* Does *not* alter file1's read position unless there is an error.
* (I.e. if it needs to seek file1, it saves and restores the file
* position. It doesn't bother for other files. Use the MEMRING file
* if you don't need this feature.)
*/
int
namesdiffer(RingFile *file1, union RingObject const *name,
PGPBoolean fAttribute)
{
RingPool *pool = file1->set.pool;
RingFile *file2;
long retpos=0;
int i;
/* Find a matching name */
file2 = ringBestFile(pool, name, 0);
pgpAssert (file2);
pgpAssert (file2->f ||
pgpVirtMaskIsEqual (&file2->set.mask, &pool->memringmask));
if (file2 == file1 && file1->f && (retpos=pgpFileTell(file1->f)) < 0) {
ringErr(file1, pgpFileTell(file1->f), kPGPError_FileOpFailed);
return kPGPError_FileOpFailed;
}
/* Note that we don't need to seek file2 here, ringPacketDiffers does */
i = ringPacketDiffers(file2, name,
(fAttribute?PKTBYTE_ATTRIBUTE:PKTBYTE_NAME), (PGPUInt32)-1);
if (file2 == file1 && file1->f &&
pgpFileSeek(file1->f, retpos, SEEK_SET) != 0) {
ringErr(file1, name->g.pos.fpos, kPGPError_FileOpFailed);
return kPGPError_FileOpFailed;
}
return i;
}
/*
* Exported variant of namesdiffer. This has a similar function, but
* a very different interface - it does not assume that the name has been
* fetched into the pktbuf.
* Returns 1 if the two names differ, 0 if the same, < 0 on error.
*/
int
ringNamesDiffer (RingSet const *set, union RingObject *name1,
union RingObject *name2)
{
char const *buf1, *buf2; /* Pointers to the names */
PGPSize len, tlen;
PGPUInt32 hash1, hash2; /* HAshes of the two names */
pgpAssert (OBJISNAME(name1));
pgpAssert (OBJISNAME(name2));
pgpAssert (pgpIsRingSetMember(set, name1));
pgpAssert (pgpIsRingSetMember(set, name2));
/* Quick test of lengths */
len = name1->n.len;
if (len != name2->n.len)
return 1;
/* Trivial case: both names are in memory */
if (NAMEISCACHED(&name1->n) && NAMEISCACHED(&name2->n))
return memcmp(name1->n.name.ptr, name2->n.name.ptr, len) != 0;
/* First, compare hashes to see what's what */
hash1 = NAMEISCACHED(&name1->n)
? ringHashBuf((const unsigned char *) name1->n.name.ptr, len)
: name1->n.name.hash;
hash2 = NAMEISCACHED(&name2->n)
? ringHashBuf((const unsigned char *) name2->n.name.ptr, len)
: name2->n.name.hash;
if (hash1 != hash2)
return 1;
/*
* At this point, we're 99% sure the names are the same, but
* we need to confirm...
* Load the first name. This may go into a cache, or the pktbuf.
*/
buf1 = ringPoolGetName(ringSetPool(set), &name1->n, &tlen);
if (!buf1)
return ringSetError(set)->error;
pgpAssert(tlen == len);
/* If name2 is available without using the pktbuf, great. */
if (NAMEISCACHED(&name2->n)) {
buf2 = name2->n.name.ptr;
return memcmp(buf1, buf2, len) != 0;
}
/*
* Otherwise, if name1 isn't in the pktbuf, we can fetch name2
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -