Support custom LSA auth provider (#246)

Support custom LSA auth provider
This commit is contained in:
bagajjal 2017-12-07 11:12:29 -08:00 committed by Yanbing
parent ef55537c8b
commit 507fe3283a
8 changed files with 269 additions and 30 deletions

View File

@ -41,9 +41,6 @@
#include <sys/types.h>
#include <pwd.h>
#ifdef WINDOWS
#include <logonuser.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
@ -59,6 +56,11 @@
#include "auth-options.h"
#include "authfd.h"
#ifdef WINDOWS
#include "logonuser.h"
#include "monitor_wrap.h"
#endif
extern Buffer loginmsg;
extern ServerOptions options;
@ -228,10 +230,59 @@ sys_auth_passwd(Authctxt *authctxt, const char *password)
}
#elif defined(WINDOWS)
void
sys_auth_passwd_lsa(Authctxt *authctxt, const char *password)
{
char *tmp = NULL, *domain = NULL, *lsa_auth_pkg = NULL;
wchar_t *lsa_auth_pkg_w = NULL;
int domain_len = 0, lsa_auth_pkg_len = 0;
HKEY reg_key = 0;
REGSAM mask = STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_WOW64_64KEY;
if ((RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\OpenSSH", 0, mask, &reg_key) == ERROR_SUCCESS) &&
(RegQueryValueExW(reg_key, L"LSAAuthenticationPackage", 0, NULL, NULL, &lsa_auth_pkg_len) == ERROR_SUCCESS)) {
lsa_auth_pkg_w = (wchar_t *) malloc(lsa_auth_pkg_len); // lsa_auth_pkg_len includes the null terminating character.
if (!lsa_auth_pkg_w)
fatal("%s: out of memory", __func__);
memset(lsa_auth_pkg_w, 0, lsa_auth_pkg_len);
if (RegQueryValueExW(reg_key, L"LSAAuthenticationPackage", 0, NULL, (LPBYTE)lsa_auth_pkg_w, &lsa_auth_pkg_len) == ERROR_SUCCESS) {
lsa_auth_pkg = utf16_to_utf8(lsa_auth_pkg_w);
if (!lsa_auth_pkg)
error("utf16_to_utf8 failed to convert lsa_auth_pkg_w:%ls", lsa_auth_pkg_w);
if ((tmp = strstr(authctxt->pw->pw_name, "@")) != NULL) {
domain_len = strlen(tmp) + 1;
domain = (char*) malloc(domain_len * sizeof(char));
memcpy(domain, tmp, domain_len);
domain[domain_len] = '\0';
}
debug("Authenticating using LSA Auth Package:%ls", lsa_auth_pkg_w);
authctxt->auth_token = mm_auth_custom_lsa(authctxt->pw->pw_name, password, domain, lsa_auth_pkg);
}
}
done:
if (domain)
free(domain);
if (lsa_auth_pkg_w)
free(lsa_auth_pkg_w);
if (lsa_auth_pkg)
free(lsa_auth_pkg);
if (reg_key)
RegCloseKey(reg_key);
}
/*
* Authenticate on Windows - Call LogonUser and retrieve user token
* Authenticate on Windows
* - Call LogonUser and retrieve user token
* - If LogonUser fails, then try the LSA (Local Security Authority) authentication.
*/
int sys_auth_passwd(Authctxt *authctxt, const char *password)
int
sys_auth_passwd(Authctxt *authctxt, const char *password)
{
wchar_t *user_utf16 = NULL, *udom_utf16 = NULL, *pwd_utf16 = NULL, *tmp;
HANDLE token = NULL;
@ -249,25 +300,33 @@ int sys_auth_passwd(Authctxt *authctxt, const char *password)
}
if (LogonUserExExWHelper(user_utf16, udom_utf16, pwd_utf16, LOGON32_LOGON_NETWORK_CLEARTEXT,
LOGON32_PROVIDER_DEFAULT, NULL, &token, NULL, NULL, NULL, NULL) == FALSE) {
if (GetLastError() == ERROR_PASSWORD_MUST_CHANGE)
/*
* TODO - need to add support to force password change
* by sending back SSH_MSG_USERAUTH_PASSWD_CHANGEREQ
*/
LOGON32_PROVIDER_DEFAULT, NULL, &token, NULL, NULL, NULL, NULL) == TRUE)
authctxt->auth_token = (void*)(INT_PTR)token;
else {
if (GetLastError() == ERROR_PASSWORD_MUST_CHANGE)
/*
* TODO - need to add support to force password change
* by sending back SSH_MSG_USERAUTH_PASSWD_CHANGEREQ
*/
error("password for user %s has expired", authctxt->pw->pw_name);
else
debug("failed to logon user: %ls domain: %ls error:%d", user_utf16, udom_utf16, GetLastError());
goto done;
}
else {
debug("Windows authentication failed for user: %ls domain: %ls error:%d", user_utf16, udom_utf16, GetLastError());
authctxt->auth_token = (void*)(INT_PTR)token;
r = 1;
/* If LSA authentication package is configured then it will return the auth_token */
sys_auth_passwd_lsa(authctxt, password);
}
}
done:
if (authctxt->auth_token)
r = 1;
if (user_utf16)
free(user_utf16);
if (pwd_utf16)
SecureZeroMemory(pwd_utf16, sizeof(wchar_t) * wcslen(pwd_utf16));
return r;
}
#endif /* WINDOWS */

