pgprngread.c
来自「著名的加密软件的应用于电子邮件中」· C语言 代码 · 共 2,259 行 · 第 1/5 页
C
2,259 行
pool->freeobjs[i] = NULL;
pool->freesets = NULL;
pool->freeiter = NULL;
memPoolEmpty(&pool->structs);
}
/* Cal the file's destructor function, if any */
if (file->destructor) {
file->destructor(file, file->f, file->arg);
file->destructor = NULL;
}
/* Final deallocation of the file */
file->flags = 0;
pool->filemask &= ~mask;
}
/*
* Check to see if an object anywhere on the list (including children,
* recursively) is included in "allocmask" but not in "filemask."
* Such an object is orphaned, an undesirable state of affairs.
* We have to check the entire keyring, recursively, because a
* given key or name might be duplicated in another keyring, but
* a signature lower down might not be.
*/
static int
ringFileCheckList(union RingObject const *obj, ringmask filemask,
ringmask allocmask)
{
while (obj) {
if (obj->g.mask & allocmask) {
/* Would closing orphan this object? */
if (!(obj->g.mask & filemask))
return 1;
/* Would closing orphan its children? */
if (!OBJISBOT(obj) && ringFileCheckList(obj->g.down,
filemask,
allocmask))
return 1;
}
obj = obj->g.next;
}
return 0;
}
/* Is it safe to close the given file? */
int
ringFileCheckClose(struct RingFile const *file)
{
struct RingPool const *pool = file->set.pool;
if (!file)
return 0;
return ringFileCheckList(pool->keys, pool->filemask & ~file->set.mask,
ringAllocMask(pool, &file->set));
}
/*
* Close the given Ringfile. Returns an error if it can't due to
* conflicts, in which case the file is NOT closed.
*/
int
ringFileClose(struct RingFile *file)
{
if (!file)
return 0; /* close(NULL) is defines as harmless */
if (ringFileCheckClose(file))
return -1;
/* Okay, nothing can fail now */
ringFileDoClose(file);
return 0;
}
/*** Routines for fetching things from the keyring ***/
/* Make sure the pool's packet buffer is large enough */
static char *
ringReserve(struct RingPool *pool, size_t len)
{
char *p;
if (pool->pktbufalloc >= len)
return pool->pktbuf;
p = (char *)pgpMemRealloc(pool->pktbuf, len);
if (!p) {
ringAllocErr(pool);
return NULL;
}
pool->pktbufalloc = len;
return pool->pktbuf = p;
}
/*
* File priorities are handled by a "higherpri" mask with each file,
* which is a mask of other files of higher priority than that file.
* If obj->g.mask & pool->filmask && file->higherpri is 0,
* this is the highest-priority file.
*/
/* Set file to the highest priority, except for the memory file */
void
ringFileHighPri(struct RingFile *file)
{
struct RingPool *pool = file->set.pool;
ringmask mask = file->set.mask;
int i;
/* Add this file to everything else's higher priority mask */
for (i = 0; i < MEMRINGBIT; i++)
pool->files[i].higherpri |= mask;
/* The only thing higher priority than this file is MEMRING */
file->higherpri = MEMRINGMASK;
}
/* Set file to the lowest priority */
void
ringFileLowPri(struct RingFile *file)
{
struct RingPool *pool = file->set.pool;
ringmask mask = ~file->set.mask;
int i;
/* Remove this file from everything else's higher priority mask */
for (i = 0; i < MEMRINGBIT; i++)
pool->files[i].higherpri &= mask;
/* Everything is higher priority than this file (except itself) */
file->higherpri = mask;
}
/* The mask of sets that this key has *any* secret components in */
static ringmask
ringKeySecMask(union RingObject const *obj)
{
ringmask secmask = 0;
pgpAssert(OBJISKEY(obj));
for (obj = obj->g.down; obj; obj = obj->g.next)
if (OBJISSEC(obj))
secmask |= obj->g.mask;
return secmask;
}
/*
* Given an object, find the best RingFile to fetch it from,
* for fetching purposes. Bits set in "avoidmask" are
* NOT valid for fetching.
*/
static struct RingFile *
ringBestFile(struct RingPool *pool, union RingObject const *obj,
ringmask avoidmask)
{
ringmask mask = obj->g.mask & pool->filemask;
struct RingFile *file;
int bit;
if (!(mask & ~avoidmask))
return NULL;
/* find highest-priority fetchable file */
for (;;) {
/* Is least-significant bit set in mask fetchable? */
if (!(mask & -mask & avoidmask)) {
bit = ringLsBitFind(mask);
file = &pool->files[bit];
/* Is it highest priority? */
if (!(mask & file->higherpri & ~avoidmask))
break;
pgpAssert(file->f);
}
mask &= mask-1;
pgpAssert(mask);
}
return file;
}
/* Macro wrapper to inline the important part */
#define ringReserve(pool, len) \
((pool)->pktbufalloc < (len) ? ringReserve(pool, len) : (pool)->pktbuf)
/*
* This is the routine which fetches a packet from a keyring file.
* It tries the highest-priority file that the object is in which is also
* listed in "avoidmask." If the memory keyring is one of those, it has
* absolute priority. (It is also not verified; it is assumed correct.)
* Otherwise, the object is fetched from the highest-priority open file.
*
* 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(struct RingPool *pool, union RingObject const *obj,
ringmask avoidmask, int pkttype, size_t maxlen, size_t *lenp,
int (*verify)(union RingObject const *, byte const *, size_t))
{
struct RingFile *file;
struct FilePos const *pos;
word32 len;
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 (file->set.mask == MEMRINGMASK) {
pgpAssert (!verify
|| verify(obj, (byte *)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 = PGPERR_KEYIO_SEEKING;
goto err;
}
i = pktByteGet(file->f, &len, (word32 *)NULL);
if (i <= 0)
goto err;
if (PKTBYTE_TYPE(i) != pkttype || len > maxlen) {
i = PGPERR_KEYIO_BADPKT;
goto err;
}
p = ringReserve(pool, (size_t)len);
if (!p)
goto errmem; /* ringErr() already called */
pool->pktbuflen = (size_t)len;
i = pgpFileRead(pool->pktbuf, (size_t)len, file->f);
if ((size_t)i != (size_t)len) {
i = pgpFileError(file->f) ? PGPERR_KEYIO_READING :
PGPERR_KEYIO_EOF;
goto err;
}
p = pool->pktbuf;
/* Okay, now verify with supplied function */
if (verify && (i = verify(obj, (byte const *)p, len)) != 0) {
if (pkttype == PKTBYTE_SECKEY
&& len
&& obj->g.flags & SECF_VERSION_BUG
&& ((byte *)p)[0] == PGPVERSION_2_6) {
/*
* 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.
*/
((byte *)p)[0] = PGPVERSION_2;
if (verify &&
(i = verify(obj, (byte const *)p, len)) != 0)
goto err;
((byte *)p)[0] = PGPVERSION_2_6;
} else {
goto err;
}
}
/* Success! */
*lenp = len;
return p;
/* Error cases (out of line) */
err:
ringErr(file, pos->fpos, 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(union RingObject const *obj, byte const *p, size_t len)
{
int i;
byte pkalg, keyID[8];
word16 keybits, validity;
word32 tstamp;
i = ringKeyParse(p, len, &pkalg, keyID, &keybits,
&tstamp, &validity, 0);
if (memcmp(keyID, obj->k.keyID, 8) == 0
&& keybits == obj->k.keybits
&& tstamp == obj->k.tstamp
/* && validity == obj->k.validity Validity sometimes stored elsewhere */
&& pkalg == obj->k.pkalg
&& (i == 0) == !(obj->g.flags & KEYF_ERROR))
return 0; /* All copascetic */
return PGPERR_KEYIO_BADPKT;
}
/* Verify that the secret we just read looks like the one we wanted to read. */
static int
ringSecVerify(union RingObject const *obj, byte const *p, size_t len)
{
int i;
byte pkalg, keyID[8];
word16 keybits, validity;
word32 tstamp;
union RingObject *key = obj->g.up;
pgpAssert(OBJISSEC(obj));
pgpAssert(OBJISKEY(key));
i = ringKeyParse(p, len, &pkalg, keyID, &keybits,
&tstamp, &validity, 1);
if (ringHashBuf(p, len) == obj->c.hash
&& memcmp(keyID, key->k.keyID, 8) == 0
&& keybits == key->k.keybits
&& tstamp == key->k.tstamp
/* && validity == key->k.validity Validity sometimes stored elsewhere */
&& pkalg == key->k.pkalg
&& (i == 0) == !(key->g.flags & KEYF_ERROR))
return 0; /* All copascetic */
return PGPERR_KEYIO_BADPKT;
}
/*
* Verify that the signature we just read looks like the one we wanted to read.
*/
static int
ringSigVerify(union RingObject const *obj, byte const *p, size_t len)
{
int i;
byte pkalg, keyID[8];
word32 tstamp;
word16 validity;
size_t extralen;
byte type, hashalg;
byte version;
i = ringSigParse(p, len, &pkalg, keyID, &tstamp, &validity,
&type, &hashalg, &extralen, &version);
if (memcmp(keyID, obj->s.by->k.keyID, 8) == 0
&& version == obj->s.version
&& tstamp == obj->s.tstamp
&& validity == obj->s.validity
&& type == obj->s.type
&& hashalg == obj->s.hashalg
&& (extralen == 5) == !(obj->g.flags & SIGF_NONFIVE)
&& (i == 0) == !(obj->g.flags & SIGF_ERROR))
return 0; /* All copascetic */
return PGPERR_KEYIO_BADPKT;
}
/*
* Verify that the unknown we just read looks like the one we wanted to read.
*/
static int
ringUnkVerify(union RingObject const *obj, byte const *p, size_t len)
{
if (ringHashBuf(p, len) == obj->u.hash)
return 0; /* All copascetic */
return PGPERR_KEYIO_BADPKT;
}
/*
* 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 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(struct RingPool *pool, struct RingName *name, size_t *lenp)
{
int i;
struct MemPool cut;
ringmask mask;
struct RingFile *file, *file2;
struct FilePos const *pos;
char *str;
word32 len;
*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;
/* find highest-priority fetchable file */
file = ringBestFile(pool, (union RingObject *)name, 0);
pgpAssert(file);
pgpAssert(file->f || file->set.mask == 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 (file->set.mask == MEMRINGMASK) {
pgpAssert(pos->fpos == name->len);
pgpAssert(ringHashBuf((byte *)pos->ptr.buf, name->len) ==
name->name.hash);
NAMESETCACHED(name);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?