Do not print line number in debug messages
[oweals/ucert.git] / ucert.c
diff --git a/ucert.c b/ucert.c
index 76960a200be077b7ba129af184c7b74c54f4cd4d..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,10 +49,10 @@ static enum {
 
 static bool quiet;
 #ifndef UCERT_STRIP_MESSAGES
-#define DPRINTF(format, ...)                                                                   \
-       do {                                                                                    \
-               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, ...) do { } while (0)
@@ -116,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;
-       size_t 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 */
@@ -175,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 = {0};
-       int len;
+       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);
@@ -328,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;
@@ -381,7 +406,7 @@ 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:
                        json = blobmsg_format_json_indent(blob_data(v), false, 0);
@@ -389,7 +414,7 @@ static void cert_dump_blob(struct blob_attr *cert[CERT_ATTR_MAX]) {
                                DPRINTF("cannot parse payload\n");
                                continue;
                        }
-                       fprintf(stdout, "payload:\n---\n%s\n---\n", json);
+                       printf("payload:\n---\n%s\n---\n", json);
                        free(json);
                        break;
                }
@@ -408,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);
        }
 
@@ -420,29 +445,26 @@ static int cert_issue(const char *certfile, const char *pubkeyfile, const char *
        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[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);
@@ -471,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);