This commit is contained in:
Yanbing 2017-02-14 13:43:06 -08:00 committed by Manoj Ampalam
parent c4a9f83682
commit 8e9236b03b
16 changed files with 558 additions and 141 deletions

View File

@ -244,7 +244,6 @@ int sys_auth_passwd(Authctxt *authctxt, const char *password)
if (sshbuf_put_u8(msg, SSH_AGENT_AUTHENTICATE) != 0 || if (sshbuf_put_u8(msg, SSH_AGENT_AUTHENTICATE) != 0 ||
sshbuf_put_cstring(msg, PASSWD_AUTH_REQUEST) != 0 || sshbuf_put_cstring(msg, PASSWD_AUTH_REQUEST) != 0 ||
sshbuf_put_cstring(msg, authctxt->pw->pw_name) != 0 || sshbuf_put_cstring(msg, authctxt->pw->pw_name) != 0 ||
sshbuf_put_cstring(msg, authctxt->pw->pw_domain) != 0 ||
sshbuf_put_cstring(msg, password) != 0 || sshbuf_put_cstring(msg, password) != 0 ||
ssh_request_reply(auth_sock, msg, msg) != 0 || ssh_request_reply(auth_sock, msg, msg) != 0 ||
sshbuf_get_u32(msg, &token) != 0) { sshbuf_get_u32(msg, &token) != 0) {

View File

@ -196,7 +196,6 @@ userauth_pubkey(Authctxt *authctxt)
(r = sshkey_to_blob(key, &blob, &blen)) != 0 || (r = sshkey_to_blob(key, &blob, &blen)) != 0 ||
(r = sshbuf_put_string(msg, blob, blen)) != 0 || (r = sshbuf_put_string(msg, blob, blen)) != 0 ||
(r = sshbuf_put_cstring(msg, authctxt->pw->pw_name)) != 0 || (r = sshbuf_put_cstring(msg, authctxt->pw->pw_name)) != 0 ||
(r = sshbuf_put_cstring(msg, authctxt->pw->pw_domain)) != 0 ||
(r = sshbuf_put_string(msg, sig, slen)) != 0 || (r = sshbuf_put_string(msg, sig, slen)) != 0 ||
(r = sshbuf_put_string(msg, buffer_ptr(&b), buffer_len(&b))) != 0 || (r = sshbuf_put_string(msg, buffer_ptr(&b), buffer_len(&b))) != 0 ||
(r = ssh_request_reply(auth_sock, msg, msg)) != 0 || (r = ssh_request_reply(auth_sock, msg, msg)) != 0 ||

View File

@ -3,7 +3,6 @@ Import-Module $PSScriptRoot\build.psm1 -Force -DisableNameChecking
$repoRoot = Get-RepositoryRoot $repoRoot = Get-RepositoryRoot
$script:logFile = join-path $repoRoot.FullName "appveyor.log" $script:logFile = join-path $repoRoot.FullName "appveyor.log"
$script:messageFile = join-path $repoRoot.FullName "BuildMessage.log" $script:messageFile = join-path $repoRoot.FullName "BuildMessage.log"
$testfailed = $false
<# <#
Called by Write-BuildMsg to write to the build log, if it exists. Called by Write-BuildMsg to write to the build log, if it exists.
@ -234,13 +233,20 @@ function Download-PSCoreMSI
.SYNOPSIS .SYNOPSIS
This function installs the tools required by our tests This function installs the tools required by our tests
1) Pester for running the tests 1) Pester for running the tests
2) sysinternals required by the tests on windows. 2) sysinternals required by the tests on windows.
#> #>
function Install-TestDependencies function Install-TestDependencies
{ {
[CmdletBinding()] [CmdletBinding()]
param () param ()
# Install chocolatey
if(-not (Get-Command "choco" -ErrorAction SilentlyContinue))
{
Write-Log -Message "Chocolatey not present. Installing chocolatey."
Invoke-Expression ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1')) 2>&1 >> $script:logFile
}
$isModuleAvailable = Get-Module 'Pester' -ListAvailable $isModuleAvailable = Get-Module 'Pester' -ListAvailable
if (-not ($isModuleAvailable)) if (-not ($isModuleAvailable))
{ {
@ -360,18 +366,18 @@ function Build-Win32OpenSSHPackage
$folderName = $NativeHostArch $folderName = $NativeHostArch
if($NativeHostArch -ieq 'x86') if($NativeHostArch -ieq 'x86')
{ {
$folderName = "Win32" $folderName = "Win32"
} }
} }
else else
{ {
if($platform -ieq "AMD64") if($platform -ieq "AMD64")
{ {
$folderName = "x64" $folderName = "x64"
} }
else else
{ {
$folderName = "Win32" $folderName = "Win32"
} }
} }
@ -430,7 +436,7 @@ function Build-Win32OpenSSHPackage
} }
Add-Type -assemblyname System.IO.Compression.FileSystem Add-Type -assemblyname System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::CreateFromDirectory($OpenSSHDir, $package) [System.IO.Compression.ZipFile]::CreateFromDirectory($OpenSSHDir, $package)
} }
<# <#
@ -494,43 +500,15 @@ function Deploy-OpenSSHTests
} }
[System.IO.DirectoryInfo] $repositoryRoot = Get-RepositoryRoot [System.IO.DirectoryInfo] $repositoryRoot = Get-RepositoryRoot
#copy all pester tests
$sourceDir = Join-Path $repositoryRoot.FullName -ChildPath "regress\pesterTests" $sourceDir = Join-Path $repositoryRoot.FullName -ChildPath "regress\pesterTests"
Copy-Item -Path "$sourceDir\*" -Destination $OpenSSHTestDir -Include *.ps1,*.psm1 -Force -ErrorAction Stop Copy-Item -Path "$sourceDir\*" -Destination $OpenSSHTestDir -Include *.ps1,*.psm1, sshd_config -Force -ErrorAction Stop
#copy all unit tests.
$sourceDir = Join-Path $repositoryRoot.FullName -ChildPath "bin\$folderName\$RealConfiguration" $sourceDir = Join-Path $repositoryRoot.FullName -ChildPath "bin\$folderName\$RealConfiguration"
Copy-Item -Path "$sourceDir\*" -Destination $OpenSSHTestDir -Exclude ssh-agent.exe, sshd.exe -Force -ErrorAction Stop Copy-Item -Path "$sourceDir\unittest-*" -Destination $OpenSSHTestDir -Force -ErrorAction Stop
#restart the service to use the test copy of sshd_config
Restart-Service sshd
$sshdConfigFile = "$OpenSSHTestDir\sshd_config"
if (-not (Test-Path -Path $sshdConfigFile -PathType Leaf))
{
Write-BuildMessage "Installation dependencies: $OpenSSHTestDir\sshd_config is missing in the folder" -Category Error
throw "$OpenSSHTestDir\sshd_config is missing in the folder"
}
if ($env:DebugMode)
{
$strToReplace = "#LogLevel INFO"
(Get-Content $sshdConfigFile).Replace($strToReplace,"LogLevel Debug3") | Set-Content $sshdConfigFile
}
if(-not ($env:psPath))
{
$psCorePath = GetLocalPSCorePath
Set-BuildVariable -Name psPath -Value $psCorePath
}
$strToReplace = "Subsystem sftp sftp-server.exe"
if($env:psPath)
{
$strNewsubSystem = @"
Subsystem sftp sftp-server.exe
Subsystem powershell $env:psPath
"@
}
(Get-Content $sshdConfigFile).Replace($strToReplace, $strNewsubSystem) | Set-Content $sshdConfigFile
} }

View File

@ -152,17 +152,6 @@ function Start-SSHBootstrap
{ {
Write-BuildMsg -AsInfo -Message "Chocolatey not present. Installing chocolatey." -Silent:$silent Write-BuildMsg -AsInfo -Message "Chocolatey not present. Installing chocolatey." -Silent:$silent
Invoke-Expression ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1')) 2>&1 >> $script:BuildLogFile Invoke-Expression ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1')) 2>&1 >> $script:BuildLogFile
if (-not ($machinePath.ToLower().Contains($chocolateyPath.ToLower())))
{
Write-BuildMsg -AsVerbose -Message "Adding $chocolateyPath to Path environment variable" -Silent:$silent
$newMachineEnvironmentPath += ";$chocolateyPath"
$env:Path += ";$chocolateyPath"
}
else
{
Write-BuildMsg -AsVerbose -Message "$chocolateyPath already present in Path environment variable" -Silent:$silent
}
} }
# Add git\cmd to the path # Add git\cmd to the path
@ -231,7 +220,7 @@ function Start-SSHBootstrap
# Install Windows 8.1 SDK # Install Windows 8.1 SDK
$packageName = "windows-sdk-8.1" $packageName = "windows-sdk-8.1"
$sdkPath = "C:\Program Files (x86)\Windows Kits\8.1\bin\x86\register_app.vbs" $sdkPath = "${env:ProgramFiles(x86)}\Windows Kits\8.1\bin\x86\register_app.vbs"
if (-not (Test-Path -Path $sdkPath)) if (-not (Test-Path -Path $sdkPath))
{ {
@ -264,7 +253,7 @@ function Start-SSHBootstrap
Write-BuildMsg -AsVerbose -Message "vcPath: $script:vcPath" -Silent:$silent Write-BuildMsg -AsVerbose -Message "vcPath: $script:vcPath" -Silent:$silent
if ((Test-Path -Path "$script:vcPath\vcvarsall.bat") -eq $false) if ((Test-Path -Path "$script:vcPath\vcvarsall.bat") -eq $false)
{ {
Write-BuildMsg -AsError -ErrorAction Stop -Message "Could not find Visual Studio vcvarsall.bat at" + $script:vcPath Write-BuildMsg -AsError -ErrorAction Stop -Message "Could not find Visual Studio vcvarsall.bat at$script:vcPath, which means some required develop kits are missing on the machine."
} }
} }

View File

@ -202,6 +202,7 @@
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\ssh-pubkey\ssh-pubkeydefs.h" /> <ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\ssh-pubkey\ssh-pubkeydefs.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\agent-request.h" /> <ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\agent-request.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\agent.h" /> <ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\agent.h" />
<ClInclude Include="$(OpenSSH-Src-Path)groupaccess.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="$(OpenSSH-Src-Path)\auth.c" /> <ClCompile Include="$(OpenSSH-Src-Path)\auth.c" />
@ -212,8 +213,9 @@
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\agentconfig.c" /> <ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\agentconfig.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\connection.c" /> <ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\connection.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\keyagent-request.c" /> <ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\keyagent-request.c" />
<ClCompile Include="..\..\..\auth-options.c" /> <ClCompile Include="$(OpenSSH-Src-Path)auth-options.c" />
<ClCompile Include="..\..\..\auth2-pubkey.c" /> <ClCompile Include="$(OpenSSH-Src-Path)auth2-pubkey.c" />
<ClCompile Include="$(OpenSSH-Src-Path)groupaccess.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="version.rc" /> <ResourceCompile Include="version.rc" />

View File

@ -248,6 +248,7 @@
<ClCompile Include="$(OpenSSH-Src-Path)sshlogin.c" /> <ClCompile Include="$(OpenSSH-Src-Path)sshlogin.c" />
<ClCompile Include="$(OpenSSH-Src-Path)sshpty.c" /> <ClCompile Include="$(OpenSSH-Src-Path)sshpty.c" />
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\wmain_sshd.c" /> <ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\wmain_sshd.c" />
<ClCompile Include="$(OpenSSH-Src-Path)groupaccess.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="version.rc" /> <ResourceCompile Include="version.rc" />

