pgprngread.c

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

C
2,259
字号
/*
 * pgpRngRead.c - Read in various parts of a keyring.
 *
 * Copyright (C) 1994-1997 Pretty Good Privacy, Inc. All rights reserved.
 *
 * The big function (>500 lines, yeep!) is ringFileOpen(); it opens another
 * keyring and merges it with the collection in memory.	 Most of the others
 * are its helpers. This is where PGPlib's great robustness in the face of
 * badly mangled keyrings is achieved.	*Every* keyring comes through here,
 * and it validates its inputs to the point of paranoia.
 *
 * This file is too big - what should be split out?
 * There are a lot of similar-but-not-quite functions.	Perhaps some
 * rethinking will allow parts of them to be merged?
 *
 * Written by Colin Plumb.
 *
 * $Id: pgpRngRead.c,v 1.24.2.12 1997/06/11 22:29:47 mhw Exp $
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include "pgpDebug.h"
#include "pgpMakeSig.h"
#include "pgpMemPool.h"
#include "pgpPktByte.h"
#include "pgpRngMnt.h"
#include "pgpRngPars.h"
#include "pgpRngPkt.h"
#include "pgpRngPriv.h"
#include "pgpTrust.h"
#include "pgpTrstPkt.h"		/* for PGP_SIGTRUSTF_CHECKED_TRIED */
#include "pgpHash.h"
#include "pgpKeySpec.h"
#include "pgpMem.h"
#include "pgpCipher.h"
#include "pgpEnv.h"
#include "pgpErr.h"
#include "pgpPubKey.h"
#include "pgpFile.h"
#include "pgpRngRead.h"
#include "pgpSigSpec.h"

#ifndef NULL
#define NULL 0
#endif

/* We have optional code to wrap newer packets in comments */
#define WRAP_NONE 1


/*
 * The largest legal PGP key uses a 64Kbit key, which is 8Kbytes.
 * As a public key, there's also 12 bytes of overhead, plus a
 * public exponent (usually 1 byte, sometimes 3, it's just stupid
 * to make it any larger).
 * Stored in a secret key an extra 11+IV bytes of overhead and
 * the secret exponent (8K), factors p and q (4+4=8K), and multiplicative
 * inverse u (4K). A total of 28K of data, plus 23+IV+e extra bytes.
 * With an 8-byte IV, that's 31+e bytes. Add an extra byte to allow
 * for p and q of differing lengths.
 * But for now, reduce this by a factor of 8, to 8Kbits, which changes
 * the maximum sizes to 1K+overhead and 3.5K + overhead.
 *
 * Without these limits, a non-fatal error (object too big) becomes
 * a fatal error (out of memory) and the implementation becomes
 * less robust. However, the limits can be set quite high without
 * harm. (Keep the maximum key size to 64K, though.)
 *
 * The limits above were derived for RSA keys. DSA/ElG keys have a prime
 * p, a small (~160 bit) prime q, a generator g, and a public exponent y.
 * That's four numbers, one of them small. If we allow enough space for four
 * full-sized numbers that should be safe. Secrets have in addition a
 * secret exponent x, generally small, so allow an additional number.
 * Signatures are small with DSA, two 160 bit numbers. However the new
 * signature packets can in principle have a lot of data. Allow two full
 * sized numbers to give us plenty of room; we may have to increase the value
 * in the future.
 */
#define RINGMPI_MAX	1024				/* Maximum 8Kbits */
#define RINGKEY_MAXLEN	(4*RINGMPI_MAX)	/* Public key maximum size */
#define RINGSEC_MAXLEN	(5*RINGMPI_MAX)	/* Secret key maximum size */
#define RINGNAME_MAXLEN	1024u		 	/* Name maximum size */
#define RINGSIG_MAXLEN	(2*RINGMPI_MAX)	/* Signature maximum size */
#define RINGUNK_MAXLEN	RINGSEC_MAXLEN
/* RINGTRUST_MAXLEN is implicit */

/*** Working with the FilePos chain ***/

/*
 * A note about the FilePos chain. Each object has one FilePos right
 * inside itself, which is the head of a list of external (allocated)
 * ones. There is one FilePos for each key file an object exists in,
 * and they are kept in increasing order by bit number.
 * Every object (except dummy keys, which aren't excessively numerous,
 * especially in large keyrings) is present in at least one physical
 * keyring, so this saves one next pointer when we're trying to conserve
 * memory for MS-DOS, at the expense of complicating the task of
 * adding to and removing from the list.
 *
 * This is because the first entry in the chain is statically allocated.
 * An actual allocation is performed when an entry is added to the
 * chain in a location other than the first, or is bumped from first
 * place by something else. A FilePos is freed when an entry is deleted
 * from the chain, or the first one is deleted and the second moves into
 * its place.
 *
 * One more piece of magic: because there is no need for the FilePos chain
 * to be null-terminated (the number of entries in it is given by the
 * mask), physical key ring MEMRINGBIT (which is the last FilePos in the
 * chain, so its next pointer is unused) is reserved for memory objects.
 * the next pointer and fpos record the location in memory and size of
 * the memory buffer holding the object, respectively.
 */

/* Find the position of an object in the given file */
static struct FilePos *
ringFilePos(union RingObject const *obj, struct RingFile const *file)
{
	struct FilePos const *pos = &obj->g.pos;
	ringmask mask;

	pgpAssert(obj->g.mask & file->set.mask);
	mask = obj->g.mask & file->set.pool->filemask & (file->set.mask-1);
	while (mask) {
		pos = pos->ptr.next;
		mask &= mask-1;
	}
	return (struct FilePos *)pos;
}

/* Allocate a FilePos from a RingFile. */
static struct FilePos *
ringFileNewFilePos(struct RingFile *file)
{
	struct FilePos *pos = file->freepos;

	if (pos) {
		file->freepos = pos->ptr.next;
	} else {
		pos = (struct FilePos *)memPoolNew(&file->fpos,struct FilePos);
		if (!pos)
			ringAllocErr(file->set.pool);
	}

	return pos;
}

static void
ringFileFreeFilePos(struct RingFile *file, struct FilePos *pos)
{
	pos->ptr.next = file->freepos;
	file->freepos = pos;
}

/*
 * Allocate and add a FilePos to the object's chain in the right place.
 * This function makes no attempt to initialize the resultant FilePos.
 *
 * NOTE that the bit specified to add may or may not be present in
 * the ring's filemask. This function must not care.
 */
static struct FilePos *
ringAddFilePos(union RingObject *obj, struct RingFile *file)
{
	ringmask mask = obj->g.mask & file->set.pool->filemask;
	ringmask mask2 = file->set.mask;
	struct FilePos *pos, *pos2;
	int bit;

	pgpAssert(!(mask & mask2));

	if (mask & (mask2-1)) {
		/* FilePos to add is not the first in the chain */
		mask &= (mask2-1);

		pos2 = ringFileNewFilePos(file);
		if (!pos2)
			return NULL;
		/* Find the predecessor of the one to be added */
		pos = &obj->g.pos;
		while ((mask &= mask-1) != 0) {
			pos = pos->ptr.next;
			pgpAssert(pos);
		}
		/* Insert pos2 into the chain after pos */
		pos2->ptr.next = pos->ptr.next;
		pos->ptr.next = pos2;
	} else {
		/* First FilePos in the chain */
		pos2 = &obj->g.pos;
		if (!mask) {
			/* First and only FilePos on chain */
			pos = NULL;
		} else {
			/* First FilePos; bump down the old first */
			bit = ringLsBitFind(mask);
			pgpAssert(bit >= 0);
			file = &file->set.pool->files[bit];
			pos = ringFileNewFilePos(file);
			if (!pos)
				return NULL;
			*pos = *pos2;
		}
		pos2->ptr.next = pos;
	}
	obj->g.mask |= mask2;
	return pos2;
}

