diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java index 94602aa2..eb178210 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java @@ -410,8 +410,12 @@ public abstract class AbstractAttestationCertificateAuthority // parse the EK Public key from the IdentityClaim once for use in supply chain validation // and later tpm20MakeCredential function RSAPublicKey ekPub = parsePublicKey(claim.getEkPublicArea().toByteArray()); - - AppraisalStatus.Status validationResult = doSupplyChainValidation(claim, ekPub); + AppraisalStatus.Status validationResult = AppraisalStatus.Status.FAIL; + try { + validationResult = doSupplyChainValidation(claim, ekPub); + } catch (Exception ex) { + LOG.error(ex); + } if (validationResult == AppraisalStatus.Status.PASS) { RSAPublicKey akPub = parsePublicKey(claim.getAkPublicArea().toByteArray()); @@ -743,78 +747,92 @@ public abstract class AbstractAttestationCertificateAuthority Pattern pattern = Pattern.compile("([^\\s]+(\\.(?i)(rimpcr|rimel|bin|log))$)"); Matcher matcher; - if (dv.hasSwidfile()) { - try { - dbBaseRim = BaseReferenceManifest.select(referenceManifestManager) - .includeArchived() - .byHashCode(Arrays.hashCode(dv.getSwidfile().toByteArray())) - .getRIM(); + if (dv.getSwidfileCount() > 0) { + for (ByteString swidFile : dv.getSwidfileList()) { + try { + dbBaseRim = BaseReferenceManifest.select(referenceManifestManager) + .includeArchived() + .byHashCode(Arrays.hashCode(swidFile.toByteArray())) + .getRIM(); - if (dbBaseRim == null) { - dbBaseRim = new BaseReferenceManifest( - String.format("%s.swidtag", - clientName), - dv.getSwidfile().toByteArray()); + if (dbBaseRim == null) { + dbBaseRim = new BaseReferenceManifest( + String.format("%s.swidtag", + clientName), + swidFile.toByteArray()); - BaseReferenceManifest base = (BaseReferenceManifest) dbBaseRim; - for (SwidResource swid : base.parseResource()) { - matcher = pattern.matcher(swid.getName()); - if (matcher.matches()) { - //found the file name - int dotIndex = swid.getName().lastIndexOf("."); - clientName = swid.getName().substring(0, dotIndex); - dbBaseRim = new BaseReferenceManifest( - String.format("%s.swidtag", - clientName), - dv.getSwidfile().toByteArray()); - break; + BaseReferenceManifest base = (BaseReferenceManifest) dbBaseRim; + for (SwidResource swid : base.parseResource()) { + matcher = pattern.matcher(swid.getName()); + if (matcher.matches()) { + //found the file name + int dotIndex = swid.getName().lastIndexOf("."); + clientName = swid.getName().substring(0, dotIndex); + dbBaseRim = new BaseReferenceManifest( + String.format("%s.swidtag", + clientName), + swidFile.toByteArray()); + break; + } } + this.referenceManifestManager.save(dbBaseRim); + } else { + LOG.info("Client provided Base RIM already loaded in database."); + dbBaseRim.restore(); + dbBaseRim.resetCreateTime(); + this.referenceManifestManager.update(dbBaseRim); } - this.referenceManifestManager.save(dbBaseRim); - } else { - LOG.info("Client provided Base RIM already loaded in database."); - } - tagId = dbBaseRim.getTagId(); - } catch (IOException ioEx) { - LOG.error(ioEx); + tagId = dbBaseRim.getTagId(); + } catch (IOException ioEx) { + LOG.error(ioEx); + } } + } else { + LOG.warn("Device did not send swid tag file..."); } - if (dv.hasLogfile()) { - try { - support = SupportReferenceManifest.select(referenceManifestManager) - .includeArchived() - .byHashCode(Arrays.hashCode(dv.getLogfile().toByteArray())) - .getRIM(); + if (dv.getLogfileCount() > 0) { + for (ByteString logFile : dv.getLogfileList()) { + try { + support = SupportReferenceManifest.select(referenceManifestManager) + .includeArchived() + .byHashCode(Arrays.hashCode(logFile.toByteArray())) + .getRIM(); - if (support == null) { - support = new SupportReferenceManifest( - String.format("%s.rimel", - clientName), - dv.getLogfile().toByteArray()); - support.setPlatformManufacturer(dv.getHw().getManufacturer()); - support.setPlatformModel(dv.getHw().getProductName()); - support.setTagId(tagId); - this.referenceManifestManager.save(support); - } else { + if (support == null) { + support = new SupportReferenceManifest( + String.format("%s.rimel", + clientName), + logFile.toByteArray()); + support.setPlatformManufacturer(dv.getHw().getManufacturer()); + support.setPlatformModel(dv.getHw().getProductName()); + support.setTagId(tagId); + this.referenceManifestManager.save(support); + } else { LOG.info("Client provided Support RIM already loaded in database."); - if (dbBaseRim != null) { - support.setPlatformManufacturer(dbBaseRim.getPlatformManufacturer()); - support.setPlatformModel(dbBaseRim.getPlatformModel()); - support.setSwidTagVersion(dbBaseRim.getSwidTagVersion()); - support.setAssociatedRim(dbBaseRim.getId()); - support.setTagId(dbBaseRim.getTagId()); - } + if (dbBaseRim != null) { + support.setPlatformManufacturer(dbBaseRim.getPlatformManufacturer()); + support.setPlatformModel(dbBaseRim.getPlatformModel()); + support.setSwidTagVersion(dbBaseRim.getSwidTagVersion()); + support.setAssociatedRim(dbBaseRim.getId()); + support.setTagId(dbBaseRim.getTagId()); + } - this.referenceManifestManager.update(support); + support.restore(); + support.resetCreateTime(); + this.referenceManifestManager.update(support); + } + } catch (IOException ioEx) { + LOG.error(ioEx); } - } catch (IOException ioEx) { - LOG.error(ioEx); } + } else { + LOG.warn("Device did not send support RIM file..."); } if (dv.hasLivelog()) { + LOG.info("Device sent bios measurement log..."); fileName = String.format("%s.measurement", clientName); try { @@ -823,6 +841,7 @@ public abstract class AbstractAttestationCertificateAuthority .byManufacturer(dv.getHw().getManufacturer()) .includeArchived().getRIM(); if (support != null) { + LOG.info("Previous bios measurement log found and being replaced..."); this.referenceManifestManager.delete(support); } support = new EventLogMeasurements(fileName, @@ -834,6 +853,8 @@ public abstract class AbstractAttestationCertificateAuthority } catch (IOException ioEx) { LOG.error(ioEx); } + } else { + LOG.warn("Device did not send bios measurement log..."); } // Get TPM info, currently unimplemented diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java index 48662096..8aa5cf2b 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java @@ -338,8 +338,6 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe .byManufacturer(manufacturer).getRIM(); supportReferenceManifest = SupportReferenceManifest.select(referenceManifestManager) .byManufacturer(manufacturer).getRIM(); - List resources = - ((BaseReferenceManifest) baseReferenceManifest).parseResource(); measurement = EventLogMeasurements.select(referenceManifestManager) .byManufacturer(manufacturer).includeArchived().getRIM(); @@ -359,6 +357,8 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe } if (passed) { + List resources = + ((BaseReferenceManifest) baseReferenceManifest).parseResource(); fwStatus = new AppraisalStatus(PASS, SupplyChainCredentialValidator.FIRMWARE_VALID); diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java index ded07cee..65298a58 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java @@ -102,6 +102,7 @@ public class ReferenceManifestDetailsPageController LOGGER.error(uuidError, iaEx); } catch (Exception ioEx) { LOGGER.error(ioEx); + LOGGER.trace(ioEx); } if (data.isEmpty()) { String notFoundMessage = "Unable to find RIM with ID: " + params.getId(); @@ -236,6 +237,10 @@ public class ReferenceManifestDetailsPageController baseRim.setAssociatedRim(support.getId()); logProcessor = new TCGEventLog(support.getRimBytes()); } + } else { + support = SupportReferenceManifest.select(referenceManifestManager) + .byEntityId(baseRim.getAssociatedRim()).getRIM(); + logProcessor = new TCGEventLog(support.getRimBytes()); } // going to have to pull the filename and grab that from the DB // to get the id to make the link diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java index eed18ab4..9f7c5c7c 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java @@ -188,7 +188,7 @@ public class ReferenceManifestPageController Map model = new HashMap<>(); PageMessages messages = new PageMessages(); String fileName; - Pattern pattern; + Pattern logPattern = Pattern.compile(LOG_FILE_PATTERN); Matcher matcher; boolean supportRIM = false; BaseReferenceManifest base; @@ -197,64 +197,63 @@ public class ReferenceManifestPageController // loop through the files for (MultipartFile file : files) { fileName = file.getOriginalFilename(); - pattern = Pattern.compile(LOG_FILE_PATTERN); - matcher = pattern.matcher(fileName); + matcher = logPattern.matcher(fileName); supportRIM = matcher.matches(); //Parse reference manifests ReferenceManifest rim = parseRIM(file, supportRIM, messages); - if (supportRIM) { - // look for associated base/support - Set rims = BaseReferenceManifest - .select(referenceManifestManager).getRIMs(); - support = (SupportReferenceManifest) rim; - // update information for associated support rim - for (BaseReferenceManifest dbRim : rims) { - for (SwidResource swid : dbRim.parseResource()) { - if (swid.getName().equals(rim.getFileName())) { - support.setSwidTagVersion(dbRim.getSwidTagVersion()); - support.setPlatformManufacturer(dbRim.getPlatformManufacturer()); - support.setPlatformModel(dbRim.getPlatformModel()); - support.setTagId(dbRim.getTagId()); - support.setAssociatedRim(dbRim.getId()); - support.setUpdated(true); - break; + //Store only if it was parsed + if (rim != null) { + if (supportRIM) { + // look for associated base/support + Set rims = BaseReferenceManifest + .select(referenceManifestManager).getRIMs(); + support = (SupportReferenceManifest) rim; + // update information for associated support rim + for (BaseReferenceManifest dbRim : rims) { + for (SwidResource swid : dbRim.parseResource()) { + if (swid.getName().equals(rim.getFileName())) { + support.setSwidTagVersion(dbRim.getSwidTagVersion()); + support.setPlatformManufacturer(dbRim.getPlatformManufacturer()); + support.setPlatformModel(dbRim.getPlatformModel()); + support.setTagId(dbRim.getTagId()); + support.setAssociatedRim(dbRim.getId()); + support.setUpdated(true); + break; + } } } - } - } else { - base = (BaseReferenceManifest) rim; + } else { + base = (BaseReferenceManifest) rim; - for (SwidResource swid : base.parseResource()) { - support = SupportReferenceManifest.select(referenceManifestManager) - .byFileName(swid.getName()).getRIM(); - if (support != null) { - base.setAssociatedRim(support.getId()); - if (support.isUpdated()) { - // this is separate because I want to break if we found it - // instead of finding it, it is uptodate but still search - break; - } else { - support.setSwidTagVersion(base.getSwidTagVersion()); - support.setPlatformManufacturer(base.getPlatformManufacturer()); - support.setPlatformModel(base.getPlatformModel()); - support.setTagId(base.getTagId()); - support.setUpdated(true); - try { - referenceManifestManager.update(support); - } catch (DBManagerException dbmEx) { - LOGGER.error(String.format("Couldn't update Support RIM " - + "%s with associated UUID %s", rim.getTagId(), - support.getId()), dbmEx); + for (SwidResource swid : base.parseResource()) { + support = SupportReferenceManifest.select(referenceManifestManager) + .byFileName(swid.getName()).getRIM(); + if (support != null) { + base.setAssociatedRim(support.getId()); + if (support.isUpdated()) { + // this is separate because I want to break if we found it + // instead of finding it, it is uptodate but still search + break; + } else { + support.setSwidTagVersion(base.getSwidTagVersion()); + support.setPlatformManufacturer(base.getPlatformManufacturer()); + support.setPlatformModel(base.getPlatformModel()); + support.setTagId(base.getTagId()); + support.setUpdated(true); + try { + referenceManifestManager.update(support); + } catch (DBManagerException dbmEx) { + LOGGER.error(String.format("Couldn't update Support RIM " + + "%s with associated UUID %s", rim.getTagId(), + support.getId()), dbmEx); + } } } } } - } - //Store only if it was parsed - if (rim != null) { storeManifest(file.getOriginalFilename(), messages, rim, diff --git a/HIRS_ProvisionerTPM2/include/Utils.h b/HIRS_ProvisionerTPM2/include/Utils.h index ca85ff45..39099750 100644 --- a/HIRS_ProvisionerTPM2/include/Utils.h +++ b/HIRS_ProvisionerTPM2/include/Utils.h @@ -5,6 +5,7 @@ #define HIRS_PROVISIONERTPM2_INCLUDE_UTILS_H_ #include +#include namespace hirs { @@ -32,6 +33,8 @@ namespace file_utils { std::string getFileAsOneLineOrEmptyString(const std::string& filename); + std::vector search_directory(const std::string& directory); + void writeBinaryFile(const std::string& bytes, const std::string& filename); diff --git a/HIRS_ProvisionerTPM2/package/rpm-post-install.sh b/HIRS_ProvisionerTPM2/package/rpm-post-install.sh index bb8f9182..468f7504 100644 --- a/HIRS_ProvisionerTPM2/package/rpm-post-install.sh +++ b/HIRS_ProvisionerTPM2/package/rpm-post-install.sh @@ -41,22 +41,30 @@ fi ln -s -f /etc/hirs/provisioner/hirs-provisioner.sh /usr/sbin/hirs-provisioner TCG_BOOT_FILE="/etc/hirs/tcg_boot.properties" -MAINFEST_DIRECTORY="/boot/tcg/manifest" -LOG_FILE_LOCATION="$MAINFEST_DIRECTORY/rim/" -TAG_FILE_LOCATION="$MAINFEST_DIRECTORY/swidtag/" +TCG_DIRECTORY="/boot/tcg" +RIM_FILE_LOCATION="$TCG_DIRECTORY/manifest/rim/" +SWIDTAG_FILE_LOCATION="$TCG_DIRECTORY/manifest/swidtag/" +CREDENTIALS_LOCATION="$TCG_DIRECTORY/cert/platform/" +BINARY_BIOS_MEASUREMENTS="/sys/kernel/security/tpm0/binary_bios_measurements" if [ ! -f "$TCG_BOOT_FILE" ]; then touch "$TCG_BOOT_FILE" fi -if [ -d "$LOG_FILE_LOCATION" ]; then - RIM_FILE=$(find "$LOG_FILE_LOCATION" -name '*.rimel' -or -name '*.bin' -or -name '*.rimpcr' -or -name '*.log') - echo "tcg.rim.file=$RIM_FILE" > "$TCG_BOOT_FILE" +if [ -d "$RIM_FILE_LOCATION" ]; then + echo "tcg.rim.dir=$RIM_FILE_LOCATION" > "$TCG_BOOT_FILE" fi -if [ -d "$TAG_FILE_LOCATION" ]; then - SWID_FILE=$(find "$TAG_FILE_LOCATION" -name '*.swidtag') - echo "tcg.swidtag.file=$SWID_FILE" >> "$TCG_BOOT_FILE" +if [ -d "$SWIDTAG_FILE_LOCATION" ]; then + echo "tcg.swidtag.dir=$SWIDTAG_FILE_LOCATION" >> "$TCG_BOOT_FILE" +fi + +if [ -d "$CREDENTIALS_LOCATION" ]; then + echo "tcg.cert.dir=$CREDENTIALS_LOCATION" >> "$TCG_BOOT_FILE" +fi + +if [ -f "$BINARY_BIOS_MEASUREMENTS" ]; then + echo "tcg.event.file=$BINARY_BIOS_MEASUREMENTS" >> "$TCG_BOOT_FILE" fi chmod -w "$TCG_BOOT_FILE" diff --git a/HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto b/HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto index af38dbac..e8cd5766 100644 --- a/HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto +++ b/HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto @@ -58,8 +58,8 @@ message DeviceInfo { required NetworkInfo nw = 3; required OsInfo os = 4; optional bytes pcrslist = 5; - optional bytes logfile = 6; - optional bytes swidfile = 7; + repeated bytes logfile = 6; + repeated bytes swidfile = 7; optional bytes livelog = 8; } diff --git a/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp b/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp index f280c924..5c185742 100644 --- a/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp +++ b/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp @@ -44,6 +44,7 @@ int provision() { Logger logger = Logger::getDefaultLogger(); CommandTpm2 tpm2; + Properties props("/etc/hirs/tcg_boot.properties"); tpm2.setAuthData(); // get endorsement credential and endorsement key @@ -62,21 +63,54 @@ int provision() { cout << "----> Collecting platform credential from TPM" << endl; string platformCredential = tpm2.getPlatformCredentialDefault(); std::vector platformCredentials; - platformCredentials.push_back(platformCredential); + + // if platformCredential is empty, not in TPM + // pull from properties file + if (platformCredential.empty()) { + const std::string& cert_dir = props.get("tcg.cert.dir", ""); + try { + platformCredentials = + hirs::file_utils::search_directory(cert_dir); + } catch (HirsRuntimeException& hirsRuntimeException) { + logger.error(hirsRuntimeException.what()); + } + } else { + platformCredentials.push_back(platformCredential); + } // collect device info cout << "----> Collecting device information" << endl; hirs::pb::DeviceInfo dv = DeviceInfoCollector::collectDeviceInfo(); dv.set_pcrslist(tpm2.getPcrList()); // collect TCG Boot files - Properties props("/etc/hirs/tcg_boot.properties"); - const std::string& rim_file = props.get("tcg.rim.file", ""); - const std::string& swid_file = props.get("tcg.swidtag.file", ""); + std::vector rim_files; + std::vector swidtag_files; + const std::string& rim_dir = props.get("tcg.rim.dir", ""); + const std::string& swid_dir = props.get("tcg.swidtag.dir", ""); + const std::string& live_log_file = props.get("tcg.event.file", ""); + try { - dv.set_logfile(hirs::file_utils::fileToString(rim_file)); - dv.set_swidfile(hirs::file_utils::fileToString(swid_file)); - dv.set_livelog(hirs::file_utils::fileToString( - "/sys/kernel/security/tpm0/binary_bios_measurements")); + rim_files = hirs::file_utils::search_directory(rim_dir); + for (const auto& rims : rim_files) { + if (rims != "") { + dv.add_logfile(rims); + } + } + } catch (HirsRuntimeException& hirsRuntimeException) { + logger.error(hirsRuntimeException.what()); + } + try { + swidtag_files = hirs::file_utils::search_directory(swid_dir); + for (const auto& swidtag : swidtag_files) { + if (swidtag != "") { + dv.add_swidfile(swidtag); + } + } + } catch (HirsRuntimeException& hirsRuntimeException) { + logger.error(hirsRuntimeException.what()); + } + try { + dv.set_livelog(hirs::file_utils::fileToString(live_log_file)); } catch (HirsRuntimeException& hirsRuntimeException) { logger.error(hirsRuntimeException.what()); } diff --git a/HIRS_ProvisionerTPM2/src/Utils.cpp b/HIRS_ProvisionerTPM2/src/Utils.cpp index 957e6a5f..d8f10b56 100644 --- a/HIRS_ProvisionerTPM2/src/Utils.cpp +++ b/HIRS_ProvisionerTPM2/src/Utils.cpp @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -118,6 +119,30 @@ namespace file_utils { return string_utils::trimNewLines(fileToString(filename, "")); } + vector search_directory(const string& directory) { + DIR *dr; + std::vector files; + dr = opendir(directory.c_str()); + + if (dr) { + struct dirent *en; + while ((en = readdir(dr)) != NULL) { + stringstream ss; + ss << directory.c_str(); + ss << en->d_name; + try { + files.push_back(fileToString(ss.str())); + } catch (HirsRuntimeException& hirsRuntimeException) { + std::cout << hirsRuntimeException.what(); + } + } + // close directory + closedir(dr); + } + + return files; + } + /** * Takes a byte string and writes the contents to a file of the given name. * @param bytes string bytes to write diff --git a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java index 4ecdbb8f..6ce019d4 100644 --- a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java +++ b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java @@ -205,12 +205,12 @@ public final class SupplyChainCredentialValidator implements CredentialValidator } try { if (trustStore == null || trustStore.size() == 0) { - message = baseErrorMessage + "a trust store\n"; + message = baseErrorMessage + "an Issuer Cert in the Trust Store\n"; LOGGER.error(message); return new AppraisalStatus(FAIL, message); } } catch (KeyStoreException e) { - message = baseErrorMessage + "an intitialized trust store"; + message = baseErrorMessage + "an initialized trust store"; LOGGER.error(message); return new AppraisalStatus(FAIL, message); } diff --git a/VERSION b/VERSION index 524cb552..227cea21 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.1 +2.0.0 diff --git a/tools/tcg_rim_tool/tcg_rim_tool.spec b/tools/tcg_rim_tool/tcg_rim_tool.spec index ed6f5a0d..502d3caf 100644 --- a/tools/tcg_rim_tool/tcg_rim_tool.spec +++ b/tools/tcg_rim_tool/tcg_rim_tool.spec @@ -23,7 +23,7 @@ rm -f /opt/hirs/rimtool/%{name}*.jar %install mkdir -p %{buildroot}/opt/hirs/rimtool/ %{buildroot}/usr/local/bin -cp build/libs/tools/%{name}-%{version}.jar %{buildroot}/opt/hirs/rimtool/ +cp build/libs/%{name}-%{version}.jar %{buildroot}/opt/hirs/rimtool/ cp ./rim_fields.json %{buildroot}/opt/hirs/rimtool/ cp ./keystore.jks %{buildroot}/opt/hirs/rimtool/ cp -r ./scripts/ %{buildroot}/opt/hirs/rimtool/