libclamav/crtmgr.c
5d49bd06
 /*
46a35abe
  *  Copyright (C) 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
5d49bd06
  *  Copyright (C) 2011 Sourcefire, Inc.
  *
  *  Authors: aCaB
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
 
8997495d
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
60d8d2c3
 #include "clamav.h"
8997495d
 #include "others.h"
56b4f4b0
 #include "crtmgr.h"
e5c6c1aa
 
 int cli_crt_init(cli_crt *x509) {
71da61a7
     int ret;
     if((ret = mp_init_multi(&x509->n, &x509->e, &x509->sig, NULL))) {
 	cli_errmsg("cli_crt_init: mp_init_multi failed with %d\n", ret);
e5c6c1aa
 	return 1;
71da61a7
     }
0f418a13
     x509->name = NULL;
278b2b28
     x509->isBlacklisted = 0;
e5c6c1aa
     x509->not_before = x509->not_after = 0;
8997495d
     x509->prev = x509->next = NULL;
01e4650b
     x509->certSign = x509->codeSign = x509->timeSign = 0;
e5c6c1aa
     return 0;
 }
 
 void cli_crt_clear(cli_crt *x509) {
cd94be7a
     UNUSEDPARAM(x509);
e5c6c1aa
     mp_clear_multi(&x509->n, &x509->e, &x509->sig, NULL);
 }
 
5e84efeb
 cli_crt *crtmgr_lookup(crtmgr *m, cli_crt *x509) {
f05aa165
     cli_crt *i;
     for(i = m->crts; i; i = i->next) {
 	if(x509->not_before >= i->not_before &&
10243534
 	   x509->not_after <= i->not_after &&
 	   (i->certSign | x509->certSign) == i->certSign &&
 	   (i->codeSign | x509->codeSign) == i->codeSign &&
 	   (i->timeSign | x509->timeSign) == i->timeSign &&
 	   !memcmp(x509->subject, i->subject, sizeof(i->subject)) &&
a4c8b3fd
 	   !memcmp(x509->serial, i->serial, sizeof(i->serial)) &&
7bcfb2f3
 	   !mp_cmp(&x509->n, &i->n) &&
0f418a13
 	   !mp_cmp(&x509->e, &i->e) && !(i->isBlacklisted)) {
71da61a7
 	    return i;
f05aa165
 	}
8997495d
     }
71da61a7
     return NULL;
 }
 
 int crtmgr_add(crtmgr *m, cli_crt *x509) {
f05aa165
     cli_crt *i;
     int ret = 0;
71da61a7
 
f05aa165
     for(i = m->crts; i; i = i->next) {
 	if(!memcmp(x509->subject, i->subject, sizeof(i->subject)) &&
a8e79d93
 	   !memcmp(x509->serial, i->subject, sizeof(i->serial)) &&
f05aa165
 	   !mp_cmp(&x509->n, &i->n) &&
 	   !mp_cmp(&x509->e, &i->e)) {
 	    if(x509->not_before >= i->not_before && x509->not_after <= i->not_after) {
 		/* Already same or broader */
10243534
 		ret = 1;
f05aa165
 	    }
 	    if(i->not_before > x509->not_before && i->not_before <= x509->not_after) {
 		/* Extend left */
 		i->not_before = x509->not_before;
 		ret = 1;
 	    }
 	    if(i->not_after >= x509->not_before && i->not_after < x509->not_after) {
 		/* Extend right */
 		i->not_after = x509->not_after;
 		ret = 1;
 	    }
10243534
 	    if(!ret)
 		continue;
 	    i->certSign |= x509->certSign;
 	    i->codeSign |= x509->codeSign;
 	    i->timeSign |= x509->timeSign;
2c2e89e1
 
f5092717
 	    return 0;
f05aa165
 	}
2c2e89e1
 
     /* If certs match, we're likely just revoking it */
     if (!memcmp(x509->subject, i->subject, sizeof(x509->subject)) &&
         !memcmp(x509->issuer, i->issuer, sizeof(x509->issuer)) &&
         !memcmp(x509->serial, i->serial, sizeof(x509->serial)) &&
         !mp_cmp(&x509->n, &i->n) &&
         !mp_cmp(&x509->e, &i->e)) {
             if (i->isBlacklisted != x509->isBlacklisted)
                 i->isBlacklisted = x509->isBlacklisted;
 
             return 0;
     }
f05aa165
     }
7bcfb2f3
 
8997495d
     i = cli_malloc(sizeof(*i));
     if(!i)
 	return 1;
 
7bcfb2f3
     if((ret = mp_init_multi(&i->n, &i->e, &i->sig, NULL))) {
 	cli_warnmsg("crtmgr_add: failed to mp_init failed with %d\n", ret);
8997495d
 	free(i);
 	return 1;
     }
7bcfb2f3
     if((ret = mp_copy(&x509->n, &i->n)) || (ret = mp_copy(&x509->e, &i->e)) || (ret = mp_copy(&x509->sig, &i->sig))) {
 	cli_warnmsg("crtmgr_add: failed to mp_init failed with %d\n", ret);
8997495d
 	cli_crt_clear(i);
 	free(i);
 	return 1;
     }
