为 Windows 调试器提供符号服务器身份验证

问题描述 投票:0回答:1

我们有一个运行 cdb 和处理 dmp 文件的工具。符号服务器需要对 cdb 进行身份验证才能从中下载文件。我们的设置不允许在未经身份验证的情况下访问符号。

我从 cdb 获取以下用于下载 ntdll 的日志。 ntdll 只是一个例子,这个 dll 可以是任何私有 dll。

日志显示它尝试使用某些凭据提供程序进行身份验证,但没有有关如何解决错误的信息。 任何人都可以共享与凭据提供程序相关的文档,如何以非交互模式向 cdb 提供凭据?

 
SYMSRV:  BYINDEX: 0x9
         c:\sym1*https://repos.net/factory/symbols/microsoftsymbols/
         ntdll.pdb
         03818EC49CBD48F2B0B378C3558734891
SYMSRV:  UNC: c:\sym1\ntdll.pdb\03818EC49CBD48F2B0B378C3558734891\ntdll.pdb - path not found
SYMSRV:  UNC: c:\sym1\ntdll.pdb\03818EC49CBD48F2B0B378C3558734891\ntdll.pd_ - path not found
SYMSRV:  UNC: c:\sym1\ntdll.pdb\03818EC49CBD48F2B0B378C3558734891\file.ptr - path not found
SYMSRV:  HTTPGET: /factory/symbols/microsoftsymbols//ntdll.pdb/03818EC49CBD48F2B0B378C3558734891/ntdll.pdb
SYMSRV:  HttpQueryInfo: 80190191 - HTTP_STATUS_DENIED
SYMSRV:  Credential Provider: Getting credentials for: protocol=https, host=repos.net, path=factory/symbols/microsoftsymbols/, interactive=0, isRetry=0
SYMSRV:  Credential Provider: Getting credentials from provider: C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\credentialproviders\gcmw\dbgcredentialprovider_gcmw.dll...
SYMSRV:  Credential Provider: DebuggerCredentialManager.exe is not found.
SYMSRV:  Credential Provider: DbgCredentialProvider_gcmw: Failed to build execution command.
HttpQueryInfo: Requesting Authentication for Web Site.
SYMSRV:  RESULT: 0x80190191
windows debugging windbg debug-symbols windows-driver
1个回答
0
投票

该文档将在不久的将来发布。我已复制该文档并将其粘贴在下面。

请注意,DbgCredentialProvider.config.xml 的具体位置取决于您安装的 Windows Kit 版本。根据您的调试器日志,您没有最新的套件,但我认为这不会影响下面的说明。

The Windows debugger infrastructure has been updated to assist symbols or sources files download when the server returns
an error HTTP_STATUS_DENIED 401 (access denied). In such case the request has to be resent with updated authentication headers
containing proper credentials for the user.

The bridge between the debugger and the credential providers is DbgCredentialProvider.dll. It will look for credential providers using the following algorithm:

    1) It opens a file DbgCredentialProvider.config.xml which should be located next to DbgCredentialProvider.dll. The search behavior that is used to locate
       DbgCredentialProvider.config.xml file may change in the future.

            The DbgCredentialProvider.config.xml file contains a folders list:

                 <?xml version="1.0" encoding="utf-8"?>
                 <!--
                 The config file is located next to DbgCredentialProvider.dll.
                 -->
                 <Settings>
                     <Folders>
                         <!--
                             This is a list of the folders which should be provided as an absolute file path or
                             relative to the location of this config file.
                         -->
                         <Folder>CredentialProviders</Folder>
                     </Folders>
                 </Settings>

                 You can list more than one folder under <Folders></Folders> elements. The folders can be relative or absolute path. If it is relative path, it is relative to the location
                 of the DbgCredentialProvider.config.xml file. The folders will be searched for providers in the order listed in the XML file.

                 In the current example the Folders collection has just one folder "CredentialProviders" and it is a relative path.

    2) Then all files with extension '*.xml' in CredentialProviders folder will be enumerated. The order in which the XML files are enumerated is unspecified. Those XML files describe which
       debugger credential providers are available for the debugger. You may find DbgCredentialProvider_gcmw.xml file (or other XML files) in CredentialProvider folder which contain data like this:

                 <?xml version="1.0" encoding="utf-8"?>
                 <CredentialProviders>
                     <!--
                           This is a list of the provider modules which should be provided as an absolute file path or
                           relative to the location of this config file.
                           The provider is a DLL, EXE or CMD file.
                     -->
                   <CredentialProvider ApiVersion="2.0.0">GCMW\DbgCredentialProvider_gcmw.dll</CredentialProvider>

                 </CredentialProviders>

       The XML file may list more than one credential provider. In this example we have just one provider DbgCredentialProvider_gcmw.dll and it is located
       in GCMW folder relative to the DbgCredentialProvider_gcmw.xml file location. The providers may be relative or absolute paths similar to the ones described
       in the section for DbgCredentialProvider.config.xml file.

