Add cryptographic card support to LUKS configuration

pull/2/head
Timothy Pearson 9 years ago
parent 9556cd9e69
commit 6095bc10ad

@ -46,11 +46,13 @@
#include <kpushbutton.h>
#include <kstdguiitem.h>
#include <tdemessagebox.h>
#include <ksslcertificate.h>
#include "cryptpassworddlg.h"
CryptPasswordDialog::CryptPasswordDialog(TQWidget *parent, TQString passwordPrompt, TQString caption)
: KDialogBase(Plain, ((caption == "")?i18n("Enter Password"):caption), Ok|Cancel, Ok, parent, 0L, true, true)
CryptPasswordDialog::CryptPasswordDialog(TQWidget *parent, TQString passwordPrompt, TQString caption, bool allow_card, KSSLCertificate* card_cert, bool* use_card)
: KDialogBase(Plain, ((caption == "")?i18n("Enter Password"):caption), Ok|Cancel, Ok, parent, 0L, true, true),
m_useCard(use_card)
{
m_base = new CryptPasswordDialogBase(plainPage());
@ -61,8 +63,19 @@ CryptPasswordDialog::CryptPasswordDialog(TQWidget *parent, TQString passwordProm
m_base->passwordPrompt->setText(passwordPrompt);
m_base->passwordIcon->setPixmap(SmallIcon("password.png"));
if (!allow_card) {
m_base->cardKeyButton->hide();
m_base->cardKeyInfo->hide();
}
else {
if (card_cert) {
m_base->cardKeyInfo->setText(card_cert->getSubject());
}
}
connect(m_base->textPasswordButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(processLockouts()));
connect(m_base->filePasswordButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(processLockouts()));
connect(m_base->cardKeyButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(processLockouts()));
connect(m_base->textPasswordEntry, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(processLockouts()));
connect(m_base->filePasswordURL, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(processLockouts()));
@ -78,9 +91,14 @@ CryptPasswordDialog::~CryptPasswordDialog()
TQByteArray CryptPasswordDialog::password() {
if (m_base->textPasswordButton->isOn() == true) {
m_password.duplicate(m_base->textPasswordEntry->password(), strlen(m_base->textPasswordEntry->password()));
if (m_useCard) *m_useCard = false;
}
else {
else if (m_base->filePasswordButton->isOn() == true) {
m_password = TQFile(m_base->filePasswordURL->url()).readAll();
if (m_useCard) *m_useCard = false;
}
else {
if (m_useCard) *m_useCard = true;
}
return m_password;
@ -90,6 +108,7 @@ void CryptPasswordDialog::processLockouts() {
if (m_base->textPasswordButton->isOn() == true) {
m_base->textPasswordEntry->setEnabled(true);
m_base->filePasswordURL->setEnabled(false);
m_base->textPasswordEntry->setFocus();
if (strlen(m_base->textPasswordEntry->password()) > 0) {
enableButtonOK(true);
}
@ -97,9 +116,10 @@ void CryptPasswordDialog::processLockouts() {
enableButtonOK(false);
}
}
else {
else if (m_base->filePasswordButton->isOn() == true) {
m_base->textPasswordEntry->setEnabled(false);
m_base->filePasswordURL->setEnabled(true);
m_base->filePasswordURL->setFocus();
if (TQFile(m_base->filePasswordURL->url()).exists()) {
enableButtonOK(true);
}
@ -107,6 +127,11 @@ void CryptPasswordDialog::processLockouts() {
enableButtonOK(false);
}
}
else {
m_base->textPasswordEntry->setEnabled(false);
m_base->filePasswordURL->setEnabled(false);
enableButtonOK(true);
}
}
void CryptPasswordDialog::virtual_hook( int id, void* data )

@ -23,6 +23,8 @@
#include "cryptpassworddlgbase.h"
class KSSLCertificate;
/**
*
* Dialog to enter LUKS passwords or password files
@ -39,7 +41,7 @@ public:
* Create a dialog that allows a user to enter LUKS passwords or password files
* @param parent Parent widget
*/
CryptPasswordDialog(TQWidget *parent, TQString passwordPrompt, TQString caption=TQString::null);
CryptPasswordDialog(TQWidget *parent, TQString passwordPrompt, TQString caption=TQString::null, bool allow_card=false, KSSLCertificate* card_cert=NULL, bool* use_card=NULL);
virtual ~CryptPasswordDialog();
TQByteArray password();
@ -53,6 +55,7 @@ private slots:
private:
CryptPasswordDialogBase* m_base;
TQByteArray m_password;
bool* m_useCard;
class CryptPasswordDialogPrivate;
CryptPasswordDialogPrivate* d;

@ -112,6 +112,22 @@
<number>17</number>
</property>
</widget>
<widget class="TQRadioButton" row="2" column="0" colspan="1">
<property name="name">
<cstring>cardKeyButton</cstring>
</property>
<property name="text">
<string>Cryptographic Card</string>
</property>
</widget>
<widget class="TQLabel" row="2" column="1" colspan="1">
<property name="name">
<cstring>cardKeyInfo</cstring>
</property>
<property name="text">
<string></string>
</property>
</widget>
</grid>
</widget>
</grid>

@ -41,6 +41,7 @@
#include <kpushbutton.h>
#include <kstdguiitem.h>
#include <tdemessagebox.h>
#include <ksslcertificate.h>
#include "cryptpassworddlg.h"
@ -800,12 +801,47 @@ void DevicePropertiesDialog::updateCryptographicCardStatusDisplay() {
int status = cdevice->cardPresent();
if ((status < 0) ||(status > 1)) {
base->labelCardStatus->setText(i18n("Unknown"));
base->labelCardCertificates->setText("");
base->groupCardCerts->hide();
}
else if (status == 0) {
base->labelCardStatus->setText(i18n("Empty"));
base->labelCardCertificates->setText("");
base->groupCardCerts->hide();
}
else if (status == 1) {
base->labelCardStatus->setText(i18n("Inserted") + TQString("<br>") + i18n("ATR: %1").arg(cdevice->cardATR()));
X509CertificatePtrList certList = cdevice->cardX509Certificates();
if (certList.count() > 0) {
// Assemble list of certificates on card
unsigned int certificate_number = 1;
TQString certInfo = "<qt>";
X509CertificatePtrList::iterator it;
for (it = certList.begin(); it != certList.end(); ++it) {
KSSLCertificate* tdeCert = KSSLCertificate::fromX509(*it);
KSSLCertificate::KSSLValidation validationStatus = tdeCert->validate();
certInfo += i18n("Certificate #%1").arg(certificate_number) + ":<br>";
certInfo += i18n("Subject") + ": " + tdeCert->getSubject() + "<br>";
certInfo += i18n("Issuer") + ": " + tdeCert->getIssuer() + "<br>";
certInfo += i18n("Status") + ": " + KSSLCertificate::verifyText(validationStatus) + "<br>";
certInfo += i18n("Valid From") + ": " + tdeCert->getNotBefore() + "<br>";
certInfo += i18n("Valid Until") + ": " + tdeCert->getNotAfter() + "<br>";
certInfo += i18n("Serial Number") + ": " + tdeCert->getSerialNumber() + "<br>";
certInfo += i18n("MD5 Digest") + ": " + tdeCert->getMD5DigestText() + "<br>";
certInfo += "<p>";
delete tdeCert;
certificate_number++;
}
certInfo += "</qt>";
base->labelCardCertificates->setText(certInfo);
base->groupCardCerts->show();
}
else {
base->labelCardCertificates->setText("");
base->groupCardCerts->hide();
}
}
}
@ -879,27 +915,169 @@ void DevicePropertiesDialog::unmountDisk() {
}
void DevicePropertiesDialog::cryptLUKSAddKey() {
int retcode;
if (m_device->type() == TDEGenericDeviceType::Disk) {
TDEStorageDevice* sdevice = static_cast<TDEStorageDevice*>(m_device);
TQListViewItem* lvi = base->cryptLUKSKeySlotList->selectedItem();
if (lvi) {
TDECryptographicCardDevice* cdevice = NULL;
unsigned int key_slot = lvi->text(0).toUInt();
bool allow_card = false;
bool use_card = false;
KSSLCertificate* card_cert = NULL;
X509* card_cert_x509;
TQString disk_uuid = sdevice->diskUUID();
TDEGenericDevice *hwdevice;
TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices();
TDEGenericHardwareList cardReaderList = hwdevices->listByDeviceClass(TDEGenericDeviceType::CryptographicCard);
for (hwdevice = cardReaderList.first(); hwdevice; hwdevice = cardReaderList.next()) {
cdevice = static_cast<TDECryptographicCardDevice*>(hwdevice);
X509CertificatePtrList certList = cdevice->cardX509Certificates();
if (certList.count() > 0) {
allow_card = true;
card_cert_x509 = certList[0];
card_cert = KSSLCertificate::fromX509(certList[0]);
}
}
TQByteArray new_password;
CryptPasswordDialog* passDlg = new CryptPasswordDialog(this, i18n("Enter the new LUKS password for key slot %1").arg(lvi->text(0)));
CryptPasswordDialog* passDlg = new CryptPasswordDialog(this, i18n("Enter the new LUKS password for key slot %1").arg(key_slot), TQString::null, allow_card, card_cert, &use_card);
if (passDlg->exec() == TQDialog::Accepted) {
new_password = passDlg->password();
if (allow_card && use_card) {
// Create new private key for disk device
if (!TQDir("/etc/trinity/luks").exists()) {
TQDir directory;
if (!directory.mkdir("/etc/trinity/luks", true)) {
KMessageBox::error(this, i18n("<qt><b>Key creation failed</b><br>Please check that you have write access to /etc/trinity and try again</qt>"), i18n("Key creation failure"));
delete card_cert;
return;
}
}
if (!TQDir("/etc/trinity/luks/card").exists()) {
TQDir directory;
if (!directory.mkdir("/etc/trinity/luks/card", true)) {
KMessageBox::error(this, i18n("<qt><b>Key creation failed</b><br>Please check that you have write access to /etc/trinity/luks and try again</qt>"), i18n("Key creation failure"));
delete card_cert;
return;
}
}
TQString cryptoFileName = TQString("/etc/trinity/luks/card/%1_slot%2").arg(disk_uuid).arg(key_slot);
TQFile file(cryptoFileName);
if (file.exists()) {
if (KMessageBox::warningYesNo(this, i18n("<qt><b>You are about to overwrite an existing card key for LUKS key slot %1</b><br>This action cannot be undone<p>Are you sure you want to proceed?</qt>").arg(key_slot), i18n("Confirmation Required")) != KMessageBox::Yes) {
delete card_cert;
return;
}
}
if (file.open(IO_WriteOnly)) {
TQByteArray randomKey;
TQByteArray encryptedRandomKey;
// Create a new secret key using the public key from the card certificate
if (TDECryptographicCardDevice::createNewSecretRSAKeyFromCertificate(randomKey, encryptedRandomKey, card_cert_x509) < 0) {
KMessageBox::error(this, i18n("<qt><b>Key creation failed</b><br>Unable to create new secret key using the provided X509 certificate</qt>"), i18n("Key creation failure"));
delete card_cert;
return;
}
// Write the encrypted key file to disk
file.writeBlock(encryptedRandomKey, encryptedRandomKey.size());
file.close();
// Use the secret key as the LUKS passcode
new_password = randomKey;
}
else {
KMessageBox::error(this, i18n("<qt><b>Key creation failed</b><br>Please check that you have write access to /etc/trinity/luks/card and try again</qt>"), i18n("Key creation failure"));
delete card_cert;
return;
}
}
delete passDlg;
if (!sdevice->cryptOperationsUnlockPasswordSet()) {
TQCString password;
passDlg = new CryptPasswordDialog(this, i18n("Enter the LUKS device unlock password"));
passDlg = new CryptPasswordDialog(this, i18n("Enter the LUKS device unlock password"), TQString::null, allow_card, card_cert, &use_card);
if (passDlg->exec() == TQDialog::Accepted) {
sdevice->cryptSetOperationsUnlockPassword(passDlg->password());
TQByteArray unlockPassword = passDlg->password();
if (use_card) {
// List all matching keys in directory and try each in turn...
TQDir luksKeyDir("/etc/trinity/luks/card/");
luksKeyDir.setFilter(TQDir::Files);
luksKeyDir.setSorting(TQDir::Unsorted);
TQValueList<TQByteArray> luksCryptedList;
TQValueList<TQByteArray> luksDecryptedList;
TQValueList<int> luksSlotNumberList;
const TQFileInfoList *luksKeyDirList = luksKeyDir.entryInfoList();
TQFileInfoListIterator it(*luksKeyDirList);
TQFileInfo *luksKeyFileInfo;
TQString errstr;
while ((luksKeyFileInfo = it.current()) != 0) {
if (luksKeyFileInfo->fileName().startsWith(disk_uuid) && luksKeyFileInfo->fileName().contains("_slot")) {
// Found candidate, try decryption
TQFile luksKeyFile(luksKeyFileInfo->absFilePath());
if (luksKeyFile.open(IO_ReadOnly)) {
TQByteArray keycrypted = luksKeyFile.readAll();
luksCryptedList.append(keycrypted);
// Parse the file name and find the matching key slot
int current_card_keyslot = -1;
TQString fileName = luksKeyFile.name();
int pos = fileName.find("_slot");
if (pos >= 0) {
fileName.remove(0, pos + strlen("_slot"));
current_card_keyslot = fileName.toInt();
luksSlotNumberList.append(current_card_keyslot);
}
}
}
++it;
}
// Decrypt LUKS keys
TQValueList<int> retCodeList;
retcode = cdevice->decryptDataEncryptedWithCertPublicKey(luksCryptedList, luksDecryptedList, retCodeList, &errstr);
TQValueList<TQByteArray>::iterator it2;
TQValueList<int>::iterator it3;
TQValueList<int>::iterator it4;
for (it2 = luksDecryptedList.begin(), it3 = retCodeList.begin(), it4 = luksSlotNumberList.begin(); it2 != luksDecryptedList.end(); ++it2, ++it3, ++it4) {
TQByteArray luksKeyData = *it2;
retcode = *it3;
int current_card_keyslot = *it4;
if (retcode == -3) {
// User cancelled
break;
}
if (retcode < 0) {
// ERROR
}
else {
// Key decryption successful, try to open LUKS device...
sdevice->cryptSetOperationsUnlockPassword(luksKeyData);
if (sdevice->cryptCheckKey(current_card_keyslot) == TDELUKSResult::Success) {
break;
}
else {
sdevice->cryptClearOperationsUnlockPassword();
}
}
}
if (!sdevice->cryptOperationsUnlockPasswordSet()) {
KMessageBox::error(this, i18n("<qt><b>Key write failed</b><br>Please check the LUKS password and try again</qt>"), i18n("Key write failure"));
}
}
else {
sdevice->cryptSetOperationsUnlockPassword(unlockPassword);
}
}
delete passDlg;
}
if (sdevice->cryptOperationsUnlockPasswordSet()) {
if ((lvi->text(1) == sdevice->cryptKeySlotFriendlyName(TDELUKSKeySlotStatus::Inactive)) || (KMessageBox::warningYesNo(this, i18n("<qt><b>You are about to overwrite the key in key slot %1</b><br>This action cannot be undone<p>Are you sure you want to proceed?</qt>").arg(lvi->text(0)), i18n("Confirmation Required")) == KMessageBox::Yes)) {
if (sdevice->cryptAddKey(lvi->text(0).toUInt(), new_password) != TDELUKSResult::Success) {
if ((lvi->text(1) == sdevice->cryptKeySlotFriendlyName(TDELUKSKeySlotStatus::Inactive)) || (KMessageBox::warningYesNo(this, i18n("<qt><b>You are about to overwrite the key in key slot %1</b><br>This action cannot be undone<p>Are you sure you want to proceed?</qt>").arg(key_slot), i18n("Confirmation Required")) == KMessageBox::Yes)) {
if (sdevice->cryptAddKey(key_slot, new_password) != TDELUKSResult::Success) {
sdevice->cryptClearOperationsUnlockPassword();
KMessageBox::error(this, i18n("<qt><b>Key write failed</b><br>Please check the LUKS password and try again</qt>"), i18n("Key write failure"));
}
@ -909,6 +1087,7 @@ void DevicePropertiesDialog::cryptLUKSAddKey() {
else {
delete passDlg;
}
delete card_cert;
}
}
@ -921,17 +1100,52 @@ void DevicePropertiesDialog::cryptLUKSDelKey() {
TQListViewItem* lvi = base->cryptLUKSKeySlotList->selectedItem();
if (lvi) {
unsigned int key_slot = lvi->text(0).toUInt();
if (KMessageBox::warningYesNo(this, i18n("<qt><b>You are about to purge the key in key slot %1</b><br>This action cannot be undone<p>Are you sure you want to proceed?</qt>").arg(lvi->text(0)), i18n("Confirmation Required")) == KMessageBox::Yes) {
if (sdevice->cryptKeySlotStatus()[lvi->text(0).toUInt()] & TDELUKSKeySlotStatus::Last) {
if (sdevice->cryptKeySlotStatus()[key_slot] & TDELUKSKeySlotStatus::Last) {
if (KMessageBox::warningYesNo(this, i18n("<qt><b>You are about to purge the last active key from the device!</b><p>This action will render the contents of the encrypted device permanently inaccessable and cannot be undone<p>Are you sure you want to proceed?</qt>"), i18n("Confirmation Required")) != KMessageBox::Yes) {
cryptLUKSPopulateList();
return;
}
}
if (sdevice->cryptDelKey(lvi->text(0).toUInt()) != TDELUKSResult::Success) {
if (sdevice->cryptDelKey(key_slot) != TDELUKSResult::Success) {
sdevice->cryptClearOperationsUnlockPassword();
KMessageBox::error(this, i18n("<qt><b>Key purge failed</b><br>The key in key slot %1 is still active</qt>").arg(lvi->text(0)), i18n("Key purge failure"));
}
else {
// See if there was a cryptographic card key associated with this device and slot
TQString disk_uuid = sdevice->diskUUID();
TQDir luksKeyDir("/etc/trinity/luks/card/");
luksKeyDir.setFilter(TQDir::Files);
luksKeyDir.setSorting(TQDir::Unsorted);
const TQFileInfoList *luksKeyDirList = luksKeyDir.entryInfoList();
TQFileInfoListIterator it(*luksKeyDirList);
TQFileInfo *luksKeyFileInfo;
TQString errstr;
while ((luksKeyFileInfo = it.current()) != 0) {
if (luksKeyFileInfo->fileName().startsWith(disk_uuid) && luksKeyFileInfo->fileName().contains("_slot")) {
// Parse the file name and find the matching key slot
int current_card_keyslot = -1;
TQString fileName = luksKeyFileInfo->absFilePath();
TQString fileNameSlot = fileName;
int pos = fileNameSlot.find("_slot");
if (pos >= 0) {
fileNameSlot.remove(0, pos + strlen("_slot"));
current_card_keyslot = fileNameSlot.toInt();
if (current_card_keyslot >= 0) {
if ((unsigned int)current_card_keyslot == key_slot) {
if (!TQFile(fileName).remove()) {
KMessageBox::error(this, i18n("<qt><b>Card key purge failed</b><br>The card key for slot %1 has been fully deactivated but is still present on your system<br>This does not present a significant security risk</qt>").arg(lvi->text(0)), i18n("Key purge failure"));
}
break;
}
}
}
}
++it;
}
}
}
}
}

@ -1652,6 +1652,30 @@
</widget>
</grid>
</widget>
<widget class="TQGroupBox" row="1" column="0">
<property name="name">
<cstring>groupCardCerts</cstring>
</property>
<property name="title">
<string>Card Certificates</string>
</property>
<grid>
<property name="name">
<cstring>unnamed</cstring>
</property>
<widget class="TQLabel" row="0" column="0" colspan="1">
<property name="name">
<cstring>labelCardCertificates</cstring>
</property>
<property name="text">
<string></string>
</property>
<property name="alignment">
<set>AlignTop|AlignLeft</set>
</property>
</widget>
</grid>
</widget>
<spacer row="8" column="0">
<property name="name" stdset="0">
<cstring>Spacer4</cstring>

@ -42,6 +42,7 @@
#include <kgenericfactory.h>
#include <unistd.h>
#include <kpassdlg.h>
#include <ksimpleconfig.h>
#include <string>
#include <stdio.h>
@ -147,7 +148,10 @@ void TDEHWManager::populateTreeView()
TDEGenericDevice *hwdevice;
for ( hwdevice = hwlist.first(); hwdevice; hwdevice = hwlist.next() ) {
if (hwdevice->type() == TDEGenericDeviceType::CryptographicCard) {
static_cast<TDECryptographicCardDevice*>(hwdevice)->enableCardMonitoring(true);
TDECryptographicCardDevice* cdevice = static_cast<TDECryptographicCardDevice*>(hwdevice);
connect(cdevice, SIGNAL(pinRequested(TQString,TDECryptographicCardDevice*)), this, SLOT(cryptographicCardPinRequested(TQString,TDECryptographicCardDevice*)));
cdevice->enableCardMonitoring(true);
cdevice->enablePINEntryCallbacks(true);
}
DeviceIconItem* item = new DeviceIconItem(base->deviceTree, hwdevice->detailedFriendlyName(), hwdevice->icon(base->deviceTree->iconSize()), hwdevice);
if ((!selected_syspath.isNull()) && (hwdevice->systemPath() == selected_syspath)) {
@ -166,7 +170,10 @@ void TDEHWManager::populateTreeView()
TDEGenericHardwareList hwlist = hwdevices->listByDeviceClass((TDEGenericDeviceType::TDEGenericDeviceType)i);
for ( hwdevice = hwlist.first(); hwdevice; hwdevice = hwlist.next() ) {
if (hwdevice->type() == TDEGenericDeviceType::CryptographicCard) {
static_cast<TDECryptographicCardDevice*>(hwdevice)->enableCardMonitoring(true);
TDECryptographicCardDevice* cdevice = static_cast<TDECryptographicCardDevice*>(hwdevice);
connect(cdevice, SIGNAL(pinRequested(TQString,TDECryptographicCardDevice*)), this, SLOT(cryptographicCardPinRequested(TQString,TDECryptographicCardDevice*)));
cdevice->enableCardMonitoring(true);
cdevice->enablePINEntryCallbacks(true);
}
DeviceIconItem* item = new DeviceIconItem(rootitem, hwdevice->detailedFriendlyName(), hwdevice->icon(base->deviceTree->iconSize()), hwdevice);
if ((!selected_syspath.isNull()) && (hwdevice->systemPath() == selected_syspath)) {
@ -186,7 +193,10 @@ void TDEHWManager::populateTreeViewLeaf(DeviceIconItem *parent, bool show_by_con
TDEGenericDevice *hwdevice;
for ( hwdevice = hwlist.first(); hwdevice; hwdevice = hwlist.next() ) {
if (hwdevice->type() == TDEGenericDeviceType::CryptographicCard) {
static_cast<TDECryptographicCardDevice*>(hwdevice)->enableCardMonitoring(true);
TDECryptographicCardDevice* cdevice = static_cast<TDECryptographicCardDevice*>(hwdevice);
connect(cdevice, SIGNAL(pinRequested(TQString,TDECryptographicCardDevice*)), this, SLOT(cryptographicCardPinRequested(TQString,TDECryptographicCardDevice*)));
cdevice->enableCardMonitoring(true);
cdevice->enablePINEntryCallbacks(true);
}
if (hwdevice->parentDevice() == parent->device()) {
DeviceIconItem* item = new DeviceIconItem(parent, hwdevice->detailedFriendlyName(), hwdevice->icon(base->deviceTree->iconSize()), hwdevice);
@ -218,6 +228,17 @@ void TDEHWManager::deviceChanged(TDEGenericDevice* device) {
}
}
void TDEHWManager::cryptographicCardPinRequested(TQString prompt, TDECryptographicCardDevice* cdevice) {
TQCString password;
int result = KPasswordDialog::getPassword(password, prompt);
if (result == KPasswordDialog::Accepted) {
cdevice->setProvidedPin(password);
}
else {
cdevice->setProvidedPin(TQString::null);
}
}
TQString TDEHWManager::quickHelp() const
{
return i18n("<h1>TDE Hardware Device Manager</h1> This module allows you to configure hardware devices on your system");

@ -61,6 +61,7 @@ private slots:
void populateTreeView();
void populateTreeViewLeaf(DeviceIconItem *parent, bool show_by_connection, TQString selected_syspath);
void deviceChanged(TDEGenericDevice*);
void cryptographicCardPinRequested(TQString prompt, TDECryptographicCardDevice* cdevice);
private:
TDEHWManagerBase *base;

Loading…
Cancel
Save