Highly technical post ahead. If you are a friend/family member reading this post, skip it, not worth your time. If you’re an IT professional running Mac OS X Server 10.6, using OS X’s RADIUS service + Open Directory to provide authentication for your network, and are experiencing an issue where OD users who have the “Password must be reset on next login” checkbox checked cannot authenticate on wireless machines, this post will be very interesting to you.
Also, I should note up front: I was never able to get a working solution to this problem. However, if you're technically minded and have command line administration experience, perhaps you can take my work and bring it to fruition. If so, please add a comment with your solution!
Introduction
Minnehaha (high school where I work) recently implemented an enterprise-class secure wireless network, using Aerohive access points, tied to RADIUS running on Mac OS X Server 10.6, integrated with Open Directory. The idea being: rather than our wifi network being wide open, rather than requiring users to authenticate each time they open a web page, or rather than installing a buggy authentication daemon on school-owned laptops, users would instead log in to the wifi network itself, using their own personal username and password (as opposed to a single shared password approach, which would create an even larger maintenance nightmare than the wide-open network).
The problem
RADIUS + OD work as expected for normal users. However, users whose passwords must be changed at next login (aka, ALL of the students at the beginning of the school year, because we reset their passwords over summer), could not log in to the wireless network until they had changed their passwords.
It’s a catch-22: the laptop cannot authenticate to the wifi until the user’s password has been changed, and the user cannot change their password until the login window is allowed to access the Open Directory (which requires being on the wifi).
Logging in to a wired machine worked as expected: the OS X login window prompted the user to change their password on first login. Subsequent logins for that user on wireless machines then worked as expected, because the checkbox requiring the user to change their password was no longer checked. (you can Google for directions on setting up a login window profile for 802.1X authentication on Mac OS X; there are numerous resources on the web that walk through those steps, so that will not be discussed here).
Desired result
Rather than requiring users to sign in to a wired lab machine before using the wireless network, we wanted to grant them access to the wifi, even if while the “change password” box checked.
Theoretically this opens a situation in which the student could bring their own computer on campus and never change their password, and theoretically someone outside of the school could figure out the student’s default password, but this was deemed a low risk compared to the hassle of requiring all users to find a wired workstation at the beginning of each school year. If and when that situation arose, we could deal with it on a case-by-case basis.
The cause
RADIUS denies access because of two lines in its source code (
freeradius
, the RADIUS implementation packaged with and used by OS X Server, is open source and available for download from http://www.opensource.apple.com/release/mac-os-x-1068/)/src/modules/rlm_opendirectory/rlm_opendirectory.c switch(odResult) { case eDSNoErr: ret = RLM_MODULE_OK; break; case eDSAuthUnknownUser: case eDSAuthInvalidUserName: case eDSAuthNewPasswordRequired: case eDSAuthPasswordExpired: case eDSAuthAccountDisabled: case eDSAuthAccountExpired: case eDSAuthAccountInactive: case eDSAuthInvalidLogonHours: case eDSAuthInvalidComputer: ret = RLM_MODULE_USERLOCK; break; default: ret = RLM_MODULE_REJECT; break; }
Courtesy of http://www.mentby.com/Group/freeradius-users/password-policy-expired-password-mschap.html, those two lines in bold need to move right under the eDSNoError case, so they return an authentication OK:
switch(odResult) { case eDSNoErr: case eDSAuthNewPasswordRequired: case eDSAuthPasswordExpired: ret = RLM_MODULE_OK; break; case eDSAuthUnknownUser: case eDSAuthInvalidUserName: case eDSAuthAccountDisabled: case eDSAuthAccountExpired: case eDSAuthAccountInactive: case eDSAuthInvalidLogonHours: case eDSAuthInvalidComputer: ret = RLM_MODULE_USERLOCK; break; default: ret = RLM_MODULE_REJECT; break; }
The solution I attempted, but eventually could not get to work
First, on your Mac OS X 10.6 Server, enable the root user and change his password to something you'll know: http://support.apple.com/kb/HT1528. You will be using this password frequently.
Open a Terminal window. (if you're not sure what that means or where to find Terminal, then please stop reading now; if you go further you WILL end up breaking your server)
Type "
su -
" to switch users to root. Type your root password.
WARNING: Seriously, if you're not comfortable using the command line, stop now, because you will break something. Not my fault if you rm -rf /
your server's hard disk.
First I tried downloading the
freeradius
source from Apple: http://www.opensource.apple.com/release/mac-os-x-1068/. Untar (fine to double-click in Finder if you'd prefer), and make the two line code change from above.I tried configuring at this point (using the command from
freeradius
's /doc/MACOSX
, but ran into dependency problems, needed something called libiodbc
. Google led me here: http://www.iodbc.org/dataspace/iodbc/wiki/iODBC/ODBCMacOSX. Download, ./configure
, and make install
that. (I encountered no build errors. It is worth noting: you need the Xcode environment installed in order to compile).The other issue I encountered was something related to
libtool
, but this site suggested a workaround: http://fedoraproject.org/wiki/Libtool2#old_libtool_files_may.2Fwill_cause_problems.All told, my configure command for
freeradius
looked like this:root# ./configure --disable-shared --with-iodbc-lib-dir=/usr/local/lib --enable-developer --with-system-libtool
And now, a couple weeks later, I don't remember if that actually worked or not, but after trying that I decided to go a different direction: MacPorts. Maybe I decided this because I was worried the
make install
would overwrite my existing /usr/sbin/radiusd
and I didn't want to irreparably break the currently-mostly-working version.Useful information:
Thefreeradius
daemon is calledradiusd
and lives in/usr/sbin
, along with its friends,radwatch
,radmin
,rc.radiusd
, and a few others. The configuration files are in/etc/raddb
. You need to be root anytime you edit these files.
Moving forward with MacPorts
Download MacPorts from http://www.macports.org and install.
As root ("
su -
"), enter "/opt/local/bin/port fetch freeradius
" (or edit root's .profile
to include /opt/local/bin
in your $PATH
, so you can type just "port fetch freeradius
").Once it's been fetched, create two files in
/opt/local/var/macports/sources/rsync.macports.org/release/ports/sysutils/freeradius/files
patch-configure.in.diff --- configure.in.orig 2009-12-30 09:44:35.000000000 -0600 +++ configure.in 2010-04-05 01:23:46.000000000 -0500 @@ -92,7 +92,7 @@ dnl use system-wide libtool, if it exists AC_ARG_WITH(system-libtool, [ --with-system-libtool Use the libtool installed in your system (default=use our own)], -[ AC_PATH_PROG(LIBTOOL, libtool,,$PATH:/usr/local/bin) ], +[ AC_PATH_PROG(LIBTOOL, glibtool,,$PATH:/usr/local/bin) ], [ LIBTOOL="`pwd`/libtool" AC_SUBST(LIBTOOL)
rlm_opendirectory.diff --- src/modules/rlm_opendirectory/rlm_opendirectory.c.orig 2010-05-24 00:40:58.000000000 -0500 +++ src/modules/rlm_opendirectory/rlm_opendirectory.c 2012-08-24 20:36:53.000000000 -0500 @@ -308,13 +308,13 @@ switch(odResult) { case eDSNoErr: + case eDSAuthNewPasswordRequired: + case eDSAuthPasswordExpired: ret = RLM_MODULE_OK; break; case eDSAuthUnknownUser: case eDSAuthInvalidUserName: - case eDSAuthNewPasswordRequired: - case eDSAuthPasswordExpired: case eDSAuthAccountDisabled: case eDSAuthAccountExpired: case eDSAuthAccountInactive:
These files are unified diffs, and can be created on your own by duplicating the file you want to modify, modifying it, then running this command:
root# diff -u modified-freeradius/src/modules/rlm_opendirectory/rlm_opendirectory.c original-freeradius/src/modules/rlm_opendirectory/rlm_opendirectory.c > rlm_opendirectory.diff
Note: if you run that command instead of copy/pasting the file contents I provided above, note that you will need to modify the file generated by diff, so the paths are relative, not absolute. Example:
--- /absolute/path/src/modules/rlm_opendirectory/rlm_opendirectory.c.orig 2010-05-24 00:40:58.000000000 -0500
becomes
--- src/modules/rlm_opendirectory/rlm_opendirectory.c.orig 2010-05-24 00:40:58.000000000 -0500
Now edit the
patchfiles
value in /opt/local/var/macports/sources/rsync.macports.org/release/ports/sysutils/freeradius/Portfile patchfiles patch-configure.in.diff \ rlm_opendirectory.diff
Note: A less elegant and less-effectual way to accomplish this (aka, what I tried first), is to download the source, make the changes, re-tar and bz2, generate new hashes (shasum -a 256
andopenssl dgst -ripemd160
) and put those in the Portfile. But that didn't work, because MacPorts would figure out the new bz2 archive didn't match the repository, and would re-download and overwrite it. The unified diff and patchfiles are the correct way to go.
I ran into an issue after configuring, again a libtool issue. https://trac.macports.org/ticket/12961 suggested adding a "
--tag=junk
" into the LIBTOOL
portion of the Make.inc
file. At first I tried making a unified diff and applying it to the Make.inc
file after I ran configure, but that didn't work. In the end, the solution is to add this to the Portfile
:
post-configure { reinplace "|/opt/local/bin/glibtool|/opt/local/bin/glibtool --tag=junk|" ${worksrcpath}/Make.inc }
This was a frustrating learning experience, due to a near-complete lack of documentation or useful Google results about this syntax. But, what I have there, works. It acts as a simple find/replace: find this normal glibtool path, and change it to have junk included. Refer to the link above for why this counter-intuitive change makes things work.
Installing freeradius
Finally! Let's try installing.
root# port install freeradius
Note: if you tried installing, then went back to correct a mistake in the portfile/diffs/etc, I found you must port clean freeradius
before trying to compile/install again.
Copy some files into /opt/local/
, because that's where the new version of radiusd
will look for them:root# cp -pr /etc/raddb /opt/local/etc root# mkdir /opt/local/var/run/radiusd
Shut down the real radius, then try running the new radiusd in debug mode (the -X flag):
root# serveradmin stop radius root# cd /opt/local/sbin root# ./radiusd -X
When I tried that, mine failed with this error:
Could not link driver rlm_sql_sqlite: file not found Make sure it (and all its dependent libraries!) are in the search path of your system's ld. /opt/local/etc/raddb/sql.conf[22]: Instantiation failed for module "sql"
Googled it, found this link: http://wiki.freeradius.org/FAQ#It-says-%22Could-not-link-...-file-not-found%22%2C-what-do-I-do%3F. Also found this page, which may or may not be useful: http://support.apple.com/kb/TA25017
Installing MySQL
Download the source from Apple: http://www.opensource.apple.com/release/mac-os-x-1068/, which links to http://www.opensource.apple.com/source/MySQL/MySQL-55/.
Compile with this command from http://hivelogic.com/articles/compiling-mysql-on-snow-leopard/ (plus a few things I figured out on my own):
root# ./configure --prefix=/usr/local/mysql --with-extra-charsets=complex \ --enable-thread-safe-client --enable-local-infile --enable-shared --with-plugins=innobase \ --with-mysql-include-dir=/usr/local/mysql_new/include/mysql \ --with-mysql-lib-dir=/usr/local/mysql_new/lib/mysql
Re-configure and install freeradius
Use this suggestion to add configure arguments to the Portfile:
http://superuser.com/questions/306550/how-do-i-install-php5-via-macports-with-a-custom-configure-option
Finished version looks like this:
configure.args --with-openssl-includes=${prefix}/include/openssl \ --with-openssl-libraries=${prefix}/lib \ --with-system-libtool \ --without-rlm_krb5 \ --with-mysql-include-dir=/usr/local/mysql_new/include/mysql \ --with-mysql-lib-dir=/usr/local/mysql_new/lib/mysql
This time it should launch successfully:
root# port install freeradius root# cd /opt/local/sbin root# ./radiusd -X
Yay! But now authentication wasn't working for any users. The output (and log file) showed many many errors like these:
Sat Aug 11 11:29:13 2012 : Error: Ignoring request to accounting address * port 1813 from unknown client 10.1.111.112 port 32774
Sat Aug 11 11:29:14 2012 : Error: Ignoring request to authentication address * port 1812 from unknown client 10.1.111.113 port 32778
After lots of Googling, my first effort was re-adding all the Aerohives into the sqlite database manually:
root# cd /etc/raddb root# sqlite3 sqlite_radius_client_database sqlite> DELETE * FROM nas; sqlite> PRAGMA table_info(nas); 0|id|int(10)|1||0 1|nasname|varchar(128)|1||0 2|shortname|varchar(32)|0||0 3|type|varchar(30)|0||0 4|ports|int(5)|0||0 5|secret|varchar(60)|1||0 6|community|varchar(50)|0||0 7|description|varchar(1024)|0||0 ... sqlite> INSERT INTO nas VALUES ("12","10.1.111.112","AEROHIVE-NAME-12","Aerohive","","passwordvalue","",""); ...
But that didn't work. Then my coworker found a solution: add the Aerohive base stations into the
clients.conf
file:
root# cp -p /etc/raddb/clients.conf /opt/local/etc/raddb/ root# vi /opt/local/etc/raddb/clients.conf client NC-AHAP-12 { ipaddr = 10.1.111.112 type = Aerohive nasname = 10.1.111.112 secret = passwordhere shortname = AEROHIVE-NAME-12 nastype = other } # add one for each access point
Note: the MacPorts compiledradiusd
wanted to read files from/opt/local/
, because that's where it lives; honestly it got very confusing, though, because sometimes it would read from/etc/raddb
, and sometimes from/opt/local/etc/raddb
; my solution to this was hard linking the files, or symlinking theraddb
directory.
Note: in order to make your customradiusd
the default as launched byServer Admin.app
or theserveradmin
CLI, it needs to live or be hard-linked in/usr/sbin/
(along withradwatch
,radmin
,rc.radiusd
, etc):
root# ln /opt/local/sbin/radiusd /opt/local/sbin or root# cp -p /opt/local/sbin/radiusd /opt/local/sbin
When we next launched
radiusd
, the "authentication address" errors disappeared and radiusd
said "Ready to process requests." We were able to authenticate on a wireless device as a normal user, but still not as a user with the reset-password box checked. I don't know why.Also, we saw these errors in the log constantly, for all the Aerohives:
Error: rlm_radutmp: Logout entry for NAS AEROHIVE-NAME-20 port 0 has wrong ID
I tried editing
/etc/raddb/users
as suggested by http://hints.macworld.com/article.php?story=20071130134610850:
DEFAULT Auth-Type = opendirectory Fall-Through = 1
But this didn’t work, either.
The new
radiusd
was left running overnight, since it appeared to be working. But at some point it stopped working, so the next morning no users were able to log in at all. I reverted to my backups of the original radiusd and raddb, though oddly the "wrong ID" error messages have persisted despite everything being put back to normal (/usr/sbin/radiusd
, /etc/raddb/
, et al).At that point, I am sad to report, I gave up. By then I had already spent 14+ hours on the project, and since:
A) we could not keep the new version of
radiusd
functional, andB) we had a viable workaround (users sign in to a wired machine to reset passwords before using a wireless laptop), and
C) Apple has already started phasing out Open Directory in future versions of OS X Server, and
D) we have other options to explore for RADIUS server devices, such as our smart switches, or a Windows-based server (blech!)
it was deemed not worthwhile to pursue this particular solution any further.
Even though I was the one who suggested abandoning efforts, I am still very bothered that I couldn't solve this. The journey was educational, but frustrating, as I felt perpetually "this close" to cracking it. I hope what I have written here will be useful to some other server admin out there, and that you are able to take this the rest of the way into a working solution. Good luck.
No comments:
Post a Comment