0f418a13
 
     if ((x509->name))
616c0259
 	i->name = strdup(x509->name);
0f418a13
     else
616c0259
 	i->name = NULL;
0f418a13
 
616c0259
     memcpy(i->raw_subject, x509->raw_subject, sizeof(i->raw_subject));
     memcpy(i->raw_issuer, x509->raw_issuer, sizeof(i->raw_issuer));
     memcpy(i->raw_tbshash, x509->raw_tbshash, sizeof(i->raw_tbshash));
     memcpy(i->raw_serial, x509->raw_serial, sizeof(i->raw_serial));
8997495d
     memcpy(i->subject, x509->subject, sizeof(i->subject));
a8e79d93
     memcpy(i->serial, x509->serial, sizeof(i->serial));
8997495d
     memcpy(i->issuer, x509->issuer, sizeof(i->issuer));
7bcfb2f3
     memcpy(i->tbshash, x509->tbshash, sizeof(i->tbshash));
8997495d
     i->not_before = x509->not_before;
     i->not_after = x509->not_after;
     i->hashtype = x509->hashtype;
10243534
     i->certSign = x509->certSign;
     i->codeSign = x509->codeSign;
     i->timeSign = x509->timeSign;
278b2b28
     i->isBlacklisted = x509->isBlacklisted;
8997495d
     i->next = m->crts;
     i->prev = NULL;
     if(m->crts)
 	m->crts->prev = i;
     m->crts = i;
71da61a7
 
7bcfb2f3
     m->items++;
8997495d
     return 0;
 }
 
7bcfb2f3
 void crtmgr_init(crtmgr *m) {
     m->crts = NULL;
     m->items = 0;
 }
 
 void crtmgr_del(crtmgr *m, cli_crt *x509) {
     cli_crt *i;
     for(i = m->crts; i; i = i->next) {
 	if(i==x509) {
2501f747
 	    if(i->prev)
7bcfb2f3
 		i->prev->next = i->next;
 	    else
 		m->crts = i->next;
 	    if(i->next)
 		i->next->prev = i->prev;
 	    cli_crt_clear(x509);
616c0259
 	    if ((x509->name))
 		free(x509->name);
7bcfb2f3
 	    free(x509);
 	    m->items--;
 	    return;
 	}
     }
 }
71da61a7
 
6bc5d0cb
 void crtmgr_free(crtmgr *m) {
     while(m->items)
 	crtmgr_del(m, m->crts);
 }
29fb5f5c
 
 static int crtmgr_rsa_verify(cli_crt *x509, mp_int *sig, cli_crt_hashtype hashtype, const uint8_t *refhash) {
     int keylen = mp_unsigned_bin_size(&x509->n), siglen = mp_unsigned_bin_size(sig);
089514b2
     int ret, j, objlen, hashlen = (hashtype == CLI_SHA1RSA) ? SHA1_HASH_SIZE : 16;
71da61a7
     uint8_t d[513];
     mp_int x;
 
     if((ret = mp_init(&x))) {
29fb5f5c
 	cli_errmsg("crtmgr_rsa_verify: mp_init failed with %d\n", ret);
71da61a7
 	return 1;
     }
29fb5f5c
 
     do {
 	if(MAX(keylen, siglen) - MIN(keylen, siglen) > 1)
 	    break;
 	if((ret = mp_exptmod(sig, &x509->e, &x509->n, &x))) {
 	    cli_warnmsg("crtmgr_rsa_verify: verification failed: mp_exptmod failed with %d\n", ret);
 	    break;
 	}
 	if(mp_unsigned_bin_size(&x) != keylen - 1)
 	    break;
 	if((ret = mp_to_unsigned_bin(&x, d))) {
 	    cli_warnmsg("crtmgr_rsa_verify: mp_unsigned_bin_size failed with %d\n", ret);
 	    break;
 	}
 	if(*d != 1) /* block type 1 */
 	    break;
 
 	keylen -= 1; /* 0xff padding */
 	for(j=1; j<keylen-2; j++)
 	    if(d[j] != 0xff)
 		break;
 	if(j == keylen - 2)
 	    break;
 	if(d[j] != 0) /* 0x00 separator */
 	    break;
 
 	j++;
 	keylen -= j; /* asn1 size */
 
089514b2
 	if(keylen < hashlen)
29fb5f5c
 	    break;
089514b2
 	if(keylen > hashlen) {
 	    /* hash is asn1 der encoded */
 	    /* SEQ { SEQ { OID, NULL }, OCTET STRING */
 	    if(keylen < 2 || d[j] != 0x30 || d[j+1] + 2 != keylen)
 		break;
 	    keylen -= 2;
 	    j+=2;
29fb5f5c
 
089514b2
 	    if(keylen <2 || d[j] != 0x30)
 		break;
29fb5f5c
 
089514b2
 	    objlen = d[j+1];
29fb5f5c
 
089514b2
 	    keylen -= 2;
 	    j+=2;
 	    if(keylen < objlen)
29fb5f5c
 		break;
089514b2
 	    if(objlen == 9) {
 		if(hashtype != CLI_SHA1RSA || memcmp(&d[j], "\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00", 9)) {
 		    cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
 		    break;
 		}
 	    } else if(objlen == 12) {
 		if(hashtype != CLI_MD5RSA || memcmp(&d[j], "\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00", 12)) {
 		    cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
 		    break;
 		}
 	    } else {
29fb5f5c
 		cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
 		break;
71da61a7
 	    }
29fb5f5c
 
089514b2
 	    keylen -= objlen;
 	    j += objlen;
 	    if(keylen < 2 || d[j] != 0x04 || d[j+1] != hashlen)
 		break;
 	    keylen -= 2;
 	    j+=2;
 	    if(keylen != hashlen)
 		break;
 	}
29fb5f5c
 	if(memcmp(&d[j], refhash, hashlen))
 	    break;
 
 	mp_clear(&x);
 	return 0;
 
     } while(0);
5e84efeb
 
29fb5f5c
     mp_clear(&x);
     return 1;
 }
 
 
