I'm writing a C# program that will enforce password complexity in accordance with the Windows Group Policy setting "Password must meet complexity requirements". Specifically, if that policy is set to Enabled either on the local machine (if it's not part of a domain) or by the Domain Security Policy (for domain members), then my software needs to enforce a complex password for its own internal security.
The issue is that I can't figure out how to read that GPO setting. Google searches have indicated that I can read GPO settings with one of these two APIs: the System.DirectoryServices library in .NET Framework, and Windows Management Instrumentation (WMI), but I haven't had any success so far.
Any insights would be helpful.
There doesn't appear to be a documented API for this task, managed or otherwise.
Managed Attempt
I tried the managed route using the System.Management assembly:
ConnectionOptions options = new ConnectionOptions();
ManagementScope scope = new ManagementScope(#"\\.\root\RSOP\Computer", options);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, new ObjectQuery("SELECT * FROM RSOP_SecuritySettingBoolean"));
foreach(ManagementObject o in searcher.Get())
{
Console.WriteLine("Key Name: {0}", o["KeyName"]);
Console.WriteLine("Precedence: {0}", o["Precedence"]);
Console.WriteLine("Setting: {0}", o["Setting"]);
}
This however will not return results. It doesn't appear to be a permission issue as providing a username/password pair to ConnectionOptions results in an exception telling you that you can not specify a username when connecting locally.
Unmanaged Attempt
I looked at NetUserModalsGet. While this will return some information on password settings:
typedef struct _USER_MODALS_INFO_0 {
DWORD usrmod0_min_passwd_len;
DWORD usrmod0_max_passwd_age;
DWORD usrmod0_min_passwd_age;
DWORD usrmod0_force_logoff;
DWORD usrmod0_password_hist_len;
} USER_MODALS_INFO_0, *PUSER_MODALS_INFO_0, *LPUSER_MODALS_INFO_0;
..it will not let tell if the Password Complexity policy is enabled.
Tool Output Scraping 'Success'
So I resorted to parsing secedit.exe output.
public static bool PasswordComplexityPolicy()
{
var tempFile = Path.GetTempFileName();
Process p = new Process();
p.StartInfo.FileName = Environment.ExpandEnvironmentVariables(#"%SystemRoot%\system32\secedit.exe");
p.StartInfo.Arguments = String.Format(#"/export /cfg ""{0}"" /quiet", tempFile);
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.Start();
p.WaitForExit();
var file = IniFile.Load(tempFile);
IniSection systemAccess = null;
var passwordComplexityString = "";
var passwordComplexity = 0;
return file.Sections.TryGetValue("System Access", out systemAccess)
&& systemAccess.TryGetValue("PasswordComplexity", out passwordComplexityString)
&& Int32.TryParse(passwordComplexityString, out passwordComplexity)
&& passwordComplexity == 1;
}
Full code here: http://gist.github.com/421802
You can use the Resultant Set of Policy (RSOP) tools. E.g. here's a VBScript (lifted from here) which will tell you what you need to know. It should be simple enough to translate this into C#.
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\rsop\computer")
Set colItems = objWMIService.ExecQuery _
("Select * from RSOP_SecuritySettingBoolean")
For Each objItem in colItems
Wscript.Echo "Key Name: " & objItem.KeyName
Wscript.Echo "Precedence: " & objItem.Precedence
Wscript.Echo "Setting: " & objItem.Setting
Wscript.Echo
Next
I came across your this Microsoft forum answer http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/f3f5a61f-2ab9-459e-a1ee-c187465198e0
Hope this helps somebody who comes across this question in the future.
Related
I am working on Adobe Echo sign,I have downloaded the sample code from their website, I am using this sample code for sendingdocument, it has some code missing in sendDocument method so I have changed it. It's giving SoapHeader Exception,with nothing in InnerException,
{"apiActionId=XHZI4WF4BV693YS"}
below is my code of sending document
public static void sendDocument(string apiKey, string fileName, string recipient)
{
ES = new EchoSignDocumentService16();
FileStream file = File.OpenRead(fileName);
secure.echosign.com.FileInfo[] fileInfos = new secure.echosign.com.FileInfo[1];
fileInfos[0] = new secure.echosign.com.FileInfo(fileName, null, file);
SenderInfo senderInfo = null;
string[] recipients = new string[1];
recipients[0] = recipient;
DocumentCreationInfo documentInfo = new DocumentCreationInfo(
recipients,
"Test from SOAP: " + fileName,
"This is neat.",
fileInfos,
SignatureType.ESIGN,
SignatureFlow.SENDER_SIGNATURE_NOT_REQUIRED
);
DocumentKey[] documentKeys;
senderInfo = new SenderInfo(recipient, "password", "APIKEY");
documentKeys = ES.sendDocument(apiKey, senderInfo, documentInfo);
Console.WriteLine("Document key is: " + documentKeys[0].documentKey);
}
its giving exception on this line
documentKeys = ES.sendDocument(apiKey, senderInfo, documentInfo);
Can anyone suggest some sample code of Adobe Echo Sign?
On the account page of your login there is an API log you can check. If you check the log entry for your request you may find more information there.
I can't see anything immediately wrong with your code however the EchoSign API guide says that the 'tos' field is deprecated and that the recipients field should be used instead. Helpfully this means you can't use the paramaterised constructor. Try creating your document creation info as such (this is C# but if you need Java it should be straightforward to figure out):
RecipientInfo[] recipientInfo = new RecipientInfo[1];
recipientInfo[0] = new RecipientInfo
{
email = "recipient",
role = RecipientRole.SIGNER,
roleSpecified = true
};
DocumentCreationInfo documentCreationInfo = new DocumentCreationInfo
{
recipients = recipientInfo,
name = "Test from SOAP: " + fileName,
message = "This is neat.",
fileInfos = fileInfos,
signatureType = SignatureType.ESIGN,
signatureFlow = SignatureFlow.SENDER_SIGNATURE_NOT_REQUIRED
};
Note that when using the recipientInfo array it seems that the roleSpecified field must be set to true. This little field tripped me up for ages and I was receiving errors similar to yours.
After having a look around the web, I have found the following posts (below) which are quite similar to my problem. However, after trying the solutions, I am still stuck.
VSTO Frustration
Setting cached variables in VSTO 3.0
Basically, I want to populate some data in a Excel file on a web server before I send it to the client.
Here is my code in the Workbook:
namespace Inno.Data.Excel
{
public partial class ThisWorkbook
{
[Cached]
public DataSet Config = new DataSet();
private void ThisWorkbook_Startup(object sender, System.EventArgs e)
{
//InitializeCachedData();
var baseUrl = (string)Config.Tables["Config"].Rows[0].ItemArray[0];
var streamIds = (string)Config.Tables["Config"].Rows[0].ItemArray[1];
MessageBox.Show(baseUrl + " " + streamIds);
}
}
}
and on the server side I have the following:
let rootUrl = Uri(x.Request.Url.GetLeftPart(UriPartial.Authority))
let setUpData (report : Report) (sd : ServerDocument) =
let url = rootUrl.AbsoluteUri
let streamIds = String.Join(",", report.series |> Seq.map(fun s -> s.id))
let dt = new System.Data.DataTable("Config")
dt.Columns.Add("BaseUrl", typeof<String>) |> ignore
dt.Columns.Add("StreamIds", typeof<String>) |> ignore
dt.Rows.Add([|box url; box streamIds|]) |> ignore
dt.AcceptChanges()
let cache = sd.CachedData.HostItems.["Inno.Data.Excel.ThisWorkbook"]
let urlItem = cache.CachedData.["Config"]
urlItem.SerializeDataInstance(dt)
sd.Save()
sd.Close()
let initialiseDocument (report : Report) (path : string) =
let fileName = report.name.Replace(" ", "_") + ".xlsx"
let sd = (new ServerDocument(path))
sd |> setUpData report
fileName
let docPath = x.Request.MapPath(Path.Combine(liveReportPath, "Inno.Data.Excel.xlsx"))
let fileName = initialiseDocument report docPath
x.File(File.OpenRead(docPath), "application/vnd.ms-excel", fileName) :> ActionResult
However, when I go to open the file after it has downloaded, I get the following error:
Microsoft.VisualStudio.Tools.Applications.Runtime.CannotFindObjectToFillException: Cannot find any public instance member with ID Config in object Inno.Data.Excel.ThisWorkbook.
Things I have tried:-
Using a simple string rather than a DataSet
Calling StartCaching("Config")
Manipulating the ServerDocument in memory using the byte[] file overload
Copying the original Excel file and operating on the copy
But now I'm out of ideas. I see that a fair number of people have had this error, and as is pointed out in this post, there was a known bug in VSTO 3.0 with manipulating files in memory.
Thanks in advance.
I have a Classic ASP page that creates a CDO.Message object to send email. The code works on Window Server 2003 but not 2008. On 2008 an "Access is denied" error gets thrown. Here is a simple test page I wrote to diagnose the problem. How can I get this to work on Windows Server 2008?
dim myMail
Set myMail=CreateObject("CDO.Message")
If Err.Number <> 0 Then
Response.Write ("Error Occurred: ")
Response.Write (Err.Description)
Else
Response.Write ("CDO.Message was created")
myMail.Subject="Sending email with CDO"
myMail.From="sender#mycompany.com"
myMail.To="recipient#mycompany.com"
myMail.TextBody="This is a message."
myMail.Send
set myMail=nothing
End If
I never got the CDO.Message object to work on Windows Server 2008. However, I found a workaround. I wrote an email class that works on Windows Server 2008. Hope this helps someone else.
[ComVisible(true)]
public class Email
{
public bool SendEmail(string strTo, string strFrom , string strSubject, string strBody)
{
bool result = false;
try
{
MailMessage message = new MailMessage();
SmtpClient client = new SmtpClient("smtp.mycompany.com");
List<string> to = recipientList(strTo);
foreach (string item in to)
{
message.To.Add(new MailAddress(item));
}
message.From = new MailAddress(strFrom);
message.Subject = strSubject;
message.Body = strBody;
client.Send(message);
result = true;
}
catch
{
result = false;
throw;
}
return result;
}
private List<string> recipientList(string strTo)
{
List<string> result = new List<string>();
string[] emailAddresses = strTo.Split(new Char[]{',',';'});
foreach (string email in emailAddresses)
{
result.Add(email.Trim());
}
return result;
}
}
As long as you're using the Microsoft SMTP server(1) you can use the IIS Metabase Explorer to give the IIS_USRS group(2) read read access to the /LM/SmtpSvc/ and /LM/SmtpSvc/1/ nodes in the IIS Metabase.
Unfortunately this solution doesn't apply to Windows 7. Microsoft does not ship an SMTP server with Windows 7, making it very difficult to get around this problem without refactoring your code.
(1) See http://www.itsolutionskb.com/2008/11/installing-and-configuring-windows-server-2008-smtp-server
(2) See http://blogs.msdn.com/b/akashb/archive/2010/05/24/error-cdo-message-1-0x80040220-the-quot-sendusing-quot-configuration-value-is-invalid-on-iis-7-5.aspx
I need to export data into an Access database. My code works, but it works with the assumption the client machine has the Microsoft.Jet.OLEDB.4.0 as a valid provider.
I want to test to see if this is true or not, in code. My problem is that I don't have the location of an existing access database and I don't want to create a new .mdb that I'd use to verify the connection and then delete it.
Is there any way to tell which providers are installed?
You could simply check for the existence of
HKEY_CLASSES_ROOT\CLSID\{dee35070-506b-11cf-b1aa-00aa00b8de95}
which is the CLSID of Microsoft.Jet.OLEDB.4.0.
you could try to detect the MDAC version on the machine and based on that extrapolate if your provider is supported?
http://www.planetsourcecode.com/vb/scripts/ShowCode.asp?txtCodeId=47262&lngWId=1
here's a snippet you can take a look at.
Each major provider has classid mentioned under the registry editor
Ex:-
HKEY_CLASSES_ROOT\CLSID{dee35070-506b-11cf-b1aa-00aa00b8de95}
which is the CLSID of Microsoft.Jet.OLEDB.4.0.
To check programmatically, use below c# code, its checked on framework 2.0
using System.Data.OleDb;
OleDbEnumerator enumerator = new OleDbEnumerator();
DataTable table = enumerator.GetElements();
bool bNameFound = false;
bool bCLSIDFound = false;
foreach (DataRow row in table.Rows)
{
foreach (DataColumn col in table.Columns)
{
if ((col.ColumnName.Contains("SOURCES_CLSID")) && (row[col].ToString().Contains("{dee35070-506b-11cf-b1aa-00aa00b8de95}")))
Console.WriteLine("CLSID of Microsoft.Jet.OLEDB.4.0. Found");
if ((col.ColumnName.Contains("SOURCES_NAME")) && (row[col].ToString().Contains("Microsoft.ACE.OLEDB.12.0")))
{
bNameFound = true;
if ((col.ColumnName.Contains("SOURCES_CLSID")) && (row[col].ToString().Contains("{3BE786A0-0366-4F5C-9434-25CF162E475E}")))
bCLSIDFound = true;
}
}
}
if (!bNameFound && !bCLSIDFound)
Console.WriteLine("Microsoft.ACE.OLEDB.12.0 Not found");
else
Console.WriteLine("Microsoft.ACE.OLEDB.12.0 found");
Remember "Fix it right & not let the test bugs bite"
I believe if you have the .NET Framework installed (needed to run VB.NET Code) then the machine has the provider you mention. MSDN
Dim reader As Object = OleDbEnumerator.GetRootEnumerator()
Dim Oleprovide As String = ""
While reader.Read
For i = 0 To reader.FieldCount - 1
If reader.GetName(i) = "SOURCES_NAME" Then
If reader.GetValue(i).ToString.Contains(".OLEDB.") = True Then
Oleprovide = reader.GetValue(i).ToString
Exit For
End If
End If
Next
End While
reader.Close()
Dim MyConnection As OleDbConnection
MyConnection = New OleDbConnection("Provider=" & Oleprovide & ";Data Source=" & existingFile.FullName & ";Extended Properties=""Excel 13.0 Xml;HDR=Yes""")
MyConnection.Open()
In ClearCase, you can list the content of a directory using "cleartool ls".
My question is how can I do the same thing using CAL (ClearCase Automation Layer). The reason I prefer the COM API is because I won't have to parse the output of "ls".
So far, I am able to get the VOB and the View successfully, but I didn't find any method for listing the content.
My code so far:
IClearCase cc = new ApplicationClass();
CCVOB vob = cc.get_VOB("\\VOB-name");
CCView view = cc.get_View("ViewTag");
Thank you for your help.
I wrote VonC's answer in C# for those interrested.
string[] files = Directory.GetFiles("View path here", "*.*", SearchOption.AllDirectories);
foreach (string file in files)
{
try
{
CCVersion ver = cc.get_Version(file);
Console.WriteLine(ver.Path);
}
catch(Exception) {/*the file is not versioned*/}
}
May be this is a good start:
Set CC = Wscript.CreateObject("ClearCase.Application")
Set DirVer = CC.Version(".")
Set FSO = CreateObject("Scripting.FileSystemObject")
Set Folder = FSO.GetFolder(DirVer.Path)
Wscript.Echo "Files under source control: "
For Each File in Folder.Files
On Error Resume Next
Set Ver = CC.Version(File.Name)
If Err.Number = 0 Then
Wscript.Echo Ver.ExtendedPath
End If
Next
The idea being to use ICCVersion methods to try accessing the version of a file. If it does not return an error, it is indeed a versioned file.
Now I know the file is versioned, how can I remove it (rmname).
Do not use RemoveVersion():
Removes irretrievably the version (equivalent to cleartool rmver)
WARNING! This is a potentially destructive operation. Because CAL does not prompt the user for input under any circumstances, there is no confirmation step when RemoveVersion is invoked. Invoking RemoveVersion is equivalent to running cleartool rmver with the -force option.
Instead use the RemoveName from the ICCElement interface.