My Problem
I'm using PInvoked Windows API functions to verify if a user is part of the local administrators group. I'm utilizing GetCurrentProcess, OpenProcessToken, GetTokenInformationand LookupAccountSid to verify if the user is a local admin.
GetTokenInformation returns a TOKEN_GROUPS struct with an array of SID_AND_ATTRIBUTES structs. I iterate over the collection and compare the user names returned by LookupAccountSid.
My problem is that, locally (or more generally on our in-house domain), this works as expected. The builtin\Administrators is located within the group membership of the current process token and my method returns true. On another domain of another developer the function returns false.
The LookupAccountSid functions properly for the first 2 iterations of the TOKEN_GROUPS struct, returning None and Everyone, and then craps out complaining that "A Parameter is incorrect."
What would cause only two groups to work correctly?
The TOKEN_GROUPS struct indicates that there are 14 groups. I'm assuming it's the SID that is invalid.
Everything that I have PInvoked I have taken from an example on the PInvoke website. The only difference is that with the LookupAccountSid I have changed the Sid parameter from a byte[] to a IntPtr because SID_AND_ATTRIBUTESis also defined with an IntPtr. Is this ok since LookupAccountSid is defined with a PSID?
LookupAccountSid PInvoke
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool LookupAccountSid(
string lpSystemName,
IntPtr Sid,
StringBuilder lpName,
ref uint cchName,
StringBuilder ReferencedDomainName,
ref uint cchReferencedDomainName,
out SID_NAME_USE peUse);
Where the code falls over
for (int i = 0; i < usize; i++)
{
accountCount = 0;
domainCount = 0;
//Get Sizes
LookupAccountSid(null, tokenGroups.Groups[i].SID, null, ref accountCount, null,
ref domainCount, out snu);
accountName2.EnsureCapacity((int) accountCount);
domainName.EnsureCapacity((int) domainCount);
if (!LookupAccountSid(null, tokenGroups.Groups[i].SID, accountName2, ref accountCount, domainName,
ref domainCount, out snu))
{
//Finds its way here after 2 iterations
//But only in a different developers domain
var error = Marshal.GetLastWin32Error();
_log.InfoFormat("Failed to look up SID's account name. {0}", new Win32Exception(error).Message);
continue;
}
If more code is needed let me know. Any help would be greatly appreciated.
It sounds like you're trying to duplicate the functionality of NetUserGetLocalGroups. You can also use NetUserGetInfo with an information level of 1, and check the value of usri1_priv in the USER_INFO_1 for USER_PRIV_ADMIN.
I'm not sure if NetUserGetLocalGroups knows about deny SIDs (If you need to verify if the current process (not the user account!) is in the admin group, you have to handle deny SIDs)
If you only need to support 2000 and later, PInvoke CheckTokenMembership (That MSDN page has a IsUserAdmin example function)
On NT4 you need to get a TokenGroups array from GetTokenInformation, but you don't call LookupAccountSid, you just call EqualSid on every item and compare it to a admin group SID you create with AllocateAndInitializeSid(...,SECURITY_BUILTIN_DOMAIN_RID,...)
Related
I want to check which user profile is assigned to my machine whether it is Roaming or Local how can I check it using c# and is there any entry of it in registry.
Thank you in Advanced
I guess one way would be to call the win32 api function GetProfileType.
In order to use it you will have to declare it as a method and use the DllImport attribute like this:
[DllImport("Userenv.dll", EntryPoint = "GetProfileType", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool GetProfileType(out ProfileType dwflags);
As I have not seen a desciption of the flag values, you will have to test it yourself. As soon as you figured out which value is returned for which profile type, you might want to create an enumeration like the following one:
[Flags]
public enum ProfileType : uint
{
Local = 0x00,
Temporary = 0x01,
Roaming = 0x02,
Mandatory = 0x04
}
Usage:
ProfileType profileType;
if (!GetProfileType(ref profileType))
{
// Error handling here...
}
if (profileType.HasFlag(ProfileType.Local))
{
// We have a local profile...
}
For details about the different User profiles check the following links:
Local User Profiles
Mandatory User Profiles
Roaming User Profiles
Temporary User Profiles
Update: Oh... it seems someone has already done the work. I've updated my answer with the (presumably) correct values and marked the question as duplicate.
We have supported different options in our product to download emails including IMAP and POP3. Providing IMAP and POP3 support was very simple as there is lot of documentation and .Net libraries available. Now we need to support MAPI.
I am having trouble with finding good library in .Net, I found one called NetMAPI but couldn't find examples. Also I am not able to understand how we specify connection properties. e.g. IMAP we have different properties like email address, password, port , SSL enabled etc. Below are few questions.
Where do I mention these connection parameters when it comes to
MAPI? ( creating profile? )
Some example says we have to mention profile name. Does that means I have to have outlook installed and create profile using outlook?
I am looking at this example but not sure how below Logon method is working on my machine.
[DllImport( "MAPI32.DLL", CharSet=CharSet.Ansi)]
private static extern int MAPILogon(IntPtr hwnd, string prf, string pw, int flg, int rsv, ref IntPtr sess );
public bool Logon( IntPtr hwnd )
{
winhandle = hwnd;
error = MAPILogon( hwnd, null, null, 0, 0, ref session );
if( error != 0 )
error = MAPILogon( hwnd, null, null, MapiLogonUI, 0, ref session );
return error == 0;
}
I am able to connect to my mailbox which is configured in outlook but don't know from where its picking up my mailbox details. I tried entering random ( invalid ) values for prf and pw fields still I am able to connect to my mailbox.
Strange but true.
Can someone please refer me to some good documentation to understand how to use MAPI ( preferably using managed API? )
Thanks
I have an ASP.NET web application that stores a HTTP cookie when a certain action has been performed (e.g. a link has been clicked). Now, I am creating a standalone C# app which needs to watch the cookies folder and recognise when a cookie entry has been created by my web application and read the contents of the cookie.
Could anyone please guide me on how to do this in C# or show sample code?
I can't help thinking that is simply the wrong way to do it... and it reaks of security abuse. Is there no better way you could do this? Perhaps hosting the page in a WebBrowser control and using an ObjectForScripting object (of your devising) so you can talk to the C# app from javascript?
You should be able to PInvoke InternetGetCookie.
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
protected static extern bool InternetGetCookie(
string url,
string name,
StringBuilder cookieData,
ref int length);
You can then call InternetGetCookie like below assuming you are using the Rest Starter Kit.
StringBuilder cookieBuffer = new StringBuilder(1024);
int size = 1024;
bool bSuccess = InternetGetCookie("domain uri", "cookie_name", cookieBuffer, ref size);
if (!bSuccess)
{
Int32 err = Marshal.GetLastWin32Error();
//log err
}
if (cookieBuffer != null && (cookieBuffer.Length > 0))
{
Microsoft.Http.Headers.Cookie cookie = Microsoft.Http.Headers.Cookie.Parse(cookieBuffer.ToString());
HeaderValues<Microsoft.Http.Headers.Cookie> requestCookies = new HeaderValues<Microsoft.Http.Headers.Cookie>();
requestCookies.Add(cookie);
}
There is another thing you can do that could cause your local application to be invoked by the clicking of a link. You'd need to register an application to a URL protocol as seen here. Here is a tutorial and sample app you can look at.
Doing this has it's own set of security implications. However, it is an easy way to invoke your application from the web page and it allows you to pass data via command line params.
I try to get the full-name of the current log-in user (Fullname, not username).
The following code C#, C++ works fine but on XP computers not connected to the Net, I get empty string as result if I run it ~20 minutes after login (It runs OK whithin the first ~20 minutes after login)
A Win32 API (GetUserNameEx) is used rather that PrincipalContext since it PrincipalContext may takes up to 15 seconds when working offline.
Any Help why am I getting an empty string as result though a user full name is specified???
- C# Code
public static string CurrentUserFullName
{
get
{
const int EXTENDED_NAME_FORMAT_NAME_DISPLAY = 3;
StringBuilder userName = new StringBuilder(256);
uint length = (uint) userName.Capacity;
string ret;
if (GetUserNameEx(EXTENDED_NAME_FORMAT_NAME_DISPLAY, userName, ref length))
{
ret = userName.ToString();
}
else
{
int errorCode = Marshal.GetLastWin32Error();
throw new Win32Exception("GetUserNameEx Failed. Error code - " + errorCode);
}
return ret;
}
}
[DllImport("Secur32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool GetUserNameEx(int nameFormat, StringBuilder lpNameBuffer, ref uint lpnSize);
- Code in C++
#include "stdafx.h"
#include <windows.h>
#define SECURITY_WIN32
#include <Security.h>
#pragma comment( lib, "Secur32.lib" )
int _tmain(int argc, _TCHAR* argv[])
{
char szName[100];
ULONG nChars = sizeof( szName );
if ( GetUserNameEx( NameDisplay, szName, &nChars ) )
{
printf( "Name: %s\n", szName);
}
else
{
printf( "Failed to GetUserNameEx\n" );
printf( "%d\n", GetLastError() );
}
return 0;
}
The function GetUserNameEx with NameDisplay can't work in offline mode. This information is only accessible when the computer is online. I recommend you to implement some caching of information like full name or other which is accessible in online mode only. For example, if the computer is online you can retrieve and save information like Full User Name. So you can have in some your config-file of in registry a mapping between users SID and it's full name. If you don't able give full name directly you can get the information from your cash.
Windows has a lot of different notification (like NotifyAddrChange) which you can use (if needed) to monitor change from online to offline mode and back.
Most information which you can get about current user session (also in offline mode) you can get from LsaGetLogonSessionData and WTSQuerySessionInformation API (GetUserNameEx you already know), but you will not find full user name inside.
If you do find a way to get full name of user in offline mode please post the information to me.
Try to use GetUserNameExA (for ASCII) instead of GetUserNameEx macro. Does it help? Print also the output of the program.
I'm curious: for a 'permamently offline' station, where (in the OS) is the user name stored? By browsing control panel Users, it looks like local user accounts have no place to store a 'NameDisplay', there's only a user name.
Where that data is stored for a non-connected node would be a mystery to me. If (in fact) the data is only stored with the domain controller, the only thing I can think of is to cache the information as mentioned earlier.
So use the 1st one, check result and then invoke the 2nd via async delegate. Your app won't get any lag and full name is certainly not it's core feature - I hope :-)
Please try disabling the UAC on your machine. I guess it should work after that.
Steps to disable it.
Go to control panel.
Then to Users.
Select which admin account you wanted to retain.
Delete the account with admin privilege, which you do not wish to have.
If in future you want to create any new account with admin rights, you create an account and make it as an admin account using change account type.
I know that the following should work:
Environment.GetEnvironmentVariable("windir", EnvironmentVariableTarget.Machine)
My problem with this call is that if for some reason someone decided to remove the "windir" Env Var , this won't work.
Is there an even more secure way to get the System drive?
string windir = Environment.SystemDirectory; // C:\windows\system32
string windrive = Path.GetPathRoot(Environment.SystemDirectory); // C:\
Note: This property internally uses the GetSystemDirectory() Win32 API. It doesn't rely on environment variables.
This one returns the path to the system directory (system32).
Environment.GetFolderPath(Environment.SpecialFolder.System)
You may be able to use that, then you don't need to rely on environment variables.
One thing i actually maybe misunderstand is that you want the System Drive, but by using "windir" you'll get the windows folder. So if you need a secure way to get the windows folder, you should use the good old API function GetWindowsDirectory.
Here is the function prepared for C# usage. ;-)
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint GetWindowsDirectory(StringBuilder lpBuffer, uint uSize);
private string WindowsDirectory()
{
uint size = 0;
size = GetWindowsDirectory(null, size);
StringBuilder sb = new StringBuilder((int)size);
GetWindowsDirectory(sb, size);
return sb.ToString();
}
So if you really need the drive on which windows is running, you could afterwards call
System.IO.Path.GetPathRoot(WindowsDirectory());
You can use the GetWindowsDirectory API to retrieve the windows directory.
Never read environment variables (any script or user can change them !)
The official method (MS internal, used by Explorer) is a Win32 api FAQ for decades (see Google groups, Win32, System api)
Theres an environment variable called SystemDrive
C:\>SET SystemDrive
SystemDrive=C: