You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
5.3 KiB
171 lines
5.3 KiB
#include "smartcardauth.h" |
|
#include "pindialog.h" |
|
#include <QtNetwork/private/qsslsocket_openssl_symbols_p.h> |
|
|
|
#include "engine_sct.h" |
|
|
|
#include <string> |
|
|
|
#include <QtCore/QDebug> |
|
|
|
ENGINE* SmartCardAuth::e=NULL; |
|
enum_certs_s* SmartCardAuth::certs_found=NULL; |
|
QWidget* SmartCardAuth::parent=0; |
|
bool SmartCardAuth::pin_configured=false; |
|
bool SmartCardAuth::pin_rejected=false; |
|
|
|
void SmartCardAuth::initialize() { |
|
//QSslSocketPrivate::ensureInitialized(); |
|
|
|
ENGINE_load_dynamic(); |
|
e = ENGINE_by_id("dynamic"); |
|
Q_ASSERT(e); |
|
|
|
int r=ENGINE_ctrl_cmd_string(e, "SO_PATH", "../openssl-act-engine/src/.libs/libengine_act.so", 0); |
|
Q_ASSERT(r); |
|
r=ENGINE_ctrl_cmd_string(e, "ID", "act", 0); |
|
Q_ASSERT(r); |
|
r=ENGINE_ctrl_cmd_string(e, "LIST_ADD", "1", 0); |
|
Q_ASSERT(r); |
|
r=ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0); |
|
Q_ASSERT(r); |
|
|
|
if(!r) |
|
{ |
|
unsigned int err = 0; |
|
while((err = q_ERR_get_error())) |
|
{ |
|
char *str = q_ERR_error_string(err, NULL); |
|
fprintf(stderr,"%s\n", str); |
|
} |
|
} |
|
|
|
r=ENGINE_init(e); |
|
|
|
} |
|
|
|
void SmartCardAuth::deinitialize() { |
|
q_ENGINE_finish(e); |
|
q_ENGINE_cleanup(); |
|
} |
|
|
|
void SmartCardAuth::setPinDlgParent(QWidget* p) { |
|
parent=p; |
|
} |
|
|
|
int SmartCardAuth::client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey) |
|
{ |
|
// NB: Keep in mind that this function is called for EVERY SSL connection to be opened. |
|
|
|
for(size_t i=certs_found->num_certs;i--;) |
|
{ |
|
const char *id_p = certs_found->certificate[i].id; |
|
|
|
if(id_p == NULL) continue; |
|
|
|
// Name has the format "slot-x-name-SwissSign_digSig" for the certificate/key we're looking for |
|
std::string name(certs_found->certificate[i].name); |
|
qDebug()<<"Certificate:"<<name.c_str(); |
|
std::string compare("-name-SwissSign_digSig"); |
|
|
|
// Compare the rightmost part of the retrieved name to locate the certificate/keypair |
|
size_t pos = name.length() - compare.length(); |
|
if(name.substr(pos) != compare) |
|
continue; |
|
|
|
// // Filter out the correct certificate depending on well-known sorting criteria |
|
// // Given example searches for a keyword in the certificate's subject, but since we've got |
|
// // the decoded certificate as an X509* structure we can check all the fields. |
|
// X509_NAME* subj_name = q_X509_get_subject_name(certs_found->certificate[i].cert); |
|
// int maxlen = 2048; |
|
// char buf[maxlen+1]; |
|
// buf[maxlen]=0; |
|
// q_X509_NAME_oneline(subj_name, buf, maxlen); |
|
// std::string subject(buf); |
|
|
|
// const char *compare="CN=Carsten Pluntke"; |
|
// if(subject.find(compare) == std::string::npos) |
|
// continue; |
|
|
|
// Here we found a suitable certificate. |
|
|
|
// Now prepare the reference to the SmartCard's private key and a copy of the certificate |
|
// to pass back to the caller. |
|
*x509 = q_X509_dup(certs_found->certificate[i].cert); |
|
*pkey = NULL; |
|
|
|
// If we don't have a PIN yet, pop up a dialog, ask for a PIN and pass it along to the engine |
|
// for usage. |
|
if(!pin_configured) |
|
{ |
|
PinDialog dlg(parent); |
|
int ok=dlg.exec(); |
|
if(ok!=1) return 0; // User cancelled |
|
QByteArray pinByteArray=dlg.pin().toAscii(); |
|
char *pin_str = pinByteArray.data(); |
|
|
|
// The engine control command takes a copy and overwrites the source array |
|
if(q_ENGINE_ctrl_cmd_string(e, "PIN", pin_str, 0)) |
|
pin_configured = true; |
|
else |
|
return 0; // Engine refuses to take the PIN |
|
|
|
*pkey = q_ENGINE_load_private_key(e, id_p, NULL, NULL); |
|
|
|
// We do a test authorization on loading of the private key. If the operation fails at all, |
|
// DON'T try again (see below) or we would instantly lock the card in a single session because |
|
// of the retries! |
|
if(!*pkey) |
|
pin_rejected = true; |
|
} |
|
|
|
// Second to nth iteration: We skipped the PIN dialog here, now load the key if we don't have the |
|
// explicit information not to do it (because the PIN is wrong) |
|
if(!*pkey && !pin_rejected) |
|
*pkey = q_ENGINE_load_private_key(e, id_p, NULL, NULL); |
|
|
|
break; |
|
} |
|
|
|
if(!*x509) { |
|
qWarning("Unable to load certificate"); |
|
return 0; |
|
} |
|
|
|
if(!*pkey) { |
|
qWarning("Unable to load key"); |
|
return 0; |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
bool SmartCardAuth::hookInitSslContext(SSL_CTX *ctx) |
|
{ |
|
bool result = false; |
|
|
|
if(!certs_found) |
|
result = (q_ENGINE_ctrl_cmd(e, "ENUM_CERTS", 0, &certs_found, NULL, 0) != 0); |
|
else |
|
result = true; |
|
|
|
#ifdef USE_CERTIFICATE_FILE |
|
// Load a specific intermediate certificate from a file |
|
//! @todo PEM-File |
|
BIO* cert_file= q_BIO_new_file("swsign_interm.pem", "r"); |
|
X509* interm=q_PEM_read_bio_X509(cert_file,NULL,NULL, NULL); |
|
q_BIO_free(cert_file); |
|
|
|
q_SSL_CTX_add_extra_chain_cert(ctx,interm); |
|
#else |
|
// Add all of the card's certificates without a private key as intermediate certs |
|
for(size_t i=certs_found->num_certs;i--;) |
|
{ |
|
if(certs_found->certificate[i].id == NULL) |
|
q_SSL_CTX_add_extra_chain_cert(ctx, q_X509_dup(certs_found->certificate[i].cert)); |
|
} |
|
#endif |
|
|
|
q_SSL_CTX_set_client_cert_cb(ctx,client_cert_cb); |
|
return true; |
|
}
|
|
|