| // Copyright 2017 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Stub implementation of Samba net. Does not talk to server, but simply returns |
| // fixed responses to predefined input. |
| |
| #include <string> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/strings/string_split.h> |
| #include <base/strings/string_util.h> |
| |
| #include "authpolicy/samba_helper.h" |
| #include "authpolicy/stub_common.h" |
| |
| namespace authpolicy { |
| namespace { |
| |
| // smbclient sub-commands. |
| constexpr char kLcdCommand[] = "lcd "; |
| constexpr char kCdCommand[] = "cd "; |
| constexpr char kGetCommand[] = "get "; |
| |
| // Expected host and share in smbclient command. First part should match domain |
| // controller name in kStubLookup (see stub_net_main.cc). |
| constexpr char kHostAndShare[] = "//DCNAME.EXAMPLE.COM/SysVol"; |
| |
| // Error printed when a "remote" GPO fails to download. |
| constexpr char kGpoDownloadError[] = |
| "NT_STATUS_ACCESS_DENIED opening remote file "; |
| |
| // Error printed when a "remote" GPO file does not exist. |
| constexpr char kGpoDoesNotExistError[] = |
| "NT_STATUS_OBJECT_NAME_NOT_FOUND opening remote file "; |
| |
| // Error printed smbclient cannot connect to a host/share. |
| constexpr char kConnectionFailedError[] = |
| "Connection to //<SERVER_NAME>/sysvol failed (Error " |
| "NT_STATUS_UNSUCCESSFUL)"; |
| |
| struct DownloadItem { |
| std::string remote_path_; |
| std::string local_path_; |
| }; |
| |
| // Finds all 'cd <remote_dir>;lcd <local_dir>;get <filename>' triplets in an |
| // smbclient command and returns a list the concatenated local and remote file |
| // paths found. |
| std::vector<DownloadItem> GetDownloadItems(const std::string& command_line) { |
| std::string remote_dir, local_dir; |
| std::vector<DownloadItem> items; |
| std::vector<std::string> subcommands = base::SplitString( |
| command_line, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| for (const std::string& subcommand : subcommands) { |
| if (StartsWithCaseSensitive(subcommand, kCdCommand)) { |
| remote_dir = subcommand.substr(strlen(kCdCommand)); |
| } else if (StartsWithCaseSensitive(subcommand, kLcdCommand)) { |
| local_dir = subcommand.substr(strlen(kLcdCommand)); |
| } else if (StartsWithCaseSensitive(subcommand, kGetCommand)) { |
| const std::string filename = subcommand.substr(strlen(kGetCommand)); |
| DownloadItem item; |
| item.remote_path_ = remote_dir + "\\" + filename; |
| item.local_path_ = base::FilePath(local_dir).Append(filename).value(); |
| items.push_back(item); |
| } |
| } |
| return items; |
| } |
| |
| int HandleCommandLine(const std::string& command_line) { |
| // Make sure the caller adds the debug level. |
| CHECK(Contains(command_line, kDebugParam)); |
| |
| if (!Contains(command_line, kHostAndShare)) { |
| WriteOutput(kConnectionFailedError, ""); |
| return kExitCodeError; |
| } |
| |
| // Stub GPO files are written to krb5.conf's directory because it's hard to |
| // pass a full file path from a unit test to a stub binary. Note that |
| // environment variables are NOT passed through ProcessExecutor. |
| const base::FilePath krb5_conf_path(GetKrb5ConfFilePath()); |
| const base::FilePath gpo_dir = krb5_conf_path.DirName(); |
| |
| std::vector<DownloadItem> items = GetDownloadItems(command_line); |
| for (const DownloadItem& item : items) { |
| base::FilePath source_path; |
| bool download_error = false; |
| if (Contains(item.local_path_, kGpo1Guid)) |
| source_path = gpo_dir.Append(kGpo1Filename); |
| else if (Contains(item.local_path_, kGpo2Guid)) |
| source_path = gpo_dir.Append(kGpo2Filename); |
| else if (Contains(item.local_path_, kErrorGpoGuid)) |
| download_error = true; |
| else if (Contains(item.local_path_, kSeccompGpoGuid)) |
| TriggerSeccompFailure(); |
| else |
| NOTREACHED() << "UNHANDLED DOWNLOAD ITEM '" << item.local_path_ << "'"; |
| |
| if (download_error) { |
| // Print "download error" warning. |
| WriteOutput(kGpoDownloadError + item.remote_path_, ""); |
| } else if (!base::PathExists(source_path)) { |
| // Print "file does not exist" warning. |
| WriteOutput(kGpoDoesNotExistError + item.remote_path_, ""); |
| } else { |
| // "Download" the file. |
| base::FilePath target_path(item.local_path_); |
| CHECK(base::CopyFile(source_path, target_path)); |
| } |
| } |
| |
| // The command should always end with 'exit;'. This makes sure smbclient |
| // always exits with code 0. |
| CHECK(base::EndsWith(command_line, "exit;", base::CompareCase::SENSITIVE)); |
| return kExitCodeOk; |
| } |
| |
| } // namespace |
| } // namespace authpolicy |
| |
| int main(int argc, char* argv[]) { |
| const std::string command_line = authpolicy::GetCommandLine(argc, argv); |
| return authpolicy::HandleCommandLine(command_line); |
| } |