static int
ringAddPos(union RingObject *obj, struct RingFile *file, word32 fpos)
{
	struct FilePos *pos;

	pos = ringAddFilePos(obj, file);
	if (!pos)
		return PGPERR_NOMEM;
	pos->fpos = fpos;
	return 0;
}

/*
 * This is needed in one obscure error case to keep things
 * consistent. The case is when a secret key appears in the same
 * file as the corresponding public key, only later.
 */
static void
ringAlterFilePos(union RingObject *obj, struct RingFile const *file,
	word32 fpos)
{
	ringmask mask = obj->g.mask & file->set.pool->filemask;
	struct FilePos *pos;

	pgpAssert(mask & file->set.mask);
	mask &= file->set.mask - 1;
	for (pos = &obj->g.pos; mask; mask &= mask-1)
		pos = pos->ptr.next;
	pos->fpos = fpos;
}

/*
* Remove a FilePos from an object's list.
*
* file is the filepos corresponding to "bit", pos is the head of a
* FilePos chain, mask is the bitmask of physical key rings, and
* bit is the number of the ring to have its position removed.
*
* NOTE that the bit specified to remove may or may not be present in
* the ring->filemask. This function must not care.
*/
static void
ringRemFilePos(union RingObject *obj, struct RingFile *file)
{
	ringmask mask = obj->g.mask & file->set.pool->filemask;
	ringmask mask2 = file->set.mask;
	struct FilePos *pos, *pos2;
	int bit;

	/* Is the bit to remove *not* the least significant bit? */
	if (mask & (mask2-1)) {
		/* FilePos to remove is not the first in the chain */

		/* Find the predecessor of the one to be removed */
		mask &= (mask2-1);
		pos = &obj->g.pos;
		while ((mask &= mask-1) != 0) {
			pos = pos->ptr.next;
			pgpAssert(pos);
		}
		/* pos->next is the one to be removed */
		pos2 = pos->ptr.next;
		pgpAssert(pos2);
		pos->ptr = pos2->ptr;
		if (mask2 == MEMRINGMASK)	/* Debugging aid */
			pos->ptr.next = NULL;
	} else {
		/* First FilePos - copy second to first, remove second */

		/* Clear this bit from the mask (in case we need to) */
		mask &= ~mask2;

		/*
		* That's it? Well, return then. The caller better
		* deallocate this object, 'cause it no longer exists
		* anywhere. Use position -1 to mark an unused slot.
		*/
		if (!mask) {
			obj->g.mask &= ~mask2;
			if (mask2 == MEMRINGMASK)	 /* Debugging aid */
				obj->g.pos.ptr.next = NULL;
			obj->g.pos.fpos = (word32)-1;	 /* Debugging aid */
			return;
		}

		/* Find the bit of the object we are removing */
		bit = ringLsBitFind(mask);
		pgpAssert(bit >= 0);
		file = &file->set.pool->files[bit];

		/* Copy the next pos to the current one */
		pos2 = obj->g.pos.ptr.next;
		pgpAssert(pos2);
		obj->g.pos = *pos2;
	}
	/* Free the FilePos */
	ringFileFreeFilePos(file, pos2);
	obj->g.mask &= ~mask2;
}

/*** Closing a Ringfile ***/

/*
 * Set the destruction function for a RingFile.
 */
void
ringFileSetDestructor(struct RingFile *file,
	void (*destructor)(struct RingFile *, struct PgpFile *, void *),
	void *arg)
{
	file->destructor = destructor;
	file->arg = arg;
}

/*
 * Helper function for ringFileDoClose.
 *
 * Delete the given file's FilePos entries from the objects in
 * the given list, and delete the objects if they are no longer
 * needed (mask has gone to 0). Recurse as necessary.
 *
 * Note that this is not used on the main keys list, because
 * there we need to preserve dummy keys which this does not
 * understand.
 *
 * This also removes any cached names from objects.
 */