View File

@ -118,7 +118,8 @@ ConEnterRawMode()
dwAttributes |= (DWORD)ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
char *envValue = NULL;
_dupenv_s(&envValue, NULL, "SSH_TERM_CONHOST_PARSER");
size_t len = 0;
_dupenv_s(&envValue, &len, "SSH_TERM_CONHOST_PARSER");
if (NULL != envValue) {
isConHostParserEnabled = atoi(envValue);

View File

@ -3,6 +3,7 @@
*/
#pragma once
#define SSH_PRIV_AGENT_MSG_ID 0
#define PUBKEY_AUTH_REQUEST "pubkey"
#define LOAD_USER_PROFILE_REQUEST "loadprofile"
#define SSH_PRIV_AGENT_MSG_ID 0
#define PUBKEY_AUTH_REQUEST "pubkey"
#define LOAD_USER_PROFILE_REQUEST "loadprofile"
#define CUSTOM_LSA_AUTH_REQUEST "lsaauth"

View File

@ -1204,7 +1204,7 @@ get_default_shell_path()
wchar_t *tmp = malloc(PATH_MAX + 1);
if (!tmp) {
printf_s("get_default_shell_path(), Unable to allocate memory");
printf_s("%s: out of memory", __func__);
exit(255);
}
@ -1257,6 +1257,9 @@ get_default_shell_path()
if (tmp)
free(tmp);
if (reg_key)
RegCloseKey(reg_key);
return default_shell_path;
}

View File

