diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs
index 4c0478add3..cb3f9f01e8 100644
--- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs
+++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs
@@ -47,7 +47,7 @@ namespace Microsoft.PowerShell
}
///
- /// PromptForChoice.
+ /// Prompt for choice.
///
///
///
@@ -60,7 +60,7 @@ namespace Microsoft.PowerShell
}
///
- /// PromptForCredential.
+ /// Prompt for credential.
///
///
///
@@ -73,7 +73,7 @@ namespace Microsoft.PowerShell
}
///
- /// PromptForCredential.
+ /// Prompt for credential.
///
///
///
@@ -88,7 +88,23 @@ namespace Microsoft.PowerShell
}
///
- /// ReadLine.
+ /// Prompt for credential.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override PSCredential PromptForCredential(string caption, string message, string userName, bool confirmPassword, string targetName, PSCredentialTypes allowedCredentialTypes, PSCredentialUIOptions options)
+ {
+ throw new PSNotImplementedException();
+ }
+
+ ///
+ /// Read line.
///
///
public override string ReadLine()
diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfacePrompt.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfacePrompt.cs
index 0afe5f987d..e1f9c9abbc 100644
--- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfacePrompt.cs
+++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfacePrompt.cs
@@ -303,10 +303,10 @@ namespace Microsoft.PowerShell
PSCredential credential = null;
credential =
PromptForCredential(
- null, // caption already written
- null, // message already written
- null,
- string.Empty);
+ caption: null, // caption already written
+ message: null, // message already written
+ userName: null,
+ targetName: string.Empty);
convertedObj = credential;
cancelInput = (convertedObj == null);
if ((credential != null) && (credential.Password.Length == 0) && listInput)
diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceSecurity.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceSecurity.cs
index 6926f68579..120bd38dcd 100644
--- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceSecurity.cs
+++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceSecurity.cs
@@ -3,10 +3,11 @@
using System;
using System.Globalization;
+using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Internal;
+using System.Runtime.InteropServices;
using System.Security;
-
using Microsoft.Win32;
namespace Microsoft.PowerShell
@@ -24,21 +25,17 @@ namespace Microsoft.PowerShell
/// this function will be modified to prompt using secure-path
/// if so configured.
///
- /// Name of the user whose creds are to be prompted for. If set to null or empty string, the function will prompt for user name first.
- /// Name of the target for which creds are being collected.
- /// Message to be displayed.
/// Caption for the message.
+ /// Message to be displayed.
+ /// Name of the user whose credentials are to be prompted for. If set to null or empty string, the function will prompt for user name first.
+ /// Name of the target for which credentials are being collected.
/// PSCredential object.
-
- public override PSCredential PromptForCredential(
- string caption,
- string message,
- string userName,
- string targetName)
+ public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName)
{
return PromptForCredential(caption,
message,
userName,
+ confirmPassword: false,
targetName,
PSCredentialTypes.Default,
PSCredentialUIOptions.Default);
@@ -47,31 +44,62 @@ namespace Microsoft.PowerShell
///
/// Prompt for credentials.
///
- /// Name of the user whose creds are to be prompted for. If set to null or empty string, the function will prompt for user name first.
- /// Name of the target for which creds are being collected.
- /// Message to be displayed.
/// Caption for the message.
- /// What type of creds can be supplied by the user.
- /// Options that control the cred gathering UI behavior.
+ /// Message to be displayed.
+ /// Name of the user whose credentials are to be prompted for. If set to null or empty string, the function will prompt for user name first.
+ /// Name of the target for which credentials are being collected.
+ /// What type of credentials can be supplied by the user.
+ /// Options that control the credential gathering UI behavior.
/// PSCredential object, or null if input was cancelled (or if reading from stdin and stdin at EOF).
+ public override PSCredential PromptForCredential(
+ string caption,
+ string message,
+ string userName,
+ string targetName,
+ PSCredentialTypes allowedCredentialTypes,
+ PSCredentialUIOptions options)
+ {
+ return PromptForCredential(
+ caption,
+ message,
+ userName,
+ confirmPassword: false,
+ targetName,
+ allowedCredentialTypes,
+ options);
+ }
+ ///
+ /// Prompt for credentials.
+ ///
+ /// Caption for the message.
+ /// Message to be displayed.
+ /// Name of the user whose credentials are to be prompted for. If set to null or empty string, the function will prompt for user name first.
+ /// Prompts user to re-enter the password for confirmation.
+ /// Name of the target for which credentials are being collected.
+ /// What type of credentials can be supplied by the user.
+ /// Options that control the credential gathering UI behavior.
+ /// PSCredential object, or null if input was cancelled (or if reading from stdin and stdin at EOF).
public override PSCredential PromptForCredential(
string caption,
string message,
string userName,
+ bool confirmPassword,
string targetName,
PSCredentialTypes allowedCredentialTypes,
PSCredentialUIOptions options)
{
PSCredential cred = null;
SecureString password = null;
+ SecureString reenterPassword = null;
string userPrompt = null;
string passwordPrompt = null;
+ string confirmPasswordPrompt = null;
+ string passwordMismatch = null;
if (!string.IsNullOrEmpty(caption))
{
// Should be a skin lookup
-
WriteLineToConsole();
WriteLineToConsole(PromptColor, RawUI.BackgroundColor, WrapToCurrentWindowWidth(caption));
}
@@ -85,9 +113,7 @@ namespace Microsoft.PowerShell
{
userPrompt = ConsoleHostUserInterfaceSecurityResources.PromptForCredential_User;
- //
// need to prompt for user name first
- //
do
{
WriteToConsole(userPrompt, true);
@@ -100,25 +126,95 @@ namespace Microsoft.PowerShell
while (userName.Length == 0);
}
- passwordPrompt = StringUtil.Format(ConsoleHostUserInterfaceSecurityResources.PromptForCredential_Password, userName
- );
+ passwordPrompt = StringUtil.Format(ConsoleHostUserInterfaceSecurityResources.PromptForCredential_Password, userName);
- //
// now, prompt for the password
- //
- WriteToConsole(passwordPrompt, true);
- password = ReadLineAsSecureString();
- if (password == null)
+ do
{
- return null;
+ WriteToConsole(passwordPrompt, true);
+ password = ReadLineAsSecureString();
+ if (password == null)
+ {
+ return null;
+ }
+ }
+ while (password.Length == 0);
+
+ if (confirmPassword)
+ {
+ confirmPasswordPrompt = StringUtil.Format(ConsoleHostUserInterfaceSecurityResources.PromptForCredential_ReenterPassword, userName);
+ passwordMismatch = StringUtil.Format(ConsoleHostUserInterfaceSecurityResources.PromptForCredential_PasswordMismatch);
+
+ // now, prompt to re-enter the password.
+ WriteToConsole(confirmPasswordPrompt, true);
+ reenterPassword = ReadLineAsSecureString();
+ if (reenterPassword == null)
+ {
+ return null;
+ }
+
+ if (!SecureStringEquals(password, reenterPassword))
+ {
+ WriteToConsole(ConsoleColor.Red, ConsoleColor.Black, passwordMismatch, false);
+ return null;
+ }
}
WriteLineToConsole();
-
cred = new PSCredential(userName, password);
-
return cred;
}
+
+ private static bool SecureStringEquals(SecureString password, SecureString confirmPassword)
+ {
+ if (password.Length != confirmPassword.Length)
+ {
+ return false;
+ }
+
+ IntPtr pwd_ptr = IntPtr.Zero;
+ IntPtr confirmPwd_ptr = IntPtr.Zero;
+ try
+ {
+ pwd_ptr = Marshal.SecureStringToBSTR(password);
+ if (pwd_ptr == IntPtr.Zero)
+ {
+ return false;
+ }
+
+ confirmPwd_ptr = Marshal.SecureStringToBSTR(confirmPassword);
+ if (confirmPwd_ptr == IntPtr.Zero)
+ {
+ return false;
+ }
+
+ int pwdLength = Marshal.ReadInt32(pwd_ptr, -4);
+ int equal = 0;
+ for (int i = 0; i < pwdLength; i++)
+ {
+ byte c1 = Marshal.ReadByte(pwd_ptr, i);
+ byte c2 = Marshal.ReadByte(confirmPwd_ptr, i);
+ equal = c1 ^ c2;
+ if (equal != 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ finally
+ {
+ if (pwd_ptr != IntPtr.Zero)
+ {
+ Marshal.ZeroFreeBSTR(pwd_ptr);
+ }
+
+ if (confirmPwd_ptr != IntPtr.Zero)
+ {
+ Marshal.ZeroFreeBSTR(confirmPwd_ptr);
+ }
+ }
+ }
}
}
-
diff --git a/src/Microsoft.PowerShell.ConsoleHost/resources/ConsoleHostUserInterfaceSecurityResources.resx b/src/Microsoft.PowerShell.ConsoleHost/resources/ConsoleHostUserInterfaceSecurityResources.resx
index 6c383d5052..6d705a7c7b 100644
--- a/src/Microsoft.PowerShell.ConsoleHost/resources/ConsoleHostUserInterfaceSecurityResources.resx
+++ b/src/Microsoft.PowerShell.ConsoleHost/resources/ConsoleHostUserInterfaceSecurityResources.resx
@@ -123,4 +123,10 @@
Password for user {0}:
+
+ Re-enter password for user {0}:
+
+
+ Passwords do not match.
+
diff --git a/src/Microsoft.PowerShell.Security/security/CredentialCommands.cs b/src/Microsoft.PowerShell.Security/security/CredentialCommands.cs
index cb23979c77..dddb9ee530 100644
--- a/src/Microsoft.PowerShell.Security/security/CredentialCommands.cs
+++ b/src/Microsoft.PowerShell.Security/security/CredentialCommands.cs
@@ -79,6 +79,12 @@ namespace Microsoft.PowerShell.Commands
private string _title = UtilsStrings.PromptForCredential_DefaultCaption;
+ ///
+ /// Gets or sets the confirm password prompt.
+ ///
+ [Parameter(ParameterSetName = messageSet)]
+ public SwitchParameter ConfirmPassword { get; set; }
+
///
/// Initializes a new instance of the GetCredentialCommand
/// class.
@@ -100,7 +106,14 @@ namespace Microsoft.PowerShell.Commands
try
{
- Credential = this.Host.UI.PromptForCredential(_title, _message, _userName, string.Empty);
+ Credential = this.Host.UI.PromptForCredential(
+ _title,
+ _message,
+ _userName,
+ ConfirmPassword,
+ string.Empty,
+ PSCredentialTypes.Default,
+ PSCredentialUIOptions.Default);
}
catch (ArgumentException exception)
{
diff --git a/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs b/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs
index 7650bf6854..0c8bf71e94 100644
--- a/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs
+++ b/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs
@@ -56,6 +56,7 @@ namespace System.Management.Automation.Host
///
///
///
+ ///
///
///
public abstract string ReadLine();
@@ -94,10 +95,12 @@ namespace System.Management.Automation.Host
/// Note that credentials (a user name and password) should be gathered with
///
///
+ ///
///
///
///
///
+ ///
///
///
public abstract SecureString ReadLineAsSecureString();
@@ -825,8 +828,9 @@ namespace System.Management.Automation.Host
///
///
///
- ///
+ ///
///
+ ///
public abstract Dictionary Prompt(string caption, string message, Collection descriptions);
///
@@ -859,9 +863,12 @@ namespace System.Management.Automation.Host
///
///
///
- public abstract PSCredential PromptForCredential(string caption, string message,
- string userName, string targetName
- );
+ ///
+ public abstract PSCredential PromptForCredential(
+ string caption,
+ string message,
+ string userName,
+ string targetName);
///
/// Prompt for credential.
@@ -883,7 +890,7 @@ namespace System.Management.Automation.Host
/// Types of credential can be supplied by the user.
///
///
- /// Options that control the credential gathering UI behavior
+ /// Options that control the credential gathering UI behavior.
///
///
/// User input credential.
@@ -893,10 +900,57 @@ namespace System.Management.Automation.Host
///
///
///
- public abstract PSCredential PromptForCredential(string caption, string message,
- string userName, string targetName, PSCredentialTypes allowedCredentialTypes,
- PSCredentialUIOptions options
- );
+ ///
+ public abstract PSCredential PromptForCredential(
+ string caption,
+ string message,
+ string userName,
+ string targetName,
+ PSCredentialTypes allowedCredentialTypes,
+ PSCredentialUIOptions options);
+
+ ///
+ /// Prompt for credential.
+ ///
+ ///
+ /// Caption for the message.
+ ///
+ ///
+ /// Text description for the credential to be prompt.
+ ///
+ ///
+ /// Name of the user whose credential is to be prompted for. If set to null or empty
+ /// string, the function will prompt for user name first.
+ ///
+ ///
+ /// Prompts user to re-enter the password for confirmation.
+ ///
+ ///
+ /// Name of the target for which the credential is being collected.
+ ///
+ ///
+ /// Types of credential can be supplied by the user.
+ ///
+ ///
+ /// Options that control the credential gathering UI behavior.
+ ///
+ ///
+ /// User input credential.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public abstract PSCredential PromptForCredential(
+ string caption,
+ string message,
+ string userName,
+ bool confirmPassword,
+ string targetName,
+ PSCredentialTypes allowedCredentialTypes,
+ PSCredentialUIOptions options);
///
/// Presents a dialog allowing the user to choose an option from a set of options.
@@ -922,6 +976,7 @@ namespace System.Management.Automation.Host
///
///
///
+ ///
public abstract int PromptForChoice(string caption, string message, Collection choices, int defaultChoice);
#endregion Dialog-oriented interaction
diff --git a/src/System.Management.Automation/engine/hostifaces/internalHostuserInterfacesecurity.cs b/src/System.Management.Automation/engine/hostifaces/internalHostuserInterfacesecurity.cs
index f5155fd7c1..86286d955e 100644
--- a/src/System.Management.Automation/engine/hostifaces/internalHostuserInterfacesecurity.cs
+++ b/src/System.Management.Automation/engine/hostifaces/internalHostuserInterfacesecurity.cs
@@ -14,38 +14,52 @@ namespace System.Management.Automation.Internal.Host
///
/// See base class.
///
-
- public override
- PSCredential
- PromptForCredential
- (
- string caption,
- string message,
- string userName,
- string targetName
- )
+ public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName)
{
- return PromptForCredential(caption, message, userName,
- targetName,
- PSCredentialTypes.Default,
- PSCredentialUIOptions.Default);
+ return PromptForCredential(
+ caption,
+ message,
+ userName,
+ confirmPassword: false,
+ targetName,
+ PSCredentialTypes.Default,
+ PSCredentialUIOptions.Default);
}
///
/// See base class.
///
-
- public override
- PSCredential
- PromptForCredential
- (
+ public override PSCredential PromptForCredential(
string caption,
string message,
string userName,
string targetName,
PSCredentialTypes allowedCredentialTypes,
- PSCredentialUIOptions options
- )
+ PSCredentialUIOptions options)
+ {
+ return PromptForCredential(
+ caption,
+ message,
+ userName,
+ confirmPassword: false,
+ targetName,
+ allowedCredentialTypes,
+ options);
+ }
+
+ ///
+ /// See base class.
+ ///
+ public override
+ PSCredential
+ PromptForCredential(
+ string caption,
+ string message,
+ string userName,
+ bool confirmPassword,
+ string targetName,
+ PSCredentialTypes allowedCredentialTypes,
+ PSCredentialUIOptions options)
{
if (_externalUI == null)
{
@@ -55,7 +69,7 @@ namespace System.Management.Automation.Internal.Host
PSCredential result = null;
try
{
- result = _externalUI.PromptForCredential(caption, message, userName, targetName, allowedCredentialTypes, options);
+ result = _externalUI.PromptForCredential(caption, message, userName, confirmPassword, targetName, allowedCredentialTypes, options);
}
catch (PipelineStoppedException)
{
diff --git a/src/System.Management.Automation/engine/remoting/server/ServerRemoteHostUserInterface.cs b/src/System.Management.Automation/engine/remoting/server/ServerRemoteHostUserInterface.cs
index 6b69bb07b8..10abafe817 100644
--- a/src/System.Management.Automation/engine/remoting/server/ServerRemoteHostUserInterface.cs
+++ b/src/System.Management.Automation/engine/remoting/server/ServerRemoteHostUserInterface.cs
@@ -233,5 +233,15 @@ namespace System.Management.Automation.Remoting
return _serverMethodExecutor.ExecuteMethod(RemoteHostMethodId.PromptForCredential2,
new object[] { caption, message, userName, targetName, allowedCredentialTypes, options });
}
+
+ ///
+ /// Prompt for credential.
+ ///
+ public override PSCredential PromptForCredential(string caption, string message, string userName, bool confirmPassword, string targetName, PSCredentialTypes allowedCredentialTypes, PSCredentialUIOptions options)
+ {
+ return _serverMethodExecutor.ExecuteMethod(
+ RemoteHostMethodId.PromptForCredential2,
+ new object[] { caption, message, userName, confirmPassword, targetName, allowedCredentialTypes, options });
+ }
}
}
diff --git a/src/System.Management.Automation/security/CredentialParameter.cs b/src/System.Management.Automation/security/CredentialParameter.cs
index 0d48e4fa73..ad9463b9df 100644
--- a/src/System.Management.Automation/security/CredentialParameter.cs
+++ b/src/System.Management.Automation/security/CredentialParameter.cs
@@ -32,6 +32,7 @@ namespace System.Management.Automation
PSCredential cred = null;
string userName = null;
bool shouldPrompt = false;
+ bool confirmPassword = false;
if ((engineIntrinsics == null) ||
(engineIntrinsics.Host == null) ||
@@ -74,10 +75,13 @@ namespace System.Management.Automation
prompt = CredentialAttributeStrings.CredentialAttribute_Prompt;
cred = engineIntrinsics.Host.UI.PromptForCredential(
- caption,
- prompt,
- userName,
- string.Empty);
+ caption,
+ prompt,
+ userName,
+ confirmPassword,
+ string.Empty,
+ PSCredentialTypes.Default,
+ PSCredentialUIOptions.Default);
}
return cred;
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/GetCredential.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Security/GetCredential.Tests.ps1
index cb9d0ee70e..45fa386001 100755
--- a/test/powershell/Modules/Microsoft.PowerShell.Security/GetCredential.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Security/GetCredential.Tests.ps1
@@ -102,4 +102,21 @@ Describe "Get-Credential Test" -Tag "CI" {
$netcred.UserName | Should -Be "John"
$netcred.Password | Should -Be "CredTest"
}
+ It "Get-credential Joe -ConfirmPassword set to false"{
+ $cred = $ps.AddScript("Get-Credential Joe -ConfirmPassword:`$false").Invoke() | Select-Object -First 1
+ $cred | Should -BeOfType System.Management.Automation.PSCredential
+ $netcred = $cred.GetNetworkCredential()
+ $netcred.UserName | Should -Be "Joe"
+ $netcred.Password | Should -Be "This is a test"
+ $th.ui.Streams.Prompt[-1] | Should -Match "Credential:[^:]+:[^:]+"
+ }
+ It "Get-Credential with only -ConfirmPassword" {
+ $cred = $ps.AddScript("Get-Credential -ConfirmPassword").Invoke() | Select-Object -First 1
+ $cred | Should -BeOfType System.Management.Automation.PSCredential
+ $netcred = $cred.GetNetworkCredential()
+ $netcred.UserName | Should -Be "John"
+ $netcred.Password | Should -Be "This is a test"
+ $th.ui.Streams.Prompt[-2] | Should -Match "Credential:[^:]+:[^:]+"
+ $th.ui.Streams.Prompt[-1] | Should -Match "Credential@[^@]+@[^@]+"
+ }
}
diff --git a/test/tools/Modules/HelpersHostCS/HelpersHostCS.psm1 b/test/tools/Modules/HelpersHostCS/HelpersHostCS.psm1
index 0408cdcc49..3be3482f98 100755
--- a/test/tools/Modules/HelpersHostCS/HelpersHostCS.psm1
+++ b/test/tools/Modules/HelpersHostCS/HelpersHostCS.psm1
@@ -10,6 +10,7 @@ using System.Globalization;
using System.Collections.ObjectModel;
using System.Security;
using System.Collections;
+using System.Runtime.InteropServices;
namespace TestHost
{
@@ -145,18 +146,36 @@ namespace TestHost
public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName)
{
- Streams.Prompt.Add("Credential:" + caption + ":" + message);
- SecureString ss = ReadLineAsSecureString();
- string userNameToUse = string.IsNullOrEmpty(userName) ? UserNameForCredential : userName;
- return new PSCredential(userNameToUse, ss);
+ return PromptForCredential(caption,
+ message,
+ userName,
+ confirmPassword: false,
+ targetName,
+ PSCredentialTypes.Default,
+ PSCredentialUIOptions.Default);
}
public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName, PSCredentialTypes allowedCredentialTypes, PSCredentialUIOptions options)
+ {
+ return PromptForCredential(caption,
+ message,
+ userName,
+ confirmPassword: false,
+ targetName,
+ allowedCredentialTypes,
+ options);
+ }
+
+ public override PSCredential PromptForCredential(string caption, string message, string userName, bool confirmPassword, string targetName, PSCredentialTypes allowedCredentialTypes, PSCredentialUIOptions options)
{
Streams.Prompt.Add("Credential:" + caption + ":" + message);
- SecureString ss = ReadLineAsSecureString();
+ SecureString password = ReadLineAsSecureString();
+ if(confirmPassword)
+ {
+ Streams.Prompt.Add("Credential@" + caption + "@" + message);
+ }
string userNameToUse = string.IsNullOrEmpty(userName) ? UserNameForCredential : userName;
- return new PSCredential(userNameToUse, ss);
+ return new PSCredential(userNameToUse, password);
}
public override string ReadLine()