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 + -
显示快捷键?