@ -41,6 +41,8 @@
#include "inc\utf.h"
#include "..\priv-agent.h"
#include "logonuser.h"
#include <Ntsecapi.h>
#include <ntstatus.h>
#pragma warning(push, 3)
@ -49,7 +51,7 @@ int pubkey_allowed(struct sshkey* pubkey, char* user_utf8);
static void
InitLsaString(LSA_STRING *lsa_string, const char *str)
{
if (str == NULL)
if (!str)
memset(lsa_string, 0, sizeof(LSA_STRING));
else {
lsa_string->Buffer = (char *)str;
@ -250,6 +252,128 @@ done:
return dup_t;
}
int
process_custom_lsa_auth_req(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con)
{
char *user = NULL, *pwd = NULL, *domain = NULL, *lsa_pkg = NULL;
size_t user_len = 0, pwd_len = 0, domain_len = 0, lsa_pkg_len = 0;
wchar_t *userw = NULL, *pwdw = NULL, *domw = NULL, *providerw = NULL;
HANDLE token = NULL, dup_token = NULL, lsa_handle = NULL;
LSA_OPERATIONAL_MODE mode;
ULONG auth_package_id, logon_info_size = 0;
NTSTATUS ret, subStatus;
wchar_t *logon_info = NULL;
LSA_STRING logon_process_name, lsa_auth_package_name, originName;
TOKEN_SOURCE sourceContext;
PVOID pProfile = NULL;
LUID logonId;
QUOTA_LIMITS quotas;
DWORD cbProfile;
int retVal = -1;
if (sshbuf_get_string_direct(request, &user, &user_len) != 0 ||
sshbuf_get_cstring(request, &pwd, &pwd_len) != 0 ||
sshbuf_get_string_direct(request, &domain, &domain_len) != 0 ||
user_len > MAX_USER_LEN || pwd_len == 0 ||
sshbuf_get_string_direct(request, &lsa_pkg, &lsa_pkg_len) != 0) {
debug("invalid LSA auth request");
goto done;
}
debug("LSA auth request, user:%s domain:%s lsa_pkg:%s ", user, domain, lsa_pkg);
/* convert everything to utf16 only if its not NULL */
if ((userw = utf8_to_utf16(user)) == NULL ||
(pwdw = utf8_to_utf16(pwd)) == NULL ||
(domain && (domw = utf8_to_utf16(domain)) == NULL)) {
debug("%s: out of memory", __func__);
goto done;
}
/* call into LSA provider , get and duplicate token */
InitLsaString(&logon_process_name, "ssh-agent");
InitLsaString(&lsa_auth_package_name, lsa_pkg);
InitLsaString(&originName, "sshd");
if ((ret = LsaRegisterLogonProcess(&logon_process_name, &lsa_handle, &mode)) != STATUS_SUCCESS) {
error("LsaRegisterLogonProcess failed, error:%ld", LsaNtStatusToWinError(ret));
goto done;
}
if ((ret = LsaLookupAuthenticationPackage(lsa_handle, &lsa_auth_package_name, &auth_package_id)) != STATUS_SUCCESS) {
error("LsaLookupAuthenticationPackage failed, lsa auth pkg:%ls error:%ld", lsa_pkg, LsaNtStatusToWinError(ret));
goto done;
}
logon_info_size = (ULONG)((wcslen(userw) + wcslen(pwdw) + wcslen(domw) + 3) * sizeof(wchar_t));
logon_info = (wchar_t *)malloc(logon_info_size);
if (NULL == logon_info)
fatal("%s:out of memory", __func__);
wcscpy_s(logon_info, logon_info_size, userw);
wcscat_s(logon_info, logon_info_size, L";");
wcscat_s(logon_info, logon_info_size, pwdw);
wcscat_s(logon_info, logon_info_size, L";");
wcscat_s(logon_info, logon_info_size, domw);
memcpy(sourceContext.SourceName, "sshd", sizeof(sourceContext.SourceName));
if (!AllocateLocallyUniqueId(&sourceContext.SourceIdentifier)) {
error("AllocateLocallyUniqueId failed, error:%d", GetLastError());
goto done;
}
if ((ret = LsaLogonUser(lsa_handle,
&originName,
Network,
auth_package_id,
logon_info,
logon_info_size,
NULL,
&sourceContext,
&pProfile,
&cbProfile,
&logonId,
&token,
&quotas,
&subStatus)) != STATUS_SUCCESS) {
if(ret == STATUS_ACCOUNT_RESTRICTION)
error("LsaLogonUser failed, error:%ld subStatus:%ld", LsaNtStatusToWinError(ret), subStatus);
else
error("LsaLogonUser failed error:%ld", LsaNtStatusToWinError(ret));
goto done;
}
if ((dup_token = duplicate_token_for_client(con, token)) == NULL)
goto done;
if (sshbuf_put_u32(response, (int)(intptr_t)dup_token) != 0)
goto done;
retVal = 0;
done:
/* delete allocated memory*/
if ((retVal == -1) && (sshbuf_put_u8(response, SSH_AGENT_FAILURE) == 0))
retVal = 0;
if (lsa_handle)
LsaDeregisterLogonProcess(lsa_handle);
if (logon_info)
free(logon_info);
if (pProfile)
LsaFreeReturnBuffer(pProfile);
if (userw)
free(userw);
if (pwdw)
free(pwdw);
if (domw)
free(domw);
if (token)
CloseHandle(token);
return retVal;
}
int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) {
int r = -1;
char *key_blob, *user, *sig, *blob;
@ -394,6 +518,8 @@ int process_privagent_request(struct sshbuf* request, struct sshbuf* response, s
return process_pubkeyauth_request(request, response, con);
else if (memcmp(opn, LOAD_USER_PROFILE_REQUEST, opn_len) == 0)
return process_loadprofile_request(request, response, con);
else if (memcmp(opn, CUSTOM_LSA_AUTH_REQUEST, opn_len) == 0)
return process_custom_lsa_auth_req(request, response, con);
else {
debug("unknown auth request: %s", opn);
return -1;

View File

@ -78,7 +78,8 @@ fd_table_initialize()
w32_io_stdin.type = NONSOCK_SYNC_FD;
char *envValue = NULL;
_dupenv_s(&envValue, NULL, SSH_ASYNC_STDIN);
size_t len = 0;
_dupenv_s(&envValue, &len, SSH_ASYNC_STDIN);
if (NULL != envValue) {
if(strcmp(envValue, "1") == 0)
w32_io_stdin.type = NONSOCK_FD;
@ -93,7 +94,7 @@ fd_table_initialize()
w32_io_stdout.type = NONSOCK_SYNC_FD;
envValue = NULL;
_dupenv_s(&envValue, NULL, SSH_ASYNC_STDOUT);
_dupenv_s(&envValue, &len, SSH_ASYNC_STDOUT);
if (NULL != envValue) {
if(strcmp(envValue, "1") == 0)
w32_io_stdout.type = NONSOCK_FD;
@ -108,7 +109,7 @@ fd_table_initialize()
w32_io_stderr.type = NONSOCK_SYNC_FD;
envValue = NULL;
_dupenv_s(&envValue, NULL, SSH_ASYNC_STDERR);
_dupenv_s(&envValue, &len, SSH_ASYNC_STDERR);
if (NULL != envValue) {
if(strcmp(envValue, "1") == 0)
w32_io_stderr.type = NONSOCK_FD;

View File

@ -152,15 +152,20 @@ void* mm_auth_pubkey(const char* user_name, const struct sshkey *key,
msg = sshbuf_new();
if (!msg)
fatal("%s: out of memory", __func__);
if (sshbuf_put_u8(msg, SSH_PRIV_AGENT_MSG_ID) != 0 ||
sshbuf_put_cstring(msg, PUBKEY_AUTH_REQUEST) != 0 ||
sshkey_to_blob(key, &blob, &blen) != 0 ||
sshbuf_put_string(msg, blob, blen) != 0 ||
sshbuf_put_cstring(msg, user_name) != 0 ||
sshbuf_put_string(msg, sig, slen) != 0 ||
sshbuf_put_string(msg, sshbuf_ptr(b), sshbuf_len(b)) != 0 ||
ssh_request_reply(agent_fd, msg, msg) != 0) {
debug("unable to send pubkeyauth request");
sshbuf_put_string(msg, sshbuf_ptr(b), sshbuf_len(b)) != 0) {
debug("unable to add data to ssh buffer");
break;
}
if(ssh_request_reply(agent_fd, msg, msg) != 0) {
error("unable to send pubkeyauth request to agent");
break;
}
@ -211,4 +216,46 @@ int mm_load_profile(const char* user_name, u_int token)
}
return result;
}
void*
mm_auth_custom_lsa(const char* user, const char* password, const char* domain, const char* lsa_auth_pkg)
{
/* Pass credentials to privileged agent to retrieve token upon successful authentication */
struct sshbuf *msg = NULL;
HANDLE token = 0;
int agent_fd;
while (1) {
if ((agent_fd = get_priv_agent_sock()) == -1)
break;
msg = sshbuf_new();
if (!msg)
fatal("%s: out of memory", __func__);
if (sshbuf_put_u8(msg, SSH_PRIV_AGENT_MSG_ID) != 0 ||
sshbuf_put_cstring(msg, CUSTOM_LSA_AUTH_REQUEST) != 0 ||
sshbuf_put_cstring(msg, user) != 0 ||
sshbuf_put_cstring(msg, password) != 0 ||
sshbuf_put_cstring(msg, domain) != 0 ||
sshbuf_put_cstring(msg, lsa_auth_pkg) != 0 ||
ssh_request_reply(agent_fd, msg, msg) != 0) {
debug("unable to send LSA Auth request");
break;
}
if (sshbuf_get_u32(msg, (u_int32_t *)&token) != 0) {
debug("failed to get the token from the agent");
break;
}
debug3("user:%s authenticated using LSA auth pkg:%s", user, lsa_auth_pkg);
break;
}
if (msg)
sshbuf_free(msg);
return (void*)(INT_PTR)token;
}

View File

@ -98,9 +98,10 @@ int mm_bsdauth_respond(void *, u_int, char **);
int mm_skey_query(void *, char **, char **, u_int *, char ***, u_int **);
int mm_skey_respond(void *, u_int, char **);
/* Windows specific */
/* Windows specific functions */
void* mm_auth_pubkey(const char*, const struct sshkey *, const u_char *, size_t,
struct sshbuf*);
int mm_load_profile(const char*, u_int );
void* mm_auth_custom_lsa(const char* user, const char* password, const char* dom, const char* lsa_auth_pkg);
#endif /* _MM_WRAP_H_ */