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