static void
ringFileCloseList(union RingObject **objp, struct RingFile *file)
{
	union RingObject *obj;
	ringmask const mask = file->set.mask;
	ringmask const filemask = file->set.pool->filemask;

	while ((obj = *objp) != NULL) {
		if (mask & obj->g.mask) {
			if (!OBJISBOT(obj))
				ringFileCloseList(&obj->g.down, file);
			ringRemFilePos(obj, file);
			/*
			* May have a child obj in the MEMRING which didn't
			* get deleted because it was not in this RingFile.
			*/
			if (!(obj->g.mask & filemask & ~MEMRINGMASK) &&
				(OBJISBOT(obj) || !obj->g.down)) {
				*objp = obj->g.next;
				ringFreeObject(file->set.pool, obj);
			} else {
				if (OBJISNAME(obj))
					ringPurgeCachedName(&obj->n, mask);
				objp = &obj->g.next;
			}
		} else
			objp = &obj->g.next;
	}
}

/*
 * Close the given Ringfile. Returns an error if it can't due to
 * conflicts, in which case the file is NOT closed.
 *
 * This performs four passes over the pool.
 * 1. The first does the bulk of the deletion, removing the
 * FilePos from the objects and deleting all things
 * at levels greater than 1.
 * 2. The second rebuilds the sigs-by lists which were broken by
 * deleting objects in the middle of them.
 * 3. The third finds all keys that are not referenced and do not
 * make any signatures, and deletes those keys.
 * 4. The fourth rebuilds the hash index of the remaining keys.
 *
 * Note that the second and third passes delete any allocated-but-not
 * linked keys, which are left by ringFileOpen if it runs out of memory
 * in mid-operation.
 */
static void
ringFileDoClose(struct RingFile *file)
{
	union RingObject *obj, **objp;
	struct RingPool *pool = file->set.pool;
	ringmask mask = file->set.mask;
	ringmask filemask = pool->filemask;
	ringmask allocmask = ringAllocMask(pool, &file->set);
	int i;

	/* Free some memory right away */
	ringFilePurgeTrouble(file);

	/* 1: Remove everything in the keyring, but don't delete the keys */
	for (obj = pool->keys; obj; obj = obj->g.next) {
		if (mask & obj->g.mask) {
			if (!OBJISBOT(obj))
				ringFileCloseList(&obj->g.down, file);
			ringRemFilePos(obj, file);
		}
	}

	/* 2: Recreate the shattered sigs-by lists */
	ringPoolListSigsBy(pool);

	/* 3: Now purge the unneeded keys */
	objp = &pool->keys;
	while ((obj = *objp) != NULL) {
		pgpAssert(OBJISKEY(obj));
		if ((obj->g.mask & filemask & ~MEMRINGMASK) || obj->g.down) {
			objp = &obj->g.next;
		} else if (obj->k.sigsby) {
			/* Retain key as a dummy key */
			pgpAssert(!(obj->g.mask & allocmask & ~MEMRINGMASK));
			pgpAssert(!obj->g.down);
			obj->g.mask = 0;
			objp = &obj->g.next;
		} else {
			/* Delete the key */
			pgpAssert(!(obj->g.mask & allocmask & ~MEMRINGMASK));
			pgpAssert(!obj->g.down);
			*objp = obj->g.next;
			ringFreeObject(pool, obj);
		}
	}

	/* 4: Re-initialize the hash chains */
	ringPoolHash(pool);

	/* Clean up the file's memory pools */
	memPoolEmpty(&file->strings);
	file->freepos = NULL;
	memPoolEmpty(&file->fpos);

	pgpAssert(!file->set.next);

	/*
	* If there's nothing in the structs MemPool that's
	* allocated, purge all the memory.
	*/
	if (!pool->keys && !pool->sets) {
		for (i = 0; i < RINGTYPE_MAX; i++)

⌨️ 快捷键说明

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