1717#include < aws/iotidentity/RegisterThingResponse.h>
1818#include < aws/iotidentity/RegisterThingSubscriptionRequest.h>
1919
20+ #include < arpa/inet.h>
2021#include < chrono>
22+ #include < ifaddrs.h>
23+ #include < iomanip>
24+ #include < net/if.h>
25+ #include < openssl/bio.h>
26+ #include < openssl/err.h>
27+ #include < openssl/evp.h>
28+ #include < openssl/pem.h>
29+ #include < openssl/x509.h>
2130#include < string>
31+ #include < sys/ioctl.h>
32+ #include < sys/socket.h>
2233#include < sys/stat.h>
34+ #include < unistd.h>
2335#include < wordexp.h>
2436
2537using namespace std ;
@@ -34,6 +46,8 @@ using namespace Aws::Iot::DeviceClient::Util;
3446constexpr char FleetProvisioning::TAG[];
3547constexpr int FleetProvisioning::DEFAULT_WAIT_TIME_SECONDS;
3648
49+ FleetProvisioning::FleetProvisioning () : collectSystemInformation(false ) {}
50+
3751bool FleetProvisioning::CreateCertificateAndKey (Iotidentity::IotIdentityClient identityClient)
3852{
3953 LOG_INFO (TAG, " Provisioning new device certificate and private key using CreateKeysAndCertificate API" );
@@ -460,6 +474,17 @@ bool FleetProvisioning::RegisterThing(Iotidentity::IotIdentityClient identityCli
460474 return false ;
461475 }
462476
477+ if (collectSystemInformation)
478+ {
479+ LOG_INFO (TAG, " Collecting system information" );
480+ if (!PopulateSystemInformation ())
481+ {
482+ LOGM_ERROR (TAG, " *** %s: Failed to collect system information. ***" , DeviceClient::DC_FATAL_ERROR);
483+ return false ;
484+ }
485+ LOGM_INFO (TAG, " System information: \n\t %s" , MapToString (templateParameters).c_str ());
486+ }
487+
463488 LOG_INFO (TAG, " Publishing to RegisterThing topic" );
464489 RegisterThingRequest registerThingRequest;
465490 registerThingRequest.TemplateName = templateName;
@@ -492,6 +517,7 @@ bool FleetProvisioning::ProvisionDevice(shared_ptr<SharedCrtResourceManager> fpC
492517 try
493518 {
494519 LOG_INFO (TAG, " Fleet Provisioning Feature has been started." );
520+ collectSystemInformation = config.fleetProvisioning .collectSystemInformation ;
495521
496522 bool didSetup = FileUtils::CreateDirectoryWithPermissions (keyDir.c_str (), S_IRWXU) &&
497523 FileUtils::CreateDirectoryWithPermissions (
@@ -745,3 +771,204 @@ bool FleetProvisioning::MapParameters(Aws::Crt::Optional<std::string> params)
745771 }
746772 return true ;
747773}
774+
775+ bool FleetProvisioning::PopulateSystemInformation ()
776+ {
777+ // Step 1: Get MAC and IP address of the device.
778+ if (!CollectNetworkInformation ())
779+ {
780+ LOG_ERROR (TAG, " *** %s: Failed to collect network information ***" );
781+ return false ;
782+ }
783+
784+ // Step 2: Get hash values of related files.
785+ char exec_path[PATH_MAX];
786+ ssize_t count = readlink (" /proc/self/exe" , exec_path, PATH_MAX);
787+ if (count == -1 )
788+ {
789+ LOG_ERROR (TAG, " *** %s: Failed to get executable path ***" );
790+ return false ;
791+ }
792+ std::string exec_path_str (exec_path, count);
793+
794+ if (!CalculateFileSHA256Value (" IoTDeviceClient" , exec_path_str))
795+ {
796+ LOG_ERROR (TAG, " *** %s: Failed to calculate IoT device client hash value ***" );
797+ return false ;
798+ }
799+
800+ // Step 3: Get provisioning certificate IDs.
801+ if (!ObtainCertificateSerialID (certPath.c_str ()))
802+ {
803+ LOG_ERROR (TAG, " *** %s: Failed to obtain provisioning certificate IDs ***" );
804+ return false ;
805+ }
806+
807+ return true ;
808+ }
809+
810+ bool FleetProvisioning::CollectNetworkInformation ()
811+ {
812+ struct ifaddrs *ifap = nullptr ;
813+ char ip[INET6_ADDRSTRLEN];
814+
815+ if (getifaddrs (&ifap) == -1 )
816+ {
817+ LOG_ERROR (TAG, " *** %s: Failed to get network interfaces ***" );
818+ return false ;
819+ }
820+
821+ int fd = socket (AF_INET, SOCK_DGRAM, 0 );
822+ if (fd == -1 )
823+ {
824+ freeifaddrs (ifap);
825+
826+ LOG_ERROR (TAG, " *** %s: Failed to create socket ***" );
827+ return false ;
828+ }
829+
830+ for (struct ifaddrs *ifa = ifap; ifa != nullptr ; ifa = ifa->ifa_next )
831+ {
832+ if (ifa->ifa_addr == nullptr )
833+ continue ;
834+
835+ int family = ifa->ifa_addr ->sa_family ;
836+ char *name = ifa->ifa_name ;
837+
838+ // We only search for addresses on eth0 interface.
839+ if (family == AF_INET && strncmp (name, " eth0" , 3 ) == 0 )
840+ {
841+ struct in_addr addr = (reinterpret_cast <struct sockaddr_in *>(ifa->ifa_addr ))->sin_addr ;
842+ inet_ntop (AF_INET, &addr, ip, INET_ADDRSTRLEN);
843+
844+ struct ifreq ifr;
845+ unsigned char *mac;
846+
847+ strncpy (ifr.ifr_name , name, IFNAMSIZ - 1 );
848+ if (ioctl (fd, SIOCGIFHWADDR, &ifr) == -1 )
849+ {
850+ close (fd);
851+ freeifaddrs (ifap);
852+
853+ LOG_ERROR (TAG, " *** %s: Failed to get MAC address for interface ***" );
854+ return false ;
855+ }
856+ mac = reinterpret_cast <unsigned char *>(ifr.ifr_hwaddr .sa_data );
857+
858+ Aws::Crt::Optional<std::string> params (FormatMessage (
859+ R"( {"DeviceIPAddress": "%s", "DeviceMACAddress": "%02x:%02x:%02x:%02x:%02x:%02x"})" ,
860+ ip,
861+ mac[0 ],
862+ mac[1 ],
863+ mac[2 ],
864+ mac[3 ],
865+ mac[4 ],
866+ mac[5 ]));
867+ MapParameters (params);
868+ LOGM_DEBUG (TAG, " Successfully collected network information: %s" , params.value ().c_str ());
869+
870+ break ;
871+ }
872+ }
873+
874+ close (fd);
875+ freeifaddrs (ifap);
876+ return true ;
877+ }
878+
879+ bool FleetProvisioning::CalculateFileSHA256Value (const char *fileName, const std::string &filePath)
880+ {
881+ std::ifstream file (filePath, std::ios::binary);
882+ if (!file.is_open ())
883+ {
884+ LOG_ERROR (TAG, " *** %s: Failed to open file" );
885+ return false ;
886+ }
887+
888+ EVP_MD_CTX *mdctx = EVP_MD_CTX_new ();
889+ if (!mdctx)
890+ {
891+ LOG_ERROR (TAG, " *** %s: Failed to create EVP_MD_CTX" );
892+ return false ;
893+ }
894+
895+ if (EVP_DigestInit_ex (mdctx, EVP_sha256 (), NULL ) != 1 )
896+ {
897+ LOG_ERROR (TAG, " *** %s: Failed to initialize EVP_DigestInit_ex" );
898+ return false ;
899+ }
900+
901+ const int bufferSize = 8192 ;
902+ char buffer[bufferSize];
903+ while (file.good ())
904+ {
905+ file.read (buffer, bufferSize);
906+
907+ if (!EVP_DigestUpdate (mdctx, buffer, file.gcount ()))
908+ {
909+ LOG_ERROR (TAG, " *** %s: Failed to update EVP_DigestUpdate" );
910+ return false ;
911+ }
912+ }
913+
914+ unsigned char hashBuffer[SHA256_DIGEST_LENGTH];
915+ if (EVP_DigestFinal_ex (mdctx, hashBuffer, NULL ) != 1 )
916+ {
917+ LOG_ERROR (TAG, " *** %s: Failed to finalize EVP_DigestFinal_ex" );
918+ return false ;
919+ }
920+ EVP_MD_CTX_free (mdctx);
921+
922+ std::stringstream ss;
923+ for (unsigned int i = 0 ; i < SHA256_DIGEST_LENGTH; i++)
924+ {
925+ ss << std::hex << std::setw (2 ) << std::setfill (' 0' ) << (int )hashBuffer[i];
926+ }
927+ std::string hash = ss.str ();
928+
929+ Aws::Crt::Optional<std::string> params (FormatMessage (R"( {"%s-SHA256Hash": "%s"})" , fileName, hash.c_str ()));
930+ MapParameters (params);
931+ LOGM_DEBUG (TAG, " File '%s' SHA256 hash: %s" , fileName, hash.c_str ());
932+
933+ return true ;
934+ }
935+
936+ bool FleetProvisioning::ObtainCertificateSerialID (const char *certPath)
937+ {
938+ BIO *certBIO = BIO_new (BIO_s_file ());
939+ if (BIO_read_filename (certBIO, certPath) <= 0 )
940+ {
941+ BIO_free (certBIO);
942+
943+ LOG_ERROR (TAG, " *** %s: Failed to open certificate file ***" );
944+ return false ;
945+ }
946+
947+ X509 *cert = PEM_read_bio_X509 (certBIO, nullptr , nullptr , nullptr );
948+ if (cert == nullptr )
949+ {
950+ BIO_free (certBIO);
951+
952+ LOG_ERROR (TAG, " *** %s: Failed to load certificate ***" );
953+ return false ;
954+ }
955+
956+ // Convert ASN1_INTEGER to a readable string
957+ ASN1_INTEGER *serial = X509_get_serialNumber (cert);
958+ BIGNUM *bn = ASN1_INTEGER_to_BN (serial, nullptr );
959+ char *hex = BN_bn2hex (bn);
960+ std::string serialNumber (hex);
961+
962+ // Clean up
963+ BN_free (bn);
964+ OPENSSL_free (hex);
965+ X509_free (cert);
966+ BIO_free (certBIO);
967+
968+ Aws::Crt::Optional<std::string> params (
969+ FormatMessage (R"( {"ProvisioningCertSerialNumber": "%s"})" , serialNumber.c_str ()));
970+ MapParameters (params);
971+ LOGM_DEBUG (TAG, " Provisioning certificate serial number: %s" , serialNumber.c_str ());
972+
973+ return true ;
974+ }
0 commit comments