]> pd.if.org Git - zpackage/commitdiff
implement trust on first use
authorNathan Wagner <nw@hydaspes.if.org>
Tue, 12 Feb 2019 21:03:55 +0000 (21:03 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Tue, 12 Feb 2019 21:03:55 +0000 (21:03 +0000)
crypto/pem.c
crypto/tlse.c
doc/zpm.8
zpm-fetchurl.c

index 7d273f9f694815889c5ff2b6f6ce0b9642cc58cf..490bc0c3dd255cdf9e167d1ccca7652abe650b8f 100644 (file)
@@ -336,10 +336,10 @@ int tls_load_root_certificates(struct TLSContext *tls,
        do {
                unsigned char *data =
                    tls_pem_decode(pem_buffer, pem_size, idx++, &len);
-               if ((!data) || (!len))
+               if (!data || !len) {
                        break;
-               struct TLSCertificate *cert =
-                   asn1_parse(NULL, data, len, 0);
+               }
+               struct TLSCertificate *cert = asn1_parse(NULL, data, len, 0);
                if (cert) {
                        if (cert->version == 2) {
                                if (cert->priv) {
index 5f5f04790d083477c6a34b6ee2522d59549a778b..8920467058eb3c76f7c57e13c19a2afa8fee3e53 100644 (file)
@@ -5162,6 +5162,12 @@ int _private_asn1_parse(struct TLSContext *context,
                pos += length;
        }
 
+       if (cert_len && cert_data) {
+               int h = find_hash("sha256");
+               size_t len = sizeof cert->fp;
+               hash_memory(h, cert_data,cert_len, cert->fp, &len);
+       }
+
        if (level == 2 && cert->sign_key && cert->sign_len
            && cert_len && cert_data) {
                free(cert->fingerprint);
index 7c578b44a8c3386cd3c82dba5cc2629afc69f355..6fa512e4dbaa690c9f37ef72f131a58bb6c6d80f 100644 (file)
--- a/doc/zpm.8
+++ b/doc/zpm.8
@@ -80,6 +80,9 @@ from https://sqlite.org/index.html
 .TP
 lzma
 from http://git.tukaani.org/xz.git
+.TP
+SSL root certificates
+taken from http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt
 .PP
 All other code written by Nathan Wagner and also placed in the public domain.
 .SH LICENSE
index 15fc36ab77796639b033d5eae8edd9dc0ecf21b5..4ffca2333b8cd16f994fcddef20fdd4e490073f5 100644 (file)
@@ -30,7 +30,193 @@ void tls_free_uri(struct tls_uri *);
 
 int open_tcp_connection(char *host, int port);
 
-int verify(struct TLSContext *context, struct TLSCertificate **chain, int len) {
+/* if trustpolicy is 0, we just accept anything */
+int verify_trust(struct TLSContext *context, struct TLSCertificate **chain, int
+               len) {
+       /* suppress unused */
+       if (context || chain || len) {
+               return 0;
+       }
+
+       return 0;
+}
+
+static char hexchars[] = "0123456789abcdefABCDEF";
+
+static void hex(char *dst, uint8_t *src, size_t len) {
+       while (len--) {
+               dst[0] = hexchars[(src[0]>>4)&0xf];
+               dst[1] = hexchars[src[0]&0xf];
+               dst+=2;
+               src++;
+       }
+}
+
+#if 0
+static void hexbin(uint8_t *dst, unsigned char *src, size_t len) {
+       size_t i;
+       int x;
+
+       for (i=0; i<len; i+=2) {
+               sscanf((const char *)src+i, "%02x", &x);
+               dst[i/2] = x;
+       }
+}
+#endif
+
+char *my_getline(struct tls_buffer *b, int fd, size_t *size) {
+       char *loc = 0;
+       char buf[4096];
+       ssize_t rv = 0;
+
+       while (b->error == 0) {
+               loc = memchr(b->buffer, '\n', b->len);
+               if (loc) {
+                       *size = loc - b->buffer + 1;
+                       return b->buffer;
+               } else {
+                       rv = read(fd, buf, sizeof buf);
+                       if (rv == -1) {
+                               return 0;
+                       }
+                       if (rv == 0) {
+                               break;
+                       }
+                       tls_buffer_append(b, buf, rv);
+               }
+       }
+       if (rv == 0) {
+               *size = b->len;
+               return b->buffer;
+       }
+       return 0;
+}
+
+/*
+ * We use a trust on first use policy.  The trust DB is a simple
+ * file in /var/lib/zpm/known_hosts, or ~/.zpm/known_hosts, or ZPM_KNOWNHOSTS.
+ * if -k is given, no verification is done
+ */
+int verify_first(struct TLSContext *context, struct TLSCertificate **chain, int
+               certs) {
+       int err;
+       char *trustfile, *homedir = 0, *host, *fp;
+       unsigned char certhash[65];
+       int trustdb;
+       struct tls_buffer tbuf;
+
+       char *line = 0;
+       size_t len = 0;
+
+       if (certs == 0 || chain == 0) {
+               return 1;
+       }
+
+       err = tls_certificate_is_valid(chain[0]);
+       if (err) {
+               return err;
+       }
+
+       if (context->sni) {
+               err = tls_certificate_valid_subject(chain[0], context->sni);
+               if (err) {
+                       return err;
+               }
+       }
+
+       hex(certhash, chain[0]->fp, 32);
+       certhash[64] = 0;
+
+       trustfile = getenv("ZPM_KNOWNHOSTS");
+       if (!trustfile) {
+               if (geteuid() == 0) {
+                       trustfile = "/var/lib/zpm/known_hosts";
+               } else {
+                       homedir = getenv("HOME");
+                       if (!homedir) {
+                               return 1;
+                       }
+                       len = snprintf(homedir, 0, "%s/.zpm/known_hosts", homedir);
+                       homedir = malloc(len+1);
+                       if (!homedir) {
+                               return 1;
+                       }
+                       len = snprintf(homedir, len+1, "%s/.zpm/known_hosts", homedir);
+                       trustfile = homedir;
+               }
+       }
+       /* cert is valid on its face, so check against the trust db */
+       trustdb = open(trustfile, O_RDWR|O_CREAT, 0600);
+       if (homedir) {
+               free(homedir);
+       }
+       if (trustdb == -1) {
+               fprintf(stderr, "cannot open trustdb: %s\n", strerror(errno));
+               return 1;
+       }
+
+       len = 0;
+       tls_buffer_init(&tbuf, 128);
+       do {
+               char *off;
+
+               tls_buffer_shift(&tbuf, len);
+               line = my_getline(&tbuf, trustdb, &len);
+
+               if (!line || !len) {
+                       break;
+               }
+
+               fp = line;
+               while (isspace(*fp)) {
+                       fp++;
+               }
+               if (*fp == '#') {
+                       continue;
+               }
+               off = strchr(line, ':');
+               if (!off) {
+                       continue;
+               }
+               host = off + 1;
+               *off = 0;
+               if (line[len-1] == '\n') {
+                       line[len-1] = 0;
+               }
+
+               if (strlen(fp) != 64) {
+                       continue;
+               }
+
+               if (len && line[len-1] == '\n') {
+                       line[len-1] = 0;
+               }
+
+               if (strcmp(context->sni, host) != 0) {
+                       continue;
+               }
+
+               int match = (memcmp(certhash, fp, 64) == 0); 
+
+               close(trustdb);
+               tls_buffer_free(&tbuf);
+               return match ? no_error : bad_certificate;
+       } while (!tbuf.error);
+
+       /* got here, so we should be at EOF, so add this host to trust db */
+       lseek(trustdb, 0, SEEK_END);
+       write(trustdb, certhash, 64);
+       write(trustdb, ":", 1);
+       write(trustdb, context->sni, strlen(context->sni));
+       write(trustdb, "\n", 1);
+       close(trustdb);
+       tls_buffer_free(&tbuf);
+
+       return no_error;
+}
+
+int verify_roots(struct TLSContext *context, struct TLSCertificate **chain, int
+               len) {
        int i, err;
 
        if (chain) {
@@ -38,10 +224,12 @@ int verify(struct TLSContext *context, struct TLSCertificate **chain, int len) {
                        struct TLSCertificate *certificate = chain[i];
                        // check validity date
                        err = tls_certificate_is_valid(certificate);
-                       if (err)
+                       if (err) {
                                return err;
-                       // check certificate in certificate->bytes of length certificate->len
-                       // the certificate is in ASN.1 DER format
+                       }
+                       // check certificate in certificate->bytes of length
+                       // certificate->len the certificate is in ASN.1 DER
+                       // format
                }
        }
        // check if chain is valid
@@ -277,13 +465,16 @@ int main(int ac, char *av[]) {
        size_t header_len;
        char *url = 0;
        int redirs = 0, redirlimit = 50, printstatus = 0;
+       int verifypolicy = 1;
 
        ltc_mp = tfm_desc;
 
-       while ((option = getopt(ac, av, "o:rIfz:#R:S")) != -1) {
+       while ((option = getopt(ac, av, "o:rIfz:#R:SkK")) != -1) {
                switch (option) {
                        case 'o': outfile = optarg; break;
                        case 'S': printstatus = 1; head = 1; break;
+                       case 'k': verifypolicy = 0; break;
+                       case 'K': verifypolicy = 2; break;
                        case 'I': head = 1;
                        case 'r': raw = 1; break;
                        case 'f': failsilent = 1; break;
@@ -368,18 +559,28 @@ int main(int ac, char *av[]) {
 
                        clientssl = tls_create_context(TLS_CLIENT, TLS_V12);
 
-                       rv = tls_load_root_file(clientssl, "root.pem");
-                       if (rv == -1) {
-                               fprintf(stderr, "Error loading root certs\n");
-                               return 1;
-                       }
-
                        /* optionally, we can set a certificate validation
                         * callback function if set_verify is not called, and
                         * root ca is set, `tls_default_verify` will be used
                         * (does exactly what `verify` does in this example)
                         */
-                       tls_set_verify(clientssl, verify);
+                       if (verifypolicy == 2) {
+                               char *cert_path = 0;
+                               cert_path = getenv("ZPM_CERTFILE");
+                               if (!cert_path) {
+                                       cert_path = "/var/lib/zpm/roots.pem";
+                               }
+                               rv = tls_load_root_file(clientssl, cert_path);
+                               if (rv == -1) {
+                                       fprintf(stderr, "Error loading root certs\n");
+                                       return 1;
+                               }
+                               tls_set_verify(clientssl, verify_roots);
+                       } else if (verifypolicy == 1) {
+                               tls_set_verify(clientssl, verify_first);
+                       } else {
+                               tls_set_verify(clientssl, verify_trust);
+                       }
 
                        if (!clientssl) {
                                fprintf(stderr, "Error initializing client context\n");