The debugger credential providers can be DLL, EXE or CMD script files. The debugger will ask every provider for credentials and it will use the credentials from the first provider
which returns success.

If the provider is implemented in a DLL it must export GetUserCredentials method as listed in this file.
If the provider is implemented in EXE or a CMD script it should be able to process the following command line parameters (case insensitive), which cannot be combined with each other.
    - Get
    - Erase
    - Store

    Get Command
    It is used to retrieve a credential. The remaining data will be passed to the provider via the standard input stream:

        protocol=https or http. For security reasons we recommend you send credentials only over https.
        host=XXX ex. host=contoso.symbols.com
        path=YYY ex. path=apis/symbol/symsrv
        resourceKind=symbols or sources. Ex resourceKind=symbols
        isretry=true or false 
        issilent=true or false
        parenthwnd=ZZZ ex. parenthwnd=593598
        <Followed by an empty line to indicate the end of the input data>

        The full URI/URL is built by concatenating <protocol>://<host>/<path> from above i.e. it is
                https://contoso.symbols.com/apis/symbol/symsrv

        The return data to the debugger is via the standard output stream as follows:

        username=xxx
        credentialkind=Basic
        password=yyy --> This can be a password or a PAT token

        Or 

        username=xxx
        credentialkind=Bearer
        header=Bearer <TOKEN_GOES_HERE> ---> Usually OAuth2 tokens begin with "ey" and it is a very long string

        Error may be returned via 

        error=zzzzz

        For example for the symbol server https://contoso.symbols.com/apis/symbol/symsrv the request would look like this:

        protocol=https
        host=contoso.symbols.com
        path=apis/symbol/symsrv
        resourceKind=symbols
        isretry=false 
        issilent=false
        parenthwnd=593598
        Followed by an empty line to indicate the end of the input data.

    Please note that the parameters above (or in the resulting output stream below) are not case sensitive.

    The debugger asks for the credential using isRetry=false. Some providers may be getting the token from their local cache. Once the debugger resends the HTTP request
    with this token the server may return 401 response again. This may be due to the token has expired. Then the debugger will ask the credential provider for a new token
    and this time the isRetry=true. In such case the provider should not use its cache but retrieve a brand new token.

    Some providers may display an authentication UI. If so it should use the 'parenthwnd' parameter so this UI would appear as a modal dialog to the main debugger window.
    Otherwise the authentication UI may be hidden behind the main debugger window and the user may be given the impression that the debugger is "frozen".

    A debugger client application similar to windbg may use may use DBG_CREDENTIAL_PROVIDER_PARENT_HWND environment variable or imagehlp/dbghelp SymSetParentWindow method to setup the parent HWND.
    You can also use IDebugAdvanced2::Request message DEBUG_REQUEST_SET_PARENT_HWND with value of HWND cast to UINT32.

    In some non-interactive environments there may be no user to interact with a UI. In such a case the parameter issilent would be true.
    The provider should not be displaying any authentication or other UI when this parameter is true.

    How to setup the silent (non interactive) environment is described below:
           a) SYMOPT_NO_PROMPTS described in this document https://learn.microsoft.com/windows-hardware/drivers/debugger/symbol-options.
           b) SSRVOPT_UNATTENDED described in this document https://learn.microsoft.com/previous-versions/ff797954(v=vs.85)
           c) IDebugSymbols::SetSymbolOptions described in this document https://learn.microsoft.com/windows-hardware/drivers/ddi/dbgeng/nf-dbgeng-idebugsymbols3-setsymboloptions
           d) From the Windows debugger you can execute the following bang command "!sym quiet" (or "!sym prompts off")

    Store Command
        The credential provider may choose to use this command to store the credentials into its cache. The input parameters are same as for Get command.
        There is no output return value needed (Error still might be returned)

    Erase Command
        The credential provider may choose to use this command to erase the credentials from its cache. The input parameters are same as for Get command.
        There is no output return value needed (Error still might be returned)

    Here is an example of a CMD file which returns an HTTP authentication header:

         OAuth2CredentialProvider.xml file located in CredentialProviders folder:

             <?xml version="1.0" encoding="utf-8"?>
             <CredentialProviders>
                 <CredentialProvider ApiVersion="2.0.0" >OAuth2CredentialProvider\OAuth2CredentialProvider.cmd</CredentialProvider>
             </CredentialProviders> 

         OAuth2CredentialProvider.cmd file located in OAuth2CredentialProvider folder:

             @echo off
             echo [email protected]
             echo header=Bearer <TOKEN_GOES_HERE>

   Here is a PS script example of returning a PAT token:

         File PatCredentialProvider.xml content:

             <?xml version="1.0" encoding="utf-8"?>
             <CredentialProviders>
                 <CredentialProvider ApiVersion="2.0.0">PATCredentialProvider\PATCredentialProvider.bat</CredentialProvider>
             </CredentialProviders>

         File PATCredentialProvider.bat located in PATCredentialProvider folder:

             @echo off
             <PATH_TO_POWERSHELL>\PowerShell.exe -NoProfile -executionpolicy Unrestricted -WindowStyle Hidden -File "%~dp0\PATCredentialProvider.ps1"

         File PATCredentialProvider.ps1 located in PATCredentialProvider folder:

             <#
            .SYNOPSIS
               Given input, parses to find out which symbol server we want credentails for, and searches the Microsoft Credential Manager for those credentials.
               If found, prints the credentials to standard output. If not, prints error.

            .INPUT
               Delivered through standard input:
               protocol=http or https
               host=xxx ex. host=contoso.symbols.com
               path=yyy ex. path=apis/symbol/symsrv
               resourceKind=symbols
               isretry=false 
               issilent=false
               parenthwnd=593598
               <empty line to mark the end of the input parameters>

            .OUTPUT

               Delivered through standard output:

               username=aaa
               password=bbb - where the password can be a password or PAT. When PAT is returned the username will be any name (not necessarily the name of the currently logged in user)<!--[SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="It's an example")]-->
               <empty line to mark the end of the output parameters>

            #>

            $logDirectory = (Get-Item Env:LoggingDirectory).Value
            $logFile = Join-Path $logDirectory "credProviderLog.txt"

            try
            {
               "Entering Credential Provider" | Out-File $logFile -Append

               $lines = While($line=Read-Host) {$line}
               $lines | Out-File $logFile -Append
               if (!(Get-Module "CredentialManager"))
               {
                   "Installing module" | Out-File $logFile -Append

                   Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force

                   Install-Module CredentialManager -force -Scope CurrentUser
               }

               $pathLine = $lines | Where-Object {$_.StartsWith("path=")} | Select-Object -First 1
               "Found path line: $pathLine" | Out-File $logFile -Append

               [regex]$regex="path=(?<ServerName>.*)"
               $pathLine -Match $regex

               $symbolPath = "symbol:$($Matches.ServerName)"

               "Found symbol path: $symbolPath" | Out-File $logFile -Append

               $PAT = Get-StoredCredential -Target $symbolPath -AsCredentialObject

               if ($PAT)
               {
                   "Found PAT!" | Out-File $logFile -Append
                   Write-Host "username=placeholder"
                   Write-Host "password=$($PAT.Password)"; # For OAuth 2 tokens You can change to output header=Bearer TOKEN
                   Write-Host
               }
               else
               {
                   "Could not locate PAT for Symbol Server: $symbolPath" | Out-File $logFile -Append
                   Write-Host "error=Could not locate PAT for Symbol Server: $symbolPath"
               }
            }
            catch [System.SystemException]
            {
               "ERROR" | Out-File $logFile -Append
            
               $_ | Out-File $logFile -Append
            }

