| #include "bmc/auth.h" | |
| #include <cstdlib> | |
| #include <filesystem> // NOLINT(build/c++17) | |
| #include <fstream> | |
| #include <ios> | |
| #include <string> | |
| #include <system_error> // NOLINT(build/c++11) | |
| #include "file/base/path.h" | |
| #include "gmi/machine_identity.pb.h" | |
| #include "net/proto2/contrib/parse_proto/parse_text_proto.h" | |
| #include "bmc/daemon_context_bmc.h" | |
| #include "proto_reader.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/time/time.h" | |
| namespace auth { | |
| namespace { | |
| using ::security_prodid::GoogleMachineIdentityProto; | |
| using google::protobuf::contrib::parse_proto::ParseTextProtoOrDie; | |
| using ::safepower_agent::DaemonContextBMC; | |
| constexpr absl::string_view test_root_cert = R"pem( | |
| -----BEGIN CERTIFICATE----- | |
| MIIDiDCCAnCgAwIBAgIIdwMbYuGm3akwDQYJKoZIhvcNAQELBQAwRTEXMBUGA1UE | |
| ChMOR29vZ2xlIFRFU1RJTkcxKjAoBgNVBAMMIUdvb2dsZSBCTUNXZWIgKipUZXN0 | |
| aW5nKiogUm9vdCBDQTAgFw03MDAxMDEwMDAwMDBaGA8yMTI1MDEwMTAwMDAwMFow | |
| RTEXMBUGA1UEChMOR29vZ2xlIFRFU1RJTkcxKjAoBgNVBAMMIUdvb2dsZSBCTUNX | |
| ZWIgKipUZXN0aW5nKiogUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC | |
| AQoCggEBAKprOCLzYyqb6Mxo+I3n70P32PQLtUQdgjDXHF2cpwI0w3ezBYn4Dx0H | |
| T7RCRvMMlxnAcRPCTBcU4I+7HnUX+mBZvFkJPJukttCOS3usC6GtD8UMNassFkSL | |
| Vepy5AV1cwXweklebe9gAgC8NsFetqbHz12jTGUfngYDdqhg2haiTTwXyoal+575 | |
| c6F2S5krWMnBcm9/bhQufi8nC8AzQ2r1e4/bSe64F+vXzsLODsGs7mtzXeEDB2/N | |
| 7pEhQbXmkuBudXtE5CpLwvNa1Y3XLZfXXewlzlxjqQarihEolv6e/XOAJma3XMh8 | |
| Ip5QE1F/YvX3PICAbjsaX4wmWNTv7tECAwEAAaN6MHgwDgYDVR0PAQH/BAQDAgIE | |
| MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMBAf8EBTADAQH/ | |
| MBkGA1UdDgQSBBCuOIbUVxFjIbMIYjmL68mVMBsGA1UdIwQUMBKAEK44htRXEWMh | |
| swhiOYvryZUwDQYJKoZIhvcNAQELBQADggEBAAZ7KtkYl4CLzPTLUhSiisckk+9K | |
| /dbiG9TjRoiD8iG2j6/lTtZr0dkEq2xgz7kfHTbINzDONn45ea+BojAKSgWcwjzj | |
| TvTX0lubQcTLNBHVAZQ8Ws/7QN2+WF6QPpKWzk44O1Lw1JLhaLSe4GauIQICt0P5 | |
| zUVrgX2EP1FdbS/PxgQvwM3gbHJh7iqWr7ASVypLzA5+YfuPRIJAEOimQbrs/0Gq | |
| UEYO7y3ijBKwLs0lm6rCzCjwacWb/tFIJK4FNTg7iTWs2t94HWopoZqp0mouAERe | |
| qOFbsHcmv8mSUfig0AaTorZQpS8htNtcsCl5HhNgAyCQh+QzvBvesrEz5Cw= | |
| -----END CERTIFICATE----- | |
| )pem"; | |
| constexpr absl::string_view gmi_proto_str = R"pb( | |
| fqdn: "test.prod.google.com" | |
| serial_number: 1234 | |
| )pb"; | |
| GoogleMachineIdentityProto GetGmiProto() { | |
| std::string gmi_proto_str_local = std::string(gmi_proto_str); | |
| return ParseTextProtoOrDie(gmi_proto_str_local); | |
| } | |
| TEST(auth_test, WorkingCredsSuccess) { | |
| DaemonContextBMC context; | |
| // create the test directory in the undeclared outputs directory, so the files | |
| // are in the artifacts for the test. | |
| const char* undeclared_outputs_dir = getenv("TEST_UNDECLARED_OUTPUTS_DIR"); | |
| std::string test_dir = file::JoinPath(undeclared_outputs_dir, | |
| "working_creds_test"); | |
| std::filesystem::create_directory(test_dir); | |
| // create the file paths | |
| std::string symlink_path = file::JoinPath(test_dir, "symlink"); | |
| std::string loas3_cert_path = file::JoinPath(test_dir, "loas3_cert"); | |
| std::string self_signed_cert_path | |
| = file::JoinPath(test_dir, "self_signed_cert"); | |
| std::string gmi_file_path | |
| = file::JoinPath(test_dir, "gmi_file"); | |
| std::string trust_bundle_file_path | |
| = file::JoinPath(test_dir, "trust_bundle_file"); | |
| // create the gmi file | |
| GoogleMachineIdentityProto gmi = GetGmiProto(); | |
| EXPECT_OK(proto_reader::WriteProto(gmi_file_path, gmi)); | |
| // create the root cert file | |
| std::ofstream out(trust_bundle_file_path, | |
| std::ios::out | std::ios::binary | std::ios::trunc); | |
| out << test_root_cert; | |
| out.flush(); | |
| out.close(); | |
| // create the self signed cert | |
| AuthManager auth_manager(symlink_path, loas3_cert_path, | |
| self_signed_cert_path, gmi_file_path, trust_bundle_file_path, | |
| absl::Milliseconds(100), ""); | |
| auto creds = auth_manager.GetCredsInfo(); | |
| EXPECT_OK(creds.status()) << "call to GetCredsInfo failed"; | |
| // the symlink should point to the self signed cert, because the loas3 cert | |
| // does not exist. | |
| std::error_code file_error; | |
| std::string symlink_target = | |
| std::filesystem::read_symlink(symlink_path, file_error); | |
| ASSERT_EQ(file_error, std::error_code()) << "unable to read symlink"; | |
| ASSERT_EQ(symlink_target, | |
| self_signed_cert_path) << "symlink not pointing to self signed cert"; | |
| // wait 200ms s to allow the scan to not see the loas3 cert. | |
| DaemonContextBMC::Get().get_io_context().run_for( | |
| absl::ToChronoMilliseconds(absl::Milliseconds(200))); | |
| // add the loas3 cert (really just copy the self signed cert) | |
| std::filesystem::copy(self_signed_cert_path, loas3_cert_path); | |
| ASSERT_EQ(std::filesystem::exists(loas3_cert_path), true) | |
| << "loas3 cert file not copied"; | |
| // wait 200ms to allow the scan to see the loas3 cert. | |
| DaemonContextBMC::Get().get_io_context().run_for( | |
| absl::ToChronoMilliseconds(absl::Milliseconds(200))); | |
| // the symlink should point to the loas3 cert, because the loas3 cert exists. | |
| std::string symlink_target_second = | |
| std::filesystem::read_symlink(symlink_path, file_error); | |
| ASSERT_EQ(file_error, std::error_code()) << "unable to read symlink"; | |
| ASSERT_EQ(symlink_target_second, loas3_cert_path) | |
| << "symlink not pointing to loas3 cert"; | |
| auto provider = auth_manager.GetProvider(); | |
| ASSERT_OK(provider.status()) << "call to GetProvider failed"; | |
| // validate the provider is able to load the loas3 cert. | |
| EXPECT_OK((*provider)->ValidateCredentials()) | |
| << "call to ValidateCredentials failed"; | |
| } | |
| TEST(auth_test, NoGmiFileUnableToCreateSelfSignedCertFails) { | |
| DaemonContextBMC context; | |
| std::string test_dir = file::JoinPath(testing::TempDir(), | |
| "no_gmi_file_test"); | |
| std::filesystem::create_directory(test_dir); | |
| // create the file paths | |
| std::string symlink_path = file::JoinPath(test_dir, "symlink"); | |
| std::string loas3_cert_path = file::JoinPath(test_dir, "loas3_cert"); | |
| std::string self_signed_cert_path = | |
| file::JoinPath(test_dir, "self_signed_cert"); | |
| std::string gmi_file_path = file::JoinPath(test_dir, "gmi_file"); | |
| std::string trust_bundle_file_path = | |
| file::JoinPath(test_dir, "trust_bundle_file"); | |
| // call the get creds info with a short scan interval | |
| // self signed certs can not be crated without the gmi file | |
| AuthManager auth_manager(symlink_path, loas3_cert_path, | |
| self_signed_cert_path, gmi_file_path, trust_bundle_file_path, | |
| absl::Milliseconds(100), ""); | |
| auto creds = auth_manager.GetCredsInfo(); | |
| EXPECT_OK(creds.status()) << "call to GetCredsInfo failed"; | |
| // the symlink will still point to the self signed cert, because the loas3 | |
| // cert does not exist. | |
| std::error_code file_error; | |
| std::string value = std::filesystem::read_symlink(symlink_path, file_error); | |
| EXPECT_EQ(file_error, std::error_code()) << "unable to read symlink"; | |
| EXPECT_EQ(value, | |
| self_signed_cert_path) << "symlink not pointing to self signed cert"; | |
| // add loas3 cert, and validate the symlink is updated. | |
| // copy the self signed cert to the loas3 cert path. | |
| std::ofstream ofs(loas3_cert_path, std::ios_base::app); | |
| ofs.close(); | |
| EXPECT_EQ(std::filesystem::exists(loas3_cert_path), true) | |
| << "loas3 cert file not copied"; | |
| DaemonContextBMC::Get().get_io_context().run_for( | |
| absl::ToChronoMilliseconds(absl::Milliseconds(400))); | |
| EXPECT_EQ(std::filesystem::read_symlink(symlink_path), | |
| loas3_cert_path) << "symlink not pointing to loas3 cert"; | |
| } | |
| TEST(auth_test, UnableToCreateSymlinkFails) { | |
| DaemonContextBMC context; | |
| std::string test_dir = file::JoinPath(testing::TempDir(), | |
| "replace_symlink_test"); | |
| std::filesystem::create_directory(test_dir); | |
| std::string symlink_path = "/not/a/valid/path/symlink"; | |
| std::string self_signed_cert_path | |
| = file::JoinPath(test_dir, "new_target_path"); | |
| std::string loas3_cert_path = file::JoinPath(test_dir, "old_target_path"); | |
| // create an empty file at the loas3 cert path, so the symlink is created. | |
| std::ofstream ofs(loas3_cert_path, std::ios_base::app); | |
| ofs.close(); | |
| EXPECT_EQ(std::filesystem::exists(loas3_cert_path), true) | |
| << "loas3 cert file not created"; | |
| AuthManager auth_manager(symlink_path, loas3_cert_path, | |
| self_signed_cert_path, "", "", absl::Milliseconds(100), ""); | |
| EXPECT_OK(auth_manager.CreateCertsSymlinkUpdate()); | |
| DaemonContextBMC::Get().get_io_context().run_for( | |
| absl::ToChronoMilliseconds(absl::Milliseconds(200))); | |
| } | |
| } // namespace | |
| } // namespace auth |