pgprngread.c

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

C
2,259
字号
	size_t extralen;

	pkalg = 0;
	err = ringSigParse((byte const *)pool->pktbuf, pool->pktbuflen,
	&pkalg, keyID, &tstamp, &validity, &type,
	&hashalg, &extralen, &version);

	/* Get key this signature is by */
	key = ringPoolFindDummyKey(pool, NULL, pkalg, keyID);
	if (!key)
		return NULL;

	/* Search for matching sigs */
	fsigp = &parent->g.down;
	for (sigp = fsigp; (sig=*sigp) != NULL; sigp = &sig->g.next) {
		if (OBJISSEC(sig) || OBJISSIG(sig))
			fsigp = &sig->g.next; /* predecessor to new sig */
		if (OBJISSIG(sig)
		&& sig->s.by == key
		&& (sig->s.by->k.pkalg == pkalg || pkalg==1) /* ViaCrypt */
		&& sig->s.tstamp == tstamp
		&& sig->s.type == type
			&& sig->s.version == version
				&& sig->s.validity == validity
				&& sig->s.hashalg == hashalg
				&& !(sig->g.flags & SIGF_ERROR) == !err
				&& !(sig->g.flags & SIGF_NONFIVE) == (extralen == 5)
				&& (i = sigsdiffer(file, sig)) <= 0)
			{
			return i<0 ? NULL : sig;
		}
	}

	/* Not found - allocate a new sig */
	sig = ringNewSig(pool);
	if (sig) {
		/* Add new sig before any user ID's */
		sig->g.next = *fsigp;
		*fsigp = sig;
		sig->s.by = (union RingObject *)key;
		sig->g.up = parent;
		if (err)
			sig->g.flags |= SIGF_ERROR;
		if (extralen != 5)
			sig->g.flags |= SIGF_NONFIVE;
		if (OBJISKEY(parent))
			sig->g.flags |= SIGF_KEY;
		sig->s.tstamp = tstamp;
		sig->s.validity = validity;
		sig->s.type = type;
		sig->s.version = version;
		sig->s.hashalg = hashalg;
		sig->s.trust = 0;
	}
	return sig;
}

/*
 * Return 1 if the blob in the ring's pktbuf differs from the
 * blob 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
unkdiffer(struct RingFile *file1, union RingObject const *unk)
{
	struct RingPool *pool = file1->set.pool;
	struct RingFile *file2;
	long retpos=0;
	int i;
	int bit;

	/* Find a matching unk */
	file2 = ringBestFile(pool, unk, 0);
	bit = ringLsBitFind(unk->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, unk->g.pos.fpos, SEEK_SET) != 0) {
		ringErr(file2, unk->g.pos.fpos, PGPERR_KEYIO_SEEKING);
		return PGPERR_KEYIO_SEEKING;
	}
	i = ringPacketDiffers(file2, unk, PKTBYTE_TYPE(unk->u.pktbyte),
	pool->pktbuflen+1);

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

/*
 * Get a struct RingUnk on the given chain matching the one in the ring's
 * pktbuf with the given pktbyte. 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 *
ringFindUnk(struct RingFile *file, union RingObject *parent, byte pktbyte)
{
	struct RingPool *pool = file->set.pool;
	union RingObject *unk, **unkp;
	word32 hash;
	int i;

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

	for (unkp = &parent->g.down; (unk=*unkp) != NULL;
			unkp = &unk->g.next) {
			/*
		* If the type, hash and pktbyte match, try to compare...
		* Note that the pktbyte *must* match or namesdiffer()
		* will complain loudly.
			*/
		if (OBJISUNK(unk)
		&& unk->u.hash == hash
		&& unk->u.pktbyte == pktbyte
		&& (i = unkdiffer(file, unk)) <= 0)
		{
			return i<0 ? NULL : unk;
		}
	}

	/* Failed to find a name - create one */
	unk = ringNewUnk(pool);
	if (unk) {
		*unkp = unk;
		unk->g.up = parent;
		unk->u.pktbyte = pktbyte;
		unk->u.hash = hash;
		/* Default new names to unknown trust */
	}
	return unk;
}

/* Return the list of trpuble associated with a RingFile. */
struct RingTrouble const *
ringFileTrouble(struct RingFile const *file)
{
	return file->trouble;
}

/*
 * Functions for manipulating the Trouble list.
*/

/* Zero out a RingFile's Trouble list; it has been dealt with. */
void
ringFilePurgeTrouble(struct RingFile *file)
{
	memPoolEmpty(&file->troublepool);
	file->trouble = NULL;
	file->troubletail = &file->trouble;
}

/*
 * Log some trouble with a RingFile.
 */
static int
ringFileLog(struct RingFile *file, union RingObject *obj, word32 num,
	word32 fpos, int type)
{
	struct RingTrouble *t;

	t = (struct RingTrouble *)memPoolNew(&file->troublepool,
			struct RingTrouble);
	if (!t)
		return PGPERR_NOMEM;

	t->next = NULL;
	t->obj = obj;
	t->num = num;
	t->fpos = fpos;
	t->type = type;

	*file->troubletail = t;
	file->troubletail = &t->next;
	return 0;
}

