--- pgp/src/keymaint.c 2018/04/24 16:38:35 1.1.1.2 +++ pgp/src/keymaint.c 2018/04/24 16:40:06 1.1.1.4 @@ -22,28 +22,79 @@ #include #include +#include #include "mpilib.h" #include "random.h" #include "crypto.h" #include "fileio.h" #include "keymgmt.h" +#include "keymaint.h" #include "mpiio.h" +#include "charset.h" #include "language.h" #include "pgp.h" +#if 1 /* def DEBUG */ +#include +#else +#define assert(x) +#endif + +struct userid; +struct signature; + +struct pubkey { + struct pubkey *pk_next; + struct pubkey *pk_hash; /* hash list for keyID */ + struct userid *pk_userids; + struct signature *pk_signed; /* signatures this key made */ + byte pk_keyid[KEYFRAGSIZE]; + byte pk_owntrust; + byte pk_depth; /* shortest cert. path to buckstop key */ +}; + +struct userid { + struct userid *uid_next; + struct pubkey *uid_key; /* backlink to key */ + struct signature *uid_signatures; + char *uid_userid; + byte uid_legit; +}; + +struct signature { + struct signature *sig_next; /* list of signatures on a userid */ + struct userid *sig_uid; /* the userid it signs */ + struct pubkey *sig_from; /* key that made this signature */ + struct signature *sig_nextfrom; /* list of sigs made by the same key (sig_from) */ + byte sig_trust; +}; + -void setup_trust(); -int maint_pass1(FILE *f); -int maint_trace_chain(FILE *f); -int maint_cmpfiles(FILE *f, FILE *g); -int trace_sig_chain(FILE *f, byte *fromID, int owner_trust, int depth); -int check_secretkey(FILE *f, long keypos); -int set_legit(FILE *f, long trustpos, int trust_count); +int maint_list(char *ringfile); +void init_trust_lst(void); long lookup_by_keyID(FILE *f, byte *srch_keyID); void show_userid(FILE *f, byte *keyID); -int show_trust(byte, byte); -int show_legit(byte, byte); -void write_trust_pos(FILE *f, byte keyctrl, long pos); + +static int maintenance(char *ringfile); +static int maint_read_data(char *ringfile); +static int maint_trace_chain(void); +static int trace_sig_chain(struct pubkey *pk, int depth); +static int maint_final(char *ringfile); +static struct pubkey * getpubkey(byte *keyID); +static void setup_trust(void); +static int check_secretkey(FILE *f, long keypos); +static void maint_init_mem(void); +static void maint_release_mem(void); +static VOID * allocn(int size); +static char * store_str(char *str); +static VOID * allocbuf(int size); +static void freebufpool(void); +static void compute_legit(struct userid *id); + + +#define ALLOC_UNIT 4000 /* memory will be allocated in chunks of this size */ + +#define MAX_DEPTH 8 /* max. value of max_cert_depth */ /* returned when trying to do a maintenance pass on a secret keyring or keyfile */ #define ERR_NOTRUST -7 @@ -54,12 +105,13 @@ void write_trust_pos(FILE *f, byte keyct #define TRUST_FAC(x) (trust_tbl[TRUST_LEV(x)]) +#define ctb_type(c) ((c&CTB_TYPE_MASK)>>2) /* * table for tuning user paranoia index. * values represent contribution of one signature indexed by the * SIGTRUST of a signature */ -int trust_tbl[8]; +static int trust_tbl[8]; static int marginal_min; static int complete_min; /* total count needed for a fully legit key */ @@ -86,8 +138,8 @@ char legit_lst[4][16] = { "complete" }; -int trustlst_len = 9; /* length of longest trust word */ -int legitlst_len = 9; /* length of longest legit word */ +static int trustlst_len = 9; /* length of longest trust word */ +static int legitlst_len = 9; /* length of longest legit word */ char floppyring[MAX_PATH] = ""; int max_cert_depth = 4; /* maximum nesting of signatures */ @@ -96,202 +148,261 @@ static boolean check_only = FALSE; static boolean mverbose; static FILE *sec_fp; static FILE *floppy_fp = NULL; -static int undefined_trust; /* number of legit keys with undef. trust */ +static int undefined_trust; /* number of complete keys with undef. trust */ + +/* + * Update trust parameters in a keyring, should be called after all + * key management functions which can affect the trust parameters. + * Changes are done "inplace", the file must be writable. + */ +int +maint_update(char *ringfile) +{ + check_only = mverbose = FALSE; + return maintenance(ringfile); +} /* - * do a maintenance pass on keyring "ringfile" + * Check trust parameters in ringfile * options can be: - * MAINT_CHECK check keyring only, "ringfile" will not be changed - * MAINT_SILENT used for implicit maintenance pass, does not print keyring contents + * MAINT_CHECK check only, don't ask if keyring should be updated * MAINT_VERBOSE verbose output, shows signature chains */ int -maintenance(char *ringfile, int options) +maint_check(char *ringfile, int options) { - FILE *f, *g; int status; - char *tmpring; - char secretkeyring[MAX_PATH]; - extern boolean moreflag; + char *fixfile; - check_only = (options & MAINT_CHECK) != 0; - mverbose = (options & MAINT_VERBOSE) != 0; - undefined_trust = 0; - if (max_cert_depth > 8) - max_cert_depth = 8; + mverbose = ((options & MAINT_VERBOSE) != 0); - if ((tmpring = tempfile(TMP_TMPDIR)) == NULL) - return(-1); - if ((g = fopen(tmpring, FOPWPBIN)) == NULL) - return(-1); + if (moreflag) + open_more(); + if (*floppyring != '\0' && (floppy_fp = fopen(floppyring, FOPRBIN)) == NULL) + fprintf(pgpout,PSTR("\nCan't open backup key ring file '%s'\n"), + floppyring); + check_only = TRUE; + status = maintenance(ringfile); + if (floppy_fp) { + fclose(floppy_fp); + floppy_fp = NULL; + } + if (status <= 0) { + if (status == 0) + maint_list(ringfile); + close_more(); + return status; + } +#ifdef xDEBUG + if (status > 0 && (options & MAINT_CHECK)) { + FILE *sav = pgpout; + if (pgpout = fopen("before.lst", "w")) { + maint_list(ringfile); + fclose(pgpout); + } + pgpout = sav; + } +#endif + /* Inform user of trust parameters to be changed... */ + if (undefined_trust) { - if ((f = fopen(ringfile,FOPRBIN)) == NULL) - { fprintf(pgpout,PSTR("\n\007Can't open key ring file '%s'\n"),ringfile); - fclose(g); - return(-1); + /* If we are just going to check, then exit now... */ + if (options & MAINT_CHECK){ + maint_list(ringfile); + } + + fprintf(pgpout, PSTR("\n%d \"trust parameter(s)\" need to be changed.\n"), + undefined_trust); + + if (options & MAINT_CHECK) { + close_more(); + return status; + } + + fprintf(pgpout, PSTR("Continue with '%s' (Y/n)? "), + ringfile); + if (!getyesno('y')) { + close_more(); + return status; + } + } + + /* do the fixes in a scratch file */ + fixfile = tempfile(0); + if (copyfiles_by_name(ringfile, fixfile) < 0) { + close_more(); + return -1; + } + check_only = mverbose = FALSE; + if ((status = maintenance(fixfile)) >= 0) { + maint_list(fixfile); + fprintf(pgpout, PSTR("\n%d \"trust parameter(s)\" changed.\n"), status); } + close_more(); + if (status > 0 && !(options & MAINT_CHECK)) { + fprintf(pgpout, PSTR("Update public keyring '%s' (Y/n)? "), ringfile); + if (getyesno('y')) + return savetempbak(fixfile, ringfile); + } + rmtemp(fixfile); + return status; +} + +static int +maintenance(char *ringfile) +{ + int status; + char secretkeyring[MAX_PATH]; + undefined_trust = 0; /* None so far... */ + + if (max_cert_depth > MAX_DEPTH) + max_cert_depth = MAX_DEPTH; buildfilename(secretkeyring, SECRET_KEYRING_FILENAME); if ((sec_fp = fopen(secretkeyring, FOPRBIN)) == NULL) fprintf(pgpout,PSTR("\nCan't open secret key ring file '%s'\n"), secretkeyring); - if (!(options & MAINT_SILENT) && *floppyring != '\0' && - (floppy_fp = fopen(floppyring, FOPRBIN)) == NULL) - fprintf(pgpout,PSTR("\nCan't open backup key ring file '%s'\n"), - floppyring); - - if ((status = copyfile(f, g, -1L)) < 0) - { fprintf(pgpout,PSTR("\n\007Could not read key from file '%s'.\n"), ringfile); - goto failed; - } - - if (moreflag && !(options & MAINT_SILENT)) - open_more(); - + setkrent(ringfile); setup_trust(); - if (mverbose) + maint_init_mem(); + if (mverbose || verbose) fprintf(pgpout, PSTR("\nPass 1: Looking for the \"ultimately-trusted\" keys...\n")); - rewind(g); - if ((status = maint_pass1(g)) < 0) + status = maint_read_data(ringfile); + if (sec_fp) { + fclose(sec_fp); + sec_fp = NULL; + } + if (status < 0) goto failed; - if (mverbose) + if (mverbose || verbose) fprintf(pgpout, PSTR("\nPass 2: Tracing signature chains...\n")); - rewind(g); - if ((status = maint_trace_chain(g)) < 0) + if ((status = maint_trace_chain()) < 0) goto failed; - if (!(options & MAINT_SILENT)) - { if (mverbose) - fprintf(pgpout, PSTR("\nPass 3: Comparing with original keyring...\n")); - rewind(f); - rewind(g); - if ((status = maint_cmpfiles(f, g)) < 0) - goto failed; - } else - status = 1; - - close_more(); - if (write_error(g)) - { status = -1; + if (verbose) + fprintf(pgpout, "\nPass 3: %s keyring...\n", + (check_only ? "Checking with" : "Updating")); + if ((status = maint_final(ringfile)) < 0) goto failed; - } - fclose(f); - fclose(g); - if (sec_fp) - fclose(sec_fp); - if (floppy_fp) - { fclose(floppy_fp); - floppy_fp = NULL; - } - -#ifdef DEBUG - if (check_only) - { - if (undefined_trust) - fprintf(pgpout, "\nKeyring contains %d fully validated key(s) with undefined trust.\n", undefined_trust); - else - if (status == 0) - fprintf(pgpout, "\nMaintenance check OK.\n"); - rmtemp(tmpring); - return(0); - } -#endif - if (status > 0) /* keyring is changed */ - { - if (options & MAINT_SILENT) - { /* implicit maintenance pass (after pgp -ka, -kr, -ks) */ - remove(ringfile); - if (savetemp(tmpring, ringfile) == NULL) - return(-1); - } - else - { /* explicit maintenance pass */ - fprintf(pgpout, PSTR("Update public keyring '%s' (Y/n)? "), ringfile); - if (!getyesno('y')) - { - rmtemp(tmpring); - return(0); - } - if (savetempbak(tmpring, ringfile)) - return(-1); - } - } - else - rmtemp(tmpring); - - return(0); + endkrent(); + maint_release_mem(); + return(status+undefined_trust); failed: - close_more(); - if (sec_fp) - fclose(sec_fp); - if (floppy_fp) - { fclose(floppy_fp); - floppy_fp = NULL; - } - fclose(f); - fclose(g); -#ifdef DEBUG - savetempbak(tmpring, "tmpring.pub"); -#else - rmtemp(tmpring); -#endif + if (verbose) + fprintf(pgpout, "maintenance pass: error exit = %d\n", status); + endkrent(); + maint_release_mem(); return(status); } /* maintenance */ +static struct pubkey *pklist, **pkhash = NULL; + +#define PK_HASHSIZE 256 /* must be power of 2 */ +#define PK_HASH(x) (*(byte *) (x) & (PK_HASHSIZE - 1)) + /* - * check if axiomatic keys are present in the secret keyring and - * clear userid and signature trust bytes + * get the pubkey struct for keyID from hash table, allocate a new + * node and insert in hash table if necessary. */ -int -maint_pass1(FILE *f) +static struct pubkey * +getpubkey(byte *keyID) +{ + struct pubkey *pk; + for (pk = pkhash[PK_HASH(keyID)]; pk; pk = pk->pk_hash) + if (memcmp(pk->pk_keyid, keyID, KEYFRAGSIZE) == 0) + return pk; + pk = allocn(sizeof(struct pubkey)); + memset(pk, 0, sizeof(struct pubkey)); + memcpy(pk->pk_keyid, keyID, KEYFRAGSIZE); + pk->pk_hash = pkhash[PK_HASH(keyID)]; + pkhash[PK_HASH(keyID)] = pk; + return pk; +} + +/* + * Read in keyring, a graph of keys, userids and signatures is built. + * Also check if axiomatic keys are present in the secret keyring and + * compare them with the floppy ring if this is requested. + */ +static int +maint_read_data(char *ringfile) { + FILE *f; int status; char userid[256]; byte keyID[KEYFRAGSIZE]; + byte sigkeyID[KEYFRAGSIZE]; byte ctb; byte keyctrl; boolean buckstop = FALSE, show_user = FALSE; - boolean compromised = FALSE; int buckstopcount = 0; - int usercount = 0; long keypos = 0; + int skip = 0; + struct pubkey *pk = NULL; + struct userid *id = NULL; + struct signature *sig = NULL; - while ((status = readkpacket(f, &ctb, userid, keyID, NULL)) != -1) - { - if (status == -3) + if ((f = fopen(ringfile,FOPRBIN)) == NULL) + { fprintf(pgpout,PSTR("\n\007Can't open key ring file '%s'\n"),ringfile); + return(-1); + } + + while ((status = readkpacket(f, &ctb, userid, keyID, sigkeyID)) != -1) { + if (status == -3 || status == -2) { + fclose(f); return(status); - if (status < 0) + } + if (status < 0 || is_ctb_type(ctb, CTB_CERT_SECKEY_TYPE)) { + skip = 1; /* version error or bad key */ continue; + } + if (skip) { + if (is_ctb_type(ctb, CTB_CERT_PUBKEY_TYPE)) + skip = 0; + else + continue; + } if (is_ctb_type(ctb, CTB_COMMENT_TYPE) || ctb == CTB_KEYCTRL) continue; - if (compromised && is_ctb_type(ctb, CTB_SKE_TYPE) && !usercount) - continue; /* compromise certificate */ + + if (pk && is_ctb_type(ctb, CTB_SKE_TYPE) && !pk->pk_userids) { + /* sig. cert before userids can only be compromise cert. */ + pk->pk_owntrust = KC_OWNERTRUST_NEVER; + continue; + } /* other packets should have trust byte */ - if (read_trust(f, &keyctrl) < 0) + if (read_trust(f, &keyctrl) < 0) { + fclose(f); return(ERR_NOTRUST); /* not a public keyring */ + } - if (is_ctb_type(ctb, CTB_CERT_PUBKEY_TYPE)) - { - if (compromised = is_compromised(f)) - keyctrl = KC_OWNERTRUST_NEVER; - if ((keyctrl & KC_BUCKSTOP)) - { - if (check_secretkey(f, keypos) == 0) - { + switch (ctb_type(ctb)) { + case CTB_CERT_PUBKEY_TYPE: + if (pk) + pk = pk->pk_next = getpubkey(keyID); + else + pk = pklist = getpubkey(keyID); + + if (pk->pk_next) { + fprintf(pgpout, PSTR("Keyring contains duplicate key: %s\n"), keyIDstring(keyID)); + fclose(f); + return -1; + } + + if ((keyctrl & KC_BUCKSTOP)) { + if (check_secretkey(f, keypos) == 0) { ++buckstopcount; buckstop = TRUE; if (mverbose) fprintf(pgpout, "* %s",keyIDstring(keyID)); - } - else - { /* not in secret keyring */ + } else { /* not in secret keyring */ keyctrl &= ~KC_BUCKSTOP; if (TRUST_LEV(keyctrl) == KC_OWNERTRUST_ULTIMATE) keyctrl = KC_OWNERTRUST_ALWAYS; @@ -299,44 +410,59 @@ maint_pass1(FILE *f) fprintf(pgpout, ". %s",keyIDstring(keyID)); } show_user = mverbose; - } - else - { + } else { buckstop = FALSE; show_user = FALSE; } - usercount = 0; keyctrl &= ~KC_VISITED; - - } - else if (ctb == CTB_USERID) - { - if (show_user) - { - if (usercount) /* more than one user ID */ + pk->pk_owntrust = keyctrl; + pk->pk_userids = id = NULL; + break; + case CTB_USERID_TYPE: + if (!pk) + break; + if (show_user) { + if (pk->pk_userids) /* more than one user ID */ fprintf(pgpout, " "); fprintf(pgpout, " %s\n", LOCAL_CHARSET(userid)); } + if (id) + id = id->uid_next = allocn(sizeof(struct userid)); + else + id = pk->pk_userids = allocn(sizeof(struct userid)); + + if (mverbose) + id->uid_userid = store_str(userid); keyctrl &= ~KC_LEGIT_MASK; if (buckstop) keyctrl |= KC_LEGIT_COMPLETE; else keyctrl |= KC_LEGIT_UNKNOWN; - ++usercount; - } - else if (is_ctb_type(ctb, CTB_SKE_TYPE)) - { - keyctrl = KC_SIGTRUST_UNDEFINED; - } - fseek(f, -1L, SEEK_CUR); - fwrite(&keyctrl, 1, 1, f); - fseek(f, 0L, SEEK_CUR); + id->uid_next = NULL; + id->uid_key = pk; + id->uid_legit = keyctrl; + id->uid_signatures = sig = NULL; + break; + case CTB_SKE_TYPE: + if (!pk || !id) + break; + if (sig) + sig = sig->sig_next = allocn(sizeof(struct signature)); + else + sig = id->uid_signatures = allocn(sizeof(struct signature)); + sig->sig_next = NULL; + sig->sig_uid = id; + sig->sig_from = getpubkey(sigkeyID); + sig->sig_nextfrom = sig->sig_from->pk_signed; + sig->sig_from->pk_signed = sig; + sig->sig_trust = keyctrl&KC_SIG_CHECKED; + break; + } /* switch ctb_type */ keypos = ftell(f); } if (buckstopcount == 0 && mverbose) - { fprintf(pgpout, PSTR("No ultimately-trusted keys.\n")); - } + fclose(f); return(0); } @@ -345,133 +471,344 @@ maint_pass1(FILE *f) * scan keyring for buckstop keys and start the recursive trace_sig_chain() * on them */ -int -maint_trace_chain(FILE *f) +static int +maint_trace_chain(void) { - int status; - char userid[256]; - byte keyID[KEYFRAGSIZE]; - long trustpos = 0; - byte ctb; - byte own_trust; - boolean buckstop = FALSE; + char *userid; + struct pubkey *pk; - while ((status = readkpacket(f, &ctb, userid, keyID, NULL)) != -1) - { - if (status == -3) - return(status); - if (status < 0) + for (pk = pklist; pk; pk = pk->pk_next) { + if (!(pk->pk_owntrust&KC_BUCKSTOP)) continue; + if (mverbose) + fprintf(pgpout, "* %s\n", LOCAL_CHARSET(pk->pk_userids->uid_userid)); + if (TRUST_LEV(pk->pk_owntrust) == KC_OWNERTRUST_UNDEFINED) { + userid = user_from_keyID(pk->pk_keyid); + SET_TRUST(&pk->pk_owntrust, ask_owntrust(userid, pk->pk_owntrust)); + } + trace_sig_chain(pk, 0); + } + return 0; +} - if (is_ctb_type(ctb, CTB_CERT_PUBKEY_TYPE)) - { - trustpos = ftell(f); - if ((status = read_trust(f, &own_trust)) < 0) - return(status); - buckstop = (own_trust & KC_BUCKSTOP) != 0; - userid[0] = '\0'; + +/* + * Find all signatures made with the key pk. + * If a trusted signature makes a key fully legit then signatures made + * with this key are also recursively traced on down the tree. + * + * depth is the level of recursion, it is used to indent the userIDs + * and to check if we don't exceed the limit "max_cert_depth" + * + * NOTE: a signature made with a key with pk_depth == max_cert_depth will + * not be counted here to limit the maximum chain length, but will be + * counted when the validity of a key is computed in maint_final() + */ +static int +trace_sig_chain(struct pubkey *pk, int depth) +{ + int d, trust_count = 0; + int counts[MAX_DEPTH]; + struct signature *sig, *s; + struct pubkey *p; + struct userid *id; + + assert(depth <= max_cert_depth); + if (pk->pk_depth && pk->pk_depth <= depth) + return 0; + pk->pk_depth = depth; + + /* Should we ask for trust. If this key is legit, then go for + * it! Ask the user.... + */ + if (TRUST_LEV(pk->pk_owntrust) == KC_OWNERTRUST_UNDEFINED) + for (id = pk->pk_userids; id; id = id->uid_next) { + compute_legit(id); + if ((id->uid_legit & KC_LEGIT_MASK) == + KC_LEGIT_COMPLETE) { + SET_TRUST(&pk->pk_owntrust, + ask_owntrust(user_from_keyID(pk->pk_keyid), + pk->pk_owntrust)); + break; + } } - else if (ctb == CTB_USERID) - { - if (buckstop) - { - buckstop = FALSE; /* only for first user id */ - if (mverbose) - fprintf(pgpout, "* %s\n", LOCAL_CHARSET(userid)); - if (TRUST_LEV(own_trust) == KC_OWNERTRUST_UNDEFINED) - { SET_TRUST(&own_trust, ask_owntrust(userid, own_trust)); - write_trust_pos(f, own_trust, trustpos); - } - if (trace_sig_chain(f, keyID, TRUST_LEV(own_trust), 0) < 0) - return(-1); + /* Return if I haven't signed anyone's keys, since I + * don't need to check any further.. -warlord 93-04-11 + */ + if (!pk->pk_signed) + return 0; + +#ifdef DEBUG + if (mverbose) + fprintf(pgpout, "%*s%d-v %s\n", 2*depth, "", depth, pk->pk_userids->uid_userid); +#endif + + /* all keys signed by pk */ + for (sig = pk->pk_signed; sig; sig = sig->sig_nextfrom) { + if (mverbose) + fprintf(pgpout, "%*s > %s\n", 2*depth, "", LOCAL_CHARSET(sig->sig_uid->uid_userid)); + + /* copy trust from signator */ + SET_TRUST(&sig->sig_trust, TRUST_LEV(pk->pk_owntrust)); + sig->sig_trust |= KC_CONTIG; /* CONTIG bit currently unused */ + + p = sig->sig_uid->uid_key; /* this key signed by pk */ + if (p->pk_owntrust & KC_BUCKSTOP) + continue; /* will be handled from main loop */ + if (p->pk_depth && p->pk_depth <= depth+1) + continue; /* already handled this key at a lower level */ + + for (d = 0; d < max_cert_depth; ++d) + counts[d] = 0; + for (s = sig->sig_uid->uid_signatures; s; s = s->sig_next) { + d = s->sig_from->pk_depth; + if (d < max_cert_depth) + counts[d] += TRUST_FAC(s->sig_trust); + } + /* + * find a combination of signatures that will make the key + * valid through the shortest cert. path. + */ + trust_count = 0; + for (d = 0; d < max_cert_depth; ++d) { + trust_count += counts[d]; + if (trust_count >= complete_min) { + trace_sig_chain(p, d+1); + break; } } } - return(0); + +#ifdef DEBUG + if (mverbose) + fprintf(pgpout, "%*s%d-^ %s\n", 2*depth, "", depth, pk->pk_userids->uid_userid); +#endif + return 0; } +/* + * compute validity of userid/key pair, the number of signatures and the + * trust level of these signatures determines the validity. + */ +static void +compute_legit(struct userid *id) +{ + struct signature *s; + int trust_count, legit; + + if (id->uid_key->pk_owntrust & KC_BUCKSTOP) + legit = KC_LEGIT_COMPLETE; + else { + trust_count = 0; + for (s = id->uid_signatures; s; s = s->sig_next) + trust_count += TRUST_FAC(s->sig_trust); + + if (trust_count == 0) + legit = KC_LEGIT_UNKNOWN; + else if (trust_count < marginal_min) + legit = KC_LEGIT_UNTRUSTED; + else if (trust_count < complete_min) + legit = KC_LEGIT_MARGINAL; + else + legit = KC_LEGIT_COMPLETE; + } + id->uid_legit = (id->uid_legit & ~KC_LEGIT_MASK) | legit; +} /* * check if the maintenance pass changed anything * returns 0 if files f and g are equal and the number of changed * trust bytes if the files are different or a negative value on error */ -int -maint_cmpfiles(FILE *f, FILE *g) +static int +maint_final(char *ringfile) { int status; + FILE *f; + long trust_pos = 0; char userid[256]; byte keyID[KEYFRAGSIZE]; byte sigkeyID[KEYFRAGSIZE]; byte ctb; - byte kc_orig, kc_new; - int usercount = 0; + byte kc_orig, kc_new = 0, mask; int changed = 0; + int skip = 0; + struct pubkey *pk; + struct userid *id = NULL; + struct signature *sig = NULL; - fprintf(pgpout, PSTR(" KeyID Trust Validity User ID\n")); - while ((status = readkpacket(f, &ctb, userid, keyID, sigkeyID)) != -1) - { - if (status == -3) + if (check_only) + f = fopen(ringfile,FOPRBIN); + else + f = fopen(ringfile,FOPRWBIN); + if (f == NULL) + { fprintf(pgpout,PSTR("\n\007Can't open key ring file '%s'\n"),ringfile); + return(-1); + } + + pk = pklist; + while ((status = readkpacket(f, &ctb, userid, keyID, sigkeyID)) != -1) { + if (status == -3 || status == -2) break; - if (status < 0) - continue; - if (is_ctb_type(ctb, CTB_COMMENT_TYPE)) - continue; - /* unexpected trust packet, probably after version error */ - if (ctb == CTB_KEYCTRL) + if (status < 0 || is_ctb_type(ctb, CTB_CERT_SECKEY_TYPE)) { + skip = 1; continue; + } + if (skip) { + if (is_ctb_type(ctb, CTB_CERT_PUBKEY_TYPE)) + skip = 0; + else + continue; + } + if (is_ctb_type(ctb, CTB_CERT_PUBKEY_TYPE) || + is_ctb_type(ctb, CTB_SKE_TYPE) || ctb == CTB_USERID) { + trust_pos = ftell(f); + if (read_trust(f, &kc_orig) < 0) { + status = ERR_NOTRUST; + if (is_ctb_type(ctb, CTB_SKE_TYPE)) + continue; /* skip compr. cert. */ + else + break; + } + } + switch (ctb_type(ctb)) { + case CTB_CERT_PUBKEY_TYPE: + assert(pk && !memcmp(pk->pk_keyid, keyID, KEYFRAGSIZE)); + assert(!sig && !id); + id = pk->pk_userids; + kc_new = pk->pk_owntrust; +#ifdef DEBUG + if (mverbose) + fprintf(pgpout, " ------ %d\n", pk->pk_depth); +#endif + pk = pk->pk_next; + mask = KC_OWNERTRUST_MASK|KC_BUCKSTOP; + break; + case CTB_USERID_TYPE: + assert(id && !sig); + sig = id->uid_signatures; + compute_legit(id); + kc_new = id->uid_legit; +#ifdef DEBUG + if (mverbose) + fprintf(pgpout, "%c %02x %02x %s\n", ' ' + (kc_new != kc_orig), + kc_orig, kc_new, id->uid_userid); +#endif + id = id->uid_next; + mask = KC_LEGIT_MASK; + break; + case CTB_SKE_TYPE: + assert(sig); + assert(!memcmp(sig->sig_from->pk_keyid, sigkeyID, KEYFRAGSIZE)); + kc_new = sig->sig_trust; +#ifdef DEBUG + if (mverbose && sig->sig_from->pk_userids) + fprintf(pgpout, "%c %02x %02x %s\n", ' ' + (kc_new != kc_orig), + kc_orig, kc_new, sig->sig_from->pk_userids->uid_userid); +#endif + sig = sig->sig_next; + mask = KC_SIGTRUST_MASK|KC_CONTIG; + break; + default: + mask = 0; + } + if ((kc_new&mask) != (kc_orig&mask)) { + if (!check_only) + write_trust_pos(f, kc_new, trust_pos); + ++changed; + } + } + fclose(f); + if (status < -1) /* -1 is OK, EOF */ + return(status); + if (pk || sig || id) { + fprintf(pgpout, "maint_final: internal error\n"); + return -1; + } + return(changed); +} /* maint_final */ - /* other packets should have trust byte */ - fseek(g, ftell(f), SEEK_SET); - if (read_trust(f, &kc_orig) < 0 || read_trust(g, &kc_new) < 0) - { status = ERR_NOTRUST; + +int +maint_list(char *ringfile) +{ + int status; + FILE *f; + char userid[256]; + byte keyID[KEYFRAGSIZE]; + byte sigkeyID[KEYFRAGSIZE]; + char *signator; + char tchar = 0; + byte ctb, kc; + int owntrust = 0; + int usercount = 0; + + if ((f = fopen(ringfile,FOPRBIN)) == NULL) + { fprintf(pgpout,PSTR("\n\007Can't open key ring file '%s'\n"),ringfile); + return(-1); + } + init_trust_lst(); + setkrent(ringfile); + init_userhash(); + + fprintf(pgpout, PSTR(" KeyID Trust Validity User ID\n")); + while ((status = readkpacket(f, &ctb, userid, keyID, sigkeyID)) != -1) { + if (status == -3 || status == -2) break; - } + if (status < 0) + continue; - if (is_ctb_type(ctb, CTB_CERT_PUBKEY_TYPE)) - { - if (is_compromised(f)) - { - fprintf(pgpout, "# "); - nextkeypacket(f, &ctb); /* skip compromise certificate */ - nextkeypacket(g, &ctb); + if (is_ctb_type(ctb, CTB_CERT_PUBKEY_TYPE) || + is_ctb_type(ctb, CTB_SKE_TYPE) || ctb == CTB_USERID) { + if (read_trust(f, &kc) < 0) { + status = ERR_NOTRUST; + /* compromise cert. don't have trust byte */ + if (!is_ctb_type(ctb, CTB_SKE_TYPE)) + break; } - else if ((kc_new & KC_BUCKSTOP)) - fprintf(pgpout, "* "); - else - fprintf(pgpout, " "); + } - fprintf(pgpout, "%s ",keyIDstring(keyID)); - changed += show_trust(kc_new, kc_orig); + switch (ctb_type(ctb)) { + case CTB_CERT_PUBKEY_TYPE: + tchar = (kc & KC_BUCKSTOP ? '*' : ' '); + owntrust = TRUST_LEV(kc); usercount = 0; userid[0] = '\0'; - } - else if (ctb == CTB_USERID) - { - if (usercount) /* more than one user ID */ + break; + case CTB_USERID_TYPE: + if (!usercount) { /* first userid */ + fprintf(pgpout, "%c %s ", tchar, keyIDstring(keyID)); + fprintf(pgpout, " %-*s", trustlst_len, trust_lst[owntrust]); + } else fprintf(pgpout, " %*s ", trustlst_len, ""); + fprintf(pgpout, " %-*s", legitlst_len, legit_lst[kc&KC_LEGIT_MASK]); + if (usercount) + putc(' ', pgpout); ++usercount; - changed += show_legit(kc_new, kc_orig); fprintf(pgpout, "%s\n", LOCAL_CHARSET(userid)); - } - else if (is_ctb_type(ctb, CTB_SKE_TYPE)) - { - if (kc_new & KC_CONTIG) - fprintf(pgpout, "c "); - else - fprintf(pgpout, " "); - changed += show_trust(kc_new, kc_orig); + break; + case CTB_SKE_TYPE: + if (!usercount) { /* sig before userid: compromise cert. */ + tchar = '#'; + break; + } + fprintf(pgpout, "%c ", (kc & KC_CONTIG) ? 'c' : ' '); + fprintf(pgpout, " %-*s", trustlst_len, trust_lst[TRUST_LEV(kc)]); fprintf(pgpout, "%*s ", legitlst_len, ""); - show_userid(f, sigkeyID); + if ((signator = user_from_keyID(sigkeyID)) == NULL) + fprintf(pgpout, "(KeyID: %s)\n",keyIDstring(sigkeyID)); + else + fprintf(pgpout, "%s\n", LOCAL_CHARSET(signator)); + break; } } - if (status >= -1 && changed) - fprintf(pgpout, PSTR("\n%d \"trust parameter(s)\" changed.\n"), changed); + endkrent(); + fclose(f); if (status < -1) /* -1 is OK, EOF */ return(status); - return(changed); -} /* maint_cmpfiles */ + return(0); +} /* maint_list */ /* @@ -480,7 +817,7 @@ maint_cmpfiles(FILE *f, FILE *g) * the longest translated string. */ void -init_trust_lst() +init_trust_lst(void) { static int initialized = 0; int i, len; @@ -511,165 +848,6 @@ init_trust_lst() initialized = 1; } /* init_trust_lst */ -/* - * show the trust level of trust byte kc_new. If it is - * different from the old level (kc_orig) the print a '!' before - * the level string - */ -int -show_trust(byte kc_new, byte kc_orig) -{ -#ifdef DEBUG - fprintf(pgpout, " %02x", kc_new); - if (kc_new != kc_orig) - { - fprintf(pgpout, " (%02x) ", kc_orig); - return(1); - } - fprintf(pgpout, " "); - return(0); -#else - init_trust_lst(); - fprintf(pgpout, "%c%-*s", (kc_new == kc_orig ? ' ' : '!'), - trustlst_len, trust_lst[TRUST_LEV(kc_new)]); - return(kc_new != kc_orig); -#endif -} /* show_trust */ - -/* - * show_legit is the same as show_trust, only it uses legit_lst - * for messages - */ -int -show_legit(byte kc_new, byte kc_orig) -{ -#ifdef DEBUG - return(show_trust(kc_new, kc_orig)); -#else - init_trust_lst(); - kc_new &= KC_LEGIT_MASK; - kc_orig &= KC_LEGIT_MASK; - fprintf(pgpout, "%c%-*s", (kc_new == kc_orig ? ' ' : '!'), - legitlst_len, legit_lst[kc_new]); - return(kc_new != kc_orig); -#endif -} /* show_legit */ - - -/* - * Find all signatures made with a key, fromID is the keyID of this key - * owner_trust is the trust level of this key. - * If a trusted signature makes a key fully legit then signatures made - * with this key are also recursively traced on down the tree. - * - * The file f is the public keyring, it must be open for update. - * depth is the level of recursion, it is used to indent the userIDs - * and to check if we don't exceed the limit "max_cert_depth" - * - * fromID should be the keyID of a fully validated key. - */ -int -trace_sig_chain(FILE *f, byte *fromID, int owner_trust, int depth) -{ - long filepos; - int status; - int trust_count = 0; - byte keyID[KEYFRAGSIZE]; - byte sigkeyID[KEYFRAGSIZE]; - byte ctb; - byte own_trust, sig_trust; - char userid[256]; - int sig_count = 0; - int compromised = 0; - long usertrustpos = 0; - long ownt_pos = 0; - boolean fromsig = FALSE; - -#ifdef DEBUG - if (mverbose) - fprintf(pgpout, "%*s--- %s\n", 2*depth, "", keyIDstring(fromID)); -#endif - filepos = ftell(f); - rewind(f); - userid[0] = '\0'; - while ((status = readkpacket(f, &ctb, userid, keyID, sigkeyID)) != -1) - { - if (status == -3) - return(status); - if (status < 0) - continue; - - if (is_ctb_type(ctb, CTB_CERT_PUBKEY_TYPE)) - { - compromised = is_compromised(f); - ownt_pos = ftell(f); - if (read_trust(f, &own_trust)) - return(ERR_NOTRUST); - userid[0] = '\0'; - } - else if (ctb == CTB_USERID) - { - usertrustpos = ftell(f); - trust_count = 0; - fromsig = FALSE; - } - else if (is_ctb_type(ctb, CTB_SKE_TYPE)) - { /* signature packet */ - if (compromised) - continue; - read_trust(f, &sig_trust); - /* we're looking for signatures made by "fromID" */ - if (memcmp(sigkeyID, fromID, sizeof(sigkeyID)) == 0) - { - ++sig_count; - if (mverbose) - fprintf(pgpout, "%*s > %s\n", 2*depth, "", - LOCAL_CHARSET(userid)); - if (TRUST_LEV(sig_trust) != owner_trust) - { - SET_TRUST(&sig_trust, owner_trust); - } - sig_trust |= KC_CONTIG; - fseek(f, -1L, SEEK_CUR); - fwrite(&sig_trust, 1, 1, f); - fseek(f, 0L, SEEK_CUR); - fromsig = TRUE; - } - - if (sig_trust & KC_CONTIG) - { - trust_count += TRUST_FAC(sig_trust); - - if (fromsig) /* fromID has signed this key */ - { - switch (set_legit(f, usertrustpos, trust_count)) - { - case -1: return(-1); - case 1: /* this signature made the key fully valid */ - if ((own_trust & (KC_BUCKSTOP|KC_VISITED)) == 0) - { - if (TRUST_LEV(own_trust) == KC_OWNERTRUST_UNDEFINED) - SET_TRUST(&own_trust, ask_owntrust(userid, own_trust)); - if (depth < max_cert_depth) - own_trust |= KC_VISITED; - write_trust_pos(f, own_trust, ownt_pos); - if (depth < max_cert_depth) - trace_sig_chain(f, keyID, TRUST_LEV(own_trust), depth+1); - } - } - } - } - } - } -#ifdef DEBUG - if (mverbose && sig_count == 0) /* no signatures from this user */ - fprintf(pgpout, PSTR("%*s (No signatures)\n"), 2*depth, ""); - if (mverbose) - fprintf(pgpout, "%*s--- %s\n", 2*depth, "", keyIDstring(fromID)); -#endif - fseek(f, filepos, SEEK_SET); - return(0); -} /* trace_sig_chain */ /* @@ -680,7 +858,7 @@ trace_sig_chain(FILE *f, byte *fromID, i * returns 1 if the key was not found, -2 if the keys were different * and 0 if the keys compared OK */ -int +static int check_secretkey(FILE *f, long keypos) { int status = -1; @@ -783,8 +961,8 @@ ex: /* * setup tables for trust scoring. */ -void -setup_trust() +static void +setup_trust(void) { /* initialize trust table */ if (marg_min == 0) /* marginally trusted signatures are ignored */ { @@ -805,49 +983,15 @@ setup_trust() } /* setup_trust */ -/* - * set valid level of a userid, computed from trust_count. - * returns 1 if the key/userid pair is fully legit. - */ -int -set_legit(FILE *f, long trustpos, int trust_count) -{ - long filepos; - byte keyctrl; - - filepos = ftell(f); - fseek(f, trustpos, SEEK_SET); /* user id trust byte */ - if (read_trust(f, &keyctrl) < 0) - return(-1); - - keyctrl &= ~KC_LEGIT_MASK; - if (trust_count < marginal_min) - keyctrl |= KC_LEGIT_UNTRUSTED; - else if (trust_count < complete_min) - keyctrl |= KC_LEGIT_MARGINAL; - else - keyctrl |= KC_LEGIT_COMPLETE; - - fseek(f, trustpos, SEEK_SET); - write_trust(f, keyctrl); - - fseek(f, filepos, SEEK_SET); - - if ((keyctrl & KC_LEGIT_MASK) == KC_LEGIT_COMPLETE) - return(1); - else - return(0); -} /* set_legit */ - - int ask_owntrust(char *userid, byte cur_trust) /* Ask for a wetware decision from the human on how much to trust this key's owner to certify other keys. Returns trust value. */ { char buf[8]; - if (check_only) - { ++undefined_trust; + if (check_only || filter_mode || batchmode) + { /* not interactive */ + ++undefined_trust; /* We complete/undefined. Why? */ return(KC_OWNERTRUST_UNDEFINED); } @@ -873,93 +1017,6 @@ to act as an introducer and certify othe } /* ask_owntrust */ -/* - * compare all key packets in keyring file f with file g - * XXX: untested - */ -int -keyring_cmp(FILE *f, FILE *g) -{ - int status, error = 0, badkeys = 0; - byte ctb; - byte keyID[KEYFRAGSIZE]; - char userid[256]; - long pktlen, keypos = 0; - - rewind(f); - while ((status = readkpacket(f, &ctb, userid, keyID, NULL)) != -1) - { - if (status == -3) - return(status); - if (status < 0) - continue; - if (is_key_ctb(ctb)) - { - pktlen = ftell(f) - keypos; - fseek(f, keypos, SEEK_SET); - if (lookup_by_keyID(g, keyID) < 0) - { - error = 1; - continue; - } - while (--pktlen >= 0 && getc(f) == getc(g)) ; - if (pktlen != -1) - { - error = 2; - ++badkeys; - } - - } - if (error && ctb == CTB_USERID) - { - switch (error) { - case 1: fprintf(pgpout, PSTR("\n\007Key for user ID \"%s\"\n\ -is not present in backup keyring\n"), LOCAL_CHARSET(userid)); - case 2: fprintf(pgpout, PSTR("\n\007Key for user ID \"%s\"\n\ -does not match key in backup keyring\n"), LOCAL_CHARSET(userid)); - } - error = 0; - } - keypos = ftell(f); - } - if (error) - status = -5; - return(status < 0 ? status : badkeys); -} /* keyring_cmp */ - - -/* - * stripped down version of readkeypacket(), the output userid - * is a null terminated string. - */ -int -readkpacket(FILE *f, byte *ctb, char *userid, byte *keyID, byte *sigkeyID) -{ - int status; - unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; - - status = readkeypacket(f, FALSE, ctb, NULL, userid, n, e, - NULL, NULL, NULL, NULL, sigkeyID, NULL); - - if (status < 0) - { -#ifdef DEBUG - if (status < -1) - fprintf(stderr, "readkeypacket returned %d\n", status); -#endif - return(status); - } - - if (keyID && is_key_ctb(*ctb)) - extract_keyID(keyID, n); - - if (userid && *ctb == CTB_USERID) - PascalToC(userid); - - return(0); -} /* readkpacket */ - - /* * scan keyfile f for keyID srch_keyID. * returns the file position of the key if it is found, and sets the @@ -977,7 +1034,7 @@ lookup_by_keyID(FILE *f, byte *srch_keyI rewind(f); while ((status = readkpacket(f, &ctb, NULL, keyID, NULL)) != -1) { - if (status == -3) + if (status == -3 || status == -2) break; if (status < 0) continue; @@ -991,7 +1048,6 @@ lookup_by_keyID(FILE *f, byte *srch_keyI return(status); } /* lookup_by_keyID */ - /* * look up the key matching "keyID" and print the first userID * of this key. File position of f is saved. @@ -1018,73 +1074,135 @@ show_userid(FILE *f, byte *keyID) fseek(f, filepos, SEEK_SET); } /* show_userid */ + +/* + * messages printed by show_key() + */ +static char *owntrust_msg[] = { + "", /* Just don't say anything in this case */ + "", + _PSTR("This user is untrusted to certify other keys.\n"), + "", /* reserved */ + "", /* reserved */ + _PSTR("This user is generally trusted to certify other keys.\n"), + _PSTR("This user is completely trusted to certify other keys.\n"), + _PSTR("This axiomatic key is ultimately trusted to certify other keys.\n"), +}; +static char *keylegit_msg[] = { + _PSTR("This key/userID association is not certified.\n"), + _PSTR("This key/userID association is not certified.\n"), + _PSTR("This key/userID association is marginally certified.\n"), + _PSTR("This key/userID association is fully certified.\n"), +}; +static char *sigtrust_msg[] = { + _PSTR(" Questionable certification from:\n "), + _PSTR(" Questionable certification from:\n "), + _PSTR(" Untrusted certification from:\n "), + "", /* reserved */ + "", /* reserved */ + _PSTR(" Generally trusted certification from:\n "), + _PSTR(" Completely trusted certification from:\n "), + _PSTR(" Axiomatically trusted certification from:\n "), +}; + /* * show the key in file f at file position keypos. * 'what' controls the info that will be shown: * SHOW_TRUST: show trust byte info * SHOW_SIGS: show signatures - * these constants can be or'ed to get both + * SHOW_HASH: show key fingerprint + * these constants can be or'ed + * + * 'what' can also be SHOW_LISTFMT to get the same format as for pgp -kv + * no signatures or extra userids will be printed in this case. + * + * 'what' can be SHOW_CHANGE, in which case it will take the keyID and + * call show_update(); */ int show_key(FILE *f, long keypos, int what) { - int status; + int status, keystatus = -1; long filepos; char userid[256]; unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; byte sigkeyID[KEYFRAGSIZE]; word32 timestamp; - byte ctb, keyctrl; + byte ctb, keyctb = 0, keyctrl; int userids = 0; boolean print_trust = FALSE; + byte hash[16]; int precision = global_precision; + int compromised = 0; + int disabled = 0; filepos = ftell(f); fseek(f, keypos, SEEK_SET); while ((status = readkeypacket(f, FALSE, &ctb, (byte *)×tamp, userid, - n, e, NULL, NULL, NULL, NULL, sigkeyID, &keyctrl)) == 0) + n, e, NULL, NULL, NULL, NULL, sigkeyID, &keyctrl)) != -1) { + if (status == -2 || status == -3) + break; if (is_key_ctb(ctb)) { if (userids) break; + if (what & SHOW_HASH) + getKeyHash(hash, n, e); + keyctb = ctb; + keystatus = status; /* remember status, could be version error */ } else if (ctb == CTB_KEYCTRL) /* trust bytes only in public keyrings */ { + if (keystatus >= 0 && !userids) /* key packet trust byte */ + if (keyctrl & KC_DISABLED) + disabled = 1; if (what & SHOW_TRUST) print_trust = TRUE; } else if (ctb == CTB_USERID) { if (userids == 0) { PascalToC(userid); /* for display */ + ++userids; + if (what & SHOW_CHANGE) { + show_update(key2IDstring(n)); + break; + } + if (what & SHOW_LISTFMT) { + if (is_ctb_type(keyctb,CTB_CERT_PUBKEY_TYPE)) + fprintf(pgpout,"pub"); + else if (is_ctb_type(keyctb,CTB_CERT_SECKEY_TYPE)) + fprintf(pgpout,"sec"); + else + fprintf(pgpout,"???"); + if (keystatus < 0) + fprintf(pgpout,"? "); + else if (compromised) + fprintf(pgpout,"# "); + else if (disabled) + fprintf(pgpout,"- "); + else + fprintf(pgpout," "); + fprintf(pgpout,"%4d/%s %s ", + countbits(n),key2IDstring(n),cdate(×tamp)); + fprintf(pgpout,"%s\n",LOCAL_CHARSET(userid)); + break; /* only print default userid */ + } fprintf(pgpout,PSTR("\nKey for user ID: %s\n"), LOCAL_CHARSET(userid)); fprintf(pgpout,PSTR("%d-bit key, Key ID %s, created %s\n"), countbits(n), key2IDstring(n), cdate(×tamp) ); - if (print_trust) - { - switch (TRUST_LEV(keyctrl)) - { - case KC_OWNERTRUST_UNDEFINED: - case KC_OWNERTRUST_UNKNOWN: - /* Just don't say anything in this case */ - break; - case KC_OWNERTRUST_NEVER: /* untrusted */ - fprintf(pgpout, PSTR("This user is untrusted to certify other keys.\n")); - break; - case KC_OWNERTRUST_USUALLY: /* marginal trust */ - fprintf(pgpout, PSTR("This user is generally trusted to certify other keys.\n")); - break; - case KC_OWNERTRUST_ALWAYS: /* complete trust */ - fprintf(pgpout, PSTR("This user is completely trusted to certify other keys.\n")); - break; - case KC_OWNERTRUST_ULTIMATE: /* axiomatic, ultimate trust */ - fprintf(pgpout, PSTR("This axiomatic key is ultimately trusted to certify other keys.\n")); - break; - default: - break; - } /* switch */ - } - ++userids; + if (keystatus == -4) + fprintf(pgpout,PSTR("Bad key format.\n")); + else if (keystatus == -6) + fprintf(pgpout,PSTR("Unrecognized version.\n")); + else if (what & SHOW_HASH) + printKeyHash(hash, FALSE); + if (compromised) + fprintf(pgpout, PSTR("Key has been revoked.\n")); + if (disabled) + fprintf(pgpout, PSTR("Key is disabled.\n")); + if (print_trust && *owntrust_msg[TRUST_LEV(keyctrl)] != '\0') + fprintf(pgpout, PSTR (owntrust_msg[TRUST_LEV(keyctrl)])); } else { PascalToC(userid); @@ -1093,57 +1211,28 @@ show_key(FILE *f, long keypos, int what) fprintf(pgpout,PSTR("Also known as: %s\n"), LOCAL_CHARSET(userid)); } - if (print_trust) - { + if (print_trust) { read_trust(f, &keyctrl); - switch (keyctrl & KC_LEGIT_MASK) - { - case 0: /* undefined certification level */ - case 1: /* uncertified */ - fprintf(pgpout, PSTR("This key/userID association is not certified.\n")); - break; - case 2: /* marginal certification */ - fprintf(pgpout, PSTR("This key/userID association is marginally certified.\n")); - break; - case 3: /* complete certification */ - fprintf(pgpout, PSTR("This key/userID association is fully certified.\n")); - break; - default: /* can't get here, right? */ - break; - } /* switch */ + fprintf(pgpout, PSTR (keylegit_msg[keyctrl&KC_LEGIT_MASK])); } /* print_trust */ } - else if ((what & SHOW_SIGS) && is_ctb_type(ctb, CTB_SKE_TYPE)) + else if (is_ctb_type(ctb, CTB_SKE_TYPE)) { - if (print_trust) - { - read_trust(f, &keyctrl); - switch (TRUST_LEV(keyctrl)) - { - case KC_SIGTRUST_UNDEFINED: /* undefined trust level */ - case KC_SIGTRUST_UNKNOWN: - /* Just don't say anything in this case */ - fprintf(pgpout, PSTR(" Questionable certification from:\n ")); - break; - case KC_SIGTRUST_UNTRUSTED: /* untrusted */ - fprintf(pgpout, PSTR(" Untrusted certification from:\n ")); - break; - case KC_SIGTRUST_MARGINAL: /* marginal trust */ - fprintf(pgpout, PSTR(" Generally trusted certification from:\n ")); - break; - case KC_SIGTRUST_COMPLETE: /* complete trust */ - fprintf(pgpout, PSTR(" Completely trusted certification from:\n ")); - break; - case KC_SIGTRUST_ULTIMATE: /* axiomatic, ultimate trust */ - fprintf(pgpout, PSTR(" Axiomatically trusted certification from:\n ")); - break; - default: /* can't get here, right? */ - break; - } /* switch */ + if (userids == 0) + compromised = 1; + if (what & SHOW_CHANGE) { + show_update(key2IDstring(n)); + break; + } + if (what & SHOW_SIGS) { + if (print_trust) { + read_trust(f, &keyctrl); + fprintf(pgpout, PSTR (sigtrust_msg[TRUST_LEV(keyctrl)])); + } + else + fprintf(pgpout, PSTR(" Certified by: ")); + show_userid(f, sigkeyID); } - else - fprintf(pgpout, PSTR(" Certified by: ")); - show_userid(f, sigkeyID); } } if (status == -1 && userids) @@ -1153,6 +1242,47 @@ show_key(FILE *f, long keypos, int what) return(status); } /* show_key */ +/* show_update -- this function just prints an update message to + * pgpout to inform the user that an update happened. + */ +void +show_update(char *s) +{ + fprintf(pgpout, "Updated keyID: 0x%s\n", s); +} + + +/* + * stripped down version of readkeypacket(), the output userid + * is a null terminated string. + */ +int +readkpacket(FILE *f, byte *ctb, char *userid, byte *keyID, byte *sigkeyID) +{ + int status; + unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; + + status = readkeypacket(f, FALSE, ctb, NULL, userid, n, e, + NULL, NULL, NULL, NULL, sigkeyID, NULL); + + if (status < 0) + { +#ifdef DEBUG + if (status < -1) + fprintf(stderr, "readkeypacket returned %d\n", status); +#endif + return(status); + } + + if (keyID && is_key_ctb(*ctb)) + extract_keyID(keyID, n); + + if (userid && *ctb == CTB_USERID) + PascalToC(userid); + + return(0); +} /* readkpacket */ + /* * write trust byte "keyctrl" to file f at file position "pos" @@ -1175,6 +1305,7 @@ write_trust_pos(FILE *f, byte keyctrl, l * returns -1 on EOF, -3 on corrupt input, and ERR_NOTRUST if * the packet was not a trust byte (this can be used to check if * a file is a keyring (with trust bytes) or a keyfile). + * The current file position is left unchanged in this case. */ int read_trust(FILE *f, byte *keyctrl) @@ -1183,11 +1314,11 @@ read_trust(FILE *f, byte *keyctrl) if (fread(buf, 1, 3, f) != 3) return -1; - if (buf[0] != CTB_KEYCTRL) - { - if (is_ctb(buf[0])) + if (buf[0] != CTB_KEYCTRL) { + if (is_ctb(buf[0])) { + fseek(f, -3L, SEEK_CUR); return(ERR_NOTRUST); - else + } else return(-3); /* bad data */ } if (buf[1] != 1) /* length must be 1 */ @@ -1196,3 +1327,250 @@ read_trust(FILE *f, byte *keyctrl) *keyctrl = buf[2]; return(0); } /* read_trust */ + + + +/****** userid lookup ******/ + +#define HASH_ALLOC (ALLOC_UNIT / sizeof(struct hashent)) + +static char * store_str(char *str); +static VOID * allocbuf(int size); +static void freebufpool(); + +static struct hashent { + struct hashent *next; + byte keyID[KEYFRAGSIZE]; + char *userid; +} **hashtbl = NULL, *hashptr; + +static char *strptr; +static int strleft = 0; +static int hashleft = 0; +static int nleft = 0; + +#define MAXKR 8 /* max. number of keyrings for user_from_keyID() */ +static char *krnames[MAXKR]; +static int nkr = 0; +/* + * Lookup userid by keyID without using the in-memory hash table. + */ +static char * +_user_from_keyID(byte *srch_keyID) +{ + FILE *f; + int i, status, found = 0; + byte keyID[KEYFRAGSIZE]; + static char userid[256]; + byte ctb; + + /* search all keyfiles set with setkrent() */ + for (i = 0; !found && i < nkr; ++i) { + if ((f = fopen(krnames[i], FOPRBIN)) == NULL) + continue; + while ((status = readkpacket(f, &ctb, userid, keyID, NULL)) != -1) { + if (status == -2 || status == -3) + break; + if (is_key_ctb(ctb) && memcmp(keyID, srch_keyID, KEYFRAGSIZE) == 0) + found = 1; + if (found && ctb == CTB_USERID) + break; + } + fclose(f); + } + return(found ? userid : NULL); +} + +/* + * Lookup userid by keyID, use hash table if initialized. + */ +char * +user_from_keyID(byte *keyID) +{ + struct hashent *p; + + if (!hashtbl) + return _user_from_keyID(keyID); + for (p = hashtbl[PK_HASH(keyID)]; p; p = p->next) + if (memcmp(keyID, p->keyID, KEYFRAGSIZE) == 0) + return p->userid; + return NULL; +} + +/* + * add keyfile to userid hash table, userids are added, endkrent() clears + * the hash table. + */ +int +setkrent(char *keyring) +{ + int i; + char pubring[MAX_PATH]; + + assert(nkr < MAXKR); + if (keyring == NULL) { + buildfilename(pubring, PUBLIC_KEYRING_FILENAME); + keyring = pubring; + } + for (i = 0; i < nkr; ++i) + if (strcmp(keyring, krnames[i]) == 0) + return 0; /* duplicate name */ + krnames[nkr++] = store_str(keyring); + return 0; +} + +void +endkrent(void) +{ + hashleft = strleft = 0; + hashtbl = NULL; + nkr = 0; + freebufpool(); +} + +/* + * create userid hash table, read all files set with setkrent() + */ +int +init_userhash(void) +{ + FILE *f; + int status, i; + byte keyID[KEYFRAGSIZE]; + char userid[256]; + byte ctb; + int keyflag; + + if (!hashtbl) { + hashtbl = allocbuf(PK_HASHSIZE * sizeof(struct hashent *)); + memset(hashtbl, 0, PK_HASHSIZE * sizeof(struct hashent *)); + } + for (i = 0; i < nkr; ++i) { + if ((f = fopen(krnames[i], FOPRBIN)) == NULL) + continue; + keyflag = 0; + while ((status = readkpacket(f, &ctb, userid, keyID, NULL)) != -1) { + if (is_key_ctb(ctb) && user_from_keyID(keyID) == NULL) + keyflag = 1; + if (keyflag && ctb == CTB_USERID) { + if (!hashleft) { + hashptr = allocbuf(HASH_ALLOC * sizeof(struct hashent)); + hashleft = HASH_ALLOC; + } + memcpy(hashptr->keyID, keyID, KEYFRAGSIZE); + hashptr->userid = store_str(userid); + hashptr->next = hashtbl[PK_HASH(keyID)]; + hashtbl[PK_HASH(keyID)] = hashptr; + ++hashptr; + --hashleft; + keyflag = 0; + } + } + fclose(f); + } + return 0; +} + +/* + * memory management routines + */ + +static void +maint_init_mem(void) +{ + pkhash = allocbuf(PK_HASHSIZE * sizeof(struct pubkey *)); + memset(pkhash, 0, PK_HASHSIZE * sizeof(struct pubkey *)); +} + +static void +maint_release_mem(void) +{ + nleft = 0; + strleft = 0; + pkhash = NULL; + freebufpool(); +} + +/* + * allocn() does the same as malloc(). Memory is allocated in chunks + * of ALLOC_UNIT bytes, all memory can be freed by calling freebufpool(). + */ +static VOID * +allocn(int size) +{ + static char *ptr; +#ifndef MSDOS /* don't align on MSDOS to save memory */ + size = (size + 3) & ~3; +#endif + assert(size < ALLOC_UNIT); + if (size > nleft) { + ptr = allocbuf(ALLOC_UNIT); + nleft = ALLOC_UNIT; + } + nleft -= size; + ptr += size; + return ptr - size; +} + +/* + * store_str does the same as strdup(), but allocates memory with allocbuf() + */ +static char * +store_str(char *str) +{ + int size = strlen(str) + 1; + if (size > ALLOC_UNIT) { + fprintf(stderr, "store_str: string too long\n"); + return NULL; + } + if (size > strleft) { + strptr = allocbuf(ALLOC_UNIT); + strleft = ALLOC_UNIT; + } + strcpy(strptr, str); + strptr += size; + strleft -= size; + return strptr - size; +} + + +static struct bufpool { + struct bufpool *next; + char buf[1]; /* variable size */ +} *bufpool = NULL; + +long totalsize = 0; + +/* + * allocate buffer, all buffers allocated with this function can be + * freed with one call to freebufpool() + */ +static VOID * +allocbuf(int size) +{ + struct bufpool *p; + + p = xmalloc(size + sizeof(struct bufpool *)); + totalsize += size; + p->next = bufpool; + bufpool = p; + return p->buf; +} + +/* + * free all memory obtained with allocbuf() + */ +static void +freebufpool(void) +{ + struct bufpool *p; + + if (verbose) + fprintf(pgpout, "\nMemory used: %ldk\n", totalsize / 1024); + totalsize = 0; + while (bufpool) { + p = bufpool; + bufpool = bufpool->next; + free(p); + } +}