C# Call LogonUser fail with SecureString - c#

everyone, I am facing an issue with LogonUser function.
I just want to know if I can import the LogonUser function into C# by this signature:
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern int LogonUser(string username, string domain, IntPtr password, int logonType, int logonProvider, ref IntPtr token);
Because I want to secure my password not using a string, but a SecureString class. Then later the function is used like below:
var passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(password);
var result = LogonUser(userName, domain, passwordPtr, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token);
I always get result = 0 and the message shows that the user name and password is incorrect.
But when I change the signature to use string password, then everything works well.
Please help me as to secure the password with SecureString is important to me.

As pointed out by Alex K, there's an example of using LogonUser in the SecureStringToGlobalAllocUnicode. Note that the P/Invoke declaration there is:
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool LogonUser(String username, String domain, IntPtr password,
int logonType, int logonProvider, ref IntPtr token);
And that CharSet = CharSet.Unicode has been specified. Unfortunately, for historical reasons, the default CharSet value is Ansi and so that's what your P/Invoke attempt is going to use.
This will be fine for the username parameter since the P/Invoke infrastructure will ensure that it converts the string appropriately. But it's not appropriate for the password parameter since you've already performed the string conversion and you've done it as Unicode - and all that P/Invoke is seeing is an IntPtr now.
I'd suggest updating your P/Invoke signature to match that given in the sample.
Another alternative would be to switch to using SecureStringToGlobalAllocAnsi and leaving your P/Invoke signature alone. But this is a seriously second-rate solution. Writing non-Unicode aware code in 2015 is seriously not to be recommended.
Just get in the habit of always specifying CharSet.Unicode in any P/Invoke signatures you write.

Related

Why LoadLibrary not working with the given path?

I'm trying to call LoadLibrary method, But it returns 0. Marshal.GetLastWin32Error is returning 126 (The specified module could not be found.).
Code:
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
string path = #"C:\junk\测试\BlueStacksKK_DeployTool_2.5.48.7209_china_gmgr\ProgramFiles\BstkC.dll";
IntPtr ptr = LoadLibrary(path);
int error = Marshal.GetLastWin32Error();
But If I move this file to some other location like C:\Test\BstkC.dll, it works fine.
Issue could be due to 测试 in path. So If we have direcotry in other languages other then English, how will it work.
Just for your information. File.Exists(path) returns true.
You have to set the character set used to Unicode, since you use non-unicode character in your path:
[DllImport("kernel32", CharSet=CharSet.Unicode)]
static extern IntPtr LoadLibrary(string lpFileName);
Now it takes the LoadLibraryA (ANSI) variant. See MSDN.
Try:
[DllImport("kernel32", SetLastError = true]
static extern IntPtr LoadLibraryW([MarshalAs(UnmanagedType.LPWStr)]string lpFileName);
The underlying Win32 API comes with two flavors: ASCII mode (which allows only ASCII characters in strings) and Unicode Mode (which allows UTF16 characters in strings).
C# is UTF16 based, basically, you invoked a ASCII-flavored function with UTF16 string, you need to explicitly tell the CLR you want the Unicode flavored function (LoadLibraryW) and retain the UTF16 encoding of the C# string (by using LPWStr).

how can I set Account Type and StartType method when add a window service automatically?

The code has been taken from here This code example works fine my side its just add a service own local system) But I prepare Setup program I must think StartMode and User Account Type method because of customers system.
so How can I set Account Type and StartType method when I gonna add windows service.
there is every appearance that ServiceBootFlag enum provide StartType but Account Type still a problem. Or I'm open different idiom for set mentioned method for adding a windows service programmatically
And I read this question all post and comments I didn't see anything for set Startmode and User Account Type method When add a windows service.
[DllImport("advapi32.dll", EntryPoint = "CreateServiceA")]
private static extern IntPtr CreateService(IntPtr hSCManager, string
lpServiceName, string lpDisplayName, ServiceRights dwDesiredAccess, int
dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl,
string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string
lpDependencies, string lp, string lpPassword);

How to make Dllimport correctly in c#?

