--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vpnengine/utlpkcs12/src/pkcs12vpn.cpp Thu Dec 17 09:14:51 2009 +0200
@@ -0,0 +1,642 @@
+/*
+* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: PKCS#12 data handler
+*
+*/
+
+
+
+#include <x509cert.h>
+#include <mpkcs12.h>
+
+#include <e32std.h>
+
+#include <securityerr.h>
+#include <sysutil.h>
+
+#include "pkcs12vpn.h"
+#include "logvpncommon.h"
+#include "vpnnotifierdefs.h"
+
+///////////////////////////////////
+// Construction and destruction
+
+EXPORT_C CPKCS12Handler* CPKCS12Handler::NewL(RPKIServiceAPI& aPkiServiceApi)
+ {
+ LOG_("-> CPKCS12Handler::NewL()");
+ CPKCS12Handler* self = NewLC(aPkiServiceApi);
+ CleanupStack::Pop(self);
+ LOG_("<- CPKCS12Handler::NewL()");
+ return self;
+ }
+
+EXPORT_C CPKCS12Handler* CPKCS12Handler::NewLC(RPKIServiceAPI& aPkiServiceApi)
+ {
+ LOG_("-> CPKCS12Handler::NewLC()");
+ CPKCS12Handler* self = new (ELeave) CPKCS12Handler(aPkiServiceApi);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ LOG_("<- CPKCS12Handler::NewLC()");
+ return self;
+ }
+
+
+EXPORT_C CPKCS12Handler* CPKCS12Handler::NewL()
+ {
+ LOG_("-> CPKCS12Handler::NewL(empty)");
+ CPKCS12Handler* self = NewLC();
+ CleanupStack::Pop(self);
+ LOG_("<- CPKCS12Handler::NewL(empty)");
+ return self;
+ }
+
+EXPORT_C CPKCS12Handler* CPKCS12Handler::NewLC()
+ {
+ LOG_("-> CPKCS12Handler::NewLC(empty)");
+ CPKCS12Handler* self = new (ELeave) CPKCS12Handler();
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ LOG_("<- CPKCS12Handler::NewLC(empty)");
+ return self;
+ }
+
+/**
+ * Release resources.
+ * Note: CPKCS12Handler's parent owns the RPKIServiceAPI instance
+ */
+EXPORT_C CPKCS12Handler::~CPKCS12Handler()
+ {
+ LOG_("-> CPKCS12Handler::~CPKCS12Handler()");
+ iPkcsHandler->Release();
+ iNotifier.Close();
+ delete iPassword;
+ delete iOutputDir;
+
+ iFileOut.Close();
+ iFs.Close();
+
+ LOG_("<- CPKCS12Handler::~CPKCS12Handler()");
+ }
+
+/**
+ * Maintain a reference to an instance of PKI API provided by the caller
+ */
+CPKCS12Handler::CPKCS12Handler(RPKIServiceAPI& aPkiServiceApi) :
+ iPkiService(&aPkiServiceApi), iDeletable(ETrue)
+ {
+ LOG_("-> CPKCS12Handler::CPKCS12Handler(RPKIServiceAPI&)");
+ LOG_("<- CPKCS12Handler::CPKCS12Handler(RPKIServiceAPI&)");
+ }
+
+CPKCS12Handler::CPKCS12Handler()
+ {
+ LOG_("-> CPKCS12Handler::CPKCS12Handler()");
+ LOG_("<- CPKCS12Handler::CPKCS12Handler()");
+ }
+
+/**
+ * Instantiate S60 PKCS#12 handler upon construction.
+ */
+void CPKCS12Handler::ConstructL()
+ {
+ LOG_("-> CPKCS12Handler::ConstructL()");
+
+ iPkcsHandler = PKCS12Factory::CreateL();
+ User::LeaveIfError(iNotifier.Connect());
+ User::LeaveIfError(iFs.Connect());
+
+ LOG_("<- CPKCS12Handler::ConstructL() OK");
+ }
+
+
+///////////////////////////////////
+// Public methods
+
+EXPORT_C void CPKCS12Handler::SaveSecurityObjectsToDiskL(const TDesC8& aData,
+ const TDesC& aPwd,
+ const TDesC& aDir)
+ {
+ LOG_("-> CPKCS12Handler::SaveSecurityObjectsToDiskL()");
+
+ if (iOutputDir)
+ {
+ delete iOutputDir;
+ iOutputDir = NULL;
+ }
+
+ // Output dir needs to end with backslash
+ iOutputDir = aDir.AllocL();
+
+ LOG_1(" Using output dir: '%S'", iOutputDir);
+
+ ExtractSecurityObjectsL(aData, aPwd);
+
+ // Save CA/user certificates to disk
+ SaveCertificatesToDiskL();
+
+ // Save private keys to disk
+ SaveKeysToDiskL();
+
+ LOG_("<- CPKCS12Handler::SaveSecurityObjectsToDiskL()");
+ }
+EXPORT_C void CPKCS12Handler::StorePKCS12ObjectL(const TDesC8& aData,
+ const TDesC16& aPwd)
+ {
+ LOG_("-> CPKCS12Handler::StorePKCS12ObjectL()");
+
+ ExtractSecurityObjectsL(aData, aPwd);
+
+ // Store CA certificates to PKI
+ StoreCertificatesL();
+
+ // Store private keys to PKI
+ StoreKeyPairsL();
+
+ // Attach related user certificates to PKI priv keys
+ AttachCertificatesL();
+
+ LOG_("<- CPKCS12Handler::StorePKCS12ObjectL() OK");
+ }
+
+EXPORT_C void CPKCS12Handler::SetApplicability(const RArray<TUid>& aUids)
+ {
+ LOG_("-> CPKCS12Handler::SetApplicability()");
+ iApplications = &aUids;
+ LOG_("<- CPKCS12Handler::SetApplicability()");
+ }
+
+EXPORT_C void CPKCS12Handler::SetDeletable(TBool aDeletable)
+ {
+ LOG_1("-> CPKCS12Handler::SetDeletable() Deletable: %d", aDeletable);
+ iDeletable = aDeletable;
+ LOG_("<- CPKCS12Handler::SetDeletable()");
+ }
+
+
+
+
+///////////////////////////////////
+// Internal methods
+
+void CPKCS12Handler::ExtractSecurityObjectsL(const TDesC8& aData,
+ const TDesC16& aPwd)
+ {
+ LOG_("-> CPKCS12Handler::ExtractSecurityObjectsL()");
+ if (iPassword)
+ {
+ delete iPassword;
+ iPassword = NULL;
+ }
+
+ // Make sure the data is in PKCS#12 format
+ if (!VerifyType(aData))
+ {
+ LOG_("<- CPKCS12Handler::ExtractSecurityObjectsL() LEAVE (KErrNotSupported)");
+ User::Leave(KErrArgument);
+ }
+
+ // If we have been provided with a valid password, then proceed
+ // to decrypt / parse; otherwise, prompt for pwd.
+ if (aPwd.Length() > 0)
+ {
+ iPassword = aPwd.AllocL();
+ LOG_(" Password provided by OMADM...");
+ }
+ else
+ {
+ LOG_(" No password provided, prompting the user for one");
+
+ iPassword = QueryPasswordL();
+ }
+
+ // Keep asking for the password until user cancels or inputs the
+ // correct password
+ while (ETrue)
+ {
+ if (iPassword)
+ {
+ LOG_1(" Non-NULL password '%S' in use, decrypting", iPassword);
+ TRAPD(err, iPkcsHandler->ParseL(aData, *iPassword));
+ if (err != KErrNone)
+ {
+ LOG_(" Breaking news: Password proved a miserable failure! Program terminated abruptly!");
+ DisplayWrongPasswordNote();
+ delete iPassword;
+ iPassword = NULL;
+ iPassword = QueryPasswordL();
+ }
+ else
+ {
+ // Correct password provided by the user,
+ // break free from the vicious cycle.
+ delete iPassword;
+ iPassword = NULL;
+ break;
+ }
+ }
+ else
+ {
+ // User got tired of guessing and resorted to cancel
+ LOG_("<- CPKCS12Handler::ExtractSecurityObjectsL() LEAVE (KErrCancel)");
+ User::Leave(KErrBadPassphrase);
+ }
+ }
+
+ // Fetch references to keys and certs
+ ExtractKeysAndCerts();
+
+ LOG_("<- CPKCS12Handler::ExtractSecurityObjectsL()");
+ }
+
+void CPKCS12Handler::SaveCertificatesToDiskL()
+ {
+ LOG_("-> CPKCS12Handler::SaveCertificatesToDiskL()");
+
+ TInt certCount(0);
+
+ // first the CAs (if any; not required necessarily)
+ if (iCACerts)
+ {
+ certCount = iCACerts->Count();
+ LOG_1(" Saving %d CA Certificates", certCount);
+
+ for (TInt i = 0; i < certCount; i++)
+ {
+ CX509Certificate* cert = iCACerts->At(i);
+
+ // Generate filename with running identifier
+ // Use TFileName, because the function isn't stack
+ // critical
+ TFileName fn;
+ fn.Format(KCAFileNameStem(), i+1);
+
+ WriteToFileL(cert->Encoding(), fn);
+ }
+ }
+ LOG(else LOG_(" No CA Certs found!"));
+
+ // Then the user certs
+ if (iUserCerts)
+ {
+ certCount = iUserCerts->Count();
+ LOG_1(" Saving %d User Certificates", certCount);
+
+ for (TInt i = 0; i < certCount; i++)
+ {
+ CX509Certificate* cert = iUserCerts->At(i);
+
+ TFileName fn;
+ fn.Format(KUserCertFileNameStem(), i+1);
+
+ WriteToFileL(cert->Encoding(), fn);
+ }
+ }
+
+ LOG(else LOG_(" No User Certs found!"));
+
+ LOG_("<- CPKCS12Handler::SaveCertificatesToDiskL()");
+ }
+
+void CPKCS12Handler::SaveKeysToDiskL()
+ {
+ LOG_("-> CPKCS12Handler::SaveKeysToDiskL()");
+
+ if (iPrivKeys)
+ {
+ TInt keycount = iPrivKeys->Count();
+ LOG_1(" Saving %d Private Keys", keycount);
+
+ for (TInt i = 0; i < keycount; i++)
+ {
+ HBufC8* key = iPrivKeys->At(i);
+ TPtrC8 keyPtr = *key;
+
+ TFileName fn;
+ fn.Format(KPrivateKeyFileNameStem(), i+1);
+
+ WriteToFileL(keyPtr, fn);
+ }
+ }
+ LOG(else LOG_(" No Private Keys found!"));
+
+ LOG_("<- CPKCS12Handler::SaveKeysToDiskL()");
+ }
+
+// Note: directory needs to end with a backslash
+// writes binary data
+void CPKCS12Handler::WriteToFileL(const TDesC8& aData,
+ const TDesC& aFileName)
+ {
+ LOG_("-> CPKCS12Handler::WriteToFileL()");
+
+ ASSERT(iOutputDir);
+ ASSERT(aFileName.Length() > 0);
+
+ // Disk space criticality check before attempting
+ // to install
+ if (SysUtil::FFSSpaceBelowCriticalLevelL(0, 0))
+ {
+ User::Leave(KErrDiskFull);
+ }
+
+ TFileName fn;
+ fn.Append(*iOutputDir);
+ fn.Append(aFileName);
+
+ LOG_1(" Opening file: '%S'", &fn);
+
+ TInt ret = iFileOut.Replace(iFs, fn, EFileWrite|EFileShareExclusive|EFileStream);
+
+ LOG_1(" File open result: %d", ret);
+
+ if (ret != KErrNone)
+ {
+ User::Leave(ret);
+ }
+
+ LOG_(" Writing data");
+
+ ret = iFileOut.Write(aData);
+ LOG_1(" Write result: %d", ret);
+
+ ret = iFileOut.Flush();
+ LOG_1(" Flush result: %d", ret);
+
+ iFileOut.Close();
+
+ LOG_("<- CPKCS12Handler::WriteToFileL()");
+ }
+
+TBool CPKCS12Handler::VerifyType(const TDesC8& aData) const
+ {
+ ASSERT(iPkcsHandler);
+
+ LOG_("-> CPKCS12Handler::VerifyType()");
+
+ TBool isPKCS12(EFalse);
+
+ // Need to check the data length before IsPKCS12Data call,
+ // otherwise an assert (instead of a more suitable)
+ // might occur
+ if (aData.Length() >= KPKCS12DataMinLength)
+ {
+ isPKCS12 = iPkcsHandler->IsPKCS12Data(aData);
+ }
+
+ LOG_1("<- CPKCS12Handler::VerifyType() RET: %d", isPKCS12);
+ return isPKCS12;
+ }
+
+void CPKCS12Handler::StoreKeyPairsL()
+ {
+ LOG_("-> CPKCS12Handler::StoreKeyPairsL()");
+
+ TInt keycount = iPrivKeys->Count();
+
+ for (TInt i = 0; i < keycount; i++)
+ {
+ HBufC8* key = iPrivKeys->At(i);
+ TPtrC8 keyPtr = *key;
+ StoreSingleKeyL(*key);
+ }
+
+ LOG_("<- CPKCS12Handler::StoreKeyPairsL() OK");
+ }
+
+/**
+ * NOTE: It should be decided what to do in following cases:
+ * 1. Key storage operation fails for a key (there can be multiple keys
+ * within a PKCS#12 package)
+ * 2. User cert attachment fails for a key (there can be multiple user
+ * certificates for any given key)
+ *
+ * At the moment, the code leaves if anything unexpected occurs.
+ * There is no rollback mechanism (anything that was added before
+ * the failure will still be in PKI stores).
+ *
+ */
+void CPKCS12Handler::StoreSingleKeyL(const TDesC8& aKey)
+ {
+ LOG_("-> CPKCS12Handler::StoreSingleKeyL()");
+
+ // Setup initial values
+ TRequestStatus requestStatus;
+ TPKIKeyIdentifier keyId;
+
+ // Perform asynchronous PKI operation synchronously
+ iPkiService->StoreKeypair(keyId, aKey, requestStatus);
+
+ User::WaitForRequest(requestStatus);
+
+ // Check for operation status
+ TInt status = requestStatus.Int();
+ if (status != KErrNone)
+ {
+ LOG_1("<- CPKCS12Handler::StoreSingleKeyL() LEAVE (%d)", status);
+ User::Leave(status);
+ }
+
+ LOG_("<- CPKCS12Handler::StoreSingleKeyL() OK");
+ }
+
+
+void CPKCS12Handler::AttachCertificatesL()
+ {
+ LOG_("-> CPKCS12Handler::AttachCertificatesL()");
+ TInt certCount = iUserCerts->Count();
+ for (TInt i = 0; i < certCount; i++)
+ {
+ CX509Certificate* cert = iUserCerts->At(i);
+ TKeyIdentifier certKeyId = cert->KeyIdentifierL();
+
+ // Note: KeyID parameter is effectively redundant
+ // (it can always be fetched from CX509Certificate object)
+ AttachCertificateL(cert, certKeyId);
+
+ }
+ LOG_("<- CPKCS12Handler::AttachCertificatesL() OK");
+ }
+
+void CPKCS12Handler::StoreCertificatesL()
+ {
+ LOG_("-> CPKCS12Handler::StoreCertificatesL()");
+ TInt certCount = iCACerts->Count();
+ for (TInt i = 0; i < certCount; i++)
+ {
+ CX509Certificate* cert = iCACerts->At(i);
+ StoreCertificateL(cert);
+ }
+ LOG_("<- CPKCS12Handler::StoreCertificatesL() OK");
+ }
+
+void CPKCS12Handler::StoreCertificateL(CX509Certificate* aCert)
+ {
+
+ LOG_("-> CPKCS12Handler::StoreCertificateL()");
+
+ LOG_1(" Deletable: %d", iDeletable);
+
+ TInt status = iPkiService->StoreCertificate(EPKICACertificate,
+ iDeletable,
+ 0,
+ EPKIRSA,
+ aCert->Encoding());
+
+ if (status)
+ {
+ LOG_1("<- CPKCS12Handler::StoreCertificateL() LEAVE (%d)", status);
+ User::Leave(status);
+ }
+
+ SetApplicabilityL(aCert);
+
+ LOG_("<- CPKCS12Handler::StoreCertificateL() OK");
+ }
+
+
+void CPKCS12Handler::AttachCertificateL(CX509Certificate* aCert,
+ const TPKIKeyIdentifier& aKeyId)
+ {
+
+ LOG_("-> CPKCS12Handler::AttachCertificateL()");
+
+ TRequestStatus requestStatus;
+ TAny* resArray(NULL);
+
+ // Perform asynchronous PKI operation synchronously
+ iPkiService->AttachCertificateL(aKeyId, DEFAULT_KEY_LEN, EPKIRSA,
+ aCert->Encoding(), &resArray,
+ requestStatus);
+
+ User::WaitForRequest(requestStatus);
+ iPkiService->Finalize(resArray);
+
+ // Check for operation status
+ TInt status = requestStatus.Int();
+ if (status != KErrNone)
+ {
+ LOG_1("<- CPKCS12Handler::AttachCertificateL() LEAVE: %d", status);
+ User::Leave(status);
+ }
+
+ LOG_("<- CPKCS12Handler::AttachCertificateL() OK");
+ }
+
+/**
+ * Not in use currently -- is Applicability a meaningful parameter
+ * for user certificates?
+ */
+void CPKCS12Handler::SetApplicabilityL(CX509Certificate* aCert)
+ {
+ LOG_("-> CPKCS12Handler::SetApplicabilityL()");
+
+ if (aCert)
+ {
+ // Only set applicability if there is atleast one applicability
+ // setting defined
+ if (iApplications && iApplications->Count() > 0)
+ {
+ LOG_(" Resolving subject name");
+
+ // Use subject name for CA certs
+ const TPtrC8* issuerName =
+ aCert->DataElementEncoding(CX509Certificate::EIssuerName);
+
+ LOG_(" Resolving serial number");
+ const TPtrC8* serialNumber =
+ aCert->DataElementEncoding(CX509Certificate::ESerialNumber);
+
+ LOG_(" Issuing PKI call");
+ iPkiService->SetApplicabilityL(*issuerName, *serialNumber, *iApplications);
+ }
+ }
+ else
+ {
+ LOG_("<- CPKCS12Handler::SetApplicabilityL() Leave: NULL argument");
+ User::Leave(KErrArgument);
+ }
+
+ LOG_("<- CPKCS12Handler::SetApplicabilityL() OK");
+ }
+
+void CPKCS12Handler::ExtractKeysAndCerts()
+ {
+ ASSERT(iPkcsHandler);
+ iCACerts = &iPkcsHandler->CACertificates();
+ iUserCerts = &iPkcsHandler->UserCertificates();
+ iPrivKeys = &iPkcsHandler->PrivateKeys();
+ }
+
+HBufC* CPKCS12Handler::QueryPasswordL()
+ {
+ LOG_("-> CPKCS12Handler::QueryPasswordL()");
+ TRequestStatus status(KErrNone);
+ LOG_(" SANITY2");
+
+ HBufC* ret(NULL);
+
+ TVpnDialogInfo dialogInfo(TVpnDialog::EPKCS12Password, 0);
+
+ TPckgBuf<TVpnDialogInfo> dialogInfoDes;
+ TPckgBuf<TVpnDialogOutput> dialogResponseDes;
+
+
+ dialogInfoDes() = dialogInfo;
+
+ iNotifier.StartNotifierAndGetResponse(status, KUidVpnDialogNotifier,
+ dialogInfoDes, dialogResponseDes);
+
+ // Wait until user has given the input
+ LOG_(" Waiting for request");
+ User::WaitForRequest(status);
+ LOG_1(" Dialog terminated with status: %d", status.Int());
+
+ if (status != KErrCancel && dialogResponseDes().iOutBuf.Length() >= 0)
+ {
+ LOG_(" Allocating");
+ ret = dialogResponseDes().iOutBuf.AllocL();
+ LOG_(" Canceling");
+ iNotifier.CancelNotifier(KUidVpnDialogNotifier);
+ LOG_1("<- CPKCS12Handler::QueryPasswordL() return: '%S'", ret);
+ }
+ else
+ {
+ LOG_("<- CPKCS12Handler::QueryPasswordL() return: NULL");
+ }
+
+ return ret;
+ }
+
+void CPKCS12Handler::DisplayWrongPasswordNote()
+ {
+ LOG_("-> CPKCS12Handler::DisplayWrongPasswordNote()");
+ TRequestStatus status(KErrNone);
+
+ LOG_1(" Constructing dialoginfo, DID: %d",
+ TVpnNoteDialog::EVpnWrongPKCS12Password);
+
+ TIPSecDialogInfo info(TNoteDialog::EInfo,
+ TVpnNoteDialog::EVpnWrongPKCS12Password); //create the input information
+ TPckgBuf<TIPSecDialogInfo> infoBuf(info); //package it in appropriate buf
+ TPckgBuf<TIPSecDialogOutput> responseBuf; //create the buf to receive the response
+
+ iNotifier.StartNotifierAndGetResponse(status, KUidVpnDialogNotifier,
+ infoBuf, responseBuf);
+ User::WaitForRequest(status);
+
+ iNotifier.CancelNotifier(KUidVpnDialogNotifier);
+
+ LOG_("<- CPKCS12Handler::DisplayWrongPasswordNote()");
+ }
+