Support custom LSA auth provider (#246)
Support custom LSA auth provider
This commit is contained in:
parent
ef55537c8b
commit
507fe3283a
|
@ -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, ®_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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
"as,
|
||||
&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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_ */
|
||||
|
|
Loading…
Reference in New Issue