📄 pgprngread.c
字号:
* The files are assigned priorities for fetching. The default is
* that the first opened file is highest and subsequent files are of
* lower priority. This is done by having each Ringfile keep a mask
* of higher-priority RingFiles. We walk along the list until we hit
* a RingFile whose higher-priority mask doesn't include any files that
* the object being sought is in.
*
* The packet fetched must pass the following validity checks:
* - It must be of the given packet type.
* - It must be no longer than "maxlen".
* - If those pass, it must be read into memory successfully.
* - It must then pass the caller-supplied "verify" function,
* which checks object-type-specific information against the
* summary information stored in the RingObject.
*
* Question: what to return when the avoidmask doesn't allow anything to
* be fetched? Is this case just an error?
*/
static void const *
ringFetchPacket(RingPool *pool, union RingObject const *obj,
PGPVirtMask *avoidmask, int pkttype, PGPSize maxlen, PGPSize *lenp,
int (*verify)(RingPool *pool, union RingObject const *, PGPByte const *,
size_t))
{
RingFile *file;
FilePos const *pos;
PGPSize len, len1;
int i;
void *p;
/* find highest-priority fetchable file */
file = ringBestFile(pool, obj, avoidmask);
if (!file) {
*lenp = (size_t)0;
return NULL; /* Is this The Right Thing? */
}
pos = ringFilePos(obj, file);
pgpAssert(pos);
/* If it's in memory, that was easy... */
if (pgpVirtMaskIsEqual (&file->set.mask, &pool->memringmask)) {
pgpAssert (!verify
|| verify(pool, obj, (PGPByte *)pos->ptr.buf, pos->fpos) == 0);
*lenp = pos->fpos;
return pos->ptr.buf;
}
/* We now have highest-priority fetchable file */
pgpAssert(file->f);
if (pgpFileSeek(file->f, pos->fpos, SEEK_SET) != 0) {
i = kPGPError_FileOpFailed;
goto err;
}
i = pktByteGet(file->f, &len, &len1, (PGPSize *)NULL);
if (i <= 0)
goto err;
if (PKTBYTE_TYPE(i) != pkttype || len > maxlen) {
i = kPGPError_BadPacket;
goto err;
}
p = ringReserve(pool, (size_t)len);
if (!p)
goto errmem; /* ringErr() already called */
pool->pktbuflen = len;
i = pktBodyRead(pool->pktbuf, len, len1, file->f);
if ((size_t)i != (size_t)len) {
i = pgpFileError(file->f) ? kPGPError_ReadFailed :
kPGPError_EOF;
goto err;
}
p = pool->pktbuf;
/* Okay, now verify with supplied function */
if (verify && (i = verify(pool, obj, (PGPByte const *)p, len)) != 0) {
if (pkttype == PKTBYTE_SECKEY
&& len
&& obj->g.flags & SECF_VERSION_BUG
&& ((PGPByte *)p)[0] == PGPVERSION_3) {
/*
* Failure may be due to version bug; fix and try
* again. If success, put it back so we behave
* consistently. Verify doesn't always fail with
* version bug, it depends on whether sec or pub was
* seen first.
*/
((PGPByte *)p)[0] = PGPVERSION_2;
if (verify &&
(i = verify(pool, obj, (PGPByte const *)p, len)) != 0)
goto err;
((PGPByte *)p)[0] = PGPVERSION_3;
} else {
goto err;
}
}
/* Success! */
*lenp = len;
return p;
/* Error cases (out of line) */
err:
ringErr(file, pos->fpos, (PGPError)i);
errmem:
*lenp = 0;
return NULL;
}
/*
* The verify functions here should *never* fail under normal
* conditions. They fail only if the keyring file has been
* changed while PGP is accessing it (which causes a fatal error).
* Did I mention that this code is *paranoid*?
* ("The computer is your friend. The computer wants you to be happy.")
*
* They operate by re-parsing the fetched data and checking that
* the cached data matches the data just fetched.
*/
/* Verify that the key we just read looks like the one we wanted to read. */
static int
ringKeyVerify(RingPool *pool, union RingObject const *obj,
PGPByte const *p, size_t len)
{
int i;
PGPByte pkalg, keyID[8], fp20n[20];
PGPUInt16 keybits, validity;
PGPUInt32 tstamp;
PGPByte v3;
i = ringKeyParse(pool->context, p, len, &pkalg, keyID, fp20n, &keybits,
&tstamp, &validity, &v3, 0);
if (memcmp(keyID, obj->k.keyID, 8) == 0
&& fp20n[0] == obj->k.fp20n
&& keybits == obj->k.keybits
&& tstamp == obj->k.tstamp
/* && validity == obj->k.validity Validity sometimes stored elsewhere */
&& pkalg == obj->k.pkalg
&& v3 == !!KEYISV3(&obj->k)
&& (i == 0) == !(obj->g.flags & KEYF_ERROR))
return 0; /* All copascetic */
return kPGPError_BadPacket;
}
/* Verify that the secret we just read looks like the one we wanted to read. */
static int
ringSecVerify(RingPool *pool, union RingObject const *obj,
PGPByte const *p, size_t len)
{
int i;
PGPByte pkalg, keyID[8], fp20n[20];
PGPUInt16 keybits, validity;
PGPUInt32 tstamp;
PGPByte v3;
union RingObject *key = obj->g.up;
pgpAssert(OBJISSEC(obj));
pgpAssert(OBJISKEY(key));
i = ringKeyParse(pool->context, p, len, &pkalg, keyID, fp20n, &keybits,
&tstamp, &validity, &v3, 1);
if (ringHashBuf(p, len) == obj->c.hash
&& memcmp(keyID, key->k.keyID, 8) == 0
&& fp20n[0] == key->k.fp20n
&& keybits == key->k.keybits
&& tstamp == key->k.tstamp
/* && validity == key->k.validity Validity sometimes stored elsewhere */
&& pkalg == key->k.pkalg
&& v3 == !!KEYISV3(&key->k)
&& (i == 0) == !(key->g.flags & KEYF_ERROR))
return 0; /* All copascetic */
return kPGPError_BadPacket;
}
/*
* Verify that the signature we just read looks like the one we wanted to read.
*/
static int
ringSigVerify(RingPool *pool, union RingObject const *obj,
PGPByte const *p, size_t len)
{
int i;
PGPByte pkalg, keyID[8];
PGPUInt32 tstamp;
PGPUInt32 sigvalidity;
size_t extralen;
PGPByte type, hashalg;
PGPByte version;
PGPBoolean exportable;
PGPBoolean revocable;
PGPBoolean hasRegExp;
PGPBoolean isX509;
PGPBoolean primaryUID;
PGPByte trustLevel;
PGPByte trustValue;
(void) pool;
i = ringSigParse(p, len, &pkalg, keyID, &tstamp, &sigvalidity,
&type, &hashalg, &extralen, &version, &exportable,
&revocable, &trustLevel, &trustValue, &hasRegExp,
&isX509, &primaryUID);
/* Allow mismatch on keyid on X509 sigs */
if ((isX509 || memcmp(keyID, obj->s.by->k.keyID, 8) == 0)
&& version == obj->s.version
&& tstamp == obj->s.tstamp
&& sigvalidity == obj->s.sigvalidity
&& type == obj->s.type
&& hashalg == obj->s.hashalg
&& (extralen == 5) == !(obj->g.flags & SIGF_NONFIVE)
&& (i == 0) == !(obj->g.flags & SIGF_ERROR)
&& (!exportable == !SIGISEXPORTABLE(&obj->s))
&& (!revocable == !SIGISREVOCABLE(&obj->s))
&& (!hasRegExp == !SIGUSESREGEXP(&obj->s))
&& (!isX509 == !SIGISX509(&obj->s))
&& (!primaryUID == !SIGISPRIMARYUID(&obj->s)))
return 0; /* All copascetic */
return kPGPError_BadPacket;
}
/*
* Verify that the CRL we just read looks like the one we wanted to read.
*/
static int
ringCRLVerify(RingPool *pool, union RingObject const *obj,
PGPByte const *p, size_t len)
{
PGPByte version;
PGPByte type;
PGPUInt32 tstamp;
PGPUInt32 tstampnext;
PGPByte const *dpoint;
PGPSize dpointlen;
(void) pool;
(void)ringCRLParse(pool, p, len, &version, &type, &tstamp, &tstampnext,
&dpoint, &dpointlen);
if (ringHashBuf(p, len) == obj->r.hash
&& version == obj->r.version
&& (type == PGPCRLTYPE_X509 ||
type == PGPCRLTYPE_X509DPOINT) == CRLISX509(&obj->r)
&& (type == PGPCRLTYPE_X509DPOINT) == CRLHASDPOINT(&obj->r)
&& (!CRLHASDPOINT(&obj->r) ||
ringHashBuf(dpoint, dpointlen) == obj->r.dpointhash)
&& tstamp == obj->r.tstamp
&& tstampnext == obj->r.tstampnext
)
return 0; /* All copascetic */
return kPGPError_BadPacket;
}
/*
* Verify that the unknown we just read looks like the one we wanted to read.
*/
static int
ringUnkVerify(RingPool *pool, union RingObject const *obj,
PGPByte const *p, size_t len)
{
(void) pool;
if (ringHashBuf(p, len) == obj->u.hash)
return 0; /* All copascetic */
return kPGPError_BadPacket;
}
/*
* Getting names is special due to the in-memory cache.
* We don't have to support "avoidmask", though.
*
* Return a pointer to a name string. Note that the string is NOT
* null-terminated; all 256 values are legal! The "lenp" argument
* returns the length. Tries to get it from memory if possible,
* then tries to load it into the cache. If it can't load it into
* the preferred file cache, load it into the pktbuf. If even
* that fails, try to find another cache that it wil fit into.
*
* (Note: although strings are not in general null-terminated, hence we
* return a length field, we do in fact put a null character at name[length]
* so that callers can be sure that there is a null either within or just
* beyond the name itself. This facilitates our regexp matching.)
*
* Note that ringNamesDiffer() uses the fact that this either returns
* with NAMEISCACHED(name) true, *or* the buffer returned is the
* pktbuf. Never both, and never a third choice.
*
* @@@ Is the ability to hande a pool==NULL still useful?
*/
static char const *
ringPoolGetName(RingPool *pool, RingName *name, PGPSize *lenp)
{
int i;
MemPool cut;
PGPVirtMask mask;
RingFile *file, *file2;
FilePos const *pos;
char *str;
PGPSize len, len1;
*lenp = name->len;
/* If we already have it, boom. */
if (NAMEISCACHED(name))
return name->name.ptr;
/* If we weren't given a pool, we can't fetch it */
if (!pool)
return NULL;
pgpVirtMaskInit (pool, &mask);
/* find highest-priority fetchable file */
file = ringBestFile(pool, (union RingObject *)name, 0);
pgpAssert(file);
pgpAssert(file->f ||
pgpVirtMaskIsEqual (&file->set.mask, &pool->memringmask));
pos = ringFilePos((union RingObject *)name, file);
pgpAssert(pos);
/* If it's in memory, that was fast - set cached if it wasn't already*/
if (pgpVirtMaskIsEqual (&file->set.mask, &pool->memringmask)) {
pgpAssert(pos->fpos == name->len);
pgpAssert(ringHashBuf((PGPByte *)pos->ptr.buf, name->len) ==
name->name.hash);
NAMESETCACHED(name);
NAMESETFILEMASK(name, MEMRINGBIT);
pgpVirtMaskCleanup (pool, &mask);
return name->name.ptr = (char *)pos->ptr.buf;
}
/* Allocate cache space for it */
/* Dummy loop to break out of */
do {
/* Try the preferred cache */
file2 = file;
cut = file2->strings; /* Remember cutback position */
str = (char *)memPoolAlloc(&file2->strings, name->len + 1, 1);
if (str)
break;
/* Try the pktbuf */
str = ringReserve(pool, name->len + 1);
if (str) {
file2 = NULL;
pool->pktbuflen = name->len;
break;
}
/* Okay, desperation time - look for any cache */
pgpVirtMaskCopy (pool, &name->mask, &mask);
pgpVirtMaskAND (pool, &pool->filemask, &mask);
pgpVirtMaskANDNOT (pool, &pool->memringmask, &mask);
pos = &name->pos;
for (;;) {
i = pgpVirtMaskLSBit(&mask);
pgpAssert(i >= 0);
pgpAssert(pool->files[i]->f);
file2 = pool->files[i];
cut = file2->strings;
str = (char *)memPoolAlloc(&file2->strings,
name->len + 1, 1);
if (str)
break;
pgpVirtMaskClearBit (pool, &mask, i);
if (pgpVirtMaskIsEmpty (&mask)) {
ringAllocErr(pool);
pgpVirtMaskCleanup (pool, &mask);
return NULL;
}
pos = pos->ptr.next;
}
} while (0); /* Dummy loop to break out of */
/* Okay, we got buffer space... get the packet */
if (pgpFileSeek(file->f, pos->fpos, SEEK_SET) != 0) {
i = kPGPError_FileOpFailed;
goto error;
}
i = pktByteGet(file->f, &len, &len1, (PGPSize *)NULL);
if (i < 0)
goto error;
if (PKTBYTE_TYPE(i) !=
(NAMEISATTR(name) ? PKTBYTE_ATTRIBUTE : PKTBYTE_NAME)
|| len != name->len)
goto badpkt;
i = pktBodyRead(str, len, len1, file->f);
str[len] = '\0'; /* null char beyond end of name */
if ((size_t)i != (size_t)len) {
i = pgpFileError(file->f) ? kPGPError_ReadFailed :
kPGPError_EOF;
goto error;
}
/* Double-check that we got the right thing. */
if (ringHashBuf((PGPByte *)str, len) != name->name.hash)
goto badpkt;
/* Success at last! */
if (file2) {
/* It's cached in file2 - set flags appropriately */
NAMESETCACHED(name);
NAMESETFILEMASK(name, pgpVirtMaskLSBit(&file2->set.mask));
name->name.ptr = str;
}
pgpVirtMaskCleanup (pool, &mask);
return str;
badpkt:
i = kPGPError_BadPacket;
error:
if (file2)
memPoolCutBack(&file2->strings, &cut);
ringErr(file, pos->fpos, (PGPError)i);
pgpVirtMaskCleanup (pool, &mask);
return NULL;
}
void const *
ringFetchObject(RingSet const *set, union RingObject *obj, PGPSize *lenp)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -