House of Derek

Thursday, August 19, 2010

IE8 Will not Display Developer Tools

IE8 has a really annoying bug where you click on the developer tools menu option but no windows appears. When you alt+tab you can see that a window was created but you can't view it because it is sized incorrectly and is off your screen. It seems like the bug is related to maximizing and then closing the developer tools window. Here's how I fixed it:

- click on developer tools
- Find the window on your task bar and right click .. choose mazimize. If you see the missing window, then you have the same problem.
- Right click on the task bar item again and choose restore -- your window will disappear again
- Right click on the task bar item and choose Move, then click the right arrow, then move your mouse and drop the tiny window into the middle of your screen.
- Now you can resize it the proper size. If this keeps happening you may want to choose the "pin" option in developer tools so it doesn't create a seperate window.

Hope this helps someone.

Friday, July 31, 2009

Problem using Fiddler to monitor SSL

For some reason, when I added my laptop to a new windows domain, Fiddler lost the ability to monitor SSL (HTTPS) requests. The basic symptoms were that when fiddler was running, I couldn't go to any SSL websites. In the fiddler logging screen, it showed a 443 connect but nothing else. Every so often, I would get a popup from MakeCert.exe that showed this error:

"Fail to acquire a security provider from the issuer's certificate"

I tried re-installing Fiddler and this didn't fix it. Finally, I deleted the fiddler root certificate from my certificate store, and the next time I started Fiddler, it worked. I'm assuming that the Fiddler root certificate got corrupted or part of it was missing and the uninstall/reinstall must not fix it. Here is how to remove the fiddler root certificate (windows XP).

1. Go to Start/Run and run "MMC"
2. Go to File..Add/Remove Snap-in
3. Click Add
4. Select Certificates and click Add
5. Selet My User Account and click finish
6. Close the dialogs and you will be in the certificates snap-in
7. Go to Certificates-Current User/Personal/Certificates
8. Right click on DO_NOT_TRUST_FiddlerRoot and delete it.

When you restart Fiddler and hit your first HTTPS site it will re-create the certificate and fiddler should work.

Friday, July 11, 2008

Password expiration email utility

I had trouble finding a free utility that would send employees emails before their windows passwords were ready to expire, so I wrote a C# console application that does it. Here is the source code:



using System;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.Configuration;
using ActiveDs;
using System.Collections;
using System.IO;
using System.Net.Mail;


