Do not print line number in debug messages
[oweals/ucert.git] / ucert.c
diff --git a/ucert.c b/ucert.c
index 6cdc2201fb3933bacfdfbb011102a167571206ee..5523b02a7eb2b977ce4c28bfd3b4c082845d6513 100644 (file)
--- a/ucert.c
+++ b/ucert.c
@@ -15,6 +15,7 @@
 
 #include <fcntl.h>
 #include <dlfcn.h>
+#include <errno.h>
 #include <stdio.h>
 #include <stdbool.h>
 #include <stdlib.h>
@@ -48,9 +49,13 @@ static enum {
 
 static bool quiet;
 #ifndef UCERT_STRIP_MESSAGES
-#define DPRINTF(format, ...) if (!quiet) fprintf(stderr, "%s(%d): " format, __func__, __LINE__, ## __VA_ARGS__)
+#define DPRINTF(format, ...)                                                           \
+       do {                                                                            \
+               if (!quiet)                                                             \
+                       fprintf(stderr, "%s: " format, __func__, ## __VA_ARGS__);       \
+       } while (0)
 #else
-#define DPRINTF(format, ...)
+#define DPRINTF(format, ...) do { } while (0)
 #endif
 
 /*
@@ -112,54 +117,75 @@ 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);
        return (outlen == len);
 }
 
-/* load certfile into list */
-static int cert_load(const char *certfile, struct list_head *chain) {
+/* reads a whole file to a buffer - returns -1 on errors and sets errno */
+static ssize_t read_file(const char *filename, void *buf, size_t len, size_t minlen) {
        FILE *f;
-       struct blob_attr *certtb[CERT_ATTR_MAX];
-       struct blob_attr *bufpt;
-       struct cert_object *cobj;
-       char filebuf[CERT_BUF_LEN];
-       int ret = 0, pret = 0;
-       int len, pos = 0;
+       ssize_t ret;
 
-       f = fopen(certfile, "r");
+       f = fopen(filename, "r");
        if (!f)
-               return 1;
+               return -1;
 
-       len = fread(&filebuf, 1, CERT_BUF_LEN - 1, f);
-       if (len < 64)
-               return 1;
+       ret = fread(buf, 1, len, f);
+
+       /* Ensure that feof() yields the correct result when the file is exactly
+        * len bytes long */
+       fgetc(f);
+
+       if (ferror(f)) {
+               ret = -1;
+       } else if (!feof(f)) {
+               errno = EOVERFLOW;
+               ret = -1;
+       } else if ((size_t)ret < minlen) {
+               errno = EINVAL;
+               ret = -1;
+       }
 
-       ret = ferror(f) || !feof(f);
        fclose(f);
-       if (ret)
+       return ret;
+}
+
+/* load certfile into list */
+static int cert_load(const char *certfile, struct list_head *chain) {
+       struct blob_attr *certtb[CERT_ATTR_MAX];
+       struct blob_attr *bufpt;
+       struct cert_object *cobj;
+       char filebuf[CERT_BUF_LEN], *end;
+       int ret = 1;
+       ssize_t len;
+
+       len = read_file(certfile, filebuf, sizeof(filebuf) - 1, 0);
+       if (len < 0) {
+               if (!quiet)
+                       perror("Unable to load certificate file");
                return 1;
+       }
 
        bufpt = (struct blob_attr *)filebuf;
-       do {
-               pret = blob_parse(bufpt, certtb, cert_policy, CERT_ATTR_MAX);
-               if (pret <= 0)
-                       /* no attributes found */
+       end = filebuf + len;
+
+       while (true) {
+               len = end - (char *)bufpt;
+               if (len <= 0)
                        break;
 
-               if (pos + blob_pad_len(bufpt) > len)
-                       /* blob exceeds filebuffer */
+               if (blob_parse_untrusted(bufpt, len, certtb, cert_policy, CERT_ATTR_MAX) <= 0)
+                       /* no attributes found */
                        break;
-               else
-                       pos += blob_pad_len(bufpt);
 
                if (!certtb[CERT_ATTR_SIGNATURE])
                        /* no signature -> drop */
@@ -171,31 +197,34 @@ static int cert_load(const char *certfile, struct list_head *chain) {
                        cobj->cert[CERT_ATTR_PAYLOAD] = blob_memdup(certtb[CERT_ATTR_PAYLOAD]);
 
                list_add_tail(&cobj->list, chain);
-               ret += pret;
-       /* repeat parsing while there is still enough remaining data in buffer */
-       } while(len > pos + sizeof(struct blob_attr) && (bufpt = blob_next(bufpt)));
+               ret = 0;
+
+               /* Repeat parsing while there is still enough remaining data in buffer
+                *
+                * Note that blob_next() is only valid for untrusted data because blob_parse_untrusted()
+                * verified that the buffer contains at least one blob, and that it is completely contained
+                * in the buffer */
+               bufpt = blob_next(bufpt);
+       }
 
-       return (ret <= 0);
+       return ret;
 }
 
 #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;
-       int len;
+       struct blob_buf sigbuf = {0};
+       ssize_t len;
        int ret;
 
-       fs = fopen(sigfile, "r");
-       if (!fs)
-               return 1;
+       len = read_file(sigfile, filebuf, sizeof(filebuf) - 1, 64);
+       if (len < 0) {
+               if (!quiet)
+                       perror("Unable to load signature file");
 
-       len = fread(&filebuf, 1, CERT_BUF_LEN - 1, fs);
-       ret = ferror(fs) || !feof(fs) || (len < 64);
-       fclose(fs);
-       if (ret)
                return 1;
+       }
 
        blob_buf_init(&sigbuf, 0);
        blob_put(&sigbuf, CERT_ATTR_SIGNATURE, filebuf, len);
@@ -269,8 +298,8 @@ static int chain_verify(const char *msgfile, const char *pubkeyfile,
        list_for_each_entry(cobj, chain, list) {
                /* blob has payload, verify that using signature */
                if (cobj->cert[CERT_ATTR_PAYLOAD]) {
-                       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);
@@ -324,7 +353,7 @@ static int chain_verify(const char *msgfile, const char *pubkeyfile,
                                   blobmsg_data_len(payloadtb[CERT_PL_ATTR_PUBKEY]),
                                   false);
 
-                       if (usign_f_pubkey(chainedfp, chainedpubkey)) {
+                       if (usign_f_pubkey(chainedfp, chainedpubkey, quiet)) {
                                DPRINTF("cannot get fingerprint for chained key\n");
                                ret = 2;
                                goto clean_and_return;
@@ -367,6 +396,7 @@ clean_and_return:
 /* 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];
@@ -376,10 +406,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;
                }
        }
@@ -397,7 +433,7 @@ static int cert_dump(const char *certfile) {
        }
 
        list_for_each_entry(cobj, &certchain, list) {
-               fprintf(stdout, "=== CHAIN ELEMENT %02u ===\n", ++count);
+               printf("=== CHAIN ELEMENT %02u ===\n", ++count);
                cert_dump_blob(cobj->cert);
        }
 
@@ -406,32 +442,29 @@ 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;
-       int pklen, siglen;
+       ssize_t 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";
 
-       pkf = fopen(pubkeyfile, "r");
-       if (!pkf)
-               return -1;
-
-       pklen = fread(pkb, 1, 512, pkf);
-       pkb[pklen] = '\0';
+       pklen = read_file(pubkeyfile, pkb, sizeof(pkb) - 1, 32);
+       if (pklen < 0) {
+               if (!quiet)
+                       perror("Unable to load public key file");
 
-       if (pklen < 32)
                return -1;
+       }
 
-       fclose(pkf);
+       pkb[pklen] = '\0';
 
-       if (usign_f_pubkey(pkfp, pubkeyfile))
+       if (usign_f_pubkey(pkfp, pubkeyfile, quiet))
                return -1;
 
        gettimeofday(&tv, NULL);
@@ -460,16 +493,15 @@ static int cert_issue(const char *certfile, const char *pubkeyfile, const char *
                if (usign_s(fname, seckeyfile, sfname, quiet))
                        return 1;
 
-               sigf = fopen(sfname, "r");
-               if (!sigf)
-                       return 1;
+               siglen = read_file(sfname, sigb, sizeof(sigb) - 1, 1);
+               if (siglen < 0) {
+                       if (!quiet)
+                               perror("Unable to load signature file");
 
-               siglen = fread(sigb, 1, 1024, sigf);
-               if (siglen < 1)
                        return 1;
+               }
 
                sigb[siglen] = '\0';
-               fclose(sigf);
 
                unlink(fname);
                unlink(sfname);
@@ -499,12 +531,12 @@ 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)) {
                DPRINTF("cannot parse cert\n");