Как определить, является ли строка учетной записью NT или идентификатором безопасности? - PullRequest
0 голосов
/ 26 апреля 2020

Вопрос

Как я могу достоверно сказать, представляет ли данная строка NTAccount или SecurityIdentifier?

Подробно

Учитывая строку, которую я могу преобразовать это к NTAccount или SecurityIdentifier через конструктор:

[string]$myAccount = '...' # some value
$ntAccount = [System.Security.Principal.NTAccount]::new($myAccount)
$sid = [System.Security.Principal.SecurityPrincipal]::new($myAccount) // throws exception if not a valid SID

Если строка не является SID, конструктор SecurityIdentifier выдаст исключение. Если строка является SID, конструктор NTAccount примет ее ... однако, когда я попытаюсь преобразовать ее в SID, будет выдано исключение System.Security.Principal.IdentityNotMapped.

$sidFromNT = $ntAccount.Translate([System.Security.Principal.SecurityPrincipal]) # throw exception
$ntFromSid = $sid.Translate([System.Security.Principal.NTAccount]) # should work as if the SID were invalid we'd have already erred

Было бы здорово, если Я мог бы сказать, что не знаю тип, используя общий базовый класс; но это абстрактно / не имеет конструктора publi c; поэтому я не могу сделать это:

[string]$account = '...'
$idRef = [System.Security.Principal.IdentityReference]::new($account) # this is not valid code
$ntFromId = $idRef.Translate([System.Security.Principal.NTAccount])
$sidFromId = $idRef.Translate([System.Security.Principal.SecurityIdentifier])

Таким образом, единственные варианты, о которых я могу думать, это варианты:

  • Используйте ошибку * для определения типа; но это делает ошибку частью моего обычного потока.
  • Проверьте, начинается ли значение S-; очень быстрый и должен работать в большинстве случаев; но это взлом.

* Примечание: я понимаю, что недопустимое значение (то есть ни учетная запись NT, ни SID) также не исключаются; Я проигнорировал этот сценарий, чтобы оставаться кратким.

-

C# Версия

Так как это вопрос. net, а не спецификация PowerShell c, вот код, иллюстрирующий ту же проблему в C# (к сожалению, я не могу изменить это, поскольку различные сайты Fiddle ограничивают требуемую функциональность).

public static void Main()
{
    var ids = new string[] {"S-1-1-0", "Everyone"}; // a list of values which may be SIDs or NTAccounts
    var pseudoRandomIndex = DateTime.Now.Millisecond % ids.Length; // saves initialising Random for a simple demo
    var idString = ids[pseudoRandomIndex]; // pick one id at random; be it a SID or an NT Account

    Debug.WriteLine($"Selected value is {idString}");

    TryToProcessIdentityReference<NTAccount, SecurityIdentifier>(idString, (id) => new NTAccount(id));
    TryToProcessIdentityReference<SecurityIdentifier, NTAccount>(idString, (id) => new SecurityIdentifier(id));
}

static void TryToProcessIdentityReference<T1, T2>(string idString, Func<string, T1> callConstructor)
    where T1 : IdentityReference
    where T2 : IdentityReference
{
    var t1Type = typeof(T1);
    var t2Type = typeof(T2);
    Console.WriteLine($"Trying to process {idString} as a {t1Type.Name} ...");
    try 
    {
        var t1 = callConstructor(idString);
        _ = t1.Translate(t2Type); 
        Debug.WriteLine($" - {idString} is a valid {t1Type.Name}!");
    } catch (Exception e) when(e is ArgumentException || e is IdentityNotMappedException) { 
        Debug.WriteLine($" - Failed to process {idString} as {t1Type.Name}; error thrown when translating to {t2Type.Name}");
        Debug.WriteLine($" - {e.ToString()}");
    }   
}

Ответы [ 2 ]

1 голос
/ 26 апреля 2020

Возможно объединение блока try..catch будет приемлемо для вас:

[string]$myAccount = '...' # some value

try {
    # test if string $myAccount is an account name
    if ($myAccount.Contains("\")) {
        $domain, $user = $myAccount.Split("\", 2)
    }
    else { 
        $domain = $env:USERDOMAIN
        $user   = $myAccount
    }
    $ntAccount = [System.Security.Principal.NTAccount]::new($domain, $user)
    $sid = [System.Security.Principal.NTAccount]::new($ntAccount).Translate([System.Security.Principal.SecurityIdentifier]).Value
    # output something
    "'$myAccount' is an account name:`r`nNT Account: $ntAccount`r`nSID: $sid"
}
catch {
    # try if string $myAccount is a SID
    if ($myAccount -like 'S-1-15-3*') {
        Write-Warning "SID '$myAccount' is a Capability type SID and cannot be resolved."
    }
    else {
        try {
            $ntAccount = [System.Security.Principal.SecurityIdentifier]::new($myAccount).Translate([System.Security.Principal.NTAccount]).Value
            "'$myAccount' is a SID:`r`nNT Account: $ntAccount`r`nSID: $myAccount"
        }
        catch {
            Write-Warning "'$myAccount' could not be identified."
        }
    }
}
0 голосов
/ 26 апреля 2020

Я понял, что могу использовать as вместо конструктора, чтобы избежать необходимости перехватывать исключения.

Версия PowerShell

[string]$idString = @('Everyone', 'S-1-1-0') | Get-Random -Count 1
[System.Security.Principal.IdentityReference]$idRef = $idString -as [System.Security.Principal.SecurityIdentifier]
if ($null -eq $idRef) {
    $idRef = $idString -as [System.Security.Principal.NTAccount]
}
"Input     = $idString"
"SID       = $($idRef.Translate([System.Security.Principal.SecurityIdentifier]).Value)"
"NTAccount = $($idRef.Translate([System.Security.Principal.NTAccount]).Value)"

C# версия

Примечание. Ключевое слово as недопустимо в сценарии C#, поэтому мое решение, приведенное выше, является указанием языка c ...

. Лучшее, что я придумал, - это инкапсулировать ошибку. часть потока в своей собственной функции, так что мы можем легко улучшить вещи, как только решение станет доступным.

public static void Main()
{
    var ids = new string[] {"S-1-1-0", "Everyone"}; // a list of values which may be SIDs or NTAccounts
    var pseudoRandomIndex = DateTime.Now.Millisecond % ids.Length; // saves initialising Random for a simple demo
    var idString = ids[pseudoRandomIndex]; // pick one id at random; be it a SID or an NT Account

    var idRef = TryParse(idString, out var temp) ? (IdentityReference)temp : (IdentityReference)new NTAccount(idString);
    Debug.WriteLine($"Selected value is {idString}");
    Debug.WriteLine($"SID:              {idRef.Translate(typeof(SecurityIdentifier))}");
    Debug.WriteLine($"NTAccount:        {idRef.Translate(typeof(NTAccount))}");
}

public static bool TryParse(string value, out SecurityIdentifier result) 
{
    try 
    {
        result = new SecurityIdentifier(value);
        return true;
    } 
    catch (ArgumentException) 
    {
        result = null;
        return false;
    }
}
...