ive had some problems building a correct .dll from a c++-project to my c#-project.
I played around with the c++-project-properties and got a .dll file which i can add and refer to in my c# web-project. I use Dllimport to make a function call to the .dll like this:
[DllImport("Filename.dll", CharSet = CharSet.Ansi)]
static extern void Function1([MarshalAs(UnmanagedType.LPStr)] string src,
int srcLen,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder dst,
int dstLen)
The c++ function header is:
__declspec(dllimport) void Function1(unsigned char *src,
unsigned long srclen,
unsigned char *dst,
unsigned long dstlen);
Im calling Function1 in c# with this:
string strSrc = "Something";
StringBuilder strDest = new StringBuilder(kryptlen-1);
int l = strSrc.Length();
Function1(strSrc, l, strDest, l);
No exceptions or errors are occuring, though im not getting the output im expecting. The function is a decrypting method that takes an encrypted string(src) and returns the decrypted version of this (dst).
Is it the way ive generated the .dll file or is it the wrong way im calling the function? Im running out of ideas ive tried most combinations.
Thanks in advice!
C++ by default unless changed uses a caller( Cdecl ) calling convention. Your C++ code does not change the calling convention. Your C# code by default ( unless you change it ) will use a callee convention ( StdCall ).
While this might not be exactly the problem your having it still is technically incorrect. Even if you were to fix your current problem you likely will end up having a problem because of the calling convention.
No exceptions or errors are occuring,
though im not getting the output im
expecting. The function is a
decrypting method that takes an
encrypted string(src) and returns the
decrypted version of this (dst).
What exactly are you getting?
The solution to the calling convention problem is to declare the calling convention.
[DllImport("Filename.dll", CharSet = CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)] static extern void Function1([MarshalAs(UnmanagedType.LPStr)] string src, int srcLen, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder dst, int dstLen)
Where'd UnmanagedType.LPWStr come from? There are no wide strings in the C++ declaration. You're also passing the source length twice, while the variable names suggest you need the source length and the destination buffer capacity.
If src is encrypted data as you say, the correct p/invoke signature is probably:
[DllImport("Filename.dll", CallingConvention = CallingConvention.CDecl)]
static extern void Function1(byte[] src,
UInt32 srcLen,
[MarshalAs(UnmanagedType.LPStr)] StringBuilder dst,
UInt32 dstLen)
Trying to force binary data into a Unicode string is a losing proposition.
After some time changing the settings back and forth i noticed that the data i had to start with was the same data i had after a decrypt and an encrypt. So I realized that the encryption/decryption was working. The problem was the data. When i had the correct data i got the correct output!
This is the end setup im stuck with now, hope it can help someone:
cryptedString = "CryptedStringFromDataBase";
StringBuilder decryptedString = new StringBuilder(300);
Decrypt(cryptedString, (uint)cryptedString.Length, decryptedString, 300);
[DllImport("Filename.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern void Decrypt(string src, UInt32 srcLen, [MarshalAs(UnmanagedType.LPStr)] StringBuilder dst, UInt32 dstLen);

What is wrong with my DLLImport of LogonUser with String Marshaling? [C#]

For some odd reason, when I marshal the LogonUser DLLImport parameters I am no longer able to login succesfully when using the INTERACTIVE logon type, it works for NETWORK logon type.
This is my code:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool LogonUser
(
[MarshalAs(UnmanagedType.LPStr)]
String lpszUsername,
[MarshalAs(UnmanagedType.LPStr)]
String lpszDomain,
[MarshalAs(UnmanagedType.LPStr)]
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr hToken
);
bResult = LogonUser(
"username",
".",
"password",
(int)LogonType.INTERACTIVE, // = 2
(int)LogonProvider.DEFAULT, // = 0
out hToken
);
Now, as-is my call to LogonUser fails (Logon Exception: Logon failure: unknown user name or bad password), but if I remove the [MarshalAs(UnmanagedType.LPStr)]s from the DLLImport it works fine, also if I switch to LogonType.NETWORK it works fine, why is it different with INTERACTIVE?
Sadly I need to keep it as I use this with other functions such as LoadUserProfile that needs it to be Marshalled (only way I could get it to work and not display unknown windows characters [squares]). Do I need to do some funky marshaling of strings or something to get it to validate correctly?
Any help would be appreciated.
Thanks,
LogonUser takes an LPTSTR, not an LPSTR, as parameters. You should just use the default string marshaling, and it will work correctly.
See LogonUser and pinvoke.net's declaration for a property P/Invoke of LogonUser.
I had the same issue, but the cause was different. Then I realized that in the place I work we must log-on our machines with a digital certificate instead of user and password.
I had forgotten that we have this restriction on our domain.
So I have to use another domain account, instead of mine, to test my application.
I don't know if these information will help but, but they maybe be of some use for other people.

c# network login

How do I perform a network login, to access a shared driver for instance, programmatically in c#? The same can be achieved by either attempting to open a share through the explorer, or by the net use shell command.
P/Invoke call to WNetAddConnection2 will do the trick. Look here for more info.
[DllImport("mpr.dll")]
public static extern int WNetAddConnection2A
(
[MarshalAs(UnmanagedType.LPArray)] NETRESOURCEA[] lpNetResource,
[MarshalAs(UnmanagedType.LPStr)] string lpPassword,
[MarshalAs(UnmanagedType.LPStr)] string UserName, int dwFlags
);
You'll need to use Windows Identity Impersonation, take a look at these links
http://blogs.msdn.com/shawnfa/archive/2005/03/21/400088.aspx
http://blogs.msdn.com/saurabhkv/archive/2008/05/29/windowsidentity-impersonation-using-c-code.aspx

Categories

Resources