Hidden deep in the bowels of the password server you will find some interesting, if seldom used, password policies that can be applied. Here Arek Dreyer takes a look at the weakpass_edit command and how it can help you refine your security policies.
Is it possible (but completely unsupported!) for you to prevent your users from changing their passwords to "weak or undesirable passwords". You have to supply the list of these "bad" passwords. Note that this does not affect the act of changing passwords with Workgroup Manager, and it breaks the ability to change passwords with the Kerberos application. Did I mention this is unsupported?
Step 1: In /Library/Preferences/com.apple.passwordserver.plist, for the key "ExternalCommand", change the value of "Disabled" to "weakpass". You don't even need to HUP the PasswordService daemon; it starts using weakpass right away.
Step 2: Use the weakpass_edit command to start your dictionary of weak or otherwise undesirable passwords which the password service should reject, like this:
<code>sudo weakpass_edit add -p riley</code>
That's it. There is no step three.
However, if you're interested in the details, I have some more information below.
On Mac OS X Server 10.4(.n?) or later, look at /Library/Preferences/com.apple.passwordserver.plist. By default, the key "ExternalCommand" has the value of "Disabled". Here's one way to figure out what happens when you change "Disabled":
Replace "Disabled" with "SomeOtherCommand.sh"
Watch the output of the command below while you change a password:
<code>sudo fs_usage | grep SomeOtherCommand.sh</code>
Here's my sample output from fs_usage above (with extra spaces removed)
13:39:24 stat /usr/sbin/authserver/tools/SomeExternalCommand.sh 0.000035 PasswordServ
I never specified /usr/sbin/authserver/tools, but there it is…take a look in /usr/sbin/authserver/tools, and you'll see the file "weakpass". So now we know that an "ExternalScript" script can be placed in /usr/sbin/authserver/tools. But let's check out what "weakpass" does before we try to write our own weakpass script. To continue the investigation, change SomeOtherCommand.sh to weakpass in com.apple.passwordserver.plist.
Unfortunately, there is no man page for weakpass. Good thing we have apropos.
"apropos weakpass" should lead you to weakpass_edit, which does have a man page. The "Files & Folders" section of the weakpass_edit man page references "/var/db/authserver/weakpasswords.[n]"
I thought I could simply create a file weakpasswords.0, stuff some cleartext words in there, and make it readable only by root. I was wrong.
I changed a password while watching the output of "fs_usage | grep weak", and weakpass was trying to open (seemingly random) "n"s in weakpasswords.[n]. The n isn't random, it is some hash or index, and I don't know the algorithm.
Don't create and edit random weakpasswords.[n] files like I did; use the weakpass_edit command, which places your entry in the correct file. If you have a bunch of weak passwords you want to inject, you could write a script which invokes weakpass_edit, so your weak passwords will be placed in the right file. I don't know the hashing algorithm, but weakpass_edit does.
Note that the contents of weakpasswords.[n] are clear text, AND when weakpass_edit creates them automatically, they are readable by owner, group, and others (of course, the parent directory /var/db/authserver is only readable by root).
When the weakpass executable rejects a password with the "passwd" command, the user will just see "sorry", as shown below:
balmoral:~ u1$ passwd
Changing password for u1.
Old password: [Here I typed in the good password]
New password: [Here I typed in "riley", the "bad" password I specified above]
Retype new password: [Here I typed in "riley" again]
Sorry
balmoral:~ u1$
If the user tries to change to a bad password via the Accounts pane of System Preferences, they will get a message indicating there is a problem: "You cannot change your password to the password you entered. Your system administrator may not allow you to change your password or there was some other problem with your password. Contact your system administrator for help."
If you force a user to change their password at next login, and they try to specify a bad password at their next login, the login window (change password dialog) will simply shake and the user must try again.
If the user tries to change their password at all with the Kerberos.app, they will get "Server error: Failed decrypting request", regardless of the proposed new password (if they fail to enter the correct old password, Kerberos.app replies with: "Kerberos Password Change Failed: Password incorrect").
Password changes will be logged to /Library/Logs/PasswordService/ApplePasswordServer.Server.log. Here are two examples:
Jun 18 2007 16:53:55 CHANGEPASS: external tool exit status = 1, message: rejected
Jun 18 2007 17:09:01 CHANGEPASS: external tool exit status = 0, message: approved
So, if we really wanted to write a script to process the password, it would have to exit with a status of 1 (and a "message" of rejected) to reject a password. Use the "strings" command on the weakpass executable if you're interested. The following strings got passed to my experimental external shell script, and as long as I used "exit 1", the password change was rejected; I didn't need to supply the message "rejected":
$1 the name of the account to change
$2 "standard" or "admin"
But how does the actual proposed password get sent to your script? I didn't see it; that's left for you to find and report back.
Note that using the ExternalCommand does add a few seconds to the password change process.
And finally, let me remind you: this is completely unsupported, the dictionaries are stored in cleartext (though readable only by root), and this breaks the ability to change passwords via Kerberos.app.
Nice article and detective work Arek. 🙂
After reading Arek’s article about using the ExternalCommand key in /Library/Preferences/com.apple.passwordserver.plist, I thought I’d add some more to the end of it for anyone attempting their own “weakpass” script or implementation…
First, to change the key:
defaults write /Library/Preferences/com.apple.passwordserver ExternalCommand weakpass
Arek didn’t have to HUP his PasswordService process, I did
sudo killall -HUP PasswordService
Third: three strings are passed to the ExternalCommand
$0=/usr/sbin/authserver/tools/weakpass
$1=name of the user account
$2 [admin / standard] user level
To do this, I used a simple shell script:
#!/bin/bash
echo $0 > /tmp/weakpass
echo $1 > /tmp/weakpass
echo $2 > /tmp/weakpass
echo $3 > /tmp/weakpass
echo $4 > /tmp/weakpass
$0, $1 and $2 struck gold, the rest were blank, so, the password does *NOT* get passed to the external command in the plist.
So, how then to write a “script” to accomplish this? Even though Arek correctly grokked that the ExternalCommand returns a 1 or 0 status to the PasswordService, the article didn’t mention that the weakpass command isn’t a script at all, it looks like a standard C tool. You can easily view the symbols by running:
nm /usr/sbin/authserver/weakpass.
For more information on how weakpass might get the password during an attempted password change using otool -l reveals it’s linked against
/usr/lib/libsasl2.2.dylib
as well as
/System/Library/PrivateFramworks/PasswordServer.framework/Versions/A/PasswordServer
which reveals that weakpass is not exactly a script and probably uses an undocumented (that’s the “Private” in a PrivateFramework) variable in the PasswordServer that might be called “proposedPass” or some such, but that’s just a WAG.
I wouldn’t expect Apple’s PasswordServer to pass a string to a shell script for the purposes of enforcing a password policy, that’s not exactly secure…
the weakpass tool is VERY strong in the sense that it doesn’t receive the password as a string from the PasswordServer, but weak in the sense that unless you know how to plugin to the password server, you can’t write your own ExternalCommand unless you also write your own app to change the password and send the string down to the ExternalCommand, but that’s not a great idea.
The “next best thing” (actually the only thing that could be done from what I know) would be to write a script that would somehow link Aspell to the weakpass wordlist, giving you full dictionary of words to check against, as well as a way to easily add custom word lists from a GUI or web page that hooked up with Aspell. I just haven’t found a way to dump the full dictionary from Aspell to standard output, once I do, the it’d be easy to pump it into a weakpass wordlist.