| // Copyright 2016 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. |
| |
| #ifndef AUTHPOLICY_SAMBA_INTERFACE_H_ |
| #define AUTHPOLICY_SAMBA_INTERFACE_H_ |
| |
| #include <memory> |
| #include <string> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/macros.h> |
| #include <base/memory/ref_counted.h> |
| #include <dbus/authpolicy/dbus-constants.h> |
| |
| #include "authpolicy/authpolicy_flags.h" |
| #include "authpolicy/authpolicy_metrics.h" |
| #include "authpolicy/constants.h" |
| #include "authpolicy/jail_helper.h" |
| #include "authpolicy/path_service.h" |
| #include "authpolicy/proto_bindings/active_directory_info.pb.h" |
| #include "authpolicy/samba_helper.h" |
| #include "authpolicy/tgt_manager.h" |
| #include "bindings/authpolicy_containers.pb.h" |
| |
| // Helper methods for Samba Active Directory authentication, machine (device) |
| // joining and policy fetching. Note: "Device" and "machine" can be used |
| // interchangably here. |
| |
| namespace authpolicy { |
| |
| class Anonymizer; |
| class AuthPolicyMetrics; |
| class PathService; |
| class ProcessExecutor; |
| |
| class SambaInterface { |
| public: |
| SambaInterface(scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| AuthPolicyMetrics* metrics, |
| const PathService* path_service, |
| const base::Closure& user_kerberos_files_changed); |
| |
| ~SambaInterface(); |
| |
| // Creates directories required by Samba code and loads configuration, if it |
| // exists. Also loads the debug flags file. Returns error |
| // - if a directory failed to create or |
| // - if |expect_config| is true and the config file fails to load. |
| ErrorType Initialize(bool expect_config); |
| |
| // Cleans all persistent state files. Returns true if all files were cleared. |
| static bool CleanState(const PathService* path_service); |
| |
| // Calls kinit to get a Kerberos ticket-granting-ticket (TGT) for the given |
| // |user_principal_name| (format: user_name@workgroup.domain). If a TGT |
| // already exists, it is renewed. The password must be readable from the pipe |
| // referenced by the file descriptor |password_fd|. On success, the user's |
| // account information is returned in |account_info|. If |account_id| is |
| // non-empty, the |account_info| is queried by |account_id| instead of by |
| // user name. This is safer since the account id is invariant, whereas the |
| // user name can change. The updated user name (or rather the sAMAccountName) |
| // is returned in the |account_info|. Thus, |account_id| should be set if |
| // known and left empty if unknown. |
| ErrorType AuthenticateUser(const std::string& user_principal_name, |
| const std::string& account_id, |
| int password_fd, |
| ActiveDirectoryAccountInfo* account_info); |
| |
| // Retrieves the status of the user account given by |account_id| (aka |
| // objectGUID). The status contains general ActiveDirectoryAccountInfo as well |
| // as the status of the user's ticket-granting-ticket (TGT). Does not fill |
| // |user_status| on error. |
| ErrorType GetUserStatus(const std::string& account_id, |
| ActiveDirectoryUserStatus* user_status); |
| |
| // Gets the user Kerberos credential cache (krb5cc) and configuration |
| // (krb5.conf) files if they exist. Does not set |files| on error. |
| ErrorType GetUserKerberosFiles(const std::string& account_id, |
| KerberosFiles* files); |
| |
| // Joins the local device with name |machine_name| to an Active Directory |
| // domain. A user principal name and password are required for authentication |
| // (see |AuthenticateUser| for details). |
| ErrorType JoinMachine(const std::string& machine_name, |
| const std::string& user_principal_name, |
| int password_fd); |
| |
| // Downloads user and extension policy from the Active Directory server and |
| // stores it in |gpo_policy_data|. |account_id_key| is the unique user GUID |
| // returned from |AuthenticateUser| in |account_info|, prefixed by "a-". The |
| // user's Kerberos authentication ticket must still be valid. If this |
| // operation fails, call |AuthenticateUser| and try again. |
| ErrorType FetchUserGpos(const std::string& account_id_key, |
| protos::GpoPolicyData* gpo_policy_data); |
| |
| // Downloads device and extension policy from the Active Directory server and |
| // stores it in |gpo_policy_data|. The device must be joined to the Active |
| // Directory domain already (see |JoinMachine|). During join, a machine |
| // password is stored in a keytab file, which is used for authentication for |
| // policy fetch. |
| ErrorType FetchDeviceGpos(protos::GpoPolicyData* gpo_policy_data); |
| |
| // Sets the default log level, see AuthPolicyFlags::DefaultLevel for details. |
| // The level persists between restarts of authpolicyd, but gets reset on |
| // reboot. |
| void SetDefaultLogLevel(AuthPolicyFlags::DefaultLevel level); |
| |
| // Disable retry sleep for unit tests. |
| void DisableRetrySleepForTesting() { |
| smbclient_retry_sleep_enabled_ = false; |
| device_tgt_manager_.DisableRetrySleepForTesting(); |
| } |
| |
| // Returns the anonymizer. |
| const Anonymizer* GetAnonymizerForTesting() const { |
| return anonymizer_.get(); |
| } |
| |
| // Renew the user ticket-granting-ticket. |
| ErrorType RenewUserTgtForTesting() { return user_tgt_manager_.RenewTgt(); } |
| |
| private: |
| // Actual implementation of AuthenticateUser() (see above). The method is |
| // wrapped in order to catch and memorize the returned error. |
| ErrorType AuthenticateUserInternal(const std::string& user_principal_name, |
| const std::string& account_id, |
| int password_fd, |
| ActiveDirectoryAccountInfo* account_info); |
| |
| // Retrieves the name of the domain controller (DC) and the IP of the key |
| // distribution center (KDC). If the full server name is 'server.realm', the |
| // DC name is set to 'server'. The DC name is required for proper kerberized |
| // authentication. The KDC address is required to speed up network |
| // communication and get rid of waiting for the machine account propagation |
| // after Active Directory domain join. |
| ErrorType GetRealmInfo(protos::RealmInfo* realm_info) const; |
| |
| // Gets the status of the user's ticket-granting-ticket (TGT). Uses klist |
| // internally to check whether the ticket is valid, expired or not present. |
| // Does not perform any server-side checks. |
| ErrorType GetUserTgtStatus(ActiveDirectoryUserStatus::TgtStatus* tgt_status); |
| |
| // Determines the password status by comparing the old |user_pwd_last_set_| |
| // timestamp to the new timestamp in |account_info|. |
| ActiveDirectoryUserStatus::PasswordStatus GetUserPasswordStatus( |
| const ActiveDirectoryAccountInfo& account_info); |
| |
| // Retrieves the name of the workgroup. Since the workgroup is expected to |
| // change very rarely, this function earlies out and returns ERROR_NONE if the |
| // workgroup has already been fetched. |
| ErrorType EnsureWorkgroup(); |
| |
| // Writes the Samba configuration file. |
| ErrorType WriteSmbConf() const; |
| |
| // Writes the Samba configuration file. If |workgroup_| is empty, the |
| // workgroup is queried from the server and the string is updated. Workgroups |
| // are only queried once a session, they are expected to change very rarely. |
| ErrorType EnsureWorkgroupAndWriteSmbConf(); |
| |
| // Writes the file with configuration information. |
| ErrorType WriteConfiguration() const; |
| |
| // Reads the file with configuration information. |
| ErrorType ReadConfiguration(); |
| |
| // Copies the machine keytab file to the state directory. The copy is owned by |
| // authpolicyd, so that authpolicyd_exec cannot modify it anymore. |
| ErrorType SecureMachineKeyTab() const; |
| |
| // Gets user account info. If |account_id| is not empty, searches by |
| // objectGUID = |account_id| only. Otherwise, searches by sAMAccountName = |
| // |user_name| and - if that fails - by userPrincipalName = |normalized_upn|. |
| // Note that sAMAccountName can be different from the name-part of the |
| // userPrincipalName and that kinit/Windows prefer sAMAccountName over |
| // userPrincipalName. Refreshes the device TGT. |
| ErrorType GetAccountInfo(const std::string& user_name, |
| const std::string& normalized_upn, |
| const std::string& account_id, |
| const protos::RealmInfo& realm_info, |
| ActiveDirectoryAccountInfo* account_info); |
| |
| // Calls net ads search with given |search_string| to retrieve |account_info|. |
| // Authenticates with the device TGT. |
| ErrorType SearchAccountInfo(const std::string& search_string, |
| ActiveDirectoryAccountInfo* account_info); |
| |
| // Calls net ads gpo list to retrieve a list of GPOs. |user_or_machine_name| |
| // may be a user or machine sAMAccountName. (The machine sAMAccountName is the |
| // machine name postfixed with '$'). |
| ErrorType GetGpoList(const std::string& user_or_machine_name, |
| PolicyScope scope, |
| protos::GpoList* gpo_list) const; |
| |
| // Downloads user or device GPOs in the given |gpo_list|. |scope| determines |
| // a sub-folder where GPOs are downloaded to. It should match |scope| from |
| // |GetGpoList|. |
| ErrorType DownloadGpos(const protos::GpoList& gpo_list, |
| const std::string& domain_controller_name, |
| PolicyScope scope, |
| std::vector<base::FilePath>* gpo_file_paths) const; |
| |
| // Parses GPOs and stores them in user/device policy protobufs. |
| ErrorType ParseGposIntoProtobuf( |
| const std::vector<base::FilePath>& gpo_file_paths, |
| const char* parser_cmd_string, |
| std::string* policy_blob) const; |
| |
| // Sets and fixes the current user by account id key. Only one account id is |
| // allowed per user. Calling this multiple times with different account ids |
| // crashes the daemon. |
| void SetUser(const std::string& account_id_key); |
| |
| // Anonymizes |realm| in different capitalizations as well as all parts. For |
| // instance, if realm is SOME.EXAMPLE.COM, anonymizes SOME, EXAMPLE and COM. |
| void AnonymizeRealm(const std::string& realm); |
| |
| // Resets internal state to an 'unenrolled' state by wiping configuration and |
| // user data. |
| void Reset(); |
| |
| // Loads |flags_default_level_| from Path::FLAGS_DEFAULT_LEVEL. Logs an |
| // error if the file exists, but the level cannot be loaded. Fails silently if |
| // the file does not exist. |
| void LoadFlagsDefaultLevel(); |
| |
| // Saves |flags_default_level_| to Path::FLAGS_DEFAULT_LEVEL. Logs on error. |
| void SaveFlagsDefaultLevel(); |
| |
| // Reloads debug flags. Should be done on every public method called from |
| // D-Bus, so that authpolicyd doesn't have to be restarted if the flags |
| // change. Note that this is cheap in a production environment where the flags |
| // file does not exist, so this is no performance concern. |
| void ReloadDebugFlags(); |
| |
| // User account_id (aka objectGUID) prefixed by "a-". |
| std::string user_account_id_key_; |
| // User logon name. |
| std::string user_sam_account_name_; |
| // Timestamp of last password change on server. |
| uint64_t user_pwd_last_set_ = 0; |
| // Is the user logged in? |
| bool user_logged_in_ = false; |
| // Last AuthenticateUser() error. |
| ErrorType last_auth_error_ = ERROR_NONE; |
| |
| std::unique_ptr<protos::ActiveDirectoryConfig> config_; |
| std::string workgroup_; |
| |
| // The order of members is carefully chosen to match initialization order, so |
| // don't mess with it unless you have a reason. |
| |
| // UMA statistics, not owned. |
| AuthPolicyMetrics* metrics_; |
| |
| // Lookup for file paths, not owned. |
| const PathService* paths_; |
| |
| // Removes sensitive data from logs. |
| std::unique_ptr<Anonymizer> anonymizer_; |
| |
| // Debug flags, loaded from Path::DEBUG_FLAGS. |
| protos::DebugFlags flags_; |
| AuthPolicyFlags::DefaultLevel flags_default_level_ = AuthPolicyFlags::kQuiet; |
| |
| // Helper to setup and run minijailed processes. |
| JailHelper jail_helper_; |
| |
| // User and device ticket-granting-ticket managers. |
| TgtManager user_tgt_manager_; |
| TgtManager device_tgt_manager_; |
| |
| // Whether kinit calls may return false negatives and must be retried. |
| bool retry_machine_kinit_ = false; |
| |
| // Whether to sleep when retrying smbclient (disable for testing). |
| bool smbclient_retry_sleep_enabled_ = true; |
| |
| DISALLOW_COPY_AND_ASSIGN(SambaInterface); |
| }; |
| |
| } // namespace authpolicy |
| |
| #endif // AUTHPOLICY_SAMBA_INTERFACE_H_ |