Run SSHD as NetworkService (#121)

PowerShell/Win32-OpenSSH#681
This commit is contained in:
Manoj Ampalam 2017-04-24 22:02:03 -07:00 committed by GitHub
parent bc3f21a6a3
commit 1ff1b07410
9 changed files with 282 additions and 361 deletions

Binary file not shown.

View File

@ -11,70 +11,6 @@ $logsdir = Join-Path $scriptdir "logs"
$sshdAccount = "NT SERVICE\SSHD"
#Idea borrowed from http://sqldbamusings.blogspot.com/2012/03/powershell-adding-accounts-to-local.html
function Add-Privilege
{
param(
[string] $Account,
[ValidateSet("SeAssignPrimaryTokenPrivilege", "SeServiceLogonRight")]
[string] $Privilege
)
#Get $Account SID
$account_sid = $null
try
{
$ntprincipal = new-object System.Security.Principal.NTAccount "$Account"
$sid = $ntprincipal.Translate([System.Security.Principal.SecurityIdentifier])
$account_sid = $sid.Value.ToString()
}
catch
{
Throw 'Unable to resolve '+ $Account
}
#Prepare policy settings file to be applied
$settings_to_export = [System.IO.Path]::GetTempFileName()
"[Unicode]" | Set-Content $settings_to_export -Encoding Unicode
"Unicode=yes" | Add-Content $settings_to_export -Force -WhatIf:$false
"[Version]" | Add-Content $settings_to_export -Force -WhatIf:$false
"signature=`"`$CHICAGO`$`"" | Add-Content $settings_to_export -Force -WhatIf:$false
"Revision=1" | Add-Content $settings_to_export -Force -WhatIf:$false
"[Privilege Rights]" | Add-Content $settings_to_export -Force -WhatIf:$false
#Get Current policy settings
$imported_settings = [System.IO.Path]::GetTempFileName()
secedit.exe /export /areas USER_RIGHTS /cfg "$($imported_settings)" > $null
if (-not(Test-Path $imported_settings)) {
Throw "Unable to import current security policy settings"
}
#find current assigned accounts to $Privilege and add it to $settings_to_export
$current_settings = Get-Content $imported_settings -Encoding Unicode
$existing_setting = $null
foreach ($setting in $current_settings) {
if ($setting -like "$Privilege`*") {
$existing_setting = $setting
}
}
#Add $account_sid to list
if ($existing_setting -eq $null) {
$Privilege + " = *" + $account_sid | Add-Content $settings_to_export -Force -WhatIf:$false
}
else
{
$existing_setting + ",*" + $account_sid | Add-Content $settings_to_export -Force -WhatIf:$false
}
#export
secedit.exe /configure /db "secedit.sdb" /cfg "$($settings_to_export)" /areas USER_RIGHTS > $null
}
if (-not (Test-Path $sshdpath)) {
throw "sshd.exe is not present in script path"
}
@ -95,10 +31,8 @@ New-Service -Name ssh-agent -BinaryPathName $sshagentpath -Description "SSH Agen
cmd.exe /c 'sc.exe sdset ssh-agent D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;RP;;;AU)'
New-Service -Name sshd -BinaryPathName $sshdpath -Description "SSH Daemon" -StartupType Manual -DependsOn ssh-agent | Out-Null
sc.exe config sshd obj= $sshdAccount
Add-Privilege -Account $sshdAccount -Privilege SeAssignPrimaryTokenPrivilege
Add-Privilege -Account $sshdAccount -Privilege SeServiceLogonRight
sc.exe config sshd obj= "NT AUTHORITY\NetworkService"
sc.exe sidtype sshd unrestricted
if(-not (test-path $logsdir -PathType Container))
{

View File

@ -1,6 +1,7 @@
/*
* Author: Manoj Ampalam <manoj.ampalam@microsoft.com>
* ssh-agent implementation on Windows
* NT Service routines
*
* Copyright (c) 2015 Microsoft Corp.
* All rights reserved
@ -43,7 +44,8 @@ static SERVICE_STATUS_HANDLE service_status_handle;
static SERVICE_STATUS service_status;
static VOID ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
static VOID
ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
service_status.dwCurrentState = dwCurrentState;
service_status.dwWin32ExitCode = dwWin32ExitCode;
@ -62,7 +64,8 @@ static VOID ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD d
SetServiceStatus(service_status_handle, &service_status);
}
static VOID WINAPI service_handler(DWORD dwControl)
static VOID WINAPI
service_handler(DWORD dwControl)
{
switch (dwControl)
{
@ -81,22 +84,27 @@ static VOID WINAPI service_handler(DWORD dwControl)
ReportSvcStatus(service_status.dwCurrentState, NO_ERROR, 0);
}
BOOL WINAPI ctrl_c_handler(
_In_ DWORD dwCtrlType
) {
BOOL WINAPI
ctrl_c_handler(_In_ DWORD dwCtrlType)
{
/* for any Ctrl type, shutdown agent*/
debug("Ctrl+C received");
agent_shutdown();
return TRUE;
}
int wmain(int argc, wchar_t **argv) {
int
wmain(int argc, wchar_t **argv)
{
w32posix_initialize();
/* this exits() on failure*/
load_config();
if (!StartServiceCtrlDispatcherW(dispatch_table)) {
if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
/*
* agent is not spawned by SCM
* Its either started in debug mode or a worker child
*/
if (argc == 2) {
if (wcsncmp(argv[1], L"-ddd", 4) == 0)
log_init("ssh-agent", 7, 1, 1);
@ -105,9 +113,10 @@ int wmain(int argc, wchar_t **argv) {
else if (wcsncmp(argv[1], L"-d", 2) == 0)
log_init("ssh-agent", 5, 1, 1);
/* Set Ctrl+C handler if starting in debug mode */
if (wcsncmp(argv[1], L"-d", 2) == 0) {
SetConsoleCtrlHandler(ctrl_c_handler, TRUE);
agent_start(TRUE, FALSE, 0);
agent_start(TRUE);
return 0;
}
@ -116,7 +125,7 @@ int wmain(int argc, wchar_t **argv) {
h += _wtoi(*(argv + 1));
if (h != 0) {
log_init("ssh-agent", config_log_level(), 1, 0);
agent_start(FALSE, TRUE, h);
agent_process_connection(h);
return 0;
}
}
@ -146,14 +155,16 @@ int wmain(int argc, wchar_t **argv) {
return 0;
}
int scm_start_service(DWORD num, LPWSTR* args) {
int
scm_start_service(DWORD num, LPWSTR* args)
{
service_status_handle = RegisterServiceCtrlHandlerW(L"ssh-agent", service_handler);
ZeroMemory(&service_status, sizeof(service_status));
service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 300);
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
log_init("ssh-agent", config_log_level(), 1, 0);
agent_start(FALSE, FALSE, 0);
agent_start(FALSE);
return 0;
}

View File

@ -44,40 +44,21 @@ static OVERLAPPED ol;
static HANDLE pipe;
static SECURITY_ATTRIBUTES sa;
static int
init_listener() {
{
if ((ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) {
debug("cannot create event ERROR:%d", GetLastError());
return GetLastError();
}
pipe = INVALID_HANDLE_VALUE;
sa.bInheritHandle = FALSE;
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(L"D:P(A;; GA;;; AU)", SDDL_REVISION_1,
&sa.lpSecurityDescriptor, &sa.nLength)) {
debug("cannot convert sddl ERROR:%d", GetLastError());
return GetLastError();
}
}
return 0;
}
static void
agent_cleanup() {
{
if (ol.hEvent != NULL)
CloseHandle(ol.hEvent);
if (pipe != INVALID_HANDLE_VALUE)
CloseHandle(pipe);
}
agent_cleanup()
{
if (ol.hEvent != NULL)
CloseHandle(ol.hEvent);
if (pipe != INVALID_HANDLE_VALUE)
CloseHandle(pipe);
if (ioc_port)
CloseHandle(ioc_port);
return;
}
static DWORD WINAPI
iocp_work(LPVOID lpParam) {
iocp_work(LPVOID lpParam)
{
DWORD bytes;
struct agent_connection* con = NULL;
OVERLAPPED *p_ol;
@ -85,7 +66,7 @@ iocp_work(LPVOID lpParam) {
con = NULL;
p_ol = NULL;
if (GetQueuedCompletionStatus(ioc_port, &bytes, &(ULONG_PTR)con, &p_ol, INFINITE) == FALSE) {
debug("iocp error: %d on %p \n", GetLastError(), con);
debug("iocp error: %d on %p", GetLastError(), con);
if (con)
agent_connection_on_error(con, GetLastError());
else
@ -93,80 +74,56 @@ iocp_work(LPVOID lpParam) {
}
else
agent_connection_on_io(con, bytes, p_ol);
}
}
static void
process_connection(HANDLE pipe) {
struct agent_connection* con;
if ((con = malloc(sizeof(struct agent_connection))) == NULL)
fatal("failed to alloc");
memset(con, 0, sizeof(struct agent_connection));
con->connection = pipe;
if (CreateIoCompletionPort(pipe, ioc_port, (ULONG_PTR)con, 0) != ioc_port)
fatal("failed to assign pipe to ioc_port");
agent_connection_on_io(con, 0, &con->ol);
iocp_work(NULL);
}
static void
agent_listen_loop() {
agent_listen_loop()
{
DWORD r;
HANDLE wait_events[2];
wait_events[0] = event_stop_agent;
wait_events[1] = ol.hEvent;
while (1) {
{
{
pipe = CreateNamedPipeW(
AGENT_PIPE_ID, // pipe name
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access
PIPE_TYPE_BYTE | // message type pipe
PIPE_READMODE_BYTE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
0, // client time-out
&sa);
pipe = CreateNamedPipeW(
AGENT_PIPE_ID, // pipe name
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access
PIPE_TYPE_BYTE | // message type pipe
PIPE_READMODE_BYTE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
0, // client time-out
&sa);
if (pipe == INVALID_HANDLE_VALUE) {
verbose("cannot create listener pipe ERROR:%d", GetLastError());
SetEvent(event_stop_agent);
}
else if (ConnectNamedPipe(pipe, &ol) != FALSE) {
verbose("ConnectNamedPipe returned TRUE unexpectedly ");
SetEvent(event_stop_agent);
}
if (pipe == INVALID_HANDLE_VALUE) {
verbose("cannot create listener pipe ERROR:%d", GetLastError());
SetEvent(event_stop_agent);
} else if (ConnectNamedPipe(pipe, &ol) != FALSE) {
verbose("ConnectNamedPipe returned TRUE unexpectedly ");
SetEvent(event_stop_agent);
}
if (GetLastError() == ERROR_PIPE_CONNECTED) {
debug("Client has already connected");
SetEvent(ol.hEvent);
}
if (GetLastError() == ERROR_PIPE_CONNECTED) {
debug("Client has already connected");
SetEvent(ol.hEvent);
}
if (GetLastError() != ERROR_IO_PENDING) {
debug("ConnectNamedPipe failed ERROR: %d", GetLastError());
SetEvent(event_stop_agent);
}
}
if (GetLastError() != ERROR_IO_PENDING) {
debug("ConnectNamedPipe failed ERROR: %d", GetLastError());
SetEvent(event_stop_agent);
}
r = WaitForMultipleObjects(2, wait_events, FALSE, INFINITE);
if (r == WAIT_OBJECT_0) {
//received signal to shutdown
/*received signal to shutdown*/
debug("shutting down");
agent_cleanup();
return;
}
else if ((r > WAIT_OBJECT_0) && (r <= (WAIT_OBJECT_0 + 1))) {
} else if ((r > WAIT_OBJECT_0) && (r <= (WAIT_OBJECT_0 + 1))) {
/* process incoming connection */
HANDLE con = pipe;
DWORD client_pid = 0;
@ -174,11 +131,10 @@ agent_listen_loop() {
GetNamedPipeClientProcessId(con, &client_pid);
verbose("client pid %d connected", client_pid);
if (debug_mode) {
process_connection(con);
agent_process_connection(con);
agent_cleanup();
return;
}
else {
} else {
/* spawn a child to take care of this*/
wchar_t path[PATH_MAX], module_path[PATH_MAX];
PROCESS_INFORMATION pi;
@ -193,8 +149,7 @@ agent_listen_loop() {
DETACHED_PROCESS, NULL, NULL,
&si, &pi) == FALSE)) {
verbose("Failed to create child process %ls ERROR:%d", module_path, GetLastError());
}
else {
} else {
debug("spawned worker %d for agent client pid %d ", pi.dwProcessId, client_pid);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
@ -203,17 +158,18 @@ agent_listen_loop() {
CloseHandle(con);
}
}
else {
} else {
fatal("wait on events ended with %d ERROR:%d", r, GetLastError());
}
}
}
void agent_cleanup_connection(struct agent_connection* con) {
void
agent_cleanup_connection(struct agent_connection* con)
{
debug("connection %p clean up", con);
CloseHandle(con->connection);
CloseHandle(con->pipe_handle);
if (con->hProfile)
UnloadUserProfile(con->auth_token, con->hProfile);
if (con->auth_token)
@ -223,45 +179,59 @@ void agent_cleanup_connection(struct agent_connection* con) {
ioc_port = NULL;
}
void agent_shutdown() {
verbose("shutdown");
void
agent_shutdown()
{
SetEvent(event_stop_agent);
}
#define REG_AGENT_SDDL L"D:P(A;; GR;;; AU)(A;; GA;;; SY)(A;; GA;;; BA)"
void
agent_start(BOOL dbg_mode, BOOL child, HANDLE pipe) {
agent_start(BOOL dbg_mode)
{
int r;
HKEY agent_root = NULL;
DWORD process_id = GetCurrentProcessId();
verbose("agent_start pid:%d, dbg:%d, child:%d, pipe:%d", process_id, dbg_mode, child, pipe);
verbose("%s pid:%d, dbg:%d", __FUNCTION__, process_id, dbg_mode);
debug_mode = dbg_mode;
memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
sa.nLength = sizeof(sa);
/* allow access to Authenticated users and Network Service */
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(L"D:P(A;;GA;;;AU)(A;;GA;;;NS)", SDDL_REVISION_1,
&sa.lpSecurityDescriptor, &sa.nLength))
fatal("cannot convert sddl ERROR:%d", GetLastError());
if ((r = RegCreateKeyExW(HKEY_LOCAL_MACHINE, SSH_AGENT_ROOT, 0, 0, 0, KEY_WRITE, &sa, &agent_root, 0)) != ERROR_SUCCESS)
fatal("cannot create agent root reg key, ERROR:%d", r);
if ((r = RegSetValueExW(agent_root, L"ProcessID", 0, REG_DWORD, (BYTE*)&process_id, 4)) != ERROR_SUCCESS)
fatal("cannot publish agent master process id ERROR:%d", r);
if ((event_stop_agent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
fatal("cannot create global stop event ERROR:%d", GetLastError());
if ((ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
fatal("cannot create event ERROR:%d", GetLastError());
pipe = INVALID_HANDLE_VALUE;
sa.bInheritHandle = FALSE;
agent_listen_loop();
}
void
agent_process_connection(HANDLE pipe)
{
struct agent_connection* con;
verbose("%s pipe:%p", __FUNCTION__, pipe);
if ((ioc_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)NULL, 0)) == NULL)
fatal("cannot create ioc port ERROR:%d", GetLastError());
if ((con = malloc(sizeof(struct agent_connection))) == NULL)
fatal("failed to alloc");
if (child == FALSE) {
SECURITY_ATTRIBUTES sa;
memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
sa.nLength = sizeof(sa);
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(REG_AGENT_SDDL, SDDL_REVISION_1, &sa.lpSecurityDescriptor, &sa.nLength))
fatal("ConvertStringSecurityDescriptorToSecurityDescriptorW failed");
if ((r = RegCreateKeyExW(HKEY_LOCAL_MACHINE, SSH_AGENT_ROOT, 0, 0, 0, KEY_WRITE, &sa, &agent_root, 0)) != ERROR_SUCCESS)
fatal("cannot create agent root reg key, ERROR:%d", r);
if ((r = RegSetValueExW(agent_root, L"ProcessID", 0, REG_DWORD, (BYTE*)&process_id, 4)) != ERROR_SUCCESS)
fatal("cannot publish agent master process id ERROR:%d", r);
if ((event_stop_agent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
fatal("cannot create global stop event ERROR:%d", GetLastError());
if ((r = init_listener()) != 0)
fatal("failed to create server pipes ERROR:%d", r);
agent_listen_loop();
}
else { /* this is a child process that processes one connection */
process_connection(pipe);
}
memset(con, 0, sizeof(struct agent_connection));
con->pipe_handle = pipe;
if (CreateIoCompletionPort(pipe, ioc_port, (ULONG_PTR)con, 0) != ioc_port)
fatal("failed to assign pipe to ioc_port");
agent_connection_on_io(con, 0, &con->ol);
iocp_work(NULL);
}

View File

@ -11,7 +11,7 @@
struct agent_connection {
OVERLAPPED ol;
HANDLE connection;
HANDLE pipe_handle;
struct {
DWORD num_bytes;
DWORD transferred;
@ -27,11 +27,9 @@ struct agent_connection {
} state;
enum {
UNKNOWN = 0,
OTHER,
LOCAL_SYSTEM,
SSHD,
NETWORK_SERVICE
} client_process;
USER, /* client is running as some user */
MACHINE /* clinet is running as machine - System, NS or LS */
} client_type;
HANDLE auth_token;
HANDLE hProfile;
};
@ -40,7 +38,8 @@ void agent_connection_on_io(struct agent_connection*, DWORD, OVERLAPPED*);
void agent_connection_on_error(struct agent_connection* , DWORD);
void agent_connection_disconnect(struct agent_connection*);
void agent_start(BOOL, BOOL, HANDLE);
void agent_start(BOOL);
void agent_process_connection(HANDLE);
void agent_shutdown();
void agent_cleanup_connection(struct agent_connection*);

View File

@ -96,7 +96,6 @@ int load_config() {
wchar_t basePath[PATH_MAX] = { 0 };
wchar_t path[PATH_MAX] = { 0 };
/* TODO - account for UNICODE paths*/
if (GetCurrentModulePath(basePath, PATH_MAX) == -1)
return -1;

View File

@ -243,7 +243,7 @@ int process_passwordauth_request(struct sshbuf* request, struct sshbuf* response
goto done;
}
if ((FALSE == GetNamedPipeClientProcessId(con->connection, &client_pid)) ||
if ((FALSE == GetNamedPipeClientProcessId(con->pipe_handle, &client_pid)) ||
((client_proc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, client_pid)) == NULL) ||
(FALSE == DuplicateHandle(GetCurrentProcess(), token, client_proc, &dup_token, TOKEN_QUERY | TOKEN_IMPERSONATE, FALSE, DUPLICATE_SAME_ACCESS)) ||
(sshbuf_put_u32(response, (int)(intptr_t)dup_token) != 0)) {
@ -317,7 +317,7 @@ int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response,
goto done;
}
if ((FALSE == GetNamedPipeClientProcessId(con->connection, &client_pid)) ||
if ((FALSE == GetNamedPipeClientProcessId(con->pipe_handle, &client_pid)) ||
( (client_proc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, client_pid)) == NULL) ||
(FALSE == DuplicateHandle(GetCurrentProcess(), token, client_proc, &dup_token, TOKEN_QUERY | TOKEN_IMPERSONATE, FALSE, DUPLICATE_SAME_ACCESS)) ||
(sshbuf_put_u32(response, (int)(intptr_t)dup_token) != 0)) {

View File

@ -39,104 +39,101 @@ int process_request(struct agent_connection*);
return; \
} while (0)
void agent_connection_on_error(struct agent_connection* con, DWORD error) {
void
agent_connection_on_error(struct agent_connection* con, DWORD error)
{
ABORT_CONNECTION_RETURN(con);
}
void agent_connection_on_io(struct agent_connection* con, DWORD bytes, OVERLAPPED* ol) {
void
agent_connection_on_io(struct agent_connection* con, DWORD bytes, OVERLAPPED* ol)
{
/* process error */
debug3("connection io %p #bytes:%d state:%d", con, bytes, con->state);
if ((bytes == 0) && (GetOverlappedResult(con->connection, ol, &bytes, FALSE) == FALSE))
if ((bytes == 0) && (GetOverlappedResult(con->pipe_handle, ol, &bytes, FALSE) == FALSE))
ABORT_CONNECTION_RETURN(con);
if (con->state == DONE)
DebugBreak();
{
switch (con->state) {
case LISTENING:
case WRITING:
/* Writing is done, read next request */
/* assert on assumption that write always completes on sending all bytes*/
if (bytes != con->io_buf.num_bytes)
DebugBreak();
con->state = READING_HEADER;
ZeroMemory(&con->io_buf, sizeof(con->io_buf));
if (!ReadFile(con->connection, con->io_buf.buf,
HEADER_SIZE, NULL, &con->ol) && (GetLastError() != ERROR_IO_PENDING))
ABORT_CONNECTION_RETURN(con);
break;
case READING_HEADER:
con->io_buf.transferred += bytes;
if (con->io_buf.transferred == HEADER_SIZE) {
con->io_buf.num_bytes = PEEK_U32(con->io_buf.buf);
con->io_buf.transferred = 0;
if (con->io_buf.num_bytes > MAX_MESSAGE_SIZE)
ABORT_CONNECTION_RETURN(con);
con->state = READING;
if (!ReadFile(con->connection, con->io_buf.buf,
con->io_buf.num_bytes, NULL, &con->ol)&&(GetLastError() != ERROR_IO_PENDING))
ABORT_CONNECTION_RETURN(con);
}
else {
if (!ReadFile(con->connection, con->io_buf.buf + con->io_buf.num_bytes,
HEADER_SIZE - con->io_buf.num_bytes, NULL, &con->ol)&& (GetLastError() != ERROR_IO_PENDING))
ABORT_CONNECTION_RETURN(con);
}
break;
case READING:
con->io_buf.transferred += bytes;
if (con->io_buf.transferred == con->io_buf.num_bytes) {
if (process_request(con) != 0) {
ABORT_CONNECTION_RETURN(con);
}
con->state = WRITING;
if (!WriteFile(con->connection, con->io_buf.buf,
con->io_buf.num_bytes, NULL, &con->ol)&& (GetLastError() != ERROR_IO_PENDING) )
ABORT_CONNECTION_RETURN(con);
}
else {
if (!ReadFile(con->connection, con->io_buf.buf + con->io_buf.transferred,
con->io_buf.num_bytes - con->io_buf.transferred, NULL, &con->ol)&& (GetLastError() != ERROR_IO_PENDING))
ABORT_CONNECTION_RETURN(con);
}
break;
default:
switch (con->state) {
case LISTENING:
case WRITING:
/* Writing is done, read next request */
/* assert on assumption that write always completes on sending all bytes*/
if (bytes != con->io_buf.num_bytes)
DebugBreak();
}
}
con->state = READING_HEADER;
ZeroMemory(&con->io_buf, sizeof(con->io_buf));
if (!ReadFile(con->pipe_handle, con->io_buf.buf,
HEADER_SIZE, NULL, &con->ol) && (GetLastError() != ERROR_IO_PENDING))
ABORT_CONNECTION_RETURN(con);
break;
case READING_HEADER:
con->io_buf.transferred += bytes;
if (con->io_buf.transferred == HEADER_SIZE) {
con->io_buf.num_bytes = PEEK_U32(con->io_buf.buf);
con->io_buf.transferred = 0;
if (con->io_buf.num_bytes > MAX_MESSAGE_SIZE)
ABORT_CONNECTION_RETURN(con);
con->state = READING;
if (!ReadFile(con->pipe_handle, con->io_buf.buf,
con->io_buf.num_bytes, NULL, &con->ol)&&(GetLastError() != ERROR_IO_PENDING))
ABORT_CONNECTION_RETURN(con);
} else {
if (!ReadFile(con->pipe_handle, con->io_buf.buf + con->io_buf.num_bytes,
HEADER_SIZE - con->io_buf.num_bytes, NULL, &con->ol)&& (GetLastError() != ERROR_IO_PENDING))
ABORT_CONNECTION_RETURN(con);
}
break;
case READING:
con->io_buf.transferred += bytes;
if (con->io_buf.transferred == con->io_buf.num_bytes) {
if (process_request(con) != 0) {
ABORT_CONNECTION_RETURN(con);
}
con->state = WRITING;
if (!WriteFile(con->pipe_handle, con->io_buf.buf,
con->io_buf.num_bytes, NULL, &con->ol)&& (GetLastError() != ERROR_IO_PENDING) )
ABORT_CONNECTION_RETURN(con);
} else {
if (!ReadFile(con->pipe_handle, con->io_buf.buf + con->io_buf.transferred,
con->io_buf.num_bytes - con->io_buf.transferred, NULL, &con->ol)&& (GetLastError() != ERROR_IO_PENDING))
ABORT_CONNECTION_RETURN(con);
}
break;
default:
DebugBreak();
}
}
void agent_connection_disconnect(struct agent_connection* con) {
CancelIoEx(con->connection, NULL);
DisconnectNamedPipe(con->connection);
void
agent_connection_disconnect(struct agent_connection* con)
{
CancelIoEx(con->pipe_handle, NULL);
DisconnectNamedPipe(con->pipe_handle);
}
static int
get_con_client_type(HANDLE pipe) {
get_con_client_type(struct agent_connection* con)
{
int r = -1;
wchar_t *sshd_act = L"NT SERVICE\\SSHD", *ref_dom = NULL;
PSID sshd_sid = NULL;
char system_sid[SECURITY_MAX_SID_SIZE];
char ns_sid[SECURITY_MAX_SID_SIZE];
DWORD sshd_sid_len = 0, reg_dom_len = 0, info_len = 0, sid_size;
char ls_sid[SECURITY_MAX_SID_SIZE];
DWORD reg_dom_len = 0, info_len = 0, sid_size;
SID_NAME_USE nuse;
HANDLE token;
TOKEN_USER* info = NULL;
HANDLE pipe = con->pipe_handle;
if (ImpersonateNamedPipeClient(pipe) == FALSE)
return -1;
if (LookupAccountNameW(NULL, sshd_act, NULL, &sshd_sid_len, NULL, &reg_dom_len, &nuse) == TRUE ||
(sshd_sid = malloc(sshd_sid_len)) == NULL ||
(ref_dom = (wchar_t*)malloc(reg_dom_len * 2)) == NULL ||
LookupAccountNameW(NULL, sshd_act, sshd_sid, &sshd_sid_len, ref_dom, &reg_dom_len, &nuse) == FALSE ||
OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &token) == FALSE ||
GetTokenInformation(token, TokenUser, NULL, 0, &info_len) == TRUE ||
(info = (TOKEN_USER*)malloc(info_len)) == NULL ||
GetTokenInformation(token, TokenUser, info, info_len, &info_len) == FALSE)
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &token) == FALSE ||
GetTokenInformation(token, TokenUser, NULL, 0, &info_len) == TRUE ||
(info = (TOKEN_USER*)malloc(info_len)) == NULL ||
GetTokenInformation(token, TokenUser, info, info_len, &info_len) == FALSE)
goto done;
sid_size = SECURITY_MAX_SID_SIZE;
@ -145,22 +142,20 @@ get_con_client_type(HANDLE pipe) {
sid_size = SECURITY_MAX_SID_SIZE;
if (CreateWellKnownSid(WinNetworkServiceSid, NULL, ns_sid, &sid_size) == FALSE)
goto done;
sid_size = SECURITY_MAX_SID_SIZE;
if (CreateWellKnownSid(WinLocalServiceSid, NULL, ls_sid, &sid_size) == FALSE)
goto done;
if (EqualSid(info->User.Sid, system_sid))
r = LOCAL_SYSTEM;
else if (EqualSid(info->User.Sid, sshd_sid))
r = SSHD;
else if (EqualSid(info->User.Sid, ns_sid))
r = NETWORK_SERVICE;
if (EqualSid(info->User.Sid, system_sid) ||
EqualSid(info->User.Sid, ls_sid) ||
EqualSid(info->User.Sid, ns_sid))
con->client_type = MACHINE;
else
r = OTHER;
con->client_type = USER;
debug2("client type: %d", r);
debug2("client type: %s", con->client_type == MACHINE? "machine" : "user");
r = 0;
done:
if (sshd_sid)
free(sshd_sid);
if (ref_dom)
free(ref_dom);
if (info)
free(info);
RevertToSelf();
@ -168,51 +163,49 @@ done:
}
static int
process_request(struct agent_connection* con) {
process_request(struct agent_connection* con)
{
int r = -1;
struct sshbuf *request = NULL, *response = NULL;
u_char type;
if (con->client_process == UNKNOWN)
if ((con->client_process = get_con_client_type(con->connection)) == -1)
goto done;
if (con->client_type == UNKNOWN && get_con_client_type(con) == -1) {
debug("unable to get client process type");
goto done;
}
//Sleep(30 * 1000);
request = sshbuf_from(con->io_buf.buf, con->io_buf.num_bytes);
response = sshbuf_new();
if ((request == NULL) || (response == NULL))
goto done;
{
u_char type;
if (sshbuf_get_u8(request, &type) != 0)
return -1;
debug("process agent request type %d", type);
if (sshbuf_get_u8(request, &type) != 0)
return -1;
debug("process agent request type %d", type);
switch (type) {
case SSH2_AGENTC_ADD_IDENTITY:
r = process_add_identity(request, response, con);
break;
case SSH2_AGENTC_REQUEST_IDENTITIES:
r = process_request_identities(request, response, con);
break;
case SSH2_AGENTC_SIGN_REQUEST:
r = process_sign_request(request, response, con);
break;
case SSH2_AGENTC_REMOVE_IDENTITY:
r = process_remove_key(request, response, con);
break;
case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
r = process_remove_all(request, response, con);
break;
case SSH_AGENT_AUTHENTICATE:
r = process_authagent_request(request, response, con);
break;
default:
debug("unknown agent request %d", type);
r = -1;
break;
}
switch (type) {
case SSH2_AGENTC_ADD_IDENTITY:
r = process_add_identity(request, response, con);
break;
case SSH2_AGENTC_REQUEST_IDENTITIES:
r = process_request_identities(request, response, con);
break;
case SSH2_AGENTC_SIGN_REQUEST:
r = process_sign_request(request, response, con);
break;
case SSH2_AGENTC_REMOVE_IDENTITY:
r = process_remove_key(request, response, con);
break;
case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
r = process_remove_all(request, response, con);
break;
case SSH_AGENT_AUTHENTICATE:
r = process_authagent_request(request, response, con);
break;
default:
debug("unknown agent request %d", type);
r = -1;
break;
}
done:

View File

@ -36,21 +36,33 @@
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383
/*
* get registry root where keys are stored
* user keys are stored in user's hive
* while system keys (host keys) in HKLM
*/
static int
get_user_root(struct agent_connection* con, HKEY *root){
get_user_root(struct agent_connection* con, HKEY *root)
{
int r = 0;
*root = NULL;
if (ImpersonateNamedPipeClient(con->connection) == FALSE)
return -1;
LONG ret;
*root = HKEY_LOCAL_MACHINE;
if (con->client_process > OTHER)
*root = HKEY_LOCAL_MACHINE;
else if (RegOpenCurrentUser(KEY_ALL_ACCESS, root) != ERROR_SUCCESS)
r = -1;
if (*root == NULL)
debug("cannot connect to user's registry root");
RevertToSelf();
if (con->client_type == USER) {
if (ImpersonateNamedPipeClient(con->pipe_handle) == FALSE)
return -1;
*root = NULL;
/*
* TODO - check that user profile is loaded,
* otherwise, this will return default profile
*/
if ((ret = RegOpenCurrentUser(KEY_ALL_ACCESS, root)) != ERROR_SUCCESS) {
debug("unable to open user's registry hive, ERROR - %d", ret);
r = -1;
}
RevertToSelf();
}
return r;
}
@ -59,8 +71,8 @@ convert_blob(struct agent_connection* con, const char *blob, DWORD blen, char **
int success = 0;
DATA_BLOB in, out;
if (con->client_process == OTHER)
if (ImpersonateNamedPipeClient(con->connection) == FALSE)
if (con->client_type == USER)
if (ImpersonateNamedPipeClient(con->pipe_handle) == FALSE)
return -1;
in.cbData = blen;
@ -73,8 +85,7 @@ convert_blob(struct agent_connection* con, const char *blob, DWORD blen, char **
debug("cannot encrypt data");
goto done;
}
}
else {
} else {
if (!CryptUnprotectData(&in, NULL, NULL, 0, NULL, 0, &out)) {
debug("cannot decrypt data");
goto done;
@ -91,7 +102,7 @@ convert_blob(struct agent_connection* con, const char *blob, DWORD blen, char **
done:
if (out.pbData)
LocalFree(out.pbData);
if (con->client_process == OTHER)
if (con->client_type == USER)
RevertToSelf();
return success? 0: -1;
}
@ -99,7 +110,8 @@ done:
#define REG_KEY_SDDL L"D:P(A;; GA;;; SY)(A;; GA;;; BA)"
int
process_add_identity(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) {
process_add_identity(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con)
{
struct sshkey* key = NULL;
int r = 0, blob_len, eblob_len, request_invalid = 0, success = 0;
size_t comment_len, pubkey_blob_len;
@ -170,7 +182,8 @@ done:
}
static int sign_blob(const struct sshkey *pubkey, u_char ** sig, size_t *siglen,
const u_char *blob, size_t blen, u_int flags, struct agent_connection* con) {
const u_char *blob, size_t blen, u_int flags, struct agent_connection* con)
{
HKEY reg = 0, sub = 0, user_root = 0;
int r = 0, success = 0;
struct sshkey* prikey = NULL;
@ -225,7 +238,8 @@ done:
}
int
process_sign_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) {
process_sign_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con)
{
u_char *blob, *data, *signature = NULL;
size_t blen, dlen, slen = 0;
u_int flags = 0;
@ -257,8 +271,7 @@ done:
sshbuf_put_string(response, signature, slen) != 0) {
r = -1;
}
}
else
} else
if (sshbuf_put_u8(response, SSH_AGENT_FAILURE) != 0)
r = -1;
}
@ -271,7 +284,8 @@ done:
}
int
process_remove_key(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) {
process_remove_key(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con)
{
HKEY user_root = 0, root = 0;
char *blob, *thumbprint = NULL;
size_t blen;
@ -285,10 +299,10 @@ process_remove_key(struct sshbuf* request, struct sshbuf* response, struct agent
}
if ((thumbprint = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL ||
get_user_root(con, &user_root) != 0 ||
RegOpenKeyExW(user_root, SSH_KEYS_ROOT, 0,
DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_WOW64_64KEY, &root) != 0 ||
RegDeleteTreeA(root, thumbprint) != 0)
get_user_root(con, &user_root) != 0 ||
RegOpenKeyExW(user_root, SSH_KEYS_ROOT, 0,
DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_WOW64_64KEY, &root) != 0 ||
RegDeleteTreeA(root, thumbprint) != 0)
goto done;
success = 1;
done:
@ -309,7 +323,8 @@ done:
return r;
}
int
process_remove_all(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) {
process_remove_all(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con)
{
HKEY user_root = 0, root = 0;
int r = 0;
@ -333,7 +348,8 @@ done:
}
int
process_request_identities(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) {
process_request_identities(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con)
{
int count = 0, index = 0, success = 0, r = 0;
HKEY root = NULL, sub = NULL, user_root = 0;
char* count_ptr = NULL;
@ -379,8 +395,7 @@ process_request_identities(struct sshbuf* request, struct sshbuf* response, stru
key_count++;
}
}
else
} else
break;
}
@ -393,8 +408,7 @@ done:
sshbuf_put_u32(response, key_count) != 0 ||
sshbuf_putb(response, identities) != 0)
goto done;
}
else
} else
r = -1;
if (pkblob)
@ -413,7 +427,8 @@ done:
}
int process_keyagent_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) {
int process_keyagent_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con)
{
u_char type;
if (sshbuf_get_u8(request, &type) != 0)