pgprngread.c

来自「著名的加密软件的应用于电子邮件中」· C语言 代码 · 共 2,259 行 · 第 1/5 页

C
2,259
字号
	if (pgpFileRead(&c, 1, f) != 1) {
		i = pgpFileError(f) ? PGPERR_KEYIO_READING : PGPERR_KEYIO_EOF;
		ringErr(file, pos->fpos, i);
		return i;
	}
	i = c & 255;
	magic = 0;	/* First char the same */
	if (i != ((byte *)pool->pktbuf)[0]) {
		if ((i ^ ((byte *)pool->pktbuf)[0]) != 1
			|| (i & ((byte *)pool->pktbuf)[0]) != 2)
			return 1;
		magic = ((byte *)pool->pktbuf)[0]; /* First char magic */
	}
	i = fileUnequalBuf(f, pool->pktbuf+1, (size_t)len-1);
	if (i < 0)
		ringErr(file, pos->fpos, 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.
 */
static int
keysdiffer(struct RingFile *file1, union RingObject const *key, int pktbyte)
{
	struct RingPool *pool = file1->set.pool;
	struct RingFile *file2;
	size_t savelen, publen;
	ringmask secmask;
	word32 max;
	int i, type;
	long retpos=0;

	pgpAssert(OBJISKEY(key));
	pgpAssert(file1->f || file1->set.mask == 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((byte const *)pool->pktbuf,
		pool->pktbuflen);
		if (publen)
			pool->pktbuflen = publen;
	}

	/* Find a file containing the key - try for public first */
	secmask = ringKeySecMask(key);

	file2 = ringBestFile(pool, key, secmask);
	if (file2) {
		max = (word32)-1;
		type = OBJISTOPKEY(key) ? PKTBYTE_PUBKEY : PKTBYTE_PUBSUBKEY;
	} else {
		file2 = ringBestFile(pool, key, 0);
		pgpAssert(file2);
		max = (word32)pool->pktbuflen;
		type = OBJISTOPKEY(key) ? PKTBYTE_SECKEY : PKTBYTE_SECSUBKEY;
	}

	if (file2 == file1 && file1->f && (retpos=pgpFileTell(file1->f)) < 0) {
		ringErr(file1, pgpFileTell(file1->f), PGPERR_KEYIO_FTELL);
		return PGPERR_KEYIO_FTELL;
	}
		
	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), PGPERR_KEYIO_SEEKING);
		return PGPERR_KEYIO_SEEKING;
	}
	
	/* Restore pktbuflen, may have been changed above */
	pool->pktbuflen = savelen;

	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(struct RingFile *file1, union RingObject const *sec)
{
	struct RingPool *pool = file1->set.pool;
	struct RingFile *file2;
	long retpos=0;
	byte pktbyte;
	int i;

	pgpAssert(OBJISSEC(sec));

	/* Find a matching signature */
	file2 = ringBestFile(pool, sec, 0);
	pgpAssert(file2);
	pgpAssert(file2->f || file2->set.mask == MEMRINGMASK);
	if (file2 == file1 && file1->f && (retpos=pgpFileTell(file1->f)) < 0) {
		ringErr(file1, pgpFileTell(file1->f), PGPERR_KEYIO_FTELL);
		return PGPERR_KEYIO_FTELL;
	}

	pktbyte = OBJISTOPKEY(sec->g.up) ? PKTBYTE_SECKEY :
	PKTBYTE_SECSUBKEY;
	i = ringPacketDiffers(file2, sec, pktbyte, (word32)-1);
	if (i < 0)
		return i;

	if (file2 == file1 && file1->f &&
			pgpFileSeek(file1->f, retpos, SEEK_SET) != 0) {
		ringErr(file1, sec->g.pos.fpos, PGPERR_KEYIO_SEEKING);
		return PGPERR_KEYIO_SEEKING;
	}
	return i;
}

