]> 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);
        do {
                unsigned char *data =
                    tls_pem_decode(pem_buffer, pem_size, idx++, &len);
-               if ((!data) || (!len))
+               if (!data || !len) {
                        break;
                        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) {
                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;
        }
 
                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);
        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
 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
 .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 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) {
        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);
                        struct TLSCertificate *certificate = chain[i];
                        // check validity date
                        err = tls_certificate_is_valid(certificate);
-                       if (err)
+                       if (err) {
                                return 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
                }
        }
        // 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;
        size_t header_len;
        char *url = 0;
        int redirs = 0, redirlimit = 50, printstatus = 0;
+       int verifypolicy = 1;
 
        ltc_mp = tfm_desc;
 
 
        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;
                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;
                        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);
 
 
                        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)
                         */
                        /* 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");
 
                        if (!clientssl) {
                                fprintf(stderr, "Error initializing client context\n");