View File

@ -150,6 +150,9 @@
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\wmain_sshd.c"> <ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\wmain_sshd.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="$(OpenSSH-Src-Path)groupaccess.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="version.rc"> <ResourceCompile Include="version.rc">

View File

@ -14,7 +14,6 @@
struct passwd { struct passwd {
char *pw_name; /* user's login name */ char *pw_name; /* user's login name */
char *pw_domain; /* user's domain name */
char *pw_passwd; /* password? */ char *pw_passwd; /* password? */
char *pw_gecos; /* ??? */ char *pw_gecos; /* ??? */
uid_t pw_uid; /* numerical user ID */ uid_t pw_uid; /* numerical user ID */

View File

@ -90,31 +90,6 @@ innetgr(const char *netgroup, const char *host, const char *user, const char *do
return -1; return -1;
} }
/* groupaccess.c*/
int
ga_init(const char *user, gid_t base)
{
return -1;
}
int
ga_match(char * const *groups, int n)
{
return -1;
}
int
ga_match_pattern_list(const char *group_pattern)
{
return -1;
}
void
ga_free(void)
{
return;
}
int int
chroot(const char *path) chroot(const char *path)
{ {

View File

@ -82,13 +82,10 @@ reset_pw()
free(pw.pw_name); free(pw.pw_name);
if (pw.pw_dir) if (pw.pw_dir)
free(pw.pw_dir); free(pw.pw_dir);
if (pw.pw_domain)
free(pw.pw_domain);
if (pw.pw_sid) if (pw.pw_sid)
free(pw.pw_sid); free(pw.pw_sid);
pw.pw_name = NULL; pw.pw_name = NULL;
pw.pw_dir = NULL; pw.pw_dir = NULL;
pw.pw_domain = NULL;
pw.pw_sid = NULL; pw.pw_sid = NULL;
} }
@ -97,14 +94,14 @@ get_passwd(const char *user_utf8, LPWSTR user_sid)
{ {
struct passwd *ret = NULL; struct passwd *ret = NULL;
wchar_t *user_utf16 = NULL, *uname_utf16, *udom_utf16, *tmp; wchar_t *user_utf16 = NULL, *uname_utf16, *udom_utf16, *tmp;
char *uname_utf8 = NULL, *udom_utf8 = NULL, *pw_home_utf8 = NULL, *user_sid_utf8 = NULL; char *uname_utf8 = NULL, *uname_upn = NULL, *udom_utf8 = NULL, *pw_home_utf8 = NULL, *user_sid_utf8 = NULL;
LPBYTE user_info = NULL; LPBYTE user_info = NULL;
LPWSTR user_sid_local = NULL; LPWSTR user_sid_local = NULL;
wchar_t reg_path[PATH_MAX], profile_home[PATH_MAX]; wchar_t reg_path[PATH_MAX], profile_home[PATH_MAX];
HKEY reg_key = 0; HKEY reg_key = 0;
int tmp_len = PATH_MAX; int tmp_len = PATH_MAX;
PDOMAIN_CONTROLLER_INFOW pdc = NULL; PDOMAIN_CONTROLLER_INFOW pdc = NULL;
DWORD dsStatus; DWORD dsStatus, uname_upn_len = 0;;
errno = 0; errno = 0;
reset_pw(); reset_pw();
@ -169,10 +166,23 @@ get_passwd(const char *user_utf8, LPWSTR user_sid)
goto done; goto done;
} }
pw.pw_name = uname_utf8; uname_upn_len = strlen(uname_utf8) + 1;
uname_utf8 = NULL; if (udom_utf8)
pw.pw_domain = udom_utf8; uname_upn_len += strlen(udom_utf8) + 1;
udom_utf8 = NULL;
if ((uname_upn = malloc(uname_upn_len)) == NULL) {
errno = ENOMEM;
goto done;
}
memcpy(uname_upn, uname_utf8, strlen(uname_utf8) + 1);
if (udom_utf8) {
/* TODO - get domain FQDN */
uname_upn[strlen(uname_utf8)] = '@';
memcpy(uname_upn + strlen(uname_utf8) + 1, udom_utf8, strlen(udom_utf8) + 1);
}
pw.pw_name = uname_upn;
uname_upn = NULL;
pw.pw_dir = pw_home_utf8; pw.pw_dir = pw_home_utf8;
pw_home_utf8 = NULL; pw_home_utf8 = NULL;
pw.pw_sid = user_sid_utf8; pw.pw_sid = user_sid_utf8;
@ -184,6 +194,8 @@ done:
free(user_utf16); free(user_utf16);
if (uname_utf8) if (uname_utf8)
free(uname_utf8); free(uname_utf8);
if (uname_upn)
free(uname_upn);
if (udom_utf8) if (udom_utf8)
free(udom_utf8); free(udom_utf8);
if (pw_home_utf8) if (pw_home_utf8)

View File

@ -38,6 +38,7 @@
#include "agent.h" #include "agent.h"
#include "agent-request.h" #include "agent-request.h"
#include "key.h" #include "key.h"
#include "inc\utf.h"
static void static void
InitLsaString(LSA_STRING *lsa_string, const char *str) InitLsaString(LSA_STRING *lsa_string, const char *str)
@ -105,7 +106,7 @@ LoadProfile(struct agent_connection* con, wchar_t* user, wchar_t* domain) {
#define MAX_PW_LEN 64 #define MAX_PW_LEN 64
static HANDLE static HANDLE
generate_user_token(wchar_t* user, wchar_t* domain) { generate_user_token(wchar_t* user_cpn) {
HANDLE lsa_handle = 0, token = 0; HANDLE lsa_handle = 0, token = 0;
LSA_OPERATIONAL_MODE mode; LSA_OPERATIONAL_MODE mode;
ULONG auth_package_id; ULONG auth_package_id;
@ -120,7 +121,7 @@ generate_user_token(wchar_t* user, wchar_t* domain) {
DWORD cbProfile; DWORD cbProfile;
BOOL domain_user; BOOL domain_user;
domain_user = (*domain != L'\0') ? TRUE : FALSE; domain_user = wcschr(user_cpn, L'@')? TRUE : FALSE;
InitLsaString(&logon_process_name, "ssh-agent"); InitLsaString(&logon_process_name, "ssh-agent");
if (domain_user) if (domain_user)
@ -137,10 +138,6 @@ generate_user_token(wchar_t* user, wchar_t* domain) {
if (domain_user) { if (domain_user) {
KERB_S4U_LOGON *s4u_logon; KERB_S4U_LOGON *s4u_logon;
wchar_t user_cpn[MAX_USER_LEN + 1 + MAX_FQDN_LEN + 1];
memcpy(user_cpn, user, wcslen(user) * 2);
user_cpn[wcslen(user)] = L'@';
memcpy(user_cpn + wcslen(user) + 1, domain, wcslen(domain) * 2 + 2);
logon_info_size = sizeof(KERB_S4U_LOGON); logon_info_size = sizeof(KERB_S4U_LOGON);
logon_info_size += (wcslen(user_cpn) * 2 + 2); logon_info_size += (wcslen(user_cpn) * 2 + 2);
logon_info = malloc(logon_info_size); logon_info = malloc(logon_info_size);
@ -157,11 +154,11 @@ generate_user_token(wchar_t* user, wchar_t* domain) {
s4u_logon->ClientRealm.MaximumLength = 0; s4u_logon->ClientRealm.MaximumLength = 0;
s4u_logon->ClientRealm.Buffer = 0; s4u_logon->ClientRealm.Buffer = 0;
} else { } else {
logon_info_size = (wcslen(user) + 1)*sizeof(wchar_t); logon_info_size = (wcslen(user_cpn) + 1)*sizeof(wchar_t);
logon_info = malloc(logon_info_size); logon_info = malloc(logon_info_size);
if (logon_info == NULL) if (logon_info == NULL)
goto done; goto done;
memcpy(logon_info, user, logon_info_size); memcpy(logon_info, user_cpn, logon_info_size);
} }
memcpy(sourceContext.SourceName,"sshagent", sizeof(sourceContext.SourceName)); memcpy(sourceContext.SourceName,"sshagent", sizeof(sourceContext.SourceName));
@ -201,36 +198,35 @@ done:
/* TODO - SecureZeroMemory password */ /* TODO - SecureZeroMemory password */
int process_passwordauth_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { int process_passwordauth_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) {
char *user = NULL, *domain = NULL, *pwd = NULL; char *user = NULL, *domain = NULL, *pwd = NULL;
wchar_t userW_buf[MAX_USER_LEN], domainW_buf[MAX_FQDN_LEN], pwdW_buf[MAX_PW_LEN]; size_t user_len, pwd_len;
wchar_t *userW = userW_buf, *domW = domainW_buf, *pwdW = pwdW_buf, *tmp; wchar_t *user_utf16 = NULL, *udom_utf16 = NULL, *pwd_utf16 = NULL, *tmp;
size_t user_len = 0, domain_len = 0, pwd_len = 0, dom_len = 0;
int r = -1; int r = -1;
HANDLE token = 0, dup_token, client_proc = 0; HANDLE token = 0, dup_token, client_proc = 0;
ULONG client_pid; ULONG client_pid;
if (sshbuf_get_cstring(request, &user, &user_len) != 0 || if (sshbuf_get_cstring(request, &user, &user_len) != 0 ||
sshbuf_get_cstring(request, &domain, &domain_len) != 0 ||
sshbuf_get_cstring(request, &pwd, &pwd_len) != 0 || sshbuf_get_cstring(request, &pwd, &pwd_len) != 0 ||
user_len == 0 || user_len == 0 ||
pwd_len == 0 || pwd_len == 0 ||
user_len > MAX_USER_LEN || user_len > MAX_USER_LEN + MAX_FQDN_LEN ||
domain_len > MAX_FQDN_LEN ||
pwd_len > MAX_PW_LEN) { pwd_len > MAX_PW_LEN) {
debug("bad password auth request"); debug("bad password auth request");
goto done; goto done;
} }
userW[0] = L'\0'; if ((user_utf16 = utf8_to_utf16(user)) == NULL ||
domW[0] = L'\0'; (pwd_utf16 = utf8_to_utf16(pwd)) == NULL) {
if ((MultiByteToWideChar(CP_UTF8, 0, user, user_len + 1, userW, MAX_USER_LEN) == 0) || debug("out of memory");
(domain_len != 0 && MultiByteToWideChar(CP_UTF8, 0, domain, domain_len + 1, domW, MAX_FQDN_LEN) == 0) ||
(MultiByteToWideChar(CP_UTF8, 0, pwd, pwd_len + 1, pwdW, MAX_PW_LEN) == 0)) {
debug("unable to convert user (%s) or password to UTF-16", user);
goto done; goto done;
} }
if (LogonUserW(userW, domW, pwdW, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &token) == FALSE) { if ((tmp = wcschr(user_utf16, L'@') ) != NULL ) {
debug("failed to logon user"); udom_utf16 = tmp + 1;
*tmp = L'\0';
}
if (LogonUserW(user_utf16, udom_utf16, pwd_utf16, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &token) == FALSE) {
debug("failed to logon user: %ls domain: %ls", user_utf16, udom_utf16);
goto done; goto done;
} }
@ -243,7 +239,7 @@ int process_passwordauth_request(struct sshbuf* request, struct sshbuf* response
} }
con->auth_token = token; con->auth_token = token;
LoadProfile(con, userW, domW); LoadProfile(con, user_utf16, udom_utf16);
r = 0; r = 0;
done: done:
/* TODO Fix this hacky protocol*/ /* TODO Fix this hacky protocol*/
@ -254,6 +250,10 @@ done:
free(user); free(user);
if (pwd) if (pwd)
free(pwd); free(pwd);
if (user_utf16)
free(user_utf16);
if (pwd_utf16)
free(pwd_utf16);
if (client_proc) if (client_proc)
CloseHandle(client_proc); CloseHandle(client_proc);
@ -262,22 +262,19 @@ done:
int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) {
int r = -1; int r = -1;
char *key_blob, *user, *domain, *usernameWithDomain, *sig, *blob; char *key_blob, *user, *sig, *blob;
size_t key_blob_len, user_len, domain_len, sig_len, blob_len; size_t key_blob_len, user_len, sig_len, blob_len;
struct sshkey *key = NULL; struct sshkey *key = NULL;
HANDLE token = NULL, restricted_token = NULL, dup_token = NULL, client_proc = NULL; HANDLE token = NULL, restricted_token = NULL, dup_token = NULL, client_proc = NULL;
wchar_t wuser[MAX_USER_LEN], wdomain[MAX_FQDN_LEN]; wchar_t *user_utf16 = NULL, *udom_utf16 = NULL, *tmp;
PWSTR wuser_home = NULL; PWSTR wuser_home = NULL;
ULONG client_pid; ULONG client_pid;
LUID_AND_ATTRIBUTES priv_to_delete[1]; LUID_AND_ATTRIBUTES priv_to_delete[1];
user = NULL; user = NULL;
domain = NULL;
if (sshbuf_get_string_direct(request, &key_blob, &key_blob_len) != 0 || if (sshbuf_get_string_direct(request, &key_blob, &key_blob_len) != 0 ||
sshbuf_get_cstring(request, &user, &user_len) != 0 || sshbuf_get_cstring(request, &user, &user_len) != 0 ||
sshbuf_get_cstring(request, &domain, &domain_len) != 0 ||
user_len > MAX_USER_LEN || user_len > MAX_USER_LEN ||
domain_len > MAX_FQDN_LEN ||
sshbuf_get_string_direct(request, &sig, &sig_len) != 0 || sshbuf_get_string_direct(request, &sig, &sig_len) != 0 ||
sshbuf_get_string_direct(request, &blob, &blob_len) != 0 || sshbuf_get_string_direct(request, &blob, &blob_len) != 0 ||
sshkey_from_blob(key_blob, key_blob_len, &key) != 0) { sshkey_from_blob(key_blob, key_blob_len, &key) != 0) {
@ -285,12 +282,13 @@ int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response,
goto done; goto done;
} }
wuser[0] = L'\0'; if ((user_utf16 = utf8_to_utf16(user)) == NULL) {
wdomain[0] = L'\0'; debug("out of memory");
if (MultiByteToWideChar(CP_UTF8, 0, user, user_len + 1, wuser, MAX_USER_LEN) == 0 || goto done;
(domain_len != 0 && MultiByteToWideChar(CP_UTF8, 0, domain, domain_len + 1, wdomain, MAX_FQDN_LEN) == 0) || }
(token = generate_user_token(wuser, wdomain)) == 0) {
debug("unable to generate token for user %ls", wuser); if ((token = generate_user_token(user_utf16)) == 0) {
debug("unable to generate token for user %ls", user_utf16);
goto done; goto done;
} }
@ -302,8 +300,8 @@ int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response,
} }
if (SHGetKnownFolderPath(&FOLDERID_Profile, 0, restricted_token, &wuser_home) != S_OK || if (SHGetKnownFolderPath(&FOLDERID_Profile, 0, restricted_token, &wuser_home) != S_OK ||
pubkey_allowed(key, wuser, wuser_home) != 1) { pubkey_allowed(key, user_utf16, wuser_home) != 1) {
debug("unable to verify public key for user %ls (profile:%ls)", wuser, wuser_home); debug("unable to verify public key for user %ls (profile:%ls)", user_utf16, wuser_home);
goto done; goto done;
} }
@ -322,7 +320,11 @@ int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response,
con->auth_token = restricted_token; con->auth_token = restricted_token;
restricted_token = NULL; restricted_token = NULL;
LoadProfile(con, wuser, wdomain); if ((tmp = wcschr(user_utf16, L'@')) != NULL) {
udom_utf16 = tmp + 1;
*tmp = L'\0';
}
LoadProfile(con, user_utf16, udom_utf16);
r = 0; r = 0;
done: done:
@ -332,6 +334,8 @@ done:
if (user) if (user)
free(user); free(user);
if (user_utf16)
free(user_utf16);
if (key) if (key)
sshkey_free(key); sshkey_free(key);
if (wuser_home) if (wuser_home)

View File

@ -33,6 +33,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <limits.h> #include <limits.h>
#ifdef WINDOWS
#include <lm.h>
#endif
#include "xmalloc.h" #include "xmalloc.h"
#include "groupaccess.h" #include "groupaccess.h"
@ -49,6 +52,82 @@ static char **groups_byname;
int int
ga_init(const char *user, gid_t base) ga_init(const char *user, gid_t base)
{ {
#ifdef WINDOWS
DWORD i = 0, j = 0;
LPLOCALGROUP_USERS_INFO_0 local_groups_info = NULL, tmp_groups_info;
wchar_t *user_utf16 = NULL, *full_name_utf16 = NULL, *udom_utf16 = NULL, *tmp;
char *group_utf8 = NULL;
DWORD entries_read = 0, total_entries = 0, full_name_len = 0, index = 0;
NET_API_STATUS nStatus;
if (ngroups > 0)
ga_free();
user_utf16 = utf8_to_utf16(user);
if ((user_utf16 = utf8_to_utf16(user)) == NULL) {
errno = ENOMEM;
goto done;
}
full_name_len = wcslen(user_utf16) + 1;
if ((full_name_utf16 = malloc(full_name_len * sizeof(wchar_t))) == NULL) {
errno = ENOMEM;
goto done;
}
if ((tmp = wcschr(user_utf16, L'@')) != NULL) {
udom_utf16 = tmp + 1;
*tmp = L'\0';
index = wcslen(udom_utf16) + 1;
wmemcpy(full_name_utf16, udom_utf16, index);
full_name_utf16[wcslen(udom_utf16)] = L'\\';
}
wmemcpy(full_name_utf16 + index, user_utf16, wcslen(user_utf16) + 1);
nStatus = NetUserGetLocalGroups(NULL,
full_name_utf16,
0,
LG_INCLUDE_INDIRECT,
(LPBYTE *)&local_groups_info,
MAX_PREFERRED_LENGTH,
&entries_read,
&total_entries);
if (NERR_Success != nStatus) {
error("NetUserGetLocalGroups() failed with error: %u",
nStatus);
errno = ENOENT;
goto done;
}
if (entries_read != total_entries) {
error("NetUserGetLocalGroups(): entries_read (%u) is not equal to "
"total_entries (%u) for user %.100s", entries_read, total_entries, user);
errno = ENOENT;
goto done;
}
if ((tmp_groups_info = local_groups_info) != NULL) {
groups_byname = xcalloc(entries_read, sizeof(*groups_byname));
for (i = 0, j = 0; i < total_entries; i++)
{
if ((group_utf8 = utf16_to_utf8(tmp_groups_info->lgrui0_name)) == NULL) {
errno = ENOMEM;
goto done;
}
groups_byname[j++] = group_utf8;
tmp_groups_info++;
}
}
done:
if(user_utf16 != NULL)
free(user_utf16);
if(full_name_utf16 != NULL)
free(full_name_utf16);
if (local_groups_info != NULL)
NetApiBufferFree(local_groups_info);
#else /* !WINDOWS */
gid_t *groups_bygid; gid_t *groups_bygid;
int i, j; int i, j;
struct group *gr; struct group *gr;
@ -70,7 +149,9 @@ ga_init(const char *user, gid_t base)
if ((gr = getgrgid(groups_bygid[i])) != NULL) if ((gr = getgrgid(groups_bygid[i])) != NULL)
groups_byname[j++] = xstrdup(gr->gr_name); groups_byname[j++] = xstrdup(gr->gr_name);
free(groups_bygid); free(groups_bygid);
#endif /* !WINDOWS */
return (ngroups = j); return (ngroups = j);
} }
/* /*

5
misc.c
View File

@ -226,11 +226,6 @@ pwcopy(struct passwd *pw)
copy->pw_dir = xstrdup(pw->pw_dir); copy->pw_dir = xstrdup(pw->pw_dir);
copy->pw_shell = xstrdup(pw->pw_shell); copy->pw_shell = xstrdup(pw->pw_shell);
#ifdef WINDOWS #ifdef WINDOWS
/* copy additionaly pw entries for Windows */
if (pw->pw_domain != NULL)
copy->pw_domain = xstrdup(pw->pw_domain);
else
copy->pw_domain = NULL;
copy->pw_sid = xstrdup(pw->pw_sid); copy->pw_sid = xstrdup(pw->pw_sid);
#endif /* WINDOWS */ #endif /* WINDOWS */

View File

@ -75,9 +75,11 @@ Class Machine
[string] $localAdminUserName = "localadmin" [string] $localAdminUserName = "localadmin"
[string] $localAdminPassword = "Bull_dog1" [string] $localAdminPassword = "Bull_dog1"
[string] $localAdminAuthorizedKeyPath [string] $localAdminAuthorizedKeyPath
[string] $sshdConfigFile = (join-path $PSScriptRoot "sshd_config")
[string] $backupFileName = (join-path $PSScriptRoot "sshd_backup")
[System.Security.SecureString] $password [System.Security.SecureString] $password
$preLatfpSetting $preLatfpSetting
$localUserprofilePath [string] $localUserprofilePath
#Members on client role #Members on client role
[string []] $clientPrivateKeyPaths [string []] $clientPrivateKeyPaths
@ -177,6 +179,47 @@ Class Machine
} }
} }
[void] SetupSingleSignon([string] $identifyFile) {
.\ssh-add.exe $identifyFile
}
[void] CleanupSingleSignon([string] $identifyFile) {
.\ssh-add.exe -d $identifyFile
}
[void] AddItemInSSHDConfig([string] $key, [string] $value) {
if ( $this.Platform -eq [PlatformType]::Windows ) {
Get-Content $this.sshdConfigFile | % {
if($_.StartsWith($key)) {
"#$_"
}
else {$_}
} | Set-Content $this.sshdConfigFile
Add-Content -Path $this.sshdConfigFile -Value "`r`n$key $value"
Restart-Service sshd
} else {
}
}
[void] BackupSSHDConfig() {
if ( $this.Platform -eq [PlatformType]::Windows ) {
if(Test-path $this.backupFileName) {
Remove-Item -Path $this.backupFileName -Force
}
Copy-Item $this.sshdConfigFile $this.backupFileName -Force
}
}
[void] RestoreSSHDConfig() {
if ( $this.Platform -eq [PlatformType]::Windows ) {
Copy-Item $this.backupFileName $this.sshdConfigFile -Force
Remove-Item -Path $this.backupFileName -Force
Restart-Service sshd
}
}
[void] SetupServerRemoting([Protocol] $protocol) { [void] SetupServerRemoting([Protocol] $protocol) {
if ($this.Platform -eq [PlatformType]::Windows) if ($this.Platform -eq [PlatformType]::Windows)
{ {
@ -240,6 +283,56 @@ Class Machine
} }
} }
[void] AddLocalUser($UserName, $password) {
$a = Get-LocalUser -Name $UserName -ErrorAction Ignore
if ($a -eq $null)
{
$pass = ConvertTo-SecureString -String $this.localAdminPassword -AsPlainText -Force
$a = New-LocalUser -Name $UserName -Password $pass -AccountNeverExpires -PasswordNeverExpires -UserMayNotChangePassword
}
}
[void] AddLocalGroup($groupName) {
$g = Get-LocalGroup -Name $groupName -ErrorAction Ignore
if ($g -eq $null)
{
$g = New-LocalGroup -Name $groupName
}
}
[void] AddUserToLocalGroup($UserName, $password, $groupName) {
if ( $this.Platform -eq [PlatformType]::Windows ) {
$this.AddLocalUser($UserName, $password)
$this.AddLocalGroup($groupName)
if((Get-LocalGroupMember -Name $groupName -Member $UserName -ErrorAction Ignore ) -eq $null)
{
Add-LocalGroupMember -Name $groupName -Member $UserName
}
} else {
#Todo add local user and add it to a user group on linux
}
}
[void] RemoveUserFromLocalGroup($UserName, $groupName) {
if ( $this.Platform -eq [PlatformType]::Windows ) {
if((Get-LocalGroupMember -Name $groupName -Member $UserName -ErrorAction Ignore ) -eq $null)
{
Remove-LocalGroupMember -Name $groupName -Member $UserName
}
} else {
#Todo add local user and add it to a user group on linux
}
}
[void] ClenaupLocalGroup($groupName) {
$g = Get-LocalGroup -Name $groupName -ErrorAction Ignore
if ($g -eq $null)
{
$g | Remove-LocalGroup
}
}
[void] AddAdminUser($UserName, $password) { [void] AddAdminUser($UserName, $password) {
if ( $this.Platform -eq [PlatformType]::Windows ) { if ( $this.Platform -eq [PlatformType]::Windows ) {
$a = Get-LocalUser -Name $UserName -ErrorAction Ignore $a = Get-LocalUser -Name $UserName -ErrorAction Ignore
@ -253,8 +346,7 @@ Class Machine
Add-LocalGroupMember -SID s-1-5-32-544 -Member $a Add-LocalGroupMember -SID s-1-5-32-544 -Member $a
} }
} else { } else {
#Todo add local user and add it to administrators group on linux #Todo add local user and add it to a administrators on linux
#Todo: get $localUserprofilePath
} }
} }