If you are writing a custom provider located in a CMD or EXE file, you can test it simply by launching it from a console window using the commands.
For example:

DebuggerCredentialManager.exe get

    This would start the application, and print something like this and then will wait for user input (an empty line indicates end of the user input).

[Information] [DebuggerCredentialProvider.102949]Microsoft Debugger Credential Manager version 2024.0409.02656.285 (Windows, .NET 6.0.29) 'get'

Please note that the providers may chose to print some diagnostic information on the output stream, but the debugger would ignore it, nor it will
display those to the user. So the examples of extra information printed here are for illustration purposes only. Other providers may print
other diagnostic information or not print anything.

Here is the information you enter (it can be any text case) in the console window (input stream):

protocol=https
host=contoso.symbols.com
path=apis/symbol/symsrv
resourceKind=symbols
isretry=false 
issilent=false
parenthwnd=593598
Then press ENTER key twice to indicate end of user input.

The provider responds via the standard output stream.
The debugger will ignore any lines not matching the pattern `key=value` where key is
one of the following: protocol, host, path, username, credentialkind,or header.
Case is ignored in the key. The debugger treats a blank line as the end of input.

[Verbose] [DebuggerCredentialProvider.103258]AzureCredentialProvider - Attempting to acquire bearer token using provider 'Msal Cache'
[Verbose] [DebuggerCredentialProvider.103300]Token expiration data - current UTC time:9/18/2024 5:33:00 PM, ExpiresOn: 9/18/2024 6:43:25 PM
[Information] [DebuggerCredentialProvider.103300]AzureCredentialProvider - Acquired bearer token using 'Msal Cache'
protocol=https
host=contoso.symbols.com
path=apis/symbol/symsrv
[email protected]
credentialkind=Bearer
header=Bearer eyJ0eXAi....
<empty line>
© www.soinside.com 2019 - 2024. All rights reserved.