Reputation: 20384
I'd like to show the standard system dialog to ask the user for an account username and password to use this information to start a process with these credentials.
I've been pointed to the CredUIPromptForCredentials
function that shows that dialog. It returns username and password as string. But the ProcessStartInfo
structure expects a password as SecureString
.
I understand that I could now use the password as string and convert it to a SecureString
character by character (there's no single function for that) - but it would defeat the idea behind the SecureString
entirely.
So I guess there must be some way to directly accept the password from the unmanaged call to CredUIPromptForCredentials
as SecureString
in .NET. After all, I really don't need to access the password in my application in any way. It's just supposed to be used to start another process and can then be forgotten as soon as possible.
So how would my P/Invoke declaration for CredUIPromptForCredentials
look like with a SecureString
? (I've started with the one from pinvoke.net for C#.)
Update: Oh, and if somebody has an example for the new function CredUIPromptForWindowsCredentials
in Windows Vista/7, that would be cool as well, because I can't even figure out how to use that at the moment.
Upvotes: 1
Views: 1815
Reputation: 22261
You can cast the IntPtr
of an unmanaged string buffer to char*
and use the SecureString(char*, int)
constructor.
// somehow, we come into posession of an IntPtr to a string
// obviously, this would be a foolish way to come into it in
// production, since stringOriginalContents is already in managed
// code, and the lifetime can therefore not be guaranteed...
var stringOriginalContents = "foobar";
IntPtr strPtr = Marshal.StringToHGlobalUni(stringOriginalContents);
int strLen = stringOriginalContents.Length;
int maxLen = 100;
// we copy the IntPtr to a SecureString, and zero out the old location
SecureString ssNew;
unsafe
{
char* strUPtr = (char*)strPtr;
// if we don't know the length, calculate
//for (strLen = 0; *(strUPtr + strLen) != '\0'
// // stop if the string is invalid
// && strLen < maxLen; strLen++)
// ;
ssNew = new SecureString((char*)strPtr, strLen);
// zero out the old memory and release, or use a Zero Free method
//for (int i = 0; i < strLen; i++)
// *(strUPtr + i) = '\0';
//Marshal.FreeHGlobal(strPtr);
// (only do one of these)
Marshal.ZeroFreeGlobalAllocUnicode(strPtr);
}
// now the securestring has the protected data, and the old memory has been
// zeroed, we can check that the securestring is correct. This, also should
// not be in production code.
string strInSecureString =
Marshal.PtrToStringUni(
Marshal.SecureStringToGlobalAllocUnicode(ssNew));
Assert.AreEqual(strInSecureString, stringOriginalContents);
Upvotes: 2