July 18, 2009

Sunny days are nice

Sometimes (often) it's nice to just sit out in the sun all day and do nothing!

July 17, 2009

Vista/Windows 7 SMTP Server

I am doing most of my coding lately on my new Windows 7 box and last night I came across a problem sending mail. I looked and looked but saw no SMTP server! It seems that Microsoft has stopped including an SMTP server with Windows 7 (and it seems vista as well). I wanted something very simple and lightweight and something that did not require much configuration on my part. Below is a list of what I found online and my thoughts on each:
  • XMail: Way more than I needed but a great one
  • Hmail: For some reason I could not get it to work for me
  • Gmail SMTP Service: I guess I could have done this...but did not even try
  • SoftStack: See below
  • Postcast: The winner (see below)
The one that I saw most people using and seemed easy enough was SoftStack. A simple install and was lightweight, but it had a fatal flaw! It seems that it can only send 10 emails per day! I needed something else, and I found it in PostCast. They have a free edition which was simple and lightweight and yet gave some advanced options if you needed it. It worked perfectly for me and is now my new SMTP server for Windows 7.

Having said all this, I don't get why Microsoft stopped including SMTP!

July 16, 2009

A better Ldap Error Message

Yesterday, I showed a simple way to do Ldap login using C# for your .NET application. To expand on that a drop, I wanted to show how exactly you use that class with a custom formatted and cleaned up Error Message.

First, here is how you use the class from yesterday (pseudocode):
string user = //passed in from somewhere;
string password = //passed in from somewhere;
string domain = //get from config or passed in from somewhere;

try {
// Note: The server is not really needed in our original class...I added it just in case
LdapLogin.VerifyCredentials(adSettings.Server, user, domain, password);

//return success
}
catch {LdapException ex) {
LdapErrorMessage error = new LdapErrorMessage(ex);
//return string.Format("Error. {0} {1} {2}", ex.Message, error.Description, ex.ServerErrorMessage)
Let's focus on the LdapErrorMessage part in the catch. The typical LdapException returns things that may not be exactly what you want to see or even what the error really is. Reading the hex code that is returned back, you can actually get a better error message like "User not found" or "Not permitted to logon at this time.". Here is the class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices.Protocols;
using System.Text.RegularExpressions;
using System.Globalization;

namespace JLFramework
{
public class LdapErrorMessage
{
public string Message { get; set; }
public int Code { get; set; }
public string LdapErr { get; set; }
public string Comment { get; set; }
public int Data { get; set; }
public string Version { get; set; }

/// 80090308: LdapErr: DSID-0C09030B, comment: AcceptSecurityContext error, data 525, v893
/// 80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data 0, vece
public LdapErrorMessage(LdapException ex)
{
if (string.IsNullOrEmpty(ex.ServerErrorMessage))
return;

this.Message = ex.ServerErrorMessage;

string pattern = @"(.*): LdapErr: (.*), comment: (.*), data (.*), (.*)";
Match match = Regex.Match(ex.ServerErrorMessage, pattern, RegexOptions.IgnoreCase);

if (match.Success)
{
this.Code = ParseHex(match.Groups[1].Value);
this.LdapErr = match.Groups[2].Value;
this.Comment = match.Groups[3].Value;
this.Data = ParseHex(match.Groups[4].Value);
this.Version = match.Groups[5].Value;
}
}

public string Description
{
get
{
// http://wiki.caballe.cat/index.php/Active_Directory_LDAP_Errros
switch (this.Data)
{
case 0x525: return "User not found.";
case 0x52e: return "Invalid credentials.";
case 0x530: return "Not permitted to logon at this time.";
case 0x532: return "Password expired.";
case 0x533: return "Account disabled.";
case 0x701: return "Account expired.";
case 0x773: return "User must reset password.";

default:
return "";
}
}
}

private int ParseHex(string s)
{
int n;
if (int.TryParse(s, NumberStyles.HexNumber, null, out n))
return n;
else
return 0;
}
}
}

One note though from a security stand-point you actually probably don't want to do this for external or perhaps even internal sites. You never want to let users know the actual error since hackers can deduce from there a way to get in. For example by saying "Account expired", I know that that the user and perhaps even password is correct on a brute force attack. Instead, I should give a catch all exception of "UserName/Password combination failed". However, from a testing perspective this is always helpful.

July 15, 2009

C# Ldap Login Class

In an earlier post I mentioned that when it comes to Win32 vs Ldap speed for login, Win32 is the clear winner. However, if you don't feel comfortable using Win32 (Interop etc.) and you also don't want to use the built in provider, then this is the class for you.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices.Protocols;
using System.Net;

namespace SecurityFramework
{
public class LdapLogin
{
public static void VerifyCredentials(string ldapServer, string userName, string domain, string password)
{
if (string.IsNullOrEmpty(ldapServer))
ldapServer = null;

bool useSSL = false;
NetworkCredential credentials = new NetworkCredential(userName, password, domain);

LdapConnection authConnect = new LdapConnection(
new LdapDirectoryIdentifier(ldapServer),
null,
AuthType.Basic
);

authConnect.SessionOptions.SecureSocketLayer = useSSL;
authConnect.SessionOptions.ProtocolVersion = 3;

try
{
authConnect.Bind(credentials);
}
catch (LdapException ex)
{
throw ex;
}
}
}
}

July 14, 2009

Advanced Debugging in Powershell

Great post can be found on the powershell blog.

July 13, 2009

Search File Properties of Office Document

I got a request to find a way to search the properties of office documents on a network drive that was created or modified by a specific user and copy that to a new location. I searched the web and did not really find anything that stood out until I came across a codeproject article that does something like what I needed, but it did not filter for one specific user or get all office doc extensions. I figured that someone else would probably need need this....you can find the code here.

I am not sure exactly why this was needed, but the idea was find the files created or authored by this user and then copy them from sourceA to sourceB. When the copy was done, it was also not to be modified in terms of date/time stamps. The code for that was actually easy:
private void CopyFile(string file, string target)
{
string targetFile = GetTargetPath(file, target);
Directory.CreateDirectory(Path.GetDirectoryName(targetFile));

File.Copy(file, targetFile, true);

File.SetCreationTime(targetFile, File.GetCreationTime(file));
File.SetLastAccessTime(targetFile, File.GetLastAccessTime(file));
File.SetLastWriteTime(targetFile, File.GetLastWriteTime(file));
File.SetLastAccessTime(targetFile, File.GetLastAccessTime(file));
}
The hard part was searching the properties...

July 12, 2009

Powershell XBox!

This is great!