/*
 * @@@ This needs fixing - we should complain about a key without
 * names in *this* file, even if it has names in others.
 */
static union RingObject const *
ringKeyHasName(union RingObject const *obj)
{
	for (obj = obj->g.down; obj; obj = obj->g.next)
		if (OBJISNAME(obj))
			break;
	return obj;
}

/*
 * How to skip things. Various helper functions return either
 * negative fatal error codes or these codes indicating what to
 * do to recover from any warnings. These never exceed 15 bits,
 * so "int" is the right type.
*/

typedef int skip_t;
#define SKIP_TRUST 1
#define SKIP_SIG 2
#define SKIP_NAME 4
#define SKIP_SUBKEY 8
#define SKIP_KEY 16

#define SKIP_SIGS (SKIP_TRUST | SKIP_SIG)
#define SKIP_TO_KEY (SKIP_SIGS | SKIP_NAME | SKIP_SUBKEY)

#define PB_PUBKEY(key) (OBJISTOPKEY(key) ? \
	PKTBYTE_BUILD(PKTBYTE_PUBKEY, 0) : \
	PKTBYTE_BUILD(PKTBYTE_PUBSUBKEY, 0))
#define PB_SECKEY(key) (OBJISTOPKEY(key) ? \
	PKTBYTE_BUILD(PKTBYTE_SECKEY, 0) : \
	PKTBYTE_BUILD(PKTBYTE_SECSUBKEY, 0))
#define PB_SIG PKTBYTE_BUILD(PKTBYTE_SIG, 0)

/*
 * Add a new key to the Pool, with its *parent* at the given level
 * (0 means adding top-level key) in the RingIterator. Leave the
 * RingIterator pointing to the newly created key. Return <0 on error,
 * 0 if the key was created, and a skip code > 0 if it was created with
 * a warning of some sort.
*/
static int
ringAddKey(struct RingFile *file, struct RingIterator *iter, word32 fpos,
	int trusted, byte pkttype)
{
	union RingObject *key, *sec, *parent;
	byte pkalg, keyID[8];
	word16 keybits;
	word32 tstamp;
	word16 validity;
	int i, err, level;

	pkalg = 0;
	err = ringKeyParse((byte const *)file->set.pool->pktbuf,
	file->set.pool->pktbuflen, &pkalg,
	keyID, &keybits, &tstamp, &validity, 0);

	if (pkttype == PKTBYTE_PUBSUBKEY) {
		/* Subkey */
		if (!iter->level) {
			i = ringFileLog(file, NULL, 0, fpos,
					PGPERR_TROUBLE_UNXSUBKEY);
			return i < 0 ? i : SKIP_SIGS;
		}
		parent = iter->stack[0];
		level = 1;
	} else {
		parent = NULL;
		level = 0;

		if (iter->level) {
			key = iter->stack[0];
			pgpAssert(OBJISKEY(key));

			/* Complain about a key with no Names */
			if (!ringKeyHasName(key)) {
				i = ringFileLog(file, key, 0,
						ringFilePos(key, file)->fpos,
						PGPERR_TROUBLE_BAREKEY);
				if (i < 0)
					return i;
			}

			/* @@@ Should we not care about this? */
			/* If keys not in sorted order, dirty */
#if 0
			/* @@@ ringSortKeys orders keys by algorithm
			so this test no longer works. */
			if (ringKeyIDcmp(key->k.keyID, keyID) > 0)
				ringFileMarkDirty(file);
#endif
		}
	}

	/* Find the matching key structure */
	key = ringPoolFindDummyKey(file->set.pool, parent, pkalg, keyID);
	if (!key)
		return ringFileError(file)->error;

	/* See if it's a new key or the same key */
	if (key->g.mask == 0) {
		/* Newly created dummy key; fill in info */
		if (pkttype == PKTBYTE_PUBSUBKEY && OBJISTOPKEY(key)) {
				/*
			* Here on a former dummy top-level key which
			* has turned out to be a subkey. Dummy key
			* would have been created if we saw a sig by
			* it. Change key to a subkey.
				*/
			i = ringFileLog(file, NULL, 0, fpos,
					PGPERR_TROUBLE_SIGSUBKEY);
			if (i < 0)
				return i;
			ringPoolUnlinkKey(file->set.pool, key);
			key->k.flags |= RINGOBJF_SUBKEY;
			ringPoolLinkKey(file->set.pool, parent, key,
					pkalg, keyID);
		}

		key->k.pkalg = pkalg;	/* ViaCrypt */
		key->k.tstamp = tstamp;
		key->k.validity = validity;
		key->k.keybits = keybits;
		key->k.trust = 0;
		if (err)
			key->g.flags |= KEYF_ERROR;

	} else if (keybits != key->k.keybits
			|| pkalg != key->k.pkalg		/* ViaCrypt */
			|| tstamp != key->k.tstamp
/*			|| validity != key->k.validity	Validity may be in sig */
			|| parent != (OBJISTOP(key) ? NULL : key->g.up)
			|| !(key->g.flags & KEYF_ERROR) != !err)
	{
		i = ringFileLog(file, key, 0, fpos,
		                PGPERR_TROUBLE_DUPKEYID);
		return i < 0 ? i : SKIP_TO_KEY;

	} else if ((i=keysdiffer(file,key,PB_PUBKEY(key))) != 0) {
		if (i < 0)
			return i;
		if ((OBJISTOPKEY(key) && pkttype==PKTBYTE_PUBSUBKEY) ||
		(OBJISSUBKEY(key) && pkttype==PKTBYTE_PUBKEY)) {
			i = ringFileLog(file, key, 0, fpos,
				PGPERR_TROUBLE_KEYSUBKEY);
			return (i < 0) ? i : SKIP_TO_KEY;
		}
/* KLUDGE: version byte bug */
			/*
		* A key with a version byte of 2 only overrides previous
		* keys with a byte of 3 if they are all untrusted
		* secret keys and this is either a secret key or from
		* a trusted public keyring. A key with a version byte
		* of 3 is only overridden if it's secret and previous
		* keys include a secret key or a trusted public key.
			*/
		if (i == PGPVERSION_2
				&& trusted
				&& !(key->g.flags & RINGOBJF_TRUST)
				&& !((ringKeySecMask(key) ^ key->g.mask)
				& file->set.pool->filemask))
			{
			i = ringFileLog(file, key, 0, fpos,
			PGPERR_TROUBLE_VERSION_BUG_PREV);
			if (i < 0)
				return i;
			/* Set all those secret version bug flags */
			for (sec = key->g.down; sec; sec = sec->g.next)
				if (OBJISSEC(sec))
					sec->g.flags |= SECF_VERSION_BUG;
		} else {
/* End of KLUDGE */
			i = ringFileLog(file, key, 0, fpos,
					PGPERR_TROUBLE_DUPKEYID);
			if (i < 0)
				return i;
			return i < 0 ? i : SKIP_TO_KEY;
			}
		}

		/*
		* Already present in this keyring?
		* If so, accept following sigs & userids (they may
		* not be duplicates), but flag a warning.
		*/
	if (key->g.mask & file->set.mask) {
		i = ringFileLog(file, key, 0, fpos, PGPERR_TROUBLE_DUPKEY);
		if (i < 0)
			goto failed;
		i = SKIP_TRUST;
	} else {
		/* Add the FilePos */
		i = ringAddPos(key, file, fpos);
		if (i < 0)
			goto failed;
	}
	/* Add successful; indicate it in the mask */
	key->g.mask |= iter->set.mask;
	iter->stack[level] = key;
	iter->level = level+1;
	return i;

failed:
	if (!key->g.mask)
		ringFreeDummyKey(file->set.pool, key);
	return i;
}