namespace PasswordNotifcation
{
class Program
{
const int ADS_SCRIPT = 0x0001;
const int ADS_ACCOUNTDISABLE = 0x0002;
const int ADS_HOMEDIR_REQUIRED = 0x0008;
const int ADS_LOCKOUT = 0x0010;
const int ADS_PASSWD_NOTREQD = 0x0020;
const int ADS_PASSWD_CANT_CHANGE = 0x0040;
const int ADS_ENCRYPTED_TEXT_PWD_ALLOWED = 0x0080;
const int ADS_TEMP_DUPLICATE_ACCOUNT = 0x0100;
const int ADS_NORMAL_ACCOUNT = 0x0200;
const int ADS_INTERDOMAIN_TRUST_ACCOUNT = 0x0800;
const int ADS_WORKSTATION_TRUST_ACCOUNT = 0x1000;
const int ADS_SERVER_TRUST_ACCOUNT = 0x2000;
const int ADS_DONT_EXPIRE_PASSWORD = 0x10000;
const int ADS_MNS_LOGON_ACCOUNT = 0x20000;
const int ADS_SMARTCARD_REQUIRED = 0x40000;
const int ADS_TRUSTED_FOR_DELEGATION = 0x80000;
const int ADS_NOT_DELEGATED = 0x100000;
const int ADS_USE_DES_KEY_ONLY = 0x200000;
const int ADS_DONT_REQ_PREAUTH = 0x400000;
const int ADS_PASSWORD_EXPIRED = 0x800000;
const int ADS_TRUSTED_TO_AUTH_FOR_DELEGATION = 0x1000000;


public static void SendMailMessage(string ToAddress,string FromAddress, string MessageSubject, string MessageText, string ToCC, string ToBCC, string MailServer)
{
System.Net.Mail.MailMessage mail = new System.Net.Mail.MailMessage(FromAddress, ToAddress, MessageSubject, MessageText);

if(ToCC != string.Empty && ToCC != null)
{
mail.CC.Add(ToCC);
}
if(ToBCC != string.Empty && ToBCC != null)
{
mail.Bcc.Add(ToBCC);
}

System.Net.Mail.SmtpClient cli = new System.Net.Mail.SmtpClient();
cli.Host = MailServer;
cli.Send(mail);
}



static void Main(string[] args)
{
if (ArgFound("-?", args))
{
Console.WriteLine(@"PasswordNotification.exe - Sends emails to any users that have passwords that are ready to expire.");
Console.WriteLine(@"Command Line Example");
Console.WriteLine(@"PasswordNotification.exe");
Console.WriteLine(@"");
Console.WriteLine(@"This utility requires two files. 1) a config file called Settings.config that contains application settings, and 2)a set of email template files that conain the warning text of each email.");
return;

}

try
{


System.Xml.XmlDocument doc = new System.Xml.XmlDocument();

doc.Load(AppDomain.CurrentDomain.SetupInformation.ApplicationBase + @"\settings.config");

System.Xml.XmlNodeList nl = doc.SelectNodes("//Warning");



System.Xml.XmlNode nodtmp = doc.SelectSingleNode("//StartingOU");
string strStartingOU = nodtmp.Attributes["Value"].Value;


nodtmp = doc.SelectSingleNode("//SmtpServer");
string strSmtpServer = nodtmp.Attributes["Value"].Value;

nodtmp = doc.SelectSingleNode("//MailFrom");
string strEmailFrom = nodtmp.Attributes["Value"].Value;

TimeSpan tsMaxPasswordAge = GetMaxPasswordAge();
int iMaxPwdAge = tsMaxPasswordAge.Days;

DirectoryEntry objDirEnt = new DirectoryEntry(strStartingOU);

DirectorySearcher ds = new DirectorySearcher(objDirEnt);
SearchResultCollection src = null;

ds.Filter = "(objectClass=user)";

src = ds.FindAll();
if (src.Count > 0)
{
foreach (SearchResult sr in src)
{
DirectoryEntry UserEntry = sr.GetDirectoryEntry();



WriteLogMessage("Processing:" + UserEntry.Name);

WriteLogMessage("Max Password Age:" + iMaxPwdAge);

//Console.WriteLine("Account control:" + UserEntry.Properties["userAccountcontrol"].Value);

int userAccountControl = Convert.ToInt32(UserEntry.Properties["userAccountcontrol"].Value);
if ((userAccountControl & ADS_DONT_EXPIRE_PASSWORD) > 0)
{
WriteLogMessage("Password never expires for:" + UserEntry.Name);
}
else
{
WriteLogMessage("Password expiration date found for:" + UserEntry.Name);

ActiveDs.IADsUser native = (ActiveDs.IADsUser)UserEntry.NativeObject;

DateTime passwordLastChanged = new DateTime(9999, 1, 1);
try
{

passwordLastChanged = native.PasswordLastChanged;


}

catch { }
string strEmailTo = "";
try
{
strEmailTo = native.EmailAddress;
}
catch { };

WriteLogMessage("Password last changed date:" + passwordLastChanged.ToString());

if (passwordLastChanged.Year != 9999)
{
DateTime expireDate = passwordLastChanged.AddDays(iMaxPwdAge);

TimeSpan ts = expireDate - DateTime.Now;

int iDaysTilExpired = ts.Days;

WriteLogMessage("Days til password expires:" + iDaysTilExpired);


foreach (System.Xml.XmlNode nod in nl)
{
int iDays = Convert.ToInt32(nod.Attributes["Days"].Value);


if (iDays == iDaysTilExpired)
{

if (strEmailTo != "")
{

string strSubject = nod.Attributes["Subject"].Value;
string strTemplatePath = nod.Attributes["Template"].Value;


using (TextReader streamReader = new StreamReader(strTemplatePath))
{
string strContents = streamReader.ReadToEnd();

strContents = strContents.Replace("%%days%%", iDays.ToString());

Console.WriteLine("Sending email to:" + strEmailTo);

SendMailMessage(strEmailTo, strEmailFrom, strSubject, strContents, "", "", strSmtpServer);
WriteLogMessage("Email sent to:" + strEmailTo);

}
}
else
{
WriteLogMessage("Email address is blank so don't send");
}

}
}
}
else
{
WriteLogMessage("Password last changed date is not set.");

}
}
}
}
}
catch (Exception ex)
{
WriteLogMessage("Error -- Exception thrown:" + ex.ToString());
}

}

static public bool ArgFound(string strArg, string[] args)
{
foreach (string strTempArg in args)
{
if (strTempArg.ToLower().StartsWith(strArg.ToLower()))
{
return true;
}
}
return false;

}

static private void WriteLogMessage(string Message)
{
Console.WriteLine(Message);
System.IO.StreamWriter sw = new System.IO.StreamWriter(@"log.txt", true);
DateTime dtTimestamp = System.DateTime.Now;

string strLine = dtTimestamp.Year + "-" + dtTimestamp.Month + "-" + dtTimestamp.Day + " " + dtTimestamp.Hour + ":" + dtTimestamp.Minute + ":" + dtTimestamp.Second + "." + dtTimestamp.Millisecond + "\t";
strLine += Message;

sw.WriteLine(strLine);
sw.Flush();
sw.Close();

}


public static TimeSpan GetMaxPasswordAge()
{
using (Domain d = Domain.GetCurrentDomain())
using (DirectoryEntry domain = d.GetDirectoryEntry())
{
DirectorySearcher ds = new DirectorySearcher(
domain,
"(objectClass=*)",
null,
SearchScope.Base
);
SearchResult sr = ds.FindOne();
TimeSpan maxPwdAge = TimeSpan.MinValue;
if (sr.Properties.Contains("maxPwdAge"))
maxPwdAge = TimeSpan.FromTicks((long)sr.Properties["maxPwdAge"][0]);
return maxPwdAge.Duration();
}
}

}
}