2501f747
 cli_crt *crtmgr_verify_crt(crtmgr *m, cli_crt *x509) {
f5092717
     cli_crt *i = m->crts, *best = NULL;
     int score = 0;
29fb5f5c
 
62023156
     for (i = m->crts; i; i = i->next) {
         if (!memcmp(i->subject, x509->subject, sizeof(i->subject)) &&
             !memcmp(i->serial, x509->serial, sizeof(i->serial))) {
             if (i->isBlacklisted)
                 return i;
         }
     }
 
29fb5f5c
     for(i = m->crts; i; i = i->next) {
2501f747
 	if(i->certSign &&
 	   !memcmp(i->subject, x509->issuer, sizeof(i->subject)) &&
f5092717
 	   !crtmgr_rsa_verify(i, &x509->sig, x509->hashtype, x509->tbshash)) {
 	    int curscore;
 	    if((x509->codeSign & i->codeSign) == x509->codeSign && (x509->timeSign & i->timeSign) == x509->timeSign)
 		return i;
 	    curscore = (x509->codeSign & i->codeSign) + (x509->timeSign & i->timeSign);
 	    if(curscore > score) {
 		best = i;
 		score = curscore;
 	    }
 	}
71da61a7
     }
f5092717
     return best;
71da61a7
 }
 
f5092717
 cli_crt *crtmgr_verify_pkcs7(crtmgr *m, const uint8_t *issuer, const uint8_t *serial, const void *signature, unsigned int signature_len, cli_crt_hashtype hashtype, const uint8_t *refhash, cli_vrfy_type vrfytype) {
29fb5f5c
     cli_crt *i;
     mp_int sig;
     int ret;
 
     if(signature_len < 1024/8 || signature_len > 4096/8+1) {
 	cli_dbgmsg("crtmgr_verify_pkcs7: unsupported sig len: %u\n", signature_len);
f5092717
 	return NULL;
29fb5f5c
     }
     if((ret = mp_init(&sig))) {
 	cli_errmsg("crtmgr_verify_pkcs7: mp_init failed with %d\n", ret);
f5092717
 	return NULL;
29fb5f5c
     }
 
     if((ret=mp_read_unsigned_bin(&sig, signature, signature_len))) {
 	cli_warnmsg("crtmgr_verify_pkcs7: mp_read_unsigned_bin failed with %d\n", ret);
f5092717
 	return NULL;
29fb5f5c
     }
 
     for(i = m->crts; i; i = i->next) {
01e4650b
 	if(vrfytype == VRFY_CODE && !i->codeSign)
 	    continue;
 	if(vrfytype == VRFY_TIME && !i->timeSign)
 	    continue;
29fb5f5c
 	if(!memcmp(i->issuer, issuer, sizeof(i->issuer)) &&
a8e79d93
 	   !memcmp(i->serial, serial, sizeof(i->serial)) &&
278b2b28
 	   !crtmgr_rsa_verify(i, &sig, hashtype, refhash)) {
29fb5f5c
 	    break;
278b2b28
         }
29fb5f5c
     }
     mp_clear(&sig);
f5092717
     return i;
29fb5f5c
 }
71da61a7
 
56b4f4b0
 int crtmgr_add_roots(struct cl_engine *engine, crtmgr *m) {
cd94be7a
     cli_crt *crt;
56b4f4b0
     /*
d12f1646
      * Certs are cached in engine->cmgr. Copy from there.
56b4f4b0
      */
d12f1646
     if (m != &(engine->cmgr)) {
56b4f4b0
        for (crt = engine->cmgr.crts; crt != NULL; crt = crt->next) {
            if (crtmgr_add(m, crt)) {
                crtmgr_free(m);
                return 1;
            }
        }
 
        return 0;
     }
7bcfb2f3
 
d12f1646
     return 0;
71da61a7
 }