static union RingObject *
ringFindSec(struct RingFile *file, union RingObject *parent)
{
	struct RingPool *pool = file->set.pool;
	union RingObject *sec, **secp;
	int i;

	/* Properties of the secret */
	word32 hash;

	hash = ringHashBuf((byte 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.)
*/
static int
namesdiffer(struct RingFile *file1, union RingObject const *name)
{
	struct RingPool *pool = file1->set.pool;
	struct RingFile *file2;
	long retpos=0;
	int i;

	/* Find a matching name */
	file2 = ringBestFile(pool, name, 0);
	pgpAssert (file2->f || file2->set.mask == MEMRINGMASK);
	if (file2 == file1 && file1->f && (retpos=pgpFileTell(file1->f)) < 0) {
		ringErr(file1, pgpFileTell(file1->f), PGPERR_KEYIO_FTELL);
		return PGPERR_KEYIO_FTELL;
	}

	if (file2->f &&
	pgpFileSeek(file2->f, name->g.pos.fpos, SEEK_SET) != 0) {
		ringErr(file2, name->g.pos.fpos, PGPERR_KEYIO_SEEKING);
		return PGPERR_KEYIO_SEEKING;
	}
	i = ringPacketDiffers(file2, name, PKTBYTE_NAME, (word32)-1);

	if (file2 == file1 && file1->f &&
			pgpFileSeek(file1->f, retpos, SEEK_SET) != 0) {
		ringErr(file1, name->g.pos.fpos, PGPERR_KEYIO_SEEKING);
		return PGPERR_KEYIO_SEEKING;
	}
	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 (struct RingSet const *set, union RingObject *name1,
	union RingObject *name2)
{
	char const *buf1, *buf2;	/* Pointers to the names */
	unsigned len, tlen;
	word32 hash1, hash2;		/* Hashes of the two names */
	
	pgpAssert (OBJISNAME(name1));
	pgpAssert (OBJISNAME(name2));
	pgpAssert (set->mask & name1->g.mask & name2->g.mask);

	/* 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,
						(unsigned long *) &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
	* without fear of clobbering it.
		*/
	if (NAMEISCACHED(&name1->n)) {
		pgpAssert(buf1 == name1->n.name.ptr);
		buf2 = ringPoolGetName(ringSetPool(set),
							&name2->n,
							(unsigned long *) &tlen);
		if (!buf2)
			return ringSetError(set)->error;
		pgpAssert(tlen == len);
		return memcmp(buf1, buf2, len) != 0;
	}
	/* name1 is not cached, so it's in the pktbuf... */
	pgpAssert(buf1 == ringSetPool(set)->pktbuf);
	/*
	* Otherwise, compare name1 in pktbuf to name2 on disk.
	* Use MEMRING as dummy entry for file1; it is only used to
	* make sure that any seeks which move its position get undone,
	* and we don't care about that at this point.
	*/
	return namesdiffer(&ringSetPool(set)->files[MEMRINGBIT], name2);
}
	

/*
 * Get a struct RingName on the given chain matching the one in the ring's
 * pktbuf. Returns 0 if it runs out of memory or can't read the file.
 * (The ring's error is set to reflect this.)
 * It does *not* add the FilePos or set the mask bit.
 * Does *not* alter the given file's read position unless there is an error.
 */
static union RingObject *
ringFindName(struct RingFile *file, union RingObject *parent)
{
	struct RingPool *pool = file->set.pool;
	union RingObject *name, **np, **pname=NULL;
	size_t const len = pool->pktbuflen;
	word32 hash;
	int i;
	char *buf;	/* Used when trying to cache name */

	hash = ringHashBuf((byte const *)pool->pktbuf, len);

	pname = &parent->g.down;
	for (np = &parent->g.down; (name=*np) != NULL; np = &name->g.next) {
		/* Position name after names, key-sigs, sec's */
		if (OBJISNAME(name) || OBJISSIG(name) || OBJISSEC(name))
			pname = &name->g.next;
		/* If not a name or wrong length, no match */
		if (!OBJISNAME(name) || name->n.len != len)
			continue;

		/* If in memory, compare that */
		if (NAMEISCACHED(&name->n)) {
			if (memcmp(name->n.name.ptr, pool->pktbuf, len) == 0)
				return name;	/* Success */
		} else {
			if (name->n.name.hash == hash
			&& (i = namesdiffer(file, name)) <= 0)
				return i<0 ? NULL : name;
		}
	}

	/* Failed to find a name - create one */
	name = ringNewName(pool);
	if (name) {
		name->g.next = *pname;
		*pname = name;
		name->g.up = parent;
		name->n.len = len;
		name->n.name.hash = hash;	/* May overwrite below */
		/* Default new names to unknown trust */
		name->n.trust = PGP_NAMETRUST_UNKNOWN;
#if PGPTRUSTMODEL>0
		name->n.valid = name->n.validity = 0;
		name->n.confidence = PGP_NEWTRUST_UNDEFINED;
		NAMESETNEWTRUST(&name->n);
#endif
#if 1
		/* You might want to disable this for MSDOS */
		if (!(file->set.mask & MEMRINGMASK)) {
			/* Don't "cache" in memring, it is for new creations */
			buf = (char *)memPoolAlloc(&file->strings, len, 1);
			if (buf) {
				memcpy(buf, pool->pktbuf, len);
				name->n.name.ptr = buf;
				name->g.flags &= ~NAMEF_FILEMASK;
				name->g.flags |= ringLsBitFind(file->set.mask);
				NAMESETCACHED(&name->n);
			}
		}
#endif
	}
	return name;
}

/*
 * 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.
 */
static int
sigsdiffer(struct RingFile *file1, union RingObject const *sig)
{
	struct RingPool *pool = file1->set.pool;
	struct RingFile *file2;
	long retpos=0;
	int i;
	int bit;

	/* Find a matching signature */
	bit = ringLsBitFind(sig->g.mask & pool->filemask);
	pgpAssert(bit >= 0);
	file2 = &pool->files[bit];
	pgpAssert(file2->f || file2->set.mask == MEMRINGMASK);
	if (file2 == file1 && file1->f &&
			(retpos = pgpFileTell(file1->f)) < 0) {
		ringErr(file1, pgpFileTell(file1->f), PGPERR_KEYIO_FTELL);
		return PGPERR_KEYIO_FTELL;
	}

	if (file2->f &&
			pgpFileSeek(file2->f, sig->g.pos.fpos, SEEK_SET) != 0) {
		ringErr(file2, sig->g.pos.fpos, PGPERR_KEYIO_SEEKING);
		return PGPERR_KEYIO_SEEKING;
	}
	i = ringPacketDiffers(file2, sig, PKTBYTE_SIG, (word32)-1);

	if (file2 == file1 && file1->f &&
			pgpFileSeek(file1->f, retpos, SEEK_SET) != 0) {
		ringErr(file1, sig->g.pos.fpos, PGPERR_KEYIO_SEEKING);
		return PGPERR_KEYIO_SEEKING;
	}
	return i;
}

static union RingObject *
ringFindSig(struct RingFile *file, union RingObject *parent)
{
	struct RingPool *pool = file->set.pool;
	union RingObject *key;
	union RingObject *sig, **sigp, **fsigp;
	int i;

	/* Properties of the signature */
	int err;
	byte pkalg, keyID[8];
	word32 tstamp;
	word16 validity;
	byte type;	
	byte hashalg;
	byte version;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?