View File

@ -0,0 +1,161 @@
using module .\PlatformAbstractLayer.psm1
Describe "Tests of sshd_config" -Tags "CI" {
BeforeAll {
$fileName = "test.txt"
$filePath = Join-Path ${TestDrive} $fileName
[Machine] $client = [Machine]::new([MachineRole]::Client)
[Machine] $server = [Machine]::new([MachineRole]::Server)
$client.SetupClient($server)
$server.SetupServer($client)
}
AfterAll {
$client.CleanupClient()
$server.CleanupServer()
}
<#
Settings in the sshd_config:
DenyUsers denyuser1 denyu*2 denyuse?3,
AllowUsers allowuser1 allowu*r2 allow?se?3 allowuser4 localuser1 localu*r2 loc?lu?er3 localadmin
DenyGroups denygroup1 denygr*p2 deny?rou?3
AllowGroups allowgroup1 allowg*2 allowg?ou?3 Adm*
#>
Context "Tests of AllowGroups, AllowUsers, DenyUsers, DenyGroups" {
BeforeAll {
Remove-Item -Path $filePath -Force -ea silentlycontinue
$password = "Bull_dog1"
$allowUser1 = "allowuser1"
$allowUser2 = "allowuser2"
$allowUser3 = "allowuser3"
$allowUser4 = "allowuser4"
$denyUser1 = "denyuser1"
$denyUser2 = "denyuser2"
$denyUser3 = "denyuser3"
$localuser1 = "localuser1"
$localuser2 = "localuser2"
$localuser3 = "localuser3"
$allowGroup1 = "allowgroup1"
$allowGroup2 = "allowgroup2"
$allowGroup3 = "allowgroup3"
$denyGroup1 = "denygroup1"
$denyGroup2 = "denygroup2"
$denyGroup3 = "denygroup3"
$client.AddPasswordSetting($password)
}
AfterEach {
Remove-Item -Path $filePath -Force -ea ignore
}
AfterAll {
$client.CleanupPasswordSetting()
}
It 'User with full name in the list of AllowUsers' {
$server.AddUserToLocalGroup($allowUser1, $password, $allowGroup1)
$client.RunCmd(".\ssh $($allowUser1)@$($server.MachineName) hostname > $filePath")
Get-Content $filePath | Should be $server.MachineName
$server.RemoveUserFromLocalGroup($allowUser1, $allowGroup1)
}
It 'User with * wildcard' {
$server.AddUserToLocalGroup($allowUser2, $password, $allowGroup1)
$client.RunCmd(".\ssh $($allowUser2)@$($server.MachineName) hostname > $filePath")
$LASTEXITCODE | Should Be 0
Get-Content $filePath | Should be $server.MachineName
$server.RemoveUserFromLocalGroup($allowUser2, $allowGroup1)
}
It 'User with ? wildcard' {
$server.AddUserToLocalGroup($allowUser3, $password, $allowGroup1)
$client.RunCmd(".\ssh $($allowUser3)@$($server.MachineName) hostname > $filePath")
$LASTEXITCODE | Should Be 0
Get-Content $filePath | Should be $server.MachineName
$server.RemoveUserFromLocalGroup($allowUser3, $allowGroup1)
}
It 'User with full name in the list of AllowUsers but not in any AllowGroups' {
$server.AddLocalUser($allowUser4, $password)
$client.RunCmd(".\ssh $($allowUser4)@$($server.MachineName) hostname > $filePath")
$LASTEXITCODE | Should Not Be 0
Get-Content $filePath | Should BeNullOrEmpty
}
It 'User with full name in the list of DenyUsers' {
$server.AddUserToLocalGroup($denyUser1, $password, $allowGroup1)
$client.RunCmd(".\ssh $($denyUser1)@$($server.MachineName) hostname > $filePath")
$LASTEXITCODE | Should Not Be 0
Get-Content $filePath | Should BeNullOrEmpty
$server.RemoveUserFromLocalGroup($denyUser1, $allowGroup1)
}
It 'User with * wildcard in the list of DenyUsers' {
$server.AddUserToLocalGroup($denyUser2, $password, $allowGroup1)
$str = ".\ssh $($denyUser2)@$($server.MachineName) hostname > $filePath"
$client.RunCmd(".\ssh $($denyUser2)@$($server.MachineName) hostname > $filePath")
$LASTEXITCODE | Should Not Be 0
Get-Content $filePath | Should BeNullOrEmpty
$server.RemoveUserFromLocalGroup($denyUser2, $allowGroup1)
}
It 'User with ? wildcard in the list of DenyUsers' {
$server.AddUserToLocalGroup($denyUser3, $password, $allowGroup1)
$client.RunCmd(".\ssh $($denyUser3)@$($server.MachineName) hostname > $filePath")
$LASTEXITCODE | Should Not Be 0
Get-Content $filePath | Should BeNullOrEmpty
$server.RemoveUserFromLocalGroup($denyUser3, $allowGroup1)
}
It 'User is listed in the list of AllowUsers but also in a full name DenyGroups and AllowGroups' {
$server.AddUserToLocalGroup($localuser1, $password, $allowGroup1)
$server.AddUserToLocalGroup($localuser1, $password, $denyGroup1)
$client.RunCmd(".\ssh $($localuser1)@$($server.MachineName) hostname > $filePath")
$LASTEXITCODE | Should Not Be 0
Get-Content $filePath | Should BeNullOrEmpty
$server.RemoveUserFromLocalGroup($localuser1, $allowGroup1)
$server.RemoveUserFromLocalGroup($localuser1, $denyGroup1)
}
It 'User is listed in the list of AllowUsers but also in a wildcard * DenyGroups' {
$server.AddUserToLocalGroup($localuser2, $password, $denyGroup2)
$client.RunCmd(".\ssh $($localuser2)@$($server.MachineName) hostname > $filePath")
$LASTEXITCODE | Should Not Be 0
Get-Content $filePath | Should BeNullOrEmpty
$server.RemoveUserFromLocalGroup($localuser2, $denyGroup2)
}
It 'User is listed in the list of AllowUsers but also in a wildcard ? DenyGroups' {
$server.AddUserToLocalGroup($localuser3, $password, $denyGroup3)
$client.RunCmd(".\ssh $($localuser3)@$($server.MachineName) hostname > $filePath")
$LASTEXITCODE | Should Not Be 0
Get-Content $filePath | Should BeNullOrEmpty
$server.RemoveUserFromLocalGroup($localuser3, $denyGroup3)
}
}
}

