blob: fcfeba3bbfe96fec3b7b545d81989192bc6dbee9 [file] [log] [blame]
// 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 <inttypes.h>
#include <string>
#include <base/check.h>
#include <base/check_op.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/notreached.h>
#include <base/strings/strcat.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include "authpolicy/constants.h"
#include "authpolicy/platform_helper.h"
#include "authpolicy/samba_helper.h"
#include "authpolicy/stub_common.h"
namespace authpolicy {
namespace {
const char kSmbConfDevice[] = "smb_device.conf";
const char kSmbConfUser[] = "smb_user.conf";
const char kMachinePass[] = "machine_pass";
const char kStateDir[] = "state";
const char kSambaDir[] = "samba";
const char kKrb5CCUser[] = "krb5cc_user";
// Prefix for the fake domain sid marker file "fake_domain_sid_<workgroup>".
const char kFakeDomainSidMarkerPrefix[] = "fake_domain_sid_";
// Various stub error messages.
const char kSmbConfArgMissingError[] =
"Can't load /etc/samba/smb.conf - run testparm to debug it";
const char kNetworkError[] = "No logon servers";
const char kWrongPasswordError[] =
"Failed to join domain: failed to lookup DC info for domain "
"'REALM.EXAMPLE.COM' over rpc: Logon failure";
const char kExpiredPasswordError[] =
"Enter user@REALM.EXAMPLE.COM's password:\n"
"Failed to join domain: failed to lookup DC info for domain "
"'REALM.EXAMPLE.COM' over rpc: Must change password";
const char kJoinAccessDeniedError[] =
"Failed to join domain: Failed to set account flags for machine account "
const char kMachineNameTooLongError[] =
"Our netbios name can be at most %zd chars long, \"%s\" is %zd chars long\n"
"Failed to join domain: The format of the specified computer name is "
const char kInvalidMachineNameError[] =
"Failed to join domain: failed to join domain 'REALM.EXAMPLE.COM' over "
"rpc: Improperly formed account name";
const char kInsufficientQuotaError[] =
"Insufficient quota exists to complete the operation";
const char kEncTypeNotSupportedError[] =
"Failed to join domain: failed to connect to AD: KDC has no support for "
"encryption type";
// Size limit for machine name.
const size_t kMaxMachineNameSize = 15;
// Stub net ads info response.
const char kStubInfo[] = R"!!!(LDAP server:
LDAP server name:
Bind Path: dc=REALM,dc=EXAMPLE,dc=COM
LDAP port: 389
Server time: %s
KDC server:
Server time offset: -91
Last machine account password change:
Wed, 31 Dec 1969 16:00:00 PST)!!!";
constexpr char kDefaultServerTime[] = "Fri, 03 Feb 2017 05:24:05 PST";
// Stub net ads info response.
const char kStubLookup[] = R"!!!(Information for Domain Controller:
GUID: fca78f31-bf15-4ca3-b730-fbe619e937b2
Is a PDC: yes
Is a GC of the forest: yes
Is an LDAP server: yes
Supports DS: yes
Is running a KDC: yes
Is running time services: yes
Is the closest DC: no
Is writable: yes
Has a hardware clock: yes
Is a non-domain NC serviced by LDAP server: no
Is NT6 DC that has some secrets: no
Is NT6 DC that has all secrets: yes
Runs Active Directory Web Services: yes
Runs on Windows 2012 or later: yes
Domain Controller: DCNAME.EXAMPLE.COM
Pre-Win2k Domain: REALM
Pre-Win2k Hostname: DCNAME
Server Site Name : SITE
Client Site Name : SITE
NT Version: 5
LMNT Token: ffff
LM20 Token: ffff)!!!";
// Stub net ads gpo list response.
const char kStubLocalGpo[] = R"!!!(---------------------
name: Local Policy
displayname: Local Policy
version: 0 (0x00000000)
version_user: 0 (0x0000)
version_machine: 0 (0x0000)
filesyspath: (null)
dspath: (null)
link: (null)
link_type: 5 machine_extensions: (null)
user_extensions: (null)
const char kStubRemoteGpo[] = R"!!!(---------------------
name: %s
displayname: test-user-policy
version: %u (0x%04x%04x)
version_user: %u (0x%04x)
version_machine: %u (0x%04x)
filesyspath: \\\SysVol\\Policies\%s
dspath: cn=%s,cn=policies,cn=system,DC=realm,DC=example,DC=com
options: %s
link: OU=test-ou,DC=realm,DC=example,DC=com
link_type: 4 GP_LINK_OU
machine_extensions: (null)
user_extensions: [{D02B1F73-3407-48AE-BA88-E8213C6761F1}]
// Stub net ads search response.
const char kStubSearchFormat[] = R"!!!(Got 1 replies
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: %s
sn: Doe
givenName: %s
initials: JD
distinguishedName: CN=%s,OU=test-ou,DC=realm,DC=example,DC=com
instanceType: 4
whenCreated: 20161018155136.0Z
whenChanged: 20170217134227.0Z
displayName: %s
uSNCreated: 287406
uSNChanged: 307152
name: John Doe
objectGUID: %s
badPwdCount: 0
codePage: 0
countryCode: 0
badPasswordTime: 131309487458845506
lastLogoff: 0
lastLogon: 131320568639495686
primaryGroupID: 513
objectSid: S-1-5-21-250062649-3667841115-373469193-1134
accountExpires: 9223372036854775807
logonCount: 1453
sAMAccountName: %s
sAMAccountType: 805306368
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=realm,DC=example,DC=com
dSCorePropagationData: 20161024075536.0Z
dSCorePropagationData: 20161024075311.0Z
dSCorePropagationData: 20161019075502.0Z
dSCorePropagationData: 16010101000000.0Z
lastLogonTimestamp: 131318125471489990
msDS-SupportedEncryptionTypes: 0)!!!";
// Password related fields in search response.
const char kStubSearchPwdFormat[] = R"!!!(
pwdLastSet: %)!!!" PRIu64 R"!!!(
userAccountControl: %)!!!" PRIu32;
// Search that doesn't find anything.
const char kStubBadSearch[] = "Got 0 replies";
// Builder for custom search results (without having a 7-line base::StringPrintf
// every time). Usage:
// search_result = SearchBuilder().SetDisplayName("John Doe").GetResult();
class SearchBuilder {
// Prints out a stub net ads search result with the set parameters.
std::string GetResult() {
std::string result = base::StringPrintf(
kStubSearchFormat, common_name_.c_str(), given_name_.c_str(),
common_name_.c_str(), display_name_.c_str(), object_guid_.c_str(),
if (output_pwd_fields) {
result += base::StringPrintf(kStubSearchPwdFormat, pwd_last_set_,
return result;
// Sets the value of the givenName key.
SearchBuilder& SetGivenName(const std::string& value) {
given_name_ = value;
return *this;
// Sets the value of the displayName key.
SearchBuilder& SetDisplayName(const std::string& value) {
display_name_ = value;
return *this;
// Sets the value of the objectUID key.
SearchBuilder& SetObjectGuid(const std::string& value) {
object_guid_ = value;
return *this;
// Sets the value of the sAMAccountName key.
SearchBuilder& SetSAMAccountName(const std::string& value) {
sam_account_name_ = value;
return *this;
// Sets the value of the common name key.
SearchBuilder& SetCommonName(const std::string& value) {
common_name_ = value;
return *this;
// Sets the value of the userAccountControl key.
SearchBuilder& SetUserAccountControl(const uint32_t value) {
user_account_control_ = value;
return *this;
// Sets the value of the pwdLastSet key.
SearchBuilder& SetPwdLastSet(const uint64_t value) {
pwd_last_set_ = value;
return *this;
// Prevents output of pwdLastSet and userAccountControl fields.
SearchBuilder& NoPwdFields() {
output_pwd_fields = false;
return *this;
std::string given_name_ = kGivenName;
std::string display_name_ = kDisplayName;
std::string object_guid_ = kAccountId;
std::string sam_account_name_ = kUserName;
std::string common_name_ = kCommonName;
uint32_t user_account_control_ = kUserAccountControl;
uint64_t pwd_last_set_ = kPwdLastSet;
bool output_pwd_fields = true;
// Searches |str| for (|searchKey|=value) and returns value. Returns an empty
// string if the key could not be found or if the value is empty.
std::string FindSearchValue(const std::string& str, const char* search_key) {
const std::string full_key = base::StringPrintf("(%s=", search_key);
size_t idx1 = str.find(full_key);
if (idx1 == std::string::npos)
return "";
const size_t idx2 = str.find(")", idx1 + full_key.size());
if (idx2 == std::string::npos)
return "";
idx1 += full_key.size();
return str.substr(idx1, idx2 - idx1);
// Prints custom stub net ads gpo list output corresponding to one remote GPO
// with the given properties. For |gpflags| see kGpFlag*.
std::string PrintGpo(const char* guid,
uint32_t version_user,
uint32_t version_machine,
int gpflags) {
DCHECK(gpflags >= 0 && gpflags < kGpFlagCount);
return base::StringPrintf(
kStubRemoteGpo, guid, (version_user << 16) | version_machine,
version_user, version_machine, version_user, version_user,
version_machine, version_machine, guid, guid, kGpFlagsStr[gpflags]);
// Reads the machine and user passwords from stdin.
// Expected format is:machine_pass + "\n" + user_pass.
bool GetNetAdsJoinPasswords(std::string* user_password,
std::string* machine_password) {
std::string passwords_str;
if (!ReadPipeToString(STDIN_FILENO, &passwords_str))
return false;
std::vector<std::string> passwords = base::SplitString(
passwords_str, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
if (passwords.size() != 2)
return false;
*machine_password = std::move(passwords[0]);
*user_password = std::move(passwords[1]);
return true;
// Reads the contents of the (stub) user Kerberos credentials cache. Returns an
// empty string if the file does not exist.
std::string GetUserKrb5CCData(const std::string& smb_conf_path) {
// Note: Can't use GetKrb5CCFilePath() here since the env var is not defined,
// so figure it out from |smb_conf_path|.
// smb.conf is at <basepath>/temp/smb_*.conf.
// krb5cc is at <basepath>/temp/samba/krb5cc_user.
const base::FilePath krb5cc_path = base::FilePath(smb_conf_path)
std::string krb5cc_data;
if (!base::PathExists(krb5cc_path))
return std::string();
CHECK(base::ReadFileToString(krb5cc_path, &krb5cc_data));
return krb5cc_data;
// Reads the smb.conf file at |smb_conf_path| and extracts the string value
// associated with given |setting|.
std::string GetStringValueFromSmbConf(const std::string& smb_conf_path,
const std::string& setting) {
std::string smb_conf;
CHECK(base::ReadFileToString(base::FilePath(smb_conf_path), &smb_conf));
std::string value;
CHECK(FindToken(smb_conf, '=', setting, &value));
return value;
// Reads the smb.conf file at |smb_conf_path| and extracts the netbios name.
std::string GetMachineNameFromSmbConf(const std::string& smb_conf_path) {
// We need the device smb.conf here, the user smb.conf doesn't contain the
// netbios name.
std::string device_smb_conf_path = smb_conf_path;
base::ReplaceFirstSubstringAfterOffset(&device_smb_conf_path, 0, kSmbConfUser,
return GetStringValueFromSmbConf(device_smb_conf_path, "netbios name");
// Returns different stub net ads search results depending on |object_guid|.
std::string GetSearchResultFromObjectGUID(const std::string& object_guid) {
SearchBuilder search_builder;
// Valid account id, return valid search result for the default user.
if (object_guid == kAccountId)
return search_builder.GetResult();
// Invalid account id, return bad "nothing found" search result.
if (object_guid == kBadAccountId)
return kStubBadSearch;
// Pretend that the password expired.
if (object_guid == kExpiredPasswordAccountId)
return search_builder.SetPwdLastSet(0).GetResult();
// Pretend that the password never expires.
if (object_guid == kNeverExpirePasswordAccountId) {
return search_builder.SetPwdLastSet(0)
// Pretend that the password changed on the server.
if (object_guid == kPasswordChangedAccountId)
return search_builder.SetPwdLastSet(kPwdLastSet + 1).GetResult();
// Pretend missing pwdLastSet and userAccountControl fields.
if (object_guid == kNoPwdFieldsAccountId)
return search_builder.NoPwdFields().GetResult();
return std::string();
// Returns different stub net ads search results depending on
// |sam_account_name|.
std::string GetSearchResultFromSAMAccountName(
const std::string& sam_account_name) {
SearchBuilder search_builder;
// Set special account ids, required during auth for tests that use the ids in
// GetUserStatus().
if (sam_account_name == kPasswordChangedUserName)
return search_builder.SetObjectGuid(kPasswordChangedAccountId).GetResult();
if (sam_account_name == kNoPwdFieldsUserName) {
return search_builder.SetObjectGuid(kNoPwdFieldsAccountId)
// In all cases, just return a search result with the proper sAMAccountName.
return search_builder.GetResult();
// Formats time according to "Fri, 03 Feb 2017 05:24:05 UTC".
std::string FormatServerTime(const base::Time& time) {
time_t utime = time.ToTimeT();
struct tm tm;
gmtime_r(&utime, &tm);
char str[64];
CHECK(strftime(str, sizeof(str), "%a, %d %b %Y %H:%M:%S UTC", &tm));
return std::string(str);
// Returns the path of a marker file to check whether "net setdomainsid" has
// been called for a given workgroup stored in a config file at |smb_conf_path|.
base::FilePath GetDomainSidMarkerPath(const std::string& smb_conf_path) {
return base::FilePath(smb_conf_path)
.Append(kFakeDomainSidMarkerPrefix +
GetStringValueFromSmbConf(smb_conf_path, "workgroup"));
// Fakes setting a "net setdomainsid" call by writing a marker file.
void SetFakeDomainSid(const std::string& smb_conf_path) {
char c = 0;
CHECK_EQ(base::WriteFile(GetDomainSidMarkerPath(smb_conf_path), &c, 1), 1);
// Checks whether "net setdomainsid" has been called before by checking the
// marker file.
bool IsFakeDomainSidSet(const std::string& smb_conf_path) {
return base::PathExists(GetDomainSidMarkerPath(smb_conf_path));
// Handles a stub 'net ads workgroup' call. Different behavior is triggered by
// passing different machine names (in smb.conf) and user credential caches.
int HandleWorkgroup(const std::string& smb_conf_path) {
// Read machine name from smb.conf.
const std::string machine_name = GetMachineNameFromSmbConf(smb_conf_path);
// Stub server ping error when the TGT is expired (to get certain behavior in
// GetUserStatus()). Note that SambaInterface::PingServer currently calls net
// ads workgroup to check if the server is available.
if (machine_name == base::ToUpperASCII(kPingServerFailMachineName) &&
GetUserKrb5CCData(smb_conf_path) == kExpiredKrb5CCData) {
WriteOutput("", kNetworkError);
return kExitCodeError;
// Select workgroup based on realm.
std::string workgroup = (GetStringValueFromSmbConf(smb_conf_path, "realm") ==
? kSecondaryWorkgroup
: kDefaultWorkgroup;
WriteOutput(base::StrCat({"Workgroup: ", workgroup}), "");
return kExitCodeOk;
// Handles a stub 'net ads join' call. Different behavior is triggered by
// passing different user principals, passwords and machine names (in smb.conf).
int HandleJoin(const std::string& command_line,
const std::string& smb_conf_path) {
// Read the passwords from stdin (should be machine_pass + "\n" + user_pass).
std::string user_password, machine_password;
if (!GetNetAdsJoinPasswords(&user_password, &machine_password)) {
LOG(ERROR) << "Failed to read passwords";
return kExitCodeError;
const std::string kUserFlag(std::string(kUserParam) + " ");
// Read machine name from smb.conf.
const std::string machine_name = GetMachineNameFromSmbConf(smb_conf_path);
// Stub too long machine name error.
if (machine_name.size() > kMaxMachineNameSize) {
base::StringPrintf(kMachineNameTooLongError, kMaxMachineNameSize,
machine_name.c_str(), machine_name.size()),
return kExitCodeError;
// Stub bad machine name error.
if (machine_name == base::ToUpperASCII(kInvalidMachineName)) {
WriteOutput(kInvalidMachineNameError, "");
return kExitCodeError;
// Stub seccomp failure.
if (machine_name == base::ToUpperASCII(kSeccompMachineName)) {
return kExitCodeOk;
// Stub insufficient quota error.
if (Contains(command_line, kUserFlag + kInsufficientQuotaUserPrincipal)) {
WriteOutput(kInsufficientQuotaError, "");
return kExitCodeError;
// Stub non-existing account error (same error as 'wrong password' error).
if (Contains(command_line, kUserFlag + kNonExistingUserPrincipal)) {
WriteOutput(kWrongPasswordError, "");
return kExitCodeError;
// Stub network error.
if (Contains(command_line, kUserFlag + kNetworkErrorUserPrincipal)) {
WriteOutput("", kNetworkError);
return kExitCodeError;
// Stub access denied error.
if (Contains(command_line, kUserFlag + kAccessDeniedUserPrincipal)) {
WriteOutput(kJoinAccessDeniedError, "");
return kExitCodeError;
// Stub encryption type not supported error.
if (Contains(command_line, kUserFlag + kEncTypeNotSupportedUserPrincipal)) {
WriteOutput(kEncTypeNotSupportedError, "");
return kExitCodeError;
// Check whether createcomputer argument matches the expected one.
if (Contains(command_line, kUserFlag + kExpectOuUserPrincipal)) {
CHECK(Contains(command_line, std::string(kCreatecomputerParam) +
<< "Bad createcomputer arg in command line " << command_line
<< ". Expected: " << kExpectedOuCreatecomputer;
return kExitCodeOk;
// Stub valid user principal. Switch behavior based on password.
if (Contains(command_line, kUserFlag + kUserPrincipal)) {
// Stub wrong password.
if (user_password == kWrongPassword) {
WriteOutput(kWrongPasswordError, "");
return kExitCodeError;
// Stub expired password.
if (user_password == kExpiredPassword) {
WriteOutput(kExpiredPasswordError, "");
return kExitCodeError;
// Stub valid password.
if (user_password == kPassword)
return kExitCodeOk;
NOTREACHED() << "UNHANDLED PASSWORD " << user_password;
return kExitCodeError;
return kExitCodeError;
// Handles a stub 'net ads info' call. Just returns stub information.
int HandleInfo(const std::string& smb_conf_path) {
// Read machine name from smb.conf.
const std::string machine_name = GetMachineNameFromSmbConf(smb_conf_path);
if (machine_name == base::ToUpperASCII(kChangePasswordMachineName)) {
// Figure out the machine pass last modified time.
// smb.conf is at <basepath>/temp/smb_*.conf, the
// password is at <basepath>/state/machine_pass.
const base::FilePath password_path = base::FilePath(smb_conf_path)
base::File::Info file_info;
if (GetFileInfo(password_path, &file_info)) {
const base::Time password_time = file_info.last_modified;
const base::Time server_time = password_time +
kDefaultMachinePasswordChangeRate +
const std::string server_time_str = FormatServerTime(server_time);
WriteOutput(base::StringPrintf(kStubInfo, server_time_str.c_str()), "");
return kExitCodeOk;
WriteOutput(base::StringPrintf(kStubInfo, kDefaultServerTime), "");
return kExitCodeOk;
// Handles a stub 'net ads lookup' call. Just returns stub information.
int HandleLookup() {
WriteOutput(kStubLookup, "");
return kExitCodeOk;
// Handles a stub 'net ads gpo list' call. Different behavior is triggered by
// passing different machine names (in smb.conf).
int HandleGpoList(const std::string& smb_conf_path) {
// Read machine name from smb.conf.
const std::string machine_name = GetMachineNameFromSmbConf(smb_conf_path);
// Stub empty GPO list.
if (machine_name == base::ToUpperASCII(kEmptyGpoMachineName))
return kExitCodeOk;
// Samba 4.10.7 requires a domain sid for this command.
if (!IsFakeDomainSidSet(smb_conf_path))
return kExitCodeError;
// All other GPO lists use the local GPO.
std::string gpos = kStubLocalGpo;
// Increase the version by default, so that GPOs will always reload properly
// (prevents nasty surprises in tests). The version is only frozen for GPO
// cache tests.
const auto test_dir = base::FilePath(smb_conf_path).DirName();
const int version = 1 + PostIncTestCounter(test_dir);
if (machine_name == base::ToUpperASCII(kGpoDownloadErrorMachineName)) {
// Stub GPO list that triggers a download error in smbclient.
gpos += PrintGpo(kErrorGpoGuid, version, version, kGpFlagAllEnabled);
} else if (machine_name == base::ToUpperASCII(kSeccompMachineName)) {
// Stub GPO list that triggers a seccomp failure in smbclient.
gpos += PrintGpo(kSeccompGpoGuid, version, version, kGpFlagAllEnabled);
} else if (machine_name == base::ToUpperASCII(kOneGpoMachineName)) {
// Stub GPO list that downloads one GPO if present.
gpos += PrintGpo(kGpo1Guid, version, version, kGpFlagAllEnabled);
} else if (machine_name == base::ToUpperASCII(kTwoGposMachineName)) {
// Stub GPO list that downloads two GPOs if present.
gpos += PrintGpo(kGpo1Guid, version, version, kGpFlagAllEnabled);
gpos += PrintGpo(kGpo2Guid, version, version, kGpFlagAllEnabled);
} else if (machine_name ==
base::ToUpperASCII(kOneGpoKeepVersionMachineName)) {
// Stub GPO list with two GPOs and frozen version.
gpos += PrintGpo(kGpo1Guid, 1, 1, kGpFlagAllEnabled);
} else if (machine_name ==
base::ToUpperASCII(kTwoGposKeepVersionMachineName)) {
// Stub GPO list with two GPOs and freezing the version of the second.
gpos += PrintGpo(kGpo1Guid, version, version, kGpFlagAllEnabled);
gpos += PrintGpo(kGpo2Guid, 1, 1, kGpFlagAllEnabled);
} else if (machine_name == base::ToUpperASCII(kZeroUserVersionMachineName)) {
// Stub GPO list that contains a GPO with version_user == 0 (should be
// ignored during user policy fetch).
gpos += PrintGpo(kGpo1Guid, 0, version, kGpFlagAllEnabled);
} else if (machine_name == base::ToUpperASCII(kDisableUserFlagMachineName)) {
// Stub GPO list that contains a GPO with kGpFlagUserDisabled set (should be
// ignored during user policy fetch).
gpos += PrintGpo(kGpo1Guid, version, version, kGpFlagUserDisabled);
} else if (machine_name == base::ToUpperASCII(kLoopbackGpoMachineName)) {
// Stub GPO list that contains
// - GPO1 when querying GPOs for the user account and
// - GPO2 when querying GPOs for the device account.
bool requesting_user_gpos = Contains(smb_conf_path, kSmbConfUser);
const char* gpo_guid = requesting_user_gpos ? kGpo1Guid : kGpo2Guid;
gpos += PrintGpo(gpo_guid, version, version, kGpFlagAllEnabled);
WriteOutput(smb_conf_path, gpos);
return kExitCodeOk;
// Handles a stub 'net ads search' call. Different behavior is triggered by
// passing different sAMAccountNames or objectGUIDs as search term.
int HandleSearch(const std::string& command_line) {
std::string sam_account_name =
FindSearchValue(command_line, kSearchSAMAccountName);
std::string object_guid_octet =
FindSearchValue(command_line, kSearchObjectGUID);
// Handle the net ads search command to detect unaffiliated users.
if (sam_account_name == base::ToUpperASCII(kUnaffiliatedMachineName) + "$")
return kExitCodeUnspecifiedError;
std::string search_result;
if (!object_guid_octet.empty()) {
// Search by objectGUID aka account id.
std::string object_guid = OctetStringToGuidForTesting(object_guid_octet);
search_result = GetSearchResultFromObjectGUID(object_guid);
} else if (!sam_account_name.empty()) {
// Search by sAMAccountName.
search_result = GetSearchResultFromSAMAccountName(sam_account_name);
} else {
WriteOutput(search_result, "");
return kExitCodeOk;
// Handles a stub 'net setdomainsid' call. Writes out a marker file to indicate
// that the domain sid has been set. This is checked in 'net ads gpo list'. This
// fakes the behavior of Samba 4.10.7, which requires the domain sid to be set
// for that command.
int HandleSetDomainSid(const std::string& smb_conf_path) {
// net setdomainsid should be called at most once for the same workgroup.
if (IsFakeDomainSidSet(smb_conf_path))
return kExitCodeError;
return kExitCodeOk;
int HandleCommandLine(const std::string& command_line,
const std::string& smb_conf_path) {
// Make sure the caller adds the debug level.
CHECK(Contains(command_line, kDebugParam));
// Stub net ads workgroup.
if (StartsWithCaseSensitive(command_line, "ads workgroup"))
return HandleWorkgroup(smb_conf_path);
// Stub net ads join.
if (StartsWithCaseSensitive(command_line, "ads join"))
return HandleJoin(command_line, smb_conf_path);
// Stub net ads info.
if (StartsWithCaseSensitive(command_line, "ads info"))
return HandleInfo(smb_conf_path);
// Stub net ads lookup.
if (StartsWithCaseSensitive(command_line, "ads lookup"))
return HandleLookup();
// Stub net ads gpo list.
if (StartsWithCaseSensitive(command_line, "ads gpo list"))
return HandleGpoList(smb_conf_path);
// Stub net ads search.
if (StartsWithCaseSensitive(command_line, "ads search"))
return HandleSearch(command_line);
// Stub net setdomainsid.
if (StartsWithCaseSensitive(command_line, "setdomainsid"))
return HandleSetDomainSid(smb_conf_path);
return kExitCodeError;
} // namespace
} // namespace authpolicy
int main(int argc, char* argv[]) {
// Find Samba configuration path ("-s" argument).
const std::string smb_conf_path =
authpolicy::GetArgValue(argc, argv, authpolicy::kConfigParam);
if (smb_conf_path.empty()) {
authpolicy::WriteOutput("", authpolicy::kSmbConfArgMissingError);
return authpolicy::kExitCodeError;
const std::string command_line = authpolicy::GetCommandLine(argc, argv);
return authpolicy::HandleCommandLine(command_line, smb_conf_path);