/*
 * Add a new secret to the Pool, as two objects: a key (with its *parent*
 * at the given level in the RingIterator; 0 means add a top-level key)
 * and a signature as its child. Leave the RingIterator pointing to the
 * newly created secret. Return <0 on error, 0 if the key was created,
 * and a skip code >0 if they key was created with a warning of some sort.
 *
 * This is *ridiculously* hairy. Is there a way to clean it up?
 * @@@ TODO: Add PGPERR_TROUBLE_OLDSEC and _NEWSEC handling.
 */
static int
ringAddSec(struct RingFile *file, struct RingIterator *iter, word32 fpos,
	byte pkttype)
{
	union RingObject *key, *sec, *parent;
	byte pkalg, keyID[8];
	word16 keybits;
	word32 tstamp;
	word16 validity;
	int i, err;
	int level;
	int flags = 0;

	pkalg = 0;
	err = ringKeyParse((byte const *)file->set.pool->pktbuf,
	file->set.pool->pktbuflen, &pkalg,
	keyID, &keybits, &tstamp, &validity, 1);

	if (pkttype == PKTBYTE_SECSUBKEY) {
		/* Subkey */
		if (!iter->level) {
			i = ringFileLog(file, NULL, 0, fpos,
					PGPERR_TROUBLE_UNXSUBKEY);
			return i < 0 ? i : SKIP_SIGS;
		}
		parent = iter->stack[0];
		level = 1;
	} else {
		parent = NULL;
		level = 0;

		if (iter->level) {
			key = iter->stack[0];
			pgpAssert(OBJISKEY(key));

			/* Complain about a key with no Names */
			if (!ringKeyHasName(key)) {
				i = ringFileLog(file, key, 0,
						ringFilePos(key, file)->fpos,
						PGPERR_TROUBLE_BAREKEY)

⌨️ 快捷键说明

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