X-Git-Url: https://git.librecmc.org/?p=oweals%2Fucert.git;a=blobdiff_plain;f=ucert.c;h=12b42a875830f0e6857b2e8c5d953befd413562f;hp=7be2f8f0691ad809f0f8851e3251c6c1a289ef3c;hb=493cc9a83702904369eade4b9b62ea492dae3600;hpb=b62d1266c14a64b39ba53cf54ebee812c3d6804d diff --git a/ucert.c b/ucert.c index 7be2f8f..12b42a8 100644 --- a/ucert.c +++ b/ucert.c @@ -48,6 +48,13 @@ static enum { static bool quiet; +/* + * ucert structure + * | BLOB | + * | SIGNATURE | PAYLOAD | + * | |[ BLOBMSG CONTAINER ]| + * | |[[T,i,v,e,f,pubkey ]]| + */ enum cert_attr { CERT_ATTR_SIGNATURE, CERT_ATTR_PAYLOAD, @@ -93,11 +100,13 @@ static const struct blobmsg_policy cert_payload_policy[CERT_PL_ATTR_MAX] = { [CERT_PL_ATTR_KEY_FINGERPRINT] = { .name = "fingerprint", .type = BLOBMSG_TYPE_STRING }, }; +/* list to store certificate chain at runtime */ struct cert_object { struct list_head list; struct blob_attr *cert[CERT_ATTR_MAX]; }; +/* write buffer to file */ static int write_file(const char *filename, void *buf, size_t len, bool append) { FILE *f; size_t outlen; @@ -111,6 +120,7 @@ static int write_file(const char *filename, void *buf, size_t len, bool append) return (outlen == len); } +/* load certfile into list */ static int cert_load(const char *certfile, struct list_head *chain) { FILE *f; struct blob_attr *certtb[CERT_ATTR_MAX]; @@ -157,6 +167,7 @@ static int cert_load(const char *certfile, struct list_head *chain) { return (ret <= 0); } +/* append signature to certfile */ static int cert_append(const char *certfile, const char *sigfile) { FILE *fs; char filebuf[CERT_BUF_LEN]; @@ -181,6 +192,7 @@ static int cert_append(const char *certfile, const char *sigfile) { return ret; } +/* verify the signature of a single chain element */ static int cert_verify_blob(struct blob_attr *cert[CERT_ATTR_MAX], const char *pubkeyfile, const char *pubkeydir) { int i; @@ -219,6 +231,7 @@ static int cert_verify_blob(struct blob_attr *cert[CERT_ATTR_MAX], return ret; } +/* verify cert chain (and message) */ static int chain_verify(const char *msgfile, const char *pubkeyfile, const char *pubkeydir, struct list_head *chain) { struct cert_object *cobj; @@ -226,6 +239,7 @@ static int chain_verify(const char *msgfile, const char *pubkeyfile, struct blob_attr *payloadtb[CERT_PL_ATTR_MAX]; char tmpdir[] = "/tmp/ucert-XXXXXX"; char chainedpubkey[256] = {0}; + char chainedfp[17] = {0}; char extsigfile[256] = {0}; int ret = 1; int checkmsg = 0; @@ -295,6 +309,19 @@ static int chain_verify(const char *msgfile, const char *pubkeyfile, blobmsg_data(payloadtb[CERT_PL_ATTR_PUBKEY]), blobmsg_data_len(payloadtb[CERT_PL_ATTR_PUBKEY]), false); + + if (usign_f_pubkey(chainedfp, chainedpubkey)) { + if (!quiet) + fprintf(stderr, "cannot get fingerprint for chained key\n"); + ret = 2; + goto clean_and_return; + } + if (pubkeydir && _usign_key_is_revoked(chainedfp, pubkeydir)) { + if (!quiet) + fprintf(stderr, "key %s has been revoked!\n", chainedfp); + ret = 4; + goto clean_and_return; + } } else { /* blob doesn't have payload, verify message using signature */ if (msgfile) { @@ -324,6 +351,7 @@ clean_and_return: return ret | checkmsg; } +/* dump single chain element to console */ static void cert_dump_blob(struct blob_attr *cert[CERT_ATTR_MAX]) { int i; @@ -344,6 +372,7 @@ static void cert_dump_blob(struct blob_attr *cert[CERT_ATTR_MAX]) { } } +/* dump certfile to console */ static int cert_dump(const char *certfile) { struct cert_object *cobj; static LIST_HEAD(certchain); @@ -363,6 +392,7 @@ static int cert_dump(const char *certfile) { return 0; } +/* issue an auth certificate for pubkey */ static int cert_issue(const char *certfile, const char *pubkeyfile, const char *seckeyfile) { struct blob_buf certbuf; struct blob_buf payloadbuf; @@ -454,11 +484,97 @@ static int cert_issue(const char *certfile, const char *pubkeyfile, const char * return 0; } -static int cert_process_revoker(const char *certfile) { - fprintf(stderr, "not implemented\n"); - return 1; +/* process revoker certificate */ +static int cert_process_revoker(const char *certfile, const char *pubkeydir) { + static LIST_HEAD(certchain); + struct cert_object *cobj; + struct blob_attr *containertb[CERT_CT_ATTR_MAX]; + struct blob_attr *payloadtb[CERT_PL_ATTR_MAX]; + struct stat st; + struct timeval tv; + uint64_t validfrom; + uint32_t certtype; + char *fingerprint; + char rfname[512]; + + int ret; + + if (cert_load(certfile, &certchain)) { + fprintf(stderr, "cannot parse cert\n"); + return 1; + } + + list_for_each_entry(cobj, &certchain, list) { + /* blob has payload, verify that using signature */ + if (!cobj->cert[CERT_ATTR_PAYLOAD]) + return 2; + ret = cert_verify_blob(cobj->cert, NULL, pubkeydir); + if (ret) + return ret; + + blobmsg_parse(cert_cont_policy, + ARRAY_SIZE(cert_cont_policy), + containertb, + blob_data(cobj->cert[CERT_ATTR_PAYLOAD]), + blob_len(cobj->cert[CERT_ATTR_PAYLOAD])); + if (!containertb[CERT_CT_ATTR_PAYLOAD]) { + fprintf(stderr, "no ucert in signed payload\n"); + return 2; + } + + blobmsg_parse(cert_payload_policy, + ARRAY_SIZE(cert_payload_policy), + payloadtb, + blobmsg_data(containertb[CERT_CT_ATTR_PAYLOAD]), + blobmsg_data_len(containertb[CERT_CT_ATTR_PAYLOAD])); + + if (!payloadtb[CERT_PL_ATTR_CERTTYPE] || + !payloadtb[CERT_PL_ATTR_VALIDFROMTIME] || + !payloadtb[CERT_PL_ATTR_KEY_FINGERPRINT]) { + fprintf(stderr, "missing mandatory ucert attributes\n"); + return 2; + } + + certtype = blobmsg_get_u32(payloadtb[CERT_PL_ATTR_CERTTYPE]); + validfrom = blobmsg_get_u64(payloadtb[CERT_PL_ATTR_VALIDFROMTIME]); + fingerprint = blobmsg_get_string(payloadtb[CERT_PL_ATTR_KEY_FINGERPRINT]); + + if (certtype != CERTTYPE_REVOKE) { + fprintf(stderr, "wrong certificate type\n"); + return 2; + } + + gettimeofday(&tv, NULL); + if (tv.tv_sec < validfrom) { + return 3; + } + + snprintf(rfname, sizeof(rfname)-1, "%s/%s", pubkeydir, fingerprint); + /* check if entry in pubkeydir exists */ + if (stat(rfname, &st) == 0) { + if (_usign_key_is_revoked(fingerprint, pubkeydir)) { + if (!quiet) + fprintf(stdout, "existing revoker deadlink for key %s\n", fingerprint); + continue; + }; + + /* remove any other entry */ + if (unlink(rfname)) + return -1; + } + + ret = symlink(".revoked.", rfname); + if (ret) + return ret; + + if (!quiet) + fprintf(stdout, "created revoker deadlink for key %s\n", fingerprint); + }; + + return ret; } +/* load and verify certfile (and message) */ static int cert_verify(const char *certfile, const char *pubkeyfile, const char *pubkeydir, const char *msgfile) { static LIST_HEAD(certchain); @@ -470,16 +586,17 @@ static int cert_verify(const char *certfile, const char *pubkeyfile, const char return chain_verify(msgfile, pubkeyfile, pubkeydir, &certchain); } +/* output help */ static int usage(const char *cmd) { fprintf(stderr, "Usage: %s \n" "Commands:\n" " -A: append signature (needs -c and -x)\n" - " -D: dump\n" + " -D: dump (needs -c)\n" " -I: issue cert and revoker (needs -c and -p and -s)\n" - " -R: process revoker certificate (needs -c)\n" - " -V: verify (needs -c and -p|-P)\n" + " -R: process revoker certificate (needs -c and -P)\n" + " -V: verify (needs -c and -p|-P, may have -m)\n" "Options:\n" " -c : certificate file\n" " -m : message file (verify only)\n" @@ -493,6 +610,7 @@ static int usage(const char *cmd) return 1; } +/* parse command line options and call functions */ int main(int argc, char *argv[]) { int ch; const char *msgfile = NULL; @@ -563,8 +681,8 @@ int main(int argc, char *argv[]) { else return usage(argv[0]); case CMD_REVOKE: - if (certfile) - return cert_process_revoker(certfile); + if (certfile && pubkeydir) + return cert_process_revoker(certfile, pubkeydir); else return usage(argv[0]); case CMD_VERIFY: