X-Git-Url: https://git.librecmc.org/?p=oweals%2Fucert.git;a=blobdiff_plain;f=ucert.c;h=7de4c12711e861bbf1ca0e5c26c082c3e04c832a;hp=7e419d4d8b04700df3b6f06803706f5be3ba6185;hb=afc86f352bf7915f81e9d38949e91306542aadee;hpb=bdc1ec45134f4f954365b3bb13b617b93c8c9bd1 diff --git a/ucert.c b/ucert.c index 7e419d4..7de4c12 100644 --- a/ucert.c +++ b/ucert.c @@ -47,6 +47,15 @@ static enum { } cmd = CMD_NONE; static bool quiet; +#ifndef UCERT_STRIP_MESSAGES +#define DPRINTF(format, ...) \ + do { \ + if (!quiet) \ + fprintf(stderr, "%s(%d): " format, __func__, __LINE__, ## __VA_ARGS__); \ + } while (0) +#else +#define DPRINTF(format, ...) do { } while (0) +#endif /* * ucert structure @@ -107,13 +116,13 @@ struct cert_object { }; /* write buffer to file */ -static int write_file(const char *filename, void *buf, size_t len, bool append) { +static bool write_file(const char *filename, void *buf, size_t len, bool append) { FILE *f; size_t outlen; f = fopen(filename, append?"a":"w"); if (!f) - return 1; + return false; outlen = fwrite(buf, 1, len, f); fclose(f); @@ -128,7 +137,7 @@ static int cert_load(const char *certfile, struct list_head *chain) { struct cert_object *cobj; char filebuf[CERT_BUF_LEN]; int ret = 0, pret = 0; - int len, pos = 0; + size_t len, pos = 0; f = fopen(certfile, "r"); if (!f) @@ -145,7 +154,7 @@ static int cert_load(const char *certfile, struct list_head *chain) { bufpt = (struct blob_attr *)filebuf; do { - pret = blob_parse(bufpt, certtb, cert_policy, CERT_ATTR_MAX); + pret = blob_parse_untrusted(bufpt, len, certtb, cert_policy, CERT_ATTR_MAX); if (pret <= 0) /* no attributes found */ break; @@ -156,22 +165,29 @@ static int cert_load(const char *certfile, struct list_head *chain) { else pos += blob_pad_len(bufpt); + if (!certtb[CERT_ATTR_SIGNATURE]) + /* no signature -> drop */ + break; + cobj = calloc(1, sizeof(*cobj)); - memcpy(cobj->cert, &certtb, sizeof(certtb)); + cobj->cert[CERT_ATTR_SIGNATURE] = blob_memdup(certtb[CERT_ATTR_SIGNATURE]); + if (certtb[CERT_ATTR_PAYLOAD]) + cobj->cert[CERT_ATTR_PAYLOAD] = blob_memdup(certtb[CERT_ATTR_PAYLOAD]); + list_add_tail(&cobj->list, chain); ret += pret; - bufpt = blob_next(bufpt); /* repeat parsing while there is still enough remaining data in buffer */ - } while(len > pos + sizeof(struct blob_attr)); + } while(len > pos + sizeof(struct blob_attr) && (bufpt = blob_next(bufpt))); return (ret <= 0); } +#ifdef UCERT_FULL /* append signature to certfile */ static int cert_append(const char *certfile, const char *sigfile) { FILE *fs; char filebuf[CERT_BUF_LEN]; - struct blob_buf sigbuf; + struct blob_buf sigbuf = {0}; int len; int ret; @@ -191,6 +207,7 @@ static int cert_append(const char *certfile, const char *sigfile) { blob_buf_free(&sigbuf); return ret; } +#endif /* verify the signature of a single chain element */ static int cert_verify_blob(struct blob_attr *cert[CERT_ATTR_MAX], @@ -243,6 +260,7 @@ static int chain_verify(const char *msgfile, const char *pubkeyfile, char extsigfile[256] = {0}; int ret = 1; int checkmsg = 0; + struct timeval tv; if (mkdtemp(tmpdir) == NULL) return errno; @@ -250,12 +268,13 @@ static int chain_verify(const char *msgfile, const char *pubkeyfile, if (msgfile) checkmsg = -1; + gettimeofday(&tv, NULL); + list_for_each_entry(cobj, chain, list) { /* blob has payload, verify that using signature */ if (cobj->cert[CERT_ATTR_PAYLOAD]) { - struct timeval tv; - uint64_t validfrom; - uint64_t expiresat; + time_t validfrom; + time_t expiresat; uint32_t certtype; ret = cert_verify_blob(cobj->cert, chainedpubkey[0]?chainedpubkey:pubkeyfile, pubkeydir); @@ -269,7 +288,7 @@ static int chain_verify(const char *msgfile, const char *pubkeyfile, blob_len(cobj->cert[CERT_ATTR_PAYLOAD])); if (!containertb[CERT_CT_ATTR_PAYLOAD]) { ret = 1; - fprintf(stderr, "no ucert in signed payload\n"); + DPRINTF("no ucert in signed payload\n"); goto clean_and_return; } blobmsg_parse(cert_payload_policy, @@ -283,7 +302,7 @@ static int chain_verify(const char *msgfile, const char *pubkeyfile, !payloadtb[CERT_PL_ATTR_EXPIRETIME] || !payloadtb[CERT_PL_ATTR_PUBKEY]) { ret = 1; - fprintf(stderr, "missing mandatory ucert attributes\n"); + DPRINTF("missing mandatory ucert attributes\n"); goto clean_and_return; } certtype = blobmsg_get_u32(payloadtb[CERT_PL_ATTR_CERTTYPE]); @@ -292,15 +311,14 @@ static int chain_verify(const char *msgfile, const char *pubkeyfile, if (certtype != CERTTYPE_AUTH) { ret = 2; - fprintf(stderr, "wrong certificate type\n"); + DPRINTF("wrong certificate type\n"); goto clean_and_return; } - gettimeofday(&tv, NULL); if (tv.tv_sec < validfrom || tv.tv_sec >= expiresat) { ret = 3; - fprintf(stderr, "certificate expired\n"); + DPRINTF("certificate expired\n"); goto clean_and_return; } @@ -311,14 +329,12 @@ static int chain_verify(const char *msgfile, const char *pubkeyfile, false); if (usign_f_pubkey(chainedfp, chainedpubkey)) { - if (!quiet) - fprintf(stderr, "cannot get fingerprint for chained key\n"); + DPRINTF("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); + DPRINTF("key %s has been revoked!\n", chainedfp); ret = 4; goto clean_and_return; } @@ -335,14 +351,14 @@ static int chain_verify(const char *msgfile, const char *pubkeyfile, pubkeydir, extsigfile, quiet); unlink(extsigfile); } else { - fprintf(stderr, "stray trailing signature without anything to verify!\n"); + DPRINTF("stray trailing signature without anything to verify!\n"); ret = 1; }; } } if (checkmsg == -1) - fprintf(stderr, "missing signature to verify message!\n"); + DPRINTF("missing signature to verify message!\n"); clean_and_return: if (chainedpubkey[0]) @@ -351,9 +367,11 @@ clean_and_return: return ret | checkmsg; } +#ifdef UCERT_FULL /* dump single chain element to console */ static void cert_dump_blob(struct blob_attr *cert[CERT_ATTR_MAX]) { int i; + char *json = NULL; for (i = 0; i < CERT_ATTR_MAX; i++) { struct blob_attr *v = cert[i]; @@ -363,10 +381,16 @@ static void cert_dump_blob(struct blob_attr *cert[CERT_ATTR_MAX]) { switch(cert_policy[i].type) { case BLOB_ATTR_BINARY: - fprintf(stdout, "signature:\n---\n%s---\n", (char *) blob_data(v)); + printf("signature:\n---\n%s---\n", (char *) blob_data(v)); break; case BLOB_ATTR_NESTED: - fprintf(stdout, "payload:\n---\n%s\n---\n", blobmsg_format_json_indent(blob_data(v), false, 0)); + json = blobmsg_format_json_indent(blob_data(v), false, 0); + if (!json) { + DPRINTF("cannot parse payload\n"); + continue; + } + printf("payload:\n---\n%s\n---\n", json); + free(json); break; } } @@ -379,14 +403,13 @@ static int cert_dump(const char *certfile) { unsigned int count = 0; if (cert_load(certfile, &certchain)) { - fprintf(stderr, "cannot parse cert\n"); + DPRINTF("cannot parse cert\n"); return 1; } list_for_each_entry(cobj, &certchain, list) { - fprintf(stderr, "=== CHAIN ELEMENT %02u ===\n", ++count); + printf("=== CHAIN ELEMENT %02u ===\n", ++count); cert_dump_blob(cobj->cert); - fprintf(stderr, "========================\n"); } return 0; @@ -394,25 +417,19 @@ static int cert_dump(const char *certfile) { /* 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; + struct blob_buf payloadbuf = {0}; + struct blob_buf certbuf = {0}; struct timeval tv; - struct stat st; int pklen, siglen; int revoker = 1; void *c; FILE *pkf, *sigf; char pkb[512]; - char sigb[512]; + char sigb[1024]; char fname[256], sfname[256]; char pkfp[17]; char tmpdir[] = "/tmp/ucert-XXXXXX"; - if (stat(certfile, &st) == 0) { - fprintf(stderr, "certfile %s exists, won't overwrite.\n", certfile); - return -1; - } - pkf = fopen(pubkeyfile, "r"); if (!pkf) return -1; @@ -472,7 +489,7 @@ static int cert_issue(const char *certfile, const char *pubkeyfile, const char * blob_put(&certbuf, CERT_ATTR_SIGNATURE, sigb, siglen); blob_put(&certbuf, CERT_ATTR_PAYLOAD, blob_data(payloadbuf.head), blob_len(payloadbuf.head)); snprintf(fname, sizeof(fname) - 1, "%s%s", certfile, revoker?".revoke":""); - write_file(fname, certbuf.head, blob_raw_len(certbuf.head), false); + write_file(fname, certbuf.head, blob_raw_len(certbuf.head), true); blob_buf_free(&certbuf); blob_buf_free(&payloadbuf); @@ -483,6 +500,7 @@ static int cert_issue(const char *certfile, const char *pubkeyfile, const char * return 0; } +#endif /* process revoker certificate */ static int cert_process_revoker(const char *certfile, const char *pubkeydir) { @@ -492,22 +510,25 @@ static int cert_process_revoker(const char *certfile, const char *pubkeydir) { struct blob_attr *payloadtb[CERT_PL_ATTR_MAX]; struct stat st; struct timeval tv; - uint64_t validfrom; - uint32_t certtype; + time_t validfrom; + enum certtype_id certtype; char *fingerprint; char rfname[512]; - int ret; + int ret = -1; if (cert_load(certfile, &certchain)) { - fprintf(stderr, "cannot parse cert\n"); + DPRINTF("cannot parse cert\n"); return 1; } + gettimeofday(&tv, NULL); + list_for_each_entry(cobj, &certchain, list) { - /* blob has payload, verify that using signature */ if (!cobj->cert[CERT_ATTR_PAYLOAD]) return 2; + + /* blob has payload, verify that using signature */ ret = cert_verify_blob(cobj->cert, NULL, pubkeydir); if (ret) return ret; @@ -518,7 +539,7 @@ static int cert_process_revoker(const char *certfile, const char *pubkeydir) { 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"); + DPRINTF("no ucert in signed payload\n"); return 2; } @@ -531,7 +552,7 @@ static int cert_process_revoker(const char *certfile, const char *pubkeydir) { if (!payloadtb[CERT_PL_ATTR_CERTTYPE] || !payloadtb[CERT_PL_ATTR_VALIDFROMTIME] || !payloadtb[CERT_PL_ATTR_KEY_FINGERPRINT]) { - fprintf(stderr, "missing mandatory ucert attributes\n"); + DPRINTF("missing mandatory ucert attributes\n"); return 2; } @@ -540,11 +561,10 @@ static int cert_process_revoker(const char *certfile, const char *pubkeydir) { fingerprint = blobmsg_get_string(payloadtb[CERT_PL_ATTR_KEY_FINGERPRINT]); if (certtype != CERTTYPE_REVOKE) { - fprintf(stderr, "wrong certificate type\n"); + DPRINTF("wrong certificate type\n"); return 2; } - gettimeofday(&tv, NULL); if (tv.tv_sec < validfrom) { return 3; } @@ -553,8 +573,7 @@ static int cert_process_revoker(const char *certfile, const char *pubkeydir) { /* 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); + DPRINTF("existing revoker deadlink for key %s\n", fingerprint); continue; }; @@ -567,8 +586,7 @@ static int cert_process_revoker(const char *certfile, const char *pubkeydir) { if (ret) return ret; - if (!quiet) - fprintf(stdout, "created revoker deadlink for key %s\n", fingerprint); + DPRINTF("created revoker deadlink for key %s\n", fingerprint); }; return ret; @@ -579,7 +597,7 @@ static int cert_verify(const char *certfile, const char *pubkeyfile, const char static LIST_HEAD(certchain); if (cert_load(certfile, &certchain)) { - fprintf(stderr, "cannot parse cert\n"); + DPRINTF("cannot parse cert\n"); return 1; } @@ -589,12 +607,15 @@ static int cert_verify(const char *certfile, const char *pubkeyfile, const char /* output help */ static int usage(const char *cmd) { +#ifndef UCERT_STRIP_MESSAGES fprintf(stderr, "Usage: %s \n" "Commands:\n" +#ifdef UCERT_FULL " -A: append signature (needs -c and -x)\n" " -D: dump (needs -c)\n" " -I: issue cert and revoker (needs -c and -p and -s)\n" +#endif /* UCERT_FULL */ " -R: process revoker certificate (needs -c and -P)\n" " -V: verify (needs -c and -p|-P, may have -m)\n" "Options:\n" @@ -603,10 +624,13 @@ static int usage(const char *cmd) " -p : public key file\n" " -P : public key directory (verify only)\n" " -q: quiet (do not print verification result, use return code only)\n" +#ifdef UCERT_FULL " -s : secret key file (issue only)\n" - " -x : signature file\n" + " -x : signature file (append only)\n" +#endif /* UCERT_FULL */ "\n", cmd); +#endif /* UCERT_STRIP_MESSAGES */ return 1; } @@ -614,15 +638,23 @@ static int usage(const char *cmd) int main(int argc, char *argv[]) { int ch; const char *msgfile = NULL; - const char *sigfile = NULL; const char *pubkeyfile = NULL; const char *pubkeydir = NULL; const char *certfile = NULL; +#ifdef UCERT_FULL + const char *sigfile = NULL; const char *seckeyfile = NULL; +#endif quiet = false; - while ((ch = getopt(argc, argv, "ADIRVc:m:p:P:qs:x:")) != -1) { + while ((ch = getopt(argc, argv, + "RVc:m:p:P:q" +#ifdef UCERT_FULL + "ADIs:x:" +#endif + )) != -1) { switch (ch) { +#ifdef UCERT_FULL case 'A': if (cmd != CMD_NONE) return usage(argv[0]); @@ -638,6 +670,7 @@ int main(int argc, char *argv[]) { return usage(argv[0]); cmd = CMD_ISSUE; break; +#endif case 'R': if (cmd != CMD_NONE) return usage(argv[0]); @@ -669,10 +702,11 @@ int main(int argc, char *argv[]) { pubkeydir = optarg; break; case 'q': - if (quiet || cmd == CMD_NONE) + if (quiet) return usage(argv[0]); quiet = true; break; +#ifdef UCERT_FULL case 's': if (seckeyfile || cmd != CMD_ISSUE || cmd == CMD_NONE) return usage(argv[0]); @@ -683,12 +717,14 @@ int main(int argc, char *argv[]) { return usage(argv[0]); sigfile = optarg; break; +#endif default: return usage(argv[0]); } } switch (cmd) { +#ifdef UCERT_FULL case CMD_APPEND: if (certfile && sigfile) return cert_append(certfile, sigfile); @@ -704,6 +740,7 @@ int main(int argc, char *argv[]) { return cert_issue(certfile, pubkeyfile, seckeyfile); else return usage(argv[0]); +#endif case CMD_REVOKE: if (certfile && pubkeydir) return cert_process_revoker(certfile, pubkeydir); @@ -714,7 +751,7 @@ int main(int argc, char *argv[]) { return cert_verify(certfile, pubkeyfile, pubkeydir, msgfile); else return usage(argv[0]); - case CMD_NONE: + default: return usage(argv[0]); }