3/26/2012 update: I've now done this on CentOS 6.2 as well.
Since I've had to do this a number of times in the last few months, I thought I should post this here so I can't forget it.
Here's some specifics on what I'm using:
- Windows Server 2008 R2
- As dc1.devexample.com (192.168.0.201)
- As Primary Domain Controller of devexample.com
- As Windows Server 2008 R2 forest functional level
- CentOS 5 / CentOS 6
- As app01.devexample.com (192.168.0.203)
- Samba 3.5.6 / Samba 3.6.3
- An internet connection. If you are not going to have an internet connection, you'll want to pre-download the files you'll need. I suggest using 'yum downloadonly' to get them.
I've spun up a brand new CentOS 5.5 VM and logged in. Oh man, does anyone else just love logging into a fresh install? Is my nerd showing? Sorry!

The first thing I need to do is take on great responsibility, so for that I'm going to need great power: let's add my account to the sudoers list.
su -
EDITOR=nano visudo

That's better. Now we can stop acting as root.
exit
Now that I can tell the operating system what to do, let's update it.

While that downloads and installs, let's add a /sbin and /usr/sbin to your path. These directories have a lot of commands that need to be run by root. Since we're going to "sudo" a lot of these commands later, having them in our path will save us from having to fully qualify the path all the time (e.g /usr/sbin/something -arg -foo).
nano ~/.bash_profile

Now is also a good time to update our hostname and network settings. On your network at home, you probably just let DHCP assign you an IP address. In a kerberized network environment, though, you'll want to assign your computers a static IP address.
sudo nano /etc/sysconfig/network

and then create an entry for that hostname in your hosts file...
sudo nano /etc/hosts

And now, assuming that you're using eth0 as your ethernet device,
sudo nano /etc/sysconfig/network-scripts/ifcfg-eth0

Since you're configuring everything statically instead of relying on DHCP, you need to configure your DNS server(s) manually too.
sudo nano /etc/resolv.conf

Finally before we reboot, let's configure our network time protocol (ntp) client. KERBEROS WILL NOT WORK unless the computers involved (KDC, Server, Client) have synchronized clocks. Variances larger than 1-2 minutes will cause kerberos to fail.
Configuring NTP correctly is actually not as easy as it should be. I don't understand why "hey go sync your clock with that guy over there every so often" requires so much work, but it does. I found a good tutorial
over here, which I will now distill for you.
We're going to completely replace the existing configuration, so you should rename it to a backup file.
sudo mv /etc/ntp.conf /etc/ntp.orig
And then we can create our own
sudo nano /etc/ntp.conf