View File

@ -0,0 +1,127 @@
# $OpenBSD: sshd_config,v 1.84 2011/05/23 03:30:07 djm Exp $
# This is the sshd server system-wide configuration file. See
# sshd_config(5) for more information.
# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin
# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented. Uncommented options override the
# default value.
#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::
# The default requires explicit activation of protocol 1
#Protocol 2
# HostKey for protocol version 1
#HostKey /etc/ssh/ssh_host_key
# HostKeys for protocol version 2
#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_dsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
# Lifetime and size of ephemeral version 1 server key
#KeyRegenerationInterval 1h
#ServerKeyBits 1024
# Logging
# obsoletes QuietMode and FascistLogging
#SyslogFacility AUTH
LogLevel DEBUG3
# Authentication:
#LoginGraceTime 2m
#PermitRootLogin yes
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10
#RSAAuthentication yes
#PubkeyAuthentication yes
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
AuthorizedKeysFile .ssh/authorized_keys
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#RhostsRSAAuthentication no
# similar for protocol version 2
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# RhostsRSAAuthentication and HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes
# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no
# Change to no to disable s/key passwords
#ChallengeResponseAuthentication yes
# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no
# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes
# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication. Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
#UsePAM no
#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
#X11Forwarding no
#X11DisplayOffset 10
#X11UseLocalhost yes
#PrintMotd yes
#PrintLastLog yes
#TCPKeepAlive yes
#UseLogin no
#UsePrivilegeSeparation yes
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#UseDNS yes
#PidFile /var/run/sshd.pid
#MaxStartups 10
#PermitTunnel no
#ChrootDirectory none
# no default banner path
#Banner none
# override default of no subsystems
Subsystem sftp sftp-server.exe
# Example of overriding settings on a per-user basis
#Match User anoncvs
# X11Forwarding no
# AllowTcpForwarding no
# ForceCommand cvs server
PubkeyAcceptedKeyTypes ssh-ed25519*
DenyUsers denyuser1 deny*2 denyuse?3,
AllowUsers allowuser1 allowu*r2 allow?se?3 allowuser4 localuser1 localu*r2 loc?lu?er3 localadmin
DenyGroups denygroup1 denygr*p2 deny?rou?3
AllowGroups allowgroup1 allowg*2 allowg?ou?3 Adm*