diff -r 000000000000 -r 2c201484c85f securityanddataprivacytools/securitytools/certapp/certapp.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/securityanddataprivacytools/securitytools/certapp/certapp.cpp Wed Jul 08 11:25:26 2009 +0100 @@ -0,0 +1,1046 @@ +/* +* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "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: +* +*/ + + +static const char * const sVersion = "certapp version 1.1.++"; +#include +#include +#include +#include +#include +#include "encdec.h" +#include "certclients.h" +#include "filecertstore.h" +#include "swicertstore.h" +#include "logger.h" +#include "stringconv.h" +#include +#include "appuidmap.h" +#include "openssl_license.h" +#include "utils.h" + +#ifdef __TOOLS2_LINUX__ +#include +#include +#else +#include +#endif // __TOOLS2_LINUX__ + +#ifdef __TOOLS2_LINUX__ +#define DIR_SEPARATOR "/" +#else +#define DIR_SEPARATOR "\\" +#endif + +enum CertStoreFileType +{ + EBinCertClients, + EHumanCertClients, + EBinFileCertStore, + EHumanFileCertStore, + EBinSwiCertStore, + EHumanSwiCertStore +}; + +struct AppMapEntry + { + TUint32 iUid; + TUint32 iInputFileIndex; + }; +typedef std::map AppMap; + +typedef std::map FCSLabelMap; // maps cert label to inputFileIndex + +typedef std::vector StringVector; +typedef std::vector CertStoreFileTypeVector; + +void ProcessCommandLine(int aArgc, char **aArgv, + StringVector &aInputFiles, + StringVector &aInputDirs, + CertStoreFileTypeVector &aInputFileTypes, + StringVector &aOutputFiles, + StringVector &aOutputDirs, + CertStoreFileTypeVector &aOutputFileTypes, + bool &aVerbose, bool &aPemOut, bool &aAllowDuplicates); + +void ProcessCertClientFiles(const std::string &aBaseDir, + const StringVector &aInputFiles, + const StringVector &aInputDirs, + const CertStoreFileTypeVector &aInputFileTypes, + bool &aAllowDuplicates, + EncDecContainer &aCertAppInfoContainer); + +void GenerateOutputFiles(const EncDecContainer &aCertAppInfoContainer, + const EncDecContainer &aFileCertStoreContainer, + const EncDecContainer &aSwiCertStoreContainer, + const std::string &aBaseDir, + const StringVector &aOutputFiles, + const StringVector &aOutputDirs, + const CertStoreFileTypeVector &aOutputFileTypes, + bool aVerbose, bool aPemOut); + +bool ValidateLabel(FCSLabelMap &aLabelMap, + const StringVector &aInputFiles, + CertStoreFileType aFileType, + TUint32 aFileIndex, + const TCertLabel &aCertLabel); + + +struct SubjectToSubjectKeyIdEntry + { + bool iDuplicate; + std::string iLabel; + TKeyIdentifier iSubjectKeyIdentifier; + }; +typedef std::map SubjectToSubjectKeyIdMap; +void BuildSubjectToSubjectKeyIdMap(const EncDecContainer &aCertStoreContainer, + SubjectToSubjectKeyIdMap &aSubjectMap); + +void SetIssuerKeyId(SubjectToSubjectKeyIdMap &aSubjectMap, + EUseCertificateExtension aUseExtension, + EncDecContainer &aCertStoreContainer); + +static const std::string OPT_PROGRESS("--progress="); +static const std::string OPT_ERRORS("--errors="); +static const std::string OPT_VERBOSE("--verbose"); +static const std::string OPT_ALLOWDUPLICATES("--allowduplicates"); +static const std::string OPT_CHDIR("--chdir="); +static const std::string OPT_HCERTCLIENTS_L("--hcertclients="); +static const std::string OPT_HCERTCLIENTS_S("--hcc="); +static const std::string OPT_BCERTCLIENTS_L("--bcertclients="); +static const std::string OPT_BCERTCLIENTS_S("--bcc="); +static const std::string OPT_HFILECERTSTORE_L("--hfilecertstore="); +static const std::string OPT_HFILECERTSTORE_S("--hca="); +static const std::string OPT_BFILECERTSTORE_L("--bfilecertstore="); +static const std::string OPT_BFILECERTSTORE_S("--bca="); +static const std::string OPT_HSWICERTSTORE_L("--hswicertstore="); +static const std::string OPT_HSWICERTSTORE_S("--hswi="); +static const std::string OPT_BSWICERTSTORE_L("--bswicertstore="); +static const std::string OPT_BSWICERTSTORE_S("--bswi="); + + + +void usage() +{ + prog << "certapp: general_options file_options --out file_options" << Log::Endl(); + prog << Log::Endl(); + prog << "Basic usage is to give one or more input files of any supported type, followed by --out and a list of output files using the same syntax" << Log::Endl(); + prog << "Typically at least one input file of type certclients should be given (via --bcertclients or --hcertclients) because this is required to encode/decode the application usage fields in the swicertstore and filecertstore files." << Log::Endl(); + prog << Log::Endl(); + prog << "general_options contains one or more of the following options:-" << Log::Endl(); + prog << "\t--help|-h\tDisplay this usage message" << Log::Endl(); + prog << "\t" << OPT_PROGRESS << "filename\tSave progress output to specified file" << Log::Endl(); + prog << "\t" << OPT_ERRORS << "filename\tSave error output to specified file" << Log::Endl(); + prog << "\t" << OPT_VERBOSE << "Include additional debug comments in output files" << Log::Endl(); + prog << "\t--license Display license information" << Log::Endl(); + prog << "\t--pemout Output certificates in PEM format (nb. format is auto-detected when reading)" << Log::Endl(); + prog << "\t" << OPT_ALLOWDUPLICATES << "\tWhen reading human readable config files, permit adding duplicate certificate labels in stores and UIDs in certclients (testing ONLY)" << Log::Endl(); + prog << "An errors/progress filename of - will write to the standard output." << Log::Endl(); + prog << "If the errors/progress filenames are identical, the output will be merged." << Log::Endl(); + prog << Log::Endl(); + prog << "Both instances of file_options contains one or more of the following options:-" << Log::Endl(); + prog << "\t" << OPT_HCERTCLIENTS_L << "|" << OPT_HCERTCLIENTS_S << "filename\t\tHuman readable certclients file" << Log::Endl(); + prog << "\t" << OPT_BCERTCLIENTS_L << "|" << OPT_BCERTCLIENTS_S << "filename\t\tBinary certclients file" << Log::Endl(); + prog << Log::Endl(); + prog << "\t" << OPT_HFILECERTSTORE_L << "|" << OPT_HFILECERTSTORE_S << "filename\tHuman readable filecertstore" << Log::Endl(); + prog << "\t" << OPT_BFILECERTSTORE_L << "|" << OPT_BFILECERTSTORE_S << "filename\tBinary filecertstore" << Log::Endl(); + prog << Log::Endl(); + prog << "\t" << OPT_HSWICERTSTORE_L << "|" << OPT_HSWICERTSTORE_S << "filename\tHuman readable swicertstore" << Log::Endl(); + prog << "\t" << OPT_BSWICERTSTORE_L << "|" << OPT_BSWICERTSTORE_S << "filename\tBinary swicertstore" << Log::Endl(); + prog << Log::Endl(); + prog << "\t" << "--out Change to specifying output files" << Log::Endl(); + prog << "\t" << "--in Change to specifying input files" << Log::Endl(); + prog << "\t" << "--chdir=relativeDir Change to the specified dir. Can be specified multiple times. Missing dir will be created if only last element is missing." << Log::Endl(); + prog << Log::Endl(); + + prog << "Examples" << Log::Endl(); + prog << "Read/dump a swicertstore" << Log::Endl(); + prog << "\tcertapp --bcertclients=certclients.dat --bswicertstore=swicertstore.dat --out --hswicertstore=swicertstore.txt" << Log::Endl(); + prog << "Read/dump a filecertstore" << Log::Endl(); + prog << "\tcertapp --bcertclients=certclients.dat --bfilecertstore=cacerts.dat --out --hfilecertstore=cacerts.txt" << Log::Endl(); + prog << "Augment a filecertstore" << Log::Endl(); + prog << "\tcertapp --bcertclients=certclients.dat --bfilecertstore=cacerts.dat --hfilecertstore=cacerts_extras.txt --out --bfilecertstore=cacerts_new.dat" << Log::Endl(); + prog << Log::Endl(); + prog << "Device file locations" << Log::Endl(); + prog << "ROM swicertstore - z:\\resource\\swicertstore.dat" << Log::Endl(); + prog << "Writable swicertstore - !:\\resource\\swicertstore\\dat\\* where ! is the system drive" << Log::Endl(); + prog << "Initial filecertstore and certclients z:\\private\\101f72a6\\cacerts.dat and certclients.dat. Copied to sys drive on first use." << Log::Endl(); + prog << "Filecertstore !:\\private\\101f72a6\\cacerts.dat and certclients.dat. where ! is the system drive." << Log::Endl(); +} + +void ChangeDir(const std::string &aBaseDir, const std::string &aRelativeDir) +{ + std::string dir(aBaseDir); + if(aRelativeDir != ".") + { + // Build dir to create and change into + dir.append(DIR_SEPARATOR); + dir.append(aRelativeDir); + } + + prog << Log::Indent() << "Setting dir to " << dir << Log::Endl(); +#ifdef __LINUX__ + (void) mkdir(dir.c_str(),0755); // May already exist so no need to check return code +#else + (void) mkdir(dir.c_str()); // May already exist so no need to check return code +#endif + if(chdir(dir.c_str()) < 0) + { + dbg << Log::Indent() << "failed to change dir to " << dir << Log::Endl(); + FatalError(); + } + return; +} + +int main(int argc, char **argv) +{ + dbg.SetStream(&std::cout); + prog.SetStream(&std::cout); + + try{ + if(argc==1) + { + prog << sVersion << " Use -h for help." << Log::Endl(); + } + + StringVector inputFiles; + StringVector inputDirs; + CertStoreFileTypeVector inputFileTypes; + + StringVector outputFiles; + StringVector outputDirs; + CertStoreFileTypeVector outputFileTypes; + + + bool verbose = false; + bool pemOut = false; + bool allowDuplicates = false; + + // Process all the command line options and file arguments + ProcessCommandLine(argc, argv, + inputFiles, inputDirs, inputFileTypes, + outputFiles, outputDirs, outputFileTypes, + verbose, pemOut, allowDuplicates); + + + // Save current directory + std::string baseDir; + { + char tmp[FILENAME_MAX]; + if(getcwd(tmp, FILENAME_MAX) == 0) + { + dbg << Log::Indent() << "Failed to read current dir" << Log::Endl(); + FatalError(); + } + baseDir = tmp; + } + + + // + // Process input files starting with certclient files and working from right to left + // + EncDecContainer certAppInfoContainer("ClientInfo", CertificateAppInfo::Factory); + ProcessCertClientFiles(baseDir, inputFiles, inputDirs, inputFileTypes, + allowDuplicates, + certAppInfoContainer); + + + // Generate config data for application uid EncDecEnum object in AppUidListEntry + AppUidMap::GenerateEnumEntries(); + + // + // Process remaining input files working from right to left + // + EncDecContainer fileCertStoreContainer("CertStoreEntries", CertStoreEntry::Factory); + FCSLabelMap fcsLabels; + EncDecContainer swiCertStoreContainer("SwiCertStoreEntries", SwiCertStoreEntry::Factory); + FCSLabelMap swiLabels; + for(int fileIndex = inputFiles.size()-1; fileIndex >= 0 ; --fileIndex) + { + CertStoreFileType fileType = inputFileTypes[fileIndex]; + if((fileType == EBinFileCertStore) || (fileType == EHumanFileCertStore)) + { + // Change to correct directory + ChangeDir(baseDir, inputDirs[fileIndex]); + + EncDecContainer tmpFileCertStoreContainer("CertStoreEntries", CertStoreEntry::Factory); + if(fileType == EBinFileCertStore) + { + prog << "Reading binary filecertstore file '" << inputFiles[fileIndex] << "'" << Log::Endl(); + AutoIndent ai(prog); + readContainer(inputFiles[fileIndex], false, tmpFileCertStoreContainer); + } + if(fileType == EHumanFileCertStore) + { + prog << "Reading human filecertstore file '" << inputFiles[fileIndex] << "'" << Log::Endl(); + AutoIndent ai(prog); + readContainer(inputFiles[fileIndex], true, tmpFileCertStoreContainer); + } + + // Now merge the new file into the running store. + prog << Log::Indent() << "Merging filecertstore data" << Log::Endl(); + AutoIndent ai(prog); + for(TUint32 entryIndex = 0; entryIndex < tmpFileCertStoreContainer.size(); ++entryIndex) + { + const CertStoreEntry &entry = static_cast(tmpFileCertStoreContainer[entryIndex]); + std::string nname = stringFromUtf16(entry.Label()); + + if(!ValidateLabel(fcsLabels, inputFiles, fileType, fileIndex, entry.Label())) + { + // Duplicate detected + if(!allowDuplicates || (fileType == EBinFileCertStore)) + { + continue; // Skip adding duplicate + } + // Only allow duplicates if debugging and reading + // human readable config file. + dbg << Log::Indent() << "Adding anyway due to " << OPT_ALLOWDUPLICATES << Log::Endl(); + } + + // Add entry + CertStoreEntry *newEntry = new CertStoreEntry; + *newEntry = entry; + fileCertStoreContainer.push_back(newEntry); + } + continue; + } + + + if((fileType == EBinSwiCertStore) || (fileType == EHumanSwiCertStore)) + { + // Change to correct directory + ChangeDir(baseDir, inputDirs[fileIndex]); + + EncDecContainer tmpSwiCertStoreContainer("SwiCertStoreEntries", SwiCertStoreEntry::Factory); + if(fileType == EBinSwiCertStore) + { + prog << "Reading binary swicertstore file '" << inputFiles[fileIndex] << "'" << Log::Endl(); + AutoIndent ai(prog); + readContainer(inputFiles[fileIndex], false, tmpSwiCertStoreContainer); + } + if(fileType == EHumanSwiCertStore) + { + prog << "Reading human swicertstore file '" << inputFiles[fileIndex] << "'" << Log::Endl(); + AutoIndent ai(prog); + readContainer(inputFiles[fileIndex], true, tmpSwiCertStoreContainer); + } + + // Now merge the new file into the running store. + prog << Log::Indent() << "Merging swicerstore data" << Log::Endl(); + AutoIndent ai(prog); + for(TUint32 entryIndex = 0; entryIndex < tmpSwiCertStoreContainer.size(); ++entryIndex) + { + const SwiCertStoreEntry &entry = static_cast(tmpSwiCertStoreContainer[entryIndex]); + std::string nname = stringFromUtf16(entry.Label()); + + if(!ValidateLabel(swiLabels, inputFiles, fileType, fileIndex, entry.Label())) + { + // Duplicate detected + if(!allowDuplicates || (fileType == EBinSwiCertStore)) + { + continue; // Skip adding duplicate + } + // Only allow duplicates if debugging and reading + // human readable config file. + dbg << Log::Indent() << "Adding anyway due to " << OPT_ALLOWDUPLICATES << Log::Endl(); + } + + // Add entry + SwiCertStoreEntry *newEntry = new SwiCertStoreEntry; + *newEntry = entry; + swiCertStoreContainer.push_back(newEntry); + } + continue; + } + } + + // Fix Certificate IDs in fileCertStoreContainer + for(TUint32 entryIndex=0; entryIndex < fileCertStoreContainer.size(); ++entryIndex) + { + CertStoreEntry &entry = static_cast(fileCertStoreContainer[entryIndex]); + entry.Info().SetOutputCertificateId(entryIndex); + } + + // Fix Certificate IDs in swicertstore container. + for(TUint32 entryIndex=0; entryIndex < swiCertStoreContainer.size(); ++entryIndex) + { + SwiCertStoreEntry &entry = static_cast(swiCertStoreContainer[entryIndex]); + entry.Info().SetOutputCertificateId(entryIndex); + } + + // + // Fix auto IssuerKeyId values + // + SubjectToSubjectKeyIdMap subjectMap; + // + // Fix IssuerKeyId values in swiCertStoreContainer + // + // We do not use the AuthorityKeyId extension and only consider + // certificates in swi certstores. + // + // First map all the SWI certificate subject names to SubjectKeyId values + BuildSubjectToSubjectKeyIdMap(swiCertStoreContainer, subjectMap); + // Now update IssuerKeyId fields which are set to auto. + // The AuthorityKeyId extension value will be ignored. + // The SubjectKeyId of a matching certificate (if there is a + // single match on issuer name) in the swi cert store will be + // used. + SetIssuerKeyId(subjectMap, KIgnoreCertificateExtension, swiCertStoreContainer); + + + // + // Fix IssuerKeyId values in fileCertStoreContainer + // + // Add all filecertstore certificates to the + // subjectName/SubjectKeyId map + BuildSubjectToSubjectKeyIdMap(fileCertStoreContainer, subjectMap); + // Now update IssuerKeyId fields which are set to auto. If an the + // AuthorityKeyId extension is present and <160bits use it, + // otherwise use the SubjectKeyId of the matching certificate (if + // there is a single match on issuer name). + SetIssuerKeyId(subjectMap, KUseCertificateExtension, fileCertStoreContainer); + + // + // Now generate output files + // + GenerateOutputFiles(certAppInfoContainer, fileCertStoreContainer, swiCertStoreContainer, + baseDir, outputFiles, outputDirs, outputFileTypes, verbose, pemOut); + + + } + catch(...) + { + dbg << Log::Indent() << "C++ expection!" << Log::Endl(); + FatalError(); + } + + prog << Log::Indent() << "Normal exit" << Log::Endl(); + prog.Stream().flush(); + dbg.Stream().flush(); + return 0; // All ok +} + +/** + ProcessCommandLine +*/ +void ProcessCommandLine(int aArgc, char **aArgv, + StringVector &aInputFiles, StringVector &aInputDirs, CertStoreFileTypeVector &aInputFileTypes, + StringVector &aOutputFiles, StringVector &aOutputDirs, CertStoreFileTypeVector &aOutputFileTypes, + bool &aVerbose, bool &aPemOut, bool &aAllowDuplicates) +{ + std::string progressFile("-"); + static std::fstream sProgressStream; + + std::string dbgFile("-"); + static std::fstream sDbgStream; + + StringVector *files = &aInputFiles; + StringVector *dirs = &aInputDirs; + CertStoreFileTypeVector *fileTypes = &aInputFileTypes; + + int argIndex=1; + // Process overall arguments (-h --progress --errors) + for(; argIndex < aArgc; ++argIndex) + { + std::string arg(aArgv[argIndex]); + + if(arg == "--license") + { + prog << sVersion << Log::Endl(); + prog << "Copyright (c) 2008 Symbian Software Ltd. All rights reserved." << Log::Endl(); + prog << "Linked against openssl. Credits and license for openssl follow:-" << Log::Endl(); + prog << openssl_license << Log::Endl(); + + continue; + } + + if(arg == OPT_VERBOSE) + { + prog << "Enabling additional output file comments" << Log::Endl(); + aVerbose = true; + + continue; + } + + if(arg == "--pemout") + { + prog << "Setting output certificate format to PEM" << Log::Endl(); + aPemOut = true; + + continue; + } + + if(arg == OPT_ALLOWDUPLICATES) + { + prog << "Allowing addition of duplicate labels in stores and UIDs in certclients when reading human readable input (testing ONLY)" << Log::Endl(); + aAllowDuplicates = true; + + continue; + } + + if((arg.find(OPT_PROGRESS) == 0) || + (arg.find(OPT_ERRORS) == 0)) + { + // The following logic is required so that if both streams + // are set to the same destination we share a streams + // object and hence avoid buffering issues. + std::string *thisFile; + std::fstream *thisStream; + Log *thisLog; + std::string *otherFile; + std::fstream *otherStream; + Log *otherLog; + + if(arg.find(OPT_PROGRESS) == 0) + { + thisFile = &progressFile; + thisStream = &sProgressStream; + thisLog = &prog; + otherFile = &dbgFile; + otherStream = &sDbgStream; + otherLog = &dbg; + + *thisFile = arg.substr(OPT_PROGRESS.size(), arg.npos); + } + else + { + thisFile = &dbgFile; + thisStream = &sDbgStream; + thisLog = &dbg; + otherFile = &progressFile; + otherStream = &sProgressStream; + otherLog = &prog; + + *thisFile = arg.substr(OPT_ERRORS.size(), arg.npos); + } + + if(*thisFile == *otherFile) + { + // Reuse existing stream. This avoids two streams opening the same file... + thisLog->SetStream(&otherLog->Stream()); + continue; + } + + // Need to open a new stream + if(thisStream->is_open()) thisStream->close(); + if(*thisFile == "-") + { + thisLog->SetStream(&std::cout); + continue; + } + + OpenUtf8FStreamForWrite(*thisStream, thisFile->c_str()); + if(thisStream->fail()) + { + if(thisLog == &dbg) + { + dbg.SetStream(&std::cout); + } + dbg << Log::Indent() << "Failed to open log file specified by " << aArgv[argIndex-1] << " '" << *thisFile << "'" << Log::Endl(); + return; + } + thisLog->SetStream(thisStream); + continue; + } + + if((strcmp(aArgv[argIndex], "--help") == 0) || (strcmp(aArgv[argIndex], "-h") == 0)) + { + usage(); + continue; + } + + // Not a general option, probably an input file... + break; + } + + // Process main arguments + for(; argIndex < aArgc; ++argIndex) + { + std::string arg(aArgv[argIndex]); + + bool gotFile = false; + if((strcmp(aArgv[argIndex], "--help") == 0) || (strcmp(aArgv[argIndex], "-h") == 0)) + { + usage(); + continue; + } + + if(arg.find(OPT_CHDIR) == 0) + { + dirs->push_back(arg.substr(OPT_CHDIR.size(), arg.npos)); + // Move to next option + ++argIndex; + if(argIndex >= aArgc) break; + arg = aArgv[argIndex]; + } + else + { + dirs->push_back("."); + } + + std::string fileArg; + if(arg.find(OPT_BCERTCLIENTS_L) == 0) + { + fileTypes->push_back(EBinCertClients); + fileArg = arg.substr(OPT_BCERTCLIENTS_L.size(), arg.npos); + gotFile = true; + } + if(arg.find(OPT_BCERTCLIENTS_S) == 0) + { + fileTypes->push_back(EBinCertClients); + fileArg = arg.substr(OPT_BCERTCLIENTS_S.size(), arg.npos); + gotFile = true; + } + if(arg.find(OPT_HCERTCLIENTS_L) == 0) + { + fileTypes->push_back(EHumanCertClients); + fileArg = arg.substr(OPT_HCERTCLIENTS_L.size(), arg.npos); + gotFile = true; + } + if(arg.find(OPT_HCERTCLIENTS_S) == 0) + { + fileTypes->push_back(EHumanCertClients); + fileArg = arg.substr(OPT_HCERTCLIENTS_S.size(), arg.npos); + gotFile = true; + } + if(arg.find(OPT_BFILECERTSTORE_L) == 0) + { + fileTypes->push_back(EBinFileCertStore); + fileArg = arg.substr(OPT_BFILECERTSTORE_L.size(), arg.npos); + gotFile = true; + } + if(arg.find(OPT_BFILECERTSTORE_S) == 0) + { + fileTypes->push_back(EBinFileCertStore); + fileArg = arg.substr(OPT_BFILECERTSTORE_S.size(), arg.npos); + gotFile = true; + } + if(arg.find(OPT_HFILECERTSTORE_L) == 0) + { + fileTypes->push_back(EHumanFileCertStore); + fileArg = arg.substr(OPT_HFILECERTSTORE_L.size(), arg.npos); + gotFile = true; + } + if(arg.find(OPT_HFILECERTSTORE_S) == 0) + { + fileTypes->push_back(EHumanFileCertStore); + fileArg = arg.substr(OPT_HFILECERTSTORE_S.size(), arg.npos); + gotFile = true; + } + if(arg.find(OPT_BSWICERTSTORE_L) == 0) + { + fileTypes->push_back(EBinSwiCertStore); + fileArg = arg.substr(OPT_BSWICERTSTORE_L.size(), arg.npos); + gotFile = true; + } + if(arg.find(OPT_BSWICERTSTORE_S) == 0) + { + fileTypes->push_back(EBinSwiCertStore); + fileArg = arg.substr(OPT_BSWICERTSTORE_S.size(), arg.npos); + gotFile = true; + } + if(arg.find(OPT_HSWICERTSTORE_L) == 0) + { + fileTypes->push_back(EHumanSwiCertStore); + fileArg = arg.substr(OPT_HSWICERTSTORE_L.size(), arg.npos); + gotFile = true; + } + if(arg.find(OPT_HSWICERTSTORE_S) == 0) + { + fileTypes->push_back(EHumanSwiCertStore); + fileArg = arg.substr(OPT_HSWICERTSTORE_S.size(), arg.npos); + gotFile = true; + } + + if(arg.find("--out") == 0) + { + files = &aOutputFiles; + dirs = &aOutputDirs; + fileTypes = &aOutputFileTypes; + continue; + } + + if(arg.find("--in") == 0) + { + files = &aInputFiles; + dirs = &aInputDirs; + fileTypes = &aInputFileTypes; + continue; + } + + if(gotFile) + { + files->push_back(fileArg); + continue; + } + + usage(); + dbg << Log::Indent() << "Unknown option " << (const char *) aArgv[argIndex] << Log::Endl(); + FatalError(); + } + return; +} + +void ProcessCertClientFiles(const std::string &aBaseDir, + const StringVector &aInputFiles, + const StringVector &aInputDirs, + const CertStoreFileTypeVector &aInputFileTypes, + bool &aAllowDuplicates, + EncDecContainer &aCertAppInfoContainer) +{ + AppMap appMap; + for(int fileIndex = aInputFiles.size()-1; fileIndex >= 0 ; --fileIndex) + { + CertStoreFileType fileType = aInputFileTypes[fileIndex]; + if((fileType != EBinCertClients) && (fileType != EHumanCertClients)) + { + continue; + } + + // Change to correct directory + ChangeDir(aBaseDir, aInputDirs[fileIndex]); + + EncDecContainer tmpCertInfoContainer("ClientInfo", CertificateAppInfo::Factory); + + if(fileType == EBinCertClients) + { + prog << Log::Indent() << "Reading binary certclients file '" << aInputFiles[fileIndex] << "'" << Log::Endl(); + AutoIndent ai(prog); + readContainer(aInputFiles[fileIndex], false, tmpCertInfoContainer); + } + else + { + prog << Log::Indent() << "Reading human certclients file '" << aInputFiles[fileIndex] << "'" << Log::Endl(); + AutoIndent ai(prog); + readContainer(aInputFiles[fileIndex], true, tmpCertInfoContainer); + } + + // Now merge the new file into the running store. + prog << Log::Indent() << "Merging certclients data" << Log::Endl(); + AutoIndent ai(prog); + AutoIndent ai2(dbg); + for(TUint32 entryIndex = 0; entryIndex < tmpCertInfoContainer.size(); ++entryIndex) + { + const CertificateAppInfo &info = static_cast(tmpCertInfoContainer[entryIndex]); + std::string nname = stringFromUtf16(info.Name()); + //prog << Log::Indent() << "checking " << nname << Log::Endl(); + + + TInt32 lastIndex; + std::string firstDef; + if(!AppUidMap::InsertUidDefinition(info.Id().iUid, nname, fileIndex, + lastIndex, firstDef)) + { + // Duplicate entry for UID + if(nname == firstDef) + { + // But both entries have the same value + prog << Log::Indent() << "Duplicate, but identical, entries for UID 0x" << info.Id().iUid << " '" << nname << "'." << Log::Endl(); + AutoIndent ai(prog); + prog << Log::Indent() << "From files '" << aInputFiles[lastIndex] << "' and '" << aInputFiles[fileIndex] << "'." << Log::Endl(); + } + else + { + // Entries have different values + dbg << Log::Indent() << "DUPLICATE entry for UID 0x" << info.Id().iUid << Log::Endl(); + AutoIndent ai(dbg); + dbg << Log::Indent() << "Ignoring '" << nname << "' from '" << aInputFiles[fileIndex] << "'." << Log::Endl(); + dbg << Log::Indent() << "Keeping '" << firstDef << "' from '" << aInputFiles[lastIndex] << "'." << Log::Endl(); + if(lastIndex == fileIndex) + { + if(fileType == EBinCertClients) + { + dbg << Log::Indent() << "Both entries are in the same binary same file!" << Log::Endl(); + continue; // Skip adding duplicate + } + dbg << Log::Indent() << "Clash is within a single text configuration file!" << Log::Endl(); + if(!aAllowDuplicates) + { + FatalError(); + } + } + } + + // Only add duplicates when debugging and the add is + // from a human readable config file. + if(!aAllowDuplicates || (fileType != EHumanCertClients)) + { + continue; // Skip adding duplicate + } + dbg << Log::Indent() << "Adding anyway due to " << OPT_ALLOWDUPLICATES << Log::Endl(); + } + + // Add entry + CertificateAppInfo *newInfo = new CertificateAppInfo; + *newInfo = info; + aCertAppInfoContainer.push_back(newInfo); + } + } +} + + + +/** + Write output files to disk + */ +void GenerateOutputFiles(const EncDecContainer &aCertAppInfoContainer, + const EncDecContainer &aFileCertStoreContainer, + const EncDecContainer &aSwiCertStoreContainer, + const std::string &aBaseDir, + const StringVector &aOutputFiles, + const StringVector &aOutputDirs, + const CertStoreFileTypeVector &aOutputFileTypes, + bool aVerbose, bool aPemOut) +{ + for(int fileIndex = aOutputFiles.size()-1; fileIndex >= 0 ; --fileIndex) + { + // Change to correct directory + ChangeDir(aBaseDir, aOutputDirs[fileIndex]); + + CertStoreFileType fileType = aOutputFileTypes[fileIndex]; + // + // Set the container and write mode to use + // + const EncDecContainer *container = 0; + bool humanReadable = false; + if(fileType == EBinCertClients) + { + container = &aCertAppInfoContainer; + humanReadable = false; + } + if(fileType == EHumanCertClients) + { + container = &aCertAppInfoContainer; + humanReadable = true; + } + if(fileType == EBinFileCertStore) + { + container = &aFileCertStoreContainer; + humanReadable = false; + } + if(fileType == EHumanFileCertStore) + { + container = &aFileCertStoreContainer; + humanReadable = true; + } + if(fileType == EBinSwiCertStore) + { + container = &aSwiCertStoreContainer; + humanReadable = false; + } + if(fileType == EHumanSwiCertStore) + { + container = &aSwiCertStoreContainer; + humanReadable = true; + } + + if(container == 0) + { + // failed to decode the output file type! + FatalError(); + } + // + // Write the container out + // + writeContainer(aOutputFiles[fileIndex].c_str(), humanReadable, aPemOut, aVerbose, *container); + } + return; +} + + + +/** + ValidateLabel + + This function maintains a map of certificate labels to input file + (ie file index) and definition. + + If the label is NOT already defined in the map, then the function + returns true, which instructs the caller to include the + label/certificate in the generated store. + + If the label is already defined in the map, then the function + returns false, which instructs the caller to NOT include the + label/certificate in the generated store. + + The files on the command line are processed right to left, so if + multiple definitions (for the same label) are seen, only the first + will be included in the generated store. + + The information saved in the map is used to generate helpful + warning/error messages as follows:- + + 1) The saved definition is the first definition encountered, and is + therefore the one which has been included in the store. + + 2) The saved file index is the index of the LAST file processed + containing a definition for this label. This may be different to + the one containing the first definition. + + Consider the following sequence:- + + First processed file - Definition for label Fred + Second processed file - Another two definitions for label Fred + + When processing the third definition (in the second file), the + saved file index will be that of the second file. The code uses + this to check if there are multiple definitions within a SINGLE + input file, for the same label. + + If the multiple definitions are within a single human readable + file, then this is considered a fatal error, otherwise only a + warning is generated. + + Duplicate definitions in different files, generate a warning. + */ +bool ValidateLabel(FCSLabelMap &aLabelMap, + const StringVector &aInputFiles, + CertStoreFileType aFileType, + TUint32 aFileIndex, + const TCertLabel &aCertLabel) +{ + std::string nname = stringFromUtf16(aCertLabel); + FCSLabelMap::value_type e(nname, aFileIndex); + std::pair result = aLabelMap.insert(e); + if(result.second == true) + { + // Not a duplicate + return true; + } + + // Duplicate label entry + dbg << Log::Indent() << "DUPLICATE label detected in '" << aInputFiles[aFileIndex] << "'." << Log::Endl(); + AutoIndent ai(dbg); + dbg << Log::Indent() << "Duplicate entry for '" << e.first << "' ignored. Keeping entry from '" << aInputFiles[(*result.first).second] << "'" <second == TUint32(aFileIndex)) + { + dbg << Log::Indent() << "Both entries are in the same file." << Log::Endl(); + if((aFileType == EHumanFileCertStore) || (aFileType == EHumanSwiCertStore)) + { + dbg << Log::Indent() << "FATAL error - Clash is within a single text configuration file - aborting!" << Log::Endl(); + FatalError(); + } + return false; // Insert failed, keep earlier def + } + else + { + // Update file index for last definition. This is used to + // detect if the last two defs were within a single file. + (result.first)->second = TUint32(aFileIndex); + return false; // Insert failed, keep earlier def + } + return false; +} + +void BuildSubjectToSubjectKeyIdMap(const EncDecContainer &aCertStoreContainer, + SubjectToSubjectKeyIdMap &aSubjectMap) +{ + // Collect subjectName/SubjectKeyId for all certs + for(TUint32 entryIndex=0; entryIndex < aCertStoreContainer.size(); ++entryIndex) + { + const CertStoreEntry &entry = static_cast(aCertStoreContainer[entryIndex]); + if(entry.Info().CertificateFormat() != EX509Certificate) + { + continue; + } + + std::pair e; + e.first = entry.CertSubject(); + e.second.iDuplicate = false; + e.second.iLabel = stringFromUtf16(entry.Label()); + e.second.iSubjectKeyIdentifier = entry.Info().SubjectKeyId().iHash; + std::pair result = aSubjectMap.insert(e); + if(result.second == false) + { + dbg << Log::Indent() << "WARNING: Certificate '" << e.second.iLabel << "' has a subject name of '" << e.first << "' which clashes with certificate '" << (*result.first).second.iLabel <<"'" << Log::Endl(); + (*result.first).second.iDuplicate = true; + } + } + +} + + +void SetIssuerKeyId(SubjectToSubjectKeyIdMap &aSubjectMap, + EUseCertificateExtension aUseExtension, + EncDecContainer &aCertStoreContainer) +{ + // Now loop across certs setting the issuer key id. + for(TUint32 entryIndex=0; entryIndex < aCertStoreContainer.size(); ++entryIndex) + { + CertStoreEntry &entry = static_cast(aCertStoreContainer[entryIndex]); + if(entry.Info().CertificateFormat() != EX509Certificate) + { + continue; + } + if(!entry.Info().IssuerKeyId().iAutoKey) + { + continue; + } + + std::string certLabel = stringFromUtf16(entry.Label()); + + prog << Log::Indent() << "Attempting to auto set IssuerIeyId for '" << certLabel << "'" << Log::Endl(); + + AutoIndent ai(prog); + + // Lookup issuer key id in certificate extension and if found use that. + // + // X509IssuerKeyId will always set the issuerName. + // If aIgnoreExtension is false, then it will attempt to read + // the AuthorityKeyId extension. If found (and <160bits), it + // will write the ID to issuerId and return true. + // Otherwise it will return false. + // Certificate read errors are fatal. + std::string issuerName; + TKeyIdentifier issuerId; + if(X509IssuerKeyId(aUseExtension, + entry.CertData(), entry.Info().CertSize(), + issuerName, issuerId)) + { + // There is an authority key id extension so use its value + prog << Log::Indent() << "Using value from certificate '" << certLabel << "' extension" << Log::Endl(); + entry.Info().IssuerKeyId().iHash = issuerId; + } + else + { + // No extension so lookup issuerName in the map + prog << Log::Indent() << "Looking up issuer '" << issuerName << "' in map" << Log::Endl(); + SubjectToSubjectKeyIdMap::const_iterator it = aSubjectMap.find(issuerName); + if(it == aSubjectMap.end()) + { + prog << Log::Indent() << "Not found - Using empty IssuerKeyId " << Log::Endl(); + } + else + { + if((*it).second.iDuplicate) + { + prog << Log::Indent() << "Found - but multiple certs with matching subject name - Using empty IssuerKeyId" << Log::Endl(); + } + else + { + prog << Log::Indent() << "Found - Using Subject Key of matching cert" << Log::Endl(); + entry.Info().IssuerKeyId().iHash = (*it).second.iSubjectKeyIdentifier; + } + } + } + } + +} + + + +// End of file