We're not done! Next, a separate ticker file must be created that lists the servers.
su -
awk '/^server/ {print $2}' /etc/ntp.conf | grep -v '127.127.1.0' > /etc/ntp/step-tickers
And then! We need to sync our clock and then configure the OS to sync every time it boots.
(assuming you're still running as root from the last step)
ntpdate -u dc1.devexample.com
chkconfig --level 345 ntpd on
Whew, all that just for clock synchronization! Now we can stop acting as root:
exit
Our server is now configured as app01.devexample.com, is set to have the IP address 192.168.0.203, and will sync its clock with the domain controller.
Let's do one last thing while your OS is updating: set firewall and SELinux policies. The first time I attempted to join a CentOS box to the domain, I pulled my hair out for a couple of days trying to debug this error: "bind failed on pipe socket /var/lib/samba/winbindd_privileged/pipe: Permission denied". I've only discovered one thing that will make this error go away: SELinux settings. You can either set the system SELinux policy from "Enforcing" to "Permissive", or you can keep it as "Enforcing" but turn off all samba protection. Up to you and your requirements, but for this blog post I'm going to set SELinux to "Permissive".
Open System > Administration > Security Level and Firewall and allow samba through the firewall on the "Firewall Options" tab.

Then go to the "SELinux" tab and change the setting to "Permissive" and apply the changes.

If you haven't installed an X server, you can just edit
/etc/selinux/config and set
SELINUX=permissive.
That's it for now: go have a coffee. Or a beer. When the update is finished, reboot. I'll wait right here for you. (P.S. - if you can't get to the internet when you come back, make sure your /etc/resolv.conf still has our custom entries in it!).
Welcome back!
We've already come a long way together and we haven't even begun to join our box to the domain, have we? For this reason, you might want to consider tools like
Centrify or
Likewise-Open. I've never used either of them myself, but I'm a fan of the group policy and auditing capability that they advertise. Even after you finish following all of my steps, you're still not going to have those two capabilities.
It's finally time to download and install samba. At
http://enterprisesamba.org, you'll find already-compiled binaries of the latest samba for CentOS 5.5 (and CentOS 6 too). These instructions assume you have a connection to the interwebs, so if you don't you'll need to pre-download these files and put them into the correct location yourself.
The enterprise samba site provides a yum repository so that you can install their binaries via the yum command line. Here's how you install the repository:
cd /etc/yum.repos.d
sudo wget http://ftp.sernet.de/pub/samba/3.5/centos/5/sernet-samba.repo # if centos 5
sudo wget http://ftp.sernet.de/pub/samba/3.6/centos/6/sernet-samba.repo # if centos 6
Now that you have the repository installed, you can install samba:
sudo yum install samba3 samba3-client samba3-winbind libsmbclient0 libwbclient0
yum will figure out all the dependencies, which you should accept.

Okay! Let's configure pam, nsswitch, samba, and kerberos! I'm going to use the built-in GUI that comes with CentOS 5.5, but I'll also post screenshots of the files that get updated by the GUI so you can edit the files manually if you aren't running X.
NEW CENTOS 6 INSTRUCTIONS!! If you are running CentOS 6 instead of CentOS 5, do not use the GUI as I just suggested. Instead, let's use the authconfig command-line tool to get everything correct.
sudo authconfig
--enableldap
--enablemkhomedir
--ldapserver=ldap://dc1.devexample.com:389
--ldapbasedn=dc=devexample,dc=com
--enablewinbind
--enablewinbindauth
--smbsecurity ads
--enablewinbindoffline
--smbservers=dc1.devexample.com
--smbworkgroup=DEVEXAMPLE
--smbrealm DEVEXAMPLE.COM
--winbindtemplateshell=/bin/bash
--disableldaptls
--enablekrb5
--krb5kdc=dc1.devexample.com
--krb5adminserver=dc1.devexample.com
--krb5realm=DEVEXAMPLE.COM
--enablekrb5kdcdns
--enablekrb5realmdns
--smbidmapuid=16777216-33554431
--smbidmapgid=16777216-33554431
--winbindseparator=+
--winbindtemplatehomedir=/home/%U
--winbindtemplateshell=/bin/bash
--enablewinbindusedefaultdomain
--enablewinbindoffline
--winbindjoin=Administrator
--disablesssd
--disablesssdauth
--enablepamaccess
--enablesysnetauth
--nostart
--update
This will ask you for the domain administrator's password and immediately join you to the domain. If you have your own domain administrator account that isn't 'Administrator', change --winbindjoin=Administrator to your account name. Only do this if your account is a domain admin.
This ought to have joined you successfully to the domain in which case you only need to add an entry into DNS on your Windows Domain Controller before you're completely done. See my instructions below on how to add a DNS entry.
END OF CENTOS 6 INSTRUCTIONS.
Open your Authentication Configuration GUI from System > Administration > Authentication.

Check "Enable Winbind Support" and then "Configure Winbind". Here are the settings I'm using - you'll want to replace them with your own, obviously. Don't click "Join Domain" - you have more configuring before you're ready to do that!

It's important that your winbind ADS realm is in ALL UPPERCASE. Notice that the winbind domain doesn't have .com after it - DEVEXAMPLE is the NetBios name of the domain, which is what winbind wants. Also notice that I've set Winbind Domain Controllers to a star (*). This will make winbind use DNS to lookup the domain controller for the devexample.com domain. This is recommended because if you set it to dc1.devexample.com and that server dies, your CentOS box won't automatically go to the backup domain controller. Also, if you want to do things like kerberize JBoss, you need winbind to use DNS to lookup the domain controller because otherwise Java will construct an incorrect service principal name when it tries to bind to LDAP.
Click OK and go to the Authentication tab. Check "Enable Kerberos Support" and then "Configure Kerberos...". Here's what my settings look like.

It's important that the REALM be in ALL UPPERCASE. Click OK. Now check "Enable Winbind Support" and "Configure Winbind...". Again, here are my settings. Again, don't click "Join Domain".

Click OK and then go to the options tab. Here's what I have for that tab:

Click OK to close out the GUI. We're almost done! The reason that we didn't click "Join Domain" at all was because we still have a little bit of configuration and cleanup to do before we attempt domain joining.
There's nothing that needs to be done in your /etc/nsswitch.conf file, but I'm including it here in case you are running a headless server and need to edit it yourself.
First, we need to add some parameters to the pam configuration file, /etc/pam.d/system-auth. Just a few lines up from the bottom of the file, find:
session optional pam_mkhomedir.so
and add the parameter skel=/etc/skel umask=0027
sudo nano /etc/pam.d/system-auth

What did you just do? When new domain users log onto your CentOS box for the first time, their home directory will be created and its contents will be copied from /etc/skel and permissions on the new user's home directory will be reasonable.
Next, let's clean up our kerberos configuration file. Even though you just used the GUI to configure kerberos for your domain, /etc/krb5.conf still has "fake" entries for example.com that we should remove. Look at the image below to see the final state of my krb5.conf file.

Finally, let's edit our samba/winbind configuration. Here's my final state:

These are the lines that I added or changed:
template homedir = /home/%U |
This tells the system to create home directories for new domain users in /home/[username]. This is nice and clean and lives side-by-side with local users. Because of this, if you're going to have local system users or if your domain forest has more than one domain in it, you'll want to set this to /home/%D/%U instead. That way, there will be no chance of name conflicts. None of those caveats apply to my example, so I used the simpler option (/home/%U). |
winbind enum users / groups |
This causes the 'getent passwd' and 'getent group' commands to include domain users and groups in its output. |
winbind separator |
You know how on windows your domain account can be represented as DOMAIN\user? Well, using a backslash as a separator can cause real problems in Linux, or at least headaches and confusion. So what I've done is configure the separator to be the plus symbol (+) - because it has far fewer issues in Linux (actually, I've never had any). Domain users will now log onto your CentOS box as DOMAIN+user. Any scripts that reference domain users or groups will also use DOMAIN+user. |
winbind use default domain = true |
When this is set to true, I can log into my CentOS box with domain accounts without having to prefix the username with DOMAIN+. If your users will be logging in from multiple domains in equal proportion, you probably don't want to set this to true. |
Now that we've configured samba and winbind, let's tell CentOS to start those services every time it boots.
sudo chkconfig --level 345 smb on
sudo chkconfig --level 345 winbind on
We are getting so close to the end! Before we join to the domain, let's configure our sudoers list so that Domain Admins can execute commands as root.
EDITOR=nano visudo

That's it for configuring your CentOS box for membership in an AD domain. You have one more thing to do before joining - create a DNS entry on your domain controller!
Log onto your Windows domain controller and open up your DNS Manager from Administrative Tools. First, you're going to create a reverse lookup zone (if one doesn't already exist), then you're going to add an A entry for your CentOS box.
If you don't have a reverse lookup zone, follow
these instructions to create one. When you're done, your DNS Manager ought to look like this:

Now, create an A record for your CentOS box:

IT'S TIME! (Unless you are using CentOS 6, in which case you've already joined to the domain. Skip the join command here, but definitely reboot even on CentOS 6)
Back on your CentOS box, join to the domain:
su -
net ads join -U Administrator osName="CentOS" osVer="5.5"

Reboot! Reboot! When your CentOS box comes back up, login as a domain user. GOGOGO!
While you're rebooting your CentOS machine, go back to your domain controller and notice that there's now an APP01 entry in your "Active Directory Users and Computers" MMC.

Once your CentOS box comes back up, login with a domain user account and see that it knows who you are and what groups you're a member of.

You have successfully joined CentOS to your Windows domain!