The code also requires a config file called settings.config that looks like this. You will need to change it to match the Active directory OU where you want the utility to start searching for users, or you can change it to the root OU. You can add as many "warning" sections as you wish for different notification schemes. The example below will send a warning 10 days, 3 days, and 1 day before a password is ready to expire. You will also need to set up the settings for your SMTP server to send the email.




<?xml version="1.0" encoding="utf-8" ?>
<Settings>

<StartingOU Value="LDAP://123.123.123.12/ou=employees,dc=mydomain,dc=com"></StartingOU>
<SmtpServer Value="mail.mydomain.com"></SmtpServer>
<MailFrom Value="System@mydomain.com"></MailFrom>
<Warning Days="1" Subject="Your password will expire soon" Template="Template1.txt"></Warning>
<Warning Days="3" Subject="Your password will expire soon" Template="Template1.txt"></Warning>
<Warning Days="10" Subject="Your password will expire soon" Template="Template1.txt"></Warning>

</Settings>








The config file above also references a templatefile called "Template1.txt" for the text of your email. The template should look something like this:





Your windows password will expire in %%days%% days.

Please go to http://123.123.123.12/EmployeeTools to reset it.

Thank You





Set up your console application as a scheduled task that runs every day. Set up the scheduled task to have read access to the active directory container for your users.

Wednesday, January 9, 2008

Installing a new circuit

I recently installed a new 20 Amp Circuit for an "over the range" microwave in my house. Most of it was pretty simple. I had to buy a 20 Amp receptacle, some 12 gauge electrical wire (at lowes it's a yellow color), a new 20 amp circuit breaker, a wire clamp for the breaker box, and that's about it.

I used a Black and Decker book on electrical wiring which was really helpful. I only ran into one problem -- When I tried to turn off the main breaker for the entire house, it would flip back to the "on" position and the power would not go off. Eventually I realized that you have to use a lot of force to shut off the main breaker switch -- I guess because it's so big, it has a large spring inside.

When I had the panel open I also noticed a crackling noise coming from one of the breakers. I eventually realized that one of the older breakers was not tightly snapped in. After pushing it, it snapped into place and the crackling stopped.

This whole project took me about 4 hours including fishing the wire, cutting a hole for the receptacle, and hooking everything up.

Thursday, December 6, 2007

Programming Headache - Access Denied when calling SetPassword

Thought I'd post this as a public service after I spent a day trying to figure it out:

Scenario:
I was trying to create a windows domain user and set their password in ASP.net 2.0. I was using the DirectoryServices namespace to do this. The creation of the user worked fine using this code:

DirectoryEntry objDirEnt = new DirectoryEntry(@"LDAP://123.123.12.12/OU=Customers,DC=acme,DC=com",@"domainadminusername",@"domainadminpassword");
DirectoryEntry newDirectoryEntry = objDirEnt.Children.Add("CN=testuser", "user");
newDirectoryEntry.Properties["samAccountName"].Value = "testuser";
newDirectoryEntry.CommitChanges();

The "invoke" call to SetPassword would work when running through the Visual Studio debugger but would fail when running the published site via IIS:

newDirectoryEntry.Invoke("SetPassword", new object[] { "testpassword"});
newDirectoryEntry.CommitChanges();

The error happend on the Invoke call and the message was System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))

I tried testing this code under 1.1 and it worked fine so it seems to only be a 2.0 problem.

The security for my application was set up to use impersonation, so my web.config file had this setting (yeah, I know, it's bad to impersonate a domain admin, but I was just trying to get it to work):

<authentication mode="Windows"/>
<identity impersonate="true" userName="domainadminusername" password="domainadminpassword"/>

I googled this problem and a lot of people had the same issue, but there were no solutions offered that worked for me. After pulling out my hair for an entire day, I finally figured out the solution.

I had to remove the server from my ldap path when I did the LDAP bind to the container. So the new code looks like this:

DirectoryEntry objDirEnt = new DirectoryEntry(@"LDAP://OU=Customers,DC=acme,DC=com",@"domainadminusername",@"domainadminpassword");

After removing the server, everything worked fine. I think this happens because the underlying setpassword API's use the format of the ldap path to determine which type of password authentication to try. Maybe someone smarter than me can explain this. Here is a Microsoft article with a cryptic reference to the serverless bind: http://msdn2.microsoft.com/en-us/library/aa746344.aspx