- Axel Tillequin and Arnaud Ebalard added support for CURLOPT_ISSUERCERT, for

OpenSSL, NSS and GnuTLS-built libcurls.
This commit is contained in:
Daniel Stenberg
2008-06-06 20:52:32 +00:00
parent b39d1e9b9d
commit 621c2b9015
12 changed files with 199 additions and 12 deletions

View File

@@ -143,6 +143,32 @@ static void showtime(struct SessionHandle *data,
infof(data, "%s", data->state.buffer);
}
static gnutls_datum load_file (const char *file)
{
FILE *f;
gnutls_datum loaded_file = { NULL, 0 };
long filelen;
void *ptr;
if (!(f = fopen(file, "r"))
|| fseek(f, 0, SEEK_END) != 0
|| (filelen = ftell(f)) < 0
|| fseek(f, 0, SEEK_SET) != 0
|| !(ptr = malloc((size_t)filelen))
|| fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) {
return loaded_file;
}
loaded_file.data = ptr;
loaded_file.size = (unsigned int)filelen;
return loaded_file;
}
static void unload_file(gnutls_datum data) {
free(data.data);
}
/* this function does a BLOCKING SSL/TLS (re-)handshake */
static CURLcode handshake(struct connectdata *conn,
gnutls_session session,
@@ -221,7 +247,8 @@ Curl_gtls_connect(struct connectdata *conn,
unsigned int cert_list_size;
const gnutls_datum *chainp;
unsigned int verify_status;
gnutls_x509_crt x509_cert;
gnutls_x509_crt x509_cert,x509_issuer;
gnutls_datum issuerp;
char certbuf[256]; /* big enough? */
size_t size;
unsigned int algo;
@@ -375,7 +402,9 @@ Curl_gtls_connect(struct connectdata *conn,
chainp = gnutls_certificate_get_peers(session, &cert_list_size);
if(!chainp) {
if(data->set.ssl.verifypeer) {
if(data->set.ssl.verifypeer ||
data->set.ssl.verifyhost ||
data->set.ssl.issuercert) {
failf(data, "failed to get server cert");
return CURLE_PEER_FAILED_VERIFICATION;
}
@@ -399,8 +428,9 @@ Curl_gtls_connect(struct connectdata *conn,
/* verify_status is a bitmask of gnutls_certificate_status bits */
if(verify_status & GNUTLS_CERT_INVALID) {
if(data->set.ssl.verifypeer) {
failf(data, "server certificate verification failed. CAfile: %s",
data->set.ssl.CAfile?data->set.ssl.CAfile:"none");
failf(data, "server certificate verification failed. CAfile: %s "
"CRLfile: %s", data->set.ssl.CAfile?data->set.ssl.CAfile:"none",
data->set.ssl.CRLfile?data->set.ssl.CRLfile:"none");
return CURLE_SSL_CACERT;
}
else
@@ -419,6 +449,21 @@ Curl_gtls_connect(struct connectdata *conn,
gnutls_x509_crt_t format */
gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);
if (data->set.ssl.issuercert) {
gnutls_x509_crt_init(&x509_issuer);
issuerp = load_file(data->set.ssl.issuercert);
gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
rc = gnutls_x509_crt_check_issuer(x509_cert,x509_issuer);
unload_file(issuerp);
if (rc <= 0) {
failf(data, "server certificate issuer check failed (IssuerCert: %s)",
data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
return CURLE_SSL_ISSUER_ERROR;
}
infof(data,"\t server certificate issuer check OK (Issuer Cert: %s)\n",
data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
}
size=sizeof(certbuf);
rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME,
0, /* the first and only one */

View File

@@ -721,6 +721,43 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
return;
}
/**
*
* Check that the Peer certificate's issuer certificate matches the one found
* by issuer_nickname. This is not exactly the way OpenSSL and GNU TLS do the
* issuer check, so we provide comments that mimic the OpenSSL
* X509_check_issued function (in x509v3/v3_purp.c)
*/
static SECStatus check_issuer_cert(struct connectdata *conn, PRFileDesc *sock, char* issuer_nickname)
{
CERTCertificate *cert,*cert_issuer,*issuer;
SECStatus res=SECSuccess;
void *proto_win = NULL;
/*
PRArenaPool *tmpArena = NULL;
CERTAuthKeyID *authorityKeyID = NULL;
SECITEM *caname = NULL;
*/
cert = SSL_PeerCertificate(sock);
cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner);
proto_win = SSL_RevealPinArg(sock);
issuer = NULL;
issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win);
if ((!cert_issuer) || (!issuer))
res = SECFailure;
else if (CERT_CompareCerts(cert_issuer,issuer)==PR_FALSE)
res = SECFailure;
CERT_DestroyCertificate(cert);
CERT_DestroyCertificate(issuer);
CERT_DestroyCertificate(cert_issuer);
return res;
}
/**
*
* Callback to pick the SSL client certificate.
@@ -853,7 +890,7 @@ int Curl_nss_close_all(struct SessionHandle *data)
return 0;
}
CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex)
CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
{
PRInt32 err;
PRFileDesc *model = NULL;
@@ -1046,6 +1083,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex)
}
else {
strncpy(nickname, data->set.str[STRING_CERT], PATH_MAX);
nickname[PATH_MAX-1]=0; /* make sure this is zero terminated */
}
if(nss_Init_Tokens(conn) != SECSuccess) {
free(nickname);
@@ -1061,7 +1099,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex)
connssl->client_nickname = strdup(nickname);
if(SSL_GetClientAuthDataHook(model,
(SSLGetClientAuthData) SelectClientCert,
(void *)connssl->client_nickname) !=
(void *)connssl) !=
SECSuccess) {
curlerr = CURLE_SSL_CERTPROBLEM;
goto error;
@@ -1074,6 +1112,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex)
else
connssl->client_nickname = NULL;
/* Import our model socket onto the existing file descriptor */
connssl->handle = PR_ImportTCPSocket(sockfd);
connssl->handle = SSL_ImportFD(model, connssl->handle);
@@ -1099,6 +1138,32 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex)
display_conn_info(conn, connssl->handle);
if (data->set.str[STRING_SSL_ISSUERCERT]) {
char *n;
char *nickname;
nickname = (char *)malloc(PATH_MAX);
if(is_file(data->set.str[STRING_SSL_ISSUERCERT])) {
n = strrchr(data->set.str[STRING_SSL_ISSUERCERT], '/');
if (n) {
n++; /* skip last slash */
snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", 1, n);
}
}
else {
strncpy(nickname, data->set.str[STRING_SSL_ISSUERCERT], PATH_MAX);
nickname[PATH_MAX-1]=0; /* make sure this is zero terminated */
}
if (check_issuer_cert(conn,connssl->handle,nickname)==SECFailure) {
infof(data,"SSL certificate issuer check failed\n");
free(nickname);
curlerr = CURLE_SSL_ISSUER_ERROR;
goto error;
}
else {
infof("SSL certificate issuer check ok\n");
}
}
return CURLE_OK;
error:

View File

@@ -1629,6 +1629,9 @@ static CURLcode servercert(struct connectdata *conn,
long lerr;
ASN1_TIME *certdate;
struct SessionHandle *data = conn->data;
X509 *issuer;
FILE *fp;
connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
if(!connssl->server_cert) {
if(strict)
@@ -1678,6 +1681,41 @@ static CURLcode servercert(struct connectdata *conn,
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
/* e.g. match issuer name with provided issuer certificate */
if (data->set.str[STRING_SSL_ISSUERCERT]) {
if (! (fp=fopen(data->set.str[STRING_SSL_ISSUERCERT],"r"))) {
if (strict)
failf(data, "SSL: Unable to open issuer cert (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
}
issuer = PEM_read_X509(fp,NULL,NULL,NULL);
if (!issuer) {
if (strict)
failf(data, "SSL: Unable to read issuer cert (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(connssl->server_cert);
X509_free(issuer);
fclose(fp);
return CURLE_SSL_ISSUER_ERROR;
}
fclose(fp);
if (X509_check_issued(issuer,connssl->server_cert) != X509_V_OK) {
if (strict)
failf(data, "SSL: Certificate issuer check failed (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(connssl->server_cert);
X509_free(issuer);
connssl->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
}
infof(data, "\t SSL certificate issuer check ok (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(issuer);
}
lerr = data->set.ssl.certverifyresult=
SSL_get_verify_result(connssl->handle);
if(data->set.ssl.certverifyresult != X509_V_OK) {
@@ -1690,12 +1728,12 @@ static CURLcode servercert(struct connectdata *conn,
retcode = CURLE_PEER_FAILED_VERIFICATION;
}
else
infof(data, "SSL certificate verify result: %s (%ld),"
infof(data, "\t SSL certificate verify result: %s (%ld),"
" continuing anyway.\n",
X509_verify_cert_error_string(lerr), lerr);
}
else
infof(data, "SSL certificate verify ok.\n");
infof(data, "\t SSL certificate verify ok.\n");
}
X509_free(connssl->server_cert);

View File

@@ -225,6 +225,9 @@ curl_easy_strerror(CURLcode error)
case CURLE_SSL_CRL_BADFILE:
return "Failed to load CRL file (path? access rights?, format?)";
case CURLE_SSL_ISSUER_ERROR:
return "Issuer check against peer certificate failed";
case CURLE_SEND_FAIL_REWIND:
return "Send failed since rewinding of the data stream failed";

View File

@@ -1819,6 +1819,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
result = setstropt(&data->set.str[STRING_SSL_CRLFILE],
va_arg(param, char *));
break;
case CURLOPT_ISSUERCERT:
/*
* Set Issuer certificate file
* to check certificates issuer
*/
result = setstropt(&data->set.str[STRING_SSL_ISSUERCERT],
va_arg(param, char *));
break;
case CURLOPT_TELNETOPTIONS:
/*
* Set a linked list of telnet options
@@ -3960,6 +3968,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
data->set.ssl.CApath = data->set.str[STRING_SSL_CAPATH];
data->set.ssl.CAfile = data->set.str[STRING_SSL_CAFILE];
data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE];
data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
data->set.ssl.random_file = data->set.str[STRING_SSL_RANDOM_FILE];
data->set.ssl.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
data->set.ssl.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST];

View File

@@ -213,6 +213,7 @@ struct ssl_config_data {
char *CApath; /* certificate dir (doesn't work on windows) */
char *CAfile; /* cerficate to verify peer against */
char *CRLfile; /* CRL to check cerficate revocation */
char *issuercert; /* optional issuer cerficate filename */
char *random_file; /* path to file containing "random" data */
char *egdsocket; /* path to file containing the EGD daemon socket */
char *cipher_list; /* list of ciphers to use */
@@ -1319,6 +1320,7 @@ enum dupstring {
STRING_USERPWD, /* <user:password>, if used */
STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */
STRING_SSL_CRLFILE, /* crl file to check certificate */
STRING_SSL_ISSUERCERT, /* issuer cert file to check certificate */
/* -- end of strings -- */
STRING_LAST /* not used, just an end-of-list marker */