зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1498518 - user authentication prompts for Windows, r=keeler,mhowell
Depends on D8480 Differential Revision: https://phabricator.services.mozilla.com/D8530 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
68af3a46d9
Коммит
4c3ce53100
|
@ -13,10 +13,206 @@ NS_IMPL_ISUPPORTS(OSReauthenticator, nsIOSReauthenticator)
|
|||
using namespace mozilla;
|
||||
using dom::Promise;
|
||||
|
||||
#if defined(XP_WIN)
|
||||
#include <combaseapi.h>
|
||||
#include <ntsecapi.h>
|
||||
#include <wincred.h>
|
||||
#include <windows.h>
|
||||
struct HandleCloser
|
||||
{
|
||||
typedef HANDLE pointer;
|
||||
void operator()(HANDLE h)
|
||||
{
|
||||
if(h != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(h);
|
||||
}
|
||||
}
|
||||
};
|
||||
struct BufferFreer
|
||||
{
|
||||
typedef LPVOID pointer;
|
||||
void operator()(LPVOID b)
|
||||
{
|
||||
CoTaskMemFree(b);
|
||||
}
|
||||
};
|
||||
typedef std::unique_ptr<HANDLE, HandleCloser> ScopedHANDLE;
|
||||
typedef std::unique_ptr<LPVOID, BufferFreer> ScopedBuffer;
|
||||
|
||||
// Get the token info holding the sid.
|
||||
std::unique_ptr<char[]>
|
||||
GetTokenInfo(ScopedHANDLE& token)
|
||||
{
|
||||
DWORD length = 0;
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-gettokeninformation
|
||||
mozilla::Unused << GetTokenInformation(token.get(), TokenUser, nullptr, 0,
|
||||
&length);
|
||||
if (!length || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
MOZ_LOG(gCredentialManagerSecretLog,
|
||||
LogLevel::Debug,
|
||||
("Unable to obtain current token info."));
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<char[]> token_info(new char[length]);
|
||||
if (!GetTokenInformation(token.get(), TokenUser, token_info.get(), length,
|
||||
&length)) {
|
||||
MOZ_LOG(gCredentialManagerSecretLog,
|
||||
LogLevel::Debug,
|
||||
("Unable to obtain current token info (second call, possible system error."));
|
||||
return nullptr;
|
||||
}
|
||||
return token_info;
|
||||
}
|
||||
|
||||
std::unique_ptr<char[]>
|
||||
GetUserTokenInfo()
|
||||
{
|
||||
// Get current user sid to make sure the same user got logged in.
|
||||
HANDLE token;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
|
||||
// Couldn't get a process token. This will fail any unlock attempts later.
|
||||
MOZ_LOG(gCredentialManagerSecretLog,
|
||||
LogLevel::Debug,
|
||||
("Unable to obtain process token."));
|
||||
return nullptr;
|
||||
}
|
||||
ScopedHANDLE scopedToken(token);
|
||||
return GetTokenInfo(scopedToken);
|
||||
}
|
||||
|
||||
// Use the Windows credential prompt to ask the user to authenticate the
|
||||
// currently used account.
|
||||
static nsresult
|
||||
ReauthenticateUserWindows(const nsACString& aPrompt,
|
||||
/* out */ bool& reauthenticated)
|
||||
{
|
||||
reauthenticated = false;
|
||||
|
||||
HANDLE lsa;
|
||||
// Get authentication handle for future user authentications.
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsaconnectuntrusted
|
||||
if (LsaConnectUntrusted(&lsa) != ERROR_SUCCESS) {
|
||||
MOZ_LOG(gCredentialManagerSecretLog,
|
||||
LogLevel::Debug,
|
||||
("Error aquiring lsa. Authentication attempts will fail."));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
ScopedHANDLE scopedLsa(lsa);
|
||||
|
||||
std::unique_ptr<char[]> userTokenInfo = GetUserTokenInfo();
|
||||
if (!userTokenInfo || lsa == INVALID_HANDLE_VALUE) {
|
||||
MOZ_LOG(gCredentialManagerSecretLog,
|
||||
LogLevel::Debug,
|
||||
("Error setting up login and user token."));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// CredUI prompt.
|
||||
CREDUI_INFOW credui = {};
|
||||
credui.cbSize = sizeof(credui);
|
||||
// TODO: maybe set parent (Firefox) here.
|
||||
credui.hwndParent = nullptr;
|
||||
const nsString& prompt = PromiseFlatString(NS_ConvertUTF8toUTF16(aPrompt));
|
||||
credui.pszMessageText = prompt.get();
|
||||
credui.pszCaptionText = nullptr;
|
||||
credui.hbmBanner = nullptr; // ignored
|
||||
|
||||
ULONG authPackage = 0;
|
||||
LPVOID outCredBuffer = nullptr;
|
||||
ULONG outCredSize = 0;
|
||||
BOOL save = false;
|
||||
// Could be used in next iteration if the previous login failed.
|
||||
DWORD err = 0;
|
||||
|
||||
// Get user's Windows credentials.
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-creduipromptforwindowscredentialsw
|
||||
err = CredUIPromptForWindowsCredentialsW(&credui, err, &authPackage,
|
||||
nullptr, 0, &outCredBuffer, &outCredSize, &save,
|
||||
CREDUIWIN_ENUMERATE_CURRENT_USER);
|
||||
ScopedBuffer scopedOutCredBuffer(outCredBuffer);
|
||||
if (err != ERROR_SUCCESS) {
|
||||
MOZ_LOG(gCredentialManagerSecretLog,
|
||||
LogLevel::Debug,
|
||||
("Error getting authPackage for user login"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Verify the credentials.
|
||||
TOKEN_SOURCE source;
|
||||
PCHAR contextName = const_cast<PCHAR>("Mozilla");
|
||||
size_t nameLength = std::min(TOKEN_SOURCE_LENGTH,
|
||||
static_cast<int>(strlen(contextName)));
|
||||
// Note that the string must not be longer than TOKEN_SOURCE_LENGTH.
|
||||
memcpy(source.SourceName, contextName, nameLength);
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-allocatelocallyuniqueid
|
||||
if (!AllocateLocallyUniqueId(&source.SourceIdentifier)) {
|
||||
MOZ_LOG(gCredentialManagerSecretLog,
|
||||
LogLevel::Debug,
|
||||
("Error allocating ID for logon process."));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NTSTATUS substs;
|
||||
void* profileBuffer = nullptr;
|
||||
ULONG profileBufferLength = 0;
|
||||
QUOTA_LIMITS limits = {0};
|
||||
LUID luid;
|
||||
HANDLE token;
|
||||
LSA_STRING name;
|
||||
name.Buffer = contextName;
|
||||
name.Length = strlen(name.Buffer);
|
||||
name.MaximumLength = name.Length;
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsalogonuser
|
||||
NTSTATUS sts = LsaLogonUser(scopedLsa.get(), &name, (SECURITY_LOGON_TYPE)Interactive,
|
||||
authPackage, scopedOutCredBuffer.get(),
|
||||
outCredSize, nullptr, &source, &profileBuffer,
|
||||
&profileBufferLength, &luid, &token, &limits,
|
||||
&substs);
|
||||
ScopedHANDLE scopedToken(token);
|
||||
LsaFreeReturnBuffer(profileBuffer);
|
||||
LsaDeregisterLogonProcess(scopedLsa.get());
|
||||
if (sts == ERROR_SUCCESS) {
|
||||
MOZ_LOG(gCredentialManagerSecretLog,
|
||||
LogLevel::Debug,
|
||||
("User logged in successfully."));
|
||||
} else {
|
||||
MOZ_LOG(gCredentialManagerSecretLog,
|
||||
LogLevel::Debug,
|
||||
("Login failed with %lx (%lx).", sts, LsaNtStatusToWinError(sts)));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// The user can select any user to log-in on the authentication prompt.
|
||||
// Make sure that the logged in user is the current user.
|
||||
std::unique_ptr<char[]> logonTokenInfo = GetTokenInfo(scopedToken);
|
||||
if (!logonTokenInfo) {
|
||||
MOZ_LOG(gCredentialManagerSecretLog,
|
||||
LogLevel::Debug,
|
||||
("Error getting logon token info."));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
PSID logonSID = reinterpret_cast<TOKEN_USER*>(logonTokenInfo.get())->User.Sid;
|
||||
PSID userSID = reinterpret_cast<TOKEN_USER*>(userTokenInfo.get())->User.Sid;
|
||||
if (EqualSid(userSID, logonSID)) {
|
||||
MOZ_LOG(gCredentialManagerSecretLog,
|
||||
LogLevel::Debug,
|
||||
("Login successfully (correct user)."));
|
||||
reauthenticated = true;
|
||||
return NS_OK;
|
||||
}
|
||||
MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
|
||||
("Login failed (wrong user)."));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static nsresult
|
||||
ReauthenticateUser(const nsACString& prompt, /* out */ bool& reauthenticated)
|
||||
{
|
||||
reauthenticated = false;
|
||||
#if defined(XP_WIN)
|
||||
return ReauthenticateUserWindows(prompt, reauthenticated);
|
||||
#endif // Reauthentication is not implemented for this platform.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -155,6 +155,9 @@ if CONFIG['OS_ARCH'] == 'Darwin':
|
|||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
OS_LIBS += [
|
||||
'credui'
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'CredentialManagerSecret.cpp',
|
||||
]
|
||||
|
|
|
@ -142,6 +142,9 @@ run-sequentially = hardcoded ports
|
|||
run-sequentially = hardcoded ports
|
||||
[test_oskeystore.js]
|
||||
[test_osreauthenticator.js]
|
||||
# Reauthentication has been implemented on Windows, so running this test results in
|
||||
# the OS popping up a dialog, which means we can't run it in automation.
|
||||
skip-if = os == 'win'
|
||||
[test_password_prompt.js]
|
||||
[test_pinning.js]
|
||||
run-sequentially = hardcoded ports
|
||||
|
|
Загрузка…
Ссылка в новой задаче